wrire up ammo and health in head_ui

This commit is contained in:
2025-04-01 23:47:51 +08:00
parent d9fad37e07
commit 8187eb58a9
3 changed files with 74 additions and 25 deletions

View File

@@ -153,6 +153,7 @@ fn sync(
new_state = Some(BackpackHead { new_state = Some(BackpackHead {
head: head.0, head: head.0,
health: hp.health(), health: hp.health(),
ammo: 1.,
}); });
} }
} }

View File

@@ -5,16 +5,29 @@ use bevy::prelude::*;
pub use backpack_ui::BackpackAction; pub use backpack_ui::BackpackAction;
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq, Reflect)]
pub struct BackpackHead { pub struct BackpackHead {
pub head: usize, pub head: usize,
pub health: f32, pub health: f32,
pub ammo: f32,
} }
impl BackpackHead { impl BackpackHead {
pub fn new(head: usize) -> Self {
Self {
head,
health: 1.0,
ammo: 1.0,
}
}
pub fn damage(&self) -> f32 { pub fn damage(&self) -> f32 {
1. - self.health 1. - self.health
} }
pub fn ammo_used(&self) -> f32 {
1. - self.ammo
}
} }
#[derive(Resource, Default)] #[derive(Resource, Default)]
@@ -37,6 +50,7 @@ fn setup(mut commands: Commands) {
.map(|i| BackpackHead { .map(|i| BackpackHead {
head: i, head: i,
health: 1., health: 1.,
ammo: 1.,
}) })
.collect(), .collect(),
}); });

View File

