heads module
This commit is contained in:
160
src/heads/mod.rs
Normal file
160
src/heads/mod.rs
Normal file
@@ -0,0 +1,160 @@
|
||||
mod heads_ui;
|
||||
|
||||
use crate::{
|
||||
GameState,
|
||||
abilities::HeadAbility,
|
||||
backpack::{BackbackSwapEvent, Backpack},
|
||||
player::head_id_to_str,
|
||||
sounds::PlaySound,
|
||||
};
|
||||
use bevy::prelude::*;
|
||||
|
||||
pub static HEAD_COUNT: usize = 18;
|
||||
pub static HEAD_SLOTS: usize = 5;
|
||||
|
||||
#[derive(Resource, Default)]
|
||||
pub struct HeadsImages {
|
||||
pub heads: Vec<Handle<Image>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Reflect)]
|
||||
pub struct HeadState {
|
||||
pub head: usize,
|
||||
pub ability: HeadAbility,
|
||||
pub health: u32,
|
||||
pub health_max: u32,
|
||||
pub ammo: u32,
|
||||
pub ammo_max: u32,
|
||||
}
|
||||
|
||||
impl HeadState {
|
||||
pub fn new(head: usize, ammo: u32) -> Self {
|
||||
Self {
|
||||
head,
|
||||
health: 100,
|
||||
health_max: 100,
|
||||
ammo,
|
||||
ammo_max: ammo,
|
||||
ability: HeadAbility::None,
|
||||
}
|
||||
}
|
||||
|
||||
fn with_ability(mut self, ability: HeadAbility) -> Self {
|
||||
self.ability = ability;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn has_ammo(&self) -> bool {
|
||||
self.ammo > 0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource, Default, Reflect)]
|
||||
#[reflect(Resource)]
|
||||
pub struct ActiveHeads {
|
||||
heads: [Option<HeadState>; 5],
|
||||
current_slot: usize,
|
||||
}
|
||||
|
||||
impl ActiveHeads {
|
||||
pub fn current(&self) -> Option<HeadState> {
|
||||
self.heads[self.current_slot]
|
||||
}
|
||||
|
||||
pub fn use_ammo(&mut self) {
|
||||
let Some(head) = &mut self.heads[self.current_slot] else {
|
||||
error!("cannot use ammo of empty head");
|
||||
return;
|
||||
};
|
||||
|
||||
head.ammo = head.ammo.saturating_sub(1);
|
||||
}
|
||||
|
||||
pub fn slot(&self) -> usize {
|
||||
self.current_slot
|
||||
}
|
||||
|
||||
pub fn head(&self, slot: usize) -> Option<HeadState> {
|
||||
self.heads[slot]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Event, Reflect)]
|
||||
pub enum SelectActiveHead {
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
#[derive(Event)]
|
||||
pub struct HeadChanged(pub usize);
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.add_plugins(heads_ui::plugin);
|
||||
|
||||
app.insert_resource(ActiveHeads {
|
||||
heads: [
|
||||
Some(HeadState::new(0, 10).with_ability(HeadAbility::Thrown)),
|
||||
Some(HeadState::new(3, 10).with_ability(HeadAbility::Gun)),
|
||||
Some(HeadState::new(6, 10).with_ability(HeadAbility::Arrow)),
|
||||
Some(HeadState::new(8, 10).with_ability(HeadAbility::Thrown)),
|
||||
Some(HeadState::new(9, 10).with_ability(HeadAbility::Gun)),
|
||||
],
|
||||
current_slot: 0,
|
||||
});
|
||||
|
||||
app.add_systems(OnEnter(GameState::Playing), setup);
|
||||
|
||||
app.add_observer(on_select_active_head);
|
||||
app.add_observer(on_swap_backpack);
|
||||
}
|
||||
|
||||
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
// TODO: load via asset loader
|
||||
let heads = (0usize..HEAD_COUNT)
|
||||
.map(|i| asset_server.load(format!("ui/heads/{}.png", head_id_to_str(i))))
|
||||
.collect();
|
||||
|
||||
commands.insert_resource(HeadsImages { heads });
|
||||
}
|
||||
|
||||
fn on_select_active_head(
|
||||
trigger: Trigger<SelectActiveHead>,
|
||||
mut commands: Commands,
|
||||
mut res: ResMut<ActiveHeads>,
|
||||
) {
|
||||
match trigger.event() {
|
||||
SelectActiveHead::Right => {
|
||||
res.current_slot = (res.current_slot + 1) % HEAD_SLOTS;
|
||||
}
|
||||
SelectActiveHead::Left => {
|
||||
res.current_slot = (res.current_slot + (HEAD_SLOTS - 1)) % HEAD_SLOTS;
|
||||
}
|
||||
}
|
||||
|
||||
commands.trigger(PlaySound::Selection);
|
||||
commands.trigger(HeadChanged(res.heads[res.current_slot].unwrap().head));
|
||||
}
|
||||
|
||||
fn on_swap_backpack(
|
||||
trigger: Trigger<BackbackSwapEvent>,
|
||||
mut commands: Commands,
|
||||
mut res: ResMut<ActiveHeads>,
|
||||
mut backpack: ResMut<Backpack>,
|
||||
) {
|
||||
let backpack_slot = trigger.event().0;
|
||||
|
||||
let head = backpack.heads.get(backpack_slot).unwrap();
|
||||
|
||||
let current_active_slot = res.current_slot;
|
||||
|
||||
let current_active_head = res.heads[current_active_slot];
|
||||
res.heads[current_active_slot] = Some(*head);
|
||||
|
||||
if let Some(old_active) = current_active_head {
|
||||
backpack.heads[backpack_slot] = old_active;
|
||||
} else {
|
||||
backpack.heads.remove(backpack_slot);
|
||||
}
|
||||
|
||||
commands.trigger(HeadChanged(res.heads[res.current_slot].unwrap().head));
|
||||
}
|
||||
Reference in New Issue
Block a user