This commit is contained in:
ntr 2019-12-07 12:42:57 +10:00
parent be0bf33753
commit cc64d21859
3 changed files with 209 additions and 215 deletions

View File

@ -4,8 +4,8 @@ use rand::prelude::*;
use failure::Error; use failure::Error;
use failure::err_msg; use failure::err_msg;
use skill::{Skill, Cast, Disable, EventVariant, EventConstruct}; use skill::{Skill, Cast};
use game::{Colour}; use game::{Colour, Disable, EventVariant, EventConstruct};
use effect::{Cooldown, Effect}; use effect::{Cooldown, Effect};
use spec::{Spec}; use spec::{Spec};
use item::{Item}; use item::{Item};
@ -423,11 +423,11 @@ impl Construct {
match skill.cd { match skill.cd {
Some(cd) => { Some(cd) => {
skill.cd = Some(cd.saturating_add(turns)); skill.cd = Some(cd.saturating_add(turns));
events.push(EventVariant::CooldownIncrease { target: self.id, turns }) events.push(EventVariant::CooldownIncrease { construct: self.id, turns })
}, },
None => { None => {
skill.cd = Some(turns); 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<EventVariant> { pub fn recharge(&mut self, red_amount: usize, blue_amount: usize) -> Vec<EventVariant> {
let mut events = vec![]; let mut events = vec![];
let target = self.id;
if self.is_ko() { return events; } if self.is_ko() { return events; }
match self.affected(Effect::Invert) { match self.affected(Effect::Invert) {
@ -565,7 +564,7 @@ impl Construct {
let blue = new_blue_life - current_blue_life; let blue = new_blue_life - current_blue_life;
if red != 0 || blue != 0 { if red != 0 || blue != 0 {
events.push(EventVariant::Recharge { target, red, blue }); events.push(EventVariant::Recharge { construct: self.id, red, blue });
} }
}, },
true => { true => {
@ -592,7 +591,7 @@ impl Construct {
let red_damage_amount = red_current_green_life - self.green_life(); let red_damage_amount = red_current_green_life - self.green_life();
events.push(EventVariant::Damage { events.push(EventVariant::Damage {
target, construct: self.id,
amount: red_damage_amount, amount: red_damage_amount,
mitigation: red_mitigation, mitigation: red_mitigation,
colour: Colour::Red, colour: Colour::Red,
@ -622,7 +621,7 @@ impl Construct {
let blue_damage_amount = blue_current_green_life - self.green_life(); let blue_damage_amount = blue_current_green_life - self.green_life();
events.push(EventVariant::Damage { events.push(EventVariant::Damage {
target, construct: self.id,
amount: blue_damage_amount, amount: blue_damage_amount,
mitigation: blue_mitigation, mitigation: blue_mitigation,
colour: Colour::Blue, colour: Colour::Blue,
@ -637,7 +636,7 @@ impl Construct {
pub fn deal_green_damage(&mut self, amount: usize) -> Vec<EventVariant> { pub fn deal_green_damage(&mut self, amount: usize) -> Vec<EventVariant> {
let mut events = vec![]; let mut events = vec![];
if self.is_ko() { return events; } if self.is_ko() { return events; }
let target = self.id; let construct = self.id;
let mods = self.effects.iter() let mods = self.effects.iter()
.filter(|e| e.effect.modifications().contains(&Stat::GreenDamageTaken)) .filter(|e| e.effect.modifications().contains(&Stat::GreenDamageTaken))
@ -657,7 +656,7 @@ impl Construct {
let overhealing = modified_power - healing; let overhealing = modified_power - healing;
events.push(EventVariant::Healing { events.push(EventVariant::Healing {
target, construct,
amount: healing, amount: healing,
overhealing, overhealing,
}); });
@ -671,7 +670,7 @@ impl Construct {
let delta = current_green_life - self.green_life(); let delta = current_green_life - self.green_life();
events.push(EventVariant::Damage { events.push(EventVariant::Damage {
target, construct,
amount: delta, amount: delta,
mitigation: 0, mitigation: 0,
colour: Colour::Green, colour: Colour::Green,
@ -687,7 +686,7 @@ impl Construct {
let mut events = vec![]; let mut events = vec![];
if self.is_ko() { return events; } if self.is_ko() { return events; }
let target = self.id; let construct = self.id;
let mods = self.effects.iter() let mods = self.effects.iter()
.filter(|e| e.effect.modifications().contains(&Stat::RedDamageTaken)) .filter(|e| e.effect.modifications().contains(&Stat::RedDamageTaken))
@ -716,7 +715,7 @@ impl Construct {
events.push( events.push(
EventVariant::Damage { EventVariant::Damage {
target, construct,
amount: delta, amount: delta,
mitigation, mitigation,
colour: Colour::Red, colour: Colour::Red,
@ -724,7 +723,7 @@ impl Construct {
} }
); );
if self.is_ko() { if self.is_ko() {
events.push(EventVariant::Ko { target }); events.push(EventVariant::Ko { construct });
} }
}, },
true => { true => {
@ -743,7 +742,7 @@ impl Construct {
if healing > 0 { if healing > 0 {
events.push( events.push(
EventVariant::Healing { EventVariant::Healing {
target, construct,
amount: healing, amount: healing,
overhealing: overhealing - recharge, overhealing: overhealing - recharge,
} }
@ -751,7 +750,7 @@ impl Construct {
} }
if recharge > 0 { 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![]; let mut events = vec![];
if self.is_ko() { return events; } if self.is_ko() { return events; }
let target = self.id; let construct = self.id;
let mods = self.effects.iter() let mods = self.effects.iter()
.filter(|e| e.effect.modifications().contains(&Stat::BlueDamageTaken)) .filter(|e| e.effect.modifications().contains(&Stat::BlueDamageTaken))
@ -787,7 +786,7 @@ impl Construct {
let delta = current_green_life - self.green_life(); let delta = current_green_life - self.green_life();
events.push(EventVariant::Damage { events.push(EventVariant::Damage {
target, construct,
amount: delta, amount: delta,
mitigation, mitigation,
colour: Colour::Blue, colour: Colour::Blue,
@ -808,11 +807,11 @@ impl Construct {
let recharge = self.blue_life.value - current_life; let recharge = self.blue_life.value - current_life;
if healing > 0 { if healing > 0 {
events.push(EventVariant::Healing { target, amount: healing, overhealing }); events.push(EventVariant::Healing { construct, amount: healing, overhealing });
} }
if recharge > 0 { 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 // todo modified durations cause of buffs
let result = EventVariant::Effect { let result = EventVariant::Effect {
target: self.id, construct: self.id,
effect: effect.effect, effect: effect.effect,
duration: effect.duration, duration: effect.duration,
display: EventConstruct::new(self) display: EventConstruct::new(self)

View File

@ -10,7 +10,8 @@ use failure::Error;
use failure::err_msg; use failure::err_msg;
use construct::{Construct, ConstructEffect, Stat}; use construct::{Construct, ConstructEffect, Stat};
use skill::{Skill, Cast, Event, EventVariant, resolve}; use skill::{Skill, Cast};
use effect::{Effect};
use player::{Player}; use player::{Player};
use instance::{TimeControl}; use instance::{TimeControl};
@ -23,6 +24,13 @@ pub enum Phase {
Finished, Finished,
} }
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
pub enum Colour {
Red,
Blue,
Green,
}
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
pub enum Value { pub enum Value {
Stat { construct: Uuid, stat: Stat, mult: usize }, Stat { construct: Uuid, stat: Stat, mult: usize },
@ -33,22 +41,140 @@ pub enum Value {
// Skills { construct: Uuid, colour: Colour }, // Skills { construct: Uuid, colour: Colour },
} }
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] #[derive(Debug,Clone,PartialEq,Serialize,Deserialize)]
pub enum Colour { pub enum Action {
Red, Hit { construct: Uuid },
Blue, Cast { construct: Uuid },
Green, Healing { construct: Uuid, values: Vec<Value>, colour: Colour },
Damage { construct: Uuid, values: Vec<Value>, 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)] #[derive(Debug,Clone,PartialEq,Serialize,Deserialize)]
pub enum Action { pub struct Event {
Hit { skill: Skill }, pub cast: Cast,
Cast { skill: Skill }, pub focus: Vec<Uuid>,
Damage { skill: Skill, construct: Uuid, values: Vec<Value>, colour: Colour }, pub variant: EventVariant,
Effect { skill: Skill, construct: Uuid, effect: ConstructEffect }, pub delay: i64,
IncreaseCooldowns { skill: Skill, construct: Uuid, turns: usize }, }
Healing { skill: Skill, construct: Uuid, values: Vec<Value>, colour: Colour },
// Recharge { skill: Skill, red: usize, blue: usize }, 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<Effect>;
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<Effect>, 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)] #[derive(Debug,Clone,Serialize,Deserialize)]
@ -445,6 +571,37 @@ impl Game {
self self
} }
fn finalise_cast(&self, cast: Cast) -> Vec<Cast> {
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 { fn resolve_stack(mut self) -> Game {
if self.phase != Phase::Resolve { if self.phase != Phase::Resolve {
panic!("game not in Resolve phase"); panic!("game not in Resolve phase");
@ -472,7 +629,12 @@ impl Game {
while let Some(cast) = self.stack.pop() { while let Some(cast) = self.stack.pop() {
// info!("{:} casts ", cast); // 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()); // 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) // 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) 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 mut events_group = vec![];
let skill = cast.skill;
// calculate values first? // calculate values first?
// for result damage value need to pass &events and .find() // for result damage value need to pass &events and .find()
for action in cast.actions() { for action in cast.actions() {
let mut events = match action { let mut events = match action {
Action::Cast { construct, skill, direction } => self.cast(construct, skill), Action::Cast { construct } => self.cast(cast),
Action::Hit { construct, skill } => self.hit(construct, skill), Action::Hit { construct } => self.hit(construct, skill),
Action::Damage { construct, values, colour } => self.damage(construct, values, colour), Action::Damage { construct, values, colour } => self.damage(construct, values, colour),
Action::Healing { construct, values, colour } => unimplemented!(), Action::Healing { construct, values, colour } => unimplemented!(),
Action::Effect { construct, effect } => self.effect(construct, effect), Action::Effect { construct, effect } => self.effect(construct, effect),
Action::IncreaseCooldowns { construct, turns } => self.increase_cooldowns(construct, turns), Action::IncreaseCooldowns { construct, turns } => self.increase_cooldowns(construct, turns),
}; };
events_group.append(&mut events); events_group.append(&mut events);
@ -519,8 +683,8 @@ impl Game {
self self
} }
fn cast(&mut self, construct: Uuid, skill: Skill) -> Vec<Event> { fn cast(&mut self, cast: Cast) -> Vec<Event> {
vec![Event::new(EventVariant::Cast { skill: skill }, construct)] 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<Event> { fn hit(&mut self, construct: Uuid, skill: Skill) -> Vec<Event> {
@ -1388,7 +1552,7 @@ mod tests {
let source = game.players[0].constructs[0].id; let source = game.players[0].constructs[0].id;
let target = game.players[1].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); println!("{:?}", game.events);
} }

View File

@ -720,175 +720,6 @@ fn end() {}
// return cast_actions(skill, &mut a, &mut b, resolutions); // return cast_actions(skill, &mut a, &mut b, resolutions);
// } // }
fn modify_cast(game: &Game, cast: Cast) -> Vec<Cast> {
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<Effect>;
#[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<Effect>, 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<Uuid>,
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)] #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
pub enum Skill { pub enum Skill {