diff --git a/core/fixme.md b/core/fixme.md index 53ce1e4f..08be114d 100644 --- a/core/fixme.md +++ b/core/fixme.md @@ -1,7 +1,6 @@ # FIXME -last round of animations skipped cause no skill phase to add new vec -check silence skill multiplier +check silence skill multiplier game ready not auto starting resolve phase purify conditional healing diff --git a/core/src/construct.rs b/core/src/construct.rs index 42e56fce..b56ac669 100644 --- a/core/src/construct.rs +++ b/core/src/construct.rs @@ -934,6 +934,26 @@ impl Construct { return vec![]; } + pub fn effect_meta(&mut self, effect: Effect, amount: usize) -> Vec { + let construct = self.id; + self.effects.iter_mut() + .filter(|ce| ce.effect == effect) + .flat_map(|ce| match ce.meta.unwrap() { + EffectMeta::AddedDamage(_) => { + let meta = EffectMeta::AddedDamage(amount); + ce.meta = Some(meta); + vec![Event::Meta { construct, effect, meta }] + }, + EffectMeta::Multiplier(_) => { + let meta = EffectMeta::Multiplier(amount); + ce.meta = Some(meta); + vec![Event::Meta { construct, effect, meta }] + }, + _ => vec![], + } + ).collect() + } + pub fn remove_all(&mut self) -> Vec { let mut removals = vec![]; if self.is_ko() { return vec![Event::TargetKo { construct: self.id }] } @@ -967,26 +987,25 @@ impl Construct { let ConstructEffect { effect: _, duration: _, meta } = self.effects.iter().find(|e| e.effect == Effect::Electric).unwrap(); - let skill = match meta { - Some(EffectMeta::CastOnHit(s)) => s, + match meta { + Some(EffectMeta::CastTick { source: _, target, skill }) => + casts.push(Cast::new(self.id, self.account, *target, *skill)), _ => panic!("no electrify skill {:?}", meta), }; - - casts.push(Cast::new(self.id, self.account, cast.source, *skill)); } - // if self.affected(Effect::Absorb) { - // let ConstructEffect { effect, duration: _, meta } = - // self.effects.iter().find(|e| e.effect == Effect::Absorb).unwrap(); + if self.affected(Effect::Absorb) { + let ConstructEffect { effect: _, duration: _, meta } = + self.effects.iter().find(|e| e.effect == Effect::Absorb).unwrap(); - // let skill = match meta { - // Some(EffectMeta::Skill(s)) => s, - // _ => panic!("no absorption skill"), - // }; + let skill = match meta { + Some(EffectMeta::CastOnHit(s)) => s, + _ => panic!("no absorb skill {:?}", meta), + }; - // casts.push(Cast::new(self.id, self.account, self.id, *skill)); - // } + casts.push(Cast::new(self.id, self.account, self.id, *skill)); + } if self.affected(Effect::Counter) && *colour == Colour::Red { let ConstructEffect { effect: _, duration: _, meta } = diff --git a/core/src/game.rs b/core/src/game.rs index 7a7e7c01..86765287 100644 --- a/core/src/game.rs +++ b/core/src/game.rs @@ -440,7 +440,8 @@ impl Game { // because need to check cooldown use before pushing them into the complete list let mut r_animation_ms = 0; while let Some(cast) = self.stack.pop() { - self.resolve(cast); + let events = vec![]; + self.resolve(cast, events); // sort the stack again in case speeds have changed self.stack_sort_speed(); @@ -463,16 +464,16 @@ impl Game { self.skill_phase_start(r_animation_ms) } - fn resolve(&mut self, cast: Cast) -> &mut Game { + fn resolve(&mut self, cast: Cast, mut events: Vec) -> Vec { // If the skill is disabled for source nothing else will happen if let Some(effects) = self.construct(cast.source).disabled(cast.skill) { self.add_resolution(&cast, &Event::Disable { construct: cast.source, effects }); - return self; + return events; } // hastestrike / hybridblast for skill in self.construct(cast.source).additional_skills(cast.skill) { - self.resolve(Cast { skill, ..cast }); + events = self.resolve(Cast { skill, ..cast }, events); } // for aoe events send the source / target animations before each set of casts @@ -487,10 +488,10 @@ impl Game { let casts = self.modify_cast(cast); for cast in casts { - self.execute(cast); + events = self.execute(cast, events); } - self + events } fn modify_cast(&self, cast: Cast) -> Vec { @@ -516,20 +517,15 @@ impl Game { return casts; } - fn execute(&mut self, cast: Cast) -> &mut Game { - // this list is used to calculate values - // that are dependent on the result of previous events - // such as healing based on damage done etc - let mut event_list = vec![]; - + fn execute(&mut self, cast: Cast, mut events: Vec) -> Vec { if self.construct(cast.target).is_ko() { self.add_resolution(&cast, &Event::TargetKo { construct: cast.target }); - return self; + return events; } if let Some(immunity) = self.construct(cast.target).immune(cast.skill) { self.add_resolution(&cast, &Event::Immune { construct: cast.target, effects: immunity }); - return self; + return events; } if self.construct(cast.target).affected(Effect::Reflect) && cast.skill.colours().contains(&Colour::Blue) && !cast.skill.is_tick() { @@ -538,24 +534,24 @@ impl Game { // both reflecting, show it and bail if self.construct(cast.source).affected(Effect::Reflect) { self.add_resolution(&cast, &Event::Reflection { construct: cast.source }); - return self; + return events; } - return self.execute(Cast { target: cast.source, ..cast }, ); + return self.execute(Cast { target: cast.source, ..cast }, events); } for action in cast.actions() { - let events = match action { + let new_events = match action { Action::Cast => vec![self.cast(cast)], Action::Hit => vec![self.hit(cast)], Action::Damage { construct, values, colour } => { - let amount = self.reduce_values(&values, &event_list); + let amount = self.reduce_values(&values, &events); self.damage(construct, amount, colour) }, Action::Heal { construct, values, colour } => { - let amount = self.reduce_values(&values, &event_list); + let amount = self.reduce_values(&values, &events); self.heal(construct, amount, colour) }, @@ -563,19 +559,37 @@ impl Game { Action::Remove { construct, effect } => self.effect_remove(construct, effect), Action::RemoveAll { construct } => self.remove_all(construct), Action::IncreaseCooldowns { construct, turns } => self.increase_cooldowns(construct, turns), + Action::SetEffectMeta { construct, values, effect } => { + let amount = self.reduce_values(&values, &events); + self.effect_meta(construct, effect, amount) + } }; - for event in events { - // this event is now considered to have happened - // for chronological ordering it is added to the resolution list - // before extra processing on it begins + // this event is now considered to have happened + // for chronological ordering it is added to the resolution list + // before extra processing on it begins + + for event in new_events { self.add_resolution(&cast, &event); - event_list.push(event); - self.event_reactions(&cast, &event_list[event_list.len() - 1]); + + let casts = match event { + Event::Damage { construct, colour: _, amount: _, mitigation: _, display: _ } => + self.construct_by_id(construct).unwrap().damage_trigger_casts(&cast, &event), + // Event::Cast {} => set_cooldown() + Event::Ko { construct } => + self.construct_by_id(construct).unwrap().on_ko(&cast, &event), + _ => vec![], + }; + + events.push(event); + + for cast in casts { + events = self.resolve(cast, events); + } } } - self + events } fn add_resolution(&mut self, cast: &Cast, event: &Event) -> &mut Game { @@ -584,23 +598,6 @@ impl Game { self } - fn event_reactions(&mut self, cast: &Cast, event: &Event) -> &mut Game { - let casts = match event { - Event::Damage { construct, colour, amount, mitigation, display: _ } => - self.construct_by_id(*construct).unwrap().damage_trigger_casts(cast, event), - // Event::Cast {} => set_cooldown() - Event::Ko { construct } => - self.construct_by_id(*construct).unwrap().on_ko(cast, event), - _ => vec![], - }; - - for cast in casts { - self.resolve(cast); - } - - self - } - fn reduce_values(&mut self, values: &Vec, events: &Vec) -> usize { values.iter() .fold(0, |total, value| { @@ -679,6 +676,10 @@ impl Game { self.construct_by_id(construct).unwrap().effect_remove(effect) } + fn effect_meta(&mut self, construct: Uuid, effect: Effect, amount: usize) -> Vec { + self.construct_by_id(construct).unwrap().effect_meta(effect, amount) + } + // could be remove by colour etc fn remove_all(&mut self, construct: Uuid) -> Vec { self.construct_by_id(construct).unwrap().remove_all() @@ -844,6 +845,7 @@ pub enum Action { Remove { construct: Uuid, effect: Effect }, RemoveAll { construct: Uuid }, IncreaseCooldowns { construct: Uuid, turns: usize }, + SetEffectMeta { construct: Uuid, values: Vec, effect: Effect }, } #[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] @@ -886,6 +888,7 @@ pub enum Event { Damage { construct: Uuid, amount: usize, mitigation: usize, colour: Colour, display: EventConstruct }, Effect { construct: Uuid, effect: Effect, duration: u8, display: EventConstruct }, Removal { construct: Uuid, effect: Effect, display: EventConstruct }, + Meta { construct: Uuid, effect: Effect, meta: EffectMeta }, Healing { construct: Uuid, amount: usize, overhealing: usize, colour: Colour, display: EventConstruct }, Inversion { construct: Uuid }, @@ -964,6 +967,7 @@ impl Event { Event::TargetKo { construct: _ } | Event::Disable { construct: _, effects: _ } | Event::Immune { construct: _, effects: _ } | + Event::Meta { construct: _, effect: _, meta: _ } | Event::Forfeit() => Animation::Skip, } } @@ -1677,7 +1681,7 @@ mod tests { let source = game.players[0].constructs[0].id; let target = game.players[1].constructs[0].id; - game.resolve(Cast::new(source, player_id, target, Skill::Bash)); + game.resolve(Cast::new(source, player_id, target, Skill::Bash), vec![]); } #[test] @@ -1687,7 +1691,7 @@ mod tests { let source = game.players[0].constructs[0].id; let target = game.players[1].constructs[0].id; - game.resolve(Cast::new(source, player_id, target, Skill::Slay)); + game.resolve(Cast::new(source, player_id, target, Skill::Slay), vec![]); let last = game.resolutions.len() - 1; let resolutions = &game.resolutions[last]; @@ -1712,9 +1716,9 @@ mod tests { let source = game.players[0].constructs[0].id; let target = game.players[1].constructs[0].id; - game.resolve(Cast::new(source, player_id, target, Skill::Strike)); - game.resolve(Cast::new(source, player_id, target, Skill::Invert)); - game.resolve(Cast::new(source, player_id, target, Skill::Strike)); + game.resolve(Cast::new(source, player_id, target, Skill::Strike), vec![]); + game.resolve(Cast::new(source, player_id, target, Skill::Invert), vec![]); + game.resolve(Cast::new(source, player_id, target, Skill::Strike), vec![]); let last = game.resolutions.len() - 1; let resolutions = &game.resolutions[last]; @@ -1739,7 +1743,7 @@ mod tests { let source = game.players[0].constructs[0].id; let target = game.players[1].constructs[0].id; - game.resolve(Cast::new(source, player_id, target, Skill::Siphon)); + game.resolve(Cast::new(source, player_id, target, Skill::Siphon), vec![]); let last = game.resolutions.len() - 1; let resolutions = &game.resolutions[last]; @@ -1767,12 +1771,12 @@ mod tests { game = game.resolve_phase_start(); // que ota? - game.resolve(Cast::new(source, player_id, target, Skill::Siphon)); + game.resolve(Cast::new(source, player_id, target, Skill::Siphon), vec![]); let last = game.resolutions.len() - 1; let resolutions = &game.resolutions[last]; let damage_events = resolutions.iter().filter(|r| match r.event { - Event::Damage { construct, colour, amount, mitigation: _, display: _ } => true, + Event::Damage { construct: _, colour: _, amount: _, mitigation: _, display: _ } => true, _ => false, }).count(); @@ -1786,8 +1790,8 @@ mod tests { let source = game.players[0].constructs[0].id; let target = game.players[1].constructs[0].id; - game.resolve(Cast::new(source, player_id, target, Skill::Reflect)); - game.resolve(Cast::new(source, player_id, target, Skill::Blast)); + game.resolve(Cast::new(source, player_id, target, Skill::Reflect), vec![]); + game.resolve(Cast::new(source, player_id, target, Skill::Blast), vec![]); let last = game.resolutions.len() - 1; let resolutions = &game.resolutions[last]; @@ -1798,4 +1802,35 @@ mod tests { _ => false, })); } + + #[test] + fn absorb_test() { + let mut game = create_2v2_test_game(); + let player_id = game.players[0].id; + let source = game.players[0].constructs[0].id; + let target = game.players[1].constructs[0].id; + + game.resolve(Cast::new(source, player_id, target, Skill::Absorb), vec![]); + game.resolve(Cast::new(source, player_id, target, Skill::Blast), vec![]); + + let last = game.resolutions.len() - 1; + let resolutions = &game.resolutions[last]; + + assert!(resolutions.iter().any(|r| match r.event { + Event::Damage { construct, colour, amount, mitigation: _, display: _ } => { + assert!(construct == target && amount > 0 && colour == Colour::Blue && r.skill == Skill::Blast); + resolutions.iter().any(|r| match r.event { + Event::Meta { construct, effect, meta } => + construct == target && effect == Effect::Absorption && { + match meta { + EffectMeta::AddedDamage(added_dmg) => added_dmg == amount, + _ => false, + } + }, + _ => false, + }) + }, + _ => false, + })); + } } diff --git a/core/src/skill.rs b/core/src/skill.rs index 792038cc..54c365eb 100644 --- a/core/src/skill.rs +++ b/core/src/skill.rs @@ -155,8 +155,13 @@ impl Cast { effect: Effect::Absorb, }, Action::Effect { - construct: self.target, - effect: ConstructEffect { effect: Effect::Absorption, duration: 3, meta: None }, + construct: self.source, + effect: ConstructEffect { effect: Effect::Absorption, duration: 3, meta: Some(EffectMeta::AddedDamage(0)) }, + }, + Action::SetEffectMeta { + construct: self.source, + effect: Effect::Absorption, + values: vec![Value::DamageReceived { construct: self.source, colour: Colour::Blue, mult: 100 }], }, ], Skill::AbsorptionPlus => vec![ @@ -166,7 +171,12 @@ impl Cast { }, Action::Effect { construct: self.target, - effect: ConstructEffect { effect: Effect::Absorption, duration: 4, meta: None }, + effect: ConstructEffect { effect: Effect::Absorption, duration: 4, meta: Some(EffectMeta::AddedDamage(0)) }, + }, + Action::SetEffectMeta { + construct: self.target, + effect: Effect::Absorption, + values: vec![Value::DamageReceived { construct: self.target, colour: Colour::Blue, mult: 100 }], }, ], Skill::AbsorptionPlusPlus => vec![ @@ -176,7 +186,12 @@ impl Cast { }, Action::Effect { construct: self.target, - effect: ConstructEffect { effect: Effect::Absorption, duration: 5, meta: None }, + effect: ConstructEffect { effect: Effect::Absorption, duration: 5, meta: Some(EffectMeta::AddedDamage(0)) }, + }, + Action::SetEffectMeta { + construct: self.target, + effect: Effect::Absorption, + values: vec![Value::DamageReceived { construct: self.target, colour: Colour::Blue, mult: 100 }], }, ],