diff --git a/assets/ron/heroes/protector.unit.ron b/assets/ron/heroes/protector.unit.ron index 6c9d57e9a..b25caf0b8 100644 --- a/assets/ron/heroes/protector.unit.ron +++ b/assets/ron/heroes/protector.unit.ron @@ -16,7 +16,12 @@ children: [], mapping: { Offset: Mul( - UnitVec(Sum(IntFloat(State(Index)), Sum(GameTime, Sin(Mul(RandomFloat, GameTime))))), + UnitVec( + Sum( + IntFloat(State(Index)), + Sum(GameTime, Sin(Mul(RandomFloat(Owner), GameTime))), + ), + ), Mul(Sin(Mul(GameTime, Float(0.3))), Float(0.19)), ), }, diff --git a/assets/ron/heroes/scavenger.unit.ron b/assets/ron/heroes/scavenger.unit.ron index 89e4a6455..d1771c6e0 100644 --- a/assets/ron/heroes/scavenger.unit.ron +++ b/assets/ron/heroes/scavenger.unit.ron @@ -9,12 +9,14 @@ material: Shape( shape: Rectangle, fill: Line, - size: Vec2E(Sum(Float(0.4), Mul(RandomFloat, Float(1.1)))), - thickness: Sum(Float(0.8), Mul(RandomFloat, Float(3.0))), + size: Vec2E(Sum(Float(0.4), Mul(RandomFloat(Owner), Float(1.1)))), + thickness: Sum(Float(0.8), Mul(RandomFloat(Owner), Float(3.0))), alpha: Float(0.2), ), children: [], - mapping: {Rotation: Sum(IntFloat(State(Index)), Sin(Sum(GameTime, Mul(RandomFloat, Float(10.0)))))}, + mapping: { + Rotation: Sum(IntFloat(State(Index)), Sin(Sum(GameTime, Mul(RandomFloat(Owner), Float(10.0))))), + }, count: 10, ), ) \ No newline at end of file diff --git a/assets/ron/heroes/virus.unit.ron b/assets/ron/heroes/virus.unit.ron new file mode 100644 index 000000000..79bc92534 --- /dev/null +++ b/assets/ron/heroes/virus.unit.ron @@ -0,0 +1,34 @@ +( + name: "Virus", + hp: 1, + atk: 1, + stacks: 1, + level: 1, + houses: "Death Knights", + description: "%trigger → %effect on %target", + trigger: Fire(trigger: BattleStart, target: EnemyUnits, effect: UseAbility("Plague"), period: 0), + representation: ( + material: Shape( + shape: Circle, + fill: Line, + fill_color: Solid, + size: Vec2E(Float(0.12)), + point1: Vec2(1.0, 1.0), + point2: Vec2(1.0, 1.0), + thickness: Float(1.0), + alpha: Float(1.0), + colors: [State(Color)], + parts: [Float(0.0)], + ), + children: [], + mapping: { + Offset: Vec2EE( + Mul(Sub(RandomFloat(Index), Float(0.5)), Sin(Sum(GameTime, Index))), + Sub(RandomFloat(Owner), Float(0.5)), + ), + }, + count: 16, + ), + state: (history: {}, birth: 0.0), + statuses: [], +) \ No newline at end of file diff --git a/assets/ron/houses/death_knights.house.ron b/assets/ron/houses/death_knights.house.ron new file mode 100644 index 000000000..84a806738 --- /dev/null +++ b/assets/ron/houses/death_knights.house.ron @@ -0,0 +1,82 @@ +#![enable(implicit_some)] +( + name: "Death Knights", + color: ("#658D1B"), + statuses: [ + ( + name: "Plague", + description: "Take {Charges} DMG every turn, [Summon Skeleton] after death", + trigger: List( + [ + Fire(trigger: TurnEnd, effect: WithTarget(Owner, Damage(Context(Charges)))), + Fire(trigger: BeforeDeath, effect: UseAbility("Summon Skeleton")), + ], + ), + ), + ], + abilities: [ + ( + name: "Plague", + description: "Target takes 1 DMG every turn, [Summon Skeleton] after death", + effect: List([AddStatus("Plague"), Vfx("apply_status")]), + ), + ( + name: "Summon Skeleton", + description: "Summon enemy {1}/{4}", + effect: WithVar(Faction, OppositeFaction, Summon("Skeleton")), + ), + ], + summons: [ + ( + name: "Skeleton", + hp: 4, + atk: 1, + stacks: 1, + level: 1, + houses: "Death Knights", + description: "", + trigger: Fire(trigger: Noop, target: Owner, effect: Noop, period: 0), + representation: ( + material: Shape( + shape: Rectangle, + fill: Opaque, + fill_color: Solid, + size: Vec2(0.05, 1.0), + point1: Vec2(1.0, 1.0), + point2: Vec2(1.0, 1.0), + thickness: Float(1.0), + alpha: Float(1.0), + colors: [State(Color)], + parts: [Float(0.0)], + ), + children: [], + mapping: { + Offset: Mul( + UnitVec( + Mul( + Sub( + RandomFloat(Sum(Index, Sum(Int(200), ToInt(GameTime)))), + Float(0.5), + ), + Mul(PI, Float(2.0)), + ), + ), + Float(0.5), + ), + Rotation: Mul( + RandomFloat( + Sum( + Mul(Index, Int(10)), + Sum(Int(29), ToInt(Sum(GameTime, Mul(Index, Float(0.1))))), + ), + ), + Mul(PI, Float(2.0)), + ), + }, + count: 12, + ), + state: (history: {}, birth: 0.0), + statuses: [], + ), + ], +) \ No newline at end of file diff --git a/src/plugins/hero_editor.rs b/src/plugins/hero_editor.rs index 41c04a775..178e6f88c 100644 --- a/src/plugins/hero_editor.rs +++ b/src/plugins/hero_editor.rs @@ -767,7 +767,6 @@ impl EditorNodeGenerator for Expression { match self { Expression::Zero | Expression::GameTime - | Expression::RandomFloat | Expression::PI | Expression::Age | Expression::SlotPosition @@ -818,6 +817,7 @@ impl EditorNodeGenerator for Expression { | Expression::FilterMaxEnemy(x) | Expression::FindUnit(x) | Expression::UnitCount(x) + | Expression::RandomFloat(x) | Expression::StatusCharges(x) => show_node( x.as_mut(), format!("{path}:x"), diff --git a/src/resourses/expression.rs b/src/resourses/expression.rs index 4c575d14f..8b876ee7f 100644 --- a/src/resourses/expression.rs +++ b/src/resourses/expression.rs @@ -4,7 +4,8 @@ use rand::{ Rng, SeedableRng, }; use rand_chacha::ChaCha8Rng; -use std::f32::consts::PI; +use std::hash::{Hash, Hasher}; +use std::{collections::hash_map::DefaultHasher, f32::consts::PI}; use super::*; @@ -13,7 +14,6 @@ pub enum Expression { #[default] Zero, GameTime, - RandomFloat, PI, Age, SlotPosition, @@ -69,6 +69,7 @@ pub enum Expression { FindUnit(Box), UnitCount(Box), ToInt(Box), + RandomFloat(Box), Vec2EE(Box, Box), Sum(Box, Box), @@ -91,10 +92,6 @@ impl Expression { pub fn get_value(&self, context: &Context, world: &mut World) -> Result { match self { Expression::Zero => Ok(VarValue::Int(0)), - Expression::RandomFloat => { - let mut rng = ChaCha8Rng::seed_from_u64(context.owner().to_bits()); - Ok(VarValue::Float(rng.gen_range(0.0..1.0))) - } Expression::Float(x) => Ok(VarValue::Float(*x)), Expression::Int(x) => Ok(VarValue::Int(*x)), Expression::Bool(x) => Ok(VarValue::Bool(*x)), @@ -134,6 +131,13 @@ impl Expression { let x = x.get_int(context, world)?; Ok(VarValue::Bool(x % 2 == 0)) } + Expression::RandomFloat(x) => { + let x = x.get_value(context, world)?; + let mut hasher = DefaultHasher::new(); + x.hash(&mut hasher); + let mut rng = ChaCha8Rng::seed_from_u64(hasher.finish()); + Ok(VarValue::Float(rng.gen_range(0.0..1.0))) + } Expression::GameTime => Ok(VarValue::Float(GameTimer::get().play_head())), Expression::PI => Ok(VarValue::Float(PI)), Expression::Sum(a, b) => { @@ -418,7 +422,6 @@ impl Expression { match self { Self::Zero | Self::GameTime - | Self::RandomFloat | Self::PI | Self::Owner | Self::Caster @@ -469,6 +472,7 @@ impl Expression { | Self::FilterMaxEnemy(x) | Self::FindUnit(x) | Self::UnitCount(x) + | Self::RandomFloat(x) | Self::Vec2E(x) => vec![x], Self::Vec2EE(a, b) @@ -530,7 +534,6 @@ impl Expression { match self { Expression::Zero | Expression::GameTime - | Expression::RandomFloat | Expression::PI | Expression::Owner | Expression::Caster @@ -582,6 +585,7 @@ impl Expression { | Expression::FilterMaxEnemy(_) | Expression::FindUnit(_) | Expression::UnitCount(_) + | Expression::RandomFloat(_) | Expression::StatusCharges(_) => hex_color!("#448AFF"), Expression::Vec2EE(_, _) | Expression::Sum(_, _) @@ -604,7 +608,6 @@ impl Expression { match self { Expression::Zero | Expression::GameTime - | Expression::RandomFloat | Expression::PI | Expression::Age | Expression::SlotPosition @@ -655,6 +658,7 @@ impl Expression { | Expression::FilterMaxEnemy(v) | Expression::FindUnit(v) | Expression::UnitCount(v) + | Expression::RandomFloat(v) | Expression::StatusCharges(v) => format!( "{} ({})", self.to_string().to_case(Case::Lower), diff --git a/src/resourses/vars.rs b/src/resourses/vars.rs index cf0f9e725..6a19b6b2b 100644 --- a/src/resourses/vars.rs +++ b/src/resourses/vars.rs @@ -1,6 +1,7 @@ use std::{cmp::Ordering, fmt::Display}; use anyhow::anyhow; +use bevy_egui::egui::epaint::util::FloatOrd; use strum_macros::{Display, EnumString}; use super::*; @@ -95,6 +96,7 @@ impl VarValue { pub fn get_int(&self) -> Result { match self { VarValue::Int(value) => Ok(*value), + VarValue::Float(value) => Ok(*value as i32), VarValue::Bool(value) => Ok(*value as i32), VarValue::None => Ok(0), _ => Err(anyhow!("Int not supported by {self:?}")), @@ -255,6 +257,34 @@ impl Display for VarValue { } } +impl std::hash::Hash for VarValue { + fn hash(&self, state: &mut H) { + match self { + VarValue::None => core::mem::discriminant(self).hash(state), + VarValue::Float(v) => (*v).ord().hash(state), + VarValue::Int(v) => (*v).hash(state), + VarValue::Vec2(Vec2 { x, y }) => { + (*x).ord().hash(state); + (*y).ord().hash(state); + } + VarValue::Bool(v) => (*v).hash(state), + VarValue::String(v) => (*v).hash(state), + VarValue::Faction(v) => (*v).hash(state), + VarValue::Entity(v) => (*v).to_bits().hash(state), + VarValue::EntityList(v) => { + for v in v { + (*v).to_bits().hash(state) + } + } + VarValue::Color(v) => { + v.r().ord().hash(state); + v.g().ord().hash(state); + v.b().ord().hash(state); + } + }; + } +} + impl VarName { pub fn show_editor(&mut self, id: impl std::hash::Hash, ui: &mut Ui) { ComboBox::from_id_source(id)