diff --git a/core/src/construct.rs b/core/src/construct.rs index 84f1b9e2..2f494922 100644 --- a/core/src/construct.rs +++ b/core/src/construct.rs @@ -4,8 +4,8 @@ use rand::prelude::*; use failure::Error; use failure::err_msg; -use skill::{Skill, Cast, Disable, EventVariant, EventConstruct}; -use game::{Colour}; +use skill::{Skill, Cast}; +use game::{Colour, Disable, EventVariant, EventConstruct}; use effect::{Cooldown, Effect}; use spec::{Spec}; use item::{Item}; @@ -423,11 +423,11 @@ impl Construct { match skill.cd { Some(cd) => { skill.cd = Some(cd.saturating_add(turns)); - events.push(EventVariant::CooldownIncrease { target: self.id, turns }) + events.push(EventVariant::CooldownIncrease { construct: self.id, turns }) }, None => { skill.cd = Some(turns); - events.push(EventVariant::CooldownIncrease { target: self.id, turns }) + events.push(EventVariant::CooldownIncrease { construct: self.id, turns }) }, } } @@ -548,7 +548,6 @@ impl Construct { pub fn recharge(&mut self, red_amount: usize, blue_amount: usize) -> Vec { let mut events = vec![]; - let target = self.id; if self.is_ko() { return events; } match self.affected(Effect::Invert) { @@ -565,7 +564,7 @@ impl Construct { let blue = new_blue_life - current_blue_life; if red != 0 || blue != 0 { - events.push(EventVariant::Recharge { target, red, blue }); + events.push(EventVariant::Recharge { construct: self.id, red, blue }); } }, true => { @@ -592,7 +591,7 @@ impl Construct { let red_damage_amount = red_current_green_life - self.green_life(); events.push(EventVariant::Damage { - target, + construct: self.id, amount: red_damage_amount, mitigation: red_mitigation, colour: Colour::Red, @@ -622,7 +621,7 @@ impl Construct { let blue_damage_amount = blue_current_green_life - self.green_life(); events.push(EventVariant::Damage { - target, + construct: self.id, amount: blue_damage_amount, mitigation: blue_mitigation, colour: Colour::Blue, @@ -637,7 +636,7 @@ impl Construct { pub fn deal_green_damage(&mut self, amount: usize) -> Vec { let mut events = vec![]; if self.is_ko() { return events; } - let target = self.id; + let construct = self.id; let mods = self.effects.iter() .filter(|e| e.effect.modifications().contains(&Stat::GreenDamageTaken)) @@ -657,7 +656,7 @@ impl Construct { let overhealing = modified_power - healing; events.push(EventVariant::Healing { - target, + construct, amount: healing, overhealing, }); @@ -671,7 +670,7 @@ impl Construct { let delta = current_green_life - self.green_life(); events.push(EventVariant::Damage { - target, + construct, amount: delta, mitigation: 0, colour: Colour::Green, @@ -687,7 +686,7 @@ impl Construct { let mut events = vec![]; if self.is_ko() { return events; } - let target = self.id; + let construct = self.id; let mods = self.effects.iter() .filter(|e| e.effect.modifications().contains(&Stat::RedDamageTaken)) @@ -716,7 +715,7 @@ impl Construct { events.push( EventVariant::Damage { - target, + construct, amount: delta, mitigation, colour: Colour::Red, @@ -724,7 +723,7 @@ impl Construct { } ); if self.is_ko() { - events.push(EventVariant::Ko { target }); + events.push(EventVariant::Ko { construct }); } }, true => { @@ -743,7 +742,7 @@ impl Construct { if healing > 0 { events.push( EventVariant::Healing { - target, + construct, amount: healing, overhealing: overhealing - recharge, } @@ -751,7 +750,7 @@ impl Construct { } if recharge > 0 { - events.push(EventVariant::Recharge { target, red: recharge, blue: 0 }); + events.push(EventVariant::Recharge { construct, red: recharge, blue: 0 }); } } }; @@ -763,7 +762,7 @@ impl Construct { let mut events = vec![]; if self.is_ko() { return events; } - let target = self.id; + let construct = self.id; let mods = self.effects.iter() .filter(|e| e.effect.modifications().contains(&Stat::BlueDamageTaken)) @@ -787,7 +786,7 @@ impl Construct { let delta = current_green_life - self.green_life(); events.push(EventVariant::Damage { - target, + construct, amount: delta, mitigation, colour: Colour::Blue, @@ -808,11 +807,11 @@ impl Construct { let recharge = self.blue_life.value - current_life; if healing > 0 { - events.push(EventVariant::Healing { target, amount: healing, overhealing }); + events.push(EventVariant::Healing { construct, amount: healing, overhealing }); } if recharge > 0 { - events.push(EventVariant::Recharge { target, red: 0, blue: recharge }); + events.push(EventVariant::Recharge { construct, red: 0, blue: recharge }); } } }; @@ -838,7 +837,7 @@ impl Construct { // todo modified durations cause of buffs let result = EventVariant::Effect { - target: self.id, + construct: self.id, effect: effect.effect, duration: effect.duration, display: EventConstruct::new(self) diff --git a/core/src/game.rs b/core/src/game.rs index 94594e9e..4745fc88 100644 --- a/core/src/game.rs +++ b/core/src/game.rs @@ -10,7 +10,8 @@ use failure::Error; use failure::err_msg; use construct::{Construct, ConstructEffect, Stat}; -use skill::{Skill, Cast, Event, EventVariant, resolve}; +use skill::{Skill, Cast}; +use effect::{Effect}; use player::{Player}; use instance::{TimeControl}; @@ -23,6 +24,13 @@ pub enum Phase { Finished, } +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +pub enum Colour { + Red, + Blue, + Green, +} + #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub enum Value { Stat { construct: Uuid, stat: Stat, mult: usize }, @@ -33,22 +41,140 @@ pub enum Value { // Skills { construct: Uuid, colour: Colour }, } -#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] -pub enum Colour { - Red, - Blue, - Green, +#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] +pub enum Action { + Hit { construct: Uuid }, + Cast { construct: Uuid }, + Healing { construct: Uuid, values: Vec, colour: Colour }, + Damage { construct: Uuid, values: Vec, colour: Colour }, + Effect { construct: Uuid, effect: ConstructEffect }, + IncreaseCooldowns { construct: Uuid, turns: usize }, + // Recharge { skill: Skill, red: usize, blue: usize }, } #[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] -pub enum Action { - Hit { skill: Skill }, - Cast { skill: Skill }, - Damage { skill: Skill, construct: Uuid, values: Vec, colour: Colour }, - Effect { skill: Skill, construct: Uuid, effect: ConstructEffect }, - IncreaseCooldowns { skill: Skill, construct: Uuid, turns: usize }, - Healing { skill: Skill, construct: Uuid, values: Vec, colour: Colour }, - // Recharge { skill: Skill, red: usize, blue: usize }, +pub struct Event { + pub cast: Cast, + pub focus: Vec, + pub variant: EventVariant, + pub delay: i64, +} + +impl Event { + pub fn new(cast: Cast, variant: EventVariant) -> Event { + let focus = match variant { + EventVariant::HitAoe { construct: _ } => vec![cast.source, cast.target], // fixme + _ => vec![cast.source, cast.target], + }; + + let delay = variant.delay(); + Event { + cast, + delay, + focus, + variant, + } + } +} + +pub type Disable = Vec; +pub type Direction = (i8, i8); + +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +pub enum EventVariant { + Cast { construct: Uuid, player: Uuid, direction: Direction }, + Hit { construct: Uuid, player: Uuid, direction: Direction }, + HitAoe { construct: Uuid }, + + Damage { construct: Uuid, amount: usize, mitigation: usize, colour: Colour, display: EventConstruct }, + Effect { construct: Uuid, effect: Effect, duration: u8, display: EventConstruct }, + Removal { construct: Uuid, effect: Option, display: EventConstruct }, + + Healing { construct: Uuid, amount: usize, overhealing: usize }, + Recharge { construct: Uuid, red: usize, blue: usize }, + Inversion { construct: Uuid }, + Reflection { construct: Uuid }, + Ko { construct: Uuid }, + + CooldownIncrease { construct: Uuid, turns: usize }, + CooldownDecrease { construct: Uuid, turns: usize }, + Forfeit (), +} + +impl EventVariant { + fn delay(&self) -> i64 { + // let source_duration = 1000; // Time for SOURCE ONLY + let target_duration = 1500; // Time for target animation + let target_delay = 500; // Add delay if theres source animation + let combat_text_delay = 1300; // Time for all post skill + let combat_text_overlap = 600; // overlap between animation and combat text + + match self { + EventVariant::Cast { construct: _, direction: _, player: _ } => target_delay, + EventVariant::Hit { construct: _, direction: _, player: _ } | + EventVariant::HitAoe { construct: _ } => target_duration - combat_text_overlap, + _ => combat_text_delay, + } + } +} + +// used to show the progress of a construct +// while the resolutions are animating +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +pub struct EventConstruct { + pub id: Uuid, + pub red: usize, + pub green: usize, + pub blue: usize, +} + +impl EventConstruct { + pub fn new(construct: &Construct) -> EventConstruct { + EventConstruct { + id: construct.id, + red: construct.red_life(), + green: construct.green_life(), + blue: construct.blue_life(), + } + } +} + +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +pub enum EventStages { + #[serde(rename = "START_SKILL END_SKILL POST_SKILL")] + AllStages, // Anim Anim Anim + #[serde(rename = "START_SKILL END_SKILL")] + StartEnd, // Anim Anim Skip + #[serde(rename = "START_SKILL POST_SKILL")] + StartPost, // Anim Skip Anim + #[serde(rename = "START_SKILL")] + StartOnly, // Anim Skip Skip + #[serde(rename = "END_SKILL POST_SKILL")] + EndPost, // Skip Anim Anim + #[serde(rename = "END_SKILL")] + EndOnly, // Skip Anim Skip + #[serde(rename = "POST_SKILL")] + PostOnly, // Skip Skip Anim +} + +impl EventStages { + fn delay(self) -> i64 { + let source_duration = 1000; // Time for SOURCE ONLY + let target_delay = 500; // Used for Source + Target + let target_duration = 1500; // Time for TARGET ONLY + let post_skill = 1000; // Time for all POST + let source_and_target_total = target_delay + target_duration; // SOURCE + TARGET time + + match self { + EventStages::AllStages => source_and_target_total + post_skill, // Anim Anim Anim + EventStages::StartEnd => source_and_target_total, // Anim Anim Skip + EventStages::StartPost => source_duration + post_skill, // Anim Skip Anim + EventStages::StartOnly => source_duration, // Anim Skip Skip + EventStages::EndPost => target_duration + post_skill, // Skip Anim Anim + EventStages::EndOnly => target_duration, // Skip Anim Skip + EventStages::PostOnly => post_skill, // Skip Skip Anim + } + } } #[derive(Debug,Clone,Serialize,Deserialize)] @@ -445,6 +571,37 @@ impl Game { self } + fn finalise_cast(&self, cast: Cast) -> Vec { + let target_player = self.players.iter() + .find(|t| t.constructs.iter().any(|c| c.id == cast.target)) + .unwrap(); + + if let Some(t) = target_player.intercepting() { + return vec![Cast { target: t.id, ..cast }]; + } + + // if self.construct[source].multistrike() { + // return vec![ + // Cast { target: t.id, ..cast }, + // Cast { target: t.id, ..cast }, + // ]; + // } + + let targets = match cast.skill.aoe() { + true => self.players.iter() + .find(|t| t.constructs.iter().any(|c| c.id == cast.target)) + .unwrap() + .constructs + .iter() + .map(|c| Cast { target: c.id, ..cast }) + .collect(), + false => vec![cast], + }; + + return targets; + } + + fn resolve_stack(mut self) -> Game { if self.phase != Phase::Resolve { panic!("game not in Resolve phase"); @@ -472,7 +629,12 @@ impl Game { while let Some(cast) = self.stack.pop() { // info!("{:} casts ", cast); - resolve(&mut self, cast); + let casts = self.finalise_cast(cast); + + for cast in casts { + self.resolve(cast); + } + // r_animation_ms = events.iter().fold(r_animation_ms, |acc, r| acc + r.clone().get_delay()); // if theres no resolution events, the skill didn't trigger (disable etc) @@ -496,20 +658,22 @@ impl Game { self.skill_phase_start(r_animation_ms) } - pub fn actions(&mut self, cast: Cast) -> &mut Game { + pub fn resolve(&mut self, cast: Cast) -> &mut Game { let mut events_group = vec![]; + let skill = cast.skill; + // calculate values first? // for result damage value need to pass &events and .find() for action in cast.actions() { let mut events = match action { - Action::Cast { construct, skill, direction } => self.cast(construct, skill), - Action::Hit { construct, skill } => self.hit(construct, skill), - Action::Damage { construct, values, colour } => self.damage(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), + Action::Cast { construct } => self.cast(cast), + Action::Hit { construct } => self.hit(construct, skill), + Action::Damage { construct, values, colour } => self.damage(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), }; events_group.append(&mut events); @@ -519,8 +683,8 @@ impl Game { self } - fn cast(&mut self, construct: Uuid, skill: Skill) -> Vec { - vec![Event::new(EventVariant::Cast { skill: skill }, construct)] + fn cast(&mut self, cast: Cast) -> Vec { + vec![Event::new(cast, EventVariant::Cast { construct: cast.source, player: cast.player, direction: self.direction(cast) })] } fn hit(&mut self, construct: Uuid, skill: Skill) -> Vec { @@ -1388,7 +1552,7 @@ mod tests { 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()); + game.resolve(Cast::new(source, player_id, target, Skill::Bash)); println!("{:?}", game.events); } diff --git a/core/src/skill.rs b/core/src/skill.rs index 8d6c79d0..fd538ad6 100644 --- a/core/src/skill.rs +++ b/core/src/skill.rs @@ -720,175 +720,6 @@ fn end() {} // return cast_actions(skill, &mut a, &mut b, resolutions); // } -fn modify_cast(game: &Game, cast: Cast) -> Vec { - let target_player = game.players.iter() - .find(|t| t.constructs.iter().any(|c| c.id == cast.target)) - .unwrap(); - - if let Some(t) = target_player.intercepting() { - return vec![Cast { target: t.id, ..cast }]; - } - - // if game.construct[source].multistrike() { - // return vec![ - // Cast { target: t.id, ..cast }, - // Cast { target: t.id, ..cast }, - // ]; - // } - - let targets = match cast.skill.aoe() { - true => game.players.iter() - .find(|t| t.constructs.iter().any(|c| c.id == cast.target)) - .unwrap() - .constructs - .iter() - .map(|c| Cast { target: c.id, ..cast }) - .collect(), - false => vec![cast], - }; - - return targets; -} - - -pub fn resolve(game: &mut Game, cast: Cast) { - let casts = modify_cast(game, cast); - - // let source = game.construct_by_id(cast.source).unwrap().clone(); - // if skill.aoe() { // Send an aoe skill event for anims - // game.event(Event::new(&source, - // &game.construct_by_id(cast.target).unwrap().clone()).event(Event::AoeSkill { skill }).stages(EventStages::StartEnd)); - // } - - for cast in casts { - game.actions(cast.actions()); - } - -} - -pub type Disable = Vec; - -#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] -pub enum EventVariant { - Cast { target: Uuid, player: Uuid, x: i8, y: i8 }, - Hit { target: Uuid, player: Uuid, x: i8, y: i8 }, - HitAoe { target: Uuid }, - - Damage { target: Uuid, amount: usize, mitigation: usize, colour: Colour, display: EventConstruct }, - Effect { target: Uuid, effect: Effect, duration: u8, display: EventConstruct }, - Removal { target: Uuid, effect: Option, display: EventConstruct }, - - Healing { target: Uuid, amount: usize, overhealing: usize }, - Recharge { target: Uuid, red: usize, blue: usize }, - Inversion { target: Uuid }, - Reflection { target: Uuid }, - Ko { target: Uuid }, - - CooldownIncrease { target: Uuid, turns: usize }, - CooldownDecrease { target: Uuid, turns: usize }, - Forfeit (), -} - -impl EventVariant { - fn delay(&self) -> i64 { - // let source_duration = 1000; // Time for SOURCE ONLY - let target_duration = 1500; // Time for target animation - let target_delay = 500; // Add delay if theres source animation - let combat_text_delay = 1300; // Time for all post skill - let combat_text_overlap = 600; // overlap between animation and combat text - - match self { - EventVariant::Cast { target: _, x: _, y: _, player: _ } => target_delay, - EventVariant::Hit { target: _, x: _, y: _, player: _ } | - EventVariant::HitAoe { target: _ } => target_duration - combat_text_overlap, - _ => combat_text_delay, - } - } - - pub fn to_event(&self, cast: Cast) -> Event { - let delay = self.delay(); - let focus = match self { - EventVariant::HitAoe { target: _ } => vec![cast.source, cast.target], // some tricky multi target here l8r - _ => vec![cast.source, cast.target], - }; - - Event { - cast, - focus, - variant: *self, - delay, - } - } -} - - -#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] -pub struct Event { - pub cast: Cast, - pub focus: Vec, - pub variant: EventVariant, - pub delay: i64, -} - - -// used to show the progress of a construct -// while the resolutions are animating -#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] -pub struct EventConstruct { - pub id: Uuid, - pub red: usize, - pub green: usize, - pub blue: usize, -} - -impl EventConstruct { - pub fn new(construct: &Construct) -> EventConstruct { - EventConstruct { - id: construct.id, - red: construct.red_life(), - green: construct.green_life(), - blue: construct.blue_life(), - } - } -} - -#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] -pub enum EventStages { - #[serde(rename = "START_SKILL END_SKILL POST_SKILL")] - AllStages, // Anim Anim Anim - #[serde(rename = "START_SKILL END_SKILL")] - StartEnd, // Anim Anim Skip - #[serde(rename = "START_SKILL POST_SKILL")] - StartPost, // Anim Skip Anim - #[serde(rename = "START_SKILL")] - StartOnly, // Anim Skip Skip - #[serde(rename = "END_SKILL POST_SKILL")] - EndPost, // Skip Anim Anim - #[serde(rename = "END_SKILL")] - EndOnly, // Skip Anim Skip - #[serde(rename = "POST_SKILL")] - PostOnly, // Skip Skip Anim -} - -impl EventStages { - fn delay(self) -> i64 { - let source_duration = 1000; // Time for SOURCE ONLY - let target_delay = 500; // Used for Source + Target - let target_duration = 1500; // Time for TARGET ONLY - let post_skill = 1000; // Time for all POST - let source_and_target_total = target_delay + target_duration; // SOURCE + TARGET time - - match self { - EventStages::AllStages => source_and_target_total + post_skill, // Anim Anim Anim - EventStages::StartEnd => source_and_target_total, // Anim Anim Skip - EventStages::StartPost => source_duration + post_skill, // Anim Skip Anim - EventStages::StartOnly => source_duration, // Anim Skip Skip - EventStages::EndPost => target_duration + post_skill, // Skip Anim Anim - EventStages::EndOnly => target_duration, // Skip Anim Skip - EventStages::PostOnly => post_skill, // Skip Skip Anim - } - } -} #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub enum Skill {