This commit is contained in:
ntr 2019-12-12 14:21:02 +10:00
parent 9ec1d2974e
commit 302da9edbf
4 changed files with 140 additions and 72 deletions

View File

@ -1,7 +1,6 @@
# FIXME # FIXME
last round of animations skipped cause no skill phase to add new vec
check silence skill multiplier
check silence skill multiplier
game ready not auto starting resolve phase game ready not auto starting resolve phase
purify conditional healing purify conditional healing

View File

@ -934,6 +934,26 @@ impl Construct {
return vec![]; return vec![];
} }
pub fn effect_meta(&mut self, effect: Effect, amount: usize) -> Vec<Event> {
let construct = self.id;
self.effects.iter_mut()
.filter(|ce| ce.effect == effect)
.flat_map(|ce| match ce.meta.unwrap() {
EffectMeta::AddedDamage(_) => {
let meta = EffectMeta::AddedDamage(amount);
ce.meta = Some(meta);
vec![Event::Meta { construct, effect, meta }]
},
EffectMeta::Multiplier(_) => {
let meta = EffectMeta::Multiplier(amount);
ce.meta = Some(meta);
vec![Event::Meta { construct, effect, meta }]
},
_ => vec![],
}
).collect()
}
pub fn remove_all(&mut self) -> Vec<Event> { pub fn remove_all(&mut self) -> Vec<Event> {
let mut removals = vec![]; let mut removals = vec![];
if self.is_ko() { return vec![Event::TargetKo { construct: self.id }] } if self.is_ko() { return vec![Event::TargetKo { construct: self.id }] }
@ -967,26 +987,25 @@ impl Construct {
let ConstructEffect { effect: _, duration: _, meta } = let ConstructEffect { effect: _, duration: _, meta } =
self.effects.iter().find(|e| e.effect == Effect::Electric).unwrap(); self.effects.iter().find(|e| e.effect == Effect::Electric).unwrap();
let skill = match meta { match meta {
Some(EffectMeta::CastOnHit(s)) => s, Some(EffectMeta::CastTick { source: _, target, skill }) =>
casts.push(Cast::new(self.id, self.account, *target, *skill)),
_ => panic!("no electrify skill {:?}", meta), _ => panic!("no electrify skill {:?}", meta),
}; };
casts.push(Cast::new(self.id, self.account, cast.source, *skill));
} }
// if self.affected(Effect::Absorb) { if self.affected(Effect::Absorb) {
// let ConstructEffect { effect, duration: _, meta } = let ConstructEffect { effect: _, duration: _, meta } =
// self.effects.iter().find(|e| e.effect == Effect::Absorb).unwrap(); self.effects.iter().find(|e| e.effect == Effect::Absorb).unwrap();
// let skill = match meta { let skill = match meta {
// Some(EffectMeta::Skill(s)) => s, Some(EffectMeta::CastOnHit(s)) => s,
// _ => panic!("no absorption skill"), _ => panic!("no absorb skill {:?}", meta),
// }; };
// casts.push(Cast::new(self.id, self.account, self.id, *skill)); casts.push(Cast::new(self.id, self.account, self.id, *skill));
// } }
if self.affected(Effect::Counter) && *colour == Colour::Red { if self.affected(Effect::Counter) && *colour == Colour::Red {
let ConstructEffect { effect: _, duration: _, meta } = let ConstructEffect { effect: _, duration: _, meta } =

View File

@ -440,7 +440,8 @@ impl Game {
// because need to check cooldown use before pushing them into the complete list // because need to check cooldown use before pushing them into the complete list
let mut r_animation_ms = 0; let mut r_animation_ms = 0;
while let Some(cast) = self.stack.pop() { while let Some(cast) = self.stack.pop() {
self.resolve(cast); let events = vec![];
self.resolve(cast, events);
// sort the stack again in case speeds have changed // sort the stack again in case speeds have changed
self.stack_sort_speed(); self.stack_sort_speed();
@ -463,16 +464,16 @@ impl Game {
self.skill_phase_start(r_animation_ms) self.skill_phase_start(r_animation_ms)
} }
fn resolve(&mut self, cast: Cast) -> &mut Game { fn resolve(&mut self, cast: Cast, mut events: Vec<Event>) -> Vec<Event> {
// If the skill is disabled for source nothing else will happen // If the skill is disabled for source nothing else will happen
if let Some(effects) = self.construct(cast.source).disabled(cast.skill) { if let Some(effects) = self.construct(cast.source).disabled(cast.skill) {
self.add_resolution(&cast, &Event::Disable { construct: cast.source, effects }); self.add_resolution(&cast, &Event::Disable { construct: cast.source, effects });
return self; return events;
} }
// hastestrike / hybridblast // hastestrike / hybridblast
for skill in self.construct(cast.source).additional_skills(cast.skill) { for skill in self.construct(cast.source).additional_skills(cast.skill) {
self.resolve(Cast { skill, ..cast }); events = self.resolve(Cast { skill, ..cast }, events);
} }
// for aoe events send the source / target animations before each set of casts // for aoe events send the source / target animations before each set of casts
@ -487,10 +488,10 @@ impl Game {
let casts = self.modify_cast(cast); let casts = self.modify_cast(cast);
for cast in casts { for cast in casts {
self.execute(cast); events = self.execute(cast, events);
} }
self events
} }
fn modify_cast(&self, cast: Cast) -> Vec<Cast> { fn modify_cast(&self, cast: Cast) -> Vec<Cast> {
@ -516,20 +517,15 @@ impl Game {
return casts; return casts;
} }
fn execute(&mut self, cast: Cast) -> &mut Game { fn execute(&mut self, cast: Cast, mut events: Vec<Event>) -> Vec<Event> {
// this list is used to calculate values
// that are dependent on the result of previous events
// such as healing based on damage done etc
let mut event_list = vec![];
if self.construct(cast.target).is_ko() { if self.construct(cast.target).is_ko() {
self.add_resolution(&cast, &Event::TargetKo { construct: cast.target }); self.add_resolution(&cast, &Event::TargetKo { construct: cast.target });
return self; return events;
} }
if let Some(immunity) = self.construct(cast.target).immune(cast.skill) { if let Some(immunity) = self.construct(cast.target).immune(cast.skill) {
self.add_resolution(&cast, &Event::Immune { construct: cast.target, effects: immunity }); self.add_resolution(&cast, &Event::Immune { construct: cast.target, effects: immunity });
return self; return events;
} }
if self.construct(cast.target).affected(Effect::Reflect) && cast.skill.colours().contains(&Colour::Blue) && !cast.skill.is_tick() { if self.construct(cast.target).affected(Effect::Reflect) && cast.skill.colours().contains(&Colour::Blue) && !cast.skill.is_tick() {
@ -538,24 +534,24 @@ impl Game {
// both reflecting, show it and bail // both reflecting, show it and bail
if self.construct(cast.source).affected(Effect::Reflect) { if self.construct(cast.source).affected(Effect::Reflect) {
self.add_resolution(&cast, &Event::Reflection { construct: cast.source }); self.add_resolution(&cast, &Event::Reflection { construct: cast.source });
return self; return events;
} }
return self.execute(Cast { target: cast.source, ..cast }, ); return self.execute(Cast { target: cast.source, ..cast }, events);
} }
for action in cast.actions() { for action in cast.actions() {
let events = match action { let new_events = match action {
Action::Cast => vec![self.cast(cast)], Action::Cast => vec![self.cast(cast)],
Action::Hit => vec![self.hit(cast)], Action::Hit => vec![self.hit(cast)],
Action::Damage { construct, values, colour } => { Action::Damage { construct, values, colour } => {
let amount = self.reduce_values(&values, &event_list); let amount = self.reduce_values(&values, &events);
self.damage(construct, amount, colour) self.damage(construct, amount, colour)
}, },
Action::Heal { construct, values, colour } => { Action::Heal { construct, values, colour } => {
let amount = self.reduce_values(&values, &event_list); let amount = self.reduce_values(&values, &events);
self.heal(construct, amount, colour) self.heal(construct, amount, colour)
}, },
@ -563,19 +559,37 @@ impl Game {
Action::Remove { construct, effect } => self.effect_remove(construct, effect), Action::Remove { construct, effect } => self.effect_remove(construct, effect),
Action::RemoveAll { construct } => self.remove_all(construct), Action::RemoveAll { construct } => self.remove_all(construct),
Action::IncreaseCooldowns { construct, turns } => self.increase_cooldowns(construct, turns), Action::IncreaseCooldowns { construct, turns } => self.increase_cooldowns(construct, turns),
Action::SetEffectMeta { construct, values, effect } => {
let amount = self.reduce_values(&values, &events);
self.effect_meta(construct, effect, amount)
}
}; };
for event in events { // this event is now considered to have happened
// this event is now considered to have happened // for chronological ordering it is added to the resolution list
// for chronological ordering it is added to the resolution list // before extra processing on it begins
// before extra processing on it begins
for event in new_events {
self.add_resolution(&cast, &event); self.add_resolution(&cast, &event);
event_list.push(event);
self.event_reactions(&cast, &event_list[event_list.len() - 1]); let casts = match event {
Event::Damage { construct, colour: _, amount: _, mitigation: _, display: _ } =>
self.construct_by_id(construct).unwrap().damage_trigger_casts(&cast, &event),
// Event::Cast {} => set_cooldown()
Event::Ko { construct } =>
self.construct_by_id(construct).unwrap().on_ko(&cast, &event),
_ => vec![],
};
events.push(event);
for cast in casts {
events = self.resolve(cast, events);
}
} }
} }
self events
} }
fn add_resolution(&mut self, cast: &Cast, event: &Event) -> &mut Game { fn add_resolution(&mut self, cast: &Cast, event: &Event) -> &mut Game {
@ -584,23 +598,6 @@ impl Game {
self self
} }
fn event_reactions(&mut self, cast: &Cast, event: &Event) -> &mut Game {
let casts = match event {
Event::Damage { construct, colour, amount, mitigation, display: _ } =>
self.construct_by_id(*construct).unwrap().damage_trigger_casts(cast, event),
// Event::Cast {} => set_cooldown()
Event::Ko { construct } =>
self.construct_by_id(*construct).unwrap().on_ko(cast, event),
_ => vec![],
};
for cast in casts {
self.resolve(cast);
}
self
}
fn reduce_values(&mut self, values: &Vec<Value>, events: &Vec<Event>) -> usize { fn reduce_values(&mut self, values: &Vec<Value>, events: &Vec<Event>) -> usize {
values.iter() values.iter()
.fold(0, |total, value| { .fold(0, |total, value| {
@ -679,6 +676,10 @@ impl Game {
self.construct_by_id(construct).unwrap().effect_remove(effect) self.construct_by_id(construct).unwrap().effect_remove(effect)
} }
fn effect_meta(&mut self, construct: Uuid, effect: Effect, amount: usize) -> Vec<Event> {
self.construct_by_id(construct).unwrap().effect_meta(effect, amount)
}
// could be remove by colour etc // could be remove by colour etc
fn remove_all(&mut self, construct: Uuid) -> Vec<Event> { fn remove_all(&mut self, construct: Uuid) -> Vec<Event> {
self.construct_by_id(construct).unwrap().remove_all() self.construct_by_id(construct).unwrap().remove_all()
@ -844,6 +845,7 @@ pub enum Action {
Remove { construct: Uuid, effect: Effect }, Remove { construct: Uuid, effect: Effect },
RemoveAll { construct: Uuid }, RemoveAll { construct: Uuid },
IncreaseCooldowns { construct: Uuid, turns: usize }, IncreaseCooldowns { construct: Uuid, turns: usize },
SetEffectMeta { construct: Uuid, values: Vec<Value>, effect: Effect },
} }
#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] #[derive(Debug,Clone,PartialEq,Serialize,Deserialize)]
@ -886,6 +888,7 @@ pub enum Event {
Damage { construct: Uuid, amount: usize, mitigation: usize, colour: Colour, display: EventConstruct }, Damage { construct: Uuid, amount: usize, mitigation: usize, colour: Colour, display: EventConstruct },
Effect { construct: Uuid, effect: Effect, duration: u8, display: EventConstruct }, Effect { construct: Uuid, effect: Effect, duration: u8, display: EventConstruct },
Removal { construct: Uuid, effect: Effect, display: EventConstruct }, Removal { construct: Uuid, effect: Effect, display: EventConstruct },
Meta { construct: Uuid, effect: Effect, meta: EffectMeta },
Healing { construct: Uuid, amount: usize, overhealing: usize, colour: Colour, display: EventConstruct }, Healing { construct: Uuid, amount: usize, overhealing: usize, colour: Colour, display: EventConstruct },
Inversion { construct: Uuid }, Inversion { construct: Uuid },
@ -964,6 +967,7 @@ impl Event {
Event::TargetKo { construct: _ } | Event::TargetKo { construct: _ } |
Event::Disable { construct: _, effects: _ } | Event::Disable { construct: _, effects: _ } |
Event::Immune { construct: _, effects: _ } | Event::Immune { construct: _, effects: _ } |
Event::Meta { construct: _, effect: _, meta: _ } |
Event::Forfeit() => Animation::Skip, Event::Forfeit() => Animation::Skip,
} }
} }
@ -1677,7 +1681,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.resolve(Cast::new(source, player_id, target, Skill::Bash)); game.resolve(Cast::new(source, player_id, target, Skill::Bash), vec![]);
} }
#[test] #[test]
@ -1687,7 +1691,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.resolve(Cast::new(source, player_id, target, Skill::Slay)); game.resolve(Cast::new(source, player_id, target, Skill::Slay), vec![]);
let last = game.resolutions.len() - 1; let last = game.resolutions.len() - 1;
let resolutions = &game.resolutions[last]; let resolutions = &game.resolutions[last];
@ -1712,9 +1716,9 @@ 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.resolve(Cast::new(source, player_id, target, Skill::Strike)); game.resolve(Cast::new(source, player_id, target, Skill::Strike), vec![]);
game.resolve(Cast::new(source, player_id, target, Skill::Invert)); game.resolve(Cast::new(source, player_id, target, Skill::Invert), vec![]);
game.resolve(Cast::new(source, player_id, target, Skill::Strike)); game.resolve(Cast::new(source, player_id, target, Skill::Strike), vec![]);
let last = game.resolutions.len() - 1; let last = game.resolutions.len() - 1;
let resolutions = &game.resolutions[last]; let resolutions = &game.resolutions[last];
@ -1739,7 +1743,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.resolve(Cast::new(source, player_id, target, Skill::Siphon)); game.resolve(Cast::new(source, player_id, target, Skill::Siphon), vec![]);
let last = game.resolutions.len() - 1; let last = game.resolutions.len() - 1;
let resolutions = &game.resolutions[last]; let resolutions = &game.resolutions[last];
@ -1767,12 +1771,12 @@ mod tests {
game = game.resolve_phase_start(); game = game.resolve_phase_start();
// que ota? // que ota?
game.resolve(Cast::new(source, player_id, target, Skill::Siphon)); game.resolve(Cast::new(source, player_id, target, Skill::Siphon), vec![]);
let last = game.resolutions.len() - 1; let last = game.resolutions.len() - 1;
let resolutions = &game.resolutions[last]; let resolutions = &game.resolutions[last];
let damage_events = resolutions.iter().filter(|r| match r.event { let damage_events = resolutions.iter().filter(|r| match r.event {
Event::Damage { construct, colour, amount, mitigation: _, display: _ } => true, Event::Damage { construct: _, colour: _, amount: _, mitigation: _, display: _ } => true,
_ => false, _ => false,
}).count(); }).count();
@ -1786,8 +1790,8 @@ 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.resolve(Cast::new(source, player_id, target, Skill::Reflect)); game.resolve(Cast::new(source, player_id, target, Skill::Reflect), vec![]);
game.resolve(Cast::new(source, player_id, target, Skill::Blast)); game.resolve(Cast::new(source, player_id, target, Skill::Blast), vec![]);
let last = game.resolutions.len() - 1; let last = game.resolutions.len() - 1;
let resolutions = &game.resolutions[last]; let resolutions = &game.resolutions[last];
@ -1798,4 +1802,35 @@ mod tests {
_ => false, _ => false,
})); }));
} }
#[test]
fn absorb_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.resolve(Cast::new(source, player_id, target, Skill::Absorb), vec![]);
game.resolve(Cast::new(source, player_id, target, Skill::Blast), vec![]);
let last = game.resolutions.len() - 1;
let resolutions = &game.resolutions[last];
assert!(resolutions.iter().any(|r| match r.event {
Event::Damage { construct, colour, amount, mitigation: _, display: _ } => {
assert!(construct == target && amount > 0 && colour == Colour::Blue && r.skill == Skill::Blast);
resolutions.iter().any(|r| match r.event {
Event::Meta { construct, effect, meta } =>
construct == target && effect == Effect::Absorption && {
match meta {
EffectMeta::AddedDamage(added_dmg) => added_dmg == amount,
_ => false,
}
},
_ => false,
})
},
_ => false,
}));
}
} }

View File

@ -155,8 +155,13 @@ impl Cast {
effect: Effect::Absorb, effect: Effect::Absorb,
}, },
Action::Effect { Action::Effect {
construct: self.target, construct: self.source,
effect: ConstructEffect { effect: Effect::Absorption, duration: 3, meta: None }, effect: ConstructEffect { effect: Effect::Absorption, duration: 3, meta: Some(EffectMeta::AddedDamage(0)) },
},
Action::SetEffectMeta {
construct: self.source,
effect: Effect::Absorption,
values: vec![Value::DamageReceived { construct: self.source, colour: Colour::Blue, mult: 100 }],
}, },
], ],
Skill::AbsorptionPlus => vec![ Skill::AbsorptionPlus => vec![
@ -166,7 +171,12 @@ impl Cast {
}, },
Action::Effect { Action::Effect {
construct: self.target, construct: self.target,
effect: ConstructEffect { effect: Effect::Absorption, duration: 4, meta: None }, effect: ConstructEffect { effect: Effect::Absorption, duration: 4, meta: Some(EffectMeta::AddedDamage(0)) },
},
Action::SetEffectMeta {
construct: self.target,
effect: Effect::Absorption,
values: vec![Value::DamageReceived { construct: self.target, colour: Colour::Blue, mult: 100 }],
}, },
], ],
Skill::AbsorptionPlusPlus => vec![ Skill::AbsorptionPlusPlus => vec![
@ -176,7 +186,12 @@ impl Cast {
}, },
Action::Effect { Action::Effect {
construct: self.target, construct: self.target,
effect: ConstructEffect { effect: Effect::Absorption, duration: 5, meta: None }, effect: ConstructEffect { effect: Effect::Absorption, duration: 5, meta: Some(EffectMeta::AddedDamage(0)) },
},
Action::SetEffectMeta {
construct: self.target,
effect: Effect::Absorption,
values: vec![Value::DamageReceived { construct: self.target, colour: Colour::Blue, mult: 100 }],
}, },
], ],