diff --git a/server/src/game.rs b/server/src/game.rs index 3ded010d..00c3c8ba 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -243,7 +243,7 @@ impl Game { self } - fn add_skill(&mut self, player_id: Uuid, source_cryp_id: Uuid, target_cryp_id: Option, skill: Skill) -> Result { + fn add_skill(&mut self, player_id: Uuid, source_cryp_id: Uuid, target_cryp_id: Option, skill: Skill) -> Result<&mut Game, Error> { // check player in game self.player_by_id(player_id)?; @@ -304,10 +304,9 @@ impl Game { } let skill = Cast::new(source_cryp_id, player_id, final_target_id, skill); - let skill_id = skill.id; self.stack.push(skill); - return Ok(skill_id); + return Ok(self); } fn player_ready(&mut self, player_id: Uuid) -> Result<&mut Game, Error> { @@ -1053,7 +1052,7 @@ mod tests { let x_cryp = x_player.cryps[0].clone(); let y_cryp = y_player.cryps[0].clone(); - let _x_stun_id = game.add_skill(x_player.id, x_cryp.id, Some(y_cryp.id), Skill::TestStun).unwrap(); + game.add_skill(x_player.id, x_cryp.id, Some(y_cryp.id), Skill::TestStun).unwrap(); game.add_skill(y_player.id, y_cryp.id, Some(x_cryp.id), Skill::TestTouch).unwrap(); game.player_ready(x_player.id).unwrap(); @@ -1086,7 +1085,7 @@ mod tests { // remove all mitigation game.player_by_id(x_player.id).unwrap().cryp_by_id(x_cryp.id).unwrap().red_life.force(0); - let _x_stun_id = game.add_skill(x_player.id, x_cryp.id, Some(y_cryp.id), Skill::TestStun).unwrap(); + game.add_skill(x_player.id, x_cryp.id, Some(y_cryp.id), Skill::TestStun).unwrap(); game.add_skill(y_player.id, y_cryp.id, Some(x_cryp.id), Skill::Attack).unwrap(); game.player_ready(x_player.id).unwrap(); @@ -1130,8 +1129,8 @@ mod tests { // second round // now we block and it should go back on cd - let _x_block_id = game.add_skill(x_player.id, x_cryp.id, Some(y_cryp.id), Skill::Stun).unwrap(); - let _y_touch_id = game.add_skill(y_player.id, y_cryp.id, Some(x_cryp.id), Skill::TestTouch).unwrap(); + game.add_skill(x_player.id, x_cryp.id, Some(y_cryp.id), Skill::Stun).unwrap(); + 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(); @@ -1152,7 +1151,7 @@ mod tests { let x_cryp = x_player.cryps[0].clone(); let y_cryp = y_player.cryps[0].clone(); - let _x_block_id = game.add_skill(x_player.id, x_cryp.id, None, Skill::TestParry).unwrap(); + game.add_skill(x_player.id, x_cryp.id, None, Skill::TestParry).unwrap(); game.add_skill(y_player.id, y_cryp.id, Some(x_cryp.id), Skill::TestStun).unwrap(); game.player_ready(x_player.id).unwrap(); @@ -1416,6 +1415,57 @@ 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_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::Decay); + + while game.cryp_by_id(x_cryp.id).unwrap().skill_on_cd(Skill::Decay).is_some() { + game.cryp_by_id(x_cryp.id).unwrap().reduce_cooldowns(); + } + + game.cryp_by_id(y_cryp.id).unwrap().learn_mut(Skill::Purify); + + while game.cryp_by_id(y_cryp.id).unwrap().skill_on_cd(Skill::Purify).is_some() { + game.cryp_by_id(y_cryp.id).unwrap().reduce_cooldowns(); + } + + // apply buff + game.add_skill(x_player.id, x_cryp.id, Some(y_cryp.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.cryp_by_id(y_cryp.id).unwrap().affected(Effect::Decay)); + + let Resolution { source: _, target: _, event } = game.resolved.pop().unwrap(); + match event { + Event::Damage { amount: _, skill, mitigation: _, colour: _ } => assert_eq!(skill, Skill::DecayTick), + _ => panic!("not decay"), + }; + + game.resolved.clear(); + + // remove + game.add_skill(y_player.id, y_cryp.id, Some(y_cryp.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: _, event }) = game.resolved.pop() { + match event { + Event::Damage { amount: _, skill, mitigation: _, colour: _ } => panic!("{:?} damage event", event), + _ => (), + } + }; + } + #[test] fn upkeep_test() { let mut game = create_2v2_test_game(); diff --git a/server/src/skill.rs b/server/src/skill.rs index 54d2c69d..793244b1 100644 --- a/server/src/skill.rs +++ b/server/src/skill.rs @@ -16,16 +16,27 @@ pub fn resolution_steps(cast: &Cast, game: &mut Game) -> Resolutions { } pub fn pre_resolve(cast: &Cast, game: &mut Game, mut resolutions: Resolutions) -> Resolutions { - let skill = cast.skill; let source = game.cryp_by_id(cast.source_cryp_id).unwrap().clone(); let targets = game.get_targets(cast.skill, &source, cast.target_cryp_id); for target_id in targets { + // 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.cryp_by_id(cast.source_cryp_id).unwrap().clone(); let mut target = game.cryp_by_id(target_id).unwrap().clone(); + + // bail out on ticks that have been removed + if cast.is_tick && target.effects.iter().find(|ce| match ce.tick { + Some(t) => t.id == cast.id, + None => false, + }).is_some() { + continue; + } + resolutions = resolve(cast.skill, &mut source, &mut target, resolutions); - // save the clones + // save the changes to the game game.update_cryp(&mut source); game.update_cryp(&mut target); @@ -200,7 +211,7 @@ fn post_resolve(_skill: Skill, game: &mut Game, mut resolutions: Resolutions) -> -#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub struct Cast { pub id: Uuid, pub source_player_id: Uuid, @@ -208,7 +219,7 @@ pub struct Cast { pub target_cryp_id: Uuid, pub skill: Skill, pub speed: u64, - pub resolutions: Resolutions, + pub is_tick: bool, } impl Cast { @@ -220,12 +231,20 @@ impl Cast { target_cryp_id, skill, speed: 0, - resolutions: vec![], + is_tick: false, }; } pub fn new_tick(source: &mut Cryp, target: &mut Cryp, skill: Skill) -> Cast { - Cast::new(source.id, source.account, target.id, skill) + Cast { + id: Uuid::new_v4(), + source_cryp_id: source.id, + source_player_id: source.account, + target_cryp_id: target.id, + skill, + speed: 0, + is_tick: true + } } pub fn used_cooldown(&self) -> bool { @@ -360,11 +379,6 @@ impl Effect { Category::Red => true, _ => false, }, - Effect::Scatter => match skill.category() { - Category::Blue => true, - Category::Red => false, - _ => false, - }, Effect::Banish => true, Effect::Injured => match skill.category() { Category::Green => true,