@@ -6,7 +6,7 @@ use crate::{
sounds::PlaySound, sounds::PlaySound,
}; };
use bevy::prelude::*; use bevy::prelude::*;
use bevy_ui_gradients::{AngularColorStop, BackgroundGradient, ConicGradient, Position}; use bevy_ui_gradients::{AngularColorStop, BackgroundGradient, ConicGradient, Gradient, Position};
use std::f32::consts::PI; use std::f32::consts::PI;
pub static HEAD_COUNT: usize = 18; pub static HEAD_COUNT: usize = 18;
@@ -35,9 +35,10 @@ pub struct HeadsImages {
pub heads: Vec<Handle<Image>>, pub heads: Vec<Handle<Image>>,
} }
#[derive(Resource, Default)] #[derive(Resource, Default, Reflect)]
#[reflect(Resource)]
pub struct ActiveHeads { pub struct ActiveHeads {
heads: [Option<usize>; 5], heads: [Option<BackpackHead>; 5],
current_slot: usize, current_slot: usize,
} }
@@ -46,8 +47,13 @@ pub struct HeadChanged(pub usize);
pub fn plugin(app: &mut App) { pub fn plugin(app: &mut App) {
app.register_type::<HeadDamage>(); app.register_type::<HeadDamage>();
app.register_type::<ActiveHeads>();
app.add_systems(OnEnter(GameState::Playing), setup); app.add_systems(OnEnter(GameState::Playing), setup);
app.add_systems(Update, update.run_if(in_state(GameState::Playing))); app.add_systems(
Update,
(update, update_ammo, update_health).run_if(in_state(GameState::Playing)),
);
app.add_observer(on_select_active_head); app.add_observer(on_select_active_head);
app.add_observer(on_swap_backpack); app.add_observer(on_swap_backpack);
} }
@@ -82,7 +88,13 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>, assets: Res<UIA
commands.insert_resource(HeadsImages { heads }); commands.insert_resource(HeadsImages { heads });
commands.insert_resource(ActiveHeads { commands.insert_resource(ActiveHeads {
heads: [Some(0), Some(3), Some(6), Some(8), Some(9)], heads: [
Some(BackpackHead::new(0)),
Some(BackpackHead::new(3)),
Some(BackpackHead::new(6)),
Some(BackpackHead::new(8)),
Some(BackpackHead::new(9)),
],
current_slot: 0, current_slot: 0,
}); });
} }
@@ -117,6 +129,7 @@ fn spawn_head_ui(
ImageNode::new(selector), ImageNode::new(selector),
HeadSelector(head_slot), HeadSelector(head_slot),
)); ));
parent.spawn(( parent.spawn((
Node { Node {
position_type: PositionType::Absolute, position_type: PositionType::Absolute,
@@ -125,12 +138,20 @@ fn spawn_head_ui(
ImageNode::new(bg), ImageNode::new(bg),
)); ));
parent.spawn(( parent.spawn((
Name::new("head-icon"),
Node { Node {
position_type: PositionType::Absolute, position_type: PositionType::Absolute,
// width: Val::Px(66.0),
// height: Val::Px(66.0),
..default() ..default()
}, },
BorderRadius::all(Val::Px(9999.)),
BackgroundGradient::from(ConicGradient {
stops: vec![
AngularColorStop::new(Color::linear_rgba(0., 0., 0., 0.9), 0.),
AngularColorStop::new(Color::linear_rgba(0., 0., 0., 0.9), PI * 1.5),
AngularColorStop::new(Color::linear_rgba(0., 0., 0., 0.0), PI * 1.5),
],
position: Position::CENTER,
}),
ImageNode::default(), ImageNode::default(),
Visibility::Hidden, Visibility::Hidden,
HeadImage(head_slot), HeadImage(head_slot),
@@ -140,16 +161,7 @@ fn spawn_head_ui(
position_type: PositionType::Absolute, position_type: PositionType::Absolute,
..default() ..default()
}, },
BorderRadius::all(Val::Px(9999.)),
ImageNode::new(regular), ImageNode::new(regular),
BackgroundGradient::from(ConicGradient {
stops: vec![
AngularColorStop::new(Color::linear_rgba(0., 0., 0., 0.9), 0.),
AngularColorStop::new(Color::linear_rgba(0., 0., 0., 0.9), PI * 1.5),
AngularColorStop::new(Color::linear_rgba(0., 0., 0., 0.0), PI * 1.5),
],
position: Position::CENTER,
}),
)); ));
parent parent
.spawn((Node { .spawn((Node {
@@ -187,7 +199,7 @@ fn update(
for (HeadImage(head), mut vis, mut image) in head_image.iter_mut() { for (HeadImage(head), mut vis, mut image) in head_image.iter_mut() {
if let Some(head) = res.heads[*head] { if let Some(head) = res.heads[*head] {
*vis = Visibility::Visible; *vis = Visibility::Visible;
image.image = heads_images.heads[head].clone(); image.image = heads_images.heads[head.head].clone();
} else { } else {
*vis = Visibility::Hidden; *vis = Visibility::Hidden;
} }
@@ -202,6 +214,31 @@ fn update(
} }
} }
fn update_ammo(res: Res<ActiveHeads>, mut gradients: Query<(&mut BackgroundGradient, &HeadImage)>) {
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;
gradient.stops[1].angle = Some(angle);
gradient.stops[2].angle = Some(angle);
}
}
}
}
fn update_health(res: Res<ActiveHeads>, mut query: Query<(&mut Node, &HeadDamage)>) {
if res.is_changed() {
for (mut node, HeadDamage(head)) in query.iter_mut() {
node.height =
Val::Percent(res.heads[*head].map(|head| head.damage()).unwrap_or(0.) * 100.);
}
}
}
fn on_select_active_head( fn on_select_active_head(
trigger: Trigger<SelectActiveHead>, trigger: Trigger<SelectActiveHead>,
mut commands: Commands, mut commands: Commands,
@@ -217,7 +254,7 @@ fn on_select_active_head(
} }
commands.trigger(PlaySound::Selection); commands.trigger(PlaySound::Selection);
commands.trigger(HeadChanged(res.heads[res.current_slot].unwrap())); commands.trigger(HeadChanged(res.heads[res.current_slot].unwrap().head));
} }
fn on_swap_backpack( fn on_swap_backpack(
@@ -233,16 +270,13 @@ fn on_swap_backpack(
let current_active_slot = res.current_slot; let current_active_slot = res.current_slot;
let current_active_head = res.heads[current_active_slot]; let current_active_head = res.heads[current_active_slot];
res.heads[current_active_slot] = Some(head.head); res.heads[current_active_slot] = Some(*head);
if let Some(old_active) = current_active_head { if let Some(old_active) = current_active_head {
backpack.heads[backpack_slot] = BackpackHead { backpack.heads[backpack_slot] = old_active;
head: old_active,
health: 1.,
};
} else { } else {
backpack.heads.remove(backpack_slot); backpack.heads.remove(backpack_slot);
} }
commands.trigger(HeadChanged(res.heads[res.current_slot].unwrap())); commands.trigger(HeadChanged(res.heads[res.current_slot].unwrap().head));
} }