proxisim/src/dto.rs
2025-11-03 16:36:45 +01:00

361 lines
10 KiB
Rust

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<WeaponMod>,
pub bonuses: Vec<WeaponBonusInfo>,
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<WeaponBonusInfo>,
pub experience: f32,
}
#[cfg_attr(feature = "json", derive(serde::Deserialize))]
#[derive(Default)]
pub struct Weapons {
pub primary: Option<Weapon>,
pub secondary: Option<Weapon>,
pub melee: Option<MeleeWeapon>,
pub temp: Option<Temp>,
}
#[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<Immunity>,
pub set: Option<Set>,
}
#[cfg_attr(feature = "json", derive(serde::Deserialize))]
#[derive(Default)]
pub struct ArmourPieces {
pub helmet: Option<Armour>,
pub body: Option<Armour>,
pub pants: Option<Armour>,
pub gloves: Option<Armour>,
pub boots: Option<Armour>,
}
#[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<Merits>,
pub education: Option<Education>,
pub weapons: Weapons,
pub armour: ArmourPieces,
pub faction: Option<FactionUpgrades>,
pub drug: Option<DrugCooldown>,
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<u32>,
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::<Strength>::new(self.stats.str),
StatBundle::<Defence>::new(self.stats.def),
StatBundle::<Speed>::new(self.stats.spd),
StatBundle::<Dexterity>::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);
}
}