diff --git a/examples/testbed/src/free_fly_camera.rs b/examples/testbed/src/free_fly_camera.rs index d92bf64..63f8cc3 100644 --- a/examples/testbed/src/free_fly_camera.rs +++ b/examples/testbed/src/free_fly_camera.rs @@ -1,12 +1,19 @@ -use std::ptr::NonNull; - use lyra_engine::{ game::Game, - input::ActionHandler, + input::{ActionHandler, CommonActionLabel}, math::{Quat, Vec3, EulerRot}, - plugin::Plugin, ecs::{system::{System, IntoSystem}, world::World, Access, Component}, DeltaTime, scene::CameraComponent, + plugin::Plugin, ecs::{Component, query::{Res, View}}, DeltaTime, scene::CameraComponent, }; +/* enum FreeFlyCameraActions { + MoveForwardBackward, + MoveLeftRight, + MoveUpDown, + LookLeftRight, + LookUpDown, + LookRoll, +} */ + #[derive(Clone, Component)] pub struct FreeFlyCamera { pub speed: f32, @@ -41,74 +48,58 @@ impl FreeFlyCamera { } } -pub struct FreeFlyCameraPlugin; +pub fn free_fly_camera_controller(delta_time: Res, handler: Res, view: View<(&mut CameraComponent, &FreeFlyCamera)>) -> anyhow::Result<()> { + let delta_time = **delta_time; + for (mut cam, fly) in view.into_iter() { + let forward = cam.transform.forward(); + let left = cam.transform.left(); + let up = Vec3::Y; -impl System for FreeFlyCameraPlugin { - fn execute(&mut self, mut world: NonNull) -> anyhow::Result<()> { - let world = unsafe { world.as_mut() }; - let delta_time = **world.get_resource::(); - let handler = world.get_resource::(); + let move_y = handler.get_axis_modifier(CommonActionLabel::MoveUpDown).unwrap_or(0.0); + let move_x = handler.get_axis_modifier(CommonActionLabel::MoveLeftRight).unwrap_or(0.0); + let move_z = handler.get_axis_modifier(CommonActionLabel::MoveForwardBackward).unwrap_or(0.0); - for (mut cam, fly) in world - .view_iter::<(&mut CameraComponent, &FreeFlyCamera)>() - { - let forward = cam.transform.forward(); - let left = cam.transform.left(); - let up = Vec3::Y; + let mut velocity = Vec3::ZERO; + velocity += move_y * up; + velocity += move_x * left; + velocity += move_z * forward; - let move_y = handler.get_axis_modifier("up_down").unwrap_or(0.0); - let move_x = handler.get_axis_modifier("left_right").unwrap_or(0.0); - let move_z = handler.get_axis_modifier("forward_backward").unwrap_or(0.0); - - let mut velocity = Vec3::ZERO; - velocity += move_y * up; - velocity += move_x * left; - velocity += move_z * forward; - - if velocity != Vec3::ZERO { - cam.transform.translation += velocity.normalize() * fly.speed * delta_time; // TODO: speeding up - } - - let motion_x = handler.get_axis_modifier("look_left_right").unwrap_or(0.0); - let motion_y = handler.get_axis_modifier("look_up_down").unwrap_or(0.0); - let motion_z = handler.get_axis_modifier("look_rotate").unwrap_or(0.0); - let mut camera_rot = Vec3::ZERO; - camera_rot.y -= motion_x * fly.mouse_sensitivity; - camera_rot.x -= motion_y * fly.mouse_sensitivity; - camera_rot.z -= motion_z * fly.mouse_sensitivity; - - if camera_rot != Vec3::ZERO { - let look_velocity = camera_rot * fly.look_speed * delta_time; - - let (mut y, mut x, _) = cam.transform.rotation.to_euler(EulerRot::YXZ); - x += look_velocity.x; - y += look_velocity.y; - x = x.clamp(-1.54, 1.54); - - // rotation is not commutative, keep this order to avoid unintended roll - cam.transform.rotation = Quat::from_axis_angle(Vec3::Y, y) - * Quat::from_axis_angle(Vec3::X, x); - } + if velocity != Vec3::ZERO { + cam.transform.translation += velocity.normalize() * fly.speed * delta_time; // TODO: speeding up } - Ok(()) + let motion_x = handler.get_axis_modifier(CommonActionLabel::LookLeftRight).unwrap_or(0.0); + let motion_y = handler.get_axis_modifier(CommonActionLabel::LookUpDown).unwrap_or(0.0); + let motion_z = handler.get_axis_modifier(CommonActionLabel::LookRoll).unwrap_or(0.0); + + let mut camera_rot = Vec3::ZERO; + camera_rot.y -= motion_x * fly.mouse_sensitivity; + camera_rot.x -= motion_y * fly.mouse_sensitivity; + camera_rot.z -= motion_z * fly.mouse_sensitivity; + + if camera_rot != Vec3::ZERO { + let look_velocity = camera_rot * fly.look_speed * delta_time; + + let (mut y, mut x, _) = cam.transform.rotation.to_euler(EulerRot::YXZ); + x += look_velocity.x; + y += look_velocity.y; + x = x.clamp(-1.54, 1.54); + + // rotation is not commutative, keep this order to avoid unintended roll + cam.transform.rotation = Quat::from_axis_angle(Vec3::Y, y) + * Quat::from_axis_angle(Vec3::X, x); + } } - fn world_access(&self) -> lyra_engine::ecs::Access { - Access::Write - } + Ok(()) } -impl IntoSystem<()> for FreeFlyCameraPlugin { - type System = Self; - - fn into_system(self) -> Self::System { - self - } -} +/// A plugin that adds the free fly camera controller system to the world. It is expected that +/// there is a [`FreeFlyCamera`] in the world, if there isn't, the camera would not move. +pub struct FreeFlyCameraPlugin; impl Plugin for FreeFlyCameraPlugin { fn setup(&self, game: &mut Game) { - game.with_system("free_fly_camera_system", FreeFlyCameraPlugin, &[]); + game.with_system("free_fly_camera_system", free_fly_camera_controller, &[]); } } diff --git a/examples/testbed/src/main.rs b/examples/testbed/src/main.rs index 283ce6e..6fd6b7f 100644 --- a/examples/testbed/src/main.rs +++ b/examples/testbed/src/main.rs @@ -1,11 +1,21 @@ use std::ptr::NonNull; -use lyra_engine::{math::{self, Vec3}, math::Transform, input::{KeyCode, ActionHandler, Action, ActionKind, LayoutId, ActionMapping, ActionSource, ActionMappingId, InputActionPlugin, MouseInput, MouseAxis}, game::Game, render::{window::{CursorGrabMode, WindowOptions}, light::{PointLight, directional::DirectionalLight, SpotLight}}, change_tracker::Ct, ecs::{system::{Criteria, CriteriaSchedule, BatchedSystem, IntoSystem}, world::World, Component}, DeltaTime, scene::{TransformComponent, ModelComponent, CameraComponent}}; +use lyra_engine::{math::{self, Vec3}, math::Transform, input::{KeyCode, ActionHandler, Action, ActionKind, LayoutId, ActionMapping, ActionSource, ActionMappingId, InputActionPlugin, MouseInput, MouseAxis, CommonActionLabel}, game::Game, render::{window::{CursorGrabMode, WindowOptions}, light::{PointLight, directional::DirectionalLight, SpotLight}}, change_tracker::Ct, ecs::{system::{Criteria, CriteriaSchedule, BatchedSystem, IntoSystem}, world::World, Component}, DeltaTime, scene::{TransformComponent, ModelComponent, CameraComponent}}; use lyra_engine::assets::{ResourceManager, Model}; mod free_fly_camera; use free_fly_camera::{FreeFlyCameraPlugin, FreeFlyCamera}; +#[derive(Clone, Copy, Hash, Debug)] +pub enum ActionLabel { + MoveForwardBackward, + MoveLeftRight, + MoveUpDown, + LookLeftRight, + LookUpDown, + LookRoll, +} + struct FixedTimestep { max_tps: u32, fixed_time: f32, @@ -233,47 +243,59 @@ async fn main() { let action_handler = ActionHandler::new() .add_layout(LayoutId::from(0)) - .add_action("forward_backward", Action::new(ActionKind::Axis)) - .add_action("left_right", Action::new(ActionKind::Axis)) - .add_action("up_down", Action::new(ActionKind::Axis)) - .add_action("look_left_right", Action::new(ActionKind::Axis)) - .add_action("look_up_down", Action::new(ActionKind::Axis)) - .add_action("look_rotate", Action::new(ActionKind::Axis)) + .add_action(CommonActionLabel::MoveForwardBackward, Action::new(ActionKind::Axis)) + .add_action(CommonActionLabel::MoveLeftRight, Action::new(ActionKind::Axis)) + .add_action(CommonActionLabel::MoveUpDown, Action::new(ActionKind::Axis)) + .add_action(CommonActionLabel::LookLeftRight, Action::new(ActionKind::Axis)) + .add_action(CommonActionLabel::LookUpDown, Action::new(ActionKind::Axis)) + .add_action(CommonActionLabel::LookRoll, Action::new(ActionKind::Axis)) .add_mapping(ActionMapping::new(LayoutId::from(0), ActionMappingId::from(0)) - .bind("forward_backward", &[ + .bind(CommonActionLabel::MoveForwardBackward, &[ ActionSource::Keyboard(KeyCode::W).into_binding_modifier(1.0), ActionSource::Keyboard(KeyCode::S).into_binding_modifier(-1.0) ]) - .bind("left_right", &[ + .bind(CommonActionLabel::MoveLeftRight, &[ ActionSource::Keyboard(KeyCode::A).into_binding_modifier(-1.0), ActionSource::Keyboard(KeyCode::D).into_binding_modifier(1.0) ]) - .bind("up_down", &[ + .bind(CommonActionLabel::MoveUpDown, &[ ActionSource::Keyboard(KeyCode::C).into_binding_modifier(1.0), ActionSource::Keyboard(KeyCode::Z).into_binding_modifier(-1.0) ]) - .bind("look_left_right", &[ + .bind(CommonActionLabel::LookLeftRight, &[ ActionSource::Mouse(MouseInput::Axis(MouseAxis::X)).into_binding(), ActionSource::Keyboard(KeyCode::Left).into_binding_modifier(-1.0), ActionSource::Keyboard(KeyCode::Right).into_binding_modifier(1.0), //ActionSource::Gamepad(GamepadFormat::DualAxis, GamepadInput::Axis(GamepadAxis::RThumbstickX)).into_binding(), ]) - .bind("look_up_down", &[ + .bind(CommonActionLabel::LookUpDown, &[ ActionSource::Mouse(MouseInput::Axis(MouseAxis::Y)).into_binding(), ActionSource::Keyboard(KeyCode::Up).into_binding_modifier(-1.0), ActionSource::Keyboard(KeyCode::Down).into_binding_modifier(1.0), //ActionSource::Gamepad(GamepadFormat::DualAxis, GamepadInput::Axis(GamepadAxis::RThumbstickY)).into_binding(), ]) - .bind("look_rotate", &[ + .bind(CommonActionLabel::LookRoll, &[ ActionSource::Keyboard(KeyCode::E).into_binding_modifier(-1.0), ActionSource::Keyboard(KeyCode::Q).into_binding_modifier(1.0), ]) .finish() ); + /* #[allow(unused_variables)] + let test_system = |world: &mut World| -> anyhow::Result<()> { + let handler = world.get_resource::(); + + if let Some(alpha) = handler.get_axis_modifier("look_rotate") { + debug!("'look_rotate': {alpha}"); + } + + Ok(()) + }; */ + game.world().add_resource(action_handler); game.with_plugin(InputActionPlugin); + //game.with_system("input_test", test_system, &[]); }; Game::initialize().await diff --git a/lyra-ecs/src/query/resource.rs b/lyra-ecs/src/query/resource.rs index cee462f..377f5de 100644 --- a/lyra-ecs/src/query/resource.rs +++ b/lyra-ecs/src/query/resource.rs @@ -79,7 +79,7 @@ impl AsQuery for QueryResource { } /// A struct used for querying resources from the World. -pub struct Res<'a, T>(Ref<'a, T>); +pub struct Res<'a, T>(pub(crate) Ref<'a, T>); impl<'a, T: ResourceObject> std::ops::Deref for Res<'a, T> { type Target = T; @@ -167,7 +167,7 @@ impl AsQuery for QueryResourceMut { } /// A struct used for querying resources from the World. -pub struct ResMut<'a, T>(RefMut<'a, T>); +pub struct ResMut<'a, T>(pub(crate) RefMut<'a, T>); impl<'a, T: ResourceObject> std::ops::Deref for ResMut<'a, T> { type Target = T; diff --git a/lyra-ecs/src/system/fn_sys.rs b/lyra-ecs/src/system/fn_sys.rs index a159f57..08a6b9d 100644 --- a/lyra-ecs/src/system/fn_sys.rs +++ b/lyra-ecs/src/system/fn_sys.rs @@ -1,6 +1,6 @@ -use std::{ptr::NonNull, marker::PhantomData, cell::{Ref, RefMut}}; +use std::{ptr::NonNull, marker::PhantomData}; -use crate::{world::World, Access, ResourceObject, query::{Query, View, AsQuery}}; +use crate::{world::World, Access, ResourceObject, query::{Query, View, AsQuery, ResMut, Res}}; use super::{System, IntoSystem}; @@ -184,12 +184,12 @@ pub struct ResourceArgFetcher { phantom: PhantomData R> } -impl<'a, R: ResourceObject> FnArg for Ref<'a, R> { +impl<'a, R: ResourceObject> FnArg for Res<'a, R> { type Fetcher = ResourceArgFetcher; } impl FnArgFetcher for ResourceArgFetcher { - type Arg<'a> = Ref<'a, R>; + type Arg<'a> = Res<'a, R>; fn new() -> Self { ResourceArgFetcher { @@ -199,7 +199,7 @@ impl FnArgFetcher for ResourceArgFetcher { unsafe fn get<'a>(&mut self, world: NonNull) -> Self::Arg<'a> { let world = world.as_ref(); - world.get_resource::() + Res(world.get_resource::()) } } @@ -207,12 +207,12 @@ pub struct ResourceMutArgFetcher { phantom: PhantomData R> } -impl<'a, R: ResourceObject> FnArg for RefMut<'a, R> { +impl<'a, R: ResourceObject> FnArg for ResMut<'a, R> { type Fetcher = ResourceMutArgFetcher; } impl FnArgFetcher for ResourceMutArgFetcher { - type Arg<'a> = RefMut<'a, R>; + type Arg<'a> = ResMut<'a, R>; fn new() -> Self { ResourceMutArgFetcher { @@ -222,15 +222,15 @@ impl FnArgFetcher for ResourceMutArgFetcher { unsafe fn get<'a>(&mut self, world: NonNull) -> Self::Arg<'a> { let world = world.as_ref(); - world.get_resource_mut::() + ResMut(world.get_resource_mut::()) } } #[cfg(test)] mod tests { - use std::{ptr::NonNull, cell::RefMut}; + use std::ptr::NonNull; - use crate::{tests::{Vec2, Vec3}, world::World, query::{QueryBorrow, View}}; + use crate::{tests::{Vec2, Vec3}, world::World, query::{QueryBorrow, View, ResMut}}; use super::{System, IntoSystem}; struct SomeCounter(u32); @@ -355,8 +355,10 @@ mod tests { world.spawn((Vec2::rand(), Vec3::rand())); world.add_resource(SomeCounter(0)); - let test_system = |mut counter: RefMut| -> anyhow::Result<()> { - counter.0 += 10; + let test_system = |mut counter: ResMut| -> anyhow::Result<()> { + // .0 is twice here since ResMut's tuple field is pub(crate). + // Users wont need to do this + counter.0.0 += 10; Ok(()) }; @@ -374,10 +376,12 @@ mod tests { world.spawn((Vec2::rand(), )); world.add_resource(SomeCounter(0)); - let test_system = |mut counter: RefMut, view: View>| -> anyhow::Result<()> { + let test_system = |mut counter: ResMut, view: View>| -> anyhow::Result<()> { for v2 in view.into_iter() { println!("Got v2 at '{:?}'", v2); - counter.0 += 1; + // .0 is twice here since ResMut's tuple field is pub(crate). + // Users wont need to do this + counter.0.0 += 1; } Ok(()) diff --git a/lyra-game/src/input/action.rs b/lyra-game/src/input/action.rs index 5b52d22..db60930 100644 --- a/lyra-game/src/input/action.rs +++ b/lyra-game/src/input/action.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, ops::Deref}; +use std::{collections::HashMap, ops::Deref, hash::{Hash, DefaultHasher, Hasher}, fmt::Debug}; use glam::Vec2; use lyra_ecs::world::World; @@ -7,6 +7,54 @@ use crate::{plugin::Plugin, game::GameStages, EventQueue}; use super::{Button, KeyCode, InputButtons, MouseMotion}; +pub trait ActionLabel: Debug { + /// Returns a unique hash of the label. + fn label_hash(&self) -> u64; +} + +impl ActionLabel for T { + fn label_hash(&self) -> u64 { + let mut s = DefaultHasher::new(); + self.hash(&mut s); + s.finish() + } +} + +#[derive(Clone)] +pub struct ActionLabelWrapper(String, u64); + +impl Debug for ActionLabelWrapper { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.0) + } +} + +impl ActionLabel for ActionLabelWrapper { + fn label_hash(&self) -> u64 { + self.1 + } +} + +impl From for ActionLabelWrapper { + fn from(value: A) -> Self { + let lbl = format!("{:?}", value); + Self(lbl, value.label_hash()) + } +} + +/// Some commonly used action labels. +/// +/// The built-in systems uses these labels +#[derive(Clone, Copy, Hash, Debug)] +pub enum CommonActionLabel { + MoveForwardBackward, + MoveLeftRight, + MoveUpDown, + LookLeftRight, + LookUpDown, + LookRoll, +} + #[allow(dead_code)] #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] pub enum GamepadFormat { @@ -204,7 +252,7 @@ impl From for ActionMappingId { pub struct ActionMapping { layout: LayoutId, id: ActionMappingId, - action_binds: HashMap>, + action_binds: HashMap>, } impl ActionMapping { @@ -221,16 +269,17 @@ impl ActionMapping { /// If the action is not in this layout, this will panic! /// /// Parameters: - /// * `action_label` - The label corresponding to the action in this Layout. + /// * `action` - The label corresponding to the action in this Layout. /// * `bind` - The Binding to add to the Action. - pub fn bind(mut self, action_label: &str, bindings: &[Binding]) -> Self { + pub fn bind(mut self, action: L, bindings: &[Binding]) -> Self + where + L: ActionLabel + { let mut bindings = bindings.to_vec(); - let action_binds = self.action_binds.entry(action_label.to_string()).or_default(); + let action_binds = self.action_binds.entry(action.label_hash()).or_default(); action_binds.append(&mut bindings); - println!("Creating action label {}", action_label); - self } @@ -257,7 +306,7 @@ impl ActionMapping { #[derive(Clone, Default)] pub struct ActionHandler { - pub actions: HashMap, + pub actions: HashMap, pub layouts: HashMap, pub current_layout: LayoutId, pub current_mapping: ActionMappingId, @@ -274,8 +323,11 @@ impl ActionHandler { self } - pub fn add_action(mut self, label: &str, action: Action) -> Self { - self.actions.insert(label.to_string(), action); + pub fn add_action(mut self, label: L, action: Action) -> Self + where + L: ActionLabel + { + self.actions.insert(label.label_hash(), action); self } @@ -290,9 +342,12 @@ impl ActionHandler { /// Returns true if the action is pressed (or was just pressed). /// /// This will panic if the action name does not correspond to an action. - pub fn is_action_pressed(&self, action_name: &str) -> bool { - let action = self.actions.get(action_name) - .unwrap_or_else(|| panic!("Action {action_name} was not found")); + pub fn is_action_pressed(&self, action: L) -> bool + where + L: ActionLabel + { + let action = self.actions.get(&action.label_hash()) + .unwrap_or_else(|| panic!("Action {action:?} was not found")); matches!(action.state, ActionState::Pressed(_) | ActionState::JustPressed(_)) } @@ -300,9 +355,12 @@ impl ActionHandler { /// Returns true if the action was just pressed. /// /// This will panic if the action name does not correspond to an action. - pub fn was_action_just_pressed(&self, action_name: &str) -> bool { - let action = self.actions.get(action_name) - .unwrap_or_else(|| panic!("Action {action_name} was not found")); + pub fn was_action_just_pressed(&self, action: L) -> bool + where + L: ActionLabel + { + let action = self.actions.get(&action.label_hash()) + .unwrap_or_else(|| panic!("Action {action:?} was not found")); matches!(action.state, ActionState::JustPressed(_)) } @@ -310,9 +368,12 @@ impl ActionHandler { /// Returns true if the action was just released. /// /// This will panic if the action name does not correspond to an action. - pub fn was_action_just_released(&self, action_name: &str) -> bool { - let action = self.actions.get(action_name) - .unwrap_or_else(|| panic!("Action {action_name} was not found")); + pub fn was_action_just_released(&self, action: L) -> bool + where + L: ActionLabel + { + let action = self.actions.get(&action.label_hash()) + .unwrap_or_else(|| panic!("Action {action:?} was not found")); matches!(action.state, ActionState::JustReleased) } @@ -320,9 +381,12 @@ impl ActionHandler { /// Returns an action's state. /// /// This will panic if the action name does not correspond to an action. - pub fn get_action_state(&self, action_name: &str) -> ActionState { - let action = self.actions.get(action_name) - .unwrap_or_else(|| panic!("Action {action_name} was not found")); + pub fn get_action_state(&self, action: L) -> ActionState + where + L: ActionLabel + { + let action = self.actions.get(&action.label_hash()) + .unwrap_or_else(|| panic!("Action {action:?} was not found")); action.state } @@ -331,9 +395,12 @@ impl ActionHandler { /// Returns `None` if the action's state is not `ActionState::Pressed` or `ActionState::JustPressed`. /// /// This will panic if the action name does not correspond to an action. - pub fn get_pressed_modifier(&self, action_name: &str) -> Option { - let action = self.actions.get(action_name) - .unwrap_or_else(|| panic!("Action {action_name} was not found")); + pub fn get_pressed_modifier(&self, action: L) -> Option + where + L: ActionLabel + { + let action = self.actions.get(&action.label_hash()) + .unwrap_or_else(|| panic!("Action {action:?} was not found")); match action.state { ActionState::Pressed(v) | ActionState::JustPressed(v) => Some(v), @@ -345,9 +412,12 @@ impl ActionHandler { /// Returns `None` if the action's state is not `ActionState::JustPressed`. /// /// This will panic if the action name does not correspond to an action. - pub fn get_just_pressed_modifier(&self, action_name: &str) -> Option { - let action = self.actions.get(action_name) - .unwrap_or_else(|| panic!("Action {action_name} was not found")); + pub fn get_just_pressed_modifier(&self, action: L) -> Option + where + L: ActionLabel + { + let action = self.actions.get(&action.label_hash()) + .unwrap_or_else(|| panic!("Action {action:?} was not found")); match action.state { ActionState::JustPressed(v) => Some(v), @@ -359,9 +429,12 @@ impl ActionHandler { /// Returns `None` if the action's state is not `ActionState::Axis`. /// /// This will panic if the action name does not correspond to an action. - pub fn get_axis_modifier(&self, action_name: &str) -> Option { - let action = self.actions.get(action_name) - .unwrap_or_else(|| panic!("Action {action_name} was not found")); + pub fn get_axis_modifier(&self, action: L) -> Option + where + L: ActionLabel + { + let action = self.actions.get(&action.label_hash()) + .unwrap_or_else(|| panic!("Action {action:?} was not found")); match action.state { ActionState::Axis(v) => Some(v), @@ -386,8 +459,8 @@ fn actions_system(world: &mut World) -> anyhow::Result<()> { let layout = handler.layouts.get(&handler.current_layout).expect("No active layout"); let mapping = layout.mappings.get(&layout.active_mapping).expect("No active mapping"); - for (action_name, _) in mapping.action_binds.clone().iter() { - let action = handler.actions.get_mut(action_name).expect("Action name for binding is invalid!"); + for (action, _) in mapping.action_binds.clone().iter() { + let action = handler.actions.get_mut(action).expect("Action name for binding is invalid!"); if action.kind == ActionKind::Axis { action.state = ActionState::Axis(0.0); } @@ -409,8 +482,8 @@ fn actions_system(world: &mut World) -> anyhow::Result<()> { let layout = handler.layouts.get(&handler.current_layout).expect("No active layout"); let mapping = layout.mappings.get(&layout.active_mapping).expect("No active mapping"); - for (action_name, binds) in mapping.action_binds.clone().iter() { - let action = handler.actions.get_mut(action_name).expect("Action name for binding is invalid!"); + for (action_lbl, binds) in mapping.action_binds.clone().iter() { + let action = handler.actions.get_mut(action_lbl).expect("Action name for binding is invalid!"); let mut new_state = None; for bind in binds.iter() {