use crate::prelude::Engine;
use bevy::{platform::collections::HashSet, prelude::*};
pub use bevy::{
input::{
ButtonState,
mouse::{MouseButton, MouseButtonInput, MouseMotion, MouseWheel},
},
window::CursorMoved,
};
pub(crate) struct MousePlugin;
impl Plugin for MousePlugin {
fn build(&self, app: &mut bevy::prelude::App) {
app.insert_resource(MouseState::default())
.add_systems(Update, (sync_mouse_state, sync_mouse_events));
}
}
pub struct MouseStateChain(MouseState);
impl MouseStateChain {
#[inline]
pub fn pressed(&self, mouse_button: MouseButton, mut then: impl FnMut(&MouseState)) -> &Self {
if self.0.pressed(mouse_button) {
then(&self.0);
}
self
}
#[inline]
pub fn pressed_any(
&self,
mouse_buttons: &[MouseButton],
mut then: impl FnMut(&MouseState),
) -> &Self {
if self.0.pressed_any(mouse_buttons) {
then(&self.0);
}
self
}
#[inline]
pub fn just_pressed(
&self,
mouse_button: MouseButton,
mut then: impl FnMut(&MouseState),
) -> &Self {
if self.0.just_pressed(mouse_button) {
then(&self.0);
}
self
}
#[inline]
pub fn just_pressed_any(
&self,
mouse_buttons: &[MouseButton],
mut then: impl FnMut(&MouseState),
) -> &Self {
if self.0.just_pressed_any(mouse_buttons) {
then(&self.0);
}
self
}
#[inline]
pub fn just_released(
&self,
mouse_button: MouseButton,
mut then: impl FnMut(&MouseState),
) -> &Self {
if self.0.just_released(mouse_button) {
then(&self.0);
}
self
}
#[inline]
pub fn just_released_any(
&self,
mouse_buttons: &[MouseButton],
mut then: impl FnMut(&MouseState),
) -> &Self {
if self.0.just_released_any(mouse_buttons) {
then(&self.0);
}
self
}
}
#[derive(Clone, Debug, Default, Resource)]
pub struct MouseState {
location: Option<Vec2>,
motion: Vec2,
wheel: MouseWheelState,
pressed: HashSet<MouseButton>,
just_pressed: HashSet<MouseButton>,
just_released: HashSet<MouseButton>,
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct MouseWheelState {
pub y: f32,
pub x: f32,
}
impl MouseState {
pub fn location(&self) -> Option<Vec2> {
self.location
}
pub fn motion(&self) -> Vec2 {
self.motion
}
pub fn wheel(&self) -> MouseWheelState {
self.wheel
}
pub fn pressed(&self, mouse_button: MouseButton) -> bool {
self.pressed.contains(&mouse_button)
}
pub fn just_pressed(&self, mouse_button: MouseButton) -> bool {
self.just_pressed.contains(&mouse_button)
}
pub fn just_released(&self, mouse_button: MouseButton) -> bool {
self.just_released.contains(&mouse_button)
}
pub fn pressed_any(&self, mouse_buttons: &[MouseButton]) -> bool {
mouse_buttons.iter().any(|k| self.pressed(*k))
}
pub fn just_pressed_any(&self, mouse_buttons: &[MouseButton]) -> bool {
mouse_buttons.iter().any(|k| self.just_pressed(*k))
}
pub fn just_released_any(&self, mouse_buttons: &[MouseButton]) -> bool {
mouse_buttons.iter().any(|k| self.just_released(*k))
}
pub fn chain(&self) -> MouseStateChain {
MouseStateChain(self.clone())
}
}
fn sync_mouse_events(
mut game_state: ResMut<Engine>,
mut mouse_button_events: MessageReader<MouseButtonInput>,
mut cursor_moved_events: MessageReader<CursorMoved>,
mut mouse_motion_events: MessageReader<MouseMotion>,
mut mouse_wheel_events: MessageReader<MouseWheel>,
) {
game_state.mouse_button_events.clear();
game_state.mouse_location_events.clear();
game_state.mouse_motion_events.clear();
game_state.mouse_wheel_events.clear();
for ev in mouse_button_events.read() {
game_state.mouse_button_events.push(*ev);
}
for ev in cursor_moved_events.read() {
let mut new_event = ev.clone();
new_event.position.x -= game_state.window_dimensions.x * 0.5;
new_event.position.y = -new_event.position.y + (game_state.window_dimensions.y * 0.5);
game_state.mouse_location_events.push(new_event);
}
for ev in mouse_motion_events.read() {
let mut ev2 = *ev;
ev2.delta.y *= -1.0;
game_state.mouse_motion_events.push(ev2);
}
for ev in mouse_wheel_events.read() {
game_state.mouse_wheel_events.push(*ev);
}
}
fn sync_mouse_state(
mouse_button_input: Res<ButtonInput<MouseButton>>,
mut mouse_state: ResMut<MouseState>,
mut mouse_motion_events: MessageReader<MouseMotion>,
mut cursor_moved_events: MessageReader<CursorMoved>,
mut mouse_wheel_events: MessageReader<MouseWheel>,
game_state: Res<Engine>,
) {
if let Some(event) = cursor_moved_events.read().last() {
let mut location = event.position;
location.x -= game_state.window_dimensions.x * 0.5;
location.y = -location.y + (game_state.window_dimensions.y * 0.5);
mouse_state.location = Some(location);
}
mouse_state.motion = Vec2::ZERO;
for ev in mouse_motion_events.read() {
let mut ev2 = *ev;
ev2.delta.y *= -1.0;
mouse_state.motion += ev2.delta;
}
mouse_state.wheel = MouseWheelState::default();
let mut cumulative_x = 0.0;
let mut cumulative_y = 0.0;
for ev in mouse_wheel_events.read() {
cumulative_x += ev.x;
cumulative_y += ev.y;
}
mouse_state.wheel.x = match cumulative_x {
x if x > 0.0 => 1.0,
x if x < 0.0 => -1.0,
_ => 0.0,
};
mouse_state.wheel.y = match cumulative_y {
y if y > 0.0 => 1.0,
y if y < 0.0 => -1.0,
_ => 0.0,
};
mouse_state.pressed.clear();
for mouse_button in mouse_button_input.get_pressed() {
mouse_state.pressed.insert(*mouse_button);
}
mouse_state.just_pressed.clear();
for mouse_button in mouse_button_input.get_just_pressed() {
mouse_state.just_pressed.insert(*mouse_button);
}
mouse_state.just_released.clear();
for mouse_button in mouse_button_input.get_just_released() {
mouse_state.just_released.insert(*mouse_button);
}
}