This commit is contained in:
ntr 2019-12-08 16:08:14 +10:00
parent 255131730c
commit 44c541edf8
3 changed files with 243 additions and 112 deletions

View File

@ -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))

View File

@ -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<Cast> {
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::<Vec<Cast>>();
// 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<Cast> {
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<Value>, resolutions: &Vec<Resolution>) -> 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::<Vec<Cast>>();
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::<Vec<Resolution>>();
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::<Vec<Resolution>>();
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<Resolution>) -> &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<Event> {
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<Value>, colour: Colour) -> Vec<Event> {
fn damage(&mut self, cast: Cast, construct: Uuid, amount: usize, colour: Colour) -> Vec<Event> {
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<Event> {
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<Value>, colour: Colour },
Heal { 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 },

View File

@ -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);
// },