hastestrike and hybridblast

This commit is contained in:
ntr 2019-12-11 16:37:58 +10:00
parent 5e8d6a62af
commit bf3c087155
4 changed files with 77 additions and 251 deletions

View File

@ -3,12 +3,7 @@
last round of animations skipped cause no skill phase to add new vec last round of animations skipped cause no skill phase to add new vec
check silence skill multiplier check silence skill multiplier
tick skips for triage etc
game ready not auto starting resolve phase game ready not auto starting resolve phase
remove immunity
banish immunity
aoe event
cooldown events leak skills cooldown events leak skills
purify conditional healing purify conditional healing

View File

@ -378,6 +378,11 @@ impl Construct {
None None
} }
pub fn additional_skills(&self, skill: Skill) -> Vec<Skill> {
self.effects.iter()
.filter_map(|e| skill.additional_skill(e.effect))
.collect::<Vec<Skill>>()
}
pub fn is_stunned(&self) -> bool { pub fn is_stunned(&self) -> bool {
self.available_skills().len() == 0 self.available_skills().len() == 0

View File

@ -134,7 +134,7 @@ impl Game {
.unwrap() .unwrap()
} }
fn all_constructs(&self) -> Vec<Construct> { fn copy_constructs(&self) -> Vec<Construct> {
self.players.clone() self.players.clone()
.into_iter() .into_iter()
.flat_map( .flat_map(
@ -426,7 +426,7 @@ impl Game {
} }
// find their statuses with ticks // find their statuses with ticks
let mut ticks = self.all_constructs() let mut ticks = self.copy_constructs()
.iter() .iter()
.flat_map( .flat_map(
|c| c.effects |c| c.effects
@ -477,6 +477,7 @@ impl Game {
self.add_resolution(&cast, &Event::Disable { construct: cast.source, effects }); self.add_resolution(&cast, &Event::Disable { construct: cast.source, effects });
return self; return self;
} }
// for aoe events send the source / target animations before each set of casts // for aoe events send the source / target animations before each set of casts
if cast.skill.aoe() { if cast.skill.aoe() {
if cast.skill.cast_animation() { if cast.skill.cast_animation() {
@ -492,6 +493,8 @@ impl Game {
self.execute(cast); self.execute(cast);
} }
self.progress_durations();
self self
} }
@ -515,8 +518,6 @@ impl Game {
false => vec![cast], false => vec![cast],
}; };
// check for reflect
return casts; return casts;
} }
@ -531,6 +532,13 @@ impl Game {
return self; 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) { if let Some(immunity) = self.construct(cast.target).immune(cast.skill) {
self.add_resolution(&cast, &Event::Immune { construct: cast.target, effects: immunity }); self.add_resolution(&cast, &Event::Immune { construct: cast.target, effects: immunity });
return self; return self;
@ -716,17 +724,25 @@ impl Game {
(x, y) (x, y)
} }
fn progress_durations(&mut self, Resolutions: &Vec<Cast>) -> &mut Game { fn progress_durations(&mut self) -> &mut Game {
for mut construct in self.all_constructs() { let last = self.resolutions.len() - 1;
// info!("progressing durations for {:}", construct.name); 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::<Vec<Uuid>>();
for mut construct in self.copy_constructs() {
if construct.is_ko() { if construct.is_ko() {
continue; continue;
} }
match Resolutions.iter().find(|s| s.source == construct.id) { if !used_cooldown.contains(&construct.id) {
Some(skill) => { construct.skill_set_cd(skill.skill); }, construct.reduce_cooldowns();
None => { construct.reduce_cooldowns(); },
}; };
// always reduce durations // always reduce durations
@ -737,66 +753,6 @@ impl Game {
self 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 { pub fn finished(&self) -> bool {
self.phase == Phase::Finished || self.players.iter().any(|t| t.constructs.iter().all(|c| c.is_ko())) self.phase == Phase::Finished || self.players.iter().any(|t| t.constructs.iter().all(|c| c.is_ko()))
} }

View File

@ -47,10 +47,6 @@ impl Cast {
} }
} }
pub fn used_cooldown(&self) -> bool {
return self.skill.base_cd().is_some();
}
pub fn actions(&self) -> Vec<Action> { pub fn actions(&self) -> Vec<Action> {
let mut rng = thread_rng(); 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![ Skill::Hybrid => vec![
Action::Effect { Action::Effect {
construct: self.target, 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::Intercept |
Skill::InterceptPlus | Skill::InterceptPlus |
Skill::InterceptPlusPlus => vec![ Skill::InterceptPlusPlus => vec![
@ -1773,6 +1785,36 @@ impl Skill {
} }
} }
pub fn additional_skill(&self, effect: Effect) -> Option<Skill> {
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<Item> { fn components(&self) -> Vec<Item> {
let mut components = Item::from(*self).components(); let mut components = Item::from(*self).components();
components.sort_unstable(); 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) // // 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);
// };