feat: added more passives

This commit is contained in:
TotallyNot 2025-11-09 17:10:54 +01:00
parent 34fe16407e
commit c7089d2deb
Signed by: pyrite
GPG key ID: 7F1BA9170CD35D15
6 changed files with 363 additions and 9 deletions

View file

@ -1,4 +1,5 @@
use bevy_ecs::{bundle::Bundle, component::Component}; use bevy_ecs::{bundle::Bundle, component::Component};
use strum::EnumIter;
use crate::bundle::player::BodyPart; use crate::bundle::player::BodyPart;
@ -186,11 +187,194 @@ pub struct Property {
#[derive(Component)] #[derive(Component)]
#[cfg_attr(feature = "json", derive(serde::Deserialize))] #[cfg_attr(feature = "json", derive(serde::Deserialize))]
#[cfg_attr(feature = "json", serde(rename_all = "snake_case"))]
pub enum DrugCooldown { pub enum DrugCooldown {
Xanax, Xanax,
Vicodin, Vicodin,
} }
#[derive(Component)]
#[cfg_attr(feature = "json", derive(serde::Deserialize))]
pub struct DrugAddiction {
pub level: u16,
}
#[cfg_attr(feature = "json", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "json", serde(rename_all = "snake_case"))]
#[derive(Debug, Clone, Copy, Component, EnumIter)]
pub enum Job {
/// 25% enemy speed reduction
AdultNovelties7,
/// 25% epinephrine effect & duration
AmusementPark7,
/// +25% Passive Dexterity
ClothingStore5,
/// +20% Armor Bonus
ClothingStore10,
/// +25% Flamethrower Damage, +10 Flamethrower Accuracy
FireworksStand5,
/// 25% passive strength
FurnitureStore7,
/// 100% fist & kick damage
FurnitureStore10,
/// +25% bonus to Speed
GasStation3,
/// Occasional 20% health regeneration (10% chance per turn)
GasStation5,
/// 50% reduction to Burning damage received
GasStation7,
/// 50% bonus to Burning damage dealt
GasStation10,
/// +25% Passive Dexterity
GentsStripClub3,
/// +50% Tyrosine effect & duration
GentsStripClub5,
/// 1/4 chance to dodge melee attacks
GentsStripClub10,
/// 1 extra clip for guns during attacks
GunShop7,
/// 10% primary & secondary weapon damage
GunShop10,
/// 20% slashing weapon damage
HairSalon10,
/// +25% Passive Defense
LadiesStripClub3,
/// +50% Serotonin effect & duration
LadiesStripClub5,
/// 30% melee damage mitigation
LadiesStripClub10,
/// +50% speed and dexterity when not wearing armor
LingeryStore5,
/// 10% maximum life
MiningCorporation7,
/// +15% passive on all stats
MusicStore10,
/// +50% flash grenade intensity
PrivateSecurityFirm3,
/// 25% full set armor mitigation bonus
PrivateSecurityFirm7,
/// 10% melee weapon damage
Pub3,
/// 10% melee weapon damage
Restaurant3,
/// +3.00 Accuracy
Zoo10,
}
impl Job {
pub fn has(self, other: Self) -> bool {
matches!(
(self, other),
(Self::AdultNovelties7, Self::AdultNovelties7)
| (Self::AmusementPark7, Self::AmusementPark7)
| (
Self::ClothingStore5,
Self::ClothingStore5 | Self::ClothingStore10
)
| (Self::ClothingStore10, Self::ClothingStore10)
| (Self::FireworksStand5, Self::FireworksStand5)
| (
Self::FurnitureStore7,
Self::FurnitureStore7 | Self::FurnitureStore10
)
| (Self::FurnitureStore10, Self::FurnitureStore10)
| (
Self::GasStation3,
Self::GasStation3 | Self::GasStation5 | Self::GasStation7 | Self::GasStation10
)
| (
Self::GasStation5,
Self::GasStation5 | Self::GasStation7 | Self::GasStation10
)
| (Self::GasStation7, Self::GasStation7 | Self::GasStation10)
| (Self::GasStation10, Self::GasStation10)
| (
Self::GentsStripClub3,
Self::GentsStripClub3 | Self::GentsStripClub5 | Self::GentsStripClub10
)
| (
Self::GentsStripClub5,
Self::GentsStripClub5 | Self::GentsStripClub10
)
| (Self::GentsStripClub10, Self::GentsStripClub10)
| (Self::GunShop7, Self::GunShop7 | Self::GunShop10)
| (Self::GunShop10, Self::GunShop10)
| (Self::HairSalon10, Self::HairSalon10)
| (
Self::LadiesStripClub3,
Self::LadiesStripClub3 | Self::LadiesStripClub5 | Self::LadiesStripClub10
)
| (
Self::LadiesStripClub5,
Self::LadiesStripClub5 | Self::LadiesStripClub10
)
| (Self::LadiesStripClub10, Self::LadiesStripClub10)
| (Self::LingeryStore5, Self::LingeryStore5)
| (Self::MiningCorporation7, Self::MiningCorporation7)
| (Self::MusicStore10, Self::MusicStore10)
| (
Self::PrivateSecurityFirm3,
Self::PrivateSecurityFirm3 | Self::PrivateSecurityFirm7
)
| (Self::PrivateSecurityFirm7, Self::PrivateSecurityFirm7)
| (Self::Pub3, Self::Pub3)
| (Self::Restaurant3, Self::Restaurant3)
| (Self::Zoo10, Self::Zoo10)
)
}
pub fn label(self) -> &'static str {
match self {
Self::AdultNovelties7 => "Adult novelties 7*",
Self::AmusementPark7 => "Amusement park 7*",
Self::ClothingStore5 => "Clothing store 5*",
Self::ClothingStore10 => "Clothing store 10*",
Self::FireworksStand5 => "Fireworks stand 5*",
Self::FurnitureStore7 => "Furniture store 7*",
Self::FurnitureStore10 => "Furniture store 10*",
Self::GasStation3 => "Gas station 3*",
Self::GasStation5 => "Gas station 5*",
Self::GasStation7 => "Gas station 7*",
Self::GasStation10 => "Gas station 10*",
Self::GentsStripClub3 => "Gents strip club 3*",
Self::GentsStripClub5 => "Gents strip club 5*",
Self::GentsStripClub10 => "Gents strip club 10*",
Self::GunShop7 => "Gun shop 7*",
Self::GunShop10 => "Gun shop 10*",
Self::HairSalon10 => "Hair salon 10*",
Self::LadiesStripClub3 => "Ladies strip club 3*",
Self::LadiesStripClub5 => "Ladies strip club 5*",
Self::LadiesStripClub10 => "Ladies strip club 10*",
Self::LingeryStore5 => "Lingery store 5*",
Self::MiningCorporation7 => "Mining corporation 7*",
Self::MusicStore10 => "Music store 10*",
Self::PrivateSecurityFirm3 => "Private security firm 3*",
Self::PrivateSecurityFirm7 => "Private security firm 7*",
Self::Pub3 => "Pub 3*",
Self::Restaurant3 => "Restaurant 3*",
Self::Zoo10 => "Zoo 10*",
}
}
pub fn supported(self) -> bool {
matches!(
self,
Self::AmusementPark7
| Self::ClothingStore5
| Self::FurnitureStore7
| Self::GasStation3
| Self::GentsStripClub3
| Self::GentsStripClub5
| Self::GentsStripClub10
| Self::LadiesStripClub3
| Self::LadiesStripClub5
| Self::LingeryStore5
| Self::MiningCorporation7
| Self::MusicStore10
)
}
}
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum EducationPartDamageBonus { pub enum EducationPartDamageBonus {
Bio2380, Bio2380,

View file

@ -149,6 +149,12 @@ pub enum ActionNullification {
GentsStripClub, GentsStripClub,
} }
#[derive(Component, Debug)]
pub enum BuildUpBonus {
Focus { bonus: f32 },
Finale { bonus: f32 },
}
impl PartDamageBonus { impl PartDamageBonus {
pub fn dmg_bonus(&self, part: BodyPart) -> Option<f32> { pub fn dmg_bonus(&self, part: BodyPart) -> Option<f32> {
match self { match self {

View file

@ -3,7 +3,10 @@ use bevy_ecs::prelude::*;
use crate::{ use crate::{
bundle::{ bundle::{
armour::PlayerArmour, armour::PlayerArmour,
passive::{DrugCooldown, Education, FactionUpgrades, Merits, PassiveBundle, Property}, passive::{
DrugAddiction, DrugCooldown, Education, FactionUpgrades, Job, Merits, PassiveBundle,
Property,
},
player::{PlayerBundle, PlayerStrategy, Weapons}, player::{PlayerBundle, PlayerStrategy, Weapons},
stat::{Defence, Dexterity, Speed, StatBundle, Strength}, stat::{Defence, Dexterity, Speed, StatBundle, Strength},
}, },
@ -49,6 +52,9 @@ pub struct PlayerDto {
pub faction: Option<FactionUpgrades>, pub faction: Option<FactionUpgrades>,
pub drug: Option<DrugCooldown>, pub drug: Option<DrugCooldown>,
pub strategy: PlayerStrategy, pub strategy: PlayerStrategy,
pub cooldown: Option<DrugCooldown>,
pub addiction: Option<DrugAddiction>,
pub job: Option<Job>,
} }
impl PlayerDto { impl PlayerDto {
@ -118,6 +124,18 @@ impl PlayerDto {
commands.insert(armour); commands.insert(armour);
} }
if let Some(cooldown) = self.cooldown {
commands.insert(cooldown);
}
if let Some(addiction) = self.addiction {
commands.insert(addiction);
}
if let Some(job) = self.job {
commands.insert(job);
}
commands commands
} }
} }

View file

@ -296,8 +296,10 @@ impl WeaponDto {
814 => commands.insert(BuffingTemp::Tyrosine), 814 => commands.insert(BuffingTemp::Tyrosine),
464 => commands.insert(BuffingTemp::Melatonin), 464 => commands.insert(BuffingTemp::Melatonin),
463 => commands.insert(BuffingTemp::Epinephrine), 463 => commands.insert(BuffingTemp::Epinephrine),
// damaging temps always hit the body // exploding temps always hit the body
_ => commands.insert(NonTargeted), // TODO: how does that interact with armour?
242 | 217 | 220 | 221 | 840 => commands.insert(NonTargeted),
_ => &mut commands,
}; };
} }

View file

@ -1,6 +1,8 @@
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use proxisim_models::bundle::{ use proxisim_models::bundle::{
passive::{DrugCooldown, Education, FactionUpgrades, Merits}, armour::PlayerArmour,
passive::{DrugAddiction, DrugCooldown, Education, FactionUpgrades, Job, Merits},
player::ActionNullification,
stat::{ stat::{
AdditiveBonus, CritRate, Defence, Dexterity, MaxHealth, SimpleStatBonus, Speed, Strength, AdditiveBonus, CritRate, Defence, Dexterity, MaxHealth, SimpleStatBonus, Speed, Strength,
}, },
@ -10,7 +12,8 @@ use crate::{
Stages, Stages,
effect::Effects, effect::Effects,
player::status_effect::{ player::status_effect::{
ExtraStatusEffectEffectiveness, Hardened, Hastened, Sharpened, Strengthened, ExtraStatusEffectDuration, ExtraStatusEffectEffectiveness, Hardened, Hastened, Sharpened,
Strengthened,
}, },
}; };
@ -21,11 +24,14 @@ fn spawn_permanent_effects(
&Education, &Education,
&FactionUpgrades, &FactionUpgrades,
Option<&DrugCooldown>, Option<&DrugCooldown>,
Option<&DrugAddiction>,
Option<&Job>,
&PlayerArmour,
)>, )>,
mut effects: Effects, mut effects: Effects,
mut commands: Commands, mut commands: Commands,
) { ) {
for (player, merits, edu, faction, drug_cd) in merit_q.iter() { for (player, merits, edu, faction, drug_cd, addiction, job, armour) in merit_q.iter() {
if merits.brawn > 0 { if merits.brawn > 0 {
effects.spawn( effects.spawn(
AdditiveBonus::<Strength>::new("brawn", (merits.brawn as f32) * 0.03), AdditiveBonus::<Strength>::new("brawn", (merits.brawn as f32) * 0.03),
@ -155,6 +161,34 @@ fn spawn_permanent_effects(
_ => (), _ => (),
} }
if let Some(addiction) = addiction {
effects.spawn(
AdditiveBonus::<Strength>::new("addiction", -0.01 * addiction.level as f32),
player,
);
effects.spawn(
AdditiveBonus::<Speed>::new("addiction", -0.01 * addiction.level as f32),
player,
);
effects.spawn(
AdditiveBonus::<Defence>::new("addiction", -0.01 * addiction.level as f32),
player,
);
effects.spawn(
AdditiveBonus::<Dexterity>::new("addiction", -0.01 * addiction.level as f32),
player,
);
}
let mut epi_eff = 1.0;
let mut mela_eff = 1.0;
let mut sero_eff = 1.0;
let mut tyro_eff = 1.0;
let mut epi_dur = 1.0;
let mut sero_dur = 1.0;
let mut tyro_dur = 1.0;
if edu.bio2410 { if edu.bio2410 {
effects.spawn(SimpleStatBonus::<CritRate>::new("BIO2410", 6), player); effects.spawn(SimpleStatBonus::<CritRate>::new("BIO2410", 6), player);
} }
@ -219,12 +253,16 @@ fn spawn_permanent_effects(
effects.spawn(AdditiveBonus::<Speed>::new("DEF2760", 0.03), player); effects.spawn(AdditiveBonus::<Speed>::new("DEF2760", 0.03), player);
} }
if edu.spt2480 { if edu.spt2480 {
commands.entity(player).insert(( epi_eff *= 1.1;
mela_eff *= 1.1;
sero_eff *= 1.1;
tyro_eff *= 1.1;
/* commands.entity(player).insert((
ExtraStatusEffectEffectiveness::<1, Strengthened>::new(1.1), ExtraStatusEffectEffectiveness::<1, Strengthened>::new(1.1),
ExtraStatusEffectEffectiveness::<1, Sharpened>::new(1.1), ExtraStatusEffectEffectiveness::<1, Sharpened>::new(1.1),
ExtraStatusEffectEffectiveness::<1, Hardened>::new(1.1), ExtraStatusEffectEffectiveness::<1, Hardened>::new(1.1),
ExtraStatusEffectEffectiveness::<1, Hastened>::new(1.1), ExtraStatusEffectEffectiveness::<1, Hastened>::new(1.1),
)); )); */
} }
if edu.spt2490 { if edu.spt2490 {
effects.spawn(AdditiveBonus::<Speed>::new("SPT2490", 0.02), player); effects.spawn(AdditiveBonus::<Speed>::new("SPT2490", 0.02), player);
@ -234,6 +272,112 @@ fn spawn_permanent_effects(
effects.spawn(AdditiveBonus::<Defence>::new("SPT2500", 0.02), player); effects.spawn(AdditiveBonus::<Defence>::new("SPT2500", 0.02), player);
effects.spawn(AdditiveBonus::<Dexterity>::new("SPT2500", 0.02), player); effects.spawn(AdditiveBonus::<Dexterity>::new("SPT2500", 0.02), player);
} }
if let Some(job) = job {
if job.has(Job::AmusementPark7) {
epi_eff *= 1.25;
epi_dur *= 1.25;
}
if job.has(Job::ClothingStore5) {
effects.spawn(
AdditiveBonus::<Dexterity>::new("clothing_store", 0.25),
player,
);
}
if job.has(Job::FurnitureStore7) {
effects.spawn(
AdditiveBonus::<Strength>::new("furniture_store", 0.25),
player,
);
}
if job.has(Job::GasStation3) {
effects.spawn(AdditiveBonus::<Speed>::new("gas_station", 0.25), player);
}
if job.has(Job::GentsStripClub3) {
effects.spawn(
AdditiveBonus::<Dexterity>::new("gents_strip_club", 0.25),
player,
);
}
if job.has(Job::GentsStripClub5) {
tyro_eff *= 1.5;
tyro_dur *= 1.5;
}
if job.has(Job::GentsStripClub10) {
commands
.entity(player)
.with_child(ActionNullification::GentsStripClub);
}
if job.has(Job::LadiesStripClub3) {
effects.spawn(
AdditiveBonus::<Defence>::new("ladies_strip_club", 0.25),
player,
);
}
if job.has(Job::LadiesStripClub5) {
sero_eff *= 1.5;
sero_dur *= 1.5;
}
if job.has(Job::LingeryStore5) && armour.into_iter().count() == 0 {
effects.spawn(
AdditiveBonus::<Dexterity>::new("lingery_store", 0.50),
player,
);
effects.spawn(AdditiveBonus::<Speed>::new("lingery_store", 0.50), player);
}
if job.has(Job::MiningCorporation7) {
effects.spawn(
SimpleStatBonus::<MaxHealth>::new("mining_corporation", 1.1),
player,
);
}
if job.has(Job::MusicStore10) {
effects.spawn(AdditiveBonus::<Strength>::new("music_store", 0.15), player);
effects.spawn(AdditiveBonus::<Speed>::new("music_store", 0.15), player);
effects.spawn(AdditiveBonus::<Defence>::new("music_store", 0.15), player);
effects.spawn(AdditiveBonus::<Dexterity>::new("music_store", 0.15), player);
}
}
if epi_eff >= 1.0 {
commands
.entity(player)
.insert(ExtraStatusEffectEffectiveness::<1, Strengthened>::new(
epi_eff,
));
}
if epi_dur >= 1.0 {
commands
.entity(player)
.insert(ExtraStatusEffectDuration::<1, Strengthened>::new(epi_dur));
}
if mela_eff >= 1.0 {
commands
.entity(player)
.insert(ExtraStatusEffectEffectiveness::<1, Hastened>::new(mela_eff));
}
if sero_eff >= 1.0 {
commands
.entity(player)
.insert(ExtraStatusEffectEffectiveness::<1, Hardened>::new(sero_eff));
}
if sero_dur >= 1.0 {
commands
.entity(player)
.insert(ExtraStatusEffectDuration::<1, Hardened>::new(sero_dur));
}
if tyro_eff >= 1.0 {
commands
.entity(player)
.insert(ExtraStatusEffectEffectiveness::<1, Sharpened>::new(
tyro_eff,
));
}
if tyro_dur >= 1.0 {
commands
.entity(player)
.insert(ExtraStatusEffectDuration::<1, Sharpened>::new(tyro_dur));
}
} }
} }

View file

@ -366,7 +366,7 @@ fn check_action_nullification(
return Some("homerun"); return Some("homerun");
} }
ActionNullification::GentsStripClub ActionNullification::GentsStripClub
if player_action.0 == WeaponSlot::Melee && shared.rng.random_bool(0.25) => if player_action.0 == WeaponSlot::Melee && shared.rng.random_ratio(1, 4) =>
{ {
return Some("dodged"); return Some("dodged");
} }