tick effect matching reimpl

This commit is contained in:
ntr 2020-01-23 16:23:25 +10:00
parent 153b3a1e9c
commit 6e3675364e
6 changed files with 85 additions and 93 deletions

View File

@ -5,6 +5,9 @@ _ntr_
* can't reset password without knowing password =\
* hard reload client on version change
electrify on death / stun
siphon stack removed check
* audio
* animation effects
* vbox combine / buy / equip etc

View File

@ -27,6 +27,3 @@ ssh -q "$TARGET" ls -lah "$CLIENT_DIST_DIR"
echo "restarting mnml service"
ssh -q -t "$TARGET" sudo service mnml restart && sleep 1 && systemctl --no-pager status mnml
echo "restarting nginx service"
ssh -q -t "$TARGET" sudo service nginx restart && sleep 1 && systemctl --no-pager status nginx

View File

@ -65,7 +65,7 @@ impl ConstructSkill {
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
pub enum EffectMeta {
CastOnHit(Skill), // maybe needs source/target
CastTick { source: Uuid, target: Uuid, skill: Skill, speed: usize, amount: usize },
CastTick { source: Uuid, target: Uuid, skill: Skill, speed: usize, amount: usize, id: Uuid },
AddedDamage(usize),
Multiplier(usize),
}
@ -100,12 +100,19 @@ impl ConstructEffect {
pub fn get_skill(&self) -> Option<Skill> {
match self.meta {
Some(EffectMeta::CastTick { source: _, target: _, skill, speed: _, amount: _ }) => Some(skill),
Some(EffectMeta::CastTick { source: _, target: _, skill, speed: _, amount: _, id: _ }) => Some(skill),
Some(EffectMeta::CastOnHit(s)) => Some(s),
_ => None,
}
}
pub fn get_tick_id(&self) -> Option<Uuid> {
match self.meta {
Some(EffectMeta::CastTick { source: _, target: _, skill: _, speed: _, amount: _, id }) => Some(id),
_ => None,
}
}
}
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
@ -561,7 +568,7 @@ impl Construct {
fn tick_damage(&self, effect: Effect) -> usize {
match self.effects.iter().find_map(|ce| match ce.effect == effect {
true => match ce.meta {
Some(EffectMeta::CastTick { source: _, target: _, skill: _, speed: _, amount }) => Some(amount),
Some(EffectMeta::CastTick { source: _, target: _, skill: _, speed: _, amount, id: _ }) => Some(amount),
_ => None,
},
false => None,

View File

@ -428,8 +428,12 @@ impl Game {
.cloned()
.filter_map(|e| e.meta)
.filter_map(move |m| match m {
EffectMeta::CastTick { source, target, skill, speed, amount: _ } =>
Some(Cast::new(source, c.account, target, skill).set_speed(speed)),
EffectMeta::CastTick { source, target, skill, speed, amount: _, id } =>
Some(
Cast::new(source, c.account, target, skill)
.set_speed(speed)
.set_id(id)
),
_ => None,
})
)
@ -506,6 +510,20 @@ impl Game {
fn resolve(&mut self, cast: Cast) -> &mut Game {
if self.finished() { return self }
// match tick skills with the effect on the target
// if no match is found the effect must have been removed during this turn
// and the skill should no longer resolve
if cast.skill.is_tick() {
let effect_match = self.construct(cast.target).effects.iter()
.filter_map(|ce| ce.get_tick_id())
.find(|id| cast.id == *id)
.is_some();
if !effect_match {
return self;
}
}
// 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 });
@ -1054,6 +1072,7 @@ mod tests {
.learn(Skill::Block)
.learn(Skill::Counter)
.learn(Skill::Siphon)
.learn(Skill::Purify)
.learn(Skill::Amplify)
.learn(Skill::Stun)
.learn(Skill::Ruin)
@ -1069,6 +1088,7 @@ mod tests {
.learn(Skill::Block)
.learn(Skill::Counter)
.learn(Skill::Siphon)
.learn(Skill::Purify)
.learn(Skill::Amplify)
.learn(Skill::Stun)
.learn(Skill::Block);
@ -1874,85 +1894,6 @@ mod tests {
return;
}
// #[test]
// fn tick_removal_test() {
// let mut game = create_test_game();
// let x_player = game.players[0].clone();
// let y_player = game.players[1].clone();
// let x_construct = x_player.constructs[0].clone();
// let y_construct = y_player.constructs[0].clone();
// // make the purify construct super fast so it beats out decay
// game.construct_by_id(y_construct.id).unwrap().speed.force(10000000);
// game.construct_by_id(x_construct.id).unwrap().learn_mut(Skill::Decay);
// while game.construct_by_id(x_construct.id).unwrap().skill_on_cd(Skill::Decay).is_some() {
// game.construct_by_id(x_construct.id).unwrap().reduce_cooldowns();
// }
// game.construct_by_id(x_construct.id).unwrap().learn_mut(Skill::Siphon);
// while game.construct_by_id(x_construct.id).unwrap().skill_on_cd(Skill::Siphon).is_some() {
// game.construct_by_id(x_construct.id).unwrap().reduce_cooldowns();
// }
// game.construct_by_id(y_construct.id).unwrap().learn_mut(Skill::Purify);
// while game.construct_by_id(y_construct.id).unwrap().skill_on_cd(Skill::Purify).is_some() {
// game.construct_by_id(y_construct.id).unwrap().reduce_cooldowns();
// }
// // apply buff
// game.add_skill(x_player.id, x_construct.id, y_construct.id, Skill::Decay).unwrap();
// game.player_ready(x_player.id).unwrap();
// game.player_ready(y_player.id).unwrap();
// game = game.resolve_phase_start();
// assert!(game.construct_by_id(y_construct.id).unwrap().affected(Effect::Decay));
// let Resolution { source: _, target: _, Resolution, stages: _ } = game.Resolutions.last().unwrap().pop().unwrap();
// match Resolution {
// Resolution::Damage { amount: _, skill, mitigation: _, colour: _ } => assert_eq!(skill, Skill::DecayTick),
// _ => panic!("not decay"),
// };
// game.Resolutions.clear();
// // remove
// game.add_skill(y_player.id, y_construct.id, y_construct.id, Skill::Purify).unwrap();
// game.player_ready(x_player.id).unwrap();
// game.player_ready(y_player.id).unwrap();
// game = game.resolve_phase_start();
// while let Some(Resolution { source: _, target: _, Resolution, stages: _ }) = game.Resolutions.last().unwrap().pop() {
// match Resolution {
// Resolution::Damage { amount: _, skill: _, mitigation: _, colour: _ } =>
// panic!("{:?} damage Resolution", Resolution),
// _ => (),
// }
// };
// game.add_skill(y_player.id, x_construct.id, y_construct.id, Skill::Siphon).unwrap();
// game.player_ready(x_player.id).unwrap();
// game.player_ready(y_player.id).unwrap();
// game = game.resolve_phase_start();
// game.Resolutions.clear();
// game.add_skill(y_player.id, y_construct.id, y_construct.id, Skill::Purify).unwrap();
// game.player_ready(x_player.id).unwrap();
// game.player_ready(y_player.id).unwrap();
// game = game.resolve_phase_start();
// while let Some(Resolution { source: _, target: _, Resolution, stages: _ }) = game.Resolutions.last().unwrap().pop() {
// match Resolution {
// Resolution::Damage { amount: _, skill: _, mitigation: _, colour: _ } =>
// panic!("{:#?} {:#?} damage Resolution", game.Resolutions, Resolution),
// _ => (),
// }
// };
// }
#[test]
fn upkeep_test() {
let mut game = create_2v2_test_game();
@ -2524,4 +2465,38 @@ mod tests {
assert_eq!(siphon_tick_dmg, siphon_dmg);
assert_eq!(siphon_tick_speed, siphon_speed);
}
#[test]
fn tick_removal_test() {
let mut game = create_test_game();
let player_id = game.players[0].id;
let opponent_id = game.players[1].id;
let source = game.players[0].constructs[0].id;
let target = game.players[1].constructs[0].id;
game.add_skill(player_id, source, target, Skill::Siphon).unwrap();
game.player_ready(player_id).unwrap();
game.player_ready(opponent_id).unwrap();
game = game.resolve_phase_start();
game.add_skill(player_id, source, target, Skill::Purify).unwrap();
game.player_ready(player_id).unwrap();
game.player_ready(opponent_id).unwrap();
game = game.resolve_phase_start();
// println!("{:#?}", game.resolutions);
let last = game.resolutions.len() - 1;
let resolutions = &game.resolutions[last];
// There should be no damage events on the target
assert!(resolutions.iter().any(|r| match r.event {
Event::Damage { construct: _, colour: _, amount: _, mitigation: _, display: _ } => true,
_ => false,
}) == false);
}
}

View File

@ -41,6 +41,15 @@ impl Cast {
}
}
// used for ticks to match
// a cast with an effect
pub fn set_id(self, id: Uuid) -> Cast {
Cast {
id,
..self
}
}
pub fn resolve(self, game: &mut Game) {
match self.skill {
Skill::Attack => attack(self, game, Attack::Base),
@ -1134,7 +1143,7 @@ fn siphon(cast: Cast, game: &mut Game, values: Siphon) {
Action::Effect {
construct: cast.target,
effect: ConstructEffect { effect: Effect::Siphon, duration: values.duration(), meta:
Some(EffectMeta::CastTick { source: cast.source, target: cast.target, skill: Skill::SiphonTick, speed: cast.speed, amount }) },
Some(EffectMeta::CastTick { id: Uuid::new_v4(), source: cast.source, target: cast.target, skill: Skill::SiphonTick, speed: cast.speed, amount }) },
}
);
@ -1729,7 +1738,7 @@ fn decay(cast: Cast, game: &mut Game, values: Decay) {
Action::Effect {
construct: cast.target,
effect: ConstructEffect { effect: Effect::Decay, duration: values.decay_duration(), meta:
Some(EffectMeta::CastTick { source: cast.source, target: cast.target, skill: Skill::DecayTick, speed: cast.speed, amount }) },
Some(EffectMeta::CastTick { id: Uuid::new_v4(), source: cast.source, target: cast.target, skill: Skill::DecayTick, speed: cast.speed, amount }) },
}
);
@ -1837,10 +1846,10 @@ fn electrocute(cast: Cast, game: &mut Game, values: Electrocute) {
Action::Effect {
construct: cast.target,
effect: ConstructEffect { effect: Effect::Electrocute, duration: values.duration(), meta:
Some(EffectMeta::CastTick { source: cast.source, target: cast.target, skill: Skill::ElectrocuteTick, speed: cast.speed, amount }) },
Some(EffectMeta::CastTick { id: Uuid::new_v4(), source: cast.source, target: cast.target, skill: Skill::ElectrocuteTick, speed: cast.speed, amount }) },
},
);
if !game.affected(cast.target, Effect::Electrocuted) {
game.action(cast,
Action::Damage {
@ -2362,7 +2371,7 @@ fn triage(cast: Cast, game: &mut Game, values: Triage) {
Action::Effect {
construct: cast.target,
effect: ConstructEffect { effect: Effect::Triage, duration: values.duration(), meta:
Some(EffectMeta::CastTick { source: cast.source, target: cast.target, skill: Skill::TriageTick, speed: cast.speed, amount }) },
Some(EffectMeta::CastTick { id: Uuid::new_v4(), source: cast.source, target: cast.target, skill: Skill::TriageTick, speed: cast.speed, amount }) },
}
);

View File

@ -109,6 +109,7 @@ pub fn start() {
#[cfg(unix)]
setup_logger().unwrap();
dotenv::from_path(Path::new("/etc/mnml/gs.conf")).ok();
info!("starting server");
let pool = pg::create_pool();
let http_pool = pool.clone();