feat: detailed characters + Noita-style mouse aiming

Character detail:
- Player: 68 body parts (was 42) — eyes, hair, cape, hat, gloves
- Goblin: 56 body parts (was 39) — red eyes, pointed ears, teeth, skin tones
- New body labels: eye, hair, cape, tooth, ear
- Updated preview_ascii label map for new part types

Mouse aiming (Noita-style):
- Track mouse position via CursorMoved events
- Track left-click via MouseInput events
- Left-click shoots projectile toward mouse cursor direction
- Player faces left/right based on mouse X relative to player screen X
- Keyboard shoot (hjkl) still works as fallback
- Player.facing_right field added
- WindowInput: mouse_x, mouse_y, mouse_left, shoot_mouse fields
- on_mouse_move(), on_mouse_button() handlers
- clear_keys() also clears mouse state on focus loss

All 171 tests + 14 scenarios pass.
This commit is contained in:
Emil
2026-06-21 18:07:46 +03:00
parent 876eb031df
commit e50df7b51e
5 changed files with 148 additions and 58 deletions
+35
View File
@@ -19,6 +19,11 @@ pub struct WindowInput {
pub cam_down: bool,
pub quit: bool,
pub paint: Option<u8>,
pub mouse_x: f64,
pub mouse_y: f64,
pub mouse_left: bool,
pub mouse_left_was_down: bool,
pub shoot_mouse: bool,
jump_was_down: bool,
shoot_left_was_down: bool,
shoot_right_was_down: bool,
@@ -51,6 +56,11 @@ impl WindowInput {
cam_down: false,
quit: false,
paint: None,
mouse_x: 0.0,
mouse_y: 0.0,
mouse_left: false,
mouse_left_was_down: false,
shoot_mouse: false,
jump_was_down: false,
shoot_left_was_down: false,
shoot_right_was_down: false,
@@ -82,6 +92,28 @@ impl WindowInput {
pub fn clear_keys(&mut self) {
self.down_keys.clear();
self.mouse_left = false;
}
pub fn on_mouse_move(&mut self, x: f64, y: f64) {
self.mouse_x = x;
self.mouse_y = y;
}
pub fn on_mouse_button(
&mut self,
button: winit::event::MouseButton,
state: winit::event::ElementState,
) {
match (button, state) {
(winit::event::MouseButton::Left, winit::event::ElementState::Pressed) => {
self.mouse_left = true;
}
(winit::event::MouseButton::Left, winit::event::ElementState::Released) => {
self.mouse_left = false;
}
_ => {}
}
}
pub fn update(&mut self) {
@@ -129,6 +161,9 @@ impl WindowInput {
self.drop_item = now_drop_item && !self.drop_item_was_down;
self.drop_item_was_down = now_drop_item;
self.shoot_mouse = self.mouse_left && !self.mouse_left_was_down;
self.mouse_left_was_down = self.mouse_left;
self.cam_left = keys.contains(&KeyCode::KeyY);
self.cam_right = keys.contains(&KeyCode::KeyU);
self.cam_up = keys.contains(&KeyCode::KeyI);