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

View file

@ -1,25 +1,22 @@
use bevy_ecs::prelude::*;
use proxisim_models::{
bundle::{
passive::{Education, EducationPartDamageBonus, FactionUpgrades, Merits},
player::{Current, PartDamageBonus, Weapons},
stat::{
AdditiveBonus, AmmoControl, ClipSize, Clips, CritRate, DamageBonus, Dexterity,
SimpleStatBonus, SimpleStatEffective, WeaponAccuracy,
},
weapon::{
Ammo, EquippedMods, Experience, Japanese, NeedsReload, Usable, Weapon, WeaponCategory,
WeaponMod, WeaponSlot,
},
use proxisim_models::bundle::{
passive::{Education, EducationPartDamageBonus, FactionUpgrades, Merits},
player::{Current, PartDamageBonus, Weapons},
stat::{
AdditiveBonus, AmmoControl, ClipSize, Clips, CritRate, DamageBonus, Dexterity,
SimpleStatBonus, SimpleStatEffective, WeaponAccuracy,
},
weapon::{
Ammo, EquippedMods, Experience, Japanese, NeedsReload, Usable, Weapon, WeaponCategory,
WeaponMod, WeaponSlot,
},
hierarchy::{HierarchyBuilder, Parent},
};
use crate::{
Stages,
effect::{Effects, TurnLimitedEffect},
log,
log::Logger,
Stages,
};
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(
weapon_q: Query<(
Entity,
@ -184,7 +162,7 @@ fn apply_passives(
&WeaponCategory,
&WeaponSlot,
Has<Japanese>,
&Parent,
&ChildOf,
)>,
player_q: Query<(&Merits, &Education, &FactionUpgrades)>,
mut effects: Effects,
@ -331,8 +309,8 @@ fn apply_passives(
weapon,
);
commands
.spawn(TurnTriggeredEffect::Mod(TurnTriggeredMod::Bipod))
.set_parent(weapon);
.entity(weapon)
.with_child(TurnTriggeredEffect::Mod(TurnTriggeredMod::Bipod));
}
WeaponMod::Tripod => {
effects.spawn(
@ -340,40 +318,38 @@ fn apply_passives(
weapon,
);
commands
.spawn(TurnTriggeredEffect::Mod(TurnTriggeredMod::Tripod))
.set_parent(weapon);
.entity(weapon)
.with_child(TurnTriggeredEffect::Mod(TurnTriggeredMod::Tripod));
}
WeaponMod::SmallLight => {
commands
.spawn(TurnTriggeredEffect::Mod(TurnTriggeredMod::SmallLight))
.set_parent(weapon);
.entity(weapon)
.with_child(TurnTriggeredEffect::Mod(TurnTriggeredMod::SmallLight));
}
WeaponMod::PrecisionLight => {
commands
.spawn(TurnTriggeredEffect::Mod(TurnTriggeredMod::PrecisionLight))
.set_parent(weapon);
.entity(weapon)
.with_child(TurnTriggeredEffect::Mod(TurnTriggeredMod::PrecisionLight));
}
WeaponMod::TacticalIlluminator => {
commands
.spawn(TurnTriggeredEffect::Mod(
TurnTriggeredMod::TacticalIlluminator,
))
.set_parent(weapon);
commands.entity(weapon).with_child(TurnTriggeredEffect::Mod(
TurnTriggeredMod::TacticalIlluminator,
));
}
WeaponMod::AdjustableTrigger => {
commands
.spawn(FirstTurnEffect::Mod(FirstTurnMod::AdjustableTrigger))
.set_parent(weapon);
.entity(weapon)
.with_child(FirstTurnEffect::Mod(FirstTurnMod::AdjustableTrigger));
}
WeaponMod::HairTrigger => {
commands
.spawn(FirstTurnEffect::Mod(FirstTurnMod::HairTrigger))
.set_parent(weapon);
.entity(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 {
WeaponCategory::HeavyArtillery => (
@ -445,10 +421,10 @@ fn apply_passives(
if education.bio2380 {
commands
.spawn(PartDamageBonus::Education(
.entity(weapon)
.with_child(PartDamageBonus::Education(
EducationPartDamageBonus::Bio2380,
))
.set_parent(weapon);
));
}
if mastery > 0 {
@ -490,7 +466,7 @@ fn reload_weapon(
mut weapon_q: Query<
(
Entity,
&Parent,
&ChildOf,
&mut Ammo,
&mut SimpleStatEffective<Clips>,
&SimpleStatEffective<ClipSize>,
@ -505,7 +481,7 @@ fn reload_weapon(
clips.value -= 1;
log!(logger, "reload_weapon", {
actor: player.get(),
actor: player.parent(),
weapon
});
@ -530,18 +506,17 @@ fn restore_ammo(
}
fn apply_first_turn_effects(
effect_q: Query<(&Parent, &FirstTurnEffect)>,
player_q: Query<&Parent>,
effect_q: Query<(&ChildOf, &FirstTurnEffect)>,
player_q: Query<&ChildOf>,
mut effects: Effects,
) {
for (weapon, effect) in effect_q.iter() {
let player = player_q.get(weapon.get()).unwrap();
effect.spawn(&mut effects, weapon.get(), player.get());
let player = player_q.get(weapon.parent()).unwrap();
effect.spawn(&mut effects, weapon.parent(), player.parent());
}
}
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
// end of the run
stages.snapshot.add_systems(apply_first_turn_effects);