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
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

View File

@ -378,6 +378,11 @@ impl Construct {
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 {
self.available_skills().len() == 0

View File

@ -134,7 +134,7 @@ impl Game {
.unwrap()
}
fn all_constructs(&self) -> Vec<Construct> {
fn copy_constructs(&self) -> Vec<Construct> {
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<Cast>) -> &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::<Vec<Uuid>>();
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()))
}

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> {
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<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> {
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);
// };