use bevy hierarchy system

This commit is contained in:
TotallyNot 2025-11-03 18:54:07 +01:00
parent 35413b563c
commit cfe2631578
Signed by: pyrite
GPG key ID: 7F1BA9170CD35D15
15 changed files with 246 additions and 643 deletions

View file

@ -1,6 +1,6 @@
use std::borrow::Cow; use std::borrow::Cow;
use bevy_ecs::{entity::Entity, world::World}; use bevy_ecs::{prelude::*, relationship::RelatedSpawner};
use strum::Display; use strum::Display;
use crate::{ use crate::{
@ -186,8 +186,8 @@ impl ArmourDto {
.collect() .collect()
} }
pub fn spawn(self, world: &mut World) -> Entity { pub fn spawn(self, spawner: &mut RelatedSpawner<'_, ChildOf>) -> Entity {
let mut commands = world.spawn(draw_id()); let mut commands = spawner.spawn(draw_id());
commands.insert(( commands.insert((
Name(self.name.to_string()), Name(self.name.to_string()),

View file

@ -52,19 +52,6 @@ pub struct PlayerDto {
impl PlayerDto { impl PlayerDto {
pub fn spawn(self, world: &mut World) -> EntityWorldMut<'_> { 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()); let mut commands = world.spawn(draw_id());
commands.insert(( commands.insert((
@ -85,22 +72,48 @@ impl PlayerDto {
faction, faction,
}); });
commands.insert(Weapons { let mut weapons = None;
primary, commands.with_children(|spawner| {
secondary, let primary = self.weapons.primary.map(|p| p.spawn(spawner));
melee, let secondary = self.weapons.secondary.map(|s| s.spawn(spawner));
temporary, let melee = self.weapons.melee.map(|m| m.spawn(spawner));
kick, let temporary = self.weapons.temporary.map(|m| m.spawn(spawner));
fists, let fists = WeaponDto::FISTS.spawn(spawner);
let kick = WeaponDto::KICK.spawn(spawner);
weapons = Some(Weapons {
primary,
secondary,
melee,
temporary,
kick,
fists,
});
});
if let Some(weapons) = weapons {
commands.insert(weapons);
}
let mut armour = None;
commands.with_children(|spawner| {
let head = self.armour.helmet.map(|a| a.spawn(spawner));
let torso = self.armour.body.map(|a| a.spawn(spawner));
let legs = self.armour.pants.map(|a| a.spawn(spawner));
let hands = self.armour.gloves.map(|a| a.spawn(spawner));
let feet = self.armour.boots.map(|a| a.spawn(spawner));
armour = Some(PlayerArmour {
torso,
head,
legs,
feet,
hands,
})
}); });
commands.insert(PlayerArmour { if let Some(armour) = armour {
torso, commands.insert(armour);
head, }
legs,
feet,
hands,
});
commands commands
} }

View file

@ -1,6 +1,6 @@
use std::borrow::Cow; use std::borrow::Cow;
use bevy_ecs::prelude::*; use bevy_ecs::{prelude::*, relationship::RelatedSpawner};
use crate::{ use crate::{
bundle::{ bundle::{
@ -252,8 +252,8 @@ impl WeaponDto {
}) })
} }
pub fn spawn(self, world: &mut World) -> Entity { pub fn spawn(self, spawner: &mut RelatedSpawner<'_, ChildOf>) -> Entity {
let mut commands = world.spawn(draw_id()); let mut commands = spawner.spawn(draw_id());
let verb = match self.kind { let verb = match self.kind {
WeaponSlot::Primary | WeaponSlot::Secondary => WeaponVerb::Fired, WeaponSlot::Primary | WeaponSlot::Secondary => WeaponVerb::Fired,

View file

@ -1,178 +0,0 @@
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
}
}

View file

@ -1,3 +1,2 @@
pub mod bundle; pub mod bundle;
pub mod dto; pub mod dto;
// pub mod hierarchy;

View file

@ -1,18 +1,15 @@
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use proxisim_models::{ use proxisim_models::bundle::armour::{
bundle::armour::{ ArmourBodyPart, ArmourBodyPartSlot, ArmourBodyParts, ArmourCoverage, ArmourValue, ArmourVec,
ArmourBodyPart, ArmourBodyPartSlot, ArmourBodyParts, ArmourCoverage, ArmourValue, BodyPartCoverage, Immunities, Immunity, PlayerArmour,
ArmourVec, BodyPartCoverage, Immunities, Immunity, PlayerArmour,
},
hierarchy::HierarchyBuilder,
}; };
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
use crate::{ use crate::{
Stages,
player::status_effect::{ player::status_effect::{
ConcussionGrenade, FlashGrenade, PepperSpray, TearGas, TempDebuffImmunity, ConcussionGrenade, FlashGrenade, PepperSpray, TearGas, TempDebuffImmunity,
}, },
Stages,
}; };
fn generate_body_parts( fn generate_body_parts(
@ -24,7 +21,7 @@ fn generate_body_parts(
let mut parts = ArmourVec::<ArmourBodyPart>::default(); let mut parts = ArmourVec::<ArmourBodyPart>::default();
for (armour, coverage, armour_value, immunities) in armour_q.iter_many(equipped_armour) { for (armour, coverage, armour_value, immunities) in armour_q.iter_many(equipped_armour) {
commands.entity(armour).set_parent(player); // commands.entity(player).add_child(armour);
if let Some(immunities) = immunities { if let Some(immunities) = immunities {
let mut player = commands.entity(player); let mut player = commands.entity(player);

View file

@ -1,10 +1,7 @@
use std::{any::TypeId, collections::HashMap, sync::Mutex}; use std::{any::TypeId, collections::HashMap, sync::Mutex};
use bevy_ecs::{prelude::*, system::SystemParam}; use bevy_ecs::{prelude::*, system::SystemParam};
use proxisim_models::{ use proxisim_models::bundle::player::{Current, Defender, Player};
bundle::player::{Current, Defender, Player},
hierarchy::{HierarchyBuilder, Parent},
};
use crate::Stages; use crate::Stages;
@ -77,13 +74,9 @@ impl<'w, 's> Effects<'w, 's> {
) -> Entity { ) -> Entity {
let id = self.registry.type_map.get(&TypeId::of::<T>()).unwrap(); let id = self.registry.type_map.get(&TypeId::of::<T>()).unwrap();
let spawned_effect = self let spawned_effect = self.commands.spawn(effect).insert(*id).insert(addins).id();
.commands
.spawn(effect) self.commands.entity(to).add_child(spawned_effect);
.insert(*id)
.insert(addins)
.set_parent(to)
.id();
self.scheduled self.scheduled
.create .create
@ -202,7 +195,7 @@ pub(crate) fn run_effects(world: &mut World) {
}; };
for entity in entities { for entity in entities {
let parent = world.entity(entity).get::<Parent>().unwrap().get(); let parent = world.entity(entity).get::<ChildOf>().unwrap().parent();
world.entity_mut(parent).remove_child(entity); world.entity_mut(parent).remove_child(entity);
world.despawn(entity); world.despawn(entity);
} }
@ -277,12 +270,12 @@ fn mark_permanent_effects(effect_q: Query<Entity, With<EffectId>>, mut commands:
} }
fn remove_transient_effects( fn remove_transient_effects(
effect_q: Query<(Entity, &Parent), (With<EffectId>, Without<Permanent>)>, effect_q: Query<(Entity, &ChildOf), (With<EffectId>, Without<Permanent>)>,
mut commands: Commands, mut commands: Commands,
) { ) {
for (effect, target) in effect_q.iter() { for (effect, target) in effect_q.iter() {
commands.entity(effect).despawn(); commands.entity(effect).despawn();
commands.entity(target.get()).remove_child(effect); commands.entity(target.parent()).remove_child(effect);
} }
} }

View file

@ -3,12 +3,11 @@ use std::collections::HashMap;
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use proxisim_models::{ use proxisim_models::{
bundle::{ bundle::{
Id, Name,
player::{Attacker, Player}, player::{Attacker, Player},
weapon::Weapon, weapon::Weapon,
Id, Name,
}, },
dto::metrics::EntityInfo, dto::metrics::EntityInfo,
hierarchy::Parent,
}; };
use crate::Stages; use crate::Stages;
@ -18,7 +17,7 @@ pub struct EntityRegistry(pub HashMap<Entity, EntityInfo>);
fn read_entities( fn read_entities(
player_q: Query<(Entity, &Name, &Id, Has<Attacker>), With<Player>>, player_q: Query<(Entity, &Name, &Id, Has<Attacker>), With<Player>>,
weapon_q: Query<(Entity, &Parent, &Name, &Id), With<Weapon>>, weapon_q: Query<(Entity, &ChildOf, &Name, &Id), With<Weapon>>,
mut registry: ResMut<EntityRegistry>, mut registry: ResMut<EntityRegistry>,
) { ) {
for (player, name, id, is_attacker) in player_q.iter() { for (player, name, id, is_attacker) in player_q.iter() {
@ -33,7 +32,7 @@ fn read_entities(
} }
for (weapon, player, name, id) in weapon_q.iter() { for (weapon, player, name, id) in weapon_q.iter() {
let (_, _, player_id, _) = player_q.get(player.get()).unwrap(); let (_, _, player_id, _) = player_q.get(player.parent()).unwrap();
registry.0.insert( registry.0.insert(
weapon, weapon,
EntityInfo::Weapon { EntityInfo::Weapon {

View file

@ -1,178 +0,0 @@
use bevy_ecs::{
prelude::*,
system::{Command, EntityCommands},
};
#[derive(Component, Clone, Copy)]
pub struct Parent(Entity);
impl Parent {
pub fn get(&self) -> Entity {
self.0
}
}
#[derive(Component, 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
}
}

View file

@ -1,16 +1,13 @@
use std::sync::Mutex; use std::sync::Mutex;
use bevy_ecs::{prelude::*, query::QuerySingleError, system::SystemParam}; use bevy_ecs::{prelude::*, query::QuerySingleError, system::SystemParam};
use proxisim_models::{ use proxisim_models::bundle::{
bundle::{ stat::{
stat::{ AdditiveBonus, BaselineStat, ClipSize, Clips, CritRate, DamageBonus, Defence, Dexterity,
AdditiveBonus, BaselineStat, ClipSize, Clips, CritRate, DamageBonus, Defence, EffectiveStat, MultiplicativeBonus, SimpleStatBaseline, SimpleStatBonus,
Dexterity, EffectiveStat, MultiplicativeBonus, SimpleStatBaseline, SimpleStatBonus, SimpleStatEffective, SimpleStatMarker, Speed, StatMarker, Strength, WeaponAccuracy,
SimpleStatEffective, SimpleStatMarker, Speed, StatMarker, Strength, WeaponAccuracy,
},
weapon::WeaponVerb,
}, },
hierarchy::Children, weapon::WeaponVerb,
}; };
use crate::{Stages, entity_registry::EntityRegistry}; use crate::{Stages, entity_registry::EntityRegistry};
@ -533,11 +530,11 @@ fn log_stat_changes<Stat: StatMarker>(
) { ) {
for (player, baseline, effective, children) in stat_q.iter() { for (player, baseline, effective, children) in stat_q.iter() {
let effects_add: Vec<_> = add_q let effects_add: Vec<_> = add_q
.iter_many(children.get()) .iter_many(children)
.map(|bonus| (bonus.label, 100.0 * bonus.value)) .map(|bonus| (bonus.label, 100.0 * bonus.value))
.collect(); .collect();
let effects_mult: Vec<_> = mult_q let effects_mult: Vec<_> = mult_q
.iter_many(children.get()) .iter_many(children)
.map(|bonus| (bonus.label, 100.0 * bonus.value)) .map(|bonus| (bonus.label, 100.0 * bonus.value))
.collect(); .collect();
@ -570,7 +567,7 @@ fn log_simple_stat_changes<Stat: SimpleStatMarker>(
{ {
for (target, baseline, effective, children) in stat_q.iter() { for (target, baseline, effective, children) in stat_q.iter() {
let bonuses: Vec<_> = bonus_q let bonuses: Vec<_> = bonus_q
.iter_many(children.get()) .iter_many(children)
.map(|bonus| (bonus.label, Stat::denormalise_bonus(bonus.value))) .map(|bonus| (bonus.label, Stat::denormalise_bonus(bonus.value)))
.collect(); .collect();

View file

@ -1,23 +1,18 @@
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use proxisim_models::{ use proxisim_models::bundle::{
bundle::{ armour::{ArmourBodyPart, ArmourBodyParts},
armour::{ArmourBodyPart, ArmourBodyParts}, passive::{FactionUpgrades, Merits},
passive::{FactionUpgrades, Merits}, player::{
player::{ Attacker, BodyPart, ChooseWeapon, CombatTurns, Current, CurrentTarget, Defeated, Defender,
Attacker, BodyPart, ChooseWeapon, CombatTurns, Current, CurrentTarget, Defeated, FightEndType, Level, MaxHealth, PartDamageBonus, Player, PlayerStrategy, Weapons,
Defender, FightEndType, Level, MaxHealth, PartDamageBonus, Player, PlayerStrategy, },
Weapons, stat::{
}, AmmoControl, Clips, CritRate, DamageBonus, Defence, Dexterity, EffectiveStat, Health,
stat::{ SimpleStatBundle, SimpleStatEffective, Speed, Strength, WeaponAccuracy,
AmmoControl, Clips, CritRate, DamageBonus, Defence, Dexterity, EffectiveStat, Health, },
SimpleStatBundle, SimpleStatEffective, Speed, Strength, WeaponAccuracy, weapon::{
}, Ammo, DamageStat, NeedsReload, NonTargeted, RateOfFire, Usable, Uses, Weapon, WeaponSlot,
weapon::{
Ammo, DamageStat, NeedsReload, NonTargeted, RateOfFire, Usable, Uses, Weapon,
WeaponSlot,
},
}, },
hierarchy::Children,
}; };
use rand::Rng as _; use rand::Rng as _;
@ -33,12 +28,12 @@ use crate::{
pub mod stats; pub mod stats;
pub mod status_effect; pub mod status_effect;
fn select_weapon( fn select_weapon<'a>(
weapons: &Weapons, weapons: &Weapons,
slot: WeaponSlot, slot: WeaponSlot,
reload: bool, reload: bool,
usable_q: &Query<(Has<NeedsReload>, Option<&Children>), With<Usable>>, usable_q: &'a Query<(Has<NeedsReload>, Option<&Children>), With<Usable>>,
) -> Option<(Entity, Option<Children>)> { ) -> Option<(Entity, Option<&'a Children>)> {
let id = match slot { let id = match slot {
WeaponSlot::Primary => weapons.primary?, WeaponSlot::Primary => weapons.primary?,
WeaponSlot::Secondary => weapons.secondary?, WeaponSlot::Secondary => weapons.secondary?,
@ -53,7 +48,7 @@ fn select_weapon(
if !reload && needs_reload { if !reload && needs_reload {
None None
} else { } else {
Some((id, children.cloned())) Some((id, children))
} }
} }
@ -155,7 +150,7 @@ pub fn pick_action(
let target = target_q.single().unwrap(); let target = target_q.single().unwrap();
if let Some(children) = children { if let Some(children) = children {
for effect in weapon_trigger_q.iter_many(children.get()) { for effect in weapon_trigger_q.iter_many(children) {
effect.trigger(&mut effects, current, target); effect.trigger(&mut effects, current, target);
} }
} }
@ -357,7 +352,7 @@ pub fn use_damaging_weapon(
let mut dmg_bonus = dmg_bonus + p_dmg_bonus; let mut dmg_bonus = dmg_bonus + p_dmg_bonus;
for part_bonus in part_bonus_q.iter_many(children.get()) { for part_bonus in part_bonus_q.iter_many(children) {
if let Some(bonus) = part_bonus.dmg_bonus(body_part) { if let Some(bonus) = part_bonus.dmg_bonus(body_part) {
dmg_bonus.value += bonus; dmg_bonus.value += bonus;
} }
@ -378,7 +373,7 @@ pub fn use_damaging_weapon(
metrics.record_histogram(Some(weapon), "dmg", dmg); metrics.record_histogram(Some(weapon), "dmg", dmg);
if dmg > 0 { if dmg > 0 {
for effect in damage_proc_q.iter_many(children.get()) { for effect in damage_proc_q.iter_many(children) {
match *effect { match *effect {
DamageProcEffect::MultiTurn { value, bonus } => { DamageProcEffect::MultiTurn { value, bonus } => {
if multi_attack_proc.is_some() { if multi_attack_proc.is_some() {

View file

@ -1,21 +1,18 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use proxisim_models::{ use proxisim_models::bundle::stat::{
bundle::stat::{ AdditiveBonus, AdditiveBonuses, AmmoControl, BaselineStat, ClipSize, Clips, CritRate,
AdditiveBonus, AdditiveBonuses, AmmoControl, BaselineStat, ClipSize, Clips, CritRate, DamageBonus, Defence, Dexterity, EffectiveStat, Health, MultiplicativeBonus,
DamageBonus, Defence, Dexterity, EffectiveStat, Health, MultiplicativeBonus, MultiplicativeBonuses, SimpleStatBonus, SimpleStatEffective, SimpleStatMarker,
MultiplicativeBonuses, SimpleStatBonus, SimpleStatEffective, SimpleStatMarker, SimpleStatSnapshot, Speed, StatMarker, StatSnapshot, Strength, WeaponAccuracy,
SimpleStatSnapshot, Speed, StatMarker, StatSnapshot, Strength, WeaponAccuracy,
},
hierarchy::Parent,
}; };
use crate::Stages; use crate::Stages;
fn add_additive_bonus<Stat: StatMarker>( fn add_additive_bonus<Stat: StatMarker>(
In(entities): In<Vec<Entity>>, In(entities): In<Vec<Entity>>,
effect_q: Query<(&AdditiveBonus<Stat>, &Parent)>, effect_q: Query<(&AdditiveBonus<Stat>, &ChildOf)>,
mut stat_q: Query<( mut stat_q: Query<(
&BaselineStat<Stat>, &BaselineStat<Stat>,
&mut AdditiveBonuses<Stat>, &mut AdditiveBonuses<Stat>,
@ -24,7 +21,7 @@ fn add_additive_bonus<Stat: StatMarker>(
)>, )>,
) { ) {
for (bonus, player) in effect_q.iter_many(entities) { for (bonus, player) in effect_q.iter_many(entities) {
let (baseline, mut add, mult, mut eff) = stat_q.get_mut(player.get()).unwrap(); let (baseline, mut add, mult, mut eff) = stat_q.get_mut(player.parent()).unwrap();
add.factor += bonus.value; add.factor += bonus.value;
eff.value = baseline.value * add.factor * mult.factor; eff.value = baseline.value * add.factor * mult.factor;
} }
@ -32,7 +29,7 @@ fn add_additive_bonus<Stat: StatMarker>(
fn revert_additive_bonus<Stat: StatMarker>( fn revert_additive_bonus<Stat: StatMarker>(
In(entities): In<Vec<Entity>>, In(entities): In<Vec<Entity>>,
effect_q: Query<(&AdditiveBonus<Stat>, &Parent)>, effect_q: Query<(&AdditiveBonus<Stat>, &ChildOf)>,
mut stat_q: Query<( mut stat_q: Query<(
&BaselineStat<Stat>, &BaselineStat<Stat>,
&mut AdditiveBonuses<Stat>, &mut AdditiveBonuses<Stat>,
@ -41,7 +38,7 @@ fn revert_additive_bonus<Stat: StatMarker>(
)>, )>,
) { ) {
for (bonus, player) in effect_q.iter_many(entities) { for (bonus, player) in effect_q.iter_many(entities) {
let (baseline, mut add, mult, mut eff) = stat_q.get_mut(player.get()).unwrap(); let (baseline, mut add, mult, mut eff) = stat_q.get_mut(player.parent()).unwrap();
add.factor -= bonus.value; add.factor -= bonus.value;
eff.value = baseline.value * add.factor * mult.factor; eff.value = baseline.value * add.factor * mult.factor;
} }
@ -49,7 +46,7 @@ fn revert_additive_bonus<Stat: StatMarker>(
fn add_multiplicative_bonus<Stat: StatMarker>( fn add_multiplicative_bonus<Stat: StatMarker>(
In(entities): In<Vec<Entity>>, In(entities): In<Vec<Entity>>,
effect_q: Query<(&MultiplicativeBonus<Stat>, &Parent)>, effect_q: Query<(&MultiplicativeBonus<Stat>, &ChildOf)>,
mut stat_q: Query<( mut stat_q: Query<(
&BaselineStat<Stat>, &BaselineStat<Stat>,
&AdditiveBonuses<Stat>, &AdditiveBonuses<Stat>,
@ -58,7 +55,7 @@ fn add_multiplicative_bonus<Stat: StatMarker>(
)>, )>,
) { ) {
for (bonus, player) in effect_q.iter_many(entities) { for (bonus, player) in effect_q.iter_many(entities) {
let (baseline, add, mut mult, mut eff) = stat_q.get_mut(player.get()).unwrap(); let (baseline, add, mut mult, mut eff) = stat_q.get_mut(player.parent()).unwrap();
mult.factor *= bonus.value; mult.factor *= bonus.value;
eff.value = baseline.value * add.factor * mult.factor; eff.value = baseline.value * add.factor * mult.factor;
} }
@ -66,7 +63,7 @@ fn add_multiplicative_bonus<Stat: StatMarker>(
fn revert_multiplicative_bonus<Stat: StatMarker>( fn revert_multiplicative_bonus<Stat: StatMarker>(
In(entities): In<Vec<Entity>>, In(entities): In<Vec<Entity>>,
effect_q: Query<(&MultiplicativeBonus<Stat>, &Parent)>, effect_q: Query<(&MultiplicativeBonus<Stat>, &ChildOf)>,
mut stat_q: Query<( mut stat_q: Query<(
&BaselineStat<Stat>, &BaselineStat<Stat>,
&AdditiveBonuses<Stat>, &AdditiveBonuses<Stat>,
@ -75,7 +72,7 @@ fn revert_multiplicative_bonus<Stat: StatMarker>(
)>, )>,
) { ) {
for (bonus, player) in effect_q.iter_many(entities) { for (bonus, player) in effect_q.iter_many(entities) {
let (baseline, add, mut mult, mut eff) = stat_q.get_mut(player.get()).unwrap(); let (baseline, add, mut mult, mut eff) = stat_q.get_mut(player.parent()).unwrap();
mult.factor /= bonus.value; mult.factor /= bonus.value;
eff.value = baseline.value * add.factor * mult.factor; eff.value = baseline.value * add.factor * mult.factor;
} }
@ -117,22 +114,22 @@ fn restore_stats<Stat: StatMarker>(
fn apply_simple_stat_bonus<Stat: SimpleStatMarker>( fn apply_simple_stat_bonus<Stat: SimpleStatMarker>(
In(entities): In<Vec<Entity>>, In(entities): In<Vec<Entity>>,
effect_q: Query<(&SimpleStatBonus<Stat>, &Parent)>, effect_q: Query<(&SimpleStatBonus<Stat>, &ChildOf)>,
mut stat_q: Query<&mut SimpleStatEffective<Stat>>, mut stat_q: Query<&mut SimpleStatEffective<Stat>>,
) { ) {
for (bonus, target) in effect_q.iter_many(&entities) { for (bonus, target) in effect_q.iter_many(&entities) {
let mut effective = stat_q.get_mut(target.get()).unwrap(); let mut effective = stat_q.get_mut(target.parent()).unwrap();
effective.value = Stat::apply_bonus(effective.value, bonus.value); effective.value = Stat::apply_bonus(effective.value, bonus.value);
} }
} }
fn revert_simple_stat_bonus<Stat: SimpleStatMarker>( fn revert_simple_stat_bonus<Stat: SimpleStatMarker>(
In(entities): In<Vec<Entity>>, In(entities): In<Vec<Entity>>,
effect_q: Query<(&SimpleStatBonus<Stat>, &Parent)>, effect_q: Query<(&SimpleStatBonus<Stat>, &ChildOf)>,
mut stat_q: Query<&mut SimpleStatEffective<Stat>>, mut stat_q: Query<&mut SimpleStatEffective<Stat>>,
) { ) {
for (bonus, target) in effect_q.iter_many(entities) { for (bonus, target) in effect_q.iter_many(entities) {
let mut effective = stat_q.get_mut(target.get()).unwrap(); let mut effective = stat_q.get_mut(target.parent()).unwrap();
effective.value = Stat::revert_bonus(effective.value, bonus.value); effective.value = Stat::revert_bonus(effective.value, bonus.value);
} }
} }

View file

@ -1,11 +1,8 @@
use std::{collections::VecDeque, marker::PhantomData}; use std::{collections::VecDeque, marker::PhantomData};
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use proxisim_models::{ use proxisim_models::bundle::stat::{
bundle::stat::{ AdditiveBonus, Defence, Dexterity, MultiplicativeBonus, Speed, StatMarker, Strength,
AdditiveBonus, Defence, Dexterity, MultiplicativeBonus, Speed, StatMarker, Strength,
},
hierarchy::Parent,
}; };
use rand::Rng as _; use rand::Rng as _;
@ -345,7 +342,7 @@ impl AdditiveStatusEffectMarker<2> for Frozen {
fn apply_additive_status_effect<const N: usize, M: AdditiveStatusEffectMarker<N>>( fn apply_additive_status_effect<const N: usize, M: AdditiveStatusEffectMarker<N>>(
In(entities): In<Vec<Entity>>, In(entities): In<Vec<Entity>>,
effect_q: Query<(Entity, &Parent, &AdditiveStatusEffect<N, M>)>, effect_q: Query<(Entity, &ChildOf, &AdditiveStatusEffect<N, M>)>,
mut parent_q: Query<Option<&mut StatusEffectStack<M>>>, mut parent_q: Query<Option<&mut StatusEffectStack<M>>>,
mut commands: Commands, mut commands: Commands,
mut effects: Effects, mut effects: Effects,
@ -353,15 +350,15 @@ fn apply_additive_status_effect<const N: usize, M: AdditiveStatusEffectMarker<N>
) { ) {
for (entity, player, effect) in effect_q.iter_many(entities) { for (entity, player, effect) in effect_q.iter_many(entities) {
log!(logger, "apply_status_effect", { log!(logger, "apply_status_effect", {
recipient: player.get(), recipient: player.parent(),
effect: std::any::type_name::<M>(), effect: std::any::type_name::<M>(),
}); });
let stack = parent_q.get_mut(player.get()).unwrap(); let stack = parent_q.get_mut(player.parent()).unwrap();
let new_effects = <M::AffectedStats as Stats<N>>::spawn_additive_effects( let new_effects = <M::AffectedStats as Stats<N>>::spawn_additive_effects(
&mut effects, &mut effects,
player.get(), player.parent(),
M::factor() * (1.0 + effect.extra_effectiveness), M::factor() * (1.0 + effect.extra_effectiveness),
std::any::type_name::<M>(), std::any::type_name::<M>(),
); );
@ -389,13 +386,13 @@ fn apply_additive_status_effect<const N: usize, M: AdditiveStatusEffectMarker<N>
fn remove_additive_status_effect<const N: usize, M: AdditiveStatusEffectMarker<N>>( fn remove_additive_status_effect<const N: usize, M: AdditiveStatusEffectMarker<N>>(
In(entities): In<Vec<Entity>>, In(entities): In<Vec<Entity>>,
effect_q: Query<(Entity, &Parent)>, effect_q: Query<(Entity, &ChildOf)>,
mut parent_q: Query<Option<&mut StatusEffectStack<M>>>, mut parent_q: Query<Option<&mut StatusEffectStack<M>>>,
linked_q: Query<&LinkedComponents<N>>, linked_q: Query<&LinkedComponents<N>>,
mut effects: Effects, mut effects: Effects,
) { ) {
for (effect, player) in effect_q.iter_many(entities) { for (effect, player) in effect_q.iter_many(entities) {
if let Some(mut stack) = parent_q.get_mut(player.get()).unwrap() if let Some(mut stack) = parent_q.get_mut(player.parent()).unwrap()
&& stack.effects.front() == Some(&effect) && stack.effects.front() == Some(&effect)
{ {
stack.effects.pop_front(); stack.effects.pop_front();
@ -412,8 +409,8 @@ fn remove_additive_status_effect<const N: usize, M: AdditiveStatusEffectMarker<N
fn apply_temp_debuff_effect<Temp: DebuffingTempMarker>( fn apply_temp_debuff_effect<Temp: DebuffingTempMarker>(
In(entities): In<Vec<Entity>>, In(entities): In<Vec<Entity>>,
mut rng: ResMut<Rng>, mut rng: ResMut<Rng>,
temp_q: Query<(Entity, &Parent, &AssociatedWeapon)>, temp_q: Query<(Entity, &ChildOf, &AssociatedWeapon)>,
weapon_q: Query<&Parent>, weapon_q: Query<&ChildOf>,
mut parent_q: Query<( mut parent_q: Query<(
Option<&mut StatusEffectStack<Temp>>, Option<&mut StatusEffectStack<Temp>>,
Has<TempDebuffImmunity<Temp>>, Has<TempDebuffImmunity<Temp>>,
@ -422,14 +419,14 @@ fn apply_temp_debuff_effect<Temp: DebuffingTempMarker>(
mut logger: Logger, mut logger: Logger,
) { ) {
for (effect, player, weapon) in temp_q.iter_many(entities) { for (effect, player, weapon) in temp_q.iter_many(entities) {
let (stack, immunity) = parent_q.get_mut(player.get()).unwrap(); let (stack, immunity) = parent_q.get_mut(player.parent()).unwrap();
let user = weapon_q.get(weapon.0).unwrap(); let user = weapon_q.get(weapon.0).unwrap();
if immunity { if immunity {
commands.entity(effect).despawn(); commands.entity(effect).despawn();
commands.entity(player.get()).remove_child(effect); commands.entity(player.parent()).remove_child(effect);
log!(logger, "used_debuff_temp", { log!(logger, "used_debuff_temp", {
actor: user.get(), actor: user.parent(),
recipient: player.get(), recipient: player.parent(),
weapon: weapon.0, weapon: weapon.0,
immune: true, immune: true,
}); });
@ -447,7 +444,7 @@ fn apply_temp_debuff_effect<Temp: DebuffingTempMarker>(
let bonus = effects.spawn( let bonus = effects.spawn(
MultiplicativeBonus::<Temp::Stat>::new(std::any::type_name::<Temp>(), effective_factor), MultiplicativeBonus::<Temp::Stat>::new(std::any::type_name::<Temp>(), effective_factor),
player.get(), player.parent(),
); );
if let Some(mut stack) = stack { if let Some(mut stack) = stack {
@ -456,7 +453,7 @@ fn apply_temp_debuff_effect<Temp: DebuffingTempMarker>(
stack.effects.push_back(effect); stack.effects.push_back(effect);
} else { } else {
commands commands
.entity(player.get()) .entity(player.parent())
.insert(StatusEffectStack::<Temp> { .insert(StatusEffectStack::<Temp> {
effects: VecDeque::from([effect]), effects: VecDeque::from([effect]),
bonus, bonus,
@ -465,8 +462,8 @@ fn apply_temp_debuff_effect<Temp: DebuffingTempMarker>(
} }
log!(logger, "used_debuff_temp", { log!(logger, "used_debuff_temp", {
actor: user.get(), actor: user.parent(),
recipient: player.get(), recipient: player.parent(),
weapon: weapon.0, weapon: weapon.0,
immune: false, immune: false,
}); });
@ -475,14 +472,14 @@ fn apply_temp_debuff_effect<Temp: DebuffingTempMarker>(
fn remove_temp_debuff_effect<Temp: DebuffingTempMarker>( fn remove_temp_debuff_effect<Temp: DebuffingTempMarker>(
In(entities): In<Vec<Entity>>, In(entities): In<Vec<Entity>>,
temp_q: Query<&Parent>, temp_q: Query<&ChildOf>,
mut parent_q: Query<(&mut StatusEffectStack<Temp>, Has<TempDebuffImmunity<Temp>>)>, mut parent_q: Query<(&mut StatusEffectStack<Temp>, Has<TempDebuffImmunity<Temp>>)>,
mut commands: Commands, mut commands: Commands,
_logger: Logger, _logger: Logger,
mut effects: Effects, mut effects: Effects,
) { ) {
for player in temp_q.iter_many(entities) { for player in temp_q.iter_many(entities) {
let (mut stack, immunity) = parent_q.get_mut(player.get()).unwrap(); let (mut stack, immunity) = parent_q.get_mut(player.parent()).unwrap();
if immunity { if immunity {
continue; continue;
} }
@ -501,13 +498,13 @@ fn remove_temp_debuff_effect<Temp: DebuffingTempMarker>(
std::any::type_name::<Temp>(), std::any::type_name::<Temp>(),
effective_factor, effective_factor,
), ),
player.get(), player.parent(),
); );
stack.effects.pop_front(); stack.effects.pop_front();
} else { } else {
commands commands
.entity(player.get()) .entity(player.parent())
.remove::<StatusEffectStack<Temp>>(); .remove::<StatusEffectStack<Temp>>();
} }
} }

View file

@ -1,14 +1,11 @@
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use proxisim_models::{ use proxisim_models::bundle::{
bundle::{ bonus::{BonusPartDamageBonus, BonusValue, WeaponBonusType},
bonus::{BonusPartDamageBonus, BonusValue, WeaponBonusType}, player::PartDamageBonus,
player::PartDamageBonus, stat::{
stat::{ AdditiveBonus, AmmoControl, Clips, CritRate, DamageBonus, SimpleStatBonus,
AdditiveBonus, AmmoControl, Clips, CritRate, DamageBonus, SimpleStatBonus, SimpleStatEffective, Speed, Strength, WeaponAccuracy,
SimpleStatEffective, Speed, Strength, WeaponAccuracy,
},
}, },
hierarchy::{HierarchyBuilder, Parent},
}; };
use crate::{ use crate::{
@ -197,7 +194,7 @@ impl BonusPartDamageBonus {
} */ } */
pub(crate) fn prepare_bonuses( pub(crate) fn prepare_bonuses(
bonus_q: Query<(&Parent, &WeaponBonusType, &BonusValue)>, bonus_q: Query<(&ChildOf, &WeaponBonusType, &BonusValue)>,
clips_q: Query<&SimpleStatEffective<Clips>>, clips_q: Query<&SimpleStatEffective<Clips>>,
mut effects: Effects, mut effects: Effects,
mut commands: Commands, mut commands: Commands,
@ -207,227 +204,227 @@ pub(crate) fn prepare_bonuses(
WeaponBonusType::Berserk => { WeaponBonusType::Berserk => {
effects.spawn( effects.spawn(
SimpleStatBonus::<DamageBonus>::new("beserk", value.0 / 100.0), SimpleStatBonus::<DamageBonus>::new("beserk", value.0 / 100.0),
weapon.get(), weapon.parent(),
); );
effects.spawn( effects.spawn(
SimpleStatBonus::<WeaponAccuracy>::new("beserk", -value.0 / 2.0 / 50.0), SimpleStatBonus::<WeaponAccuracy>::new("beserk", -value.0 / 2.0 / 50.0),
weapon.get(), weapon.parent(),
); );
} }
WeaponBonusType::Conserve => { WeaponBonusType::Conserve => {
effects.spawn( effects.spawn(
SimpleStatBonus::<AmmoControl>::new("conserve", value.0 / 100.0), SimpleStatBonus::<AmmoControl>::new("conserve", value.0 / 100.0),
weapon.get(), weapon.parent(),
); );
} }
WeaponBonusType::Expose => { WeaponBonusType::Expose => {
effects.spawn( effects.spawn(
SimpleStatBonus::<CritRate>::new("expose", (value.0 / 0.5) as u16), SimpleStatBonus::<CritRate>::new("expose", (value.0 / 0.5) as u16),
weapon.get(), weapon.parent(),
); );
} }
WeaponBonusType::Grace => { WeaponBonusType::Grace => {
effects.spawn( effects.spawn(
SimpleStatBonus::<DamageBonus>::new("grace", -value.0 / 2.0 / 100.0), SimpleStatBonus::<DamageBonus>::new("grace", -value.0 / 2.0 / 100.0),
weapon.get(), weapon.parent(),
); );
effects.spawn( effects.spawn(
SimpleStatBonus::<WeaponAccuracy>::new("grace", value.0 / 50.0), SimpleStatBonus::<WeaponAccuracy>::new("grace", value.0 / 50.0),
weapon.get(), weapon.parent(),
); );
} }
WeaponBonusType::Powerful => { WeaponBonusType::Powerful => {
effects.spawn( effects.spawn(
SimpleStatBonus::<DamageBonus>::new("powerful", value.0 / 100.0), SimpleStatBonus::<DamageBonus>::new("powerful", value.0 / 100.0),
weapon.get(), weapon.parent(),
); );
} }
WeaponBonusType::Specialist => { WeaponBonusType::Specialist => {
effects.spawn( effects.spawn(
SimpleStatBonus::<DamageBonus>::new("specialist", value.0 / 100.0), SimpleStatBonus::<DamageBonus>::new("specialist", value.0 / 100.0),
weapon.get(), weapon.parent(),
); );
if let Ok(clips) = clips_q.get(weapon.get()) { if let Ok(clips) = clips_q.get(weapon.parent()) {
effects.spawn( effects.spawn(
SimpleStatBonus::<Clips>::new("specialist", -(clips.value as i16)), SimpleStatBonus::<Clips>::new("specialist", -(clips.value as i16)),
weapon.get(), weapon.parent(),
); );
} }
} }
WeaponBonusType::Empower => { WeaponBonusType::Empower => {
commands commands
.spawn(TurnTriggeredEffect::Bonus { .entity(weapon.parent())
.with_child(TurnTriggeredEffect::Bonus {
value: value.0, value: value.0,
bonus: TurnTriggeredBonus::Empower, bonus: TurnTriggeredBonus::Empower,
}) });
.set_parent(weapon.get());
} }
WeaponBonusType::Quicken => { WeaponBonusType::Quicken => {
commands commands
.spawn(TurnTriggeredEffect::Bonus { .entity(weapon.parent())
.with_child(TurnTriggeredEffect::Bonus {
value: value.0, value: value.0,
bonus: TurnTriggeredBonus::Quicken, bonus: TurnTriggeredBonus::Quicken,
}) });
.set_parent(weapon.get());
} }
WeaponBonusType::Assassinate => { WeaponBonusType::Assassinate => {
commands commands
.spawn(FirstTurnEffect::Bonus { .entity(weapon.parent())
.with_child(FirstTurnEffect::Bonus {
value: value.0, value: value.0,
bonus: FirstTurnBonus::Assassinate, bonus: FirstTurnBonus::Assassinate,
}) });
.set_parent(weapon.get());
} }
WeaponBonusType::Blindfire => { WeaponBonusType::Blindfire => {
commands commands
.spawn(DamageProcEffect::MultiTurn { .entity(weapon.parent())
.with_child(DamageProcEffect::MultiTurn {
value: value.0, value: value.0,
bonus: MultiTurnBonus::Blindfire, bonus: MultiTurnBonus::Blindfire,
}) });
.set_parent(weapon.get());
} }
WeaponBonusType::Fury => { WeaponBonusType::Fury => {
commands commands
.spawn(DamageProcEffect::MultiTurn { .entity(weapon.parent())
.with_child(DamageProcEffect::MultiTurn {
value: value.0, value: value.0,
bonus: MultiTurnBonus::Fury, bonus: MultiTurnBonus::Fury,
}) });
.set_parent(weapon.get());
} }
WeaponBonusType::Rage => { WeaponBonusType::Rage => {
commands commands
.spawn(DamageProcEffect::MultiTurn { .entity(weapon.parent())
.with_child(DamageProcEffect::MultiTurn {
value: value.0, value: value.0,
bonus: MultiTurnBonus::Rage, bonus: MultiTurnBonus::Rage,
}) });
.set_parent(weapon.get());
} }
WeaponBonusType::DoubleTap => { WeaponBonusType::DoubleTap => {
commands commands
.spawn(DamageProcEffect::MultiTurn { .entity(weapon.parent())
.with_child(DamageProcEffect::MultiTurn {
value: value.0, value: value.0,
bonus: MultiTurnBonus::DoubleTap, bonus: MultiTurnBonus::DoubleTap,
}) });
.set_parent(weapon.get());
} }
WeaponBonusType::Achilles => { WeaponBonusType::Achilles => {
commands commands
.spawn(PartDamageBonus::WeaponBonus { .entity(weapon.parent())
.with_child(PartDamageBonus::WeaponBonus {
value: value.0 / 100.0, value: value.0 / 100.0,
bonus: BonusPartDamageBonus::Achilles, bonus: BonusPartDamageBonus::Achilles,
}) });
.set_parent(weapon.get());
} }
WeaponBonusType::Cupid => { WeaponBonusType::Cupid => {
commands commands
.spawn(PartDamageBonus::WeaponBonus { .entity(weapon.parent())
.with_child(PartDamageBonus::WeaponBonus {
value: value.0 / 100.0, value: value.0 / 100.0,
bonus: BonusPartDamageBonus::Cupid, bonus: BonusPartDamageBonus::Cupid,
}) });
.set_parent(weapon.get());
} }
WeaponBonusType::Crusher => { WeaponBonusType::Crusher => {
commands commands
.spawn(PartDamageBonus::WeaponBonus { .entity(weapon.parent())
.with_child(PartDamageBonus::WeaponBonus {
value: value.0 / 100.0, value: value.0 / 100.0,
bonus: BonusPartDamageBonus::Crusher, bonus: BonusPartDamageBonus::Crusher,
}) });
.set_parent(weapon.get());
} }
WeaponBonusType::Deadeye => { WeaponBonusType::Deadeye => {
commands commands
.spawn(PartDamageBonus::WeaponBonus { .entity(weapon.parent())
.with_child(PartDamageBonus::WeaponBonus {
value: value.0 / 100.0, value: value.0 / 100.0,
bonus: BonusPartDamageBonus::Deadeye, bonus: BonusPartDamageBonus::Deadeye,
}) });
.set_parent(weapon.get());
} }
WeaponBonusType::Throttle => { WeaponBonusType::Throttle => {
commands commands
.spawn(PartDamageBonus::WeaponBonus { .entity(weapon.parent())
.with_child(PartDamageBonus::WeaponBonus {
value: value.0 / 100.0, value: value.0 / 100.0,
bonus: BonusPartDamageBonus::Throttle, bonus: BonusPartDamageBonus::Throttle,
}) });
.set_parent(weapon.get());
} }
WeaponBonusType::Roshambo => { WeaponBonusType::Roshambo => {
commands commands
.spawn(PartDamageBonus::WeaponBonus { .entity(weapon.parent())
.with_child(PartDamageBonus::WeaponBonus {
value: value.0 / 100.0, value: value.0 / 100.0,
bonus: BonusPartDamageBonus::Roshambo, bonus: BonusPartDamageBonus::Roshambo,
}) });
.set_parent(weapon.get());
} }
WeaponBonusType::Cripple => { WeaponBonusType::Cripple => {
commands commands
.spawn(DamageProcEffect::OpponentEffect { .entity(weapon.parent())
.with_child(DamageProcEffect::OpponentEffect {
value: value.0, value: value.0,
bonus: OpponentStatusEffect::Cripple, bonus: OpponentStatusEffect::Cripple,
}) });
.set_parent(weapon.get());
} }
WeaponBonusType::Demoralise => { WeaponBonusType::Demoralise => {
commands commands
.spawn(DamageProcEffect::OpponentEffect { .entity(weapon.parent())
.with_child(DamageProcEffect::OpponentEffect {
value: value.0, value: value.0,
bonus: OpponentStatusEffect::Demoralise, bonus: OpponentStatusEffect::Demoralise,
}) });
.set_parent(weapon.get());
} }
WeaponBonusType::Freeze => { WeaponBonusType::Freeze => {
commands commands
.spawn(DamageProcEffect::OpponentEffect { .entity(weapon.parent())
.with_child(DamageProcEffect::OpponentEffect {
value: value.0, value: value.0,
bonus: OpponentStatusEffect::Freeze, bonus: OpponentStatusEffect::Freeze,
}) });
.set_parent(weapon.get());
} }
WeaponBonusType::Slow => { WeaponBonusType::Slow => {
commands commands
.spawn(DamageProcEffect::OpponentEffect { .entity(weapon.parent())
.with_child(DamageProcEffect::OpponentEffect {
value: value.0, value: value.0,
bonus: OpponentStatusEffect::Slow, bonus: OpponentStatusEffect::Slow,
}) });
.set_parent(weapon.get());
} }
WeaponBonusType::Toxin => { WeaponBonusType::Toxin => {
commands commands
.spawn(DamageProcEffect::OpponentEffect { .entity(weapon.parent())
.with_child(DamageProcEffect::OpponentEffect {
value: value.0, value: value.0,
bonus: OpponentStatusEffect::Toxin, bonus: OpponentStatusEffect::Toxin,
}) });
.set_parent(weapon.get());
} }
WeaponBonusType::Weaken => { WeaponBonusType::Weaken => {
commands commands
.spawn(DamageProcEffect::OpponentEffect { .entity(weapon.parent())
.with_child(DamageProcEffect::OpponentEffect {
value: value.0, value: value.0,
bonus: OpponentStatusEffect::Weaken, bonus: OpponentStatusEffect::Weaken,
}) });
.set_parent(weapon.get());
} }
WeaponBonusType::Wither => { WeaponBonusType::Wither => {
commands commands
.spawn(DamageProcEffect::OpponentEffect { .entity(weapon.parent())
.with_child(DamageProcEffect::OpponentEffect {
value: value.0, value: value.0,
bonus: OpponentStatusEffect::Wither, bonus: OpponentStatusEffect::Wither,
}) });
.set_parent(weapon.get());
} }
WeaponBonusType::Motivate => { WeaponBonusType::Motivate => {
commands commands
.spawn(DamageProcEffect::SelfEffect { .entity(weapon.parent())
.with_child(DamageProcEffect::SelfEffect {
value: value.0, value: value.0,
bonus: SelfStatusEffect::Motivate, bonus: SelfStatusEffect::Motivate,
}) });
.set_parent(weapon.get());
} }
val => unimplemented!("{val:?}"), val => unimplemented!("{val:?}"),

View file

@ -1,25 +1,22 @@
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use proxisim_models::{ use proxisim_models::bundle::{
bundle::{ passive::{Education, EducationPartDamageBonus, FactionUpgrades, Merits},
passive::{Education, EducationPartDamageBonus, FactionUpgrades, Merits}, player::{Current, PartDamageBonus, Weapons},
player::{Current, PartDamageBonus, Weapons}, stat::{
stat::{ AdditiveBonus, AmmoControl, ClipSize, Clips, CritRate, DamageBonus, Dexterity,
AdditiveBonus, AmmoControl, ClipSize, Clips, CritRate, DamageBonus, Dexterity, SimpleStatBonus, SimpleStatEffective, WeaponAccuracy,
SimpleStatBonus, SimpleStatEffective, WeaponAccuracy, },
}, weapon::{
weapon::{ Ammo, EquippedMods, Experience, Japanese, NeedsReload, Usable, Weapon, WeaponCategory,
Ammo, EquippedMods, Experience, Japanese, NeedsReload, Usable, Weapon, WeaponCategory, WeaponMod, WeaponSlot,
WeaponMod, WeaponSlot,
},
}, },
hierarchy::{HierarchyBuilder, Parent},
}; };
use crate::{ use crate::{
Stages,
effect::{Effects, TurnLimitedEffect}, effect::{Effects, TurnLimitedEffect},
log, log,
log::Logger, log::Logger,
Stages,
}; };
use self::bonus::{ use self::bonus::{
@ -157,25 +154,6 @@ pub enum DamageProcEffect {
}, },
} }
fn set_owner(weapons_q: Query<(Entity, &Weapons)>, mut commands: Commands) {
for (player, weapons) in weapons_q.iter() {
if let Some(primary) = weapons.primary {
commands.entity(primary).set_parent(player);
}
if let Some(secondary) = weapons.secondary {
commands.entity(secondary).set_parent(player);
}
if let Some(melee) = weapons.melee {
commands.entity(melee).set_parent(player);
}
if let Some(temp) = weapons.temporary {
commands.entity(temp).set_parent(player);
}
commands.entity(weapons.fists).set_parent(player);
commands.entity(weapons.kick).set_parent(player);
}
}
fn apply_passives( fn apply_passives(
weapon_q: Query<( weapon_q: Query<(
Entity, Entity,
@ -184,7 +162,7 @@ fn apply_passives(
&WeaponCategory, &WeaponCategory,
&WeaponSlot, &WeaponSlot,
Has<Japanese>, Has<Japanese>,
&Parent, &ChildOf,
)>, )>,
player_q: Query<(&Merits, &Education, &FactionUpgrades)>, player_q: Query<(&Merits, &Education, &FactionUpgrades)>,
mut effects: Effects, mut effects: Effects,
@ -331,8 +309,8 @@ fn apply_passives(
weapon, weapon,
); );
commands commands
.spawn(TurnTriggeredEffect::Mod(TurnTriggeredMod::Bipod)) .entity(weapon)
.set_parent(weapon); .with_child(TurnTriggeredEffect::Mod(TurnTriggeredMod::Bipod));
} }
WeaponMod::Tripod => { WeaponMod::Tripod => {
effects.spawn( effects.spawn(
@ -340,40 +318,38 @@ fn apply_passives(
weapon, weapon,
); );
commands commands
.spawn(TurnTriggeredEffect::Mod(TurnTriggeredMod::Tripod)) .entity(weapon)
.set_parent(weapon); .with_child(TurnTriggeredEffect::Mod(TurnTriggeredMod::Tripod));
} }
WeaponMod::SmallLight => { WeaponMod::SmallLight => {
commands commands
.spawn(TurnTriggeredEffect::Mod(TurnTriggeredMod::SmallLight)) .entity(weapon)
.set_parent(weapon); .with_child(TurnTriggeredEffect::Mod(TurnTriggeredMod::SmallLight));
} }
WeaponMod::PrecisionLight => { WeaponMod::PrecisionLight => {
commands commands
.spawn(TurnTriggeredEffect::Mod(TurnTriggeredMod::PrecisionLight)) .entity(weapon)
.set_parent(weapon); .with_child(TurnTriggeredEffect::Mod(TurnTriggeredMod::PrecisionLight));
} }
WeaponMod::TacticalIlluminator => { WeaponMod::TacticalIlluminator => {
commands commands.entity(weapon).with_child(TurnTriggeredEffect::Mod(
.spawn(TurnTriggeredEffect::Mod( TurnTriggeredMod::TacticalIlluminator,
TurnTriggeredMod::TacticalIlluminator, ));
))
.set_parent(weapon);
} }
WeaponMod::AdjustableTrigger => { WeaponMod::AdjustableTrigger => {
commands commands
.spawn(FirstTurnEffect::Mod(FirstTurnMod::AdjustableTrigger)) .entity(weapon)
.set_parent(weapon); .with_child(FirstTurnEffect::Mod(FirstTurnMod::AdjustableTrigger));
} }
WeaponMod::HairTrigger => { WeaponMod::HairTrigger => {
commands commands
.spawn(FirstTurnEffect::Mod(FirstTurnMod::HairTrigger)) .entity(weapon)
.set_parent(weapon); .with_child(FirstTurnEffect::Mod(FirstTurnMod::HairTrigger));
} }
} }
} }
let (merits, education, faction) = player_q.get(player.get()).unwrap(); let (merits, education, faction) = player_q.get(player.parent()).unwrap();
let (mastery, edu_acc) = match cat { let (mastery, edu_acc) = match cat {
WeaponCategory::HeavyArtillery => ( WeaponCategory::HeavyArtillery => (
@ -445,10 +421,10 @@ fn apply_passives(
if education.bio2380 { if education.bio2380 {
commands commands
.spawn(PartDamageBonus::Education( .entity(weapon)
.with_child(PartDamageBonus::Education(
EducationPartDamageBonus::Bio2380, EducationPartDamageBonus::Bio2380,
)) ));
.set_parent(weapon);
} }
if mastery > 0 { if mastery > 0 {
@ -490,7 +466,7 @@ fn reload_weapon(
mut weapon_q: Query< mut weapon_q: Query<
( (
Entity, Entity,
&Parent, &ChildOf,
&mut Ammo, &mut Ammo,
&mut SimpleStatEffective<Clips>, &mut SimpleStatEffective<Clips>,
&SimpleStatEffective<ClipSize>, &SimpleStatEffective<ClipSize>,
@ -505,7 +481,7 @@ fn reload_weapon(
clips.value -= 1; clips.value -= 1;
log!(logger, "reload_weapon", { log!(logger, "reload_weapon", {
actor: player.get(), actor: player.parent(),
weapon weapon
}); });
@ -530,18 +506,17 @@ fn restore_ammo(
} }
fn apply_first_turn_effects( fn apply_first_turn_effects(
effect_q: Query<(&Parent, &FirstTurnEffect)>, effect_q: Query<(&ChildOf, &FirstTurnEffect)>,
player_q: Query<&Parent>, player_q: Query<&ChildOf>,
mut effects: Effects, mut effects: Effects,
) { ) {
for (weapon, effect) in effect_q.iter() { for (weapon, effect) in effect_q.iter() {
let player = player_q.get(weapon.get()).unwrap(); let player = player_q.get(weapon.parent()).unwrap();
effect.spawn(&mut effects, weapon.get(), player.get()); effect.spawn(&mut effects, weapon.parent(), player.parent());
} }
} }
pub(crate) fn configure(stages: &mut Stages) { pub(crate) fn configure(stages: &mut Stages) {
stages.equip.add_systems(set_owner);
// running this in the snapshot layer ensures that the stat increases aren't restored at the // running this in the snapshot layer ensures that the stat increases aren't restored at the
// end of the run // end of the run
stages.snapshot.add_systems(apply_first_turn_effects); stages.snapshot.add_systems(apply_first_turn_effects);