relaod mechanic
This commit is contained in:
@@ -82,6 +82,7 @@ fn on_trigger_state(
|
||||
player_rot: Query<&Transform, With<PlayerRig>>,
|
||||
player_transform: Query<&Transform, With<Player>>,
|
||||
mut active_heads: ResMut<ActiveHeads>,
|
||||
time: Res<Time>,
|
||||
) {
|
||||
if matches!(trigger.event(), TriggerState::Active) {
|
||||
let Some(state) = active_heads.current() else {
|
||||
@@ -107,7 +108,7 @@ fn on_trigger_state(
|
||||
pos: transform.translation,
|
||||
};
|
||||
|
||||
active_heads.use_ammo();
|
||||
active_heads.use_ammo(time.elapsed_secs());
|
||||
|
||||
match state.ability {
|
||||
HeadAbility::Thrown => commands.trigger(TriggerThrow(trigger_state)),
|
||||
|
||||
@@ -154,6 +154,7 @@ fn sync(
|
||||
head: head.0,
|
||||
health: hp.health(),
|
||||
ammo: 1.,
|
||||
reloading: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,6 +262,7 @@ fn swap_head_inputs(
|
||||
backpack: Res<Backpack>,
|
||||
mut commands: Commands,
|
||||
mut state: ResMut<BackpackUiState>,
|
||||
time: Res<Time>,
|
||||
) {
|
||||
if state.count == 0 {
|
||||
return;
|
||||
@@ -292,17 +293,17 @@ fn swap_head_inputs(
|
||||
|
||||
if changed {
|
||||
commands.trigger(PlaySound::Selection);
|
||||
sync(&backpack, &mut state);
|
||||
sync(&backpack, &mut state, time.elapsed_secs());
|
||||
}
|
||||
}
|
||||
|
||||
fn sync_on_change(backpack: Res<Backpack>, mut state: ResMut<BackpackUiState>) {
|
||||
if backpack.is_changed() {
|
||||
sync(&backpack, &mut state);
|
||||
fn sync_on_change(backpack: Res<Backpack>, mut state: ResMut<BackpackUiState>, time: Res<Time>) {
|
||||
if backpack.is_changed() || backpack.reloading() {
|
||||
sync(&backpack, &mut state, time.elapsed_secs());
|
||||
}
|
||||
}
|
||||
|
||||
fn sync(backpack: &Res<Backpack>, state: &mut ResMut<BackpackUiState>) {
|
||||
fn sync(backpack: &Res<Backpack>, state: &mut ResMut<BackpackUiState>, time: f32) {
|
||||
state.count = backpack.heads.len();
|
||||
|
||||
state.scroll = state.scroll.min(state.count.saturating_sub(HEAD_SLOTS));
|
||||
@@ -316,7 +317,7 @@ fn sync(backpack: &Res<Backpack>, state: &mut ResMut<BackpackUiState>) {
|
||||
|
||||
for i in 0..HEAD_SLOTS {
|
||||
if let Some(head) = backpack.heads.get(i + state.scroll) {
|
||||
state.heads[i] = Some(UiHeadState::from(*head));
|
||||
state.heads[i] = Some(UiHeadState::new(*head, time));
|
||||
} else {
|
||||
state.heads[i] = None;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,18 @@ pub struct Backpack {
|
||||
pub heads: Vec<HeadState>,
|
||||
}
|
||||
|
||||
impl Backpack {
|
||||
pub fn reloading(&self) -> bool {
|
||||
for head in &self.heads {
|
||||
if !head.has_ammo() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Event)]
|
||||
pub struct BackbackSwapEvent(pub usize);
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ pub struct UiHeadState {
|
||||
pub head: usize,
|
||||
pub health: f32,
|
||||
pub ammo: f32,
|
||||
pub reloading: Option<f32>,
|
||||
}
|
||||
|
||||
impl UiHeadState {
|
||||
@@ -16,14 +17,23 @@ impl UiHeadState {
|
||||
pub fn ammo_used(&self) -> f32 {
|
||||
1. - self.ammo
|
||||
}
|
||||
|
||||
pub fn reloading(&self) -> Option<f32> {
|
||||
self.reloading
|
||||
}
|
||||
|
||||
impl From<HeadState> for UiHeadState {
|
||||
fn from(value: HeadState) -> Self {
|
||||
pub(crate) fn new(value: HeadState, time: f32) -> Self {
|
||||
let reloading = if value.has_ammo() {
|
||||
None
|
||||
} else {
|
||||
Some((time - value.last_use) / value.reload_duration)
|
||||
};
|
||||
|
||||
Self {
|
||||
head: value.head,
|
||||
ammo: value.ammo as f32 / value.ammo_max as f32,
|
||||
health: value.health as f32 / value.health_max as f32,
|
||||
reloading,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,11 +182,18 @@ fn update_ammo(
|
||||
if res.is_changed() {
|
||||
for (mut gradient, HeadImage(head)) in gradients.iter_mut() {
|
||||
if let Some(head) = res.heads[*head] {
|
||||
let ammo_used = head.ammo_used();
|
||||
let Gradient::Conic(gradient) = &mut gradient.0[0] else {
|
||||
continue;
|
||||
};
|
||||
let angle = PI * 2. * ammo_used;
|
||||
|
||||
let progress = if let Some(reloading) = head.reloading() {
|
||||
1. - reloading
|
||||
} else {
|
||||
head.ammo_used()
|
||||
};
|
||||
|
||||
let angle = progress * PI * 2.;
|
||||
|
||||
gradient.stops[1].angle = Some(angle);
|
||||
gradient.stops[2].angle = Some(angle);
|
||||
}
|
||||
@@ -203,11 +210,13 @@ fn update_health(res: Res<UiActiveHeads>, mut query: Query<(&mut Node, &HeadDama
|
||||
}
|
||||
}
|
||||
|
||||
fn sync(active: Res<ActiveHeads>, mut state: ResMut<UiActiveHeads>) {
|
||||
if active.is_changed() {
|
||||
fn sync(active: Res<ActiveHeads>, mut state: ResMut<UiActiveHeads>, time: Res<Time>) {
|
||||
if active.is_changed() || active.reloading() {
|
||||
state.current_slot = active.slot();
|
||||
for i in 0..HEAD_SLOTS {
|
||||
state.heads[i] = active.head(i).map(UiHeadState::from);
|
||||
state.heads[i] = active
|
||||
.head(i)
|
||||
.map(|state| UiHeadState::new(state, time.elapsed_secs()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ pub struct HeadState {
|
||||
pub health_max: u32,
|
||||
pub ammo: u32,
|
||||
pub ammo_max: u32,
|
||||
pub reload_duration: f32,
|
||||
pub last_use: f32,
|
||||
}
|
||||
|
||||
impl HeadState {
|
||||
@@ -36,6 +38,8 @@ impl HeadState {
|
||||
ammo,
|
||||
ammo_max: ammo,
|
||||
ability: HeadAbility::None,
|
||||
reload_duration: 5.,
|
||||
last_use: 0.,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,12 +65,13 @@ impl ActiveHeads {
|
||||
self.heads[self.current_slot]
|
||||
}
|
||||
|
||||
pub fn use_ammo(&mut self) {
|
||||
pub fn use_ammo(&mut self, time: f32) {
|
||||
let Some(head) = &mut self.heads[self.current_slot] else {
|
||||
error!("cannot use ammo of empty head");
|
||||
return;
|
||||
};
|
||||
|
||||
head.last_use = time;
|
||||
head.ammo = head.ammo.saturating_sub(1);
|
||||
}
|
||||
|
||||
@@ -77,6 +82,16 @@ impl ActiveHeads {
|
||||
pub fn head(&self, slot: usize) -> Option<HeadState> {
|
||||
self.heads[slot]
|
||||
}
|
||||
|
||||
pub fn reloading(&self) -> bool {
|
||||
for head in self.heads {
|
||||
if head.map(|head| head.ammo == 0).unwrap_or(false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Event, Reflect)]
|
||||
@@ -103,6 +118,7 @@ pub fn plugin(app: &mut App) {
|
||||
});
|
||||
|
||||
app.add_systems(OnEnter(GameState::Playing), setup);
|
||||
app.add_systems(Update, reload.run_if(in_state(GameState::Playing)));
|
||||
|
||||
app.add_observer(on_select_active_head);
|
||||
app.add_observer(on_swap_backpack);
|
||||
@@ -117,6 +133,22 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
commands.insert_resource(HeadsImages { heads });
|
||||
}
|
||||
|
||||
fn reload(mut active: ResMut<ActiveHeads>, time: Res<Time>) {
|
||||
if !active.reloading() {
|
||||
return;
|
||||
}
|
||||
|
||||
for head in active.heads.iter_mut() {
|
||||
let Some(head) = head else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if !head.has_ammo() && (head.last_use + head.reload_duration <= time.elapsed_secs()) {
|
||||
head.ammo = head.ammo_max;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn on_select_active_head(
|
||||
trigger: Trigger<SelectActiveHead>,
|
||||
mut commands: Commands,
|
||||
|
||||
Reference in New Issue
Block a user