diff --git a/core/fixme.md b/core/fixme.md index 82bbae82..d2d0b80d 100644 --- a/core/fixme.md +++ b/core/fixme.md @@ -3,12 +3,7 @@ last round of animations skipped cause no skill phase to add new vec check silence skill multiplier -tick skips for triage etc game ready not auto starting resolve phase -remove immunity - banish immunity - -aoe event cooldown events leak skills purify conditional healing diff --git a/core/src/construct.rs b/core/src/construct.rs index 216c2dd0..0a3fdccb 100644 --- a/core/src/construct.rs +++ b/core/src/construct.rs @@ -378,6 +378,11 @@ impl Construct { None } + pub fn additional_skills(&self, skill: Skill) -> Vec { + self.effects.iter() + .filter_map(|e| skill.additional_skill(e.effect)) + .collect::>() + } pub fn is_stunned(&self) -> bool { self.available_skills().len() == 0 diff --git a/core/src/game.rs b/core/src/game.rs index d5adb9b5..df47b821 100644 --- a/core/src/game.rs +++ b/core/src/game.rs @@ -134,7 +134,7 @@ impl Game { .unwrap() } - fn all_constructs(&self) -> Vec { + fn copy_constructs(&self) -> Vec { self.players.clone() .into_iter() .flat_map( @@ -426,7 +426,7 @@ impl Game { } // find their statuses with ticks - let mut ticks = self.all_constructs() + let mut ticks = self.copy_constructs() .iter() .flat_map( |c| c.effects @@ -477,6 +477,7 @@ impl Game { self.add_resolution(&cast, &Event::Disable { construct: cast.source, effects }); return self; } + // for aoe events send the source / target animations before each set of casts if cast.skill.aoe() { if cast.skill.cast_animation() { @@ -492,6 +493,8 @@ impl Game { self.execute(cast); } + self.progress_durations(); + self } @@ -515,8 +518,6 @@ impl Game { false => vec![cast], }; - // check for reflect - return casts; } @@ -531,6 +532,13 @@ impl Game { return self; } + // hastestrike / hybridblast + // i think this goes here... + // maybe in modify casts or something + for skill in self.construct(cast.target).additional_skills(cast.skill) { + self.resolve(Cast { skill, ..cast }); + } + if let Some(immunity) = self.construct(cast.target).immune(cast.skill) { self.add_resolution(&cast, &Event::Immune { construct: cast.target, effects: immunity }); return self; @@ -716,17 +724,25 @@ impl Game { (x, y) } - fn progress_durations(&mut self, Resolutions: &Vec) -> &mut Game { - for mut construct in self.all_constructs() { - // info!("progressing durations for {:}", construct.name); + fn progress_durations(&mut self) -> &mut Game { + let last = self.resolutions.len() - 1; + let used_cooldown = self.resolutions[last].iter() + .filter_map(|r| match r.event { + Event::Cast { construct, player: _, direction: _ } => match r.skill.base_cd().is_some() { + true => Some(construct), + false => None, + } + _ => None, + }) + .collect::>(); + for mut construct in self.copy_constructs() { if construct.is_ko() { continue; } - match Resolutions.iter().find(|s| s.source == construct.id) { - Some(skill) => { construct.skill_set_cd(skill.skill); }, - None => { construct.reduce_cooldowns(); }, + if !used_cooldown.contains(&construct.id) { + construct.reduce_cooldowns(); }; // always reduce durations @@ -737,66 +753,6 @@ impl Game { self } - // fn log_resolution(&mut self, speed: usize, resolution: &Resolution) -> &mut Game { - // let Resolution { source, target, Resolution, stages: _ } = resolution; - // match Resolution { - // Resolution::Ko { skill: _ }=> - // self.log.push(format!("{:} KO!", target.name)), - - // Resolution::Disable { skill, disable } => - // self.log.push(format!("{:} {:?} {:} disabled {:?}", - // source.name, skill, target.name, disable)), - - // Resolution::Immunity { skill, immunity } => - // self.log.push(format!("[{:}] {:} {:?} {:} immune {:?}", - // speed, source.name, skill, target.name, immunity)), - - // Resolution::TargetKo { skill } => - // self.log.push(format!("[{:}] {:} {:?} {:} - target is KO", - // speed, source.name, skill, target.name)), - - // Resolution::Damage { skill, amount, mitigation, colour: _ } => - // self.log.push(format!("[{:}] {:} {:?} {:} {:} ({:} mitigated)", - // speed, source.name, skill, target.name, amount, mitigation)), - - // Resolution::Healing { skill, amount, overhealing } => - // self.log.push(format!("[{:}] {:} {:?} {:} {:} healing ({:}OH)", - // speed, source.name, skill, target.name, amount, overhealing)), - - // Resolution::Inversion { skill } => - // self.log.push(format!("[{:}] {:} {:?} {:} INVERTED", - // speed, source.name, skill, target.name)), - - // Resolution::Reflection { skill } => - // self.log.push(format!("[{:}] {:} {:?} {:} REFLECTED", - // speed, source.name, skill, target.name)), - - // Resolution::Effect { skill, effect, duration, construct_effects: _ } => - // self.log.push(format!("[{:}] {:} {:?} {:} {:?} {:}T", - // speed, source.name, skill, target.name, effect, duration)), - - // Resolution::Skill { skill } => - // self.log.push(format!("[{:}] {:} {:?} {:}", - // speed, source.name, skill, target.name)), - - // Resolution::Removal { effect, construct_effects: _ } => - // self.log.push(format!("[{:}] {:?} removed {:} {:?}", - // speed, source.name, target.name, effect)), - - // Resolution::Recharge { skill, red, blue } => - // self.log.push(format!("[{:}] {:} {:?} {:} {:}R {:}B", - // speed, source.name, skill, target.name, red, blue)), - - // Resolution::Evasion { skill, evasion_rating } => - // self.log.push(format!("[{:}] {:} {:?} {:} evaded ({:}%)", - // speed, source.name, skill, target.name, evasion_rating)), - - // Resolution::Incomplete => panic!("incomplete resolution {:?}", resolution), - // } - - // self - // } - pub fn finished(&self) -> bool { self.phase == Phase::Finished || self.players.iter().any(|t| t.constructs.iter().all(|c| c.is_ko())) } diff --git a/core/src/skill.rs b/core/src/skill.rs index db4f0f30..d6e0e991 100644 --- a/core/src/skill.rs +++ b/core/src/skill.rs @@ -47,10 +47,6 @@ impl Cast { } } - pub fn used_cooldown(&self) -> bool { - return self.skill.base_cd().is_some(); - } - pub fn actions(&self) -> Vec { let mut rng = thread_rng(); @@ -509,6 +505,14 @@ impl Cast { }, ], + Skill::HasteStrike => vec![ + Action::Damage { + construct: self.target, + colour: Colour::Red, + values: vec![Value::Stat { construct: self.source, stat: Stat::Speed, mult: self.skill.multiplier() }], + }, + ], + Skill::Hybrid => vec![ Action::Effect { construct: self.target, @@ -528,6 +532,14 @@ impl Cast { }, ], + Skill::HybridBlast => vec![ + Action::Damage { + construct: self.target, + colour: Colour::Blue, + values: vec![Value::Stat { construct: self.source, stat: Stat::GreenPower, mult: self.skill.multiplier() }], + }, + ], + Skill::Intercept | Skill::InterceptPlus | Skill::InterceptPlusPlus => vec![ @@ -1773,6 +1785,36 @@ impl Skill { } } + pub fn additional_skill(&self, effect: Effect) -> Option { + match effect { + Effect::Haste => match self { + Skill::Slay | + Skill::SlayPlus | + Skill::SlayPlusPlus | + Skill::Chaos | + Skill::ChaosPlus | + Skill::ChaosPlusPlus | + Skill::Strike | + Skill::StrikePlus | + Skill::StrikePlusPlus => Some(Skill::HasteStrike), + _ => None, + }, + Effect::Hybrid => match self { + Skill::Blast| + Skill::BlastPlus | + Skill::BlastPlusPlus | + Skill::Chaos | + Skill::ChaosPlus | + Skill::ChaosPlusPlus | + Skill::Siphon | + Skill::SiphonPlus | + Skill::SiphonPlusPlus => Some(Skill::HybridBlast), + _ => None, + }, + _ => None, + } + } + fn components(&self) -> Vec { let mut components = Item::from(*self).components(); components.sort_unstable(); @@ -2145,177 +2187,5 @@ mod tests { // } } - - // Skill::Strike => game.event( - // Event::Damage { - // colour: Colour::Red, - // amount: SkillPower { construct: cast.source, skill: Skill::Strike } - // }, - // Event::LifeSteal { - // amount: CastDamage { id: cast.id, colour: Colour::Red, target: cast.source }, - // }, - // ), - - // Skill::Attack => game.event(Event::Damage { - // colour: Colour::Red, - // amount: Stat { construct: cast.source, stat: Stat::RedPower, pct: ATTACK_RED_POWER_PCT } - // }), - - - // Skill::Bash => game.event(Event::Damage { - // colour: Colour::Red, - // amounts: vec![ - // Stat { construct: cast.source, stat: Stat::RedPower, pct: ATTACK_RED_POWER_PCT }, - // Cooldowns { construct: cast.source }, - // ], - // }) - - // // we clone the current state of the target and source - // // so we can modify them during the resolution - // // no more than 1 mutable ref allowed on game - - // let mut source = game.construct_by_id(cast.source).unwrap().clone(); - // let mut target = game.construct_by_id(target_id).unwrap().clone(); - - // // bail out on ticks that have been removed - // if skill.is_tick() && target.effects.iter().find(|ce| match ce.tick { - // Some(t) => t.id == cast.id, - // None => false, - // }).is_none() { - // return; - // } - - // if let Some(_disable) = source.disabled(skill) { - // game.event(Event::new(source, target).event(Event::Disable { disable, skill }).stages(EventStages::PostOnly)); - // return; - // } - - // if target.is_ko() { - // game.event(Event::new(source, target).event(Event::TargetKo { skill }).stages(EventStages::PostOnly)); - // return; - // } - - // if target.affected(Effect::Reflect) && skill.colours().contains(&Colour::Blue) && !skill.is_tick() { - // // guard against overflow - // if source.affected(Effect::Reflect) { - // } - // game.event(Event::new(source, target).event(Event::Reflection { skill })); - // return cast_actions(skill, &mut source.clone(), source, resolutions); - // } - - // // haste_strike_check(game) - // if source.affected(Effect::Haste) { - // match skill { - // Skill::Slay | - // Skill::SlayPlus | - // Skill::SlayPlusPlus | - // Skill::Chaos | - // Skill::ChaosPlus | - // Skill::ChaosPlusPlus | - // Skill::Strike | - // Skill::StrikePlus | - // Skill::StrikePlusPlus => { - // let amount = source.speed().pct(Skill::HasteStrike.multiplier()); - // target.deal_red_damage(Skill::HasteStrike, amount) - // .into_iter() - // .for_each(|e| game.event(Event::new(source, target).event(e))); - // }, - // _ => (), - // } - // } - - // if source.affected(Effect::Hybrid) { - // match skill { - // Skill::Blast| - // Skill::BlastPlus | - // Skill::BlastPlusPlus | - // Skill::Chaos | - // Skill::ChaosPlus | - // Skill::ChaosPlusPlus | - // Skill::Siphon | - // Skill::SiphonPlus | - // Skill::SiphonPlusPlus => { - // let amount = source.green_power().pct(Skill::HybridBlast.multiplier()); - // target.deal_blue_damage(Skill::HybridBlast, amount) - // .into_iter() - // .for_each(|e| game.event(Event::new(source, target).event(e))); - // }, - // _ => (), - // } - // } - - // match self.category() == EffectCategory::Red { - // true => { - // if let Some(evasion) = target.evade(*self) { - // game.event(evasion); - // return Event; - // } - // }, - // false => (), - // } - - // for Event { source: event_source, target: event_target, event, stages: _ } in resolutions.clone() { - // let mut source = game.construct_by_id(event_source.id).unwrap().clone(); - // let mut target = game.construct_by_id(event_target.id).unwrap().clone(); - - // match event { - // Event::Damage { amount, skill, mitigation, colour: c } => { - // if target.affected(Effect::Electric) && !skill.is_tick() { - // let ConstructEffect { effect: _, duration: _, meta, tick: _ } = target.effects.iter() - // .find(|e| e.effect == Effect::Electric).unwrap().clone(); - // match meta { - // Some(EffectMeta::CastOnHit(s)) => { - // // Gurad against reflect overflow - // if !(source.affected(Effect::Reflect) && target.affected(Effect::Reflect)) { - // // Check reflect don't bother if electrocute is procing on death - // if source.affected(Effect::Reflect) && !target.is_ko() { - // game.event(Event::new(&target, &source) - // .event(Event::Reflection { skill: s }).stages(EventStages::EndPost)); - // electrocute(&mut source, &mut target, resolutions, s); - // } else { - // electrocute(&mut target, &mut source, resolutions, s); - // } - // } - // }, - // _ => panic!("no electrify skill"), - // }; - // } - - // if target.affected(Effect::Absorb) && !target.is_ko() { - // let ConstructEffect { effect: _, duration: _, meta, tick: _ } = target.effects.iter() - // .find(|e| e.effect == Effect::Absorb).unwrap().clone(); - // match meta { - // Some(EffectMeta::CastOnHit(s)) => { - // absorption(&mut source, &mut target, resolutions, skill, amount + mitigation, s); - // }, - // _ => panic!("no absorb skill"), - // }; - // } - // if c == Colour::Red { - // if target.affected(Effect::Counter) && !target.is_ko() { - // let ConstructEffect { effect: _, duration: _, meta, tick: _ } = target.effects.iter() - // .find(|e| e.effect == Effect::Counter).unwrap().clone(); - // match meta { - // Some(EffectMeta::CastOnHit(s)) => { - // counter_attack(&mut target, &mut source, resolutions, s); - // }, - // _ => panic!("no counter skill"), - // }; - // } - // } - - // if target.is_ko() && event_target.green == 0 { - // // Make sure target ko is from this event - // target.effects.clear(); - // game.event(Event::new(&source, &target).event(Event::Ko()).stages(EventStages::PostOnly)); - // } - // }, - // _ => (), - // }; - - - // game.update_construct(&mut source); - // game.update_construct(&mut target); - // };