updates?
This commit is contained in:
parent
e7d6b74aab
commit
35413b563c
33 changed files with 10238 additions and 1891 deletions
15
models/Cargo.toml
Normal file
15
models/Cargo.toml
Normal file
|
|
@ -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"] }
|
||||
200
models/src/bundle/armour.rs
Normal file
200
models/src/bundle/armour.rs
Normal file
|
|
@ -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<Immunity>);
|
||||
|
||||
#[derive(Component, Default)]
|
||||
pub struct Armour;
|
||||
|
||||
#[derive(Component, Default)]
|
||||
pub struct ArmourCoverage(pub ArmourVec<f32>);
|
||||
|
||||
impl From<CoverageDto> 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<Entity>,
|
||||
pub torso: Option<Entity>,
|
||||
pub legs: Option<Entity>,
|
||||
pub feet: Option<Entity>,
|
||||
pub hands: Option<Entity>,
|
||||
}
|
||||
|
||||
#[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<BodyPartCoverage>,
|
||||
}
|
||||
|
||||
#[derive(Component, Debug)]
|
||||
pub struct ArmourBodyParts(pub ArmourVec<Entity>);
|
||||
|
||||
pub struct ArmourIter<'a> {
|
||||
state: Option<ArmourIterState>,
|
||||
equipped_armour: &'a PlayerArmour,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for ArmourIter<'a> {
|
||||
type Item = Entity;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
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<BodyPart> 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>(pub [T; 10]);
|
||||
|
||||
impl<T> std::ops::Index<ArmourBodyPartSlot> for ArmourVec<T> {
|
||||
type Output = T;
|
||||
|
||||
fn index(&self, index: ArmourBodyPartSlot) -> &Self::Output {
|
||||
&self.0[index as usize]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::ops::IndexMut<ArmourBodyPartSlot> for ArmourVec<T> {
|
||||
fn index_mut(&mut self, index: ArmourBodyPartSlot) -> &mut Self::Output {
|
||||
&mut self.0[index as usize]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoIterator for ArmourVec<T> {
|
||||
type Item = T;
|
||||
|
||||
type IntoIter = std::array::IntoIter<T, 10>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Distribution<Option<&'a BodyPartCoverage>> for &'a ArmourBodyPart {
|
||||
fn sample<R: rand::prelude::Rng + ?Sized>(&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
|
||||
}
|
||||
}
|
||||
199
models/src/bundle/bonus.rs
Normal file
199
models/src/bundle/bonus.rs
Normal file
|
|
@ -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<f32> {
|
||||
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,
|
||||
}
|
||||
14
models/src/bundle/mod.rs
Normal file
14
models/src/bundle/mod.rs
Normal file
|
|
@ -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);
|
||||
207
models/src/bundle/passive.rs
Normal file
207
models/src/bundle/passive.rs
Normal file
|
|
@ -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<f32> {
|
||||
match part {
|
||||
BodyPart::Throat => Some(0.10),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Bundle, Default)]
|
||||
pub(crate) struct PassiveBundle {
|
||||
pub merits: Merits,
|
||||
pub education: Education,
|
||||
pub faction: FactionUpgrades,
|
||||
}
|
||||
184
models/src/bundle/player.rs
Normal file
184
models/src/bundle/player.rs
Normal file
|
|
@ -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<Entity>,
|
||||
pub secondary: Option<Entity>,
|
||||
pub melee: Option<Entity>,
|
||||
pub temporary: Option<Entity>,
|
||||
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<WeaponSlot>,
|
||||
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<f32> {
|
||||
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<CritRate>,
|
||||
// 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<WeaponAccuracy>,
|
||||
pub dmg_bonus: SimpleStatBundle<DamageBonus>,
|
||||
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
408
models/src/bundle/stat.rs
Normal file
408
models/src/bundle/stat.rs
Normal file
|
|
@ -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<Stat: SimpleStatMarker> {
|
||||
pub value: Stat::ValueType,
|
||||
marker: PhantomData<Stat>,
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct SimpleStatEffective<Stat: SimpleStatMarker> {
|
||||
pub value: Stat::ValueType,
|
||||
marker: PhantomData<Stat>,
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct SimpleStatBonus<Stat: SimpleStatMarker> {
|
||||
pub label: &'static str,
|
||||
pub value: Stat::BonusType,
|
||||
marker: PhantomData<Stat>,
|
||||
}
|
||||
|
||||
impl<Stat: SimpleStatMarker> SimpleStatBonus<Stat> {
|
||||
pub fn new(label: &'static str, value: Stat::BonusType) -> Self {
|
||||
Self {
|
||||
label,
|
||||
value,
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct SimpleStatSnapshot<Stat: SimpleStatMarker> {
|
||||
pub value: Stat::ValueType,
|
||||
pub marker: PhantomData<Stat>,
|
||||
}
|
||||
|
||||
#[derive(Bundle)]
|
||||
pub struct SimpleStatBundle<Stat: SimpleStatMarker> {
|
||||
pub baseline: SimpleStatBaseline<Stat>,
|
||||
pub effective: SimpleStatEffective<Stat>,
|
||||
}
|
||||
|
||||
impl<Stat: SimpleStatMarker> SimpleStatBundle<Stat> {
|
||||
pub fn new(value: Stat::ValueType) -> Self {
|
||||
Self {
|
||||
baseline: SimpleStatBaseline {
|
||||
value,
|
||||
marker: PhantomData,
|
||||
},
|
||||
effective: SimpleStatEffective {
|
||||
value,
|
||||
marker: PhantomData,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Stat: SimpleStatMarker> Clone for SimpleStatEffective<Stat> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<Stat: SimpleStatMarker> Copy for SimpleStatEffective<Stat> 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<BodyPart> for SimpleStatEffective<CritRate> {
|
||||
fn sample<R: rand::prelude::Rng + ?Sized>(&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<Stat> std::ops::Add<&SimpleStatEffective<Stat>> for &SimpleStatEffective<Stat>
|
||||
where
|
||||
Stat: SimpleStatMarker,
|
||||
Stat::ValueType: std::ops::Add<Stat::ValueType, Output = Stat::ValueType>,
|
||||
{
|
||||
type Output = SimpleStatEffective<Stat>;
|
||||
|
||||
fn add(self, rhs: &SimpleStatEffective<Stat>) -> 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<Stat: StatMarker> {
|
||||
pub value: f32,
|
||||
pub marker: PhantomData<Stat>,
|
||||
}
|
||||
|
||||
impl<Stat: StatMarker> Default for BaselineStat<Stat> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
value: 10.0,
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component, Default)]
|
||||
pub struct EffectiveStat<Stat: StatMarker> {
|
||||
pub value: f32,
|
||||
pub marker: PhantomData<Stat>,
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct AdditiveBonuses<Stat: StatMarker> {
|
||||
pub factor: f32,
|
||||
pub marker: PhantomData<Stat>,
|
||||
}
|
||||
|
||||
impl<Stat: StatMarker> Default for AdditiveBonuses<Stat> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
factor: 1.0,
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct MultiplicativeBonuses<Stat: StatMarker> {
|
||||
pub factor: f32,
|
||||
pub marker: PhantomData<Stat>,
|
||||
}
|
||||
|
||||
impl<Stat: StatMarker> Default for MultiplicativeBonuses<Stat> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
factor: 1.0,
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Bundle, Default)]
|
||||
pub struct StatBundle<Stat: StatMarker> {
|
||||
pub baseline: BaselineStat<Stat>,
|
||||
pub additive: AdditiveBonuses<Stat>,
|
||||
pub multiplicative: MultiplicativeBonuses<Stat>,
|
||||
pub effective: EffectiveStat<Stat>,
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct StatSnapshot<Stat: StatMarker> {
|
||||
pub additive_bonuses: f32,
|
||||
pub multiplicative_bonuses: f32,
|
||||
pub effective: f32,
|
||||
pub marker: PhantomData<Stat>,
|
||||
}
|
||||
|
||||
impl<Stat: StatMarker> StatBundle<Stat> {
|
||||
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<Stat: StatMarker> {
|
||||
pub label: &'static str,
|
||||
pub value: f32,
|
||||
marker: PhantomData<Stat>,
|
||||
}
|
||||
|
||||
impl<Stat: StatMarker> AdditiveBonus<Stat> {
|
||||
pub fn new(label: &'static str, value: f32) -> Self {
|
||||
Self {
|
||||
label,
|
||||
value,
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct MultiplicativeBonus<Stat: StatMarker> {
|
||||
pub label: &'static str,
|
||||
pub value: f32,
|
||||
marker: PhantomData<Stat>,
|
||||
}
|
||||
|
||||
impl<Stat: StatMarker> MultiplicativeBonus<Stat> {
|
||||
pub fn new(label: &'static str, value: f32) -> Self {
|
||||
Self {
|
||||
label,
|
||||
value,
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
230
models/src/bundle/weapon.rs
Normal file
230
models/src/bundle/weapon.rs
Normal file
|
|
@ -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<WeaponMod>);
|
||||
|
||||
#[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<CritRate>,
|
||||
pub dmg: DamageStat,
|
||||
pub acc: SimpleStatBundle<WeaponAccuracy>,
|
||||
pub dmg_bonus: SimpleStatBundle<DamageBonus>,
|
||||
pub equipped_mods: EquippedMods,
|
||||
pub experience: Experience,
|
||||
pub category: WeaponCategory,
|
||||
}
|
||||
|
||||
#[derive(Bundle)]
|
||||
pub struct AmmoWeaponBundle {
|
||||
pub ammo: Ammo,
|
||||
pub clips: SimpleStatBundle<Clips>,
|
||||
pub clip_size: SimpleStatBundle<ClipSize>,
|
||||
pub rate_of_fire: RateOfFire,
|
||||
pub ammo_control: SimpleStatBundle<AmmoControl>,
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
2122
models/src/dto/armour.rs
Normal file
2122
models/src/dto/armour.rs
Normal file
File diff suppressed because it is too large
Load diff
32
models/src/dto/metrics.rs
Normal file
32
models/src/dto/metrics.rs
Normal file
|
|
@ -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<u32>,
|
||||
pub entity: EntityInfo,
|
||||
pub label: &'static str,
|
||||
}
|
||||
14
models/src/dto/mod.rs
Normal file
14
models/src/dto/mod.rs
Normal file
|
|
@ -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))
|
||||
}
|
||||
107
models/src/dto/player.rs
Normal file
107
models/src/dto/player.rs
Normal file
|
|
@ -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<WeaponDto>,
|
||||
pub secondary: Option<WeaponDto>,
|
||||
pub melee: Option<WeaponDto>,
|
||||
pub temporary: Option<WeaponDto>,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "json", derive(serde::Deserialize))]
|
||||
#[derive(Default)]
|
||||
pub struct EquippedArmour {
|
||||
pub helmet: Option<ArmourDto>,
|
||||
pub body: Option<ArmourDto>,
|
||||
pub pants: Option<ArmourDto>,
|
||||
pub gloves: Option<ArmourDto>,
|
||||
pub boots: Option<ArmourDto>,
|
||||
}
|
||||
|
||||
#[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<Merits>,
|
||||
pub education: Option<Education>,
|
||||
pub weapons: EquippedWeapons,
|
||||
pub armour: EquippedArmour,
|
||||
pub faction: Option<FactionUpgrades>,
|
||||
pub drug: Option<DrugCooldown>,
|
||||
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::<Strength>::new(self.stats.str),
|
||||
StatBundle::<Defence>::new(self.stats.def),
|
||||
StatBundle::<Speed>::new(self.stats.spd),
|
||||
StatBundle::<Dexterity>::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
|
||||
}
|
||||
}
|
||||
5609
models/src/dto/weapon.rs
Normal file
5609
models/src/dto/weapon.rs
Normal file
File diff suppressed because it is too large
Load diff
178
models/src/hierarchy.rs
Normal file
178
models/src/hierarchy.rs
Normal file
|
|
@ -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<Entity>);
|
||||
|
||||
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>() {
|
||||
children.0.push(self.child);
|
||||
} else {
|
||||
parent.insert(Children(vec![self.child]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AddChildren {
|
||||
parent: Entity,
|
||||
children: Vec<Entity>,
|
||||
}
|
||||
|
||||
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>() {
|
||||
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::<Children>()
|
||||
.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::<Vec<_>>(),
|
||||
);
|
||||
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::<Parent>();
|
||||
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>() {
|
||||
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::<Children>() {
|
||||
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::<Parent>()
|
||||
.update_location();
|
||||
}
|
||||
if let Some(mut children) = self.get_mut::<Children>() {
|
||||
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
|
||||
}
|
||||
}
|
||||
3
models/src/lib.rs
Normal file
3
models/src/lib.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
pub mod bundle;
|
||||
pub mod dto;
|
||||
// pub mod hierarchy;
|
||||
Loading…
Add table
Add a link
Reference in a new issue