1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
use rusty_core::prelude::Vec2;
use serde::{Deserialize, Serialize};

/// Stateful, stack-based button processor.  You can use this to process button state/values and
/// update a `PlayerInput` that you can send to the server.  Also handles the attack button.
pub struct ButtonProcessor {
    horizontal: Vec<ButtonValue>,
    vertical: Vec<ButtonValue>,
    pub direction: Vec2,
}

impl ButtonProcessor {
    /// Create a new `ButtonProcessor`
    pub fn new() -> Self {
        Self {
            horizontal: Vec::new(),
            vertical: Vec::new(),
            direction: Vec2::new(0.0, 0.0),
        }
    }
    /// Process one button, and update the direction vector.
    pub fn process(&mut self, button_value: ButtonValue, button_state: ButtonState) {
        match button_state {
            ButtonState::Pressed => match button_value {
                ButtonValue::Up | ButtonValue::Down => self.vertical.push(button_value),
                ButtonValue::Left | ButtonValue::Right => self.horizontal.push(button_value),
                _ => (),
            },
            ButtonState::Released => match button_value {
                ButtonValue::Up | ButtonValue::Down => self.vertical.retain(|&x| x != button_value),
                ButtonValue::Left | ButtonValue::Right => {
                    self.horizontal.retain(|&x| x != button_value)
                }
                _ => (),
            },
        }
        // Set horizontal movement based on the stack
        if let Some(last_horiz) = self.horizontal.last() {
            match last_horiz {
                ButtonValue::Left => self.direction.x = -1.0,
                ButtonValue::Right => self.direction.x = 1.0,
                _ => {}
            }
        } else {
            self.direction.x = 0.0;
        }
        // Set vertical movement based on the stack
        if let Some(last_vert) = self.vertical.last() {
            match last_vert {
                ButtonValue::Up => self.direction.y = 1.0,
                ButtonValue::Down => self.direction.y = -1.0,
                _ => {}
            }
        } else {
            self.direction.y = 0.0;
        }
        // Normalize
        if self.direction.magnitude() > 0.01 {
            self.direction = self.direction.normalize();
        }
    }
}

/// Abstracted button values you may receive (arrow keys and WASD keys combined into directions, for
/// example)
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq)]
pub enum ButtonValue {
    /// An abstracted button that combines: Arrow Up, W, Comma (Dvorak)
    Up,
    /// An abstracted button that combines: Arrow Down, S, O (Dvorak)
    Down,
    /// An abstracted button that combines: Arrow Left, A
    Left,
    /// An abstracted button that combines: Arrow Right, D, E (Dvorak)
    Right,
    /// An abstracted button that combines: Left Mouse Button, Space Bar, Backspace
    Action1,
    /// An abstracted button that combines: Right Mouse Button, Enter, Return
    Action2,
    /// An abstracted button that combines: Any other Mouse Button, Tab
    Action3,
    /// An abstracted button that combines: =/+ key
    Increase,
    /// An abstracted button that combines: -/_ key
    Decrease,
}

/// Whether a button was pressed or released
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq)]
pub enum ButtonState {
    /// A button was just pressed
    Pressed,
    /// A button was just released
    Released,
}

/// `GameEvent` represents game events caused by a user, such as the mouse moving around, buttons
/// being pushed, or the window being closed.
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq)]
pub enum GameEvent {
    /// The user pressed Escape or closed the window. We should quit the game.
    Quit,
    /// Indicates the current position the mouse has moved to.  The mouse is now at this location in
    /// OpenGL coordinates.  Note that on some operating systems this event will fire even if the
    /// cursor is outside the bounds of the window.
    MouseMoved { position: Vec2 },
    /// Indicates that a button with variant `ButtonValue` has been either pressed or released
    /// (variant of `ButtonState`).  Note that both mouse buttons and keyboard buttons are
    /// abstracted and collected together into a few logical game buttons.
    Button {
        button_value: ButtonValue,
        button_state: ButtonState,
    },
}