use bevy_ecs::prelude::*; pub use crate::entity_registry::EntityInfo; pub use crate::passives::{DrugCooldown, Education, FactionUpgrades, Merits}; pub use crate::player::PlayerStrategy; use crate::weapon::Japanese; pub use crate::weapon::{bonus::WeaponBonus, temp::Temp, WeaponCategory, WeaponMod, WeaponSlot}; use crate::{ armour::EquippedArmour, passives::PassiveBundle, player::PlayerBundle, weapon::{AmmoWeaponBundle, DamagingWeaponBundle, WeaponBundle, WeaponVerb}, }; use crate::{ armour::{ArmourBundle, Immunities, Immunity, Set}, weapon::bonus::WeaponBonusBundle, }; use crate::{ hierarchy::HierarchyBuilder, player::stats::{Defence, Dexterity, Speed, StatBundle, Strength}, }; #[cfg_attr(feature = "json", derive(serde::Deserialize))] pub struct Stats { pub str: f32, pub def: f32, pub spd: f32, pub dex: f32, } #[cfg_attr(feature = "json", derive(serde::Deserialize))] pub struct WeaponAmmo { pub clips: u16, pub clip_size: u16, pub rate_of_fire: [u16; 2], } #[cfg_attr(feature = "json", derive(serde::Deserialize))] pub struct WeaponBonusInfo { pub bonus: WeaponBonus, pub value: f32, } #[cfg_attr(feature = "json", derive(serde::Deserialize))] pub struct Weapon { pub name: String, pub cat: WeaponCategory, pub dmg: f32, pub acc: f32, pub ammo: WeaponAmmo, pub mods: Vec, pub bonuses: Vec, pub experience: f32, } #[cfg_attr(feature = "json", derive(serde::Deserialize))] pub struct MeleeWeapon { pub name: String, pub cat: WeaponCategory, pub japanese: bool, pub dmg: f32, pub acc: f32, pub bonuses: Vec, pub experience: f32, } #[cfg_attr(feature = "json", derive(serde::Deserialize))] #[derive(Default)] pub struct Weapons { pub primary: Option, pub secondary: Option, pub melee: Option, pub temp: Option, } #[cfg_attr(feature = "json", derive(serde::Deserialize))] #[derive(Default)] pub struct Armour { pub armour_value: f32, pub name: String, pub coverage: [f32; 10], pub immunities: Vec, pub set: Option, } #[cfg_attr(feature = "json", derive(serde::Deserialize))] #[derive(Default)] pub struct ArmourPieces { pub helmet: Option, pub body: Option, pub pants: Option, pub gloves: Option, pub boots: Option, } #[cfg_attr(feature = "json", derive(serde::Deserialize))] pub struct Player { pub name: String, pub id: usize, pub level: u16, pub stats: Stats, pub merits: Option, pub education: Option, pub weapons: Weapons, pub armour: ArmourPieces, pub faction: Option, pub drug: Option, pub strategy: PlayerStrategy, } #[cfg_attr(feature = "json", derive(serde::Serialize))] #[derive(Debug)] pub struct Counter { pub value: u64, pub entity: EntityInfo, pub label: &'static str, } #[cfg_attr(feature = "json", derive(serde::Serialize))] #[derive(Debug)] pub struct Histogram { pub values: Vec, pub entity: EntityInfo, pub label: &'static str, } impl Player { pub(crate) fn spawn(self, world: &mut World) -> EntityWorldMut<'_> { let primary = self.weapons.primary.map(|w| { let primary = world .spawn(( WeaponBundle::new( w.name, self.id * 100 + 3, WeaponVerb::Fired, WeaponSlot::Primary, ), DamagingWeaponBundle::new(w.dmg, w.acc, w.mods, w.experience, w.cat), AmmoWeaponBundle::new(w.ammo.clips, w.ammo.clip_size, w.ammo.rate_of_fire), )) .id(); if !w.bonuses.is_empty() { for bonus in w.bonuses { world .spawn(WeaponBonusBundle::new(bonus.bonus, bonus.value)) .set_parent(primary); } } primary }); let secondary = self.weapons.secondary.map(|w| { let secondary = world .spawn(( WeaponBundle::new( w.name, self.id * 100 + 4, WeaponVerb::Fired, WeaponSlot::Secondary, ), DamagingWeaponBundle::new(w.dmg, w.acc, w.mods, w.experience, w.cat), AmmoWeaponBundle::new(w.ammo.clips, w.ammo.clip_size, w.ammo.rate_of_fire), )) .id(); if !w.bonuses.is_empty() { for bonus in w.bonuses { world .spawn(WeaponBonusBundle::new(bonus.bonus, bonus.value)) .set_parent(secondary); } } secondary }); let melee = self.weapons.melee.map(|w| { let mut melee = world.spawn(( WeaponBundle::new( w.name, self.id * 100 + 5, WeaponVerb::Hit, WeaponSlot::Melee, ), DamagingWeaponBundle::new(w.dmg, w.acc, Vec::default(), w.experience, w.cat), )); if w.japanese { melee.insert(Japanese); } let melee = melee.id(); if !w.bonuses.is_empty() { for bonus in w.bonuses { world .spawn(WeaponBonusBundle::new(bonus.bonus, bonus.value)) .set_parent(melee); } } melee }); let temporary = self .weapons .temp .map(|t| t.spawn(world, self.id * 100 + 2).id()); let fists = world .spawn(( WeaponBundle::fists(self.id * 100), DamagingWeaponBundle::fists(), )) .id(); let kick = world .spawn(( WeaponBundle::kick(self.id * 100 + 1), DamagingWeaponBundle::kick(), )) .id(); let weapons = crate::player::Weapons { primary, secondary, melee, temporary, fists: Some(fists), kick: Some(kick), }; let helmet = self.armour.helmet.map(|h| { let mut helmet = world.spawn(ArmourBundle::new(h.name, h.coverage, h.armour_value)); if let Some(set) = h.set { helmet.insert(set); } if !h.immunities.is_empty() { helmet.insert(Immunities(h.immunities)); } helmet.id() }); let body = self.armour.body.map(|b| { let mut body = world.spawn(ArmourBundle::new(b.name, b.coverage, b.armour_value)); if let Some(set) = b.set { body.insert(set); } body.id() }); let pants = self.armour.pants.map(|p| { let mut pants = world.spawn(ArmourBundle::new(p.name, p.coverage, p.armour_value)); if let Some(set) = p.set { pants.insert(set); } pants.id() }); let gloves = self.armour.gloves.map(|g| { let mut gloves = world.spawn(ArmourBundle::new(g.name, g.coverage, g.armour_value)); if let Some(set) = g.set { gloves.insert(set); } gloves.id() }); let boots = self.armour.boots.map(|b| { let mut boots = world.spawn(ArmourBundle::new(b.name, b.coverage, b.armour_value)); if let Some(set) = b.set { boots.insert(set); } boots.id() }); let armour = EquippedArmour { head: helmet, body, legs: pants, hands: gloves, feet: boots, }; let mut player = world.spawn(( PlayerBundle::new(self.name, self.id, self.level, self.strategy), StatBundle::::new(self.stats.str), StatBundle::::new(self.stats.def), StatBundle::::new(self.stats.spd), StatBundle::::new(self.stats.dex), weapons, armour, PassiveBundle { merits: self.merits.unwrap_or_default(), education: self.education.unwrap_or_default(), faction: self.faction.unwrap_or_default(), }, )); if let Some(drug) = self.drug { player.insert(drug); } player } } #[cfg(test)] mod tests { use super::*; #[test] fn test_spawn_player() { let mut world = World::new(); let player = Player { name: "Test".to_owned(), id: 0, level: 10, stats: Stats { str: 10.0, def: 10.0, spd: 10.0, dex: 10.0, }, merits: Default::default(), education: Default::default(), faction: Default::default(), drug: None, strategy: PlayerStrategy::AlwaysFists, weapons: Weapons { primary: Some(Weapon { name: "Test".to_owned(), cat: WeaponCategory::Rifle, dmg: 50.0, acc: 50.0, ammo: WeaponAmmo { clips: 3, clip_size: 25, rate_of_fire: [3, 5], }, mods: Vec::default(), bonuses: Vec::default(), experience: 100.0, }), ..Default::default() }, armour: ArmourPieces { helmet: Some(Armour { name: "Test".to_owned(), armour_value: 50.0, coverage: [0.0, 0.0, 0.0, 0.0, 100.0, 0.0, 0.0, 0.0, 0.0, 0.0], immunities: Vec::default(), set: None, }), ..Default::default() }, }; player.spawn(&mut world); } }