• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Xevion / Pac-Man / 17012447928

16 Aug 2025 08:12PM UTC coverage: 38.85% (-12.3%) from 51.196%
17012447928

Pull #3

github

Xevion
chore: add cargo checks to pre-commit
Pull Request #3: ECS Refactor

161 of 1172 new or added lines in 23 files covered. (13.74%)

9 existing lines in 4 files now uncovered.

777 of 2000 relevant lines covered (38.85%)

101.8 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/systems/input.rs
1
use std::collections::{HashMap, HashSet};
2

3
use bevy_ecs::{
4
    event::EventWriter,
5
    resource::Resource,
6
    system::{NonSendMut, Res, ResMut},
7
};
8
use glam::Vec2;
9
use sdl2::{event::Event, keyboard::Keycode, EventPump};
10

11
use crate::systems::components::DeltaTime;
12
use crate::{
13
    events::{GameCommand, GameEvent},
14
    map::direction::Direction,
15
};
16

17
#[derive(Resource, Default, Debug, Copy, Clone)]
18
pub enum CursorPosition {
19
    #[default]
20
    None,
21
    Some {
22
        position: Vec2,
23
        remaining_time: f32,
24
    },
25
}
26

27
#[derive(Resource, Debug, Clone)]
28
pub struct Bindings {
29
    key_bindings: HashMap<Keycode, GameCommand>,
30
    movement_keys: HashSet<Keycode>,
31
    last_movement_key: Option<Keycode>,
32
}
33

34
impl Default for Bindings {
NEW
35
    fn default() -> Self {
×
NEW
36
        let mut key_bindings = HashMap::new();
×
NEW
37

×
NEW
38
        // Player movement
×
NEW
39
        key_bindings.insert(Keycode::Up, GameCommand::MovePlayer(Direction::Up));
×
NEW
40
        key_bindings.insert(Keycode::W, GameCommand::MovePlayer(Direction::Up));
×
NEW
41
        key_bindings.insert(Keycode::Down, GameCommand::MovePlayer(Direction::Down));
×
NEW
42
        key_bindings.insert(Keycode::S, GameCommand::MovePlayer(Direction::Down));
×
NEW
43
        key_bindings.insert(Keycode::Left, GameCommand::MovePlayer(Direction::Left));
×
NEW
44
        key_bindings.insert(Keycode::A, GameCommand::MovePlayer(Direction::Left));
×
NEW
45
        key_bindings.insert(Keycode::Right, GameCommand::MovePlayer(Direction::Right));
×
NEW
46
        key_bindings.insert(Keycode::D, GameCommand::MovePlayer(Direction::Right));
×
NEW
47

×
NEW
48
        // Game actions
×
NEW
49
        key_bindings.insert(Keycode::P, GameCommand::TogglePause);
×
NEW
50
        key_bindings.insert(Keycode::Space, GameCommand::ToggleDebug);
×
NEW
51
        key_bindings.insert(Keycode::M, GameCommand::MuteAudio);
×
NEW
52
        key_bindings.insert(Keycode::R, GameCommand::ResetLevel);
×
NEW
53
        key_bindings.insert(Keycode::Escape, GameCommand::Exit);
×
NEW
54
        key_bindings.insert(Keycode::Q, GameCommand::Exit);
×
NEW
55

×
NEW
56
        let movement_keys = HashSet::from([
×
NEW
57
            Keycode::W,
×
NEW
58
            Keycode::A,
×
NEW
59
            Keycode::S,
×
NEW
60
            Keycode::D,
×
NEW
61
            Keycode::Up,
×
NEW
62
            Keycode::Down,
×
NEW
63
            Keycode::Left,
×
NEW
64
            Keycode::Right,
×
NEW
65
        ]);
×
NEW
66

×
NEW
67
        Self {
×
NEW
68
            key_bindings,
×
NEW
69
            movement_keys,
×
NEW
70
            last_movement_key: None,
×
NEW
71
        }
×
NEW
72
    }
×
73
}
74

NEW
75
pub fn input_system(
×
NEW
76
    delta_time: Res<DeltaTime>,
×
NEW
77
    mut bindings: ResMut<Bindings>,
×
NEW
78
    mut writer: EventWriter<GameEvent>,
×
NEW
79
    mut pump: NonSendMut<&'static mut EventPump>,
×
NEW
80
    mut cursor: ResMut<CursorPosition>,
×
NEW
81
) {
×
NEW
82
    let mut movement_key_pressed = false;
×
NEW
83
    let mut cursor_seen = false;
×
84

NEW
85
    for event in pump.poll_iter() {
×
NEW
86
        match event {
×
NEW
87
            Event::Quit { .. } => {
×
NEW
88
                writer.write(GameEvent::Command(GameCommand::Exit));
×
NEW
89
            }
×
NEW
90
            Event::MouseMotion { x, y, .. } => {
×
NEW
91
                *cursor = CursorPosition::Some {
×
NEW
92
                    position: Vec2::new(x as f32, y as f32),
×
NEW
93
                    remaining_time: 0.20,
×
NEW
94
                };
×
NEW
95
                cursor_seen = true;
×
NEW
96
            }
×
97
            Event::KeyUp {
98
                repeat: false,
NEW
99
                keycode: Some(key),
×
100
                ..
101
            } => {
102
                // If the last movement key was released, then forget it.
NEW
103
                if let Some(last_movement_key) = bindings.last_movement_key {
×
NEW
104
                    if last_movement_key == key {
×
NEW
105
                        bindings.last_movement_key = None;
×
NEW
106
                    }
×
NEW
107
                }
×
108
            }
109
            Event::KeyDown {
NEW
110
                keycode: Some(key),
×
NEW
111
                repeat: false,
×
NEW
112
                ..
×
NEW
113
            } => {
×
NEW
114
                let command = bindings.key_bindings.get(&key).copied();
×
NEW
115
                if let Some(command) = command {
×
NEW
116
                    writer.write(GameEvent::Command(command));
×
NEW
117
                }
×
118

NEW
119
                if bindings.movement_keys.contains(&key) {
×
NEW
120
                    movement_key_pressed = true;
×
NEW
121
                    bindings.last_movement_key = Some(key);
×
NEW
122
                }
×
123
            }
NEW
124
            _ => {}
×
125
        }
126
    }
127

NEW
128
    if let Some(last_movement_key) = bindings.last_movement_key {
×
NEW
129
        if !movement_key_pressed {
×
NEW
130
            let command = bindings.key_bindings.get(&last_movement_key).copied();
×
NEW
131
            if let Some(command) = command {
×
NEW
132
                writer.write(GameEvent::Command(command));
×
NEW
133
            }
×
NEW
134
        }
×
NEW
135
    }
×
136

NEW
137
    if let (false, CursorPosition::Some { remaining_time, .. }) = (cursor_seen, &mut *cursor) {
×
NEW
138
        *remaining_time -= delta_time.0;
×
NEW
139
        if *remaining_time <= 0.0 {
×
NEW
140
            *cursor = CursorPosition::None;
×
NEW
141
        }
×
NEW
142
    }
×
NEW
143
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc