diff --git a/core/src/construct.rs b/core/src/construct.rs index 26fab7ce..e614c237 100644 --- a/core/src/construct.rs +++ b/core/src/construct.rs @@ -112,20 +112,22 @@ impl ConstructEffect { #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub enum Stat { - Str, - Agi, - Int, - GreenLife, - Speed, - RedPower, - BluePower, - GreenPower, - RedDamageTaken, - BlueDamageTaken, - GreenDamageTaken, RedLife, + RedPower, + RedDamageTaken, + + GreenLife, + GreenPower, + GreenDamageTaken, + BlueLife, - Evasion, + BluePower, + BlueDamageTaken, + + Speed, + + Cooldowns, + Skills(Colour), } #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] @@ -474,6 +476,29 @@ impl Construct { } // Stats + + // fixme put everything through this fn + pub fn stat(&self, stat: Stat) -> usize { + match stat { + Stat::RedLife => self.red_life(), + Stat::RedPower => self.red_power(), + Stat::RedDamageTaken => unimplemented!(), + + Stat::GreenLife => self.green_life(), + Stat::GreenPower => self.green_power(), + Stat::GreenDamageTaken => unimplemented!(), + + Stat::BlueLife => self.blue_life(), + Stat::BluePower => self.blue_power(), + Stat::BlueDamageTaken => unimplemented!(), + + Stat::Speed => self.speed(), + + Stat::Cooldowns => unimplemented!(), + Stat::Skills(Colour) => unimplemented!(), + } + } + pub fn red_power(&self) -> usize { let red_power_mods = self.effects.iter() .filter(|e| e.effect.modifications().contains(&Stat::RedPower)) diff --git a/core/src/game.rs b/core/src/game.rs index 84bb86f8..ae5df71c 100644 --- a/core/src/game.rs +++ b/core/src/game.rs @@ -12,6 +12,7 @@ use failure::err_msg; use construct::{Construct, ConstructEffect, Stat}; use skill::{Skill, Cast}; use effect::{Effect}; +use util::{IntPct}; use player::{Player}; use instance::{TimeControl}; @@ -410,7 +411,63 @@ impl Game { self } - fn finalise_cast(&self, cast: Cast) -> Vec { + fn resolve_stack(mut self) -> Game { + if self.phase != Phase::Resolve { + panic!("game not in Resolve phase"); + } + + // find their statuses with ticks + let mut ticks = self.all_constructs() + .iter() + .flat_map( + |c| c.effects + .iter() + .cloned() + .filter_map(|e| e.tick)) + .collect::>(); + + // add them to the stack + self.stack.append(&mut ticks); + + self.stack_sort_speed(); + + // temp vec of this round's resolving skills + // because need to check cooldown use before pushing them into the complete list + let mut casters = vec![]; + let mut r_animation_ms = 0; + while let Some(cast) = self.stack.pop() { + // info!("{:} casts ", cast); + + let casts = self.pre_resolve(cast); + + for cast in casts { + self.resolve(cast); + } + + // r_animation_ms = Resolutions.iter().fold(r_animation_ms, |acc, r| acc + r.clone().get_delay()); + + // if theres no resolution Resolutions, the skill didn't trigger (disable etc) + // if Resolutions.len() > 0 && cast.used_cooldown() { + // casters.push(cast); + // } + + // sort the stack again in case speeds have changed + self.stack_sort_speed(); + }; + + // info!("{:#?}", self.casts); + + // handle cooldowns and statuses + self.progress_durations(&casters); + + if self.finished() { + return self.finish() + } + + self.skill_phase_start(r_animation_ms) + } + + fn pre_resolve(&self, cast: Cast) -> Vec { let target_player = self.players.iter() .find(|t| t.constructs.iter().any(|c| c.id == cast.target)) .unwrap(); @@ -440,88 +497,144 @@ impl Game { return targets; } + fn calculate_amount(&mut self, values: &Vec, resolutions: &Vec) -> usize { + values.iter() + .fold(0, |total, value| { + total + match value { + Value::Stat { construct, stat, mult } => + self.construct_by_id(*construct).unwrap().stat(*stat).pct(*mult), - fn resolve_stack(mut self) -> Game { - if self.phase != Phase::Resolve { - panic!("game not in Resolve phase"); - } + Value::Fixed { value } => *value, - // find their statuses with ticks - let mut ticks = self.all_constructs() - .iter() - .flat_map( - |c| c.effects - .iter() - .cloned() - .filter_map(|e| e.tick)) - .collect::>(); + Value::Cooldowns { construct, mult } => unimplemented!(), - // add them to the stack - self.stack.append(&mut ticks); + Value::ColourSkills { construct, colour, mult } => unimplemented!(), - self.stack_sort_speed(); - - // temp vec of this round's resolving skills - // because need to check cooldown use before pushing them into the complete list - let mut casters = vec![]; - let mut r_animation_ms = 0; - while let Some(cast) = self.stack.pop() { - // info!("{:} casts ", cast); - - let casts = self.finalise_cast(cast); - - for cast in casts { - self.resolve(cast); - } - - // r_animation_ms = Resolutions.iter().fold(r_animation_ms, |acc, r| acc + r.clone().get_delay()); - - // if theres no resolution Resolutions, the skill didn't trigger (disable etc) - // if Resolutions.len() > 0 && cast.used_cooldown() { - // casters.push(cast); - // } - - // sort the stack again in case speeds have changed - self.stack_sort_speed(); - }; - - // info!("{:#?}", self.casts); - - // handle cooldowns and statuses - self.progress_durations(&casters); - - if self.finished() { - return self.finish() - } - - self.skill_phase_start(r_animation_ms) + Value::DamageTaken { construct, colour, mult } => unimplemented!(), + // Skills { construct: Uuid, colour: Colour }, + } + }) } - pub fn resolve(&mut self, cast: Cast) -> &mut Game { + fn resolve(&mut self, cast: Cast) -> &mut Game { let skill = cast.skill; // calculate values first? // for result damage value need to pass &Resolutions and .find() - let mut resolutions = cast.actions().into_iter() - .flat_map(|action| { - match action { - Action::Cast { construct } => self.cast(cast), - Action::Hit { construct } => self.hit(cast), - Action::Damage { construct, values, colour } => self.damage(cast, construct, values, colour), - Action::Healing { construct, values, colour } => unimplemented!(), - Action::Effect { construct, effect } => self.effect(construct, effect), - Action::IncreaseCooldowns { construct, turns } => self.increase_cooldowns(construct, turns), - } - }) - .map(|event| Resolution::new(cast, event)) - .collect::>(); + let mut resolved = vec![]; - self.resolutions.last_mut().unwrap().append(&mut resolutions); + for action in cast.actions() { + let events = match action { + Action::Cast { construct } => self.cast(cast), + Action::Hit { construct } => self.hit(cast), + + Action::Damage { construct, values, colour } => { + let amount = self.calculate_amount(&values, &resolved); + self.damage(cast, construct, amount, colour) + }, + + Action::Heal { construct, values, colour } => { + let amount = self.calculate_amount(&values, &resolved); + self.heal(cast, construct, amount, colour) + }, + + Action::Effect { construct, effect } => self.effect(construct, effect), + Action::IncreaseCooldowns { construct, turns } => self.increase_cooldowns(construct, turns), + }; + + let mut resolutions = events.into_iter() + .map(|event| Resolution::new(cast, event)) + .collect::>(); + + resolved.append(&mut resolutions); + } + +// fn slay(source: &mut Construct, target: &mut Construct, skill: Skill) { +// let amount = source.red_power().pct(skill.multiplier()) + source.green_power().pct(skill.multiplier()); +// let slay_events = target.deal_red_damage(skill, amount); +// for e in slay_events { +// match e { +// Event::Damage { amount, mitigation: _, colour: _, skill: _ } => { +// game.event(Event::new(source, target).event(e)); +// let heal = source.deal_green_damage(skill, amount.pct(50)); +// for h in heal { +// game.event(Event::new(source, source).event(h).stages(EventStages::PostOnly)); +// }; +// }, +// _ => game.event(Event::new(source, target).event(e)), +// } +// } +// } + + self.resolutions.last_mut().unwrap().append(&mut resolved); self } + fn post_resolve(&mut self, resolutions: &Vec) -> &mut Game { + self + // 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::Skill(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::Skill(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::Skill(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)); + // } + // }, + // _ => (), + // }; + // } + } + fn cast(&mut self, cast: Cast) -> Vec { vec![Event::Cast { construct: cast.source, player: cast.player, direction: self.direction(cast) }] } @@ -530,9 +643,15 @@ impl Game { vec![Event::Hit { construct: cast.target, player: cast.player, direction: self.direction(cast) }] } - fn damage(&mut self, cast: Cast, construct: Uuid, values: Vec, colour: Colour) -> Vec { + fn damage(&mut self, cast: Cast, construct: Uuid, amount: usize, colour: Colour) -> Vec { match colour { - _ => self.construct_by_id(construct).unwrap().deal_red_damage(128) // fixme unwrap + _ => self.construct_by_id(construct).unwrap().deal_red_damage(amount) // fixme unwrap + } + } + + fn heal(&mut self, cast: Cast, construct: Uuid, value: usize, colour: Colour) -> Vec { + match colour { + _ => self.construct_by_id(construct).unwrap().deal_red_damage(value) // fixme unwrap } } @@ -744,7 +863,7 @@ pub enum Value { pub enum Action { Hit { construct: Uuid }, Cast { construct: Uuid }, - Healing { construct: Uuid, values: Vec, colour: Colour }, + Heal { construct: Uuid, values: Vec, colour: Colour }, Damage { construct: Uuid, values: Vec, colour: Colour }, Effect { construct: Uuid, effect: ConstructEffect }, IncreaseCooldowns { construct: Uuid, turns: usize }, diff --git a/core/src/skill.rs b/core/src/skill.rs index 3d198768..408319cc 100644 --- a/core/src/skill.rs +++ b/core/src/skill.rs @@ -85,6 +85,12 @@ impl Cast { }, ], + Skill::Stun => vec![ + Action::Effect { + construct: self.target, + effect: ConstructEffect { effect: Effect::Stun, duration: 2, meta: None, tick: None }, + }, + ], Skill::Amplify => vec![ @@ -111,7 +117,7 @@ impl Cast { construct: self.target, effect: ConstructEffect { effect: Effect::Absorb, duration: 1, meta: Some(EffectMeta::Skill(Skill::Absorption)), tick: None }, }, - Action::Healing { + Action::Heal { construct: self.target, values: vec![Value::Stat { construct: self.source, stat: Stat::BluePower, mult: self.skill.multiplier() }], colour: Colour::Blue, @@ -122,7 +128,7 @@ impl Cast { construct: self.target, effect: ConstructEffect { effect: Effect::Absorb, duration: 1, meta: Some(EffectMeta::Skill(Skill::AbsorptionPlus)), tick: None }, }, - Action::Healing { + Action::Heal { construct: self.target, values: vec![Value::Stat { construct: self.source, stat: Stat::BluePower, mult: self.skill.multiplier() }], colour: Colour::Blue, @@ -133,7 +139,7 @@ impl Cast { construct: self.target, effect: ConstructEffect { effect: Effect::Absorb, duration: 1, meta: Some(EffectMeta::Skill(Skill::AbsorptionPlusPlus)), tick: None }, }, - Action::Healing { + Action::Heal { construct: self.target, values: vec![Value::Stat { construct: self.source, stat: Stat::BluePower, mult: self.skill.multiplier() }], colour: Colour::Blue, @@ -277,7 +283,7 @@ impl Cast { construct: self.target, effect: ConstructEffect {effect: Effect::Intercept, duration: 1, meta: None, tick: None}, }, - Action::Healing { + Action::Heal { construct: self.target, values: vec![Value::Stat { construct: self.source, stat: Stat::RedPower, mult: self.skill.multiplier() }], colour: Colour::Red, @@ -307,38 +313,19 @@ impl Cast { colour: Colour::Red, values: vec![Value::Stat { construct: self.source, stat: Stat::RedPower, mult: self.skill.multiplier() }], }, - Action::Healing { + Action::Heal { construct: self.target, colour: Colour::Green, values: vec![Value::DamageTaken { construct: self.target, colour: Colour::Red, mult: 50 }], }, ], -// fn slay(source: &mut Construct, target: &mut Construct, skill: Skill) { -// let amount = source.red_power().pct(skill.multiplier()) + source.green_power().pct(skill.multiplier()); -// let slay_events = target.deal_red_damage(skill, amount); - -// for e in slay_events { -// match e { -// Event::Damage { amount, mitigation: _, colour: _, skill: _ } => { -// game.event(Event::new(source, target).event(e)); -// let heal = source.deal_green_damage(skill, amount.pct(50)); -// for h in heal { -// game.event(Event::new(source, source).event(h).stages(EventStages::PostOnly)); -// }; -// }, -// _ => game.event(Event::new(source, target).event(e)), -// } -// } - -// } - Skill::Sleep => vec![ Action::Effect { construct: self.target, effect: ConstructEffect {effect: Effect::Stun, duration: 2, meta: None, tick: None}, }, - Action::Healing { + Action::Heal { construct: self.target, values: vec![Value::Stat { construct: self.source, stat: Stat::GreenPower, mult: self.skill.multiplier() }], colour: Colour::Blue, @@ -349,7 +336,7 @@ impl Cast { construct: self.target, effect: ConstructEffect {effect: Effect::Stun, duration: 2, meta: None, tick: None}, }, - Action::Healing { + Action::Heal { construct: self.target, values: vec![Value::Stat { construct: self.source, stat: Stat::GreenPower, mult: self.skill.multiplier() }], colour: Colour::Blue, @@ -360,7 +347,7 @@ impl Cast { construct: self.target, effect: ConstructEffect {effect: Effect::Stun, duration: 2, meta: None, tick: None}, }, - Action::Healing { + Action::Heal { construct: self.target, values: vec![Value::Stat { construct: self.source, stat: Stat::GreenPower, mult: self.skill.multiplier() }], colour: Colour::Blue, @@ -384,7 +371,7 @@ impl Cast { construct: self.target, effect: ConstructEffect {effect: Effect::Sustain, duration: 1, meta: None, tick: None}, }, - Action::Healing { + Action::Heal { construct: self.target, values: vec![Value::Stat { construct: self.source, stat: Stat::RedPower, mult: self.skill.multiplier() }], colour: Colour::Red, @@ -1706,7 +1693,7 @@ mod tests { // //}; // match resolutions.remove(0).event { - // Event::Healing { skill: _, overhealing: _, amount } => assert!(amount > 0), + // Event::Heal { skill: _, overhealing: _, amount } => assert!(amount > 0), // _ => panic!("not healing from inversion"), // }; @@ -1779,7 +1766,7 @@ mod tests { // let Event { source: _, target, event, stages: _ } = resolutions.remove(0); // match event { - // Event::Healing { amount, skill: _, overhealing: _ } => { + // Event::Heal { amount, skill: _, overhealing: _ } => { // assert_eq!(amount, 256.pct(Skill::SiphonTick.multiplier()) + 220.pct(Skill::SiphonTick.multiplier())); // assert_eq!(target.id, x.id); // },