scatter and revised skill resolution

This commit is contained in:
ntr 2019-05-07 19:48:17 +10:00
parent cd6e31b306
commit 69a0cd4f73
4 changed files with 207 additions and 163 deletions

View File

@ -564,6 +564,11 @@ impl Cryp {
} }
} }
if self.is_ko() {
events.push(Event::Ko { skill });
self.effects.clear();
}
return events; return events;
} }
@ -637,6 +642,12 @@ impl Cryp {
} }
}; };
if self.is_ko() {
events.push(Event::Ko { skill });
self.effects.clear();
}
return events; return events;
} }
@ -706,6 +717,11 @@ impl Cryp {
} }
}; };
if self.is_ko() {
events.push(Event::Ko { skill });
self.effects.clear();
}
return events; return events;
} }

View File

@ -1163,9 +1163,124 @@ mod tests {
// should not be stunned because of parry // should not be stunned because of parry
assert!(game.player_by_id(x_player.id).unwrap().cryps[0].is_stunned() == false); assert!(game.player_by_id(x_player.id).unwrap().cryps[0].is_stunned() == false);
// riposte // riposte
assert!(game.player_by_id(y_player.id).unwrap().cryps[0].green_life() == (1024 - x_cryp.red_damage().pct(Skill::Riposte.multiplier()))); assert_eq!(game.player_by_id(y_player.id).unwrap().cryps[0].green_life(), (1024 - x_cryp.red_damage().pct(Skill::Riposte.multiplier())));
} }
#[test]
fn corrupt_test() {
let mut game = create_test_game();
let x_player = game.players[0].clone();
let y_player = game.players[1].clone();
let x_cryp = x_player.cryps[0].clone();
let y_cryp = y_player.cryps[0].clone();
game.cryp_by_id(x_cryp.id).unwrap().learn_mut(Skill::Corrupt);
while game.cryp_by_id(x_cryp.id).unwrap().skill_on_cd(Skill::Corrupt).is_some() {
game.cryp_by_id(x_cryp.id).unwrap().reduce_cooldowns();
}
// apply buff
game.add_skill(x_player.id, x_cryp.id, None, Skill::Corrupt).unwrap();
game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap();
game = game.resolve_phase_start();
assert!(game.cryp_by_id(x_cryp.id).unwrap().affected(Effect::Corrupt));
// attack and receive debuff
game.add_skill(y_player.id, y_cryp.id, Some(x_cryp.id), Skill::TestTouch).unwrap();
game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap();
game = game.resolve_phase_start();
assert!(game.cryp_by_id(y_cryp.id).unwrap().affected(Effect::Corruption));
}
#[test]
fn scatter_test() {
let mut game = create_test_game();
let x_player = game.players[0].clone();
let y_player = game.players[1].clone();
let x_cryp = x_player.cryps[0].clone();
let y_cryp = y_player.cryps[0].clone();
game.cryp_by_id(x_cryp.id).unwrap().learn_mut(Skill::Scatter);
while game.cryp_by_id(x_cryp.id).unwrap().skill_on_cd(Skill::Scatter).is_some() {
game.cryp_by_id(x_cryp.id).unwrap().reduce_cooldowns();
}
// apply buff
game.add_skill(x_player.id, x_cryp.id, Some(y_cryp.id), Skill::Scatter).unwrap();
game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap();
game = game.resolve_phase_start();
assert!(game.cryp_by_id(x_cryp.id).unwrap().affected(Effect::Scatter));
let Resolution { source: _, target: _, event } = game.resolved.pop().unwrap();
match event {
Event::Effect { effect, skill: _, duration: _ } => assert_eq!(effect, Effect::Scatter),
_ => panic!("not siphon"),
};
let Resolution { source: _, target: _, event } = game.resolved.pop().unwrap();
match event {
Event::Recharge { red: _, blue: _, skill: _ } => (),
_ => panic!("scatter result was not recharge"),
}
// attack and receive scatter hit
game.add_skill(y_player.id, y_cryp.id, Some(x_cryp.id), Skill::Attack).unwrap();
game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap();
game = game.resolve_phase_start();
let Resolution { source: _, target, event } = game.resolved.pop().unwrap();
assert_eq!(target.id, y_cryp.id);
match event {
Event::Damage { amount, skill: _, mitigation: _, colour: _} =>
assert_eq!(amount, 256.pct(Skill::Attack.multiplier()) >> 1),
_ => panic!("not damage scatter"),
};
}
// #[test]
// fn hatred_test() {
// let mut game = create_test_game();
// let x_player = game.players[0].clone();
// let y_player = game.players[1].clone();
// let x_cryp = x_player.cryps[0].clone();
// let y_cryp = y_player.cryps[0].clone();
// game.cryp_by_id(x_cryp.id).unwrap().learn_mut(Skill::Hostility);
// while game.cryp_by_id(x_cryp.id).unwrap().skill_on_cd(Skill::Hostility).is_some() {
// game.cryp_by_id(x_cryp.id).unwrap().reduce_cooldowns();
// }
// // apply buff
// game.add_skill(x_player.id, x_cryp.id, Some(x_cryp.id), Skill::Hostility).unwrap();
// game.player_ready(x_player.id).unwrap();
// game.player_ready(y_player.id).unwrap();
// game = game.resolve_phase_start();
// assert!(game.cryp_by_id(x_cryp.id).unwrap().affected(Effect::Hostility));
// // attack and receive debuff
// game.add_skill(y_player.id, y_cryp.id, Some(x_cryp.id), Skill::TestAttack).unwrap();
// game.player_ready(x_player.id).unwrap();
// game.player_ready(y_player.id).unwrap();
// game = game.resolve_phase_start();
// println!("{:#?}", game);
// assert!(game.cryp_by_id(y_cryp.id).unwrap().affected(Effect::Hatred));
// }
#[test] #[test]
fn aoe_test() { fn aoe_test() {
let mut game = create_2v2_test_game(); let mut game = create_2v2_test_game();

View File

@ -17,16 +17,20 @@ pub fn resolution_steps(cast: &Cast, game: &mut Game) -> Resolutions {
pub fn pre_resolve(cast: &Cast, game: &mut Game, mut resolutions: Resolutions) -> Resolutions { pub fn pre_resolve(cast: &Cast, game: &mut Game, mut resolutions: Resolutions) -> Resolutions {
let skill = cast.skill; let skill = cast.skill;
let mut source = game.cryp_by_id(cast.source_cryp_id).unwrap().clone(); let source = game.cryp_by_id(cast.source_cryp_id).unwrap().clone();
let targets = game.get_targets(cast.skill, &source, cast.target_cryp_id); let targets = game.get_targets(cast.skill, &source, cast.target_cryp_id);
for target_id in targets { for target_id in targets {
let mut source = game.cryp_by_id(cast.source_cryp_id).unwrap().clone(); let mut source = game.cryp_by_id(cast.source_cryp_id).unwrap().clone();
let mut target = game.cryp_by_id(target_id).unwrap().clone(); let mut target = game.cryp_by_id(target_id).unwrap().clone();
resolutions = resolve(cast.skill, &mut source, &mut target, resolutions); resolutions = resolve(cast.skill, &mut source, &mut target, resolutions);
// save the clones // save the clones
game.update_cryp(&mut source); game.update_cryp(&mut source);
game.update_cryp(&mut target); game.update_cryp(&mut target);
// do additional steps
resolutions = post_resolve(cast.skill, game, resolutions);
} }
return resolutions; return resolutions;
@ -52,10 +56,6 @@ pub fn resolve(skill: Skill, source: &mut Cryp, target: &mut Cryp, mut resolutio
return resolve(skill, target, source, resolutions); return resolve(skill, target, source, resolutions);
} }
if target.affected(Effect::Scatter) {
resolutions = resolve(skill, target, source, resolutions);
}
// match self.category() == Category::Red { // match self.category() == Category::Red {
// true => { // true => {
// if let Some(evasion) = target.evade(*self) { // if let Some(evasion) = target.evade(*self) {
@ -126,36 +126,43 @@ pub fn resolve(skill: Skill, source: &mut Cryp, target: &mut Cryp, mut resolutio
Skill::TestSiphon => siphon(source, target, resolutions, Skill::Siphon), Skill::TestSiphon => siphon(source, target, resolutions, Skill::Siphon),
}; };
// if any event dealt damage to target cryp return resolutions;
// hit them with corruption }
// on damage events fn post_resolve(_skill: Skill, game: &mut Game, mut resolutions: Resolutions) -> Resolutions {
// todo not sure if this fucks up with multiple calls to resolve for Resolution { source, target, event } in resolutions.clone() {
// have to think let mut source = game.cryp_by_id(source.id).unwrap().clone();
for r in resolutions.clone() { let mut target = game.cryp_by_id(target.id).unwrap().clone();
match r.event {
match event {
Event::Damage { amount, skill, mitigation: _, colour: _ } => { Event::Damage { amount, skill, mitigation: _, colour: _ } => {
if target.affected(Effect::Corrupt) { if target.affected(Effect::Corrupt) {
resolutions = corruption(target, source, resolutions, Skill::Corrupt); resolutions = corruption(&mut target, &mut source, resolutions, Skill::Corrupt);
} }
if target.affected(Effect::Hostility) { if target.affected(Effect::Hostility) {
resolutions = hatred(source, target, resolutions, skill, amount, Skill::Hostility); resolutions = hatred(&mut source, &mut target, resolutions, skill, amount, Skill::Hostility);
}
// beware that scatter doesn't cause any damage
// because then applying it will proc this
if target.affected(Effect::Scatter) {
resolutions = scatter_hit(&source, &target, resolutions, game, event)
} }
}, },
Event::Immunity { skill: _, immunity } => match immunity.contains(&Effect::Parry) { Event::Immunity { skill: _, immunity } => match immunity.contains(&Effect::Parry) {
true => resolutions = riposte(target, source, resolutions, Skill::Riposte), true => {
resolutions = riposte(&mut target, &mut source, resolutions, Skill::Riposte);
}
false => (), false => (),
}, },
_ => (), _ => (),
} };
};
// i don't think we need to check the source being ko game.update_cryp(&mut source);
if target.is_ko() { game.update_cryp(&mut target);
resolutions.push(Resolution::new(source, target).event(Event::Ko { skill })); };
target.effects.clear();
}
return resolutions; return resolutions;
} }
@ -381,6 +388,8 @@ impl Effect {
Effect::Haste => vec![Stat::Speed], Effect::Haste => vec![Stat::Speed],
Effect::Slow => vec![Stat::Speed], Effect::Slow => vec![Stat::Speed],
Effect::Scatter => vec![Stat::BlueDamageTaken, Stat::GreenDamageTaken, Stat::RedDamageTaken],
_ => vec![], _ => vec![],
} }
} }
@ -399,6 +408,8 @@ impl Effect {
Effect::Impurity => value << 1, Effect::Impurity => value << 1,
Effect::Wither => value >> 1, Effect::Wither => value >> 1,
Effect::Scatter => value >> 1,
Effect::Hatred => value + match meta { Effect::Hatred => value + match meta {
Some(EffectMeta::AddedDamage(d)) => d, Some(EffectMeta::AddedDamage(d)) => d,
_ => panic!("hatred meta not damage"), _ => panic!("hatred meta not damage"),
@ -610,6 +621,7 @@ impl Skill {
Skill::CorruptionTick => 80, Skill::CorruptionTick => 80,
Skill::DecayTick => 25, Skill::DecayTick => 25,
Skill::Riposte => 70, Skill::Riposte => 70,
Skill::Scatter => 140,
_ => 100, _ => 100,
} }
@ -1255,11 +1267,43 @@ fn siphon_tick(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, s
} }
fn scatter(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { fn scatter(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions {
let scatter = CrypEffect::new(Effect::Scatter, skill.duration()); let effect = CrypEffect::new(Effect::Scatter, skill.duration())
results.push(Resolution::new(source, target).event(target.add_effect(skill, scatter))); .set_meta(EffectMeta::ScatterTarget(target.id));
let blue_amount = source.blue_damage().pct(skill.multiplier());
results.push(Resolution::new(source, target).event(target.recharge(skill, 0, blue_amount)));
results.push(Resolution::new(source, target).event(source.add_effect(skill, effect)));
return results; return results;
} }
fn scatter_hit(source: &Cryp, target: &Cryp, mut results: Resolutions, game: &mut Game, event: Event) -> Resolutions {
match event {
Event::Damage { amount, skill, mitigation: _, colour } => {
let scatter = target.effects.iter().find(|e| e.effect == Effect::Scatter).unwrap();
if let Some(EffectMeta::ScatterTarget(scatter_target_id)) = scatter.meta {
let mut scatter_target = game.cryp_by_id(scatter_target_id).unwrap();
let res = match colour {
Category::RedDamage => scatter_target.deal_red_damage(skill, amount),
Category::BlueDamage => scatter_target.deal_blue_damage(skill, amount),
Category::GreenDamage => scatter_target.deal_green_damage(skill, amount),
_ => panic!("{:?} unknown damage type", colour),
};
results.push(Resolution::new(target, scatter_target).event(Event::Skill { skill: Skill::Scatter }));
res.into_iter().for_each(|e| results.push(Resolution::new(&source, &scatter_target).event(e)));
} else {
panic!("not a scatter target {:?}", scatter);
}
return results;
},
_ => panic!("{:?} scatter hit not damage event", event),
}
}
fn silence(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions { fn silence(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill) -> Resolutions {
let silence = CrypEffect::new(Effect::Silence, skill.duration()); let silence = CrypEffect::new(Effect::Silence, skill.duration());
results.push(Resolution::new(source, target).event(target.add_effect(skill, silence))); results.push(Resolution::new(source, target).event(target.add_effect(skill, silence)));
@ -1513,47 +1557,6 @@ mod tests {
}; };
} }
#[test]
fn corrupt_test() {
let mut x = Cryp::new()
.named(&"muji".to_string());
let mut y = Cryp::new()
.named(&"camel".to_string());
corrupt(&mut y.clone(), &mut y, vec![], Skill::Corrupt);
assert!(y.affected(Effect::Corrupt));
resolve(Skill::Attack, &mut x, &mut y, vec![]);
assert!(x.affected(Effect::Corruption));
}
#[test]
fn hatred_test() {
let mut x = Cryp::new()
.named(&"muji".to_string());
let mut y = Cryp::new()
.named(&"camel".to_string());
hostility(&mut y.clone(), &mut y, vec![], Skill::Hostility);
assert!(y.affected(Effect::Hostility));
resolve(Skill::TestAttack, &mut x, &mut y, vec![]);
assert!(y.affected(Effect::Hatred));
let mut results = resolve(Skill::TestAttack, &mut y, &mut x, vec![]);
let Resolution { source: _, target: _, event } = results.remove(0);
match event {
Event::Damage { amount, skill: _, mitigation: _, colour: _} => assert_eq!(amount, 512),
_ => panic!("not damage hatred"),
};
}
#[test] #[test]
fn triage_test() { fn triage_test() {
let mut x = Cryp::new() let mut x = Cryp::new()
@ -1639,93 +1642,3 @@ mod tests {
assert!(!x.effects.iter().any(|e| e.effect == Effect::Decay)); assert!(!x.effects.iter().any(|e| e.effect == Effect::Decay));
} }
} }
// pub enum Skill {
// Attack,
// // -----------------
// // Nature
// // -----------------
// Block, // reduce damage
// Parry, // avoid all damage
// Snare,
// Paralyse,
// Strangle, // physical dot and disable
// Strike,
// Stun,
// // Evade, // actively evade
// // -----------------
// // Technology
// // -----------------
// Replicate,
// Swarm,
// Orbit,
// Repair,
// Scan, // track?
// // -----------------
// // Nonviolence
// // -----------------
// Heal,
// Triage, // hot
// TriageTick,
// Throw, // no damage stun, adds vulnerable
// Charm,
// Calm,
// Rez,
// // Sleep,
// // Nightmare,
// // -------------------
// // Destruction
// // -------------------
// Blast,
// Amplify,
// Decay, // dot
// DecayTick, // dot
// Siphon,
// SiphonTick,
// Curse,
// Corrupt, // aoe dot
// Ruin, // aoe
// // -----------------
// // Purity
// // -----------------
// Empower,
// Slay,
// Shield,
// Silence,
// Inquiry,
// Purify,
// Purge,
// // Precision,
// // -----------------
// // Chaos
// // -----------------
// Banish,
// Hex,
// Fear,
// Taunt,
// Pause, // speed slow
// Haste,
// Slow,
// // used by tests, no cd, no damage
// TestTouch,
// TestStun,
// TestBlock,
// TestParry,
// TestSiphon,
// }

View File

@ -359,9 +359,9 @@ fn get_combos() -> Vec<Combo> {
Combo { units: vec![Var::Buff, Var::Green, Var::Blue], var: Var::Impurity }, // To be added Combo { units: vec![Var::Buff, Var::Green, Var::Blue], var: Var::Impurity }, // To be added
Combo { units: vec![Var::Buff, Var::Red, Var::Blue], var: Var::Amplify }, // Red and blue damage buff Combo { units: vec![Var::Buff, Var::Red, Var::Blue], var: Var::Amplify }, // Red and blue damage buff
Combo { units: vec![Var::Debuff, Var::Red, Var::Red], var: Var::Snare }, Combo { units: vec![Var::Debuff, Var::Red, Var::Red], var: Var::Snare },
Combo { units: vec![Var::Debuff, Var::Green, Var::Green], var: Var::Purge }, // make it disable green Combo { units: vec![Var::Debuff, Var::Green, Var::Green], var: Var::Purge }, // make it disable green
Combo { units: vec![Var::Debuff, Var::Blue, Var::Blue], var: Var::Silence }, Combo { units: vec![Var::Debuff, Var::Blue, Var::Blue], var: Var::Silence },
Combo { units: vec![Var::Debuff, Var::Red, Var::Green], var: Var::Curse }, // To be reworked Combo { units: vec![Var::Debuff, Var::Red, Var::Green], var: Var::Curse }, // To be reworked
Combo { units: vec![Var::Debuff, Var::Green, Var::Blue], var: Var::Decay }, Combo { units: vec![Var::Debuff, Var::Green, Var::Blue], var: Var::Decay },
Combo { units: vec![Var::Debuff, Var::Red, Var::Blue], var: Var::Invert }, Combo { units: vec![Var::Debuff, Var::Red, Var::Blue], var: Var::Invert },
@ -677,8 +677,8 @@ mod tests {
assert_eq!(count.red, 2); assert_eq!(count.red, 2);
} }
#[test] // #[test]
fn vbox_info_test() { // fn vbox_info_test() {
println!("{:#?}", vbox_info()); // println!("{:#?}", vbox_info());
} // }
} }