diff --git a/Design.md b/Design.md index 76be8b9..c1219e8 100644 --- a/Design.md +++ b/Design.md @@ -24,8 +24,9 @@ Multiplayer - [x] Issue with moving around quickly - [x] Bring Crouching back - [x] Inspect animation & state (procedural) - - [ ] TODO: Attachment editor system when in inspect mode - - [ ] TODO: remove Start and End optic from Slots, instead make one slot, and have code that checks for the starter slot and the end slot + - [x] Attachment editor system when in inspect mode + - [x] remove Start and End optic from Slots, instead make one slot, and have code that checks for the starter slot and the end slot + - [ ] SightSlot not retaining position when dropped or switched. - [x] High Ready & Low Ready system with state - [x] High ready animation (procedural) - [x] Low ready animation (procedural) diff --git a/src/comps/core/weapons/attachments/attachment.rs b/src/comps/core/weapons/attachments/attachment.rs index 1094c61..e8eb545 100644 --- a/src/comps/core/weapons/attachments/attachment.rs +++ b/src/comps/core/weapons/attachments/attachment.rs @@ -5,5 +5,16 @@ /// - Aim Speed /// - pub trait Attachment { + /// Starts at 1 + fn current_attachment_index(&self) -> u32; + fn all_attachments() -> Vec where Self: Sized; +} +/// Used to switch attachments. +/// +/// - Next == Next attachment +/// - Previous == Previous attachment +pub enum Position { + Next, + Previous, } \ No newline at end of file diff --git a/src/comps/core/weapons/attachments/compensator.rs b/src/comps/core/weapons/attachments/compensator.rs index cbe7839..afbcec2 100644 --- a/src/comps/core/weapons/attachments/compensator.rs +++ b/src/comps/core/weapons/attachments/compensator.rs @@ -1,9 +1,23 @@ use bevy::{reflect::{Reflect, std_traits::ReflectDefault}, ecs::{component::Component, reflect::ReflectComponent}}; +use super::attachment::Attachment; + #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Reflect, Component, Debug, Default)] #[reflect(Component, Default)] pub enum Compensator { #[default] FirstCompensator, +} + +impl Attachment for Compensator { + fn current_attachment_index(&self) -> u32 { + match self { + Compensator::FirstCompensator => 1, + } + } + + fn all_attachments() -> Vec where Self: Sized { + Vec::from([Self::FirstCompensator]) + } } \ No newline at end of file diff --git a/src/comps/core/weapons/attachments/foregrip.rs b/src/comps/core/weapons/attachments/foregrip.rs index 4307623..bc5ce20 100644 --- a/src/comps/core/weapons/attachments/foregrip.rs +++ b/src/comps/core/weapons/attachments/foregrip.rs @@ -1,9 +1,23 @@ use bevy::{reflect::{Reflect, std_traits::ReflectDefault}, ecs::{component::Component, reflect::ReflectComponent}}; +use super::attachment::Attachment; + #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Reflect, Component, Debug, Default)] #[reflect(Component, Default)] pub enum ForeGrip { #[default] Pk5, +} + +impl Attachment for ForeGrip { + fn current_attachment_index(&self) -> u32 { + match self { + ForeGrip::Pk5 => 1, + } + } + + fn all_attachments() -> Vec where Self: Sized { + Vec::from([Self::Pk5]) + } } \ No newline at end of file diff --git a/src/comps/core/weapons/attachments/magazine.rs b/src/comps/core/weapons/attachments/magazine.rs index 6d397db..d289ed2 100644 --- a/src/comps/core/weapons/attachments/magazine.rs +++ b/src/comps/core/weapons/attachments/magazine.rs @@ -2,6 +2,8 @@ use bevy::{reflect::{Reflect, std_traits::ReflectDefault}, ecs::{component::Comp use crate::comps::core::weapons::magazine_data::MagazineData; +use super::attachment::Attachment; + #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Reflect, Component, Debug, Default)] #[reflect(Component, Default)] @@ -19,4 +21,16 @@ impl Magazine { Magazine::Ak105 => 30, } } +} + +impl Attachment for Magazine { + fn current_attachment_index(&self) -> u32 { + match self { + Self::Ak105 => 1, + } + } + + fn all_attachments() -> Vec where Self: Sized { + Vec::from([Self::Ak105]) + } } \ No newline at end of file diff --git a/src/comps/core/weapons/attachments/optic.rs b/src/comps/core/weapons/attachments/optic.rs index 7f600e8..8e0d4f6 100644 --- a/src/comps/core/weapons/attachments/optic.rs +++ b/src/comps/core/weapons/attachments/optic.rs @@ -1,9 +1,23 @@ use bevy::{reflect::{Reflect, std_traits::ReflectDefault}, ecs::{component::Component, reflect::ReflectComponent}}; +use super::attachment::Attachment; + #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Reflect, Component, Debug, Default)] #[reflect(Component, Default)] pub enum Optic { #[default] AimpointT1, +} + +impl Attachment for Optic { + fn current_attachment_index(&self) -> u32 { + match self { + Self::AimpointT1 => 1, + } + } + + fn all_attachments() -> Vec where Self: Sized { + Vec::from([Self::AimpointT1]) + } } \ No newline at end of file diff --git a/src/comps/core/weapons/attachments/silencer.rs b/src/comps/core/weapons/attachments/silencer.rs index 6a46a4a..914fefd 100644 --- a/src/comps/core/weapons/attachments/silencer.rs +++ b/src/comps/core/weapons/attachments/silencer.rs @@ -1,9 +1,23 @@ use bevy::{reflect::{Reflect, std_traits::ReflectDefault}, ecs::{component::Component, reflect::ReflectComponent}}; +use super::attachment::Attachment; + #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Reflect, Component, Debug, Default)] #[reflect(Component, Default)] pub enum Silencer { #[default] FirstSilencer +} + +impl Attachment for Silencer { + fn current_attachment_index(&self) -> u32 { + match self { + Self::FirstSilencer => 1, + } + } + + fn all_attachments() -> Vec where Self: Sized { + Vec::from([Self::FirstSilencer]) + } } \ No newline at end of file diff --git a/src/comps/core/weapons/attachments/stock.rs b/src/comps/core/weapons/attachments/stock.rs index 2f4637d..dbea646 100644 --- a/src/comps/core/weapons/attachments/stock.rs +++ b/src/comps/core/weapons/attachments/stock.rs @@ -1,9 +1,23 @@ use bevy::{reflect::{Reflect, std_traits::ReflectDefault}, ecs::{component::Component, reflect::ReflectComponent}}; +use super::attachment::Attachment; + #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Reflect, Component, Debug, Default)] #[reflect(Component, Default)] pub enum Stock { #[default] MagpullTan, +} + +impl Attachment for Stock { + fn current_attachment_index(&self) -> u32 { + match self { + Self::MagpullTan => 1, + } + } + + fn all_attachments() -> Vec where Self: Sized { + Vec::from([Self::MagpullTan]) + } } \ No newline at end of file diff --git a/src/comps/core/weapons/attachments/weapon_attachment.rs b/src/comps/core/weapons/attachments/weapon_attachment.rs index 66b8dab..ca332a8 100644 --- a/src/comps/core/weapons/attachments/weapon_attachment.rs +++ b/src/comps/core/weapons/attachments/weapon_attachment.rs @@ -2,7 +2,7 @@ use bevy::prelude::*; use crate::comps::core::weapons::{attachment_slot::AttachmentSlot, slot::slot::WeaponSlot}; -use super::{compensator::Compensator, magazine::Magazine, stock::Stock, foregrip::ForeGrip, optic::Optic}; +use super::{compensator::Compensator, magazine::Magazine, stock::Stock, foregrip::ForeGrip, optic::Optic, attachment::{Attachment, Position}}; #[derive(Component, PartialEq, Eq, PartialOrd, Ord, Clone, Reflect, Debug)] #[reflect(Component, Default)] @@ -67,4 +67,73 @@ impl WeaponAttachment { WeaponAttachment::Optic(_) => slot == &WeaponSlot::SightSlot, } } + /* */ + pub fn next_attachment(&self) -> Self { + match self { + WeaponAttachment::Compensator(compensator) => { + let next_index = compensator.current_attachment_index() + 1; + let all_compensators = Compensator::all_attachments(); + WeaponAttachment::Compensator(if next_index > all_compensators.len() as u32 { all_compensators.get(0).unwrap().clone() } else { all_compensators.get((next_index - 1) as usize).unwrap().clone() }) + }, + WeaponAttachment::Magazine(magazine) => { + let next_index = magazine.current_attachment_index() + 1; + let all_magazines = Magazine::all_attachments(); + WeaponAttachment::Magazine(if next_index > all_magazines.len() as u32 { all_magazines.get(0).unwrap().clone() } else { all_magazines.get((next_index - 1) as usize).unwrap().clone() }) + }, + WeaponAttachment::Stock(stock) => { + let next_index = stock.current_attachment_index() + 1; + let all_stocks = Stock::all_attachments(); + WeaponAttachment::Stock(if next_index > all_stocks.len() as u32 { all_stocks.get(0).unwrap().clone() } else { all_stocks.get((next_index - 1) as usize).unwrap().clone() }) + }, + WeaponAttachment::ForeGrip(foregrip) => { + let next_index = foregrip.current_attachment_index() + 1; + let all_foregrips = ForeGrip::all_attachments(); + WeaponAttachment::ForeGrip(if next_index > all_foregrips.len() as u32 { all_foregrips.get(0).unwrap().clone() } else { all_foregrips.get((next_index - 1) as usize).unwrap().clone() }) + }, + WeaponAttachment::Optic(optic) => { + let next_index = optic.current_attachment_index() + 1; + let all_optics = Optic::all_attachments(); + WeaponAttachment::Optic(if next_index > all_optics.len() as u32 { all_optics.get(0).unwrap().clone() } else { all_optics.get((next_index - 1) as usize).unwrap().clone() }) + }, + } + } + pub fn previous_attachment(&self) -> Self { + match self { + WeaponAttachment::Compensator(compensator) => { + let next_index = compensator.current_attachment_index() - 1; + let all_compensators = Compensator::all_attachments(); + WeaponAttachment::Compensator(if next_index == 0 { all_compensators.get(all_compensators.len() - 1).unwrap().clone() } else { all_compensators.get((next_index - 1) as usize).unwrap().clone() }) + }, + WeaponAttachment::Magazine(magazine) => { + let next_index = magazine.current_attachment_index() - 1; + let all_magazines = Magazine::all_attachments(); + WeaponAttachment::Magazine(if next_index == 0 { all_magazines.get(all_magazines.len() - 1).unwrap().clone() } else { all_magazines.get((next_index - 1) as usize).unwrap().clone() }) + }, + WeaponAttachment::Stock(stock) => { + let next_index = stock.current_attachment_index() - 1; + let all_stocks = Stock::all_attachments(); + WeaponAttachment::Stock(if next_index == 0 { all_stocks.get(all_stocks.len() - 1).unwrap().clone() } else { all_stocks.get((next_index - 1) as usize).unwrap().clone() }) + }, + WeaponAttachment::ForeGrip(foregrip) => { + let next_index = foregrip.current_attachment_index() - 1; + let all_foregrips = ForeGrip::all_attachments(); + WeaponAttachment::ForeGrip(if next_index == 0{ all_foregrips.get(all_foregrips.len() - 1).unwrap().clone() } else { all_foregrips.get((next_index - 1) as usize).unwrap().clone() }) + }, + WeaponAttachment::Optic(optic) => { + let next_index = optic.current_attachment_index() - 1; + let all_optics = Optic::all_attachments(); + WeaponAttachment::Optic(if next_index == 0 { all_optics.get(all_optics.len() - 1).unwrap().clone() } else { all_optics.get((next_index - 1) as usize).unwrap().clone() }) + }, + } + } + pub fn get_from_slot(slot: &WeaponSlot) -> Option { + match slot { + WeaponSlot::ForeGripSlot => Some(WeaponAttachment::ForeGrip(Default::default())), + WeaponSlot::MagazineSlot => Some(WeaponAttachment::Magazine(Default::default())), + WeaponSlot::CompensatorSlot => Some(WeaponAttachment::Compensator(Default::default())), + WeaponSlot::SightSlot => Some(WeaponAttachment::Optic(Default::default())), + WeaponSlot::StockSlot => Some(WeaponAttachment::Stock(Default::default())), + WeaponSlot::UtilitySlot => None, + } + } } \ No newline at end of file diff --git a/src/comps/core/weapons/firearm_state.rs b/src/comps/core/weapons/firearm_state.rs index 3584c51..24d3f6d 100644 --- a/src/comps/core/weapons/firearm_state.rs +++ b/src/comps/core/weapons/firearm_state.rs @@ -1,16 +1,55 @@ use bevy::prelude::*; -use super::{attachment_slot::AttachmentSlot, slot::slot::WeaponSlot, magazine_data::MagazineData}; +use super::{attachment_slot::AttachmentSlot, slot::slot::WeaponSlot, magazine_data::MagazineData, attachments::{weapon_attachment::WeaponAttachment, attachment::Position}}; #[derive(Component, Reflect, PartialEq, Debug, Clone)] pub struct FirearmState { pub attachment_slots: Vec, pub magazine_data: Option, + pub optic_pos: Option, } impl FirearmState { pub fn new(attachment_slots: Vec, magazine_data: Option) -> Self { - Self { attachment_slots, magazine_data: None } + Self { attachment_slots, magazine_data: None, optic_pos: None } } -} \ No newline at end of file + /// Call this function when a user requests to switch an attachment in a specific slot. + /// This function assumes you already took the necessary validations regarding if the user has the attachments or not. + /// + /// Passing a None value will remove the attachment from the AttachmentSlot with the WeaponSlot specified. + /// Passing a Some(true/false) value will switch the attachment +/- one position + pub fn switch_attachment_by_pos(&mut self, slot: &WeaponSlot, pos_opt: Option) { + for attachment_slot in self.attachment_slots.iter_mut() { + if &attachment_slot.slot_type == slot { + match pos_opt { + Some(ref pos) => { + match attachment_slot.attachment { + Some(ref attachment) => match pos { + Position::Next => attachment_slot.attachment = Some(attachment.next_attachment()), + Position::Previous => attachment_slot.attachment = Some(attachment.previous_attachment()), + }, + None => attachment_slot.attachment = WeaponAttachment::get_from_slot(slot), + } + }, + None => attachment_slot.attachment = None, + } + } + } + } +} + +/* +Some(add) => { + let add_by = if add { 1 } else { -1 }; + match attachment_slot.attachment { + Some(attachment) => , + None => { // This means there was no attachment there. pass -1 to the from_index() fn. It will return the last one + + }, + } + }, + None => { + attachment_slot.attachment = None; + }, +*/ \ No newline at end of file diff --git a/src/logic/core/guns/inspect.rs b/src/logic/core/guns/inspect.rs index 66ac0d9..706cd4e 100644 --- a/src/logic/core/guns/inspect.rs +++ b/src/logic/core/guns/inspect.rs @@ -1,16 +1,17 @@ use bevy::{prelude::*, input::mouse::MouseWheel, ecs::system::SystemParam}; -use crate::{comps::core::{markers::{proxy::{character::in_player_hands_parent::InPlayerHandsParent, physics::utils::TransformExt, weapons::sight_placement::{SightPlacementStart, SightPlacementEnd}}, player::Player, inspect_screen::InspectScreenSlotUiMarker}, weapons::slot::slot::WeaponSlot}, ui::game::game_ui_state::GameUiState, utils::hierarchy::find_child_in_parent_children}; +use crate::{comps::core::{markers::{proxy::{character::in_player_hands_parent::InPlayerHandsParent, physics::utils::TransformExt, weapons::sight_placement::{SightPlacementStart, SightPlacementEnd}}, player::Player, inspect_screen::InspectScreenSlotUiMarker}, weapons::{slot::slot::WeaponSlot, firearm_state::FirearmState, attachments::attachment::Position}}, ui::game::game_ui_state::GameUiState, utils::hierarchy::find_child_in_parent_children}; use super::player_firing::PlayerFiringInfo; #[derive(SystemParam)] pub struct InspectFirearmQueryParams<'w, 's> { children: Query<'w, 's, &'static Children>, - in_player_hands_parent_query: Query<'w, 's, (&'static mut Transform, Entity, &'static Parent), (With, Without, Without, Without)>, - player_firing_info_query: Query<'w, 's, (&'static mut PlayerFiringInfo, Entity), With>, + in_player_hands_parent_query: Query<'w, 's, (&'static mut Transform, Entity), (With, Without, Without, Without)>, + player_firing_info_query: Query<'w, 's, &'static mut PlayerFiringInfo, With>, inspectable_slot_query: Query<'w, 's, &'static mut InspectScreenSlotUiMarker>, - slot_query: Query<'w, 's, (&'static mut Transform, &'static Parent, &'static Children, &'static WeaponSlot), (Without, Without)>, + firearm_query: Query<'w, 's, (Entity, &'static mut FirearmState)>, + slot_query: Query<'w, 's, (&'static mut Transform, &'static Parent, &'static WeaponSlot), (Without, Without)>, sight_placement_query: Query<'w, 's, (&'static Parent, &'static mut Transform), (Or<(With, With)>, Without)>, } @@ -22,11 +23,8 @@ pub fn inspect_firearm( keyboard_input: Res>, mut mouse_wheel_events: EventReader, ) { - for (mut player_firing_info, player_entity) in queries.player_firing_info_query.iter_mut() { - for (mut in_player_hands_parent_transform, in_player_hands_entity, in_player_hands_parent) in queries.in_player_hands_parent_query.iter_mut() { - //if !find_child_in_parent_children(&mut commands, player_entity, in_player_hands_parent.get(), &queries.children) { - // continue; - //} + for mut player_firing_info in queries.player_firing_info_query.iter_mut() { + for (mut in_player_hands_parent_transform, in_player_hands_entity) in queries.in_player_hands_parent_query.iter_mut() { if player_firing_info.is_inspecting { let inspect_hand_transform: Transform = Transform { translation: Vec3 { x: 0.0, y: -0.1, z: -0.8 }, @@ -51,28 +49,44 @@ pub fn inspect_firearm( } for mut inspectable_slot in queries.inspectable_slot_query.iter_mut() { if let Some(selected) = selected { // If user selects a slot - if inspectable_slot.slot.get_index() == selected && inspectable_slot.active { - inspectable_slot.selected = true; - - } else { - inspectable_slot.selected = false; - } + inspectable_slot.selected = inspectable_slot.slot.get_index() == selected && inspectable_slot.active; } - - - for (mut slot_transform, slot_parent, slot_children, slot) in queries.slot_query.iter_mut() { - if slot != &inspectable_slot.slot { continue; } + for (mut slot_transform, slot_parent, slot) in queries.slot_query.iter_mut() { + if slot != &inspectable_slot.slot || !inspectable_slot.selected { continue; } if !find_child_in_parent_children(&mut commands, in_player_hands_entity, slot_parent.get(), &queries.children) { continue; } - - if keyboard_input.just_pressed(KeyCode::A) || keyboard_input.just_pressed(KeyCode::Left) { - - } else if keyboard_input.just_pressed(KeyCode::D) || keyboard_input.just_pressed(KeyCode::Right) { - + match (keyboard_input.just_pressed(KeyCode::A) || keyboard_input.just_pressed(KeyCode::Left), keyboard_input.just_pressed(KeyCode::D) || keyboard_input.just_pressed(KeyCode::Right)) { + (true, false) => { // -1 to index + for (firearm_entity, mut firearm_state) in queries.firearm_query.iter_mut() { + if !find_child_in_parent_children(&mut commands, in_player_hands_entity, firearm_entity, &queries.children) { + continue; + } + firearm_state.switch_attachment_by_pos(slot, Some(Position::Previous)); + } + }, + (false, true) => { // +1 to index + for (firearm_entity, mut firearm_state) in queries.firearm_query.iter_mut() { + if !find_child_in_parent_children(&mut commands, in_player_hands_entity, firearm_entity, &queries.children) { + continue; + } + firearm_state.switch_attachment_by_pos(slot, Some(Position::Next)); + } + }, + _ => {} // On no press or both pressed, do nothing + }; + if keyboard_input.just_pressed(KeyCode::Back) { + for (firearm_entity, mut firearm_state) in queries.firearm_query.iter_mut() { + if !find_child_in_parent_children(&mut commands, in_player_hands_entity, firearm_entity, &queries.children) { + continue; + } + firearm_state.switch_attachment_by_pos(slot, None); + if slot == &WeaponSlot::SightSlot { + firearm_state.optic_pos = None; + } + } } - if inspectable_slot.slot == WeaponSlot::SightSlot && inspectable_slot.selected { - println!("sightslot"); + if inspectable_slot.slot == WeaponSlot::SightSlot { // The optic should sit in between these two transforms let mut pos_1 = None; let mut pos_2 = None; @@ -85,7 +99,6 @@ pub fn inspect_firearm( } } for mouse_wheel_event in mouse_wheel_events.read() { // TODO: Specifically for optic, scroll wheel == position change - println!("mouse"); match (pos_1, pos_2) { (None, None) => break, (None, Some(pos_2)) => { slot_transform.translation = pos_2 }, @@ -93,7 +106,12 @@ pub fn inspect_firearm( (Some(pos_1), Some(pos_2)) => { let move_sight_by_y = mouse_wheel_event.y * time.delta_seconds() * 1.0; slot_transform.translation.y = (move_sight_by_y + slot_transform.translation.y).clamp(pos_1.y.min(pos_2.y), pos_1.y.max(pos_2.y)); - println!("moved"); + for (firearm_entity, mut firearm_state) in queries.firearm_query.iter_mut() { + if !find_child_in_parent_children(&mut commands, in_player_hands_entity, firearm_entity, &queries.children) { + continue; + } + firearm_state.optic_pos = Some(slot_transform.translation); + } }, }; // TODO: Translate SightSlot transform by mouse_wheel_event.y * time.delta_time_seconds() * sens_multiplier