From 35413b563cba5beb04e8ab46b666104d85b827d9 Mon Sep 17 00:00:00 2001 From: TotallyNot <44345987+TotallyNot@users.noreply.github.com> Date: Mon, 3 Nov 2025 16:36:45 +0100 Subject: [PATCH] updates? --- Cargo.toml | 23 +- models/Cargo.toml | 15 + models/src/bundle/armour.rs | 200 ++ models/src/bundle/bonus.rs | 199 ++ models/src/bundle/mod.rs | 14 + models/src/bundle/passive.rs | 207 ++ models/src/bundle/player.rs | 184 ++ models/src/bundle/stat.rs | 408 +++ models/src/bundle/weapon.rs | 230 ++ models/src/dto/armour.rs | 2122 +++++++++++++ models/src/dto/metrics.rs | 32 + models/src/dto/mod.rs | 14 + models/src/dto/player.rs | 107 + models/src/dto/weapon.rs | 5609 ++++++++++++++++++++++++++++++++++ models/src/hierarchy.rs | 178 ++ models/src/lib.rs | 3 + src/armour/mod.rs | 241 +- src/attacker.json | 177 ++ src/defender.json | 97 + src/dto.rs | 2 +- src/effect.rs | 29 +- src/entity_registry.rs | 28 +- src/hierarchy.rs | 12 +- src/lib.rs | 151 +- src/log.rs | 106 +- src/metrics.rs | 31 +- src/passives.rs | 215 +- src/player/mod.rs | 376 +-- src/player/stats.rs | 414 +-- src/player/status_effect.rs | 41 +- src/weapon/bonus.rs | 186 +- src/weapon/mod.rs | 231 +- src/weapon/temp.rs | 247 +- 33 files changed, 10238 insertions(+), 1891 deletions(-) create mode 100644 models/Cargo.toml create mode 100644 models/src/bundle/armour.rs create mode 100644 models/src/bundle/bonus.rs create mode 100644 models/src/bundle/mod.rs create mode 100644 models/src/bundle/passive.rs create mode 100644 models/src/bundle/player.rs create mode 100644 models/src/bundle/stat.rs create mode 100644 models/src/bundle/weapon.rs create mode 100644 models/src/dto/armour.rs create mode 100644 models/src/dto/metrics.rs create mode 100644 models/src/dto/mod.rs create mode 100644 models/src/dto/player.rs create mode 100644 models/src/dto/weapon.rs create mode 100644 models/src/hierarchy.rs create mode 100644 models/src/lib.rs create mode 100644 src/attacker.json create mode 100644 src/defender.json diff --git a/Cargo.toml b/Cargo.toml index 4c2297a..861b235 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,23 +1,32 @@ [workspace] resolver = "2" -members = [".", "macros"] +members = [".", "macros", "models"] [package] name = "proxisim" version = "0.1.0" -edition = "2021" +edition = "2024" [features] default = ["json"] +debug = ["bevy_ecs/bevy_debug_stepping"] json = ["dep:serde", "dep:serde_json"] [dependencies] -bevy_ecs = "0.12.1" -rand = { version = "0.8.5", default-features = false, features = ["std", "alloc", "small_rng"] } -rand_distr = "0.4.3" -strum = { version = "0.25.0", features = ["derive"] } -serde = { version = "1", features = [ "derive" ], optional = true } +bevy_ecs = { version = "0.17.2", features = [] } +rand = { version = "0.9.2", default-features = false, features = [ + "std", + "alloc", + "small_rng", + "os_rng", +] } +rand_distr = "0.5.1" +strum = { version = "0.27.2", features = ["derive"] } +serde = { version = "1", features = ["derive"], optional = true } serde_json = { version = "1", optional = true } [dependencies.macros] path = "macros" + +[dependencies.proxisim_models] +path = "models" diff --git a/models/Cargo.toml b/models/Cargo.toml new file mode 100644 index 0000000..830140a --- /dev/null +++ b/models/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "proxisim_models" +version = "0.1.0" +edition = "2024" + +[features] +json = ["dep:serde_json", "dep:serde"] +default = ["json"] + +[dependencies] +serde = { version = "1", features = ["derive"], optional = true } +serde_json = { version = "1", optional = true } +bevy_ecs = "0.17.2" +rand = { version = "0.9.2", default-features = false } +strum = { version = "0.27.2", features = ["derive"] } diff --git a/models/src/bundle/armour.rs b/models/src/bundle/armour.rs new file mode 100644 index 0000000..7d983c6 --- /dev/null +++ b/models/src/bundle/armour.rs @@ -0,0 +1,200 @@ +use bevy_ecs::{component::Component, entity::Entity}; +use rand::distr::Distribution; +use strum::Display; + +use crate::{bundle::player::BodyPart, dto::armour::CoverageDto}; + +#[derive(Clone, Copy, Debug, Display, Component)] +#[cfg_attr(feature = "json", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "json", serde(rename_all = "snake_case"))] +pub enum Immunity { + Radiation, + NerveGas, + TearGas, + PepperSpray, + FlashGrenade, + ConcussionGrenade, + Insanity, +} + +#[derive(Component, Default)] +pub struct Immunities(pub Vec); + +#[derive(Component, Default)] +pub struct Armour; + +#[derive(Component, Default)] +pub struct ArmourCoverage(pub ArmourVec); + +impl From for ArmourCoverage { + fn from(value: CoverageDto) -> Self { + let mut result = Self::default(); + + result.0[ArmourBodyPartSlot::Heart] = value.heart; + result.0[ArmourBodyPartSlot::Stomach] = value.stomach; + result.0[ArmourBodyPartSlot::Chest] = value.chest; + result.0[ArmourBodyPartSlot::Arms] = value.arm; + result.0[ArmourBodyPartSlot::Groin] = value.groin; + result.0[ArmourBodyPartSlot::Legs] = value.leg; + result.0[ArmourBodyPartSlot::Throat] = value.throat; + result.0[ArmourBodyPartSlot::Hands] = value.hand; + result.0[ArmourBodyPartSlot::Feet] = value.foot; + result.0[ArmourBodyPartSlot::Head] = value.head; + + result + } +} + +#[derive(Component, Default)] +pub struct ArmourValue(pub f32); + +enum ArmourIterState { + Head, + Body, + Legs, + Feet, + Hands, +} + +#[derive(Debug, Clone, Component)] +pub struct PlayerArmour { + pub head: Option, + pub torso: Option, + pub legs: Option, + pub feet: Option, + pub hands: Option, +} + +#[derive(Debug)] +pub struct BodyPartCoverage { + pub armour: Entity, + pub coverage: f32, + pub armour_value: f32, +} + +#[derive(Component, Default, Debug)] +pub struct ArmourBodyPart { + pub armour_pieces: Vec, +} + +#[derive(Component, Debug)] +pub struct ArmourBodyParts(pub ArmourVec); + +pub struct ArmourIter<'a> { + state: Option, + equipped_armour: &'a PlayerArmour, +} + +impl<'a> Iterator for ArmourIter<'a> { + type Item = Entity; + + fn next(&mut self) -> Option { + loop { + let (next, piece) = match self.state { + None => (ArmourIterState::Head, self.equipped_armour.head), + Some(ArmourIterState::Head) => (ArmourIterState::Body, self.equipped_armour.torso), + Some(ArmourIterState::Body) => (ArmourIterState::Legs, self.equipped_armour.legs), + Some(ArmourIterState::Legs) => (ArmourIterState::Feet, self.equipped_armour.feet), + Some(ArmourIterState::Feet) => (ArmourIterState::Hands, self.equipped_armour.hands), + Some(ArmourIterState::Hands) => return None, + }; + + self.state = Some(next); + if piece.is_some() { + return piece; + } + } + } +} + +impl<'a> IntoIterator for &'a PlayerArmour { + type Item = Entity; + + type IntoIter = ArmourIter<'a>; + + fn into_iter(self) -> Self::IntoIter { + ArmourIter { + state: None, + equipped_armour: self, + } + } +} + +#[repr(usize)] +#[derive(Clone, Copy, strum::EnumIter)] +pub enum ArmourBodyPartSlot { + Arms, + Stomach, + Heart, + Chest, + Throat, + Hands, + Groin, + Legs, + Head, + Feet, +} + +impl From for ArmourBodyPartSlot { + fn from(value: BodyPart) -> Self { + match value { + BodyPart::LeftArm | BodyPart::RightArm => Self::Arms, + BodyPart::Stomach => Self::Stomach, + BodyPart::Heart => Self::Heart, + BodyPart::Chest => Self::Chest, + BodyPart::Throat => Self::Throat, + BodyPart::LeftHand | BodyPart::RightHand => Self::Hands, + BodyPart::Groin => Self::Groin, + BodyPart::LeftLeg | BodyPart::RightLeg => Self::Legs, + BodyPart::Head => Self::Head, + BodyPart::LeftFoot | BodyPart::RightFoot => Self::Feet, + } + } +} + +#[derive(Debug, Clone, Copy, Default)] +pub struct ArmourVec(pub [T; 10]); + +impl std::ops::Index for ArmourVec { + type Output = T; + + fn index(&self, index: ArmourBodyPartSlot) -> &Self::Output { + &self.0[index as usize] + } +} + +impl std::ops::IndexMut for ArmourVec { + fn index_mut(&mut self, index: ArmourBodyPartSlot) -> &mut Self::Output { + &mut self.0[index as usize] + } +} + +impl IntoIterator for ArmourVec { + type Item = T; + + type IntoIter = std::array::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl<'a> Distribution> for &'a ArmourBodyPart { + fn sample(&self, rng: &mut R) -> Option<&'a BodyPartCoverage> { + let mut current = None; + for piece in &self.armour_pieces { + // NOTE: This is not strictly speaking correct, but the edge cases where this applies + // should be very rare, and it should be a decent enough heuristic to barely make a + // difference from the actual pixel comparisons that torn seems to be using for this. + if current + .map(|c: &BodyPartCoverage| c.armour_value) + .unwrap_or_default() + < piece.armour_value + && rng.random_bool(piece.coverage as f64) + { + current = Some(piece); + } + } + current + } +} diff --git a/models/src/bundle/bonus.rs b/models/src/bundle/bonus.rs new file mode 100644 index 0000000..c641158 --- /dev/null +++ b/models/src/bundle/bonus.rs @@ -0,0 +1,199 @@ +use bevy_ecs::{bundle::Bundle, component::Component}; +use strum::Display; + +use crate::bundle::player::BodyPart; + +#[derive(Component, Debug, Clone, Copy)] +#[cfg_attr(feature = "json", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "json", serde(rename_all = "snake_case"))] +pub enum WeaponBonusType { + // Weapon passives + Berserk, + Conserve, + Expose, + Grace, + Powerful, + Specialist, + + // Turn triggered passives + Empower, + Quicken, + + // First turn effects + Assassinate, + + // Additive status effects triggered by damaging hits + Cripple, + Demoralise, + Freeze, + Motivate, + Slow, + Toxin, + Weaken, + Wither, + + // DOT status effects + Bleed, + Burning, + Lacerate, + Poison, + SevereBurning, + + // Other status effects + Eviscerate, + Paralyse, + Schock, + Stun, + + // Multi attack bonuses + Blindfire, + Fury, + DoubleTap, + Rage, + + // Body part multipliers + Achilles, + Crusher, + Cupid, + Deadeye, + Roshambo, + Throttle, + + // Attack nullification types + Homerun, + Parry, +} + +#[derive(Component)] +pub struct BonusValue(pub f32); + +#[derive(Bundle)] +pub struct WeaponBonusBundle { + pub bonus: WeaponBonusType, + pub value: BonusValue, +} + +impl WeaponBonusBundle { + pub fn new(bonus: WeaponBonusType, value: f32) -> Self { + Self { + bonus, + value: BonusValue(value), + } + } +} + +#[derive(Clone, Copy)] +pub enum TurnTriggeredBonus { + Empower, + Quicken, +} + +#[derive(Clone, Copy)] +pub enum FirstTurnBonus { + Assassinate, +} + +#[derive(Clone, Copy)] +pub enum MultiTurnBonus { + Blindfire, + Fury, + DoubleTap, + Rage, +} + +impl MultiTurnBonus { + #[inline(always)] + pub fn counter_label(self) -> &'static str { + match self { + Self::Blindfire => "proc_blindfire", + Self::Fury => "proc_fury", + Self::DoubleTap => "proc_double_tap", + Self::Rage => "proc_rage", + } + } +} + +#[derive(Clone, Copy)] +pub enum OpponentStatusEffect { + Cripple, + // TODO: implement for group fights + Demoralise, + Freeze, + Slow, + Toxin, + Weaken, + Wither, +} + +#[derive(Clone, Copy)] +pub enum SelfStatusEffect { + Motivate, +} + +#[derive(Clone, Copy)] +pub enum BonusPartDamageBonus { + Achilles, + Crusher, + Cupid, + Deadeye, + Roshambo, + Throttle, +} + +impl BonusPartDamageBonus { + pub fn dmg_bonus(self, part: BodyPart, value: f32) -> Option { + match self { + Self::Achilles => match part { + BodyPart::LeftFoot | BodyPart::RightFoot => Some(value), + _ => None, + }, + Self::Crusher => { + if part == BodyPart::Head { + Some(value) + } else { + None + } + } + Self::Cupid => { + if part == BodyPart::Heart { + Some(value) + } else { + None + } + } + Self::Deadeye => match part { + BodyPart::Head | BodyPart::Heart | BodyPart::Throat => Some(value), + _ => None, + }, + Self::Roshambo => { + if part == BodyPart::Groin { + Some(value) + } else { + None + } + } + Self::Throttle => { + if part == BodyPart::Throat { + Some(value) + } else { + None + } + } + } + } +} + +#[derive(Clone, Copy, Debug, Display)] +#[cfg_attr(feature = "json", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "json", serde(rename_all = "snake_case"))] +pub enum ArmourBonusType { + Impregnable, + Impenetrable, + Insurmountable, + Invulnerable, + Imperviable, + Immmutable, + Irrepressible, + Kinetokinesis, + Impassable, +} diff --git a/models/src/bundle/mod.rs b/models/src/bundle/mod.rs new file mode 100644 index 0000000..c88503a --- /dev/null +++ b/models/src/bundle/mod.rs @@ -0,0 +1,14 @@ +use bevy_ecs::component::Component; + +pub mod armour; +pub mod bonus; +pub mod passive; +pub mod player; +pub mod stat; +pub mod weapon; + +#[derive(Component, Debug, Default)] +pub struct Name(pub String); + +#[derive(Component, Debug, Default, Clone, Copy)] +pub struct Id(pub usize); diff --git a/models/src/bundle/passive.rs b/models/src/bundle/passive.rs new file mode 100644 index 0000000..aca3091 --- /dev/null +++ b/models/src/bundle/passive.rs @@ -0,0 +1,207 @@ +use bevy_ecs::{bundle::Bundle, component::Component}; + +use crate::bundle::player::BodyPart; + +#[derive(Component, Default)] +#[cfg_attr(feature = "json", derive(serde::Deserialize))] +pub struct Merits { + pub life: u16, + pub crits: u16, + + pub brawn: u16, + pub protection: u16, + pub sharpness: u16, + pub evasion: u16, + + pub heavy_artillery_mastery: u16, + pub machine_gun_mastery: u16, + pub rifle_mastery: u16, + pub smg_mastery: u16, + pub shotgun_mastery: u16, + pub pistol_mastery: u16, + pub club_mastery: u16, + pub piercing_mastery: u16, + pub slashing_mastery: u16, + pub mechanical_mastery: u16, + pub temporary_mastery: u16, +} + +#[derive(Component)] +#[cfg_attr(feature = "json", derive(serde::Deserialize))] +pub struct Education { + /// Gain a 1% damage bonus to all weapons + pub bio2350: bool, + /// Gain a 10% damage increase when hitting an opponent's throat + pub bio2380: bool, + /// Gain a 3% chance increase of achieving a critical hit + pub bio2410: bool, + + /// Gain a 1% passive bonus to speed + pub cbt2790: bool, + /// Gain a +1.00 accuracy increase with Machine Guns + pub cbt2820: bool, + /// Gain a +1.00 accuracy increase with Submachine guns + pub cbt2830: bool, + /// Gain a +1.00 accuracy increase with Pistols + pub cbt2840: bool, + /// Gain a +1.00 accuracy increase with Rifles + pub cbt2850: bool, + /// Gain a +1.00 accuracy increase with Heavy Artillery + pub cbt2860: bool, + /// Gain a +1.00 accuracy increase with Shotguns + pub cbt2125: bool, + + /// Gain a +1.00 accuracy increase with Temporary weapons + pub gen2116: bool, + /// Gain a 5% damage increase with Temporary weapons + pub gen2119: bool, + + /// Gain a 1% passive bonus to dexterity + pub haf2104: bool, + /// Gain a 1% passive bonus to speed + pub haf2105: bool, + /// Gain a 1% passive bonus to strength + pub haf2106: bool, + /// Gain a 2% passive bonus to strength + pub haf2107: bool, + /// Gain a 1% passive bonus to dexterity + pub haf2108: bool, + /// Gain a 3% passive bonus to speed + pub haf2109: bool, + + /// Gain a 10% damage increase with Japanese blade weapons + pub his2160: bool, + /// Gain a 2% bonus to all melee damage + pub his2170: bool, + + /// Gain a 1% passive bonus to speed + pub mth2240: bool, + /// Gain a 1% passive bonus to speed + pub mth2250: bool, + /// Gain a 1% passive bonus to defense + pub mth2260: bool, + /// Gain a 2% passive bonus to defense + pub mth2320: bool, + + /// Gain a 5% bonus to ammo conservation + pub mth2310: bool, + /// Gain a 20% bonus to ammo conservation + pub mth3330: bool, + + /// Gain a 1% passive bonus to dexterity + pub psy2640: bool, + /// Gain a 2% passive bonus to dexterity + pub psy2650: bool, + /// Gain a 4% passive bonus to dexterity + pub psy2660: bool, + /// Gain an 8% passive bonus to dexterity + pub psy2670: bool, + + /// Gain a 1% passive bonus to defense + pub def2710: bool, + /// Gain a 2% passive bonus to defense + pub def2730: bool, + /// Gain a 3% passive bonus to defense + pub def2740: bool, + /// Gain a 2% passive bonus to speed + pub def2750: bool, + /// Gain a 3% passive bonus to speed + pub def2760: bool, + /// Gain a 100% increase in damage dealt when using fists alone + pub def3770: bool, + + /// Gain a 10% increase in steroid effectiveness + // NOTE: this effect is additive with the strip club perks + pub spt2480: bool, + /// Gain a 2% passive bonus to speed and strength + pub spt2490: bool, + /// Gain a 2% passive bonus to defense and dexterity + pub spt2500: bool, +} + +impl Default for Education { + fn default() -> Self { + Self { + bio2350: true, + bio2380: true, + bio2410: true, + cbt2790: true, + cbt2820: true, + cbt2830: true, + cbt2840: true, + cbt2850: true, + cbt2860: true, + cbt2125: true, + gen2116: true, + gen2119: true, + haf2104: true, + haf2105: true, + haf2106: true, + haf2107: true, + haf2108: true, + haf2109: true, + his2160: true, + his2170: true, + mth2240: true, + mth2250: true, + mth2260: true, + mth2320: true, + mth2310: true, + mth3330: true, + psy2640: true, + psy2650: true, + psy2660: true, + psy2670: true, + def2710: true, + def2730: true, + def2740: true, + def2750: true, + def2760: true, + def3770: true, + spt2480: true, + spt2490: true, + spt2500: true, + } + } +} + +#[derive(Component, Default)] +#[cfg_attr(feature = "json", derive(serde::Deserialize))] +pub struct FactionUpgrades { + pub str: u16, + pub spd: u16, + pub def: u16, + pub dex: u16, + pub life: u16, + pub acc: u16, + pub dmg: u16, + pub side_effects: u16, +} + +#[derive(Component)] +#[cfg_attr(feature = "json", derive(serde::Deserialize))] +pub enum DrugCooldown { + Xanax, + Vicodin, +} + +#[derive(Clone, Copy)] +pub enum EducationPartDamageBonus { + Bio2380, +} + +impl EducationPartDamageBonus { + pub fn dmg_bonus(self, part: BodyPart) -> Option { + match part { + BodyPart::Throat => Some(0.10), + _ => None, + } + } +} + +#[derive(Bundle, Default)] +pub(crate) struct PassiveBundle { + pub merits: Merits, + pub education: Education, + pub faction: FactionUpgrades, +} diff --git a/models/src/bundle/player.rs b/models/src/bundle/player.rs new file mode 100644 index 0000000..6a2fe6c --- /dev/null +++ b/models/src/bundle/player.rs @@ -0,0 +1,184 @@ +use bevy_ecs::{bundle::Bundle, component::Component, entity::Entity, message::Message}; +use strum::Display; + +use crate::bundle::{ + Name, + bonus::BonusPartDamageBonus, + passive::EducationPartDamageBonus, + stat::{CritRate, DamageBonus, SimpleStatBundle, WeaponAccuracy}, + weapon::WeaponSlot, +}; + +#[derive(Component)] +pub struct Attacker; + +#[derive(Component)] +pub struct Defender; + +#[derive(Component)] +pub struct Defeated; + +#[derive(Component)] +pub struct Current; + +#[derive(Component)] +pub struct CurrentTarget; + +#[derive(Component, Default)] +pub struct Player; + +#[derive(Component)] +pub struct Level(pub u16); + +impl Default for Level { + fn default() -> Self { + Self(1) + } +} + +#[derive(Component, Debug)] +pub struct MaxHealth(pub u16); + +impl Default for MaxHealth { + fn default() -> Self { + Self(100) + } +} + +#[derive(Component, Debug, Default)] +pub struct CombatTurns(pub u16); + +#[derive(Component, Debug)] +pub struct Weapons { + pub primary: Option, + pub secondary: Option, + pub melee: Option, + pub temporary: Option, + pub fists: Entity, + pub kick: Entity, +} + +#[derive(Component, Debug)] +#[cfg_attr(feature = "json", derive(serde::Deserialize))] +#[cfg_attr(feature = "json", serde(tag = "type", rename_all = "snake_case"))] +pub enum PlayerStrategy { + AlwaysFists, + AlwaysKicks, + PrimaryMelee { + reload: bool, + }, + InOrder { + order: Vec, + reload: bool, + }, +} + +impl Default for PlayerStrategy { + fn default() -> Self { + Self::AlwaysFists + } +} + +#[derive(Message)] +pub struct ChooseWeapon(pub Entity); + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum BodyPart { + LeftHand, + RightHand, + LeftArm, + RightArm, + LeftFoot, + RightFoot, + LeftLeg, + RightLeg, + Stomach, + Chest, + Groin, + Head, + Throat, + Heart, +} + +impl std::fmt::Display for BodyPart { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::LeftHand => write!(f, "Left hand"), + Self::RightHand => write!(f, "Right hand"), + Self::LeftArm => write!(f, "Left arm"), + Self::RightArm => write!(f, "Right arm"), + Self::LeftFoot => write!(f, "Left foot"), + Self::RightFoot => write!(f, "Right foot"), + Self::LeftLeg => write!(f, "Left leg"), + Self::RightLeg => write!(f, "Right leg"), + Self::Stomach => write!(f, "Stomach"), + Self::Chest => write!(f, "Chest"), + Self::Groin => write!(f, "Groin"), + Self::Head => write!(f, "Head"), + Self::Throat => write!(f, "Throat"), + Self::Heart => write!(f, "Heart"), + } + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Display)] +pub enum FightEndType { + Victory, + Stalemate, + Loss, +} + +#[derive(Component)] +pub enum PartDamageBonus { + Education(EducationPartDamageBonus), + WeaponBonus { + value: f32, + bonus: BonusPartDamageBonus, + }, +} + +#[derive(Message)] +pub enum HealthRestore { + Cauterise, + Serotonin { extra_effectiveness: f32 }, + Bloodlust { dmg: u32, value: f32 }, +} + +impl PartDamageBonus { + pub fn dmg_bonus(&self, part: BodyPart) -> Option { + match self { + Self::Education(edu) => edu.dmg_bonus(part), + Self::WeaponBonus { value, bonus } => bonus.dmg_bonus(part, *value), + } + } +} + +#[derive(Bundle)] +pub struct PlayerBundle { + pub name: Name, + pub player: Player, + pub level: Level, + pub crit_rate: SimpleStatBundle, + // TODO: since these two need to be tracked here anyways it might be preferable to shift all + // player specific passives here instead of tracking them on the weapons + pub acc_bonus: SimpleStatBundle, + pub dmg_bonus: SimpleStatBundle, + + pub strategy: PlayerStrategy, + pub combat_turns: CombatTurns, +} + +impl PlayerBundle { + pub fn new(name: impl ToString, level: u16, strategy: PlayerStrategy) -> Self { + Self { + name: Name(name.to_string()), + player: Player, + level: Level(level), + crit_rate: SimpleStatBundle::new(24), + acc_bonus: SimpleStatBundle::new(0.0), + dmg_bonus: SimpleStatBundle::new(0.0), + strategy, + combat_turns: Default::default(), + } + } +} diff --git a/models/src/bundle/stat.rs b/models/src/bundle/stat.rs new file mode 100644 index 0000000..5c34c8a --- /dev/null +++ b/models/src/bundle/stat.rs @@ -0,0 +1,408 @@ +use std::marker::PhantomData; + +use bevy_ecs::{bundle::Bundle, component::Component}; + +use crate::bundle::player::BodyPart; + +pub trait SimpleStatMarker: Send + Sync + 'static { + type ValueType: Send + Sync + Copy + std::fmt::Display + 'static; + type BonusType: Send + Sync + Copy + 'static; + + fn apply_bonus(value: Self::ValueType, bonus: Self::BonusType) -> Self::ValueType; + fn revert_bonus(value: Self::ValueType, bonus: Self::BonusType) -> Self::ValueType; + fn denormalise_value(value: Self::ValueType) -> Self::ValueType { + value + } + fn denormalise_bonus(value: Self::BonusType) -> Self::BonusType { + value + } +} + +#[derive(Component)] +pub struct SimpleStatBaseline { + pub value: Stat::ValueType, + marker: PhantomData, +} + +#[derive(Component)] +pub struct SimpleStatEffective { + pub value: Stat::ValueType, + marker: PhantomData, +} + +#[derive(Component)] +pub struct SimpleStatBonus { + pub label: &'static str, + pub value: Stat::BonusType, + marker: PhantomData, +} + +impl SimpleStatBonus { + pub fn new(label: &'static str, value: Stat::BonusType) -> Self { + Self { + label, + value, + marker: PhantomData, + } + } +} + +#[derive(Component)] +pub struct SimpleStatSnapshot { + pub value: Stat::ValueType, + pub marker: PhantomData, +} + +#[derive(Bundle)] +pub struct SimpleStatBundle { + pub baseline: SimpleStatBaseline, + pub effective: SimpleStatEffective, +} + +impl SimpleStatBundle { + pub fn new(value: Stat::ValueType) -> Self { + Self { + baseline: SimpleStatBaseline { + value, + marker: PhantomData, + }, + effective: SimpleStatEffective { + value, + marker: PhantomData, + }, + } + } +} + +impl Clone for SimpleStatEffective { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for SimpleStatEffective where Stat::ValueType: Copy {} + +#[derive(Default)] +pub struct CritRate; + +impl SimpleStatMarker for CritRate { + type ValueType = u16; + type BonusType = u16; + fn apply_bonus(value: Self::ValueType, bonus: Self::BonusType) -> Self::ValueType { + value + bonus + } + fn revert_bonus(value: Self::ValueType, bonus: Self::BonusType) -> Self::ValueType { + value - bonus + } +} + +impl rand::distr::Distribution for SimpleStatEffective { + fn sample(&self, rng: &mut R) -> BodyPart { + if rng.random_ratio((self.value) as u32, 200) { + match rng.random_range(1..=10) { + 1 => BodyPart::Heart, + 2 => BodyPart::Throat, + _ => BodyPart::Heart, + } + } else { + match rng.random_range(1..=20) { + 1 => BodyPart::LeftHand, + 2 => BodyPart::RightHand, + 3 => BodyPart::LeftArm, + 4 => BodyPart::RightArm, + 5 => BodyPart::LeftFoot, + 6 => BodyPart::RightFoot, + 7 | 8 => BodyPart::RightLeg, + 9 | 10 => BodyPart::LeftLeg, + 11..=15 => BodyPart::Chest, + 16 => BodyPart::Groin, + _ => BodyPart::Stomach, + } + } + } +} + +impl std::ops::Add<&SimpleStatEffective> for &SimpleStatEffective +where + Stat: SimpleStatMarker, + Stat::ValueType: std::ops::Add, +{ + type Output = SimpleStatEffective; + + fn add(self, rhs: &SimpleStatEffective) -> Self::Output { + SimpleStatEffective { + value: self.value + rhs.value, + marker: PhantomData, + } + } +} + +#[derive(Default)] +pub struct AmmoControl; + +impl SimpleStatMarker for AmmoControl { + type ValueType = f32; + type BonusType = f32; + fn apply_bonus(value: Self::ValueType, bonus: Self::BonusType) -> Self::ValueType { + value + bonus + } + fn revert_bonus(value: Self::ValueType, bonus: Self::BonusType) -> Self::ValueType { + value - bonus + } + fn denormalise_value(value: Self::ValueType) -> Self::ValueType { + value * 100.0 + } + fn denormalise_bonus(value: Self::BonusType) -> Self::BonusType { + value * 100.0 + } +} + +#[derive(Default)] +pub struct DamageBonus; + +impl SimpleStatMarker for DamageBonus { + type ValueType = f32; + type BonusType = f32; + fn apply_bonus(value: Self::ValueType, bonus: Self::BonusType) -> Self::ValueType { + value + bonus + } + fn revert_bonus(value: Self::ValueType, bonus: Self::BonusType) -> Self::ValueType { + value - bonus + } + fn denormalise_value(value: Self::ValueType) -> Self::ValueType { + value * 100.0 + } + fn denormalise_bonus(value: Self::BonusType) -> Self::BonusType { + value * 100.0 + } +} + +#[derive(Default)] +pub struct WeaponAccuracy; + +impl SimpleStatMarker for WeaponAccuracy { + type ValueType = f32; + type BonusType = f32; + fn apply_bonus(value: Self::ValueType, bonus: Self::BonusType) -> Self::ValueType { + value + bonus + } + fn revert_bonus(value: Self::ValueType, bonus: Self::BonusType) -> Self::ValueType { + value - bonus + } + fn denormalise_value(value: Self::ValueType) -> Self::ValueType { + value * 50.0 + 50.0 + } + fn denormalise_bonus(value: Self::BonusType) -> Self::BonusType { + value * 50.0 + } +} + +#[derive(Default)] +pub struct ClipSize; + +impl SimpleStatMarker for ClipSize { + type ValueType = u16; + type BonusType = f32; + fn apply_bonus(value: Self::ValueType, bonus: Self::BonusType) -> Self::ValueType { + ((value as f32) * bonus).round() as u16 + } + fn revert_bonus(value: Self::ValueType, bonus: Self::BonusType) -> Self::ValueType { + ((value as f32) / bonus).round() as u16 + } +} + +#[derive(Default)] +pub struct Clips; + +impl SimpleStatMarker for Clips { + type ValueType = u16; + type BonusType = i16; + fn apply_bonus(value: Self::ValueType, bonus: Self::BonusType) -> Self::ValueType { + ((value as i16) + bonus) as u16 + } + fn revert_bonus(value: Self::ValueType, bonus: Self::BonusType) -> Self::ValueType { + ((value as i16) - bonus) as u16 + } +} + +#[derive(Default)] +pub struct Health; + +impl SimpleStatMarker for Health { + type ValueType = u16; + type BonusType = u16; + fn apply_bonus(value: Self::ValueType, bonus: Self::BonusType) -> Self::ValueType { + value + bonus + } + fn revert_bonus(value: Self::ValueType, bonus: Self::BonusType) -> Self::ValueType { + value - bonus + } +} + +#[derive(Debug, Clone, Copy)] +pub enum StatType { + Str, + Def, + Spd, + Dex, +} + +pub trait StatMarker: Send + Sync + 'static { + fn stat_type() -> StatType; +} + +#[derive(Debug, Default)] +pub struct Strength; +impl StatMarker for Strength { + fn stat_type() -> StatType { + StatType::Str + } +} + +#[derive(Debug, Default)] +pub struct Defence; +impl StatMarker for Defence { + fn stat_type() -> StatType { + StatType::Def + } +} + +#[derive(Debug, Default)] +pub struct Speed; +impl StatMarker for Speed { + fn stat_type() -> StatType { + StatType::Spd + } +} + +#[derive(Debug, Default)] +pub struct Dexterity; +impl StatMarker for Dexterity { + fn stat_type() -> StatType { + StatType::Dex + } +} + +#[derive(Component)] +pub struct BaselineStat { + pub value: f32, + pub marker: PhantomData, +} + +impl Default for BaselineStat { + fn default() -> Self { + Self { + value: 10.0, + marker: PhantomData, + } + } +} + +#[derive(Component, Default)] +pub struct EffectiveStat { + pub value: f32, + pub marker: PhantomData, +} + +#[derive(Component)] +pub struct AdditiveBonuses { + pub factor: f32, + pub marker: PhantomData, +} + +impl Default for AdditiveBonuses { + fn default() -> Self { + Self { + factor: 1.0, + marker: PhantomData, + } + } +} + +#[derive(Component)] +pub struct MultiplicativeBonuses { + pub factor: f32, + pub marker: PhantomData, +} + +impl Default for MultiplicativeBonuses { + fn default() -> Self { + Self { + factor: 1.0, + marker: PhantomData, + } + } +} + +#[derive(Bundle, Default)] +pub struct StatBundle { + pub baseline: BaselineStat, + pub additive: AdditiveBonuses, + pub multiplicative: MultiplicativeBonuses, + pub effective: EffectiveStat, +} + +#[derive(Component)] +pub struct StatSnapshot { + pub additive_bonuses: f32, + pub multiplicative_bonuses: f32, + pub effective: f32, + pub marker: PhantomData, +} + +impl StatBundle { + pub fn new(baseline: f32) -> Self { + Self { + baseline: BaselineStat { + value: baseline, + marker: PhantomData, + }, + effective: EffectiveStat { + value: baseline, + marker: PhantomData, + }, + additive: AdditiveBonuses { + factor: 1.0, + marker: PhantomData, + }, + multiplicative: MultiplicativeBonuses { + factor: 1.0, + marker: PhantomData, + }, + } + } +} + +#[derive(Component)] +pub struct AdditiveBonus { + pub label: &'static str, + pub value: f32, + marker: PhantomData, +} + +impl AdditiveBonus { + pub fn new(label: &'static str, value: f32) -> Self { + Self { + label, + value, + marker: PhantomData, + } + } +} + +#[derive(Component)] +pub struct MultiplicativeBonus { + pub label: &'static str, + pub value: f32, + marker: PhantomData, +} + +impl MultiplicativeBonus { + pub fn new(label: &'static str, value: f32) -> Self { + Self { + label, + value, + marker: PhantomData, + } + } +} diff --git a/models/src/bundle/weapon.rs b/models/src/bundle/weapon.rs new file mode 100644 index 0000000..aaaf19c --- /dev/null +++ b/models/src/bundle/weapon.rs @@ -0,0 +1,230 @@ +use bevy_ecs::{bundle::Bundle, component::Component}; +use strum::Display; + +use crate::{ + bundle::{ + Name, + stat::{ + AmmoControl, ClipSize, Clips, CritRate, DamageBonus, SimpleStatBundle, WeaponAccuracy, + }, + }, + dto::weapon::{WeaponAmmo, WeaponDto}, +}; + +#[derive(Component)] +pub struct Usable; + +#[derive(Component)] +pub struct Weapon; + +#[derive(Component, Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "json", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "json", serde(rename_all = "snake_case"))] +pub enum WeaponSlot { + Primary, + Secondary, + Melee, + Temporary, + Fists, + Kick, +} + +#[derive(Component, Debug, Clone, Copy)] +pub enum WeaponVerb { + Hit, + Kicked, + Fired, + Threw, + Exploded, +} + +#[derive(Component, Clone, Copy)] +#[cfg_attr(feature = "json", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "json", serde(rename_all = "snake_case"))] +pub enum WeaponCategory { + HeavyArtillery, + MachineGun, + Rifle, + Smg, + Shotgun, + Pistol, + Clubbing, + Piercing, + Slashing, + Mechanical, + Temporary, + HandToHand, +} + +#[derive(Component, Debug)] +pub struct DamageStat(pub f32); + +#[derive(Component)] +pub struct Japanese; + +#[derive(Component)] +pub struct Ammo(pub u16); + +#[derive(Component)] +pub struct RateOfFire(pub [u16; 2]); + +#[derive(Component)] +pub struct NeedsReload; + +#[derive(Component, Default)] +pub struct NonTargeted; + +#[derive(Component, Default)] +pub struct Temporary; + +#[derive(Component)] +pub struct Uses(pub u16); + +impl Default for Uses { + fn default() -> Self { + Self(1) + } +} + +#[derive(Clone, Copy)] +#[cfg_attr(feature = "json", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "json", serde(rename_all = "snake_case"))] +pub enum WeaponMod { + ReflexSight, + HolographicSight, + AcogSight, + ThermalSight, + Laser1mw, + Laser5mw, + Laser30mw, + Laser100mw, + SmallSuppressor, + StandardSuppressor, + LargeSuppressor, + ExtendedMags, + HighCapacityMags, + ExtraClip, + ExtraClip2, + AdjustableTrigger, + HairTrigger, + Bipod, + Tripod, + CustomGrip, + SkeetChoke, + ImprovedChoke, + FullChoke, + RecoilPad, + StandardBrake, + HeavyDutyBrake, + TacticalBrake, + SmallLight, + PrecisionLight, + TacticalIlluminator, +} + +#[derive(Clone, Copy)] +pub enum TurnTriggeredMod { + Bipod, + Tripod, + SmallLight, + PrecisionLight, + TacticalIlluminator, +} + +#[derive(Component, Debug, Clone, Copy, Display)] +pub enum DebuffingTemp { + TearGas, + SmokeGrenade, + PepperSpray, + ConcussionGrenade, + FlashGrenade, +} + +#[derive(Component, Debug, Clone, Copy, Display)] +pub enum BuffingTemp { + Serotonin, + Tyrosine, + Melatonin, + Epinephrine, +} + +#[derive(Component)] +pub struct EquippedMods(pub Vec); + +#[derive(Component)] +pub struct Experience(pub f32); + +#[derive(Bundle)] +pub struct WeaponBundle { + pub usable: Usable, + pub weapon: Weapon, + pub name: Name, + pub verb: WeaponVerb, + pub slot: WeaponSlot, +} + +#[derive(Bundle)] +pub struct DamagingWeaponBundle { + pub crit_rate: SimpleStatBundle, + pub dmg: DamageStat, + pub acc: SimpleStatBundle, + pub dmg_bonus: SimpleStatBundle, + pub equipped_mods: EquippedMods, + pub experience: Experience, + pub category: WeaponCategory, +} + +#[derive(Bundle)] +pub struct AmmoWeaponBundle { + pub ammo: Ammo, + pub clips: SimpleStatBundle, + pub clip_size: SimpleStatBundle, + pub rate_of_fire: RateOfFire, + pub ammo_control: SimpleStatBundle, +} + +impl WeaponBundle { + pub fn new(name: String, verb: WeaponVerb, slot: WeaponSlot) -> Self { + Self { + usable: Usable, + weapon: Weapon, + name: Name(name), + verb, + slot, + } + } + + pub fn fists() -> Self { + Self::new("Fists".to_owned(), WeaponVerb::Hit, WeaponSlot::Fists) + } + + pub fn kick() -> Self { + Self::new("Kick".to_owned(), WeaponVerb::Kicked, WeaponSlot::Kick) + } +} + +impl AmmoWeaponBundle { + pub fn new(weapon_ammo: &WeaponAmmo) -> Self { + Self { + ammo: Ammo(weapon_ammo.clip_size), + clips: SimpleStatBundle::new(2), + clip_size: SimpleStatBundle::new(weapon_ammo.clip_size), + rate_of_fire: RateOfFire(weapon_ammo.rate_of_fire), + ammo_control: SimpleStatBundle::new(0.0), + } + } +} + +impl DamagingWeaponBundle { + pub fn new(dto: &WeaponDto) -> Self { + Self { + crit_rate: SimpleStatBundle::new(0), + dmg: DamageStat(dto.dmg.unwrap_or(dto.base_dmg) / 10.0), + acc: SimpleStatBundle::new((dto.acc.unwrap_or(dto.base_acc) - 50.0) / 50.0), + dmg_bonus: SimpleStatBundle::new(0.0), + equipped_mods: EquippedMods(dto.mods.iter().filter_map(|m| *m).collect()), + experience: Experience(dto.experience.unwrap_or_default()), + category: dto.cat, + } + } +} diff --git a/models/src/dto/armour.rs b/models/src/dto/armour.rs new file mode 100644 index 0000000..a05e02d --- /dev/null +++ b/models/src/dto/armour.rs @@ -0,0 +1,2122 @@ +use std::borrow::Cow; + +use bevy_ecs::{entity::Entity, world::World}; +use strum::Display; + +use crate::{ + bundle::{ + Name, + armour::{Armour, ArmourCoverage, ArmourValue, Immunities, Immunity}, + bonus::ArmourBonusType, + }, + dto::draw_id, +}; + +#[derive(Clone, Copy, Debug, Display, PartialEq, Eq)] +#[cfg_attr(feature = "json", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "json", serde(rename_all = "snake_case"))] +pub enum ArmourSlot { + Head, + Body, + Hands, + Legs, + Feet, +} + +#[derive(Debug, Clone)] +#[cfg_attr(feature = "json", derive(serde::Deserialize, serde::Serialize))] +pub struct ArmourBonusInfo { + pub kind: ArmourBonusType, + pub value: Option, +} + +#[derive(Debug, Clone)] +#[cfg_attr(feature = "json", derive(serde::Deserialize, serde::Serialize))] +pub struct CoverageDto { + pub body: f32, + pub heart: f32, + pub stomach: f32, + pub chest: f32, + pub arm: f32, + pub groin: f32, + pub leg: f32, + pub throat: f32, + pub hand: f32, + pub foot: f32, + pub head: f32, +} + +#[derive(Debug, Clone)] +#[cfg_attr(feature = "json", derive(serde::Deserialize, serde::Serialize))] +pub struct ArmourDto { + pub slot: ArmourSlot, + pub id: i32, + pub name: Cow<'static, str>, + pub base_armour: f32, + pub armour: Option, + pub coverage: CoverageDto, + pub immunities: Cow<'static, [Immunity]>, + pub bonus: Option, +} + +impl ArmourDto { + pub fn new(name: &str, armour: Option, bonus: Option) -> Option { + let base = match name { + "Leather Vest" => Self::LEATHER_VEST, + "Police Vest" => Self::POLICE_VEST, + "Bulletproof Vest" => Self::BULLETPROOF_VEST, + "Full Body Armor" => Self::FULL_BODY_ARMOR, + "Outer Tactical Vest" => Self::OUTER_TACTICAL_VEST, + "Chain Mail" => Self::CHAIN_MAIL, + "Flak Jacket" => Self::FLAK_JACKET, + "Combat Vest" => Self::COMBAT_VEST, + "Liquid Body Armor" => Self::LIQUID_BODY_ARMOR, + "Flexible Body Armor" => Self::FLEXIBLE_BODY_ARMOR, + "Hazmat Suit" => Self::HAZMAT_SUIT, + "Medieval Helmet" => Self::MEDIEVAL_HELMET, + "Kevlar Gloves" => Self::KEVLAR_GLOVES, + "WWII Helmet" => Self::WWII_HELMET, + "Motorcycle Helmet" => Self::MOTORCYCLE_HELMET, + "Construction Helmet" => Self::CONSTRUCTION_HELMET, + "Welding Helmet" => Self::WELDING_HELMET, + "Safety Boots" => Self::SAFETY_BOOTS, + "Hiking Boots" => Self::HIKING_BOOTS, + "Leather Helmet" => Self::LEATHER_HELMET, + "Leather Pants" => Self::LEATHER_PANTS, + "Leather Boots" => Self::LEATHER_BOOTS, + "Leather Gloves" => Self::LEATHER_GLOVES, + "Combat Helmet" => Self::COMBAT_HELMET, + "Combat Pants" => Self::COMBAT_PANTS, + "Combat Boots" => Self::COMBAT_BOOTS, + "Combat Gloves" => Self::COMBAT_GLOVES, + "Riot Helmet" => Self::RIOT_HELMET, + "Riot Body" => Self::RIOT_BODY, + "Riot Pants" => Self::RIOT_PANTS, + "Riot Boots" => Self::RIOT_BOOTS, + "Riot Gloves" => Self::RIOT_GLOVES, + "Dune Helmet" => Self::DUNE_HELMET, + "Dune Vest" => Self::DUNE_VEST, + "Dune Pants" => Self::DUNE_PANTS, + "Dune Boots" => Self::DUNE_BOOTS, + "Dune Gloves" => Self::DUNE_GLOVES, + "Assault Helmet" => Self::ASSAULT_HELMET, + "Assault Body" => Self::ASSAULT_BODY, + "Assault Pants" => Self::ASSAULT_PANTS, + "Assault Boots" => Self::ASSAULT_BOOTS, + "Assault Gloves" => Self::ASSAULT_GLOVES, + "Delta Gas Mask" => Self::DELTA_GAS_MASK, + "Delta Body" => Self::DELTA_BODY, + "Delta Pants" => Self::DELTA_PANTS, + "Delta Boots" => Self::DELTA_BOOTS, + "Delta Gloves" => Self::DELTA_GLOVES, + "Marauder Face Mask" => Self::MARAUDER_FACE_MASK, + "Marauder Body" => Self::MARAUDER_BODY, + "Marauder Pants" => Self::MARAUDER_PANTS, + "Marauder Boots" => Self::MARAUDER_BOOTS, + "Marauder Gloves" => Self::MARAUDER_GLOVES, + "EOD Helmet" => Self::EOD_HELMET, + "EOD Apron" => Self::EOD_APRON, + "EOD Pants" => Self::EOD_PANTS, + "EOD Boots" => Self::EOD_BOOTS, + "EOD Gloves" => Self::EOD_GLOVES, + "Kevlar Lab Coat" => Self::KEVLAR_LAB_COAT, + "M'aol Visage" => Self::M_AOL_VISAGE, + "M'aol Spathe" => Self::M_AOL_SPATHE, + "M'aol Britches" => Self::M_AOL_BRITCHES, + "M'aol Hooves" => Self::M_AOL_HOOVES, + "M'aol Clawshields" => Self::M_AOL_CLAWSHIELDS, + "Starshield Breastplate" => Self::STARSHIELD_BREASTPLATE, + "Sentinel Helmet" => Self::SENTINEL_HELMET, + "Sentinel Apron" => Self::SENTINEL_APRON, + "Sentinel Pants" => Self::SENTINEL_PANTS, + "Sentinel Boots" => Self::SENTINEL_BOOTS, + "Sentinel Gloves" => Self::SENTINEL_GLOVES, + "Vanguard Respirator" => Self::VANGUARD_RESPIRATOR, + "Vanguard Body" => Self::VANGUARD_BODY, + "Vanguard Pants" => Self::VANGUARD_PANTS, + "Vanguard Boots" => Self::VANGUARD_BOOTS, + "Vanguard Gloves" => Self::VANGUARD_GLOVES, + _ => return None, + }; + + Some(Self { + armour, + bonus: base.bonus.map(|b| ArmourBonusInfo { value: bonus, ..b }), + ..base + }) + } + + pub fn head_pieces() -> Vec { + Self::ALL + .iter() + .filter(|a| a.slot == ArmourSlot::Head) + .cloned() + .collect() + } + + pub fn body_pieces() -> Vec { + Self::ALL + .iter() + .filter(|a| a.slot == ArmourSlot::Body) + .cloned() + .collect() + } + + pub fn leg_pieces() -> Vec { + Self::ALL + .iter() + .filter(|a| a.slot == ArmourSlot::Legs) + .cloned() + .collect() + } + + pub fn foot_pieces() -> Vec { + Self::ALL + .iter() + .filter(|a| a.slot == ArmourSlot::Feet) + .cloned() + .collect() + } + + pub fn hand_pieces() -> Vec { + Self::ALL + .iter() + .filter(|a| a.slot == ArmourSlot::Hands) + .cloned() + .collect() + } + + pub fn spawn(self, world: &mut World) -> Entity { + let mut commands = world.spawn(draw_id()); + + commands.insert(( + Name(self.name.to_string()), + Armour, + ArmourValue(self.armour.unwrap_or(self.base_armour)), + ArmourCoverage::from(self.coverage), + Immunities(self.immunities.to_vec()), + )); + + commands.id() + } + + pub const ALL: &[Self] = &[ + Self::LEATHER_VEST, + Self::POLICE_VEST, + Self::BULLETPROOF_VEST, + Self::FULL_BODY_ARMOR, + Self::OUTER_TACTICAL_VEST, + Self::CHAIN_MAIL, + Self::FLAK_JACKET, + Self::COMBAT_VEST, + Self::LIQUID_BODY_ARMOR, + Self::FLEXIBLE_BODY_ARMOR, + Self::HAZMAT_SUIT, + Self::MEDIEVAL_HELMET, + Self::KEVLAR_GLOVES, + Self::WWII_HELMET, + Self::MOTORCYCLE_HELMET, + Self::CONSTRUCTION_HELMET, + Self::WELDING_HELMET, + Self::SAFETY_BOOTS, + Self::HIKING_BOOTS, + Self::LEATHER_HELMET, + Self::LEATHER_PANTS, + Self::LEATHER_BOOTS, + Self::LEATHER_GLOVES, + Self::COMBAT_HELMET, + Self::COMBAT_PANTS, + Self::COMBAT_BOOTS, + Self::COMBAT_GLOVES, + Self::RIOT_HELMET, + Self::RIOT_BODY, + Self::RIOT_PANTS, + Self::RIOT_BOOTS, + Self::RIOT_GLOVES, + Self::DUNE_HELMET, + Self::DUNE_VEST, + Self::DUNE_PANTS, + Self::DUNE_BOOTS, + Self::DUNE_GLOVES, + Self::ASSAULT_HELMET, + Self::ASSAULT_BODY, + Self::ASSAULT_PANTS, + Self::ASSAULT_BOOTS, + Self::ASSAULT_GLOVES, + Self::DELTA_GAS_MASK, + Self::DELTA_BODY, + Self::DELTA_PANTS, + Self::DELTA_BOOTS, + Self::DELTA_GLOVES, + Self::MARAUDER_FACE_MASK, + Self::MARAUDER_BODY, + Self::MARAUDER_PANTS, + Self::MARAUDER_BOOTS, + Self::MARAUDER_GLOVES, + Self::EOD_HELMET, + Self::EOD_APRON, + Self::EOD_PANTS, + Self::EOD_BOOTS, + Self::EOD_GLOVES, + Self::KEVLAR_LAB_COAT, + Self::M_AOL_VISAGE, + Self::M_AOL_SPATHE, + Self::M_AOL_BRITCHES, + Self::M_AOL_HOOVES, + Self::M_AOL_CLAWSHIELDS, + Self::STARSHIELD_BREASTPLATE, + Self::SENTINEL_HELMET, + Self::SENTINEL_APRON, + Self::SENTINEL_PANTS, + Self::SENTINEL_BOOTS, + Self::SENTINEL_GLOVES, + Self::VANGUARD_RESPIRATOR, + Self::VANGUARD_BODY, + Self::VANGUARD_PANTS, + Self::VANGUARD_BOOTS, + Self::VANGUARD_GLOVES, + ]; + + const LEATHER_VEST: ArmourDto = ArmourDto { + slot: ArmourSlot::Body, + id: 32, + name: Cow::Borrowed("Leather Vest"), + base_armour: 20.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: None, + coverage: CoverageDto { + body: 21.12, + heart: 97.27, + stomach: 95.09, + chest: 90.22, + arm: 6.5, + groin: 0.0, + leg: 0.0, + throat: 0.0, + hand: 0.0, + foot: 0.0, + head: 0.0, + }, + }; + + const POLICE_VEST: ArmourDto = ArmourDto { + slot: ArmourSlot::Body, + id: 33, + name: Cow::Borrowed("Police Vest"), + base_armour: 32.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: None, + coverage: CoverageDto { + body: 26.79, + heart: 100.0, + stomach: 100.0, + chest: 95.44, + arm: 35.34, + groin: 7.45, + leg: 0.77, + throat: 0.0, + hand: 0.0, + foot: 0.0, + head: 0.0, + }, + }; + + const BULLETPROOF_VEST: ArmourDto = ArmourDto { + slot: ArmourSlot::Body, + id: 34, + name: Cow::Borrowed("Bulletproof Vest"), + base_armour: 34.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: None, + coverage: CoverageDto { + body: 32.7, + heart: 100.0, + stomach: 100.0, + chest: 95.45, + arm: 35.87, + groin: 89.16, + leg: 0.78, + throat: 0.0, + hand: 0.0, + foot: 0.0, + head: 0.0, + }, + }; + + const FULL_BODY_ARMOR: ArmourDto = ArmourDto { + slot: ArmourSlot::Body, + id: 49, + name: Cow::Borrowed("Full Body Armor"), + base_armour: 31.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: None, + coverage: CoverageDto { + body: 48.46, + heart: 100.0, + stomach: 100.0, + chest: 100.0, + arm: 100.0, + groin: 60.68, + leg: 0.28, + throat: 90.68, + hand: 13.4, + foot: 0.0, + head: 0.0, + }, + }; + + const OUTER_TACTICAL_VEST: ArmourDto = ArmourDto { + slot: ArmourSlot::Body, + id: 50, + name: Cow::Borrowed("Outer Tactical Vest"), + base_armour: 36.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: None, + coverage: CoverageDto { + body: 27.54, + heart: 100.0, + stomach: 100.0, + chest: 100.0, + arm: 23.91, + groin: 35.95, + leg: 0.93, + throat: 0.0, + hand: 0.0, + foot: 0.0, + head: 0.0, + }, + }; + + const CHAIN_MAIL: ArmourDto = ArmourDto { + slot: ArmourSlot::Body, + id: 176, + name: Cow::Borrowed("Chain Mail"), + base_armour: 23.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: None, + coverage: CoverageDto { + body: 39.41, + heart: 100.0, + stomach: 100.0, + chest: 100.0, + arm: 99.9, + groin: 48.46, + leg: 1.93, + throat: 0.0, + hand: 0.0, + foot: 0.0, + head: 0.0, + }, + }; + + const FLAK_JACKET: ArmourDto = ArmourDto { + slot: ArmourSlot::Body, + id: 178, + name: Cow::Borrowed("Flak Jacket"), + base_armour: 30.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: None, + coverage: CoverageDto { + body: 26.52, + heart: 100.0, + stomach: 99.99, + chest: 96.42, + arm: 35.79, + groin: 3.12, + leg: 0.14, + throat: 0.0, + hand: 0.0, + foot: 0.0, + head: 0.0, + }, + }; + + const COMBAT_VEST: ArmourDto = ArmourDto { + slot: ArmourSlot::Body, + id: 332, + name: Cow::Borrowed("Combat Vest"), + base_armour: 38.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: None, + coverage: CoverageDto { + body: 27.45, + heart: 100.0, + stomach: 100.0, + chest: 100.0, + arm: 23.18, + groin: 36.04, + leg: 0.98, + throat: 0.0, + hand: 0.0, + foot: 0.0, + head: 0.0, + }, + }; + + const LIQUID_BODY_ARMOR: ArmourDto = ArmourDto { + slot: ArmourSlot::Body, + id: 333, + name: Cow::Borrowed("Liquid Body Armor"), + base_armour: 40.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: None, + coverage: CoverageDto { + body: 24.12, + heart: 100.0, + stomach: 99.59, + chest: 98.48, + arm: 19.85, + groin: 0.0, + leg: 0.0, + throat: 0.0, + hand: 0.0, + foot: 0.0, + head: 0.0, + }, + }; + + const FLEXIBLE_BODY_ARMOR: ArmourDto = ArmourDto { + slot: ArmourSlot::Body, + id: 334, + name: Cow::Borrowed("Flexible Body Armor"), + base_armour: 42.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: None, + coverage: CoverageDto { + body: 26.96, + heart: 100.0, + stomach: 96.57, + chest: 95.73, + arm: 36.16, + groin: 11.88, + leg: 0.51, + throat: 0.0, + hand: 0.0, + foot: 0.0, + head: 0.0, + }, + }; + + const HAZMAT_SUIT: ArmourDto = ArmourDto { + slot: ArmourSlot::Head, + id: 348, + name: Cow::Borrowed("Hazmat Suit"), + base_armour: 10.0, + armour: None, + immunities: Cow::Borrowed(&[ + Immunity::Radiation, + Immunity::PepperSpray, + Immunity::NerveGas, + Immunity::TearGas, + ]), + bonus: None, + coverage: CoverageDto { + body: 97.85, + heart: 100.0, + stomach: 100.0, + chest: 100.0, + arm: 100.0, + groin: 100.0, + leg: 100.0, + throat: 100.0, + hand: 100.0, + foot: 100.0, + head: 70.0, + }, + }; + + const MEDIEVAL_HELMET: ArmourDto = ArmourDto { + slot: ArmourSlot::Head, + id: 538, + name: Cow::Borrowed("Medieval Helmet"), + base_armour: 25.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: None, + coverage: CoverageDto { + body: 9.71, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.0, + groin: 0.0, + leg: 0.0, + throat: 42.24, + hand: 0.0, + foot: 0.0, + head: 93.54, + }, + }; + + const KEVLAR_GLOVES: ArmourDto = ArmourDto { + slot: ArmourSlot::Hands, + id: 640, + name: Cow::Borrowed("Kevlar Gloves"), + base_armour: 32.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: None, + coverage: CoverageDto { + body: 14.34, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.32, + groin: 0.0, + leg: 0.0, + throat: 0.0, + hand: 100.0, + foot: 0.0, + head: 0.0, + }, + }; + + const WWII_HELMET: ArmourDto = ArmourDto { + slot: ArmourSlot::Head, + id: 641, + name: Cow::Borrowed("WWII Helmet"), + base_armour: 34.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: None, + coverage: CoverageDto { + body: 4.64, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.0, + groin: 0.0, + leg: 0.0, + throat: 0.0, + hand: 0.0, + foot: 0.0, + head: 64.85, + }, + }; + + const MOTORCYCLE_HELMET: ArmourDto = ArmourDto { + slot: ArmourSlot::Head, + id: 642, + name: Cow::Borrowed("Motorcycle Helmet"), + base_armour: 30.0, + armour: None, + immunities: Cow::Borrowed(&[Immunity::PepperSpray]), + bonus: None, + coverage: CoverageDto { + body: 7.76, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.0, + groin: 0.0, + leg: 0.0, + throat: 23.19, + hand: 0.0, + foot: 0.0, + head: 85.35, + }, + }; + + const CONSTRUCTION_HELMET: ArmourDto = ArmourDto { + slot: ArmourSlot::Head, + id: 643, + name: Cow::Borrowed("Construction Helmet"), + base_armour: 30.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: None, + coverage: CoverageDto { + body: 2.84, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.0, + groin: 0.0, + leg: 0.0, + throat: 0.0, + hand: 0.0, + foot: 0.0, + head: 39.68, + }, + }; + + const WELDING_HELMET: ArmourDto = ArmourDto { + slot: ArmourSlot::Head, + id: 644, + name: Cow::Borrowed("Welding Helmet"), + base_armour: 34.0, + armour: None, + immunities: Cow::Borrowed(&[Immunity::FlashGrenade, Immunity::PepperSpray]), + bonus: None, + coverage: CoverageDto { + body: 10.7, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.0, + groin: 0.0, + leg: 0.0, + throat: 49.53, + hand: 0.0, + foot: 0.0, + head: 100.0, + }, + }; + + const SAFETY_BOOTS: ArmourDto = ArmourDto { + slot: ArmourSlot::Feet, + id: 645, + name: Cow::Borrowed("Safety Boots"), + base_armour: 30.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: None, + coverage: CoverageDto { + body: 14.96, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.0, + groin: 0.0, + leg: 4.74, + throat: 0.0, + hand: 0.0, + foot: 100.0, + head: 0.0, + }, + }; + + const HIKING_BOOTS: ArmourDto = ArmourDto { + slot: ArmourSlot::Feet, + id: 646, + name: Cow::Borrowed("Hiking Boots"), + base_armour: 24.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: None, + coverage: CoverageDto { + body: 15.03, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.0, + groin: 0.0, + leg: 5.22, + throat: 0.0, + hand: 0.0, + foot: 100.0, + head: 0.0, + }, + }; + + const LEATHER_HELMET: ArmourDto = ArmourDto { + slot: ArmourSlot::Head, + id: 647, + name: Cow::Borrowed("Leather Helmet"), + base_armour: 20.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: None, + coverage: CoverageDto { + #[allow(clippy::approx_constant)] + body: 3.14, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.0, + groin: 0.0, + leg: 0.0, + throat: 8.84, + hand: 0.0, + foot: 0.0, + head: 35.12, + }, + }; + + const LEATHER_PANTS: ArmourDto = ArmourDto { + slot: ArmourSlot::Legs, + id: 648, + name: Cow::Borrowed("Leather Pants"), + base_armour: 20.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: None, + coverage: CoverageDto { + body: 24.17, + heart: 0.0, + stomach: 7.5, + chest: 0.0, + arm: 0.0, + groin: 100.0, + leg: 100.0, + throat: 0.0, + hand: 0.0, + foot: 15.49, + head: 0.0, + }, + }; + + const LEATHER_BOOTS: ArmourDto = ArmourDto { + slot: ArmourSlot::Feet, + id: 649, + name: Cow::Borrowed("Leather Boots"), + base_armour: 20.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: None, + coverage: CoverageDto { + body: 15.54, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.0, + groin: 0.0, + leg: 8.77, + throat: 0.0, + hand: 0.0, + foot: 100.0, + head: 0.0, + }, + }; + + const LEATHER_GLOVES: ArmourDto = ArmourDto { + slot: ArmourSlot::Hands, + id: 650, + name: Cow::Borrowed("Leather Gloves"), + base_armour: 20.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: None, + coverage: CoverageDto { + body: 14.3, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.0, + groin: 0.0, + leg: 0.0, + throat: 0.0, + hand: 100.0, + foot: 0.0, + head: 0.0, + }, + }; + + const COMBAT_HELMET: ArmourDto = ArmourDto { + slot: ArmourSlot::Head, + id: 651, + name: Cow::Borrowed("Combat Helmet"), + base_armour: 38.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: None, + coverage: CoverageDto { + body: 4.83, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.0, + groin: 0.0, + leg: 0.0, + throat: 0.0, + hand: 0.0, + foot: 0.0, + head: 67.49, + }, + }; + + const COMBAT_PANTS: ArmourDto = ArmourDto { + slot: ArmourSlot::Legs, + id: 652, + name: Cow::Borrowed("Combat Pants"), + base_armour: 38.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: None, + coverage: CoverageDto { + body: 24.29, + heart: 0.0, + stomach: 7.62, + chest: 0.0, + arm: 0.0, + groin: 100.0, + leg: 100.0, + throat: 0.0, + hand: 0.0, + foot: 16.28, + head: 0.0, + }, + }; + + const COMBAT_BOOTS: ArmourDto = ArmourDto { + slot: ArmourSlot::Feet, + id: 653, + name: Cow::Borrowed("Combat Boots"), + base_armour: 38.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: None, + coverage: CoverageDto { + body: 15.42, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.0, + groin: 0.0, + leg: 7.92, + throat: 0.0, + hand: 0.0, + foot: 100.0, + head: 0.0, + }, + }; + + const COMBAT_GLOVES: ArmourDto = ArmourDto { + slot: ArmourSlot::Hands, + id: 654, + name: Cow::Borrowed("Combat Gloves"), + base_armour: 38.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: None, + coverage: CoverageDto { + body: 14.34, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.32, + groin: 0.0, + leg: 0.0, + throat: 0.0, + hand: 100.0, + foot: 0.0, + head: 0.0, + }, + }; + + const RIOT_HELMET: ArmourDto = ArmourDto { + slot: ArmourSlot::Head, + id: 655, + name: Cow::Borrowed("Riot Helmet"), + base_armour: 35.0, + armour: None, + immunities: Cow::Borrowed(&[Immunity::PepperSpray]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Impregnable, + value: None, + }), + coverage: CoverageDto { + body: 7.13, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.0, + groin: 0.0, + leg: 0.0, + throat: 0.0, + hand: 0.0, + foot: 0.0, + head: 99.61, + }, + }; + + const RIOT_BODY: ArmourDto = ArmourDto { + slot: ArmourSlot::Body, + id: 656, + name: Cow::Borrowed("Riot Body"), + base_armour: 45.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Impregnable, + value: None, + }), + coverage: CoverageDto { + body: 51.99, + heart: 100.0, + stomach: 100.0, + chest: 100.0, + arm: 100.0, + groin: 99.9, + leg: 3.01, + throat: 79.06, + hand: 21.6, + foot: 0.0, + head: 0.0, + }, + }; + + const RIOT_PANTS: ArmourDto = ArmourDto { + slot: ArmourSlot::Legs, + id: 657, + name: Cow::Borrowed("Riot Pants"), + base_armour: 45.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Impregnable, + value: None, + }), + coverage: CoverageDto { + body: 27.28, + heart: 0.0, + stomach: 2.12, + chest: 0.0, + arm: 0.0, + groin: 100.0, + leg: 100.0, + throat: 0.0, + hand: 0.0, + foot: 39.96, + head: 0.0, + }, + }; + + const RIOT_BOOTS: ArmourDto = ArmourDto { + slot: ArmourSlot::Feet, + id: 658, + name: Cow::Borrowed("Riot Boots"), + base_armour: 45.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Impregnable, + value: None, + }), + coverage: CoverageDto { + body: 15.95, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.0, + groin: 0.0, + leg: 11.62, + throat: 0.0, + hand: 0.0, + foot: 100.0, + head: 0.0, + }, + }; + + const RIOT_GLOVES: ArmourDto = ArmourDto { + slot: ArmourSlot::Hands, + id: 659, + name: Cow::Borrowed("Riot Gloves"), + base_armour: 45.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Impregnable, + value: None, + }), + coverage: CoverageDto { + body: 14.41, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.78, + groin: 0.0, + leg: 0.0, + throat: 0.0, + hand: 100.0, + foot: 0.0, + head: 0.0, + }, + }; + + const DUNE_HELMET: ArmourDto = ArmourDto { + slot: ArmourSlot::Head, + id: 660, + name: Cow::Borrowed("Dune Helmet"), + base_armour: 44.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Insurmountable, + value: None, + }), + coverage: CoverageDto { + body: 4.64, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.0, + groin: 0.0, + leg: 0.0, + throat: 0.0, + hand: 0.0, + foot: 0.0, + head: 64.83, + }, + }; + + const DUNE_VEST: ArmourDto = ArmourDto { + slot: ArmourSlot::Body, + id: 661, + name: Cow::Borrowed("Dune Vest"), + base_armour: 44.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Insurmountable, + value: None, + }), + coverage: CoverageDto { + body: 45.49, + heart: 100.0, + stomach: 100.0, + chest: 100.0, + arm: 100.0, + groin: 9.05, + leg: 0.46, + throat: 83.93, + hand: 21.6, + foot: 0.0, + head: 0.0, + }, + }; + + const DUNE_PANTS: ArmourDto = ArmourDto { + slot: ArmourSlot::Legs, + id: 662, + name: Cow::Borrowed("Dune Pants"), + base_armour: 44.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Insurmountable, + value: None, + }), + coverage: CoverageDto { + body: 24.03, + heart: 0.0, + stomach: 12.28, + chest: 0.0, + arm: 0.0, + groin: 100.0, + leg: 100.0, + throat: 0.0, + hand: 0.0, + foot: 12.1, + head: 0.0, + }, + }; + + const DUNE_BOOTS: ArmourDto = ArmourDto { + slot: ArmourSlot::Feet, + id: 663, + name: Cow::Borrowed("Dune Boots"), + base_armour: 44.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Insurmountable, + value: None, + }), + coverage: CoverageDto { + body: 15.78, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.0, + groin: 0.0, + leg: 10.47, + throat: 0.0, + hand: 0.0, + foot: 100.0, + head: 0.0, + }, + }; + + const DUNE_GLOVES: ArmourDto = ArmourDto { + slot: ArmourSlot::Hands, + id: 664, + name: Cow::Borrowed("Dune Gloves"), + base_armour: 44.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Insurmountable, + value: None, + }), + coverage: CoverageDto { + body: 14.36, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.42, + groin: 0.0, + leg: 0.0, + throat: 0.0, + hand: 100.0, + foot: 0.0, + head: 0.0, + }, + }; + + const ASSAULT_HELMET: ArmourDto = ArmourDto { + slot: ArmourSlot::Head, + id: 665, + name: Cow::Borrowed("Assault Helmet"), + base_armour: 46.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Impenetrable, + value: None, + }), + coverage: CoverageDto { + body: 5.05, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.0, + groin: 0.0, + leg: 0.0, + throat: 3.99, + hand: 0.0, + foot: 0.0, + head: 66.53, + }, + }; + + const ASSAULT_BODY: ArmourDto = ArmourDto { + slot: ArmourSlot::Body, + id: 666, + name: Cow::Borrowed("Assault Body"), + base_armour: 46.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Impenetrable, + value: None, + }), + coverage: CoverageDto { + body: 45.55, + heart: 100.0, + stomach: 100.0, + chest: 100.0, + arm: 100.0, + groin: 14.15, + leg: 0.56, + throat: 81.16, + hand: 20.73, + foot: 0.0, + head: 0.0, + }, + }; + + const ASSAULT_PANTS: ArmourDto = ArmourDto { + slot: ArmourSlot::Legs, + id: 667, + name: Cow::Borrowed("Assault Pants"), + base_armour: 46.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Impenetrable, + value: None, + }), + coverage: CoverageDto { + body: 26.44, + heart: 0.0, + stomach: 3.84, + chest: 0.0, + arm: 0.0, + groin: 100.0, + leg: 100.0, + throat: 0.0, + hand: 0.0, + foot: 33.25, + head: 0.0, + }, + }; + + const ASSAULT_BOOTS: ArmourDto = ArmourDto { + slot: ArmourSlot::Feet, + id: 668, + name: Cow::Borrowed("Assault Boots"), + base_armour: 46.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Impenetrable, + value: None, + }), + coverage: CoverageDto { + body: 15.58, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.0, + groin: 0.0, + leg: 9.07, + throat: 0.0, + hand: 0.0, + foot: 100.0, + head: 0.0, + }, + }; + + const ASSAULT_GLOVES: ArmourDto = ArmourDto { + slot: ArmourSlot::Hands, + id: 669, + name: Cow::Borrowed("Assault Gloves"), + base_armour: 46.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Impenetrable, + value: None, + }), + coverage: CoverageDto { + body: 14.37, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.5, + groin: 0.0, + leg: 0.0, + throat: 0.0, + hand: 100.0, + foot: 0.0, + head: 0.0, + }, + }; + + const DELTA_GAS_MASK: ArmourDto = ArmourDto { + slot: ArmourSlot::Head, + id: 670, + name: Cow::Borrowed("Delta Gas Mask"), + base_armour: 49.0, + armour: None, + immunities: Cow::Borrowed(&[Immunity::NerveGas, Immunity::TearGas, Immunity::PepperSpray]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Invulnerable, + value: None, + }), + coverage: CoverageDto { + body: 1.94, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.0, + groin: 0.0, + leg: 0.0, + throat: 0.0, + hand: 0.0, + foot: 0.0, + head: 27.13, + }, + }; + + const DELTA_BODY: ArmourDto = ArmourDto { + slot: ArmourSlot::Body, + id: 671, + name: Cow::Borrowed("Delta Body"), + base_armour: 49.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Invulnerable, + value: None, + }), + coverage: CoverageDto { + body: 46.24, + heart: 100.0, + stomach: 100.0, + chest: 100.0, + arm: 100.0, + groin: 30.7, + leg: 0.64, + throat: 88.81, + hand: 13.4, + foot: 0.0, + head: 0.0, + }, + }; + + const DELTA_PANTS: ArmourDto = ArmourDto { + slot: ArmourSlot::Legs, + id: 672, + name: Cow::Borrowed("Delta Pants"), + base_armour: 49.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Invulnerable, + value: None, + }), + coverage: CoverageDto { + body: 26.18, + heart: 0.0, + stomach: 8.59, + chest: 0.0, + arm: 0.0, + groin: 100.0, + leg: 100.0, + throat: 0.0, + hand: 0.0, + foot: 29.06, + head: 0.0, + }, + }; + + const DELTA_BOOTS: ArmourDto = ArmourDto { + slot: ArmourSlot::Feet, + id: 673, + name: Cow::Borrowed("Delta Boots"), + base_armour: 49.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Invulnerable, + value: None, + }), + coverage: CoverageDto { + body: 15.75, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.0, + groin: 0.0, + leg: 10.25, + throat: 0.0, + hand: 0.0, + foot: 100.0, + head: 0.0, + }, + }; + + const DELTA_GLOVES: ArmourDto = ArmourDto { + slot: ArmourSlot::Hands, + id: 674, + name: Cow::Borrowed("Delta Gloves"), + base_armour: 49.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Invulnerable, + value: None, + }), + coverage: CoverageDto { + body: 14.37, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.5, + groin: 0.0, + leg: 0.0, + throat: 0.0, + hand: 100.0, + foot: 0.0, + head: 0.0, + }, + }; + + const MARAUDER_FACE_MASK: ArmourDto = ArmourDto { + slot: ArmourSlot::Head, + id: 675, + name: Cow::Borrowed("Marauder Face Mask"), + base_armour: 40.0, + armour: None, + immunities: Cow::Borrowed(&[Immunity::PepperSpray]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Invulnerable, + value: None, + }), + coverage: CoverageDto { + body: 11.49, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.0, + groin: 0.0, + leg: 0.0, + throat: 60.57, + hand: 0.0, + foot: 0.0, + head: 100.0, + }, + }; + + const MARAUDER_BODY: ArmourDto = ArmourDto { + slot: ArmourSlot::Body, + id: 676, + name: Cow::Borrowed("Marauder Body"), + base_armour: 52.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Imperviable, + value: None, + }), + coverage: CoverageDto { + body: 42.47, + heart: 100.0, + stomach: 100.0, + chest: 100.0, + arm: 100.0, + groin: 0.0, + leg: 0.0, + throat: 84.72, + hand: 5.05, + foot: 0.0, + head: 0.0, + }, + }; + + const MARAUDER_PANTS: ArmourDto = ArmourDto { + slot: ArmourSlot::Legs, + id: 677, + name: Cow::Borrowed("Marauder Pants"), + base_armour: 52.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Imperviable, + value: None, + }), + coverage: CoverageDto { + body: 20.99, + heart: 0.0, + stomach: 0.52, + chest: 0.0, + arm: 0.0, + groin: 100.0, + leg: 96.69, + throat: 0.0, + hand: 0.0, + foot: 0.0, + head: 0.0, + }, + }; + + const MARAUDER_BOOTS: ArmourDto = ArmourDto { + slot: ArmourSlot::Feet, + id: 678, + name: Cow::Borrowed("Marauder Boots"), + base_armour: 52.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Invulnerable, + value: None, + }), + coverage: CoverageDto { + body: 15.65, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.0, + groin: 0.0, + leg: 9.53, + throat: 0.0, + hand: 0.0, + foot: 100.0, + head: 0.0, + }, + }; + + const MARAUDER_GLOVES: ArmourDto = ArmourDto { + slot: ArmourSlot::Hands, + id: 679, + name: Cow::Borrowed("Marauder Gloves"), + base_armour: 52.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Invulnerable, + value: None, + }), + coverage: CoverageDto { + body: 14.37, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.5, + groin: 0.0, + leg: 0.0, + throat: 0.0, + hand: 100.0, + foot: 0.0, + head: 0.0, + }, + }; + + const EOD_HELMET: ArmourDto = ArmourDto { + slot: ArmourSlot::Head, + id: 680, + name: Cow::Borrowed("EOD Helmet"), + base_armour: 55.0, + armour: None, + immunities: Cow::Borrowed(&[Immunity::ConcussionGrenade, Immunity::PepperSpray]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Impassable, + value: None, + }), + coverage: CoverageDto { + body: 10.22, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.0, + groin: 0.0, + leg: 0.0, + throat: 42.9, + hand: 0.0, + foot: 0.0, + head: 100.0, + }, + }; + + const EOD_APRON: ArmourDto = ArmourDto { + slot: ArmourSlot::Body, + id: 681, + name: Cow::Borrowed("EOD Apron"), + base_armour: 55.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Impassable, + value: None, + }), + coverage: CoverageDto { + body: 55.24, + heart: 100.0, + stomach: 100.0, + chest: 100.0, + arm: 100.0, + groin: 100.0, + leg: 17.58, + throat: 100.0, + hand: 16.88, + foot: 0.0, + head: 4.68, + }, + }; + + const EOD_PANTS: ArmourDto = ArmourDto { + slot: ArmourSlot::Legs, + id: 682, + name: Cow::Borrowed("EOD Pants"), + base_armour: 55.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Impassable, + value: None, + }), + coverage: CoverageDto { + body: 26.01, + heart: 0.0, + stomach: 23.94, + chest: 0.0, + arm: 0.0, + groin: 100.0, + leg: 100.0, + throat: 0.0, + hand: 0.0, + foot: 20.13, + head: 0.0, + }, + }; + + const EOD_BOOTS: ArmourDto = ArmourDto { + slot: ArmourSlot::Feet, + id: 683, + name: Cow::Borrowed("EOD Boots"), + base_armour: 55.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Impassable, + value: None, + }), + coverage: CoverageDto { + body: 15.17, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.0, + groin: 0.0, + leg: 6.19, + throat: 0.0, + hand: 0.0, + foot: 100.0, + head: 0.0, + }, + }; + + const EOD_GLOVES: ArmourDto = ArmourDto { + slot: ArmourSlot::Hands, + id: 684, + name: Cow::Borrowed("EOD Gloves"), + base_armour: 55.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Impassable, + value: None, + }), + coverage: CoverageDto { + body: 14.37, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.5, + groin: 0.0, + leg: 0.0, + throat: 0.0, + hand: 100.0, + foot: 0.0, + head: 0.0, + }, + }; + + const KEVLAR_LAB_COAT: ArmourDto = ArmourDto { + slot: ArmourSlot::Body, + id: 848, + name: Cow::Borrowed("Kevlar Lab Coat"), + base_armour: 32.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: None, + coverage: CoverageDto { + body: 29.15, + heart: 0.0, + stomach: 14.4, + chest: 31.69, + arm: 100.0, + groin: 12.05, + leg: 44.1, + throat: 0.0, + hand: 31.0, + foot: 0.0, + head: 0.0, + }, + }; + + const M_AOL_VISAGE: ArmourDto = ArmourDto { + slot: ArmourSlot::Head, + id: 1164, + name: Cow::Borrowed("M'aol Visage"), + base_armour: 38.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Kinetokinesis, + value: None, + }), + coverage: CoverageDto { + body: 37.76, + heart: 70.75, + stomach: 18.22, + chest: 81.03, + arm: 7.22, + groin: 0.33, + leg: 0.0, + throat: 100.0, + hand: 0.0, + foot: 0.0, + head: 100.0, + }, + }; + + const M_AOL_SPATHE: ArmourDto = ArmourDto { + slot: ArmourSlot::Body, + id: 1165, + name: Cow::Borrowed("M'aol Spathe"), + base_armour: 50.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Kinetokinesis, + value: None, + }), + coverage: CoverageDto { + body: 46.94, + heart: 100.0, + stomach: 95.91, + chest: 100.0, + arm: 99.35, + groin: 5.14, + leg: 0.0, + throat: 68.97, + hand: 0.0, + foot: 0.0, + head: 0.0, + }, + }; + + const M_AOL_BRITCHES: ArmourDto = ArmourDto { + slot: ArmourSlot::Legs, + id: 1166, + name: Cow::Borrowed("M'aol Britches"), + base_armour: 50.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Kinetokinesis, + value: None, + }), + coverage: CoverageDto { + body: 19.47, + heart: 0.0, + stomach: 4.46, + chest: 0.0, + arm: 0.0, + groin: 100.0, + leg: 90.19, + throat: 0.0, + hand: 0.0, + foot: 0.0, + head: 0.0, + }, + }; + + const M_AOL_HOOVES: ArmourDto = ArmourDto { + slot: ArmourSlot::Feet, + id: 1167, + name: Cow::Borrowed("M'aol Hooves"), + base_armour: 50.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Kinetokinesis, + value: None, + }), + coverage: CoverageDto { + body: 10.99, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.0, + groin: 0.0, + leg: 9.9, + throat: 0.0, + hand: 0.0, + foot: 100.0, + head: 0.0, + }, + }; + + const M_AOL_CLAWSHIELDS: ArmourDto = ArmourDto { + slot: ArmourSlot::Hands, + id: 1168, + name: Cow::Borrowed("M'aol Clawshields"), + base_armour: 50.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Kinetokinesis, + value: None, + }), + coverage: CoverageDto { + body: 10.18, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 1.81, + groin: 0.0, + leg: 0.0, + throat: 0.0, + hand: 100.0, + foot: 0.0, + head: 0.0, + }, + }; + + const STARSHIELD_BREASTPLATE: ArmourDto = ArmourDto { + slot: ArmourSlot::Body, + id: 1174, + name: Cow::Borrowed("Starshield Breastplate"), + base_armour: 39.0, + armour: None, + immunities: Cow::Borrowed(&[Immunity::Insanity]), + bonus: None, + coverage: CoverageDto { + body: 33.43, + heart: 100.0, + stomach: 100.0, + chest: 100.0, + arm: 10.89, + groin: 8.17, + leg: 0.0, + throat: 15.19, + hand: 0.0, + foot: 0.0, + head: 0.0, + }, + }; + + const SENTINEL_HELMET: ArmourDto = ArmourDto { + slot: ArmourSlot::Head, + id: 1307, + name: Cow::Borrowed("Sentinel Helmet"), + base_armour: 53.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Immmutable, + value: None, + }), + coverage: CoverageDto { + body: 5.33, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.0, + groin: 0.0, + leg: 0.0, + throat: 0.0, + hand: 0.0, + foot: 0.0, + head: 74.24, + }, + }; + + const SENTINEL_APRON: ArmourDto = ArmourDto { + slot: ArmourSlot::Body, + id: 1308, + name: Cow::Borrowed("Sentinel Apron"), + base_armour: 53.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Immmutable, + value: None, + }), + coverage: CoverageDto { + body: 42.69, + heart: 100.0, + stomach: 100.0, + chest: 100.0, + arm: 100.0, + groin: 0.0, + leg: 2.24, + throat: 95.91, + hand: 0.0, + foot: 0.0, + head: 0.0, + }, + }; + + const SENTINEL_PANTS: ArmourDto = ArmourDto { + slot: ArmourSlot::Legs, + id: 1309, + name: Cow::Borrowed("Sentinel Pants"), + base_armour: 53.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Immmutable, + value: None, + }), + coverage: CoverageDto { + body: 24.74, + heart: 0.0, + stomach: 4.71, + chest: 0.0, + arm: 0.0, + groin: 100.0, + leg: 100.0, + throat: 0.0, + hand: 0.0, + foot: 20.84, + head: 0.0, + }, + }; + + const SENTINEL_BOOTS: ArmourDto = ArmourDto { + slot: ArmourSlot::Feet, + id: 1310, + name: Cow::Borrowed("Sentinel Boots"), + base_armour: 53.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Immmutable, + value: None, + }), + coverage: CoverageDto { + body: 15.11, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.0, + groin: 0.0, + leg: 5.63, + throat: 0.0, + hand: 0.0, + foot: 100.0, + head: 0.0, + }, + }; + + const SENTINEL_GLOVES: ArmourDto = ArmourDto { + slot: ArmourSlot::Hands, + id: 1311, + name: Cow::Borrowed("Sentinel Gloves"), + base_armour: 53.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Immmutable, + value: None, + }), + coverage: CoverageDto { + body: 14.37, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.55, + groin: 0.0, + leg: 0.0, + throat: 0.0, + hand: 100.0, + foot: 0.0, + head: 0.0, + }, + }; + + const VANGUARD_RESPIRATOR: ArmourDto = ArmourDto { + slot: ArmourSlot::Head, + id: 1355, + name: Cow::Borrowed("Vanguard Respirator"), + base_armour: 48.0, + armour: None, + immunities: Cow::Borrowed(&[Immunity::PepperSpray, Immunity::NerveGas, Immunity::TearGas]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Irrepressible, + value: None, + }), + coverage: CoverageDto { + body: 2.82, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.0, + groin: 0.0, + leg: 0.0, + throat: 0.0, + hand: 0.0, + foot: 0.0, + head: 39.6, + }, + }; + + const VANGUARD_BODY: ArmourDto = ArmourDto { + slot: ArmourSlot::Body, + id: 1356, + name: Cow::Borrowed("Vanguard Body"), + base_armour: 48.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Irrepressible, + value: None, + }), + coverage: CoverageDto { + body: 44.28, + heart: 100.0, + stomach: 100.0, + chest: 100.0, + arm: 100.0, + groin: 35.39, + leg: 0.28, + throat: 83.53, + hand: 0.13, + foot: 0.0, + head: 0.0, + }, + }; + + const VANGUARD_PANTS: ArmourDto = ArmourDto { + slot: ArmourSlot::Legs, + id: 1357, + name: Cow::Borrowed("Vanguard Pants"), + base_armour: 48.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Irrepressible, + value: None, + }), + coverage: CoverageDto { + body: 24.96, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.0, + groin: 99.07, + leg: 100.0, + throat: 0.0, + hand: 0.0, + foot: 25.2, + head: 0.0, + }, + }; + + const VANGUARD_BOOTS: ArmourDto = ArmourDto { + slot: ArmourSlot::Feet, + id: 1358, + name: Cow::Borrowed("Vanguard Boots"), + base_armour: 48.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Irrepressible, + value: None, + }), + coverage: CoverageDto { + body: 15.13, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.0, + groin: 0.0, + leg: 5.83, + throat: 0.0, + hand: 0.0, + foot: 100.0, + head: 0.0, + }, + }; + + const VANGUARD_GLOVES: ArmourDto = ArmourDto { + slot: ArmourSlot::Hands, + id: 1359, + name: Cow::Borrowed("Vanguard Gloves"), + base_armour: 48.0, + armour: None, + immunities: Cow::Borrowed(&[]), + bonus: Some(ArmourBonusInfo { + kind: ArmourBonusType::Irrepressible, + value: None, + }), + coverage: CoverageDto { + body: 14.4, + heart: 0.0, + stomach: 0.0, + chest: 0.0, + arm: 0.73, + groin: 0.0, + leg: 0.0, + throat: 0.0, + hand: 100.0, + foot: 0.0, + head: 0.0, + }, + }; +} diff --git a/models/src/dto/metrics.rs b/models/src/dto/metrics.rs new file mode 100644 index 0000000..1ced5a2 --- /dev/null +++ b/models/src/dto/metrics.rs @@ -0,0 +1,32 @@ +#[cfg_attr(feature = "json", derive(serde::Serialize))] +#[cfg_attr(feature = "json", serde(tag = "type", rename_all = "snake_case"))] +#[derive(Debug, Clone)] +pub enum EntityInfo { + Player { + name: String, + id: usize, + is_attacker: bool, + }, + Weapon { + name: String, + owner: usize, + id: usize, + }, + Global, +} + +#[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, +} diff --git a/models/src/dto/mod.rs b/models/src/dto/mod.rs new file mode 100644 index 0000000..543b34e --- /dev/null +++ b/models/src/dto/mod.rs @@ -0,0 +1,14 @@ +use std::sync::atomic::{AtomicUsize, Ordering}; + +use crate::bundle::Id; + +pub mod armour; +pub mod metrics; +pub mod player; +pub mod weapon; + +static ID_COUNTER: AtomicUsize = AtomicUsize::new(0); + +fn draw_id() -> Id { + Id(ID_COUNTER.fetch_add(1, Ordering::Relaxed)) +} diff --git a/models/src/dto/player.rs b/models/src/dto/player.rs new file mode 100644 index 0000000..41b49fc --- /dev/null +++ b/models/src/dto/player.rs @@ -0,0 +1,107 @@ +use bevy_ecs::prelude::*; + +use crate::{ + bundle::{ + armour::PlayerArmour, + passive::{DrugCooldown, Education, FactionUpgrades, Merits, PassiveBundle}, + player::{PlayerBundle, PlayerStrategy, Weapons}, + stat::{Defence, Dexterity, Speed, StatBundle, Strength}, + }, + dto::{armour::ArmourDto, draw_id, weapon::WeaponDto}, +}; + +#[cfg_attr(feature = "json", derive(serde::Deserialize))] +pub struct EquippedWeapons { + pub primary: Option, + pub secondary: Option, + pub melee: Option, + pub temporary: Option, +} + +#[cfg_attr(feature = "json", derive(serde::Deserialize))] +#[derive(Default)] +pub struct EquippedArmour { + pub helmet: Option, + pub body: Option, + pub pants: Option, + pub gloves: Option, + pub boots: Option, +} + +#[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 PlayerDto { + pub name: String, + pub level: u16, + pub stats: Stats, + pub merits: Option, + pub education: Option, + pub weapons: EquippedWeapons, + pub armour: EquippedArmour, + pub faction: Option, + pub drug: Option, + pub strategy: PlayerStrategy, +} + +impl PlayerDto { + pub fn spawn(self, world: &mut World) -> EntityWorldMut<'_> { + let primary = self.weapons.primary.map(|p| p.spawn(world)); + let secondary = self.weapons.secondary.map(|s| s.spawn(world)); + let melee = self.weapons.melee.map(|m| m.spawn(world)); + let temporary = self.weapons.temporary.map(|m| m.spawn(world)); + let fists = WeaponDto::FISTS.spawn(world); + let kick = WeaponDto::KITCHEN_KNIFE.spawn(world); + + let head = self.armour.helmet.map(|a| a.spawn(world)); + let torso = self.armour.body.map(|a| a.spawn(world)); + let legs = self.armour.pants.map(|a| a.spawn(world)); + let hands = self.armour.gloves.map(|a| a.spawn(world)); + let feet = self.armour.boots.map(|a| a.spawn(world)); + + let mut commands = world.spawn(draw_id()); + + commands.insert(( + PlayerBundle::new(self.name, self.level, self.strategy), + StatBundle::::new(self.stats.str), + StatBundle::::new(self.stats.def), + StatBundle::::new(self.stats.spd), + StatBundle::::new(self.stats.dex), + )); + + let education = self.education.unwrap_or_default(); + let merits = self.merits.unwrap_or_default(); + let faction = self.faction.unwrap_or_default(); + + commands.insert(PassiveBundle { + education, + merits, + faction, + }); + + commands.insert(Weapons { + primary, + secondary, + melee, + temporary, + kick, + fists, + }); + + commands.insert(PlayerArmour { + torso, + head, + legs, + feet, + hands, + }); + + commands + } +} diff --git a/models/src/dto/weapon.rs b/models/src/dto/weapon.rs new file mode 100644 index 0000000..62bb65b --- /dev/null +++ b/models/src/dto/weapon.rs @@ -0,0 +1,5609 @@ +use std::borrow::Cow; + +use bevy_ecs::prelude::*; + +use crate::{ + bundle::{ + bonus::{WeaponBonusBundle, WeaponBonusType}, + weapon::{ + AmmoWeaponBundle, BuffingTemp, DamagingWeaponBundle, DebuffingTemp, EquippedMods, + Japanese, NonTargeted, Temporary, Uses, WeaponBundle, WeaponCategory, WeaponMod, + WeaponSlot, WeaponVerb, + }, + }, + dto::draw_id, + // hierarchy::HierarchyBuilder, +}; + +#[cfg_attr(feature = "json", derive(serde::Deserialize, serde::Serialize))] +pub struct WeaponAmmo { + pub clip_size: u16, + pub rate_of_fire: [u16; 2], +} + +#[cfg_attr(feature = "json", derive(serde::Deserialize, serde::Serialize))] +pub struct WeaponBonusInfo { + pub bonus: WeaponBonusType, + pub value: f32, +} + +#[cfg_attr(feature = "json", derive(serde::Deserialize, serde::Serialize))] +pub struct WeaponDto { + pub id: i32, + pub name: Cow<'static, str>, + pub kind: WeaponSlot, + pub cat: WeaponCategory, + pub base_dmg: f32, + pub base_acc: f32, + pub dmg: Option, + pub acc: Option, + pub ammo: Option, + pub mods: [Option; 2], + pub bonuses: [Option; 2], + pub compatible_mods: Cow<'static, [WeaponMod]>, + pub experience: Option, + pub japanese: bool, +} + +impl WeaponDto { + pub fn new( + name: &str, + dmg: Option, + acc: Option, + mods: [Option; 2], + bonuses: [Option; 2], + experience: Option, + ) -> Option { + let base = match name { + "Hammer" => Self::HAMMER, + "Baseball Bat" => Self::BASEBALL_BAT, + "Crowbar" => Self::CROWBAR, + "Knuckle Dusters" => Self::KNUCKLE_DUSTERS, + "Pen Knife" => Self::PEN_KNIFE, + "Kitchen Knife" => Self::KITCHEN_KNIFE, + "Dagger" => Self::DAGGER, + "Axe" => Self::AXE, + "Scimitar" => Self::SCIMITAR, + "Chainsaw" => Self::CHAINSAW, + "Samurai Sword" => Self::SAMURAI_SWORD, + "Glock 17" => Self::GLOCK_17, + "Raven MP25" => Self::RAVEN_MP25, + "Ruger 57" => Self::RUGER_57, + "Beretta M9" => Self::BERETTA_M9, + "USP" => Self::USP, + "Beretta 92FS" => Self::BERETTA_92FS, + "Fiveseven" => Self::FIVESEVEN, + "Magnum" => Self::MAGNUM, + "Desert Eagle" => Self::DESERT_EAGLE, + "Dual 92G Berettas" => Self::DUAL_92G_BERETTAS, + "Sawed-Off Shotgun" => Self::SAWED_OFF_SHOTGUN, + "Benelli M1 Tactical" => Self::BENELLI_M1_TACTICAL, + "MP5 Navy" => Self::MP5_NAVY, + "P90" => Self::P90, + "AK-47" => Self::AK_47, + "M4A1 Colt Carbine" => Self::M4A1_COLT_CARBINE, + "Benelli M4 Super" => Self::BENELLI_M4_SUPER, + "M16 A2 Rifle" => Self::M16_A2_RIFLE, + "Steyr AUG" => Self::STEYR_AUG, + "M249 SAW" => Self::M249_SAW, + "Minigun" => Self::MINIGUN, + "Snow Cannon" => Self::SNOW_CANNON, + "Neutrilux 2000" => Self::NEUTRILUX_2000, + "Springfield 1911" => Self::SPRINGFIELD_1911, + "Egg Propelled Launcher" => Self::EGG_PROPELLED_LAUNCHER, + "9mm Uzi" => Self::_9MM_UZI, + "RPG Launcher" => Self::RPG_LAUNCHER, + "Leather Bullwhip" => Self::LEATHER_BULLWHIP, + "Ninja Claws" => Self::NINJA_CLAWS, + "Yasukuni Sword" => Self::YASUKUNI_SWORD, + "Rusty Sword" => Self::RUSTY_SWORD, + "Wand of Destruction" => Self::WAND_OF_DESTRUCTION, + "Butterfly Knife" => Self::BUTTERFLY_KNIFE, + "XM8 Rifle" => Self::XM8_RIFLE, + "Taser" => Self::TASER, + "Cobra Derringer" => Self::COBRA_DERRINGER, + "S&W Revolver" => Self::S_W_REVOLVER, + "Claymore Sword" => Self::CLAYMORE_SWORD, + "Crossbow" => Self::CROSSBOW, + "Enfield SA-80" => Self::ENFIELD_SA_80, + "Grenade" => Self::GRENADE, + "Stick Grenade" => Self::STICK_GRENADE, + "Flash Grenade" => Self::FLASH_GRENADE, + "Jackhammer" => Self::JACKHAMMER, + "Swiss Army Knife" => Self::SWISS_ARMY_KNIFE, + "Mag 7" => Self::MAG_7, + "Smoke Grenade" => Self::SMOKE_GRENADE, + "Spear" => Self::SPEAR, + "Vektor CR-21" => Self::VEKTOR_CR_21, + "Claymore Mine" => Self::CLAYMORE_MINE, + "Flare Gun" => Self::FLARE_GUN, + "Heckler & Koch SL8" => Self::HECKLER_KOCH_SL8, + "SIG 550" => Self::SIG_550, + "BT MP9" => Self::BT_MP9, + "Chain Whip" => Self::CHAIN_WHIP, + "Wooden Nunchaku" => Self::WOODEN_NUNCHAKU, + "Kama" => Self::KAMA, + "Kodachi" => Self::KODACHI, + "Sai" => Self::SAI, + "Ninja Star" => Self::NINJA_STAR, + "Type 98 Anti Tank" => Self::TYPE_98_ANTI_TANK, + "Bushmaster Carbon 15" => Self::BUSHMASTER_CARBON_15, + "HEG" => Self::HEG, + "Taurus" => Self::TAURUS, + "Blowgun" => Self::BLOWGUN, + "Bo Staff" => Self::BO_STAFF, + "Fireworks" => Self::FIREWORKS, + "Katana" => Self::KATANA, + "Qsz-92" => Self::QSZ_92, + "SKS Carbine" => Self::SKS_CARBINE, + "Twin Tiger Hooks" => Self::TWIN_TIGER_HOOKS, + "Wushu Double Axes" => Self::WUSHU_DOUBLE_AXES, + "Ithaca 37" => Self::ITHACA_37, + "Lorcin 380" => Self::LORCIN_380, + "S&W M29" => Self::S_W_M29, + "Flamethrower" => Self::FLAMETHROWER, + "Tear Gas" => Self::TEAR_GAS, + "Throwing Knife" => Self::THROWING_KNIFE, + "Dual Axes" => Self::DUAL_AXES, + "Dual Hammers" => Self::DUAL_HAMMERS, + "Dual Scimitars" => Self::DUAL_SCIMITARS, + "Dual Samurai Swords" => Self::DUAL_SAMURAI_SWORDS, + "Pair of High Heels" => Self::PAIR_OF_HIGH_HEELS, + "Fine Chisel" => Self::FINE_CHISEL, + "Ivory Walking Cane" => Self::IVORY_WALKING_CANE, + "Gold Plated AK-47" => Self::GOLD_PLATED_AK_47, + "Handbag" => Self::HANDBAG, + "Pink Mac-10" => Self::PINK_MAC_10, + "Macana" => Self::MACANA, + "Pepper Spray" => Self::PEPPER_SPRAY, + "Slingshot" => Self::SLINGSHOT, + "Brick" => Self::BRICK, + "Metal Nunchaku" => Self::METAL_NUNCHAKU, + "Flail" => Self::FLAIL, + "SIG 552" => Self::SIG_552, + "ArmaLite M-15A4" => Self::ARMALITE_M_15A4, + "Guandao" => Self::GUANDAO, + "Lead Pipe" => Self::LEAD_PIPE, + "Ice Pick" => Self::ICE_PICK, + "Cricket Bat" => Self::CRICKET_BAT, + "Frying Pan" => Self::FRYING_PAN, + "Pillow" => Self::PILLOW, + "Epinephrine" => Self::EPINEPHRINE, + "Melatonin" => Self::MELATONIN, + "Serotonin" => Self::SEROTONIN, + "MP5k" => Self::MP5K, + "AK74U" => Self::AK74U, + "Skorpion" => Self::SKORPION, + "TMP" => Self::TMP, + "Thompson" => Self::THOMPSON, + "MP 40" => Self::MP_40, + "Luger" => Self::LUGER, + "Blunderbuss" => Self::BLUNDERBUSS, + "Blood Spattered Sickle" => Self::BLOOD_SPATTERED_SICKLE, + "Dual TMPs" => Self::DUAL_TMPS, + "Dual Bushmasters" => Self::DUAL_BUSHMASTERS, + "Dual MP5s" => Self::DUAL_MP5S, + "Dual P90s" => Self::DUAL_P90S, + "Dual Uzis" => Self::DUAL_UZIS, + "Book" => Self::BOOK, + "Golden Broomstick" => Self::GOLDEN_BROOMSTICK, + "Devil's Pitchfork" => Self::DEVIL_S_PITCHFORK, + "Pair of Ice Skates" => Self::PAIR_OF_ICE_SKATES, + "Diamond Icicle" => Self::DIAMOND_ICICLE, + "Snowball" => Self::SNOWBALL, + "Tavor TAR-21" => Self::TAVOR_TAR_21, + "Harpoon" => Self::HARPOON, + "Diamond Bladed Knife" => Self::DIAMOND_BLADED_KNIFE, + "Naval Cutlass" => Self::NAVAL_CUTLASS, + "Trout" => Self::TROUT, + "Petrified Humerus" => Self::PETRIFIED_HUMERUS, + "Molotov Cocktail" => Self::MOLOTOV_COCKTAIL, + "Plastic Sword" => Self::PLASTIC_SWORD, + "Penelope" => Self::PENELOPE, + "Duke's Hammer" => Self::DUKE_S_HAMMER, + "Tyrosine" => Self::TYROSINE, + "Nock Gun" => Self::NOCK_GUN, + "Beretta Pico" => Self::BERETTA_PICO, + "Riding Crop" => Self::RIDING_CROP, + "Rheinmetall MG 3" => Self::RHEINMETALL_MG_3, + "Homemade Pocket Shotgun" => Self::HOMEMADE_POCKET_SHOTGUN, + "Madball" => Self::MADBALL, + "Nail Bomb" => Self::NAIL_BOMB, + "Tranquilizer Gun" => Self::TRANQUILIZER_GUN, + "Bolt Gun" => Self::BOLT_GUN, + "Scalpel" => Self::SCALPEL, + "Nerve Gas" => Self::NERVE_GAS, + "Sledgehammer" => Self::SLEDGEHAMMER, + "Bug Swatter" => Self::BUG_SWATTER, + "Prototype" => Self::PROTOTYPE, + "Concussion Grenade" => Self::CONCUSSION_GRENADE, + "Bread Knife" => Self::BREAD_KNIFE, + "Semtex" => Self::SEMTEX, + "Poison Umbrella" => Self::POISON_UMBRELLA, + "Millwall Brick" => Self::MILLWALL_BRICK, + "SMAW Launcher" => Self::SMAW_LAUNCHER, + "China Lake" => Self::CHINA_LAKE, + "Milkor MGL" => Self::MILKOR_MGL, + "PKM" => Self::PKM, + "Negev NG-5" => Self::NEGEV_NG_5, + "Stoner 96" => Self::STONER_96, + "Meat Hook" => Self::MEAT_HOOK, + "Cleaver" => Self::CLEAVER, + "Crystalline Falcata" => Self::CRYSTALLINE_FALCATA, + "Stygian Darkness" => Self::STYGIAN_DARKNESS, + "Party Popper" => Self::PARTY_POPPER, + "Wrench" => Self::WRENCH, + "Golf Club" => Self::GOLF_CLUB, + "Bone Saw" => Self::BONE_SAW, + "Cattle Prod" => Self::CATTLE_PROD, + "Glitter Bomb" => Self::GLITTER_BOMB, + "Ban Hammer" => Self::BAN_HAMMER, + "Bolas" => Self::BOLAS, + _ => return None, + }; + + Some(Self { + acc, + dmg, + mods, + bonuses, + experience, + ..base + }) + } + + pub fn spawn(self, world: &mut World) -> Entity { + let mut commands = world.spawn(draw_id()); + + let verb = match self.kind { + WeaponSlot::Primary | WeaponSlot::Secondary => WeaponVerb::Fired, + WeaponSlot::Melee | WeaponSlot::Fists => WeaponVerb::Hit, + WeaponSlot::Kick => WeaponVerb::Kicked, + WeaponSlot::Temporary => { + if self.base_dmg > 0.0 { + WeaponVerb::Exploded + } else { + WeaponVerb::Threw + } + } + }; + + commands.insert(WeaponBundle::new(self.name.to_string(), verb, self.kind)); + + if let Some(ammo) = &self.ammo { + commands.insert(AmmoWeaponBundle::new(ammo)); + } + + if self.base_dmg > 0.0 { + commands.insert(DamagingWeaponBundle::new(&self)); + } + + if self.japanese { + commands.insert(Japanese); + } + + if self.kind == WeaponSlot::Temporary { + commands.insert((Temporary, Uses::default())); + + match self.id { + 256 => commands.insert(DebuffingTemp::TearGas), + 226 => commands.insert(DebuffingTemp::SmokeGrenade), + 392 => commands.insert(DebuffingTemp::PepperSpray), + 1042 => commands.insert(DebuffingTemp::ConcussionGrenade), + 222 => commands.insert(DebuffingTemp::FlashGrenade), + 465 => commands.insert(BuffingTemp::Serotonin), + 814 => commands.insert(BuffingTemp::Tyrosine), + 464 => commands.insert(BuffingTemp::Melatonin), + 463 => commands.insert(BuffingTemp::Epinephrine), + // damaging temps always hit the body + _ => commands.insert(NonTargeted), + }; + } + + let mods = self.mods.into_iter().flatten().collect(); + commands.insert(EquippedMods(mods)); + + if let Some(bonus) = &self.bonuses[0] { + commands.insert(children![WeaponBonusBundle::new(bonus.bonus, bonus.value)]); + } + + if let Some(bonus) = &self.bonuses[1] { + commands.insert(children![WeaponBonusBundle::new(bonus.bonus, bonus.value)]); + } + + commands.id() + } + + pub const PRIMARY: &[WeaponDto] = &[ + Self::SAWED_OFF_SHOTGUN, + Self::BENELLI_M1_TACTICAL, + Self::MP5_NAVY, + Self::P90, + Self::AK_47, + Self::M4A1_COLT_CARBINE, + Self::BENELLI_M4_SUPER, + Self::M16_A2_RIFLE, + Self::STEYR_AUG, + Self::M249_SAW, + Self::MINIGUN, + Self::SNOW_CANNON, + Self::NEUTRILUX_2000, + Self::EGG_PROPELLED_LAUNCHER, + Self::_9MM_UZI, + Self::XM8_RIFLE, + Self::ENFIELD_SA_80, + Self::JACKHAMMER, + Self::MAG_7, + Self::VEKTOR_CR_21, + Self::HECKLER_KOCH_SL8, + Self::SIG_550, + Self::BUSHMASTER_CARBON_15, + Self::SKS_CARBINE, + Self::ITHACA_37, + Self::GOLD_PLATED_AK_47, + Self::SIG_552, + Self::ARMALITE_M_15A4, + Self::AK74U, + Self::THOMPSON, + Self::MP_40, + Self::DUAL_TMPS, + Self::DUAL_BUSHMASTERS, + Self::DUAL_MP5S, + Self::DUAL_P90S, + Self::DUAL_UZIS, + Self::TAVOR_TAR_21, + Self::NOCK_GUN, + Self::RHEINMETALL_MG_3, + Self::PKM, + Self::NEGEV_NG_5, + Self::STONER_96, + ]; + + pub const SECONDARY: &[WeaponDto] = &[ + Self::GLOCK_17, + Self::RAVEN_MP25, + Self::RUGER_57, + Self::BERETTA_M9, + Self::USP, + Self::BERETTA_92FS, + Self::FIVESEVEN, + Self::MAGNUM, + Self::DESERT_EAGLE, + Self::DUAL_92G_BERETTAS, + Self::SPRINGFIELD_1911, + Self::RPG_LAUNCHER, + Self::TASER, + Self::COBRA_DERRINGER, + Self::S_W_REVOLVER, + Self::CROSSBOW, + Self::FLARE_GUN, + Self::BT_MP9, + Self::TYPE_98_ANTI_TANK, + Self::TAURUS, + Self::BLOWGUN, + Self::QSZ_92, + Self::LORCIN_380, + Self::S_W_M29, + Self::FLAMETHROWER, + Self::PINK_MAC_10, + Self::SLINGSHOT, + Self::MP5K, + Self::SKORPION, + Self::TMP, + Self::LUGER, + Self::BLUNDERBUSS, + Self::HARPOON, + Self::BERETTA_PICO, + Self::HOMEMADE_POCKET_SHOTGUN, + Self::TRANQUILIZER_GUN, + Self::PROTOTYPE, + Self::SMAW_LAUNCHER, + Self::CHINA_LAKE, + Self::MILKOR_MGL, + ]; + + pub const MELEE: &[WeaponDto] = &[ + Self::HAMMER, + Self::BASEBALL_BAT, + Self::CROWBAR, + Self::KNUCKLE_DUSTERS, + Self::PEN_KNIFE, + Self::KITCHEN_KNIFE, + Self::DAGGER, + Self::AXE, + Self::SCIMITAR, + Self::CHAINSAW, + Self::SAMURAI_SWORD, + Self::LEATHER_BULLWHIP, + Self::NINJA_CLAWS, + Self::YASUKUNI_SWORD, + Self::RUSTY_SWORD, + Self::WAND_OF_DESTRUCTION, + Self::BUTTERFLY_KNIFE, + Self::CLAYMORE_SWORD, + Self::SWISS_ARMY_KNIFE, + Self::SPEAR, + Self::CHAIN_WHIP, + Self::WOODEN_NUNCHAKU, + Self::KAMA, + Self::KODACHI, + Self::SAI, + Self::BO_STAFF, + Self::KATANA, + Self::TWIN_TIGER_HOOKS, + Self::WUSHU_DOUBLE_AXES, + Self::DUAL_AXES, + Self::DUAL_HAMMERS, + Self::DUAL_SCIMITARS, + Self::DUAL_SAMURAI_SWORDS, + Self::PAIR_OF_HIGH_HEELS, + Self::FINE_CHISEL, + Self::IVORY_WALKING_CANE, + Self::HANDBAG, + Self::MACANA, + Self::METAL_NUNCHAKU, + Self::FLAIL, + Self::GUANDAO, + Self::LEAD_PIPE, + Self::ICE_PICK, + Self::CRICKET_BAT, + Self::FRYING_PAN, + Self::PILLOW, + Self::BLOOD_SPATTERED_SICKLE, + Self::GOLDEN_BROOMSTICK, + Self::DEVIL_S_PITCHFORK, + Self::PAIR_OF_ICE_SKATES, + Self::DIAMOND_ICICLE, + Self::DIAMOND_BLADED_KNIFE, + Self::NAVAL_CUTLASS, + Self::PETRIFIED_HUMERUS, + Self::PLASTIC_SWORD, + Self::PENELOPE, + Self::DUKE_S_HAMMER, + Self::RIDING_CROP, + Self::MADBALL, + Self::BOLT_GUN, + Self::SCALPEL, + Self::SLEDGEHAMMER, + Self::BUG_SWATTER, + Self::BREAD_KNIFE, + Self::POISON_UMBRELLA, + Self::MILLWALL_BRICK, + Self::MEAT_HOOK, + Self::CLEAVER, + Self::CRYSTALLINE_FALCATA, + Self::GOLF_CLUB, + Self::BONE_SAW, + Self::CATTLE_PROD, + Self::BAN_HAMMER, + ]; + + pub const TEMPORARY: &[WeaponDto] = &[ + Self::GRENADE, + Self::STICK_GRENADE, + Self::FLASH_GRENADE, + Self::SMOKE_GRENADE, + Self::CLAYMORE_MINE, + Self::NINJA_STAR, + Self::HEG, + Self::FIREWORKS, + Self::TEAR_GAS, + Self::THROWING_KNIFE, + Self::PEPPER_SPRAY, + Self::BRICK, + Self::EPINEPHRINE, + Self::MELATONIN, + Self::SEROTONIN, + Self::BOOK, + Self::SNOWBALL, + Self::TROUT, + Self::MOLOTOV_COCKTAIL, + Self::TYROSINE, + Self::NAIL_BOMB, + Self::NERVE_GAS, + Self::CONCUSSION_GRENADE, + Self::SEMTEX, + Self::STYGIAN_DARKNESS, + Self::PARTY_POPPER, + Self::WRENCH, + Self::GLITTER_BOMB, + Self::BOLAS, + ]; + + pub const HAMMER: WeaponDto = WeaponDto { + id: 1, + name: Cow::Borrowed("Hammer"), + cat: WeaponCategory::Clubbing, + kind: WeaponSlot::Melee, + base_dmg: 17.0, + base_acc: 55.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const BASEBALL_BAT: WeaponDto = WeaponDto { + id: 2, + name: Cow::Borrowed("Baseball Bat"), + cat: WeaponCategory::Clubbing, + kind: WeaponSlot::Melee, + base_dmg: 16.0, + base_acc: 57.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const CROWBAR: WeaponDto = WeaponDto { + id: 3, + name: Cow::Borrowed("Crowbar"), + cat: WeaponCategory::Clubbing, + kind: WeaponSlot::Melee, + base_dmg: 20.0, + base_acc: 52.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const KNUCKLE_DUSTERS: WeaponDto = WeaponDto { + id: 4, + name: Cow::Borrowed("Knuckle Dusters"), + cat: WeaponCategory::Clubbing, + kind: WeaponSlot::Melee, + base_dmg: 11.0, + base_acc: 62.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const PEN_KNIFE: WeaponDto = WeaponDto { + id: 5, + name: Cow::Borrowed("Pen Knife"), + cat: WeaponCategory::Piercing, + kind: WeaponSlot::Melee, + base_dmg: 21.0, + base_acc: 45.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const KITCHEN_KNIFE: WeaponDto = WeaponDto { + id: 6, + name: Cow::Borrowed("Kitchen Knife"), + cat: WeaponCategory::Piercing, + kind: WeaponSlot::Melee, + base_dmg: 25.0, + base_acc: 55.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const DAGGER: WeaponDto = WeaponDto { + id: 7, + name: Cow::Borrowed("Dagger"), + cat: WeaponCategory::Piercing, + kind: WeaponSlot::Melee, + base_dmg: 28.0, + base_acc: 60.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const AXE: WeaponDto = WeaponDto { + id: 8, + name: Cow::Borrowed("Axe"), + cat: WeaponCategory::Clubbing, + kind: WeaponSlot::Melee, + base_dmg: 34.0, + base_acc: 52.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const SCIMITAR: WeaponDto = WeaponDto { + id: 9, + name: Cow::Borrowed("Scimitar"), + cat: WeaponCategory::Slashing, + kind: WeaponSlot::Melee, + base_dmg: 40.0, + base_acc: 58.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const CHAINSAW: WeaponDto = WeaponDto { + id: 10, + name: Cow::Borrowed("Chainsaw"), + cat: WeaponCategory::Mechanical, + kind: WeaponSlot::Melee, + base_dmg: 61.0, + base_acc: 23.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const SAMURAI_SWORD: WeaponDto = WeaponDto { + id: 11, + name: Cow::Borrowed("Samurai Sword"), + cat: WeaponCategory::Slashing, + kind: WeaponSlot::Melee, + base_dmg: 58.0, + base_acc: 52.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: true, + }; + + pub const GLOCK_17: WeaponDto = WeaponDto { + id: 12, + name: Cow::Borrowed("Glock 17"), + cat: WeaponCategory::Pistol, + kind: WeaponSlot::Secondary, + base_dmg: 28.0, + base_acc: 53.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 20, + rate_of_fire: [3, 6], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const RAVEN_MP25: WeaponDto = WeaponDto { + id: 13, + name: Cow::Borrowed("Raven MP25"), + cat: WeaponCategory::Pistol, + kind: WeaponSlot::Secondary, + base_dmg: 29.0, + base_acc: 52.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 6, + rate_of_fire: [3, 5], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const RUGER_57: WeaponDto = WeaponDto { + id: 14, + name: Cow::Borrowed("Ruger 57"), + cat: WeaponCategory::Pistol, + kind: WeaponSlot::Secondary, + base_dmg: 32.0, + base_acc: 56.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 20, + rate_of_fire: [3, 4], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const BERETTA_M9: WeaponDto = WeaponDto { + id: 15, + name: Cow::Borrowed("Beretta M9"), + cat: WeaponCategory::Pistol, + kind: WeaponSlot::Secondary, + base_dmg: 36.0, + base_acc: 54.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 17, + rate_of_fire: [4, 5], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const USP: WeaponDto = WeaponDto { + id: 16, + name: Cow::Borrowed("USP"), + cat: WeaponCategory::Pistol, + kind: WeaponSlot::Secondary, + base_dmg: 44.0, + base_acc: 58.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 15, + rate_of_fire: [4, 5], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const BERETTA_92FS: WeaponDto = WeaponDto { + id: 17, + name: Cow::Borrowed("Beretta 92FS"), + cat: WeaponCategory::Pistol, + kind: WeaponSlot::Secondary, + base_dmg: 48.0, + base_acc: 51.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 20, + rate_of_fire: [3, 6], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const FIVESEVEN: WeaponDto = WeaponDto { + id: 18, + name: Cow::Borrowed("Fiveseven"), + cat: WeaponCategory::Pistol, + kind: WeaponSlot::Secondary, + base_dmg: 52.0, + base_acc: 49.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 20, + rate_of_fire: [6, 7], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const MAGNUM: WeaponDto = WeaponDto { + id: 19, + name: Cow::Borrowed("Magnum"), + cat: WeaponCategory::Pistol, + kind: WeaponSlot::Secondary, + base_dmg: 55.0, + base_acc: 38.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 6, + rate_of_fire: [1, 2], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const DESERT_EAGLE: WeaponDto = WeaponDto { + id: 20, + name: Cow::Borrowed("Desert Eagle"), + cat: WeaponCategory::Pistol, + kind: WeaponSlot::Secondary, + base_dmg: 59.0, + base_acc: 36.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 8, + rate_of_fire: [2, 3], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const DUAL_92G_BERETTAS: WeaponDto = WeaponDto { + id: 21, + name: Cow::Borrowed("Dual 92G Berettas"), + cat: WeaponCategory::Pistol, + kind: WeaponSlot::Secondary, + base_dmg: 64.0, + base_acc: 30.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 46, + rate_of_fire: [5, 15], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const SAWED_OFF_SHOTGUN: WeaponDto = WeaponDto { + id: 22, + name: Cow::Borrowed("Sawed-Off Shotgun"), + cat: WeaponCategory::Shotgun, + kind: WeaponSlot::Primary, + base_dmg: 41.0, + base_acc: 63.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 2, + rate_of_fire: [1, 2], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::SkeetChoke, + WeaponMod::ImprovedChoke, + WeaponMod::FullChoke, + WeaponMod::RecoilPad, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const BENELLI_M1_TACTICAL: WeaponDto = WeaponDto { + id: 23, + name: Cow::Borrowed("Benelli M1 Tactical"), + cat: WeaponCategory::Shotgun, + kind: WeaponSlot::Primary, + base_dmg: 39.0, + base_acc: 65.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 7, + rate_of_fire: [2, 3], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::SkeetChoke, + WeaponMod::ImprovedChoke, + WeaponMod::FullChoke, + WeaponMod::RecoilPad, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const MP5_NAVY: WeaponDto = WeaponDto { + id: 24, + name: Cow::Borrowed("MP5 Navy"), + cat: WeaponCategory::Smg, + kind: WeaponSlot::Primary, + base_dmg: 45.0, + base_acc: 51.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 30, + rate_of_fire: [5, 8], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const P90: WeaponDto = WeaponDto { + id: 25, + name: Cow::Borrowed("P90"), + cat: WeaponCategory::Smg, + kind: WeaponSlot::Primary, + base_dmg: 48.0, + base_acc: 51.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 50, + rate_of_fire: [10, 30], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const AK_47: WeaponDto = WeaponDto { + id: 26, + name: Cow::Borrowed("AK-47"), + cat: WeaponCategory::Rifle, + kind: WeaponSlot::Primary, + base_dmg: 56.0, + base_acc: 52.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 30, + rate_of_fire: [4, 8], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::Bipod, + WeaponMod::Tripod, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::RecoilPad, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const M4A1_COLT_CARBINE: WeaponDto = WeaponDto { + id: 27, + name: Cow::Borrowed("M4A1 Colt Carbine"), + cat: WeaponCategory::Rifle, + kind: WeaponSlot::Primary, + base_dmg: 55.0, + base_acc: 47.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 30, + rate_of_fire: [4, 9], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::Bipod, + WeaponMod::Tripod, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::RecoilPad, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const BENELLI_M4_SUPER: WeaponDto = WeaponDto { + id: 28, + name: Cow::Borrowed("Benelli M4 Super"), + cat: WeaponCategory::Shotgun, + kind: WeaponSlot::Primary, + base_dmg: 59.0, + base_acc: 55.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 7, + rate_of_fire: [2, 3], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::SkeetChoke, + WeaponMod::ImprovedChoke, + WeaponMod::FullChoke, + WeaponMod::RecoilPad, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const M16_A2_RIFLE: WeaponDto = WeaponDto { + id: 29, + name: Cow::Borrowed("M16 A2 Rifle"), + cat: WeaponCategory::Rifle, + kind: WeaponSlot::Primary, + base_dmg: 61.0, + base_acc: 47.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 30, + rate_of_fire: [5, 9], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::Bipod, + WeaponMod::Tripod, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::RecoilPad, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const STEYR_AUG: WeaponDto = WeaponDto { + id: 30, + name: Cow::Borrowed("Steyr AUG"), + cat: WeaponCategory::Rifle, + kind: WeaponSlot::Primary, + base_dmg: 64.0, + base_acc: 45.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 30, + rate_of_fire: [5, 8], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::Bipod, + WeaponMod::Tripod, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::RecoilPad, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const M249_SAW: WeaponDto = WeaponDto { + id: 31, + name: Cow::Borrowed("M249 SAW"), + cat: WeaponCategory::MachineGun, + kind: WeaponSlot::Primary, + base_dmg: 67.0, + base_acc: 41.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 100, + rate_of_fire: [15, 25], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::Bipod, + WeaponMod::Tripod, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::RecoilPad, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + ]), + experience: None, + japanese: false, + }; + + pub const MINIGUN: WeaponDto = WeaponDto { + id: 63, + name: Cow::Borrowed("Minigun"), + cat: WeaponCategory::MachineGun, + kind: WeaponSlot::Primary, + base_dmg: 72.0, + base_acc: 28.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 200, + rate_of_fire: [20, 30], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::Bipod, + WeaponMod::Tripod, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::RecoilPad, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + ]), + experience: None, + japanese: false, + }; + + pub const SNOW_CANNON: WeaponDto = WeaponDto { + id: 76, + name: Cow::Borrowed("Snow Cannon"), + cat: WeaponCategory::HeavyArtillery, + kind: WeaponSlot::Primary, + base_dmg: 52.0, + base_acc: 24.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 950, + rate_of_fire: [3, 10], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::Bipod, + WeaponMod::Tripod, + ]), + experience: None, + japanese: false, + }; + + pub const NEUTRILUX_2000: WeaponDto = WeaponDto { + id: 98, + name: Cow::Borrowed("Neutrilux 2000"), + cat: WeaponCategory::MachineGun, + kind: WeaponSlot::Primary, + base_dmg: 59.0, + base_acc: 24.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 1000, + rate_of_fire: [233, 533], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::Bipod, + WeaponMod::Tripod, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::RecoilPad, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + ]), + experience: None, + japanese: false, + }; + + pub const SPRINGFIELD_1911: WeaponDto = WeaponDto { + id: 99, + name: Cow::Borrowed("Springfield 1911"), + cat: WeaponCategory::Pistol, + kind: WeaponSlot::Secondary, + base_dmg: 33.0, + base_acc: 57.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 8, + rate_of_fire: [2, 3], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const EGG_PROPELLED_LAUNCHER: WeaponDto = WeaponDto { + id: 100, + name: Cow::Borrowed("Egg Propelled Launcher"), + cat: WeaponCategory::HeavyArtillery, + kind: WeaponSlot::Primary, + base_dmg: 64.0, + base_acc: 24.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 1000, + rate_of_fire: [255, 555], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::Bipod, + WeaponMod::Tripod, + ]), + experience: None, + japanese: false, + }; + + pub const _9MM_UZI: WeaponDto = WeaponDto { + id: 108, + name: Cow::Borrowed("9mm Uzi"), + cat: WeaponCategory::Smg, + kind: WeaponSlot::Primary, + base_dmg: 65.0, + base_acc: 43.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 30, + rate_of_fire: [15, 25], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const RPG_LAUNCHER: WeaponDto = WeaponDto { + id: 109, + name: Cow::Borrowed("RPG Launcher"), + cat: WeaponCategory::HeavyArtillery, + kind: WeaponSlot::Secondary, + base_dmg: 77.0, + base_acc: 39.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 1, + rate_of_fire: [1, 1], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::Bipod, + WeaponMod::Tripod, + ]), + experience: None, + japanese: false, + }; + + pub const LEATHER_BULLWHIP: WeaponDto = WeaponDto { + id: 110, + name: Cow::Borrowed("Leather Bullwhip"), + cat: WeaponCategory::Slashing, + kind: WeaponSlot::Melee, + base_dmg: 27.0, + base_acc: 52.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const NINJA_CLAWS: WeaponDto = WeaponDto { + id: 111, + name: Cow::Borrowed("Ninja Claws"), + cat: WeaponCategory::Piercing, + kind: WeaponSlot::Melee, + base_dmg: 39.0, + base_acc: 51.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const YASUKUNI_SWORD: WeaponDto = WeaponDto { + id: 146, + name: Cow::Borrowed("Yasukuni Sword"), + cat: WeaponCategory::Slashing, + kind: WeaponSlot::Melee, + base_dmg: 65.0, + base_acc: 49.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: true, + }; + + pub const RUSTY_SWORD: WeaponDto = WeaponDto { + id: 147, + name: Cow::Borrowed("Rusty Sword"), + cat: WeaponCategory::Slashing, + kind: WeaponSlot::Melee, + base_dmg: 22.0, + base_acc: 15.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const WAND_OF_DESTRUCTION: WeaponDto = WeaponDto { + id: 170, + name: Cow::Borrowed("Wand of Destruction"), + cat: WeaponCategory::Piercing, + kind: WeaponSlot::Melee, + base_dmg: 60.0, + base_acc: 24.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const BUTTERFLY_KNIFE: WeaponDto = WeaponDto { + id: 173, + name: Cow::Borrowed("Butterfly Knife"), + cat: WeaponCategory::Piercing, + kind: WeaponSlot::Melee, + base_dmg: 24.0, + base_acc: 55.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const XM8_RIFLE: WeaponDto = WeaponDto { + id: 174, + name: Cow::Borrowed("XM8 Rifle"), + cat: WeaponCategory::Rifle, + kind: WeaponSlot::Primary, + base_dmg: 50.0, + base_acc: 56.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 30, + rate_of_fire: [5, 20], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::Bipod, + WeaponMod::Tripod, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::RecoilPad, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const TASER: WeaponDto = WeaponDto { + id: 175, + name: Cow::Borrowed("Taser"), + cat: WeaponCategory::Mechanical, + kind: WeaponSlot::Secondary, + base_dmg: 1.0, + base_acc: 54.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 2, + rate_of_fire: [1, 1], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const COBRA_DERRINGER: WeaponDto = WeaponDto { + id: 177, + name: Cow::Borrowed("Cobra Derringer"), + cat: WeaponCategory::Pistol, + kind: WeaponSlot::Secondary, + base_dmg: 61.0, + base_acc: 53.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 2, + rate_of_fire: [1, 2], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const S_W_REVOLVER: WeaponDto = WeaponDto { + id: 189, + name: Cow::Borrowed("S&W Revolver"), + cat: WeaponCategory::Pistol, + kind: WeaponSlot::Secondary, + base_dmg: 42.0, + base_acc: 54.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 6, + rate_of_fire: [1, 2], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const CLAYMORE_SWORD: WeaponDto = WeaponDto { + id: 217, + name: Cow::Borrowed("Claymore Sword"), + cat: WeaponCategory::Slashing, + kind: WeaponSlot::Melee, + base_dmg: 57.0, + base_acc: 49.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const CROSSBOW: WeaponDto = WeaponDto { + id: 218, + name: Cow::Borrowed("Crossbow"), + cat: WeaponCategory::Piercing, + kind: WeaponSlot::Secondary, + base_dmg: 35.0, + base_acc: 63.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 1, + rate_of_fire: [1, 1], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const ENFIELD_SA_80: WeaponDto = WeaponDto { + id: 219, + name: Cow::Borrowed("Enfield SA-80"), + cat: WeaponCategory::Rifle, + kind: WeaponSlot::Primary, + base_dmg: 63.0, + base_acc: 55.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 30, + rate_of_fire: [2, 5], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::Bipod, + WeaponMod::Tripod, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::RecoilPad, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const GRENADE: WeaponDto = WeaponDto { + id: 220, + name: Cow::Borrowed("Grenade"), + cat: WeaponCategory::Temporary, + kind: WeaponSlot::Temporary, + base_dmg: 86.0, + base_acc: 106.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const STICK_GRENADE: WeaponDto = WeaponDto { + id: 221, + name: Cow::Borrowed("Stick Grenade"), + cat: WeaponCategory::Temporary, + kind: WeaponSlot::Temporary, + base_dmg: 87.0, + base_acc: 97.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const FLASH_GRENADE: WeaponDto = WeaponDto { + id: 222, + name: Cow::Borrowed("Flash Grenade"), + cat: WeaponCategory::Temporary, + kind: WeaponSlot::Temporary, + base_dmg: 0.0, + base_acc: 200.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const JACKHAMMER: WeaponDto = WeaponDto { + id: 223, + name: Cow::Borrowed("Jackhammer"), + cat: WeaponCategory::Shotgun, + kind: WeaponSlot::Primary, + base_dmg: 69.0, + base_acc: 52.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 10, + rate_of_fire: [5, 10], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::SkeetChoke, + WeaponMod::ImprovedChoke, + WeaponMod::FullChoke, + WeaponMod::RecoilPad, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const SWISS_ARMY_KNIFE: WeaponDto = WeaponDto { + id: 224, + name: Cow::Borrowed("Swiss Army Knife"), + cat: WeaponCategory::Piercing, + kind: WeaponSlot::Melee, + base_dmg: 23.0, + base_acc: 52.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const MAG_7: WeaponDto = WeaponDto { + id: 225, + name: Cow::Borrowed("Mag 7"), + cat: WeaponCategory::Shotgun, + kind: WeaponSlot::Primary, + base_dmg: 56.0, + base_acc: 62.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 5, + rate_of_fire: [2, 4], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::SkeetChoke, + WeaponMod::ImprovedChoke, + WeaponMod::FullChoke, + WeaponMod::RecoilPad, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const SMOKE_GRENADE: WeaponDto = WeaponDto { + id: 226, + name: Cow::Borrowed("Smoke Grenade"), + cat: WeaponCategory::Temporary, + kind: WeaponSlot::Temporary, + base_dmg: 0.0, + base_acc: 200.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const SPEAR: WeaponDto = WeaponDto { + id: 227, + name: Cow::Borrowed("Spear"), + cat: WeaponCategory::Piercing, + kind: WeaponSlot::Melee, + base_dmg: 38.0, + base_acc: 48.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const VEKTOR_CR_21: WeaponDto = WeaponDto { + id: 228, + name: Cow::Borrowed("Vektor CR-21"), + cat: WeaponCategory::Rifle, + kind: WeaponSlot::Primary, + base_dmg: 50.0, + base_acc: 48.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 20, + rate_of_fire: [7, 8], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::Bipod, + WeaponMod::Tripod, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::RecoilPad, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const CLAYMORE_MINE: WeaponDto = WeaponDto { + id: 229, + name: Cow::Borrowed("Claymore Mine"), + cat: WeaponCategory::Temporary, + kind: WeaponSlot::Temporary, + base_dmg: 83.0, + base_acc: 27.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const FLARE_GUN: WeaponDto = WeaponDto { + id: 230, + name: Cow::Borrowed("Flare Gun"), + cat: WeaponCategory::Pistol, + kind: WeaponSlot::Secondary, + base_dmg: 18.0, + base_acc: 22.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 1, + rate_of_fire: [1, 1], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const HECKLER_KOCH_SL8: WeaponDto = WeaponDto { + id: 231, + name: Cow::Borrowed("Heckler & Koch SL8"), + cat: WeaponCategory::Rifle, + kind: WeaponSlot::Primary, + base_dmg: 60.0, + base_acc: 46.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 10, + rate_of_fire: [2, 9], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::Bipod, + WeaponMod::Tripod, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::RecoilPad, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const SIG_550: WeaponDto = WeaponDto { + id: 232, + name: Cow::Borrowed("SIG 550"), + cat: WeaponCategory::Rifle, + kind: WeaponSlot::Primary, + base_dmg: 62.0, + base_acc: 50.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 20, + rate_of_fire: [4, 7], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::Bipod, + WeaponMod::Tripod, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::RecoilPad, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const BT_MP9: WeaponDto = WeaponDto { + id: 233, + name: Cow::Borrowed("BT MP9"), + cat: WeaponCategory::Smg, + kind: WeaponSlot::Secondary, + base_dmg: 61.0, + base_acc: 55.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 30, + rate_of_fire: [2, 25], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const CHAIN_WHIP: WeaponDto = WeaponDto { + id: 234, + name: Cow::Borrowed("Chain Whip"), + cat: WeaponCategory::Slashing, + kind: WeaponSlot::Melee, + base_dmg: 31.0, + base_acc: 52.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const WOODEN_NUNCHAKU: WeaponDto = WeaponDto { + id: 235, + name: Cow::Borrowed("Wooden Nunchaku"), + cat: WeaponCategory::Clubbing, + kind: WeaponSlot::Melee, + base_dmg: 22.0, + base_acc: 59.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const KAMA: WeaponDto = WeaponDto { + id: 236, + name: Cow::Borrowed("Kama"), + cat: WeaponCategory::Slashing, + kind: WeaponSlot::Melee, + base_dmg: 35.0, + base_acc: 55.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: true, + }; + + pub const KODACHI: WeaponDto = WeaponDto { + id: 237, + name: Cow::Borrowed("Kodachi"), + cat: WeaponCategory::Slashing, + kind: WeaponSlot::Melee, + base_dmg: 62.0, + base_acc: 56.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: true, + }; + + pub const SAI: WeaponDto = WeaponDto { + id: 238, + name: Cow::Borrowed("Sai"), + cat: WeaponCategory::Piercing, + kind: WeaponSlot::Melee, + base_dmg: 29.0, + base_acc: 52.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: true, + }; + + pub const NINJA_STAR: WeaponDto = WeaponDto { + id: 239, + name: Cow::Borrowed("Ninja Star"), + cat: WeaponCategory::Temporary, + kind: WeaponSlot::Temporary, + base_dmg: 67.0, + base_acc: 14.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const TYPE_98_ANTI_TANK: WeaponDto = WeaponDto { + id: 240, + name: Cow::Borrowed("Type 98 Anti Tank"), + cat: WeaponCategory::HeavyArtillery, + kind: WeaponSlot::Secondary, + base_dmg: 78.0, + base_acc: 25.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 1, + rate_of_fire: [1, 1], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::Bipod, + WeaponMod::Tripod, + ]), + experience: None, + japanese: false, + }; + + pub const BUSHMASTER_CARBON_15: WeaponDto = WeaponDto { + id: 241, + name: Cow::Borrowed("Bushmaster Carbon 15"), + cat: WeaponCategory::Smg, + kind: WeaponSlot::Primary, + base_dmg: 50.0, + base_acc: 57.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 30, + rate_of_fire: [2, 28], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const HEG: WeaponDto = WeaponDto { + id: 242, + name: Cow::Borrowed("HEG"), + cat: WeaponCategory::Temporary, + kind: WeaponSlot::Temporary, + base_dmg: 90.0, + base_acc: 116.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const TAURUS: WeaponDto = WeaponDto { + id: 243, + name: Cow::Borrowed("Taurus"), + cat: WeaponCategory::Pistol, + kind: WeaponSlot::Secondary, + base_dmg: 30.0, + base_acc: 57.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 13, + rate_of_fire: [1, 12], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const BLOWGUN: WeaponDto = WeaponDto { + id: 244, + name: Cow::Borrowed("Blowgun"), + cat: WeaponCategory::Piercing, + kind: WeaponSlot::Secondary, + base_dmg: 15.0, + base_acc: 39.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 1, + rate_of_fire: [1, 1], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const BO_STAFF: WeaponDto = WeaponDto { + id: 245, + name: Cow::Borrowed("Bo Staff"), + cat: WeaponCategory::Clubbing, + kind: WeaponSlot::Melee, + base_dmg: 13.0, + base_acc: 55.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const FIREWORKS: WeaponDto = WeaponDto { + id: 246, + name: Cow::Borrowed("Fireworks"), + cat: WeaponCategory::Temporary, + kind: WeaponSlot::Temporary, + base_dmg: 45.0, + base_acc: 34.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const KATANA: WeaponDto = WeaponDto { + id: 247, + name: Cow::Borrowed("Katana"), + cat: WeaponCategory::Slashing, + kind: WeaponSlot::Melee, + base_dmg: 52.0, + base_acc: 55.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: true, + }; + + pub const QSZ_92: WeaponDto = WeaponDto { + id: 248, + name: Cow::Borrowed("Qsz-92"), + cat: WeaponCategory::Pistol, + kind: WeaponSlot::Secondary, + base_dmg: 62.0, + base_acc: 53.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 15, + rate_of_fire: [2, 12], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const SKS_CARBINE: WeaponDto = WeaponDto { + id: 249, + name: Cow::Borrowed("SKS Carbine"), + cat: WeaponCategory::Rifle, + kind: WeaponSlot::Primary, + base_dmg: 46.0, + base_acc: 47.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 10, + rate_of_fire: [1, 8], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::Bipod, + WeaponMod::Tripod, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::RecoilPad, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const TWIN_TIGER_HOOKS: WeaponDto = WeaponDto { + id: 250, + name: Cow::Borrowed("Twin Tiger Hooks"), + cat: WeaponCategory::Piercing, + kind: WeaponSlot::Melee, + base_dmg: 50.0, + base_acc: 53.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const WUSHU_DOUBLE_AXES: WeaponDto = WeaponDto { + id: 251, + name: Cow::Borrowed("Wushu Double Axes"), + cat: WeaponCategory::Clubbing, + kind: WeaponSlot::Melee, + base_dmg: 53.0, + base_acc: 51.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const ITHACA_37: WeaponDto = WeaponDto { + id: 252, + name: Cow::Borrowed("Ithaca 37"), + cat: WeaponCategory::Shotgun, + kind: WeaponSlot::Primary, + base_dmg: 49.0, + base_acc: 62.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 4, + rate_of_fire: [1, 4], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::SkeetChoke, + WeaponMod::ImprovedChoke, + WeaponMod::FullChoke, + WeaponMod::RecoilPad, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const LORCIN_380: WeaponDto = WeaponDto { + id: 253, + name: Cow::Borrowed("Lorcin 380"), + cat: WeaponCategory::Pistol, + kind: WeaponSlot::Secondary, + base_dmg: 27.0, + base_acc: 41.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 6, + rate_of_fire: [1, 5], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const S_W_M29: WeaponDto = WeaponDto { + id: 254, + name: Cow::Borrowed("S&W M29"), + cat: WeaponCategory::Pistol, + kind: WeaponSlot::Secondary, + base_dmg: 47.0, + base_acc: 52.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 6, + rate_of_fire: [2, 5], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const FLAMETHROWER: WeaponDto = WeaponDto { + id: 255, + name: Cow::Borrowed("Flamethrower"), + cat: WeaponCategory::HeavyArtillery, + kind: WeaponSlot::Secondary, + base_dmg: 67.0, + base_acc: 39.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 1, + rate_of_fire: [1, 1], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::Bipod, + WeaponMod::Tripod, + ]), + experience: None, + japanese: false, + }; + + pub const TEAR_GAS: WeaponDto = WeaponDto { + id: 256, + name: Cow::Borrowed("Tear Gas"), + cat: WeaponCategory::Temporary, + kind: WeaponSlot::Temporary, + base_dmg: 0.0, + base_acc: 200.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const THROWING_KNIFE: WeaponDto = WeaponDto { + id: 257, + name: Cow::Borrowed("Throwing Knife"), + cat: WeaponCategory::Temporary, + kind: WeaponSlot::Temporary, + base_dmg: 69.0, + base_acc: 10.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const DUAL_AXES: WeaponDto = WeaponDto { + id: 289, + name: Cow::Borrowed("Dual Axes"), + cat: WeaponCategory::Clubbing, + kind: WeaponSlot::Melee, + base_dmg: 70.0, + base_acc: 54.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const DUAL_HAMMERS: WeaponDto = WeaponDto { + id: 290, + name: Cow::Borrowed("Dual Hammers"), + cat: WeaponCategory::Clubbing, + kind: WeaponSlot::Melee, + base_dmg: 70.0, + base_acc: 54.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const DUAL_SCIMITARS: WeaponDto = WeaponDto { + id: 291, + name: Cow::Borrowed("Dual Scimitars"), + cat: WeaponCategory::Slashing, + kind: WeaponSlot::Melee, + base_dmg: 70.0, + base_acc: 54.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const DUAL_SAMURAI_SWORDS: WeaponDto = WeaponDto { + id: 292, + name: Cow::Borrowed("Dual Samurai Swords"), + cat: WeaponCategory::Slashing, + kind: WeaponSlot::Melee, + base_dmg: 70.0, + base_acc: 54.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: true, + }; + + pub const PAIR_OF_HIGH_HEELS: WeaponDto = WeaponDto { + id: 346, + name: Cow::Borrowed("Pair of High Heels"), + cat: WeaponCategory::Piercing, + kind: WeaponSlot::Melee, + base_dmg: 40.0, + base_acc: 63.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const FINE_CHISEL: WeaponDto = WeaponDto { + id: 359, + name: Cow::Borrowed("Fine Chisel"), + cat: WeaponCategory::Piercing, + kind: WeaponSlot::Melee, + base_dmg: 16.0, + base_acc: 50.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const IVORY_WALKING_CANE: WeaponDto = WeaponDto { + id: 360, + name: Cow::Borrowed("Ivory Walking Cane"), + cat: WeaponCategory::Clubbing, + kind: WeaponSlot::Melee, + base_dmg: 53.0, + base_acc: 57.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const GOLD_PLATED_AK_47: WeaponDto = WeaponDto { + id: 382, + name: Cow::Borrowed("Gold Plated AK-47"), + cat: WeaponCategory::Rifle, + kind: WeaponSlot::Primary, + base_dmg: 75.0, + base_acc: 62.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 45, + rate_of_fire: [4, 6], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::Bipod, + WeaponMod::Tripod, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::RecoilPad, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const HANDBAG: WeaponDto = WeaponDto { + id: 387, + name: Cow::Borrowed("Handbag"), + cat: WeaponCategory::Clubbing, + kind: WeaponSlot::Melee, + base_dmg: 67.0, + base_acc: 63.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const PINK_MAC_10: WeaponDto = WeaponDto { + id: 388, + name: Cow::Borrowed("Pink Mac-10"), + cat: WeaponCategory::Smg, + kind: WeaponSlot::Secondary, + base_dmg: 74.0, + base_acc: 45.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 120, + rate_of_fire: [10, 15], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const MACANA: WeaponDto = WeaponDto { + id: 391, + name: Cow::Borrowed("Macana"), + cat: WeaponCategory::Piercing, + kind: WeaponSlot::Melee, + base_dmg: 57.0, + base_acc: 65.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const PEPPER_SPRAY: WeaponDto = WeaponDto { + id: 392, + name: Cow::Borrowed("Pepper Spray"), + cat: WeaponCategory::Temporary, + kind: WeaponSlot::Temporary, + base_dmg: 0.0, + base_acc: 200.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const SLINGSHOT: WeaponDto = WeaponDto { + id: 393, + name: Cow::Borrowed("Slingshot"), + cat: WeaponCategory::Clubbing, + kind: WeaponSlot::Secondary, + base_dmg: 14.0, + base_acc: 54.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 1, + rate_of_fire: [1, 1], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const BRICK: WeaponDto = WeaponDto { + id: 394, + name: Cow::Borrowed("Brick"), + cat: WeaponCategory::Temporary, + kind: WeaponSlot::Temporary, + base_dmg: 28.0, + base_acc: 43.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const METAL_NUNCHAKU: WeaponDto = WeaponDto { + id: 395, + name: Cow::Borrowed("Metal Nunchaku"), + cat: WeaponCategory::Clubbing, + kind: WeaponSlot::Melee, + base_dmg: 61.0, + base_acc: 60.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const FLAIL: WeaponDto = WeaponDto { + id: 397, + name: Cow::Borrowed("Flail"), + cat: WeaponCategory::Clubbing, + kind: WeaponSlot::Melee, + base_dmg: 71.0, + base_acc: 28.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const SIG_552: WeaponDto = WeaponDto { + id: 398, + name: Cow::Borrowed("SIG 552"), + cat: WeaponCategory::Rifle, + kind: WeaponSlot::Primary, + base_dmg: 69.0, + base_acc: 50.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 30, + rate_of_fire: [4, 7], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::Bipod, + WeaponMod::Tripod, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::RecoilPad, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const ARMALITE_M_15A4: WeaponDto = WeaponDto { + id: 399, + name: Cow::Borrowed("ArmaLite M-15A4"), + cat: WeaponCategory::Rifle, + kind: WeaponSlot::Primary, + base_dmg: 68.0, + base_acc: 57.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 15, + rate_of_fire: [3, 5], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::Bipod, + WeaponMod::Tripod, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::RecoilPad, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const GUANDAO: WeaponDto = WeaponDto { + id: 400, + name: Cow::Borrowed("Guandao"), + cat: WeaponCategory::Slashing, + kind: WeaponSlot::Melee, + base_dmg: 63.0, + base_acc: 35.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const LEAD_PIPE: WeaponDto = WeaponDto { + id: 401, + name: Cow::Borrowed("Lead Pipe"), + cat: WeaponCategory::Clubbing, + kind: WeaponSlot::Melee, + base_dmg: 26.0, + base_acc: 33.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const ICE_PICK: WeaponDto = WeaponDto { + id: 402, + name: Cow::Borrowed("Ice Pick"), + cat: WeaponCategory::Piercing, + kind: WeaponSlot::Melee, + base_dmg: 51.0, + base_acc: 60.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const CRICKET_BAT: WeaponDto = WeaponDto { + id: 438, + name: Cow::Borrowed("Cricket Bat"), + cat: WeaponCategory::Clubbing, + kind: WeaponSlot::Melee, + base_dmg: 18.0, + base_acc: 42.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const FRYING_PAN: WeaponDto = WeaponDto { + id: 439, + name: Cow::Borrowed("Frying Pan"), + cat: WeaponCategory::Clubbing, + kind: WeaponSlot::Melee, + base_dmg: 19.0, + base_acc: 43.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const PILLOW: WeaponDto = WeaponDto { + id: 440, + name: Cow::Borrowed("Pillow"), + cat: WeaponCategory::Clubbing, + kind: WeaponSlot::Melee, + base_dmg: 1.0, + base_acc: 63.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const EPINEPHRINE: WeaponDto = WeaponDto { + id: 463, + name: Cow::Borrowed("Epinephrine"), + cat: WeaponCategory::Temporary, + kind: WeaponSlot::Temporary, + base_dmg: 0.0, + base_acc: 0.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const MELATONIN: WeaponDto = WeaponDto { + id: 464, + name: Cow::Borrowed("Melatonin"), + cat: WeaponCategory::Temporary, + kind: WeaponSlot::Temporary, + base_dmg: 0.0, + base_acc: 0.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const SEROTONIN: WeaponDto = WeaponDto { + id: 465, + name: Cow::Borrowed("Serotonin"), + cat: WeaponCategory::Temporary, + kind: WeaponSlot::Temporary, + base_dmg: 0.0, + base_acc: 0.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const MP5K: WeaponDto = WeaponDto { + id: 483, + name: Cow::Borrowed("MP5k"), + cat: WeaponCategory::Smg, + kind: WeaponSlot::Secondary, + base_dmg: 42.0, + base_acc: 52.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 15, + rate_of_fire: [5, 7], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const AK74U: WeaponDto = WeaponDto { + id: 484, + name: Cow::Borrowed("AK74U"), + cat: WeaponCategory::Smg, + kind: WeaponSlot::Primary, + base_dmg: 46.0, + base_acc: 41.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 30, + rate_of_fire: [4, 6], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const SKORPION: WeaponDto = WeaponDto { + id: 485, + name: Cow::Borrowed("Skorpion"), + cat: WeaponCategory::Smg, + kind: WeaponSlot::Secondary, + base_dmg: 40.0, + base_acc: 54.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 20, + rate_of_fire: [3, 5], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const TMP: WeaponDto = WeaponDto { + id: 486, + name: Cow::Borrowed("TMP"), + cat: WeaponCategory::Smg, + kind: WeaponSlot::Secondary, + base_dmg: 38.0, + base_acc: 45.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 15, + rate_of_fire: [3, 6], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const THOMPSON: WeaponDto = WeaponDto { + id: 487, + name: Cow::Borrowed("Thompson"), + cat: WeaponCategory::Smg, + kind: WeaponSlot::Primary, + base_dmg: 39.0, + base_acc: 43.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 20, + rate_of_fire: [3, 5], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const MP_40: WeaponDto = WeaponDto { + id: 488, + name: Cow::Borrowed("MP 40"), + cat: WeaponCategory::Smg, + kind: WeaponSlot::Primary, + base_dmg: 37.0, + base_acc: 41.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 32, + rate_of_fire: [3, 5], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const LUGER: WeaponDto = WeaponDto { + id: 489, + name: Cow::Borrowed("Luger"), + cat: WeaponCategory::Pistol, + kind: WeaponSlot::Secondary, + base_dmg: 35.0, + base_acc: 48.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 8, + rate_of_fire: [1, 3], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const BLUNDERBUSS: WeaponDto = WeaponDto { + id: 490, + name: Cow::Borrowed("Blunderbuss"), + cat: WeaponCategory::Shotgun, + kind: WeaponSlot::Secondary, + base_dmg: 46.0, + base_acc: 24.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 1, + rate_of_fire: [1, 1], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::SkeetChoke, + WeaponMod::ImprovedChoke, + WeaponMod::FullChoke, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const BLOOD_SPATTERED_SICKLE: WeaponDto = WeaponDto { + id: 539, + name: Cow::Borrowed("Blood Spattered Sickle"), + cat: WeaponCategory::Slashing, + kind: WeaponSlot::Melee, + base_dmg: 36.0, + base_acc: 55.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const DUAL_TMPS: WeaponDto = WeaponDto { + id: 545, + name: Cow::Borrowed("Dual TMPs"), + cat: WeaponCategory::Smg, + kind: WeaponSlot::Primary, + base_dmg: 79.0, + base_acc: 38.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 30, + rate_of_fire: [15, 25], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const DUAL_BUSHMASTERS: WeaponDto = WeaponDto { + id: 546, + name: Cow::Borrowed("Dual Bushmasters"), + cat: WeaponCategory::Smg, + kind: WeaponSlot::Primary, + base_dmg: 76.0, + base_acc: 47.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 60, + rate_of_fire: [4, 30], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const DUAL_MP5S: WeaponDto = WeaponDto { + id: 547, + name: Cow::Borrowed("Dual MP5s"), + cat: WeaponCategory::Smg, + kind: WeaponSlot::Primary, + base_dmg: 78.0, + base_acc: 45.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 60, + rate_of_fire: [10, 16], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const DUAL_P90S: WeaponDto = WeaponDto { + id: 548, + name: Cow::Borrowed("Dual P90s"), + cat: WeaponCategory::Smg, + kind: WeaponSlot::Primary, + base_dmg: 77.0, + base_acc: 45.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 100, + rate_of_fire: [10, 20], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const DUAL_UZIS: WeaponDto = WeaponDto { + id: 549, + name: Cow::Borrowed("Dual Uzis"), + cat: WeaponCategory::Smg, + kind: WeaponSlot::Primary, + base_dmg: 80.0, + base_acc: 36.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 60, + rate_of_fire: [15, 25], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const BOOK: WeaponDto = WeaponDto { + id: 581, + name: Cow::Borrowed("Book"), + cat: WeaponCategory::Temporary, + kind: WeaponSlot::Temporary, + base_dmg: 10.0, + base_acc: 88.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const GOLDEN_BROOMSTICK: WeaponDto = WeaponDto { + id: 599, + name: Cow::Borrowed("Golden Broomstick"), + cat: WeaponCategory::Clubbing, + kind: WeaponSlot::Melee, + base_dmg: 60.0, + base_acc: 48.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const DEVIL_S_PITCHFORK: WeaponDto = WeaponDto { + id: 600, + name: Cow::Borrowed("Devil's Pitchfork"), + cat: WeaponCategory::Piercing, + kind: WeaponSlot::Melee, + base_dmg: 61.0, + base_acc: 41.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const PAIR_OF_ICE_SKATES: WeaponDto = WeaponDto { + id: 604, + name: Cow::Borrowed("Pair of Ice Skates"), + cat: WeaponCategory::Slashing, + kind: WeaponSlot::Melee, + base_dmg: 43.0, + base_acc: 45.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const DIAMOND_ICICLE: WeaponDto = WeaponDto { + id: 605, + name: Cow::Borrowed("Diamond Icicle"), + cat: WeaponCategory::Piercing, + kind: WeaponSlot::Melee, + base_dmg: 45.0, + base_acc: 48.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const SNOWBALL: WeaponDto = WeaponDto { + id: 611, + name: Cow::Borrowed("Snowball"), + cat: WeaponCategory::Temporary, + kind: WeaponSlot::Temporary, + base_dmg: 5.0, + base_acc: 50.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const TAVOR_TAR_21: WeaponDto = WeaponDto { + id: 612, + name: Cow::Borrowed("Tavor TAR-21"), + cat: WeaponCategory::Rifle, + kind: WeaponSlot::Primary, + base_dmg: 65.0, + base_acc: 52.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 30, + rate_of_fire: [4, 8], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::Bipod, + WeaponMod::Tripod, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::RecoilPad, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const HARPOON: WeaponDto = WeaponDto { + id: 613, + name: Cow::Borrowed("Harpoon"), + cat: WeaponCategory::Piercing, + kind: WeaponSlot::Secondary, + base_dmg: 47.0, + base_acc: 63.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 1, + rate_of_fire: [1, 1], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const DIAMOND_BLADED_KNIFE: WeaponDto = WeaponDto { + id: 614, + name: Cow::Borrowed("Diamond Bladed Knife"), + cat: WeaponCategory::Piercing, + kind: WeaponSlot::Melee, + base_dmg: 60.0, + base_acc: 62.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const NAVAL_CUTLASS: WeaponDto = WeaponDto { + id: 615, + name: Cow::Borrowed("Naval Cutlass"), + cat: WeaponCategory::Slashing, + kind: WeaponSlot::Melee, + base_dmg: 64.0, + base_acc: 52.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const TROUT: WeaponDto = WeaponDto { + id: 616, + name: Cow::Borrowed("Trout"), + cat: WeaponCategory::Temporary, + kind: WeaponSlot::Temporary, + base_dmg: 35.0, + base_acc: 90.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const PETRIFIED_HUMERUS: WeaponDto = WeaponDto { + id: 632, + name: Cow::Borrowed("Petrified Humerus"), + cat: WeaponCategory::Clubbing, + kind: WeaponSlot::Melee, + base_dmg: 48.0, + base_acc: 48.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const MOLOTOV_COCKTAIL: WeaponDto = WeaponDto { + id: 742, + name: Cow::Borrowed("Molotov Cocktail"), + cat: WeaponCategory::Temporary, + kind: WeaponSlot::Temporary, + base_dmg: 85.0, + base_acc: 78.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const PLASTIC_SWORD: WeaponDto = WeaponDto { + id: 790, + name: Cow::Borrowed("Plastic Sword"), + cat: WeaponCategory::Clubbing, + kind: WeaponSlot::Melee, + base_dmg: 5.0, + base_acc: 29.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const PENELOPE: WeaponDto = WeaponDto { + id: 792, + name: Cow::Borrowed("Penelope"), + cat: WeaponCategory::Clubbing, + kind: WeaponSlot::Melee, + base_dmg: 17.0, + base_acc: 57.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const DUKE_S_HAMMER: WeaponDto = WeaponDto { + id: 805, + name: Cow::Borrowed("Duke's Hammer"), + cat: WeaponCategory::Clubbing, + kind: WeaponSlot::Melee, + base_dmg: 18.0, + base_acc: 55.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const TYROSINE: WeaponDto = WeaponDto { + id: 814, + name: Cow::Borrowed("Tyrosine"), + cat: WeaponCategory::Temporary, + kind: WeaponSlot::Temporary, + base_dmg: 0.0, + base_acc: 0.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const NOCK_GUN: WeaponDto = WeaponDto { + id: 830, + name: Cow::Borrowed("Nock Gun"), + cat: WeaponCategory::Shotgun, + kind: WeaponSlot::Primary, + base_dmg: 95.0, + base_acc: 45.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 7, + rate_of_fire: [7, 7], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::SkeetChoke, + WeaponMod::ImprovedChoke, + WeaponMod::FullChoke, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const BERETTA_PICO: WeaponDto = WeaponDto { + id: 831, + name: Cow::Borrowed("Beretta Pico"), + cat: WeaponCategory::Pistol, + kind: WeaponSlot::Secondary, + base_dmg: 54.0, + base_acc: 53.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 6, + rate_of_fire: [1, 2], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const RIDING_CROP: WeaponDto = WeaponDto { + id: 832, + name: Cow::Borrowed("Riding Crop"), + cat: WeaponCategory::Slashing, + kind: WeaponSlot::Melee, + base_dmg: 21.0, + base_acc: 54.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const RHEINMETALL_MG_3: WeaponDto = WeaponDto { + id: 837, + name: Cow::Borrowed("Rheinmetall MG 3"), + cat: WeaponCategory::MachineGun, + kind: WeaponSlot::Primary, + base_dmg: 66.0, + base_acc: 36.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 100, + rate_of_fire: [20, 30], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::Bipod, + WeaponMod::Tripod, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::RecoilPad, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + ]), + experience: None, + japanese: false, + }; + + pub const HOMEMADE_POCKET_SHOTGUN: WeaponDto = WeaponDto { + id: 838, + name: Cow::Borrowed("Homemade Pocket Shotgun"), + cat: WeaponCategory::Shotgun, + kind: WeaponSlot::Secondary, + base_dmg: 63.0, + base_acc: 60.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 1, + rate_of_fire: [1, 1], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::SkeetChoke, + WeaponMod::ImprovedChoke, + WeaponMod::FullChoke, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const MADBALL: WeaponDto = WeaponDto { + id: 839, + name: Cow::Borrowed("Madball"), + cat: WeaponCategory::Clubbing, + kind: WeaponSlot::Melee, + base_dmg: 60.0, + base_acc: 45.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const NAIL_BOMB: WeaponDto = WeaponDto { + id: 840, + name: Cow::Borrowed("Nail Bomb"), + cat: WeaponCategory::Temporary, + kind: WeaponSlot::Temporary, + base_dmg: 99.0, + base_acc: 106.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const TRANQUILIZER_GUN: WeaponDto = WeaponDto { + id: 844, + name: Cow::Borrowed("Tranquilizer Gun"), + cat: WeaponCategory::Piercing, + kind: WeaponSlot::Secondary, + base_dmg: 15.0, + base_acc: 45.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 1, + rate_of_fire: [1, 1], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const BOLT_GUN: WeaponDto = WeaponDto { + id: 845, + name: Cow::Borrowed("Bolt Gun"), + cat: WeaponCategory::Mechanical, + kind: WeaponSlot::Melee, + base_dmg: 58.0, + base_acc: 53.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + pub const SCALPEL: WeaponDto = WeaponDto { + id: 846, + name: Cow::Borrowed("Scalpel"), + cat: WeaponCategory::Piercing, + kind: WeaponSlot::Melee, + base_dmg: 56.0, + base_acc: 52.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const NERVE_GAS: WeaponDto = WeaponDto { + id: 847, + name: Cow::Borrowed("Nerve Gas"), + cat: WeaponCategory::Temporary, + kind: WeaponSlot::Temporary, + base_dmg: 37.0, + base_acc: 97.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + pub const SLEDGEHAMMER: WeaponDto = WeaponDto { + id: 850, + name: Cow::Borrowed("Sledgehammer"), + cat: WeaponCategory::Clubbing, + kind: WeaponSlot::Melee, + base_dmg: 58.0, + base_acc: 50.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + const BUG_SWATTER: WeaponDto = WeaponDto { + id: 871, + name: Cow::Borrowed("Bug Swatter"), + cat: WeaponCategory::Slashing, + kind: WeaponSlot::Melee, + base_dmg: 5.0, + base_acc: 59.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + const PROTOTYPE: WeaponDto = WeaponDto { + id: 874, + name: Cow::Borrowed("Prototype"), + cat: WeaponCategory::Rifle, + kind: WeaponSlot::Secondary, + base_dmg: 68.0, + base_acc: 57.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 15, + rate_of_fire: [3, 5], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::SmallSuppressor, + WeaponMod::StandardSuppressor, + WeaponMod::LargeSuppressor, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::Bipod, + WeaponMod::Tripod, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::RecoilPad, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + WeaponMod::SmallLight, + WeaponMod::PrecisionLight, + WeaponMod::TacticalIlluminator, + ]), + experience: None, + japanese: false, + }; + + const CONCUSSION_GRENADE: WeaponDto = WeaponDto { + id: 1042, + name: Cow::Borrowed("Concussion Grenade"), + cat: WeaponCategory::Temporary, + kind: WeaponSlot::Temporary, + base_dmg: 0.0, + base_acc: 200.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + const BREAD_KNIFE: WeaponDto = WeaponDto { + id: 1053, + name: Cow::Borrowed("Bread Knife"), + cat: WeaponCategory::Slashing, + kind: WeaponSlot::Melee, + base_dmg: 41.0, + base_acc: 65.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + const SEMTEX: WeaponDto = WeaponDto { + id: 1054, + name: Cow::Borrowed("Semtex"), + cat: WeaponCategory::Temporary, + kind: WeaponSlot::Temporary, + base_dmg: 81.0, + base_acc: 200.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + const POISON_UMBRELLA: WeaponDto = WeaponDto { + id: 1055, + name: Cow::Borrowed("Poison Umbrella"), + cat: WeaponCategory::Piercing, + kind: WeaponSlot::Melee, + base_dmg: 35.0, + base_acc: 49.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + const MILLWALL_BRICK: WeaponDto = WeaponDto { + id: 1056, + name: Cow::Borrowed("Millwall Brick"), + cat: WeaponCategory::Clubbing, + kind: WeaponSlot::Melee, + base_dmg: 40.0, + base_acc: 47.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + const SMAW_LAUNCHER: WeaponDto = WeaponDto { + id: 1152, + name: Cow::Borrowed("SMAW Launcher"), + cat: WeaponCategory::HeavyArtillery, + kind: WeaponSlot::Secondary, + base_dmg: 76.0, + base_acc: 42.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 1, + rate_of_fire: [1, 1], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::Bipod, + WeaponMod::Tripod, + ]), + experience: None, + japanese: false, + }; + + const CHINA_LAKE: WeaponDto = WeaponDto { + id: 1153, + name: Cow::Borrowed("China Lake"), + cat: WeaponCategory::HeavyArtillery, + kind: WeaponSlot::Secondary, + base_dmg: 74.0, + base_acc: 44.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 3, + rate_of_fire: [1, 1], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::Bipod, + WeaponMod::Tripod, + ]), + experience: None, + japanese: false, + }; + + const MILKOR_MGL: WeaponDto = WeaponDto { + id: 1154, + name: Cow::Borrowed("Milkor MGL"), + cat: WeaponCategory::HeavyArtillery, + kind: WeaponSlot::Secondary, + base_dmg: 73.0, + base_acc: 40.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 6, + rate_of_fire: [1, 1], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::Bipod, + WeaponMod::Tripod, + ]), + experience: None, + japanese: false, + }; + + const PKM: WeaponDto = WeaponDto { + id: 1155, + name: Cow::Borrowed("PKM"), + cat: WeaponCategory::MachineGun, + kind: WeaponSlot::Primary, + base_dmg: 70.0, + base_acc: 45.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 50, + rate_of_fire: [9, 17], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::Bipod, + WeaponMod::Tripod, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::RecoilPad, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + ]), + experience: None, + japanese: false, + }; + + const NEGEV_NG_5: WeaponDto = WeaponDto { + id: 1156, + name: Cow::Borrowed("Negev NG-5"), + cat: WeaponCategory::MachineGun, + kind: WeaponSlot::Primary, + base_dmg: 68.0, + base_acc: 36.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 200, + rate_of_fire: [18, 27], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::Bipod, + WeaponMod::Tripod, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::RecoilPad, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + ]), + experience: None, + japanese: false, + }; + + const STONER_96: WeaponDto = WeaponDto { + id: 1157, + name: Cow::Borrowed("Stoner 96"), + cat: WeaponCategory::MachineGun, + kind: WeaponSlot::Primary, + base_dmg: 69.0, + base_acc: 49.0, + dmg: None, + acc: None, + ammo: Some(WeaponAmmo { + clip_size: 100, + rate_of_fire: [9, 14], + }), + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[ + WeaponMod::ReflexSight, + WeaponMod::HolographicSight, + WeaponMod::AcogSight, + WeaponMod::ThermalSight, + WeaponMod::Laser1mw, + WeaponMod::Laser5mw, + WeaponMod::Laser30mw, + WeaponMod::Laser100mw, + WeaponMod::ExtendedMags, + WeaponMod::HighCapacityMags, + WeaponMod::ExtraClip, + WeaponMod::ExtraClip2, + WeaponMod::Bipod, + WeaponMod::Tripod, + WeaponMod::AdjustableTrigger, + WeaponMod::HairTrigger, + WeaponMod::CustomGrip, + WeaponMod::RecoilPad, + WeaponMod::StandardBrake, + WeaponMod::HeavyDutyBrake, + WeaponMod::TacticalBrake, + ]), + experience: None, + japanese: false, + }; + + const MEAT_HOOK: WeaponDto = WeaponDto { + id: 1158, + name: Cow::Borrowed("Meat Hook"), + cat: WeaponCategory::Piercing, + kind: WeaponSlot::Melee, + base_dmg: 62.0, + base_acc: 39.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + const CLEAVER: WeaponDto = WeaponDto { + id: 1159, + name: Cow::Borrowed("Cleaver"), + cat: WeaponCategory::Slashing, + kind: WeaponSlot::Melee, + base_dmg: 51.0, + base_acc: 56.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + const CRYSTALLINE_FALCATA: WeaponDto = WeaponDto { + id: 1173, + name: Cow::Borrowed("Crystalline Falcata"), + cat: WeaponCategory::Slashing, + kind: WeaponSlot::Melee, + base_dmg: 37.0, + base_acc: 67.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + const STYGIAN_DARKNESS: WeaponDto = WeaponDto { + id: 1175, + name: Cow::Borrowed("Stygian Darkness"), + cat: WeaponCategory::Temporary, + kind: WeaponSlot::Temporary, + base_dmg: 0.0, + base_acc: 200.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + const PARTY_POPPER: WeaponDto = WeaponDto { + id: 1178, + name: Cow::Borrowed("Party Popper"), + cat: WeaponCategory::Temporary, + kind: WeaponSlot::Temporary, + base_dmg: 0.0, + base_acc: 200.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + const WRENCH: WeaponDto = WeaponDto { + id: 1205, + name: Cow::Borrowed("Wrench"), + cat: WeaponCategory::Temporary, + kind: WeaponSlot::Temporary, + base_dmg: 14.0, + base_acc: 53.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + const GOLF_CLUB: WeaponDto = WeaponDto { + id: 1231, + name: Cow::Borrowed("Golf Club"), + cat: WeaponCategory::Clubbing, + kind: WeaponSlot::Melee, + base_dmg: 29.0, + base_acc: 59.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + const BONE_SAW: WeaponDto = WeaponDto { + id: 1255, + name: Cow::Borrowed("Bone Saw"), + cat: WeaponCategory::Slashing, + kind: WeaponSlot::Melee, + base_dmg: 54.0, + base_acc: 52.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + const CATTLE_PROD: WeaponDto = WeaponDto { + id: 1257, + name: Cow::Borrowed("Cattle Prod"), + cat: WeaponCategory::Mechanical, + kind: WeaponSlot::Melee, + base_dmg: 1.0, + base_acc: 59.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + const GLITTER_BOMB: WeaponDto = WeaponDto { + id: 1294, + name: Cow::Borrowed("Glitter Bomb"), + cat: WeaponCategory::Temporary, + kind: WeaponSlot::Temporary, + base_dmg: 0.0, + base_acc: 200.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + const BAN_HAMMER: WeaponDto = WeaponDto { + id: 1296, + name: Cow::Borrowed("Ban Hammer"), + cat: WeaponCategory::Clubbing, + kind: WeaponSlot::Melee, + base_dmg: 27.0, + base_acc: 58.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + const BOLAS: WeaponDto = WeaponDto { + id: 1456, + name: Cow::Borrowed("Bolas"), + cat: WeaponCategory::Temporary, + kind: WeaponSlot::Temporary, + base_dmg: 0.0, + base_acc: 31.0, + dmg: None, + acc: None, + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + // NOTE: The accuracy value is taken from the attack page. The damage value here differs + // from the one in Proxima's simulator, but in some quick tests 10.0 proofed to be a better fit + // This might have changed in the weapon damage update + pub const FISTS: WeaponDto = WeaponDto { + id: -1, + name: Cow::Borrowed("Fists"), + cat: WeaponCategory::HandToHand, + kind: WeaponSlot::Fists, + base_dmg: 10.0, + dmg: Some(10.0), + base_acc: 50.0, + acc: Some(50.0), + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; + + // NOTE: The accuracy value is taken from the attack page. The damage value here differs + // from the one in Proxima's simulator, but in some quick tests 30.0 proofed to be a better fit + // This might have changed in the weapon damage update + pub const KICK: WeaponDto = WeaponDto { + id: -1, + name: Cow::Borrowed("Kick"), + cat: WeaponCategory::HandToHand, + kind: WeaponSlot::Fists, + base_dmg: 30.0, + dmg: Some(30.0), + base_acc: 40.0, + acc: Some(40.0), + ammo: None, + bonuses: [None, None], + mods: [None, None], + compatible_mods: Cow::Borrowed(&[]), + experience: None, + japanese: false, + }; +} diff --git a/models/src/hierarchy.rs b/models/src/hierarchy.rs new file mode 100644 index 0000000..48c8803 --- /dev/null +++ b/models/src/hierarchy.rs @@ -0,0 +1,178 @@ +use bevy_ecs::{ + prelude::*, + system::{Command, EntityCommands}, +}; + +#[derive(Component, Debug, Clone, Copy)] +pub struct Parent(Entity); + +impl Parent { + pub fn get(&self) -> Entity { + self.0 + } +} + +#[derive(Component, Debug, Clone, Default)] +pub struct Children(Vec); + +impl Children { + pub fn get(&self) -> &[Entity] { + &self.0 + } +} + +struct AddChild { + parent: Entity, + child: Entity, +} + +impl Command for AddChild { + fn apply(self, world: &mut World) { + let mut parent = world.entity_mut(self.parent); + if let Some(mut children) = parent.get_mut::() { + children.0.push(self.child); + } else { + parent.insert(Children(vec![self.child])); + } + } +} + +struct AddChildren { + parent: Entity, + children: Vec, +} + +impl Command for AddChildren { + fn apply(mut self, world: &mut World) { + let mut parent = world.entity_mut(self.parent); + if let Some(mut children) = parent.get_mut::() { + children.0.append(&mut self.children); + } else { + parent.insert(Children(self.children)); + } + } +} + +struct RemoveChild { + parent: Entity, + child: Entity, +} + +impl Command for RemoveChild { + fn apply(self, world: &mut World) { + let mut parent = world.entity_mut(self.parent); + let mut children = parent + .get_mut::() + .expect("Parent component has no children"); + children.0.retain(|child| *child != self.child); + } +} + +pub trait HierarchyBuilder { + fn add_child(&mut self, child: Entity) -> &mut Self; + + fn add_children(&mut self, children: impl AsRef<[Entity]>) -> &mut Self; + + fn remove_child(&mut self, child: Entity) -> &mut Self; + + fn set_parent(&mut self, parent: Entity) -> &mut Self; +} + +impl<'a> HierarchyBuilder for EntityCommands<'a> { + fn add_child(&mut self, child: Entity) -> &mut Self { + let parent = self.id(); + self.commands().queue(AddChild { parent, child }); + self.commands().entity(child).insert(Parent(parent)); + self + } + + fn add_children(&mut self, children: impl AsRef<[Entity]>) -> &mut Self { + let children = children.as_ref(); + let parent = self.id(); + self.commands().queue(AddChildren { + parent, + children: children.to_owned(), + }); + self.commands().insert_batch( + children + .iter() + .map(|e| (*e, Parent(parent))) + .collect::>(), + ); + self + } + + fn remove_child(&mut self, child: Entity) -> &mut Self { + let parent = self.id(); + self.commands().queue(RemoveChild { parent, child }); + self.commands().entity(child).remove::(); + self + } + + fn set_parent(&mut self, parent: Entity) -> &mut Self { + let child = self.id(); + self.commands().queue(AddChild { parent, child }); + self.commands().entity(child).insert(Parent(parent)); + self + } +} + +impl<'w> HierarchyBuilder for EntityWorldMut<'w> { + fn add_child(&mut self, child: Entity) -> &mut Self { + let parent_id = self.id(); + unsafe { + self.world_mut() + .entity_mut(child) + .insert(Parent(parent_id)) + .update_location(); + } + if let Some(mut children) = self.get_mut::() { + children.0.push(child); + self + } else { + self.insert(Children(vec![child])) + } + } + + fn add_children(&mut self, children: impl AsRef<[Entity]>) -> &mut Self { + let parent_id = self.id(); + unsafe { + for child in children.as_ref() { + self.world_mut() + .entity_mut(*child) + .insert(Parent(parent_id)) + .update_location(); + } + } + if let Some(mut old_children) = self.get_mut::() { + old_children.0.append(&mut children.as_ref().to_owned()); + self + } else { + self.insert(Children(children.as_ref().to_owned())) + } + } + + fn remove_child(&mut self, child: Entity) -> &mut Self { + unsafe { + self.world_mut() + .entity_mut(child) + .remove::() + .update_location(); + } + if let Some(mut children) = self.get_mut::() { + children.0.retain(|c| *c != child); + } + self + } + + fn set_parent(&mut self, parent: Entity) -> &mut Self { + let child_id = self.id(); + unsafe { + self.world_mut() + .entity_mut(parent) + .add_child(child_id) + .update_location() + } + self + } +} diff --git a/models/src/lib.rs b/models/src/lib.rs new file mode 100644 index 0000000..38a09f9 --- /dev/null +++ b/models/src/lib.rs @@ -0,0 +1,3 @@ +pub mod bundle; +pub mod dto; +// pub mod hierarchy; diff --git a/src/armour/mod.rs b/src/armour/mod.rs index 74d7987..bc3b2a1 100644 --- a/src/armour/mod.rs +++ b/src/armour/mod.rs @@ -1,227 +1,22 @@ use bevy_ecs::prelude::*; -use rand::distributions::Distribution; +use proxisim_models::{ + bundle::armour::{ + ArmourBodyPart, ArmourBodyPartSlot, ArmourBodyParts, ArmourCoverage, ArmourValue, + ArmourVec, BodyPartCoverage, Immunities, Immunity, PlayerArmour, + }, + hierarchy::HierarchyBuilder, +}; use strum::IntoEnumIterator; use crate::{ - hierarchy::HierarchyBuilder, - player::{ - status_effect::{ - ConcussionGrenade, FlashGrenade, PepperSpray, Sand, TearGas, TempDebuffImmunity, - }, - BodyPart, + player::status_effect::{ + ConcussionGrenade, FlashGrenade, PepperSpray, TearGas, TempDebuffImmunity, }, - Name, Stages, + Stages, }; -#[derive(Component, Default)] -pub struct Armour; - -#[derive(Debug)] -pub struct BodyPartCoverage { - pub armour: Entity, - pub coverage: f32, - pub armour_value: f32, -} - -#[derive(Component, Default)] -pub struct ArmourCoverage(ArmourVec); - -#[derive(Component, Default)] -pub struct ArmourValue(pub f32); - -#[derive(Component)] -#[cfg_attr(feature = "json", derive(serde::Deserialize))] -#[cfg_attr(feature = "json", serde(rename_all = "snake_case"))] -pub enum Set { - Riot, - Assault, - Dune, - Delta, - Vanguard, - Sentinel, - Marauder, - Eod, -} - -#[cfg_attr(feature = "json", derive(serde::Deserialize))] -#[cfg_attr(feature = "json", serde(rename_all = "snake_case"))] -pub enum Immunity { - Radiation, - TearGas, - PepperSpray, - NerveGas, - Sand, - ConcussionGrenades, - FlashGrenades, -} - -#[derive(Component)] -pub struct Immunities(pub Vec); - -#[derive(Bundle, Default)] -pub struct ArmourBundle { - pub name: Name, - pub armour: Armour, - pub coverage: ArmourCoverage, - pub armour_value: ArmourValue, -} - -impl ArmourBundle { - pub fn new(name: String, coverage: [f32; 10], armour_value: f32) -> Self { - Self { - name: Name(name), - armour: Armour, - coverage: ArmourCoverage(ArmourVec(coverage)), - armour_value: ArmourValue(armour_value / 100.0), - } - } -} - -#[derive(Component, Default, Debug)] -pub struct ArmourBodyPart { - pub armour_pieces: Vec, -} - -#[derive(Component, Default)] -pub struct EquippedArmour { - pub head: Option, - pub body: Option, - pub legs: Option, - pub feet: Option, - pub hands: Option, -} - -#[derive(Component, Debug)] -pub struct ArmourBodyParts(pub ArmourVec); - -enum ArmourIterState { - Head, - Body, - Legs, - Feet, - Hands, -} - -pub struct ArmourIter<'a> { - state: ArmourIterState, - equipped_armour: &'a EquippedArmour, -} - -impl<'a> Iterator for ArmourIter<'a> { - type Item = Entity; - - fn next(&mut self) -> Option { - loop { - let (next, piece) = match self.state { - ArmourIterState::Head => (ArmourIterState::Body, self.equipped_armour.head), - ArmourIterState::Body => (ArmourIterState::Legs, self.equipped_armour.body), - ArmourIterState::Legs => (ArmourIterState::Feet, self.equipped_armour.legs), - ArmourIterState::Feet => (ArmourIterState::Hands, self.equipped_armour.feet), - ArmourIterState::Hands => return self.equipped_armour.hands, - }; - - self.state = next; - if piece.is_some() { - return piece; - } - } - } -} - -impl<'a> IntoIterator for &'a EquippedArmour { - type Item = Entity; - - type IntoIter = ArmourIter<'a>; - - fn into_iter(self) -> Self::IntoIter { - ArmourIter { - state: ArmourIterState::Head, - equipped_armour: self, - } - } -} - -#[repr(usize)] -#[derive(Clone, Copy, strum::EnumIter)] -pub enum ArmourBodyPartSlot { - Arms, - Stomach, - Heart, - Chest, - Throat, - Hands, - Groin, - Legs, - Head, - Feet, -} - -impl From for ArmourBodyPartSlot { - fn from(value: BodyPart) -> Self { - match value { - BodyPart::LeftArm | BodyPart::RightArm => Self::Arms, - BodyPart::Stomach => Self::Stomach, - BodyPart::Heart => Self::Heart, - BodyPart::Chest => Self::Chest, - BodyPart::Throat => Self::Throat, - BodyPart::LeftHand | BodyPart::RightHand => Self::Hands, - BodyPart::Groin => Self::Groin, - BodyPart::LeftLeg | BodyPart::RightLeg => Self::Legs, - BodyPart::Head => Self::Head, - BodyPart::LeftFoot | BodyPart::RightFoot => Self::Feet, - } - } -} - -#[derive(Debug, Clone, Copy, Default)] -pub struct ArmourVec([T; 10]); - -impl std::ops::Index for ArmourVec { - type Output = T; - - fn index(&self, index: ArmourBodyPartSlot) -> &Self::Output { - &self.0[index as usize] - } -} - -impl std::ops::IndexMut for ArmourVec { - fn index_mut(&mut self, index: ArmourBodyPartSlot) -> &mut Self::Output { - &mut self.0[index as usize] - } -} - -impl IntoIterator for ArmourVec { - type Item = T; - - type IntoIter = std::array::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl<'a> Distribution> for &'a ArmourBodyPart { - fn sample(&self, rng: &mut R) -> Option<&'a BodyPartCoverage> { - let mut current = None; - for piece in &self.armour_pieces { - // NOTE: This is not strictly speaking correct, but the edge cases where this applies - // should be very rare, and it should be a decent enough heuristic to barely make a - // difference from the actual pixel comparisons that torn seems to be using for this. - if current - .map(|c: &BodyPartCoverage| c.armour_value) - .unwrap_or_default() - < piece.armour_value - && rng.gen_bool(piece.coverage as f64) - { - current = Some(piece); - } - } - current - } -} - fn generate_body_parts( - equip_q: Query<(Entity, &EquippedArmour)>, + equip_q: Query<(Entity, &PlayerArmour)>, armour_q: Query<(Entity, &ArmourCoverage, &ArmourValue, Option<&Immunities>)>, mut commands: Commands, ) { @@ -246,15 +41,15 @@ fn generate_body_parts( Immunity::PepperSpray => { player.insert(TempDebuffImmunity::::default()); } - Immunity::FlashGrenades => { + Immunity::FlashGrenade => { player.insert(TempDebuffImmunity::::default()); } - Immunity::Sand => { - player.insert(TempDebuffImmunity::::default()); - } - Immunity::ConcussionGrenades => { + Immunity::ConcussionGrenade => { player.insert(TempDebuffImmunity::::default()); } + Immunity::Insanity => { + // TODO: Insanity + } } } } @@ -264,7 +59,7 @@ fn generate_body_parts( parts[slot].armour_pieces.push(BodyPartCoverage { armour, coverage: coverage.0[slot] / 100.0, - armour_value: armour_value.0, + armour_value: armour_value.0 / 100.0, }); } } @@ -274,7 +69,7 @@ fn generate_body_parts( commands .entity(player) - .add_children(parts) + .add_children(&parts) .insert(ArmourBodyParts(ArmourVec(parts))); } } diff --git a/src/attacker.json b/src/attacker.json new file mode 100644 index 0000000..62d2173 --- /dev/null +++ b/src/attacker.json @@ -0,0 +1,177 @@ +{ + "level": 100, + "name": "Pyrit", + "stats": { + "str": 1500203325, + "spd": 1547656240, + "def": 1597091496, + "dex": 2618494699 + }, + "strategy": { + "type": "in_order", + "order": [ + "temporary", + "primary" + ], + "reload": true + }, + "education": { + "bio2350": true, + "bio2380": true, + "bio2410": true, + "cbt2790": false, + "cbt2820": true, + "cbt2830": true, + "cbt2840": true, + "cbt2850": true, + "cbt2860": true, + "cbt2125": true, + "gen2116": true, + "gen2119": true, + "haf2104": false, + "haf2105": false, + "haf2106": false, + "haf2107": false, + "haf2108": false, + "haf2109": false, + "his2160": true, + "his2170": true, + "mth2240": false, + "mth2250": false, + "mth2260": false, + "mth2320": false, + "mth2310": true, + "mth3330": true, + "psy2640": false, + "psy2650": false, + "psy2660": false, + "psy2670": false, + "def2710": false, + "def2730": false, + "def2740": false, + "def2750": false, + "def2760": false, + "def3770": true, + "spt2480": true, + "spt2490": false, + "spt2500": false + }, + "merits": { + "life": 10, + "crits": 10, + "brawn": 0, + "protection": 0, + "sharpness": 0, + "evasion": 0, + "heavy_artillery_mastery": 0, + "machine_gun_mastery": 0, + "rifle_mastery": 5, + "smg_mastery": 10, + "shotgun_mastery": 0, + "pistol_mastery": 0, + "club_mastery": 10, + "piercing_mastery": 0, + "slashing_mastery": 0, + "mechanical_mastery": 0, + "temporary_mastery": 0 + }, + "faction": { + "str": 0, + "def": 0, + "spd": 0, + "dex": 0, + "life": 0, + "dmg": 0, + "acc": 0, + "side_effects": 10 + }, + "weapons": { + "primary": { + "id": 488, + "name": "MP 40", + "kind": "primary", + "cat": "smg", + "base_dmg": 37, + "base_acc": 41, + "dmg": 5, + "acc": 41, + "ammo": { + "clip_size": 32, + "rate_of_fire": [ + 3, + 5 + ] + }, + "mods": [ + "high_capacity_mags", + null + ], + "bonuses": [ + { + "bonus": "specialist", + "value": 10 + }, + null + ], + "compatible_mods": [ + "reflex_sight", + "holographic_sight", + "acog_sight", + "thermal_sight", + "laser1mw", + "laser5mw", + "laser30mw", + "laser100mw", + "small_suppressor", + "standard_suppressor", + "large_suppressor", + "extended_mags", + "high_capacity_mags", + "extra_clip", + "extra_clip2", + "adjustable_trigger", + "hair_trigger", + "custom_grip", + "standard_brake", + "heavy_duty_brake", + "tactical_brake", + "small_light", + "precision_light", + "tactical_illuminator" + ], + "experience": 0, + "japanese": false + }, + "secondary": null, + "melee": null, + "temporary": { + "id": 464, + "name": "Melatonin", + "kind": "temporary", + "cat": "temporary", + "base_dmg": 0, + "base_acc": 0, + "dmg": 0, + "acc": 0, + "ammo": null, + "mods": [ + null, + null + ], + "bonuses": [ + null, + null + ], + "compatible_mods": [], + "experience": 0, + "japanese": false + } + }, + "armour": { + "helmet": null, + "body": null, + "pants": null, + "gloves": null, + "boots": null + } +} diff --git a/src/defender.json b/src/defender.json new file mode 100644 index 0000000..530e51e --- /dev/null +++ b/src/defender.json @@ -0,0 +1,97 @@ +{ + "level": 100, + "name": "Evil Pyrit", + "stats": { + "str": 1415286156, + "spd": 1344108854, + "def": 1425974550, + "dex": 2182078916 + }, + "strategy": { + "type": "in_order", + "order": [], + "reload": false + }, + "education": { + "bio2350": true, + "bio2380": true, + "bio2410": true, + "cbt2790": true, + "cbt2820": true, + "cbt2830": true, + "cbt2840": true, + "cbt2850": true, + "cbt2860": true, + "cbt2125": true, + "gen2116": true, + "gen2119": true, + "haf2104": true, + "haf2105": true, + "haf2106": true, + "haf2107": true, + "haf2108": true, + "haf2109": true, + "his2160": true, + "his2170": true, + "mth2240": true, + "mth2250": true, + "mth2260": true, + "mth2320": true, + "mth2310": true, + "mth3330": true, + "psy2640": true, + "psy2650": true, + "psy2660": true, + "psy2670": true, + "def2710": true, + "def2730": true, + "def2740": true, + "def2750": true, + "def2760": true, + "def3770": true, + "spt2480": true, + "spt2490": true, + "spt2500": true + }, + "merits": { + "life": 10, + "crits": 10, + "brawn": 10, + "protection": 10, + "sharpness": 10, + "evasion": 10, + "heavy_artillery_mastery": 0, + "machine_gun_mastery": 0, + "rifle_mastery": 5, + "smg_mastery": 10, + "shotgun_mastery": 0, + "pistol_mastery": 0, + "club_mastery": 0, + "piercing_mastery": 0, + "slashing_mastery": 0, + "mechanical_mastery": 0, + "temporary_mastery": 0 + }, + "faction": { + "str": 0, + "def": 0, + "spd": 0, + "dex": 0, + "life": 0, + "dmg": 0, + "acc": 0, + "side_effects": 10 + }, + "weapons": { + "primary": null, + "secondary": null, + "melee": null + }, + "armour": { + "helmet": null, + "body": null, + "pants": null, + "gloves": null, + "boots": null + } +} diff --git a/src/dto.rs b/src/dto.rs index e72b76e..b496d06 100644 --- a/src/dto.rs +++ b/src/dto.rs @@ -1,6 +1,6 @@ use bevy_ecs::prelude::*; -use crate::entity_registry::EntityInfo; +pub use crate::entity_registry::EntityInfo; pub use crate::passives::{DrugCooldown, Education, FactionUpgrades, Merits}; pub use crate::player::PlayerStrategy; use crate::weapon::Japanese; diff --git a/src/effect.rs b/src/effect.rs index 1f5b7e1..aa371a1 100644 --- a/src/effect.rs +++ b/src/effect.rs @@ -1,21 +1,21 @@ use std::{any::TypeId, collections::HashMap, sync::Mutex}; use bevy_ecs::{prelude::*, system::SystemParam}; - -use crate::{ +use proxisim_models::{ + bundle::player::{Current, Defender, Player}, hierarchy::{HierarchyBuilder, Parent}, - player::{Current, Defender, Player}, - Stages, }; +use crate::Stages; + const SECONDS_PER_ACTION: f32 = 1.1; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Component)] pub struct EffectId(usize); pub struct EffectInfo { - pub apply: Box, Out = ()>>, - pub teardown: Box, Out = ()>>, + pub apply: Box>, Out = ()>>, + pub teardown: Box>, Out = ()>>, } // TODO: remove need for unsafe code by splitting the system info from this struct @@ -114,8 +114,8 @@ impl<'w, 's> Effects<'w, 's> { pub struct EffectBuilder<'w> { world: &'w mut World, - apply: Option, Out = ()>>>, - teardown: Option, Out = ()>>>, + apply: Option>, Out = ()>>>, + teardown: Option>, Out = ()>>>, id: EffectId, } @@ -123,7 +123,7 @@ impl<'r> EffectBuilder<'r> { #[must_use] pub fn apply(&mut self, system: S) -> &mut Self where - S: IntoSystem, (), A> + 'static, + S: IntoSystem>, (), A> + 'static, { let mut system = IntoSystem::into_system(system); system.initialize(self.world); @@ -134,7 +134,7 @@ impl<'r> EffectBuilder<'r> { #[must_use] pub fn teardown(&mut self, system: S) -> &mut Self where - S: IntoSystem, (), T> + 'static, + S: IntoSystem>, (), T> + 'static, { let mut system = IntoSystem::into_system(system); system.initialize(self.world); @@ -174,7 +174,7 @@ pub(crate) fn run_effects(world: &mut World) { let unsafe_world = world.as_unsafe_world_cell(); let mut registry = unsafe_world.get_resource_mut::().unwrap(); let info = registry.system_info.get_mut(&next_effect).unwrap(); - info.apply.run(entities, unsafe_world.world_mut()); + info.apply.run(entities, unsafe_world.world_mut()).unwrap(); info.apply.apply_deferred(unsafe_world.world_mut()); }; } @@ -196,7 +196,8 @@ pub(crate) fn run_effects(world: &mut World) { let mut registry = unsafe_world.get_resource_mut::().unwrap(); let info = registry.system_info.get_mut(&next_effect).unwrap(); info.teardown - .run(entities.clone(), unsafe_world.world_mut()); + .run(entities.clone(), unsafe_world.world_mut()) + .unwrap(); info.teardown.apply_deferred(unsafe_world.world_mut()); }; @@ -209,7 +210,7 @@ pub(crate) fn run_effects(world: &mut World) { } #[must_use] -pub(crate) fn register_effect(stages: &mut Stages) -> EffectBuilder { +pub(crate) fn register_effect(stages: &mut Stages) -> EffectBuilder<'_> { let mut registry = stages.world.resource_mut::(); let id = EffectId(registry.id_counter); registry.id_counter += 1; @@ -245,7 +246,7 @@ fn advance_clock( mut clock: ResMut, current_q: Query, (With, With)>, ) { - let is_defender = current_q.single(); + let is_defender = current_q.single().unwrap(); if !is_defender { clock.0 += SECONDS_PER_ACTION; } diff --git a/src/entity_registry.rs b/src/entity_registry.rs index e21d846..eb0022f 100644 --- a/src/entity_registry.rs +++ b/src/entity_registry.rs @@ -1,29 +1,17 @@ use std::collections::HashMap; use bevy_ecs::prelude::*; - -use crate::{ +use proxisim_models::{ + bundle::{ + player::{Attacker, Player}, + weapon::Weapon, + Id, Name, + }, + dto::metrics::EntityInfo, hierarchy::Parent, - player::{Attacker, Player}, - weapon::Weapon, - Id, Name, Stages, }; -#[cfg_attr(feature = "json", derive(serde::Serialize))] -#[cfg_attr(feature = "json", serde(tag = "type", rename_all = "snake_case"))] -#[derive(Debug, Clone)] -pub enum EntityInfo { - Player { - name: String, - id: usize, - is_attacker: bool, - }, - Weapon { - name: String, - owner: usize, - id: usize, - }, -} +use crate::Stages; #[derive(Resource, Default)] pub struct EntityRegistry(pub HashMap); diff --git a/src/hierarchy.rs b/src/hierarchy.rs index 9252b85..0d20a7a 100644 --- a/src/hierarchy.rs +++ b/src/hierarchy.rs @@ -78,10 +78,10 @@ pub trait HierarchyBuilder { fn set_parent(&mut self, parent: Entity) -> &mut Self; } -impl<'w, 's, 'a> HierarchyBuilder for EntityCommands<'w, 's, 'a> { +impl<'a> HierarchyBuilder for EntityCommands<'a> { fn add_child(&mut self, child: Entity) -> &mut Self { let parent = self.id(); - self.commands().add(AddChild { parent, child }); + self.commands().queue(AddChild { parent, child }); self.commands().entity(child).insert(Parent(parent)); self } @@ -89,11 +89,11 @@ impl<'w, 's, 'a> HierarchyBuilder for EntityCommands<'w, 's, 'a> { fn add_children(&mut self, children: impl AsRef<[Entity]>) -> &mut Self { let children = children.as_ref(); let parent = self.id(); - self.commands().add(AddChildren { + self.commands().queue(AddChildren { parent, children: children.to_owned(), }); - self.commands().insert_or_spawn_batch( + self.commands().insert_batch( children .iter() .map(|e| (*e, Parent(parent))) @@ -104,14 +104,14 @@ impl<'w, 's, 'a> HierarchyBuilder for EntityCommands<'w, 's, 'a> { fn remove_child(&mut self, child: Entity) -> &mut Self { let parent = self.id(); - self.commands().add(RemoveChild { parent, child }); + self.commands().queue(RemoveChild { parent, child }); self.commands().entity(child).remove::(); self } fn set_parent(&mut self, parent: Entity) -> &mut Self { let child = self.id(); - self.commands().add(AddChild { parent, child }); + self.commands().queue(AddChild { parent, child }); self.commands().entity(child).insert(Parent(parent)); self } diff --git a/src/lib.rs b/src/lib.rs index a721411..05b16e9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,32 +1,29 @@ #![warn(clippy::perf, clippy::style, clippy::all)] #![allow(clippy::type_complexity)] -use bevy_ecs::{prelude::*, schedule::ScheduleLabel}; +use bevy_ecs::{message::MessageRegistry, prelude::*, schedule::ScheduleLabel}; use effect::{register_effect, EffectBuilder}; use metrics::Metrics; +use proxisim_models::{ + bundle::player::{Attacker, Current, Defender}, + dto::{ + metrics::{Counter, Histogram}, + player::PlayerDto, + }, +}; use rand::SeedableRng; -use crate::{ - log::{Log, Logging}, - player::{Attacker, Current, Defender}, -}; +use crate::log::{Log, Logging}; mod armour; -pub mod dto; mod effect; mod entity_registry; -mod hierarchy; +// mod hierarchy; pub mod log; mod metrics; mod passives; mod player; mod weapon; -#[derive(Component, Debug, Default)] -struct Name(String); - -#[derive(Component, Debug, Default)] -struct Id(usize); - // TODO: This is a bottleneck, so probably better to change this to a `Local` or use `thread_rng` // instead. Then again, the whole simulator isn't very parallelisable, so it may be a moot point #[derive(Resource)] @@ -79,19 +76,20 @@ struct Stages { impl Stages { fn add_event(&mut self) -> &mut Self where - T: Event, + T: Message, { - if !self.world.contains_resource::>() { - self.world.init_resource::>(); + if !self.world.contains_resource::>() { + /* self.world.init_resource::>(); self.pre_turn.add_systems( bevy_ecs::event::event_update_system:: .run_if(bevy_ecs::event::event_update_condition::), - ); + ); */ + MessageRegistry::register_message::(&mut self.world); } self } - fn register_effect(&mut self) -> EffectBuilder { + fn register_effect(&mut self) -> EffectBuilder<'_> { register_effect::(self) } } @@ -99,7 +97,7 @@ impl Stages { pub struct Simulation(Stages); impl Simulation { - pub fn new(attacker: dto::Player, defender: dto::Player) -> Self { + pub fn new(attacker: PlayerDto, defender: PlayerDto) -> Self { let world = World::new(); let mut stages = Stages { equip: Schedule::new(Stage::Equip), @@ -125,7 +123,7 @@ impl Simulation { stages.world.insert_resource(FightStatus::Ongoing); stages .world - .insert_resource(Rng(rand::rngs::SmallRng::from_entropy())); + .insert_resource(Rng(rand::rngs::SmallRng::from_os_rng())); stages.world.insert_resource(Logging(true)); @@ -157,17 +155,17 @@ impl Simulation { metrics.active = recording; } - pub fn consume_metrics(&mut self) -> (Vec, Vec) { + pub fn consume_metrics(&mut self) -> (Vec, Vec) { let entities = self.0.world.entities().len(); let components = self.0.world.components().len(); self.0 .world .resource::() - .increment_counter(Entity::from_raw(0), "entities", entities.into()); + .increment_counter(None, "entities", entities.into()); self.0 .world .resource::() - .increment_counter(Entity::from_raw(0), "components", components as u64); + .increment_counter(None, "components", components as u64); metrics::consume_metrics(&self.0.world) } @@ -333,110 +331,12 @@ impl Simulation { mod tests { use super::*; - fn attacker() -> dto::Player { - use dto::*; - - Player { - name: "Test".to_owned(), - id: 0, - level: 10, - stats: Stats { - str: 10_000.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![WeaponMod::HairTrigger], - bonuses: vec![WeaponBonusInfo { - bonus: WeaponBonus::Expose, - value: 9.0, - }], - experience: 100.0, - }), - ..Default::default() - }, - armour: ArmourPieces { - helmet: Some(Armour { - name: "Test".to_owned(), - armour_value: 50.0, - coverage: [ - 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, - ], - immunities: Vec::default(), - set: None, - }), - ..Default::default() - }, - } + fn attacker() -> PlayerDto { + serde_json::from_str(include_str!("./attacker.json")).unwrap() } - fn defender() -> dto::Player { - use dto::*; - - Player { - name: "Test".to_owned(), - id: 1, - level: 10, - stats: Stats { - str: 10_000.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![WeaponBonusInfo { - bonus: WeaponBonus::Powerful, - value: 35.0, - }], - experience: 100.0, - }), - ..Default::default() - }, - armour: ArmourPieces { - helmet: Some(Armour { - name: "Test".to_owned(), - armour_value: 50.0, - coverage: [ - 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, - ], - immunities: Vec::default(), - set: None, - }), - ..Default::default() - }, - } + fn defender() -> PlayerDto { + serde_json::from_str(include_str!("./defender.json")).unwrap() } #[test] @@ -450,6 +350,5 @@ mod tests { sim.set_metrics(true); sim.run_once(); sim.consume_metrics(); - panic!(); } } diff --git a/src/log.rs b/src/log.rs index cb8ec48..4773fa9 100644 --- a/src/log.rs +++ b/src/log.rs @@ -1,23 +1,24 @@ use std::sync::Mutex; -use bevy_ecs::{prelude::*, system::SystemParam}; - -use crate::{ - entity_registry::EntityRegistry, - hierarchy::Children, - player::stats::{ - AdditiveBonus, BaselineStat, CritRate, DamageBonus, Defence, Dexterity, EffectiveStat, - MultiplicativeBonus, SimpleStatBaseline, SimpleStatBonus, SimpleStatEffective, - SimpleStatMarker, Speed, StatMarker, Strength, WeaponAccuracy, +use bevy_ecs::{prelude::*, query::QuerySingleError, system::SystemParam}; +use proxisim_models::{ + bundle::{ + stat::{ + AdditiveBonus, BaselineStat, ClipSize, Clips, CritRate, DamageBonus, Defence, + Dexterity, EffectiveStat, MultiplicativeBonus, SimpleStatBaseline, SimpleStatBonus, + SimpleStatEffective, SimpleStatMarker, Speed, StatMarker, Strength, WeaponAccuracy, + }, + weapon::WeaponVerb, }, - weapon::WeaponVerb, - Stages, + hierarchy::Children, }; +use crate::{Stages, entity_registry::EntityRegistry}; + #[derive(Resource)] pub struct Logging(pub bool); -#[derive(Event)] +#[derive(Message)] struct LogEvent(Mutex>); impl From for LogEvent { @@ -38,66 +39,71 @@ pub struct WeaponInfo { } #[derive(Clone)] -pub enum LogValue<'a> { +pub enum LogValue { Float(f32), Unsigned(u32), + Signed(i32), Bool(bool), String(String), Entity(Entity), OptionNone, - Display(&'a (dyn std::fmt::Display + Send + Sync)), - Debug(&'a (dyn std::fmt::Debug + Send + Sync)), Player(Entity), Weapon(Entity), - Array(Vec>), - Map(Vec<(&'static str, LogValue<'a>)>), + Array(Vec), + Map(Vec<(&'static str, LogValue)>), } -impl<'a> From for LogValue<'a> { +impl From for LogValue { fn from(value: String) -> Self { Self::String(value) } } -impl<'a> From<&'a str> for LogValue<'static> { +impl<'a> From<&'a str> for LogValue { fn from(value: &'a str) -> Self { Self::String(value.to_owned()) } } -impl<'a> From for LogValue<'a> { +impl From for LogValue { fn from(value: f32) -> Self { Self::Float(value) } } -impl<'a> From for LogValue<'a> { +impl From for LogValue { fn from(value: u32) -> Self { Self::Unsigned(value) } } -impl<'a> From for LogValue<'a> { +impl From for LogValue { fn from(value: u16) -> Self { Self::Unsigned(value.into()) } } -impl From for LogValue<'static> { +impl From for LogValue { + fn from(value: i16) -> Self { + Self::Signed(value.into()) + } +} + +impl From for LogValue { fn from(value: bool) -> Self { Self::Bool(value) } } -impl From for LogValue<'static> { +impl From for LogValue { fn from(value: Entity) -> Self { Self::Entity(value) } } -impl<'a, T> From> for LogValue<'a> +impl From> for LogValue where - T: Into>, + T: Into, { fn from(value: Option) -> Self { match value { @@ -107,26 +113,38 @@ where } } -impl<'a, V> From> for LogValue<'a> +impl From> for LogValue where - V: Into>, + V: Into, { fn from(value: Vec) -> Self { LogValue::Array(value.into_iter().map(Into::into).collect()) } } -impl<'a, V> From> for LogValue<'a> +impl From> for LogValue where - V: Into>, + V: Into, { fn from(value: Vec<(&'static str, V)>) -> Self { LogValue::Map(value.into_iter().map(|(k, v)| (k, v.into())).collect()) } } +impl From> for LogValue +where + V: Into, +{ + fn from(value: Result) -> Self { + match value { + Ok(value) => value.into(), + Err(_) => LogValue::String("No found".to_owned()), + } + } +} + #[cfg(feature = "json")] -impl<'a> LogValue<'a> { +impl LogValue { fn to_value(&self, entity_registry: &EntityRegistry) -> serde_json::Value { match self { LogValue::OptionNone => serde_json::Value::Null, @@ -136,8 +154,7 @@ impl<'a> LogValue<'a> { LogValue::String(val) => serde_json::Value::String(val.clone()), LogValue::Bool(val) => serde_json::Value::Bool(*val), LogValue::Unsigned(val) => serde_json::Value::Number(serde_json::Number::from(*val)), - LogValue::Debug(boxed) => serde_json::Value::String(format!("{boxed:?}")), - LogValue::Display(boxed) => serde_json::Value::String(format!("{boxed}")), + LogValue::Signed(val) => serde_json::Value::Number(serde_json::Number::from(*val)), LogValue::Player(id) => { serde_json::to_value(entity_registry.0.get(id).unwrap()).unwrap() } @@ -162,7 +179,7 @@ impl<'a> LogValue<'a> { trait LogMessage: Send + Sync + 'static { fn tag(&self) -> &'static str; - fn entries(&self) -> Vec<(&'static str, LogValue<'_>)>; + fn entries(&self) -> Vec<(&'static str, LogValue)>; } #[derive(Resource, Default)] @@ -212,11 +229,10 @@ impl std::fmt::Display for Log { LogValue::Float(val) => write!(f, "{val}")?, LogValue::Bool(val) => write!(f, "{val}")?, LogValue::Unsigned(val) => write!(f, "{val}")?, + LogValue::Signed(val) => write!(f, "{val}")?, LogValue::OptionNone => write!(f, "None")?, - LogValue::Display(val) => write!(f, "\"{val}\"")?, - LogValue::Debug(val) => write!(f, "\"{val:?}\"")?, LogValue::Player(id) | LogValue::Weapon(id) | LogValue::Entity(id) => { - write!(f, "{:?}", id)? + write!(f, "{id}")? } LogValue::Array(_) | LogValue::Map(_) => (), }; @@ -421,7 +437,7 @@ impl std::fmt::Display for Log { #[derive(SystemParam)] pub struct Logger<'w> { - event_writer: EventWriter<'w, LogEvent>, + event_writer: MessageWriter<'w, LogEvent>, logging: Res<'w, Logging>, } @@ -431,7 +447,7 @@ impl<'w> Logger<'w> { B: FnOnce() -> DynamicLogMessage, { if self.logging.0 { - self.event_writer.send(body().into()); + self.event_writer.write(body().into()); } } } @@ -440,7 +456,7 @@ fn logging_enabled(logging: Res) -> bool { logging.0 } -fn append_log_messages(mut events: EventReader, mut log: ResMut) { +fn append_log_messages(mut events: MessageReader, mut log: ResMut) { for event in events.read() { if let Some(entry) = event.0.lock().unwrap().take() { log.entries.push(entry); @@ -450,7 +466,7 @@ fn append_log_messages(mut events: EventReader, mut log: ResMut) pub struct DynamicLogMessage { pub label: &'static str, - pub entries: Vec<(&'static str, LogValue<'static>)>, + pub entries: Vec<(&'static str, LogValue)>, } impl LogMessage for DynamicLogMessage { @@ -458,7 +474,7 @@ impl LogMessage for DynamicLogMessage { self.label } - fn entries(&self) -> Vec<(&'static str, LogValue<'_>)> { + fn entries(&self) -> Vec<(&'static str, LogValue)> { self.entries.clone() } } @@ -549,8 +565,8 @@ fn log_simple_stat_changes( bonus_q: Query<&SimpleStatBonus>, mut logger: Logger, ) where - Stat::ValueType: Into>, - Stat::BonusType: Into>, + Stat::ValueType: Into, + Stat::BonusType: Into, { for (target, baseline, effective, children) in stat_q.iter() { let bonuses: Vec<_> = bonus_q @@ -588,6 +604,8 @@ pub(crate) fn configure(stages: &mut Stages) { log_simple_stat_changes::, log_simple_stat_changes::, log_simple_stat_changes::, + log_simple_stat_changes::, + log_simple_stat_changes::, ) .run_if(logging_enabled), ); diff --git a/src/metrics.rs b/src/metrics.rs index 9b10a52..fdf9fb3 100644 --- a/src/metrics.rs +++ b/src/metrics.rs @@ -4,7 +4,7 @@ use std::{ sync::{atomic, Mutex, RwLock}, }; -use crate::{dto, entity_registry::EntityRegistry, Stages}; +use crate::{entity_registry::EntityRegistry, Stages}; #[derive(Default)] pub struct Histogram @@ -38,7 +38,7 @@ impl Counter { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct MetricKey { - entity: Entity, + entity: Option, label: &'static str, } @@ -50,7 +50,7 @@ pub struct Metrics { } impl Metrics { - pub fn record_histogram(&self, entity: Entity, label: &'static str, value: u32) { + pub fn record_histogram(&self, entity: Option, label: &'static str, value: u32) { if self.active { let key = MetricKey { entity, label }; let r_hist = self.histograms.read().unwrap(); @@ -70,7 +70,7 @@ impl Metrics { } } - pub fn increment_counter(&self, entity: Entity, label: &'static str, value: u64) { + pub fn increment_counter(&self, entity: Option, label: &'static str, value: u64) { if self.active { let key = MetricKey { entity, label }; let r_counters = self.counters.read().unwrap(); @@ -91,7 +91,12 @@ impl Metrics { } } -pub(crate) fn consume_metrics(world: &World) -> (Vec, Vec) { +pub(crate) fn consume_metrics( + world: &World, +) -> ( + Vec, + Vec, +) { let metrics = world.resource::(); let entities = world.resource::(); @@ -100,8 +105,12 @@ pub(crate) fn consume_metrics(world: &World) -> (Vec, Vec (Vec, Vec Self { - Self { - bio2350: true, - bio2380: true, - bio2410: true, - cbt2790: true, - cbt2820: true, - cbt2830: true, - cbt2840: true, - cbt2850: true, - cbt2860: true, - cbt2125: true, - gen2116: true, - gen2119: true, - haf2104: true, - haf2105: true, - haf2106: true, - haf2107: true, - haf2108: true, - haf2109: true, - his2160: true, - his2170: true, - mth2240: true, - mth2250: true, - mth2260: true, - mth2320: true, - mth2310: true, - mth3330: true, - psy2640: true, - psy2650: true, - psy2660: true, - psy2670: true, - def2710: true, - def2730: true, - def2740: true, - def2750: true, - def2760: true, - def3770: true, - spt2480: true, - spt2490: true, - spt2500: true, - } - } -} - -#[derive(Component, Default)] -#[cfg_attr(feature = "json", derive(serde::Deserialize))] -pub struct FactionUpgrades { - pub str: u16, - pub spd: u16, - pub def: u16, - pub dex: u16, - pub life: u16, - pub acc: u16, - pub dmg: u16, - pub side_effects: u16, -} - -#[derive(Component)] -#[cfg_attr(feature = "json", derive(serde::Deserialize))] -pub enum DrugCooldown { - Xanax, - Vicodin, -} - -#[derive(Clone, Copy)] -pub enum EducationPartDamageBonus { - Bio2380, -} - -impl EducationPartDamageBonus { - pub fn dmg_bonus(self, part: BodyPart) -> Option { - match part { - BodyPart::Throat => Some(0.10), - _ => None, - } - } -} - -#[derive(Bundle, Default)] -pub(crate) struct PassiveBundle { - pub merits: Merits, - pub education: Education, - pub faction: FactionUpgrades, -} +use crate::{effect::Effects, Stages}; fn spawn_permanent_effects( merit_q: Query<( diff --git a/src/player/mod.rs b/src/player/mod.rs index a2e8b77..d6884ef 100644 --- a/src/player/mod.rs +++ b/src/player/mod.rs @@ -1,224 +1,59 @@ use bevy_ecs::prelude::*; +use proxisim_models::{ + bundle::{ + armour::{ArmourBodyPart, ArmourBodyParts}, + passive::{FactionUpgrades, Merits}, + player::{ + Attacker, BodyPart, ChooseWeapon, CombatTurns, Current, CurrentTarget, Defeated, + Defender, FightEndType, Level, MaxHealth, PartDamageBonus, Player, PlayerStrategy, + Weapons, + }, + stat::{ + AmmoControl, Clips, CritRate, DamageBonus, Defence, Dexterity, EffectiveStat, Health, + SimpleStatBundle, SimpleStatEffective, Speed, Strength, WeaponAccuracy, + }, + weapon::{ + Ammo, DamageStat, NeedsReload, NonTargeted, RateOfFire, Usable, Uses, Weapon, + WeaponSlot, + }, + }, + hierarchy::Children, +}; use rand::Rng as _; -use strum::Display; use crate::{ - armour, + FightStatus, Rng, Stages, effect::Effects, - hierarchy::Children, log, log::Logger, metrics::Metrics, - passives::{EducationPartDamageBonus, FactionUpgrades, Merits}, - weapon::{ - bonus::{BonusPartDamageBonus, MultiTurnBonus}, - temp::{NonTargeted, Uses}, - Ammo, DamageProcEffect, DamageStat, NeedsReload, RateOfFire, TurnTriggeredEffect, Usable, - Weapon, WeaponSlot, - }, - FightStatus, Id, Name, Rng, Stages, -}; - -use self::stats::{ - AmmoControl, Clips, CritRate, DamageBonus, Defence, Dexterity, EffectiveStat, Health, - SimpleStatBundle, SimpleStatEffective, Speed, Strength, WeaponAccuracy, + weapon::{DamageProcEffect, TurnTriggeredEffect, bonus::MultiTurnBonus}, }; pub mod stats; pub mod status_effect; -#[derive(Component)] -pub struct Attacker; +fn select_weapon( + weapons: &Weapons, + slot: WeaponSlot, + reload: bool, + usable_q: &Query<(Has, Option<&Children>), With>, +) -> Option<(Entity, Option)> { + let id = match slot { + WeaponSlot::Primary => weapons.primary?, + WeaponSlot::Secondary => weapons.secondary?, + WeaponSlot::Melee => weapons.melee?, + WeaponSlot::Temporary => weapons.temporary?, + WeaponSlot::Fists => weapons.fists, + WeaponSlot::Kick => weapons.kick, + }; -#[derive(Component)] -pub struct Defender; + let (needs_reload, children) = usable_q.get(id).ok()?; -#[derive(Component)] -pub struct Defeated; - -#[derive(Component)] -pub struct Current; - -#[derive(Component)] -pub struct CurrentTarget; - -#[derive(Component, Default)] -pub struct Player; - -#[derive(Component)] -pub struct Level(pub u16); - -impl Default for Level { - fn default() -> Self { - Self(1) - } -} - -#[derive(Component, Debug)] -pub struct MaxHealth(pub u16); - -impl Default for MaxHealth { - fn default() -> Self { - Self(100) - } -} - -#[derive(Component, Debug, Default)] -pub struct CombatTurns(pub u16); - -#[derive(Component, Default, Debug)] -pub struct Weapons { - pub primary: Option, - pub secondary: Option, - pub melee: Option, - pub temporary: Option, - pub fists: Option, - pub kick: Option, -} - -impl Weapons { - fn select( - &self, - slot: WeaponSlot, - reload: bool, - usable_q: &Query<(Has, &Children), With>, - ) -> Option<(Entity, Children)> { - let id = match slot { - WeaponSlot::Primary => self.primary?, - WeaponSlot::Secondary => self.secondary?, - WeaponSlot::Melee => self.melee?, - WeaponSlot::Temporary => self.temporary?, - WeaponSlot::Fists => self.fists?, - WeaponSlot::Kick => self.kick?, - }; - - let (needs_reload, children) = usable_q.get(id).ok()?; - - if !reload && needs_reload { - None - } else { - Some((id, children.clone())) - } - } -} - -#[derive(Component, Debug)] -#[cfg_attr(feature = "json", derive(serde::Deserialize))] -#[cfg_attr(feature = "json", serde(tag = "type", rename_all = "snake_case"))] -pub enum PlayerStrategy { - AlwaysFists, - AlwaysKicks, - PrimaryMelee { - reload: bool, - }, - InOrder { - order: Vec, - reload: bool, - }, -} - -impl Default for PlayerStrategy { - fn default() -> Self { - Self::AlwaysFists - } -} - -#[derive(Event)] -pub struct ChooseWeapon(pub Entity); - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum BodyPart { - LeftHand, - RightHand, - LeftArm, - RightArm, - LeftFoot, - RightFoot, - LeftLeg, - RightLeg, - Stomach, - Chest, - Groin, - Head, - Throat, - Heart, -} - -impl std::fmt::Display for BodyPart { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::LeftHand => write!(f, "Left hand"), - Self::RightHand => write!(f, "Right hand"), - Self::LeftArm => write!(f, "Left arm"), - Self::RightArm => write!(f, "Right arm"), - Self::LeftFoot => write!(f, "Left foot"), - Self::RightFoot => write!(f, "Right foot"), - Self::LeftLeg => write!(f, "Left leg"), - Self::RightLeg => write!(f, "Right leg"), - Self::Stomach => write!(f, "Stomach"), - Self::Chest => write!(f, "Chest"), - Self::Groin => write!(f, "Groin"), - Self::Head => write!(f, "Head"), - Self::Throat => write!(f, "Throat"), - Self::Heart => write!(f, "Heart"), - } - } -} - -#[derive(Clone, Copy, PartialEq, Eq, Display)] -pub enum FightEndType { - Victory, - Stalemate, - Loss, -} - -#[derive(Component)] -pub enum PartDamageBonus { - Education(EducationPartDamageBonus), - WeaponBonus { - value: f32, - bonus: BonusPartDamageBonus, - }, -} - -impl PartDamageBonus { - pub fn dmg_bonus(&self, part: BodyPart) -> Option { - match self { - Self::Education(edu) => edu.dmg_bonus(part), - Self::WeaponBonus { value, bonus } => bonus.dmg_bonus(part, *value), - } - } -} - -#[derive(Bundle)] -pub struct PlayerBundle { - pub name: Name, - pub id: Id, - pub player: Player, - pub level: Level, - pub crit_rate: SimpleStatBundle, - // TODO: since these two need to be tracked here anyways it might be preferable to shift all - // player specific passives here instead of tracking them on the weapons - pub acc_bonus: SimpleStatBundle, - pub dmg_bonus: SimpleStatBundle, - - pub strategy: PlayerStrategy, - pub combat_turns: CombatTurns, -} - -impl PlayerBundle { - pub fn new(name: impl ToString, id: usize, level: u16, strategy: PlayerStrategy) -> Self { - Self { - name: Name(name.to_string()), - id: Id(id), - player: Player, - level: Level(level), - crit_rate: SimpleStatBundle::new(24), - acc_bonus: SimpleStatBundle::new(0.0), - dmg_bonus: SimpleStatBundle::new(0.0), - strategy, - combat_turns: Default::default(), - } + if !reload && needs_reload { + None + } else { + Some((id, children.cloned())) } } @@ -250,7 +85,7 @@ fn designate_first( mut commands: Commands, ) { let attacker = attacker_q.iter().next().unwrap(); - let defender = defender_q.single(); + let defender = defender_q.single().unwrap(); commands.entity(attacker).insert(Current); commands.entity(defender).insert(CurrentTarget); } @@ -260,8 +95,8 @@ fn change_roles( target_q: Query>, mut commands: Commands, ) { - let current = current_q.single(); - let target = target_q.single(); + let current = current_q.single().unwrap(); + let target = target_q.single().unwrap(); // TODO: Group fights commands @@ -291,36 +126,38 @@ pub fn pick_action( (With, With), >, target_q: Query>, - usable_q: Query<(Has, &Children), With>, + usable_q: Query<(Has, Option<&Children>), With>, weapon_trigger_q: Query<&TurnTriggeredEffect>, mut commands: Commands, mut effects: Effects, metrics: Res, ) { - let (current, weapons, strat, mut turns) = p_query.single_mut(); + let (current, weapons, strat, mut turns) = p_query.single_mut().unwrap(); let (weapon, children) = match strat { - PlayerStrategy::AlwaysFists => (weapons.fists.unwrap(), Default::default()), - PlayerStrategy::AlwaysKicks => weapons - .select(WeaponSlot::Kick, true, &usable_q) - .unwrap_or_else(|| (weapons.fists.unwrap(), Default::default())), - PlayerStrategy::PrimaryMelee { reload } => weapons - .select(WeaponSlot::Primary, *reload, &usable_q) - .or_else(|| weapons.select(WeaponSlot::Melee, true, &usable_q)) - .unwrap_or_else(|| (weapons.fists.unwrap(), Default::default())), + PlayerStrategy::AlwaysFists => (weapons.fists, None), + PlayerStrategy::AlwaysKicks => select_weapon(weapons, WeaponSlot::Kick, true, &usable_q) + .unwrap_or_else(|| (weapons.fists, Default::default())), + PlayerStrategy::PrimaryMelee { reload } => { + select_weapon(weapons, WeaponSlot::Primary, *reload, &usable_q) + .or_else(|| select_weapon(weapons, WeaponSlot::Melee, true, &usable_q)) + .unwrap_or_else(|| (weapons.fists, Default::default())) + } PlayerStrategy::InOrder { order, reload } => order .iter() - .find_map(|slot| weapons.select(*slot, *reload, &usable_q)) - .unwrap_or_else(|| (weapons.fists.unwrap(), Default::default())), + .find_map(|slot| select_weapon(weapons, *slot, *reload, &usable_q)) + .unwrap_or_else(|| (weapons.fists, Default::default())), }; - metrics.increment_counter(current, "turn", 1); - metrics.increment_counter(weapon, "turn", 1); + metrics.increment_counter(Some(current), "turn", 1); + metrics.increment_counter(Some(weapon), "turn", 1); commands.entity(weapon).insert(Current); - let target = target_q.single(); + let target = target_q.single().unwrap(); - for effect in weapon_trigger_q.iter_many(children.get()) { - effect.trigger(&mut effects, current, target); + if let Some(children) = children { + for effect in weapon_trigger_q.iter_many(children.get()) { + effect.trigger(&mut effects, current, target); + } } turns.0 += 1; @@ -368,12 +205,12 @@ pub fn use_damaging_weapon( Entity, &EffectiveStat, &EffectiveStat, - &armour::ArmourBodyParts, + &ArmourBodyParts, &mut SimpleStatEffective, ), With, >, - armour_q: Query<&armour::ArmourBodyPart>, + armour_q: Query<&ArmourBodyPart>, (damage_proc_q, part_bonus_q): (Query<&DamageProcEffect>, Query<&PartDamageBonus>), (mut ammo_q, mut temp_q): ( Query<( @@ -392,13 +229,13 @@ pub fn use_damaging_weapon( Effects, ), ) { - let Ok((weapon, w_dmg, acc, dmg_bonus, crit, children, non_targeted)) = weapon_q.get_single() + let Ok((weapon, w_dmg, acc, dmg_bonus, crit, children, non_targeted)) = weapon_q.single() else { return; }; let (player, player_spd, player_str, player_crit, acc_bonus, p_dmg_bonus, attacker) = - player_q.single(); - let (target, target_dex, target_def, armour_parts, mut health) = target_q.single_mut(); + player_q.single().unwrap(); + let (target, target_dex, target_def, armour_parts, mut health) = target_q.single_mut().unwrap(); if let Ok(mut uses) = temp_q.get_mut(weapon) { uses.0 -= 1; @@ -448,10 +285,10 @@ pub fn use_damaging_weapon( + 30.0; loop { - let rounds = ammo.as_mut().map(|(ref mut ammo, clips, rof)| { - let rounds = (rng.gen_range(rof.clone()).round() as u16).clamp(1, ammo.0); - metrics.increment_counter(player, "rounds_fired", rounds.into()); - metrics.increment_counter(weapon, "rounds_fired", rounds.into()); + let rounds = ammo.as_mut().map(|(ammo, clips, rof)| { + let rounds = (rng.random_range(rof.clone()).round() as u16).clamp(1, ammo.0); + metrics.increment_counter(Some(player), "rounds_fired", rounds.into()); + metrics.increment_counter(Some(weapon), "rounds_fired", rounds.into()); ammo.0 -= rounds; if ammo.0 == 0 { if clips.value == 0 { @@ -469,7 +306,7 @@ pub fn use_damaging_weapon( base_hit_chance + acc_eff.value * (1.0 - base_hit_chance) }; - if hit_chance <= 1.0 && !rng.gen_bool(hit_chance as f64) { + if hit_chance <= 1.0 && !rng.random_bool(hit_chance as f64) { log!(logger, "miss_target", { weapon: weapon, actor: player, @@ -477,8 +314,8 @@ pub fn use_damaging_weapon( rounds: rounds, hit_chance: hit_chance, }); - metrics.increment_counter(player, "miss", 1); - metrics.increment_counter(weapon, "miss", 1); + metrics.increment_counter(Some(player), "miss", 1); + metrics.increment_counter(Some(weapon), "miss", 1); if multi_attack_proc.is_none() { return; @@ -492,8 +329,8 @@ pub fn use_damaging_weapon( let mult = match body_part { BodyPart::Head | BodyPart::Heart | BodyPart::Throat => { - metrics.increment_counter(player, "crit", 1); - metrics.increment_counter(weapon, "crit", 1); + metrics.increment_counter(Some(player), "crit", 1); + metrics.increment_counter(Some(weapon), "crit", 1); 1.0 } BodyPart::LeftHand @@ -506,8 +343,8 @@ pub fn use_damaging_weapon( BodyPart::Groin | BodyPart::Stomach | BodyPart::Chest => 1.0 / 1.75, }; - metrics.increment_counter(player, "hit", 1); - metrics.increment_counter(weapon, "hit", 1); + metrics.increment_counter(Some(player), "hit", 1); + metrics.increment_counter(Some(weapon), "hit", 1); let armour_parts = armour_q.get(armour_parts.0[body_part.into()]).unwrap(); let piece = rng.sample(armour_parts); @@ -530,15 +367,15 @@ pub fn use_damaging_weapon( let dmg = dmg_intrinsic * w_dmg.0 - * dmg_bonus.value + * (1.0 + dmg_bonus.value) * (1.0 - armour_mitigation) * (1.0 - def_mitigation) * mult * dmg_spread; let dmg = dmg.round() as u32; - metrics.record_histogram(player, "dmg", dmg); - metrics.record_histogram(weapon, "dmg", dmg); + metrics.record_histogram(Some(player), "dmg", dmg); + metrics.record_histogram(Some(weapon), "dmg", dmg); if dmg > 0 { for effect in damage_proc_q.iter_many(children.get()) { @@ -548,7 +385,7 @@ pub fn use_damaging_weapon( continue; } let chance = (value / 100.0) as f64; - if chance > 1.0 || rng.gen_bool(chance) { + if chance > 1.0 || rng.random_bool(chance) { match bonus { MultiTurnBonus::Blindfire => { multi_attack_proc = Some(MultiAttack::Blindfire) @@ -559,26 +396,26 @@ pub fn use_damaging_weapon( } MultiTurnBonus::Rage => { multi_attack_proc = - Some(MultiAttack::Rage(rng.gen_range(2..=8))) + Some(MultiAttack::Rage(rng.random_range(2..=8))) } MultiTurnBonus::DoubleTap => { multi_attack_proc = Some(MultiAttack::DoubleTap { first_shot: true }) } }; - metrics.increment_counter(player, bonus.counter_label(), 1); - metrics.increment_counter(weapon, bonus.counter_label(), 1); + metrics.increment_counter(Some(player), bonus.counter_label(), 1); + metrics.increment_counter(Some(weapon), bonus.counter_label(), 1); } } DamageProcEffect::SelfEffect { value, bonus } => { let chance = (value / 100.0) as f64; - if chance > 1.0 || rng.gen_bool(chance) { + if chance > 1.0 || rng.random_bool(chance) { bonus.spawn(player, &mut effects); } } DamageProcEffect::OpponentEffect { value, bonus } => { let chance = (value / 100.0) as f64; - if chance > 1.0 || rng.gen_bool(chance) { + if chance > 1.0 || rng.random_bool(chance) { bonus.spawn(target, &mut effects, &mut rng.0); } } @@ -592,19 +429,20 @@ pub fn use_damaging_weapon( log!(logger, "hit_target", { actor: player, + acc: acc_eff.value, recipient: target, - weapon: weapon, + weapon, part: %body_part, part_mult: mult, - dmg: dmg, - rounds: rounds, + rounds, health_before: health_before, health_after: health.value, - dmg_spread: dmg_spread, - dmg_intrinsic: dmg_intrinsic, - armour_mitigation: armour_mitigation, - def_mitigation: def_mitigation, - weapon_dmg: w_dmg.0, + dmg, + dmg_spread, + dmg_intrinsic, + dmg_weapon: w_dmg.0, + armour_mitigation, + def_mitigation, bonus_dmg: dmg_bonus.value, hit_chance: hit_chance, crit_rate: crit.value, @@ -623,17 +461,23 @@ pub fn use_damaging_weapon( FightEndType::Loss }, }); - metrics.increment_counter(player, "victory", 1); + metrics.increment_counter(Some(player), "victory", 1); } } + // Technically only douple tap and blindfire have this condition, but we can run into with + // invalid bonus/weapon combinations without checking this for all bonuses + if ammo.as_ref().is_some_and(|(a, _, _)| a.0 == 0) { + break; + } + match multi_attack_proc { Some(MultiAttack::Blindfire) => { - if !ammo.as_ref().map(|(a, _, _)| a.0 != 0).unwrap_or_default() { + acc_eff.value -= 5.0 / 50.0; + // Prevent infinite loop if used on a melee + if ammo.is_none() { break; } - - acc_eff.value -= 5.0 / 50.0; } Some(MultiAttack::Fury { first_hit: true }) => { multi_attack_proc = Some(MultiAttack::Fury { first_hit: false }) @@ -658,7 +502,7 @@ pub fn check_stalemate( mut logger: Logger, metrics: Res, ) { - let (current, current_turns, attacker) = current_q.single(); + let (current, current_turns, attacker) = current_q.single().unwrap(); if *state == FightStatus::Ongoing && current_turns.0 >= 25 && attacker { commands.entity(current).insert(Defeated); let target = target_q.single(); @@ -668,7 +512,7 @@ pub fn check_stalemate( recipient: target, fight_end_type: %FightEndType::Stalemate, }); - metrics.increment_counter(current, "stalemate", 1); + metrics.increment_counter(Some(current), "stalemate", 1); if other_attackers_q.is_empty() { *state = FightStatus::Over @@ -705,7 +549,7 @@ fn record_post_fight_stats( metrics: Res, ) { for (player, health) in player_q.iter() { - metrics.record_histogram(player, "rem_health", health.value as u32); + metrics.record_histogram(Some(player), "rem_health", health.value as u32); } } diff --git a/src/player/stats.rs b/src/player/stats.rs index ca49dcd..bf823b5 100644 --- a/src/player/stats.rs +++ b/src/player/stats.rs @@ -1,411 +1,17 @@ use std::marker::PhantomData; use bevy_ecs::prelude::*; +use proxisim_models::{ + bundle::stat::{ + AdditiveBonus, AdditiveBonuses, AmmoControl, BaselineStat, ClipSize, Clips, CritRate, + DamageBonus, Defence, Dexterity, EffectiveStat, Health, MultiplicativeBonus, + MultiplicativeBonuses, SimpleStatBonus, SimpleStatEffective, SimpleStatMarker, + SimpleStatSnapshot, Speed, StatMarker, StatSnapshot, Strength, WeaponAccuracy, + }, + hierarchy::Parent, +}; -use crate::{hierarchy::Parent, player::BodyPart, Stages}; - -pub trait SimpleStatMarker: Send + Sync + 'static { - type ValueType: Send + Sync + Copy + std::fmt::Display + 'static; - type BonusType: Send + Sync + Copy + 'static; - - fn apply_bonus(value: Self::ValueType, bonus: Self::BonusType) -> Self::ValueType; - fn revert_bonus(value: Self::ValueType, bonus: Self::BonusType) -> Self::ValueType; - fn denormalise_value(value: Self::ValueType) -> Self::ValueType { - value - } - fn denormalise_bonus(value: Self::BonusType) -> Self::BonusType { - value - } -} - -#[derive(Component)] -pub struct SimpleStatBaseline { - pub value: Stat::ValueType, - marker: PhantomData, -} - -#[derive(Component)] -pub struct SimpleStatEffective { - pub value: Stat::ValueType, - marker: PhantomData, -} - -#[derive(Component)] -pub struct SimpleStatBonus { - pub label: &'static str, - pub value: Stat::BonusType, - marker: PhantomData, -} - -impl SimpleStatBonus { - pub fn new(label: &'static str, value: Stat::BonusType) -> Self { - Self { - label, - value, - marker: PhantomData, - } - } -} - -#[derive(Component)] -struct SimpleStatSnapshot { - value: Stat::ValueType, - marker: PhantomData, -} - -#[derive(Bundle)] -pub struct SimpleStatBundle { - baseline: SimpleStatBaseline, - effective: SimpleStatEffective, -} - -impl SimpleStatBundle { - pub fn new(value: Stat::ValueType) -> Self { - Self { - baseline: SimpleStatBaseline { - value, - marker: PhantomData, - }, - effective: SimpleStatEffective { - value, - marker: PhantomData, - }, - } - } -} - -impl Clone for SimpleStatEffective { - fn clone(&self) -> Self { - *self - } -} - -impl Copy for SimpleStatEffective where Stat::ValueType: Copy {} - -#[derive(Default)] -pub struct CritRate; - -impl SimpleStatMarker for CritRate { - type ValueType = u16; - type BonusType = u16; - fn apply_bonus(value: Self::ValueType, bonus: Self::BonusType) -> Self::ValueType { - value + bonus - } - fn revert_bonus(value: Self::ValueType, bonus: Self::BonusType) -> Self::ValueType { - value - bonus - } -} - -impl std::ops::Add<&SimpleStatEffective> for &SimpleStatEffective -where - Stat: SimpleStatMarker, - Stat::ValueType: std::ops::Add, -{ - type Output = SimpleStatEffective; - - fn add(self, rhs: &SimpleStatEffective) -> Self::Output { - SimpleStatEffective { - value: self.value + rhs.value, - marker: PhantomData, - } - } -} - -impl rand::distributions::Distribution for SimpleStatEffective { - fn sample(&self, rng: &mut R) -> BodyPart { - if rng.gen_ratio((self.value) as u32, 200) { - match rng.gen_range(1..=10) { - 1 => BodyPart::Heart, - 2 => BodyPart::Throat, - _ => BodyPart::Heart, - } - } else { - match rng.gen_range(1..=20) { - 1 => BodyPart::LeftHand, - 2 => BodyPart::RightHand, - 3 => BodyPart::LeftArm, - 4 => BodyPart::RightArm, - 5 => BodyPart::LeftFoot, - 6 => BodyPart::RightFoot, - 7 | 8 => BodyPart::RightLeg, - 9 | 10 => BodyPart::LeftLeg, - 11..=15 => BodyPart::Chest, - 16 => BodyPart::Groin, - _ => BodyPart::Stomach, - } - } - } -} - -#[derive(Default)] -pub struct AmmoControl; - -impl SimpleStatMarker for AmmoControl { - type ValueType = f32; - type BonusType = f32; - fn apply_bonus(value: Self::ValueType, bonus: Self::BonusType) -> Self::ValueType { - value + bonus - } - fn revert_bonus(value: Self::ValueType, bonus: Self::BonusType) -> Self::ValueType { - value - bonus - } - fn denormalise_value(value: Self::ValueType) -> Self::ValueType { - value * 100.0 - } - fn denormalise_bonus(value: Self::BonusType) -> Self::BonusType { - value * 100.0 - } -} - -#[derive(Default)] -pub struct DamageBonus; - -impl SimpleStatMarker for DamageBonus { - type ValueType = f32; - type BonusType = f32; - fn apply_bonus(value: Self::ValueType, bonus: Self::BonusType) -> Self::ValueType { - value + bonus - } - fn revert_bonus(value: Self::ValueType, bonus: Self::BonusType) -> Self::ValueType { - value - bonus - } - fn denormalise_value(value: Self::ValueType) -> Self::ValueType { - value * 100.0 - } - fn denormalise_bonus(value: Self::BonusType) -> Self::BonusType { - value * 100.0 - } -} - -#[derive(Default)] -pub struct WeaponAccuracy; - -impl SimpleStatMarker for WeaponAccuracy { - type ValueType = f32; - type BonusType = f32; - fn apply_bonus(value: Self::ValueType, bonus: Self::BonusType) -> Self::ValueType { - value + bonus - } - fn revert_bonus(value: Self::ValueType, bonus: Self::BonusType) -> Self::ValueType { - value - bonus - } - fn denormalise_value(value: Self::ValueType) -> Self::ValueType { - value * 50.0 + 50.0 - } - fn denormalise_bonus(value: Self::BonusType) -> Self::BonusType { - value * 50.0 - } -} - -#[derive(Default)] -pub struct ClipSize; - -impl SimpleStatMarker for ClipSize { - type ValueType = u16; - type BonusType = f32; - fn apply_bonus(value: Self::ValueType, bonus: Self::BonusType) -> Self::ValueType { - ((value as f32) * bonus).round() as u16 - } - fn revert_bonus(value: Self::ValueType, bonus: Self::BonusType) -> Self::ValueType { - ((value as f32) / bonus).round() as u16 - } -} - -#[derive(Default)] -pub struct Clips; - -impl SimpleStatMarker for Clips { - type ValueType = u16; - type BonusType = i16; - fn apply_bonus(value: Self::ValueType, bonus: Self::BonusType) -> Self::ValueType { - ((value as i16) + bonus) as u16 - } - fn revert_bonus(value: Self::ValueType, bonus: Self::BonusType) -> Self::ValueType { - ((value as i16) - bonus) as u16 - } -} - -#[derive(Default)] -pub struct Health; - -impl SimpleStatMarker for Health { - type ValueType = u16; - type BonusType = u16; - fn apply_bonus(value: Self::ValueType, bonus: Self::BonusType) -> Self::ValueType { - value + bonus - } - fn revert_bonus(value: Self::ValueType, bonus: Self::BonusType) -> Self::ValueType { - value - bonus - } -} - -#[derive(Debug, Clone, Copy)] -pub enum StatType { - Str, - Def, - Spd, - Dex, -} - -pub trait StatMarker: Send + Sync + 'static { - fn stat_type() -> StatType; -} - -#[derive(Debug, Default)] -pub struct Strength; -impl StatMarker for Strength { - fn stat_type() -> StatType { - StatType::Str - } -} - -#[derive(Debug, Default)] -pub struct Defence; -impl StatMarker for Defence { - fn stat_type() -> StatType { - StatType::Def - } -} - -#[derive(Debug, Default)] -pub struct Speed; -impl StatMarker for Speed { - fn stat_type() -> StatType { - StatType::Spd - } -} - -#[derive(Debug, Default)] -pub struct Dexterity; -impl StatMarker for Dexterity { - fn stat_type() -> StatType { - StatType::Dex - } -} - -#[derive(Component)] -pub struct BaselineStat { - pub value: f32, - pub marker: PhantomData, -} - -impl Default for BaselineStat { - fn default() -> Self { - Self { - value: 10.0, - marker: PhantomData, - } - } -} - -#[derive(Component, Default)] -pub struct EffectiveStat { - pub value: f32, - pub marker: PhantomData, -} - -#[derive(Component)] -pub struct AdditiveBonuses { - pub factor: f32, - pub marker: PhantomData, -} - -impl Default for AdditiveBonuses { - fn default() -> Self { - Self { - factor: 1.0, - marker: PhantomData, - } - } -} - -#[derive(Component)] -pub struct MultiplicativeBonuses { - pub factor: f32, - pub marker: PhantomData, -} - -impl Default for MultiplicativeBonuses { - fn default() -> Self { - Self { - factor: 1.0, - marker: PhantomData, - } - } -} - -#[derive(Bundle, Default)] -pub struct StatBundle { - baseline: BaselineStat, - additive: AdditiveBonuses, - multiplicative: MultiplicativeBonuses, - effective: EffectiveStat, -} - -#[derive(Component)] -struct StatSnapshot { - additive_bonuses: f32, - multiplicative_bonuses: f32, - effective: f32, - marker: PhantomData, -} - -impl StatBundle { - pub fn new(baseline: f32) -> Self { - Self { - baseline: BaselineStat { - value: baseline, - marker: PhantomData, - }, - effective: EffectiveStat { - value: baseline, - marker: PhantomData, - }, - additive: AdditiveBonuses { - factor: 1.0, - marker: PhantomData, - }, - multiplicative: MultiplicativeBonuses { - factor: 1.0, - marker: PhantomData, - }, - } - } -} - -#[derive(Component)] -pub struct AdditiveBonus { - pub label: &'static str, - pub value: f32, - marker: PhantomData, -} - -impl AdditiveBonus { - pub fn new(label: &'static str, value: f32) -> Self { - Self { - label, - value, - marker: PhantomData, - } - } -} - -#[derive(Component)] -pub struct MultiplicativeBonus { - pub label: &'static str, - pub value: f32, - marker: PhantomData, -} - -impl MultiplicativeBonus { - pub fn new(label: &'static str, value: f32) -> Self { - Self { - label, - value, - marker: PhantomData, - } - } -} +use crate::Stages; fn add_additive_bonus( In(entities): In>, diff --git a/src/player/status_effect.rs b/src/player/status_effect.rs index ceb6b7c..918d4a2 100644 --- a/src/player/status_effect.rs +++ b/src/player/status_effect.rs @@ -1,19 +1,20 @@ use std::{collections::VecDeque, marker::PhantomData}; use bevy_ecs::prelude::*; +use proxisim_models::{ + bundle::stat::{ + AdditiveBonus, Defence, Dexterity, MultiplicativeBonus, Speed, StatMarker, Strength, + }, + hierarchy::Parent, +}; use rand::Rng as _; use crate::{ + Rng, Stages, effect::{Effects, TimeLimitedEffect}, - hierarchy::{HierarchyBuilder, Parent}, log, log::Logger, weapon::temp::AssociatedWeapon, - Rng, Stages, -}; - -use super::stats::{ - AdditiveBonus, Defence, Dexterity, MultiplicativeBonus, Speed, StatMarker, Strength, }; #[derive(Component)] @@ -101,19 +102,6 @@ impl DebuffingTempMarker for FlashGrenade { } } -#[derive(Component, Default)] -pub struct Sand; - -impl DebuffingTempMarker for Sand { - type Stat = Speed; - fn factor() -> f32 { - 1.0 / 5.0 - } - fn duration() -> std::ops::Range { - 15.0..20.0 - } -} - #[derive(Component)] struct LinkedComponents([Entity; N]); @@ -136,7 +124,7 @@ where value: f32, label: &'static str, ) -> [Entity; 1] { - <(T,) as Stats<1>>::spawn_additive_effects(effects, target, value, label) + [effects.spawn(AdditiveBonus::::new(label, value), target)] } } @@ -155,7 +143,7 @@ macro_rules! impl_n_stats { }; } -impl_n_stats!(1, A); +// impl_n_stats!(1, A); impl_n_stats!(2, A, B); impl_n_stats!(3, A, B, C); impl_n_stats!(4, A, B, C, D); @@ -407,10 +395,10 @@ fn remove_additive_status_effect( continue; } - let duration = rng.gen_range(Temp::duration()); + let duration = rng.random_range(Temp::duration()); commands.entity(effect).insert(TimeLimitedEffect(duration)); let stack_size = stack.as_ref().map_or(0, |s| s.effects.len()) as i32; @@ -547,7 +535,6 @@ pub(crate) fn configure(stages: &mut Stages) { register_debuff_temp::(stages); register_debuff_temp::(stages); register_debuff_temp::(stages); - register_debuff_temp::(stages); register_status_effect::<1, Withered>(stages); register_status_effect::<1, Weakened>(stages); diff --git a/src/weapon/bonus.rs b/src/weapon/bonus.rs index 7d6a34b..ebd1f46 100644 --- a/src/weapon/bonus.rs +++ b/src/weapon/bonus.rs @@ -1,102 +1,26 @@ use bevy_ecs::prelude::*; - -use crate::{ - effect::{Effects, TurnLimitedEffect}, - hierarchy::{HierarchyBuilder, Parent}, - player::{ - stats::{ +use proxisim_models::{ + bundle::{ + bonus::{BonusPartDamageBonus, BonusValue, WeaponBonusType}, + player::PartDamageBonus, + stat::{ AdditiveBonus, AmmoControl, Clips, CritRate, DamageBonus, SimpleStatBonus, SimpleStatEffective, Speed, Strength, WeaponAccuracy, }, - status_effect::{ - AdditiveStatusEffect, Crippled, Demoralise, Frozen, Motivate, Slow, Weakened, Withered, - }, - BodyPart, PartDamageBonus, }, + hierarchy::{HierarchyBuilder, Parent}, +}; + +use crate::{ Stages, + effect::{Effects, TurnLimitedEffect}, + player::status_effect::{ + AdditiveStatusEffect, Crippled, Demoralise, Frozen, Motivate, Slow, Weakened, Withered, + }, }; use super::{DamageProcEffect, FirstTurnEffect, TurnTriggeredEffect}; -#[derive(Component, Debug, Clone, Copy)] -#[cfg_attr(feature = "json", derive(serde::Deserialize))] -#[cfg_attr(feature = "json", serde(rename_all = "snake_case"))] -pub enum WeaponBonus { - // Weapon passives - Berserk, - Conserve, - Expose, - Grace, - Powerful, - Specialist, - - // Turn triggered passives - Empower, - Quicken, - - // First turn effects - Assassinate, - - // Additive status effects triggered by damaging hits - Cripple, - Demoralise, - Freeze, - Motivate, - Slow, - Toxin, - Weaken, - Wither, - - // DOT status effects - Bleed, - Burning, - Lacerate, - Poison, - SevereBurning, - - // Other status effects - Eviscerate, - Paralyse, - Schock, - Stun, - - // Multi attack bonuses - Blindfire, - Fury, - DoubleTap, - Rage, - - // Body part multipliers - Achilles, - Crusher, - Cupid, - Deadeye, - Roshambo, - Throttle, - - // Attack nullification types - Homerun, - Parry, -} - -#[derive(Component)] -pub struct BonusValue(f32); - -#[derive(Bundle)] -pub struct WeaponBonusBundle { - pub bonus: WeaponBonus, - pub value: BonusValue, -} - -impl WeaponBonusBundle { - pub fn new(bonus: WeaponBonus, value: f32) -> Self { - Self { - bonus, - value: BonusValue(value), - } - } -} - #[derive(Clone, Copy)] pub enum TurnTriggeredBonus { Empower, @@ -188,7 +112,7 @@ impl OpponentStatusEffect { Self::Slow => { effects.spawn(AdditiveStatusEffect::<1, Slow>::default(), target); } - Self::Toxin => match rng.gen_range(0..4) { + Self::Toxin => match rng.random_range(0..4) { 0 => OpponentStatusEffect::Cripple.spawn(target, effects, rng), 1 => OpponentStatusEffect::Slow.spawn(target, effects, rng), 2 => OpponentStatusEffect::Weaken.spawn(target, effects, rng), @@ -219,7 +143,7 @@ impl SelfStatusEffect { } } -#[derive(Clone, Copy)] +/* #[derive(Clone, Copy)] pub enum BonusPartDamageBonus { Achilles, Crusher, @@ -270,21 +194,17 @@ impl BonusPartDamageBonus { } } } -} +} */ pub(crate) fn prepare_bonuses( - bonus_q: Query<( - &Parent, - &WeaponBonus, - &BonusValue, - Option<&SimpleStatEffective>, - )>, + bonus_q: Query<(&Parent, &WeaponBonusType, &BonusValue)>, + clips_q: Query<&SimpleStatEffective>, mut effects: Effects, mut commands: Commands, ) { - for (weapon, bonus, value, clips) in bonus_q.iter() { + for (weapon, bonus, value) in bonus_q.iter() { match bonus { - WeaponBonus::Berserk => { + WeaponBonusType::Berserk => { effects.spawn( SimpleStatBonus::::new("beserk", value.0 / 100.0), weapon.get(), @@ -294,19 +214,19 @@ pub(crate) fn prepare_bonuses( weapon.get(), ); } - WeaponBonus::Conserve => { + WeaponBonusType::Conserve => { effects.spawn( SimpleStatBonus::::new("conserve", value.0 / 100.0), weapon.get(), ); } - WeaponBonus::Expose => { + WeaponBonusType::Expose => { effects.spawn( SimpleStatBonus::::new("expose", (value.0 / 0.5) as u16), weapon.get(), ); } - WeaponBonus::Grace => { + WeaponBonusType::Grace => { effects.spawn( SimpleStatBonus::::new("grace", -value.0 / 2.0 / 100.0), weapon.get(), @@ -316,27 +236,27 @@ pub(crate) fn prepare_bonuses( weapon.get(), ); } - WeaponBonus::Powerful => { + WeaponBonusType::Powerful => { effects.spawn( SimpleStatBonus::::new("powerful", value.0 / 100.0), weapon.get(), ); } - WeaponBonus::Specialist => { + WeaponBonusType::Specialist => { effects.spawn( SimpleStatBonus::::new("specialist", value.0 / 100.0), weapon.get(), ); - effects.spawn( - SimpleStatBonus::::new( - "specialist", - -clips.map(|c| c.value as i16).unwrap_or_default(), - ), - weapon.get(), - ); + + if let Ok(clips) = clips_q.get(weapon.get()) { + effects.spawn( + SimpleStatBonus::::new("specialist", -(clips.value as i16)), + weapon.get(), + ); + } } - WeaponBonus::Empower => { + WeaponBonusType::Empower => { commands .spawn(TurnTriggeredEffect::Bonus { value: value.0, @@ -344,7 +264,7 @@ pub(crate) fn prepare_bonuses( }) .set_parent(weapon.get()); } - WeaponBonus::Quicken => { + WeaponBonusType::Quicken => { commands .spawn(TurnTriggeredEffect::Bonus { value: value.0, @@ -353,7 +273,7 @@ pub(crate) fn prepare_bonuses( .set_parent(weapon.get()); } - WeaponBonus::Assassinate => { + WeaponBonusType::Assassinate => { commands .spawn(FirstTurnEffect::Bonus { value: value.0, @@ -362,7 +282,7 @@ pub(crate) fn prepare_bonuses( .set_parent(weapon.get()); } - WeaponBonus::Blindfire => { + WeaponBonusType::Blindfire => { commands .spawn(DamageProcEffect::MultiTurn { value: value.0, @@ -370,7 +290,7 @@ pub(crate) fn prepare_bonuses( }) .set_parent(weapon.get()); } - WeaponBonus::Fury => { + WeaponBonusType::Fury => { commands .spawn(DamageProcEffect::MultiTurn { value: value.0, @@ -378,7 +298,7 @@ pub(crate) fn prepare_bonuses( }) .set_parent(weapon.get()); } - WeaponBonus::Rage => { + WeaponBonusType::Rage => { commands .spawn(DamageProcEffect::MultiTurn { value: value.0, @@ -386,7 +306,7 @@ pub(crate) fn prepare_bonuses( }) .set_parent(weapon.get()); } - WeaponBonus::DoubleTap => { + WeaponBonusType::DoubleTap => { commands .spawn(DamageProcEffect::MultiTurn { value: value.0, @@ -395,7 +315,7 @@ pub(crate) fn prepare_bonuses( .set_parent(weapon.get()); } - WeaponBonus::Achilles => { + WeaponBonusType::Achilles => { commands .spawn(PartDamageBonus::WeaponBonus { value: value.0 / 100.0, @@ -403,7 +323,7 @@ pub(crate) fn prepare_bonuses( }) .set_parent(weapon.get()); } - WeaponBonus::Cupid => { + WeaponBonusType::Cupid => { commands .spawn(PartDamageBonus::WeaponBonus { value: value.0 / 100.0, @@ -411,7 +331,7 @@ pub(crate) fn prepare_bonuses( }) .set_parent(weapon.get()); } - WeaponBonus::Crusher => { + WeaponBonusType::Crusher => { commands .spawn(PartDamageBonus::WeaponBonus { value: value.0 / 100.0, @@ -419,7 +339,7 @@ pub(crate) fn prepare_bonuses( }) .set_parent(weapon.get()); } - WeaponBonus::Deadeye => { + WeaponBonusType::Deadeye => { commands .spawn(PartDamageBonus::WeaponBonus { value: value.0 / 100.0, @@ -427,7 +347,7 @@ pub(crate) fn prepare_bonuses( }) .set_parent(weapon.get()); } - WeaponBonus::Throttle => { + WeaponBonusType::Throttle => { commands .spawn(PartDamageBonus::WeaponBonus { value: value.0 / 100.0, @@ -435,7 +355,7 @@ pub(crate) fn prepare_bonuses( }) .set_parent(weapon.get()); } - WeaponBonus::Roshambo => { + WeaponBonusType::Roshambo => { commands .spawn(PartDamageBonus::WeaponBonus { value: value.0 / 100.0, @@ -444,7 +364,7 @@ pub(crate) fn prepare_bonuses( .set_parent(weapon.get()); } - WeaponBonus::Cripple => { + WeaponBonusType::Cripple => { commands .spawn(DamageProcEffect::OpponentEffect { value: value.0, @@ -452,7 +372,7 @@ pub(crate) fn prepare_bonuses( }) .set_parent(weapon.get()); } - WeaponBonus::Demoralise => { + WeaponBonusType::Demoralise => { commands .spawn(DamageProcEffect::OpponentEffect { value: value.0, @@ -460,7 +380,7 @@ pub(crate) fn prepare_bonuses( }) .set_parent(weapon.get()); } - WeaponBonus::Freeze => { + WeaponBonusType::Freeze => { commands .spawn(DamageProcEffect::OpponentEffect { value: value.0, @@ -468,7 +388,7 @@ pub(crate) fn prepare_bonuses( }) .set_parent(weapon.get()); } - WeaponBonus::Slow => { + WeaponBonusType::Slow => { commands .spawn(DamageProcEffect::OpponentEffect { value: value.0, @@ -476,7 +396,7 @@ pub(crate) fn prepare_bonuses( }) .set_parent(weapon.get()); } - WeaponBonus::Toxin => { + WeaponBonusType::Toxin => { commands .spawn(DamageProcEffect::OpponentEffect { value: value.0, @@ -484,7 +404,7 @@ pub(crate) fn prepare_bonuses( }) .set_parent(weapon.get()); } - WeaponBonus::Weaken => { + WeaponBonusType::Weaken => { commands .spawn(DamageProcEffect::OpponentEffect { value: value.0, @@ -492,7 +412,7 @@ pub(crate) fn prepare_bonuses( }) .set_parent(weapon.get()); } - WeaponBonus::Wither => { + WeaponBonusType::Wither => { commands .spawn(DamageProcEffect::OpponentEffect { value: value.0, @@ -501,7 +421,7 @@ pub(crate) fn prepare_bonuses( .set_parent(weapon.get()); } - WeaponBonus::Motivate => { + WeaponBonusType::Motivate => { commands .spawn(DamageProcEffect::SelfEffect { value: value.0, diff --git a/src/weapon/mod.rs b/src/weapon/mod.rs index 44e6ae1..64599eb 100644 --- a/src/weapon/mod.rs +++ b/src/weapon/mod.rs @@ -1,19 +1,25 @@ use bevy_ecs::prelude::*; +use proxisim_models::{ + bundle::{ + passive::{Education, EducationPartDamageBonus, FactionUpgrades, Merits}, + player::{Current, PartDamageBonus, Weapons}, + stat::{ + AdditiveBonus, AmmoControl, ClipSize, Clips, CritRate, DamageBonus, Dexterity, + SimpleStatBonus, SimpleStatEffective, WeaponAccuracy, + }, + weapon::{ + Ammo, EquippedMods, Experience, Japanese, NeedsReload, Usable, Weapon, WeaponCategory, + WeaponMod, WeaponSlot, + }, + }, + hierarchy::{HierarchyBuilder, Parent}, +}; use crate::{ effect::{Effects, TurnLimitedEffect}, - hierarchy::{HierarchyBuilder, Parent}, log, log::Logger, - passives::{Education, EducationPartDamageBonus, FactionUpgrades, Merits}, - player::{ - stats::{ - AdditiveBonus, AmmoControl, ClipSize, Clips, CritRate, DamageBonus, Dexterity, - SimpleStatBonus, SimpleStatBundle, SimpleStatEffective, WeaponAccuracy, - }, - Current, PartDamageBonus, Weapons, - }, - Id, Name, Stages, + Stages, }; use self::bonus::{ @@ -23,102 +29,6 @@ use self::bonus::{ pub mod bonus; pub mod temp; -#[derive(Component)] -pub struct Usable; - -#[derive(Component)] -pub struct Weapon; - -#[derive(Component, Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "json", derive(serde::Deserialize))] -#[cfg_attr(feature = "json", serde(rename_all = "snake_case"))] -pub enum WeaponSlot { - Primary, - Secondary, - Melee, - Temporary, - Fists, - Kick, -} - -#[derive(Component, Debug, Clone, Copy)] -pub enum WeaponVerb { - Hit, - Kicked, - Fired, - Threw, - Exploded, -} - -#[derive(Component, Clone, Copy)] -#[cfg_attr(feature = "json", derive(serde::Deserialize))] -#[cfg_attr(feature = "json", serde(rename_all = "snake_case"))] -pub enum WeaponCategory { - HeavyArtillery, - MachineGun, - Rifle, - Smg, - Shotgun, - Pistol, - Clubbing, - Piercing, - Slashing, - Mechanical, - Temporary, - HandToHand, -} - -#[derive(Component, Debug)] -pub struct DamageStat(pub f32); - -#[derive(Component)] -pub struct Japanese; - -#[derive(Component)] -pub struct Ammo(pub u16); - -#[derive(Component)] -pub struct RateOfFire(pub [u16; 2]); - -#[derive(Component)] -pub struct NeedsReload; - -#[derive(Clone, Copy)] -#[cfg_attr(feature = "json", derive(serde::Deserialize))] -#[cfg_attr(feature = "json", serde(rename_all = "snake_case"))] -pub enum WeaponMod { - ReflexSight, - HolographicSight, - AcogSight, - ThermalSight, - Laser1mw, - Laser5mw, - Laser30mw, - Laser100mw, - SmallSuppressor, - StandardSuppressor, - LargeSuppressor, - ExtendedMags, - HighCapacityMags, - ExtraClip, - ExtraClip2, - AdjustableTrigger, - HairTrigger, - Bipod, - Tripod, - CustomGrip, - SkeetChoke, - ImprovedChoke, - FullChoke, - RecoilPad, - StandardBrake, - HeavyDutyBrake, - TacticalBrake, - SmallLight, - PrecisionLight, - TacticalIlluminator, -} - #[derive(Clone, Copy)] pub enum TurnTriggeredMod { Bipod, @@ -247,109 +157,6 @@ pub enum DamageProcEffect { }, } -#[derive(Component)] -pub struct EquippedMods(pub Vec); - -#[derive(Component)] -pub struct Experience(pub f32); - -#[derive(Bundle)] -pub struct WeaponBundle { - pub usable: Usable, - pub weapon: Weapon, - pub name: Name, - pub id: Id, - pub verb: WeaponVerb, - pub slot: WeaponSlot, -} - -#[derive(Bundle)] -pub struct DamagingWeaponBundle { - pub crit_rate: SimpleStatBundle, - pub dmg: DamageStat, - pub acc: SimpleStatBundle, - pub dmg_bonus: SimpleStatBundle, - pub equipped_mods: EquippedMods, - pub experience: Experience, - pub category: WeaponCategory, -} - -#[derive(Bundle)] -pub struct AmmoWeaponBundle { - pub ammo: Ammo, - pub clips: SimpleStatBundle, - pub clip_size: SimpleStatBundle, - pub rate_of_fire: RateOfFire, - pub ammo_control: SimpleStatBundle, -} - -impl WeaponBundle { - pub fn new(name: String, id: usize, verb: WeaponVerb, slot: WeaponSlot) -> Self { - Self { - usable: Usable, - weapon: Weapon, - name: Name(name), - id: Id(id), - verb, - slot, - } - } - - pub fn fists(id: usize) -> Self { - Self::new("Fists".to_owned(), id, WeaponVerb::Hit, WeaponSlot::Fists) - } - - pub fn kick(id: usize) -> Self { - Self::new("Kick".to_owned(), id, WeaponVerb::Kicked, WeaponSlot::Kick) - } -} - -impl DamagingWeaponBundle { - pub fn new( - dmg: f32, - acc: f32, - mods: Vec, - exp: f32, - category: WeaponCategory, - ) -> Self { - Self { - crit_rate: SimpleStatBundle::new(0), - dmg: DamageStat(dmg / 10.0), - acc: SimpleStatBundle::new((acc - 50.0) / 50.0), - dmg_bonus: SimpleStatBundle::new(1.0), - equipped_mods: EquippedMods(mods), - experience: Experience(exp), - category, - } - } - - pub fn fists() -> Self { - // NOTE: The accuracy value is taken from the attack page. The damage value here differs - // from the one in Proxima's simulator, but in some quick tests 10.0 proofed to be a better fit - // This might have changed in the weapon damage update - Self::new(10.0, 50.0, Vec::default(), 0.0, WeaponCategory::HandToHand) - } - - pub fn kick() -> Self { - // NOTE: The accuracy value is taken from the attack page. The damage value here differs - // from the one in Proxima's simulator, but in some quick tests 30.0 proofed to be a better fit - // This might have changed in the weapon damage update - Self::new(30.0, 40.0, Vec::default(), 0.0, WeaponCategory::HandToHand) - } -} - -impl AmmoWeaponBundle { - pub fn new(clips: u16, clip_size: u16, rof: [u16; 2]) -> Self { - Self { - ammo: Ammo(clip_size), - clips: SimpleStatBundle::new(clips - 1), - clip_size: SimpleStatBundle::new(clip_size), - rate_of_fire: RateOfFire(rof), - ammo_control: SimpleStatBundle::new(0.0), - } - } -} - fn set_owner(weapons_q: Query<(Entity, &Weapons)>, mut commands: Commands) { for (player, weapons) in weapons_q.iter() { if let Some(primary) = weapons.primary { @@ -364,10 +171,8 @@ fn set_owner(weapons_q: Query<(Entity, &Weapons)>, mut commands: Commands) { if let Some(temp) = weapons.temporary { commands.entity(temp).set_parent(player); } - commands.entity(weapons.fists.unwrap()).set_parent(player); - if let Some(kick) = weapons.kick { - commands.entity(kick).set_parent(player); - } + commands.entity(weapons.fists).set_parent(player); + commands.entity(weapons.kick).set_parent(player); } } diff --git a/src/weapon/temp.rs b/src/weapon/temp.rs index 4c849c1..3ad7ace 100644 --- a/src/weapon/temp.rs +++ b/src/weapon/temp.rs @@ -1,202 +1,33 @@ use bevy_ecs::prelude::*; -use strum::Display; - -use crate::{ - effect::Effects, - player::{ - status_effect::{ - ConcussionGrenade, FlashGrenade, PepperSpray, Sand, SmokeGrenade, TearGas, - TempDebuffEffect, - }, - Current, CurrentTarget, - }, - Stages, +use proxisim_models::bundle::{ + player::{Current, CurrentTarget, Player}, + weapon::{BuffingTemp, DebuffingTemp, Usable, Uses}, }; -use super::{DamagingWeaponBundle, Usable, WeaponBundle, WeaponCategory, WeaponSlot, WeaponVerb}; - -#[derive(Component, Default)] -pub struct NonTargeted; - -#[derive(Component, Default)] -pub struct Temporary; - -#[derive(Component)] -pub struct Uses(pub u16); - -impl Default for Uses { - fn default() -> Self { - Self(1) - } -} +use crate::{ + Stages, + effect::Effects, + log, + log::Logger, + player::status_effect::{ + AdditiveStatusEffect, ConcussionGrenade, FlashGrenade, Hardened, Hastened, PepperSpray, + Sharpened, SmokeGrenade, Strengthened, TearGas, TempDebuffEffect, + }, +}; #[derive(Component)] pub struct AssociatedWeapon(pub Entity); -#[derive(Component, Debug, Clone, Copy, Display)] -pub enum DebuffingTemp { - TearGas, - SmokeGrenade, - PepperSpray, - ConcussionGrenade, - FlashGrenade, - Sand, -} - -#[derive(Bundle, Default)] -pub struct TemporaryBundle { - pub temporary: Temporary, - pub uses: Uses, -} - -#[derive(Debug, Clone, Copy)] -#[cfg_attr(feature = "json", derive(serde::Deserialize))] -pub enum Temp { - Heg, - NailBomb, - Grenade, - Fireworks, - ClaymoreMine, - TearGas, - SmokeGrenade, - PepperSpray, - ConcussionGrenade, - FlashGrenade, - Sand, -} - -impl Temp { - pub fn spawn(self, world: &mut World, id: usize) -> EntityWorldMut<'_> { - match self { - Self::Heg => world.spawn(( - WeaponBundle::new( - "HEG".to_owned(), - id, - WeaponVerb::Exploded, - WeaponSlot::Temporary, - ), - DamagingWeaponBundle::new(90.00, 116.00, vec![], 0.0, WeaponCategory::Temporary), - TemporaryBundle::default(), - NonTargeted, - )), - Self::NailBomb => world.spawn(( - WeaponBundle::new( - "Nail Bomb".to_owned(), - id, - WeaponVerb::Exploded, - WeaponSlot::Temporary, - ), - DamagingWeaponBundle::new(99.00, 106.00, vec![], 0.0, WeaponCategory::Temporary), - TemporaryBundle::default(), - NonTargeted, - )), - Self::Grenade => world.spawn(( - WeaponBundle::new( - "Grenade".to_owned(), - id, - WeaponVerb::Exploded, - WeaponSlot::Temporary, - ), - DamagingWeaponBundle::new(86.00, 106.00, vec![], 0.0, WeaponCategory::Temporary), - TemporaryBundle::default(), - NonTargeted, - )), - Self::Fireworks => world.spawn(( - WeaponBundle::new( - "Fireworks".to_owned(), - id, - WeaponVerb::Exploded, - WeaponSlot::Temporary, - ), - DamagingWeaponBundle::new(45.00, 34.00, vec![], 0.0, WeaponCategory::Temporary), - TemporaryBundle::default(), - NonTargeted, - )), - Self::ClaymoreMine => world.spawn(( - WeaponBundle::new( - "Claymore Mine".to_owned(), - id, - WeaponVerb::Exploded, - WeaponSlot::Temporary, - ), - DamagingWeaponBundle::new(83.00, 27.00, vec![], 0.0, WeaponCategory::Temporary), - TemporaryBundle::default(), - NonTargeted, - )), - Self::TearGas => world.spawn(( - WeaponBundle::new( - "Tear Gas".to_owned(), - id, - WeaponVerb::Exploded, - WeaponSlot::Temporary, - ), - TemporaryBundle::default(), - DebuffingTemp::TearGas, - )), - Self::SmokeGrenade => world.spawn(( - WeaponBundle::new( - "Smoke Grenade".to_owned(), - id, - WeaponVerb::Exploded, - WeaponSlot::Temporary, - ), - TemporaryBundle::default(), - DebuffingTemp::SmokeGrenade, - )), - Self::PepperSpray => world.spawn(( - WeaponBundle::new( - "Pepper Spray".to_owned(), - id, - WeaponVerb::Exploded, - WeaponSlot::Temporary, - ), - TemporaryBundle::default(), - DebuffingTemp::PepperSpray, - )), - Self::ConcussionGrenade => world.spawn(( - WeaponBundle::new( - "Concussion Grenade".to_owned(), - id, - WeaponVerb::Exploded, - WeaponSlot::Temporary, - ), - TemporaryBundle::default(), - DebuffingTemp::ConcussionGrenade, - )), - Self::FlashGrenade => world.spawn(( - WeaponBundle::new( - "Flash Grenade".to_owned(), - id, - WeaponVerb::Exploded, - WeaponSlot::Temporary, - ), - TemporaryBundle::default(), - DebuffingTemp::FlashGrenade, - )), - Self::Sand => world.spawn(( - WeaponBundle::new( - "Sand".to_owned(), - id, - WeaponVerb::Exploded, - WeaponSlot::Temporary, - ), - TemporaryBundle::default(), - DebuffingTemp::Sand, - )), - } - } -} - fn use_debuffing_temp( mut temp_q: Query<(Entity, &DebuffingTemp, &mut Uses), With>, target_q: Query>, mut effects: Effects, mut commands: Commands, ) { - let Ok((weapon, temp, mut uses)) = temp_q.get_single_mut() else { + let Ok((weapon, temp, mut uses)) = temp_q.single_mut() else { return; }; - let target = target_q.single(); + let target = target_q.single().unwrap(); match temp { DebuffingTemp::TearGas => effects.spawn_and_insert( @@ -224,13 +55,54 @@ fn use_debuffing_temp( target, AssociatedWeapon(weapon), ), - DebuffingTemp::Sand => effects.spawn_and_insert( - TempDebuffEffect::::default(), - target, + }; + + uses.0 -= 1; + if uses.0 == 0 { + commands.entity(weapon).remove::(); + } +} + +fn use_buffing_temp( + mut temp_q: Query<(Entity, &BuffingTemp, &mut Uses), With>, + current_q: Query, With)>, + mut effects: Effects, + mut commands: Commands, + mut logger: Logger, +) { + let Ok((weapon, temp, mut uses)) = temp_q.single_mut() else { + return; + }; + let current = current_q.single().unwrap(); + + match temp { + BuffingTemp::Serotonin => effects.spawn_and_insert( + AdditiveStatusEffect::<1, Hardened>::default(), + current, + AssociatedWeapon(weapon), + ), + BuffingTemp::Tyrosine => effects.spawn_and_insert( + AdditiveStatusEffect::<1, Sharpened>::default(), + current, + AssociatedWeapon(weapon), + ), + BuffingTemp::Melatonin => effects.spawn_and_insert( + AdditiveStatusEffect::<1, Hastened>::default(), + current, + AssociatedWeapon(weapon), + ), + BuffingTemp::Epinephrine => effects.spawn_and_insert( + AdditiveStatusEffect::<1, Strengthened>::default(), + current, AssociatedWeapon(weapon), ), }; + log!(logger, "used_buff_temp", { + actor: current, + weapon: weapon, + }); + uses.0 -= 1; if uses.0 == 0 { commands.entity(weapon).remove::(); @@ -245,5 +117,6 @@ fn restore_uses(mut uses_q: Query<&mut Uses>) { pub(crate) fn configure(stages: &mut Stages) { stages.turn.add_systems(use_debuffing_temp); + stages.turn.add_systems(use_buffing_temp); stages.restore.add_systems(restore_uses); }