diff --git a/core/fixme.md b/core/fixme.md index c79cbc99..32934560 100644 --- a/core/fixme.md +++ b/core/fixme.md @@ -2,4 +2,7 @@ remove immunity aoe event +cooldown events leak skills +hit event + reqs moving eventconstruct into specific events cooldown checking -> go through round and find all casters \ No newline at end of file diff --git a/core/src/construct.rs b/core/src/construct.rs index 475e70df..81aa785b 100644 --- a/core/src/construct.rs +++ b/core/src/construct.rs @@ -4,7 +4,7 @@ use rand::prelude::*; use failure::Error; use failure::err_msg; -use skill::{Skill, Cast, Immunity, Disable, Event, EventVariant}; +use skill::{Skill, Cast, Disable, Event, EventVariant}; use game::{Colour}; use effect::{Cooldown, Effect}; use spec::{Spec}; @@ -341,24 +341,6 @@ impl Construct { self } - pub fn immune(&self, skill: Skill) -> Option { - // also checked in resolve stage so shouldn't happen really - if self.is_ko() { - return Some(vec![Effect::Ko]); - } - - let immunities = self.effects.iter() - .filter(|e| e.effect.immune(skill)) - .map(|e| e.effect) - .collect::>(); - - if immunities.len() > 0 { - return Some(immunities); - } - - None - } - pub fn disabled(&self, skill: Skill) -> Option { if self.is_ko() && !skill.ko_castable() { return Some(vec![Effect::Ko]); @@ -433,6 +415,29 @@ impl Construct { self } + pub fn increase_cooldowns(&mut self, turns: usize) -> Vec { + let mut events = vec![]; + + let state = self.clone(); + + for skill in self.skills.iter_mut() { + if skill.skill.base_cd().is_some() { // if has a cooldown + match skill.cd { + Some(cd) => { + skill.cd = Some(cd.saturating_add(turns)); + events.push(Event::new(EventVariant::CooldownIncrease { turns, skill: skill.skill }, &state)) + }, + None => { + skill.cd = Some(turns); + events.push(Event::new(EventVariant::CooldownIncrease { turns, skill: skill.skill }, &state)) + }, + } + } + } + + return events; + } + pub fn reduce_cooldowns(&mut self) -> &mut Construct { for skill in self.skills.iter_mut() { // if used cooldown @@ -543,16 +548,10 @@ impl Construct { } } - pub fn recharge(&mut self, skill: Skill, red_amount: usize, blue_amount: usize) -> Vec { + pub fn recharge(&mut self, red_amount: usize, blue_amount: usize) -> Vec { let mut events = vec![]; - // Should red type immunity block recharge??? - if let Some(immunity) = self.immune(skill) { - if !self.is_ko() { - events.push(Event::new(EventVariant::Immunity { skill, immunity }, self)); - } - return events; - } + if self.is_ko() { return events; } match self.affected(Effect::Invert) { false => { @@ -568,7 +567,7 @@ impl Construct { let blue = new_blue_life - current_blue_life; if red != 0 || blue != 0 { - events.push(Event::new(EventVariant::Recharge { red, blue, skill }, self)); + events.push(Event::new(EventVariant::Recharge { red, blue }, self)); } }, true => { @@ -595,7 +594,6 @@ impl Construct { let red_damage_amount = red_current_green_life - self.green_life(); events.push(Event::new(EventVariant::Damage { - skill, amount: red_damage_amount, mitigation: red_mitigation, colour: Colour::Red @@ -624,7 +622,6 @@ impl Construct { let blue_damage_amount = blue_current_green_life - self.green_life(); events.push(Event::new(EventVariant::Damage { - skill, amount: blue_damage_amount, mitigation: blue_mitigation, colour: Colour::Blue @@ -635,14 +632,9 @@ impl Construct { return events; } - pub fn deal_green_damage(&mut self, skill: Skill, amount: usize) -> Vec { + pub fn deal_green_damage(&mut self, amount: usize) -> Vec { let mut events = vec![]; - if let Some(immunity) = self.immune(skill) { - if !self.is_ko() { - events.push(Event::new(EventVariant::Immunity { skill, immunity }, self)); - } - return events; - } + if self.is_ko() { return events; } let mods = self.effects.iter() .filter(|e| e.effect.modifications().contains(&Stat::GreenDamageTaken)) @@ -662,7 +654,6 @@ impl Construct { let overhealing = modified_power - healing; events.push(Event::new(EventVariant::Healing { - skill, amount: healing, overhealing, }, self)); @@ -676,7 +667,6 @@ impl Construct { let delta = current_green_life - self.green_life(); events.push(Event::new(EventVariant::Damage { - skill, amount: delta, mitigation: 0, colour: Colour::Green, @@ -687,15 +677,10 @@ impl Construct { return events; } - pub fn deal_red_damage(&mut self, skill: Skill, amount: usize) -> Vec { + pub fn deal_red_damage(&mut self, amount: usize) -> Vec { let mut events = vec![]; - if let Some(immunity) = self.immune(skill) { - if !self.is_ko() { - events.push(Event::new(EventVariant::Immunity { skill, immunity }, self)); - } - return events; - } + if self.is_ko() { return events; } let mods = self.effects.iter() .filter(|e| e.effect.modifications().contains(&Stat::RedDamageTaken)) @@ -724,7 +709,6 @@ impl Construct { events.push(Event::new( EventVariant::Damage { - skill, amount: delta, mitigation, colour: Colour::Red, @@ -748,7 +732,6 @@ impl Construct { if healing > 0 { events.push(Event::new( EventVariant::Healing{ - skill, amount: healing, overhealing: overhealing - recharge, }, self @@ -756,7 +739,7 @@ impl Construct { } if recharge > 0 { - events.push(Event::new(EventVariant::Recharge { red: recharge, blue: 0, skill }, self)); + events.push(Event::new(EventVariant::Recharge { red: recharge, blue: 0 }, self)); } } }; @@ -764,15 +747,10 @@ impl Construct { return events; } - pub fn deal_blue_damage(&mut self, skill: Skill, amount: usize) -> Vec { + pub fn deal_blue_damage(&mut self, amount: usize) -> Vec { let mut events = vec![]; - if let Some(immunity) = self.immune(skill) { - if !self.is_ko() { - events.push(Event::new(EventVariant::Immunity { skill, immunity }, self)); - } - return events; - } + if self.is_ko() { return events; } let mods = self.effects.iter() .filter(|e| e.effect.modifications().contains(&Stat::BlueDamageTaken)) @@ -796,7 +774,6 @@ impl Construct { let delta = current_green_life - self.green_life(); events.push(Event::new(EventVariant::Damage { - skill, amount: delta, mitigation, colour: Colour::Blue, @@ -818,7 +795,6 @@ impl Construct { if healing > 0 { events.push( Event::new(EventVariant::Healing { - skill, amount: healing, overhealing, }, @@ -827,7 +803,7 @@ impl Construct { } if recharge > 0 { - events.push(Event::new(EventVariant::Recharge { red: 0, blue: recharge, skill }, self)); + events.push(Event::new(EventVariant::Recharge { red: 0, blue: recharge }, self)); } } }; @@ -835,16 +811,10 @@ impl Construct { return events; } - pub fn add_effect(&mut self, skill: Skill, effect: ConstructEffect) -> Vec { - if let Some(immunity) = self.immune(skill) { - return vec![ - Event::new( - EventVariant::Immunity{ - skill, - immunity, - }, self) - ]; - } + pub fn add_effect(&mut self, effect: ConstructEffect) -> Vec { + if self.is_ko() { return vec![] } + + if self.affected(Effect::Banish) { panic!("banish immunity not fixt yet") } if let Some(p) = self.effects.iter().position(|ce| ce.effect == effect.effect) { // duplicate effect @@ -863,7 +833,6 @@ impl Construct { effect: effect.effect, duration: effect.duration, construct_effects: self.effects.clone(), - skill, }, self ); diff --git a/core/src/effect.rs b/core/src/effect.rs index b1cb1ca4..f8aebe89 100644 --- a/core/src/effect.rs +++ b/core/src/effect.rs @@ -3,7 +3,7 @@ use game::{Colour}; use skill::{Skill}; use util::{IntPct}; -pub type Cooldown = Option; +pub type Cooldown = Option; #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub enum Effect { diff --git a/core/src/game.rs b/core/src/game.rs index e5c43517..a5c094f3 100644 --- a/core/src/game.rs +++ b/core/src/game.rs @@ -11,7 +11,7 @@ use failure::err_msg; use construct::{Construct, ConstructEffect, Stat}; use skill::{Skill, Cast, Event, resolve}; -use effect::{Effect}; + use player::{Player}; use instance::{TimeControl}; @@ -40,9 +40,9 @@ pub enum Colour { #[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] pub enum Action { - Damage { construct: Uuid, skill: Skill, values: Vec, colour: Colour }, - Effect { construct: Uuid, skill: Skill, effect: ConstructEffect }, - IncreaseCooldowns { construct: Uuid, skill: Skill, turns: usize }, + Damage { construct: Uuid, values: Vec, colour: Colour }, + Effect { construct: Uuid, effect: ConstructEffect }, + IncreaseCooldowns { construct: Uuid, turns: usize }, // Recharge { skill: Skill, red: usize, blue: usize }, } @@ -498,31 +498,31 @@ impl Game { pub fn actions(&mut self, actions: Vec) -> &mut Game { for action in actions { match action { - Action::Damage { construct, skill, values, colour } => self.damage(construct, skill, values, colour), - Action::Effect { construct, skill, effect } => self.effect(construct, skill, effect), - Action::IncreaseCooldowns { construct, skill, turns } => self.increase_cooldowns(construct, skill, turns), + Action::Damage { construct, values, colour } => self.damage(construct, values, colour), + Action::Effect { construct, effect } => self.effect(construct, effect), + Action::IncreaseCooldowns { construct, turns } => self.increase_cooldowns(construct, turns), }; } self } - fn damage(&mut self, construct: Uuid, skill: Skill, values: Vec, colour: Colour) -> &mut Game { + fn damage(&mut self, construct: Uuid, values: Vec, colour: Colour) -> &mut Game { let resolutions = match colour { - _ => self.construct_by_id(construct).unwrap().deal_red_damage(skill, 128) // fixme unwrap + _ => self.construct_by_id(construct).unwrap().deal_red_damage(128) // fixme unwrap }; self.resolution_add(resolutions); self } - fn effect(&mut self, construct: Uuid, skill: Skill, effect: ConstructEffect) -> &mut Game { - let resolutions = self.construct_by_id(construct).unwrap().add_effect(skill, effect); + fn effect(&mut self, construct: Uuid, effect: ConstructEffect) -> &mut Game { + let resolutions = self.construct_by_id(construct).unwrap().add_effect(effect); self.resolution_add(resolutions); self } - fn increase_cooldowns(&mut self, construct: Uuid, skill: Skill, turns: usize) -> &mut Game { + fn increase_cooldowns(&mut self, construct: Uuid, turns: usize) -> &mut Game { let resolutions = self.construct_by_id(construct).unwrap().increase_cooldowns(turns); self.resolution_add(resolutions); self @@ -1339,5 +1339,19 @@ mod tests { let target = game.players[1].constructs[0].id; game.add_skill(player_id, source, target, Skill::Attack).unwrap(); game = game.resolve_phase_start(); + + println!("{:?}", game); + } + + #[test] + fn bash_test() { + let mut game = create_2v2_test_game(); + let player_id = game.players[0].id; + let source = game.players[0].constructs[0].id; + let target = game.players[1].constructs[0].id; + + game.actions(Cast::new(source, player_id, target, Skill::Bash).actions()); + + println!("{:?}", game); } } diff --git a/core/src/item.rs b/core/src/item.rs index 12121ff3..1390e6fe 100644 --- a/core/src/item.rs +++ b/core/src/item.rs @@ -739,7 +739,7 @@ impl Item { Item::Sustain| Item::SustainPlus | Item::SustainPlusPlus => format!( - "Construct cannot be KO'd while active and provides immunity to disables. Lasts {:?}T. + "Construct cannot be KO'd while active. Lasts {:?}T. Recharges target RedLife based on {:?}% RedPower.", self.into_skill().unwrap().effect()[0].get_duration(), self.into_skill().unwrap().multiplier()), diff --git a/core/src/skill.rs b/core/src/skill.rs index 375cb31b..ecd0063b 100644 --- a/core/src/skill.rs +++ b/core/src/skill.rs @@ -62,7 +62,9 @@ pub fn resolve(game: &mut Game, cast: Cast) { // } for cast in casts { - game.actions(cast_actions(cast)); + let mut actions = vec![]; // fixme hit event + actions.append(&mut cast.actions()); + game.actions(actions); } } @@ -103,10 +105,123 @@ impl Cast { pub fn used_cooldown(&self) -> bool { return self.skill.base_cd().is_some(); } + + pub fn actions(&self) -> Vec { + match self.skill { + + Skill::Amplify => vec![ + Action::Effect { + construct: self.target, + effect: ConstructEffect { effect: Effect::Amplify, duration: 2, meta: Some(EffectMeta::Multiplier(150)), tick: None }, + }, + ], + Skill::AmplifyPlus => vec![ + Action::Effect { + construct: self.target, + effect: ConstructEffect { effect: Effect::Amplify, duration: 3, meta: Some(EffectMeta::Multiplier(175)), tick: None }, + }, + ], + Skill::AmplifyPlusPlus => vec![ + Action::Effect { + construct: self.target, + effect: ConstructEffect { effect: Effect::Amplify, duration: 4, meta: Some(EffectMeta::Multiplier(200)), tick: None }, + }, + ], + + Skill::Attack => vec![ + Action::Damage { + construct: self.target, + colour: Colour::Red, + values: vec![Value::Stat { construct: self.source, stat: Stat::RedPower, mult: self.skill.multiplier() }], + }, + ], + + + Skill::Banish => vec![ + Action::Effect { + construct: self.target, + effect: ConstructEffect { effect: Effect::Banish, duration: 2, meta: None, tick: None } + } + ], + Skill::BanishPlus => vec![ + Action::Effect { + construct: self.target, + effect: ConstructEffect { effect: Effect::Banish, duration: 2, meta: None, tick: None } + } + ], + Skill::BanishPlusPlus => vec![ + Action::Effect { + construct: self.target, + effect: ConstructEffect { effect: Effect::Banish, duration: 2, meta: None, tick: None } + } + ], + + Skill::Bash => vec![ + Action::Damage { + construct: self.target, + colour: Colour::Red, + values: vec![ + Value::Stat { construct: self.source, stat: Stat::RedPower, mult: self.skill.multiplier() }, + Value::Cooldowns { construct: self.source, mult: 45 }, + ], + }, + Action::Effect { + construct: self.target, + effect: ConstructEffect {effect: Effect::Stun, duration: 2, meta: Some(EffectMeta::Skill(Skill::Bash)), tick: None } + }, + Action::IncreaseCooldowns { + construct: self.target, + turns: 1, + }, + ], + + // fn bash(source: &mut Construct, target: &mut Construct, skill: Skill) { + // skill.effect().into_iter() + // .for_each(|e| (game.event(Event::new(source, target).event(target.add_effect(skill, e))))); + + // if resolutions.iter().any(|r| match r.event { + // Event::Effect { effect, skill: effect_skill, duration: _, construct_effects: _ } + // => effect == Effect::Stun && skill == effect_skill, + // _ => false, + // }) { + // let mut cds = 0; + // for cs in target.skills.iter_mut() { + // if cs.skill.base_cd().is_some() { + // cs.cd = match cs.cd { + // None => Some(1), + // Some(i) => Some(i + 1), + // }; + + // cds += 1; + // } + // } + + // let amount = source.red_power().pct(skill.multiplier().pct(100 + 45usize.saturating_mul(cds))); + // target.deal_red_damage(skill, amount) + // .into_iter() + // .for_each(|e| game.event(Event::new(source, target).event(e).stages(EventStages::PostOnly))); + // } + + // } + + Skill::Strike | + Skill::StrikePlus | + Skill::StrikePlusPlus => vec![ + Action::Damage { + construct: self.target, + colour: Colour::Red, + values: vec![Value::Stat { construct: self.source, stat: Stat::RedPower, mult: self.skill.multiplier() }], + }, + ], + + _ => unimplemented!() + } + +} + } pub type Disable = Vec; -pub type Immunity = Vec; #[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] pub struct Event { @@ -136,17 +251,17 @@ impl Event { #[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] pub enum EventVariant { - Disable { skill: Skill, disable: Disable }, - Immunity { skill: Skill, immunity: Immunity }, - Damage { skill: Skill, amount: usize, mitigation: usize, colour: Colour }, - Healing { skill: Skill, amount: usize, overhealing: usize }, - Recharge { skill: Skill, red: usize, blue: usize }, + Hit { skill: Skill }, + Disable { disable: Disable }, + Damage { amount: usize, mitigation: usize, colour: Colour }, + Healing { amount: usize, overhealing: usize }, + Recharge { red: usize, blue: usize }, Inversion { skill: Skill }, Reflection { skill: Skill }, AoeSkill { skill: Skill }, Skill { skill: Skill }, - Effect { skill: Skill, effect: Effect, duration: u8, construct_effects: Vec }, - Removal { skill: Skill, effect: Option, construct_effects: Vec }, + Effect { effect: Effect, duration: u8, construct_effects: Vec }, + Removal { effect: Option, construct_effects: Vec }, TargetKo { skill: Skill }, // skill not necessary but makes it neater as all events are arrays in js Ko (), @@ -154,34 +269,35 @@ pub enum EventVariant { Incomplete (), // not used Evasion { skill: Skill, evasion_rating: usize }, + + CooldownIncrease { turns: usize, skill: Skill }, + CooldownDecrease { turns: usize, skill: Skill }, } impl EventVariant { fn stages(&self) -> EventStages { match self { - EventVariant::Disable { skill, disable} + EventVariant::Disable { disable: _} => EventStages::PostOnly, - EventVariant::Immunity { skill, immunity} + EventVariant::Damage { amount: _, mitigation: _, colour: _} => EventStages::PostOnly, - EventVariant::Damage { skill, amount, mitigation, colour} + EventVariant::Healing { amount: _, overhealing: _} => EventStages::PostOnly, - EventVariant::Healing { skill, amount, overhealing} + EventVariant::Recharge { red: _, blue: _} => EventStages::PostOnly, - EventVariant::Recharge { skill, red, blue} + EventVariant::Inversion { skill: _ } => EventStages::PostOnly, - EventVariant::Inversion { skill } + EventVariant::Reflection { skill: _ } => EventStages::PostOnly, - EventVariant::Reflection { skill } + EventVariant::AoeSkill { skill: _ } => EventStages::PostOnly, - EventVariant::AoeSkill { skill } + EventVariant::Skill { skill: _ } => EventStages::PostOnly, - EventVariant::Skill { skill } + EventVariant::Effect { effect: _, duration: _, construct_effects: _ } => EventStages::PostOnly, - EventVariant::Effect { skill, effect, duration, construct_effects } + EventVariant::Removal { effect: _, construct_effects: _ } => EventStages::PostOnly, - EventVariant::Removal { skill, effect, construct_effects } - => EventStages::PostOnly, - EventVariant::TargetKo { skill } + EventVariant::TargetKo { skill: _ } => EventStages::PostOnly, EventVariant::Ko () => EventStages::PostOnly, @@ -189,7 +305,15 @@ impl EventVariant { => EventStages::PostOnly, EventVariant::Incomplete () => EventStages::PostOnly, - EventVariant::Evasion { skill, evasion_rating } + EventVariant::Evasion { skill: _, evasion_rating: _ } + => EventStages::PostOnly, + + EventVariant::CooldownDecrease { skill: _, turns: _ } + => EventStages::PostOnly, + EventVariant::CooldownIncrease { skill: _, turns: _ } + => EventStages::PostOnly, + + EventVariant::Hit { skill: _ } => EventStages::PostOnly, } } @@ -1065,130 +1189,6 @@ impl Skill { } } -pub fn cast_actions(cast: Cast) -> Vec { - match cast.skill { - - Skill::Amplify => vec![ - Action::Effect { - construct: cast.target, - skill: cast.skill, - effect: ConstructEffect { effect: Effect::Amplify, duration: 2, meta: Some(EffectMeta::Multiplier(150)), tick: None }, - }, - ], - Skill::AmplifyPlus => vec![ - Action::Effect { - construct: cast.target, - skill: cast.skill, - effect: ConstructEffect { effect: Effect::Amplify, duration: 3, meta: Some(EffectMeta::Multiplier(175)), tick: None }, - }, - ], - Skill::AmplifyPlusPlus => vec![ - Action::Effect { - construct: cast.target, - skill: cast.skill, - effect: ConstructEffect { effect: Effect::Amplify, duration: 4, meta: Some(EffectMeta::Multiplier(200)), tick: None }, - }, - ], - - Skill::Attack => vec![ - Action::Damage { - construct: cast.target, - skill: cast.skill, - colour: Colour::Red, - values: vec![Value::Stat { construct: cast.source, stat: Stat::RedPower, mult: cast.skill.multiplier() }], - }, - ], - - - Skill::Banish => vec![ - Action::Effect { - construct: cast.target, - skill: cast.skill, - effect: ConstructEffect { effect: Effect::Banish, duration: 2, meta: None, tick: None } - } - ], - Skill::BanishPlus => vec![ - Action::Effect { - construct: cast.target, - skill: cast.skill, - effect: ConstructEffect { effect: Effect::Banish, duration: 2, meta: None, tick: None } - } - ], - Skill::BanishPlusPlus => vec![ - Action::Effect { - construct: cast.target, - skill: cast.skill, - effect: ConstructEffect { effect: Effect::Banish, duration: 2, meta: None, tick: None } - } - ], - - Skill::Bash => vec![ - Action::Damage { - construct: cast.target, - skill: cast.skill, - colour: Colour::Red, - values: vec![ - Value::Stat { construct: cast.source, stat: Stat::RedPower, mult: cast.skill.multiplier() }, - Value::Cooldowns { construct: cast.source, mult: 45 }, - ], - }, - Action::Effect { - construct: cast.target, - skill: cast.skill, - effect: ConstructEffect {effect: Effect::Stun, duration: 2, meta: Some(EffectMeta::Skill(Skill::Bash)), tick: None } - }, - Action::IncreaseCooldowns { - construct: cast.target, - skill: cast.skill, - turns: 1, - }, - ], - - // fn bash(source: &mut Construct, target: &mut Construct, skill: Skill) { -// skill.effect().into_iter() -// .for_each(|e| (game.event(Event::new(source, target).event(target.add_effect(skill, e))))); - -// if resolutions.iter().any(|r| match r.event { -// Event::Effect { effect, skill: effect_skill, duration: _, construct_effects: _ } -// => effect == Effect::Stun && skill == effect_skill, -// _ => false, -// }) { -// let mut cds = 0; -// for cs in target.skills.iter_mut() { -// if cs.skill.base_cd().is_some() { -// cs.cd = match cs.cd { -// None => Some(1), -// Some(i) => Some(i + 1), -// }; - -// cds += 1; -// } -// } - -// let amount = source.red_power().pct(skill.multiplier().pct(100 + 45usize.saturating_mul(cds))); -// target.deal_red_damage(skill, amount) -// .into_iter() -// .for_each(|e| game.event(Event::new(source, target).event(e).stages(EventStages::PostOnly))); -// } - -// } - - Skill::Strike | - Skill::StrikePlus | - Skill::StrikePlusPlus => vec![ - Action::Damage { - construct: cast.target, - skill: cast.skill, - colour: Colour::Red, - values: vec![Value::Stat { construct: cast.source, stat: Stat::RedPower, mult: cast.skill.multiplier() }], - }, - ], - - _ => unimplemented!() - } - -} - // fn sleep(source: &mut Construct, target: &mut Construct, skill: Skill) { // skill.effect().into_iter() // .for_each(|e| (game.event(Event::new(source, target).event(target.add_effect(skill, e))))); @@ -1635,10 +1635,9 @@ mod tests { #[test] fn attack_actions_test() { let cast = Cast::new(Uuid::new_v4(), Uuid::new_v4(), Uuid::new_v4(), Skill::Attack); - let mut actions = cast_actions(cast); - match actions.remove(0) { - Action::Damage { construct: _, skill, values: _, colour } => { - assert_eq!(skill, Skill::Attack); + let mut actions = cast.actions(); + match actions[0] { + Action::Damage { construct: _, values: _, colour } => { assert_eq!(colour, Colour::Red); }, _ => panic!("{:?}", actions),