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
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
purify conditional healing

View File

@ -934,6 +934,26 @@ impl Construct {
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> {
let mut removals = vec![];
if self.is_ko() { return vec![Event::TargetKo { construct: self.id }] }
@ -967,26 +987,25 @@ impl Construct {
let ConstructEffect { effect: _, duration: _, meta } =
self.effects.iter().find(|e| e.effect == Effect::Electric).unwrap();
let skill = match meta {
Some(EffectMeta::CastOnHit(s)) => s,
match meta {
Some(EffectMeta::CastTick { source: _, target, skill }) =>
casts.push(Cast::new(self.id, self.account, *target, *skill)),
_ => panic!("no electrify skill {:?}", meta),
};
casts.push(Cast::new(self.id, self.account, cast.source, *skill));
}
// if self.affected(Effect::Absorb) {
// let ConstructEffect { effect, duration: _, meta } =
// self.effects.iter().find(|e| e.effect == Effect::Absorb).unwrap();
if self.affected(Effect::Absorb) {
let ConstructEffect { effect: _, duration: _, meta } =
self.effects.iter().find(|e| e.effect == Effect::Absorb).unwrap();
// let skill = match meta {
// Some(EffectMeta::Skill(s)) => s,
// _ => panic!("no absorption skill"),
// };
let skill = match meta {
Some(EffectMeta::CastOnHit(s)) => s,
_ => 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 {
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
let mut r_animation_ms = 0;
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
self.stack_sort_speed();
@ -463,16 +464,16 @@ impl Game {
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 let Some(effects) = self.construct(cast.source).disabled(cast.skill) {
self.add_resolution(&cast, &Event::Disable { construct: cast.source, effects });
return self;
return events;
}
// hastestrike / hybridblast
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
@ -487,10 +488,10 @@ impl Game {
let casts = self.modify_cast(cast);
for cast in casts {
self.execute(cast);
events = self.execute(cast, events);
}
self
events
}
fn modify_cast(&self, cast: Cast) -> Vec<Cast> {
@ -516,20 +517,15 @@ impl Game {
return casts;
}
fn execute(&mut self, cast: Cast) -> &mut Game {
// 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![];
fn execute(&mut self, cast: Cast, mut events: Vec<Event>) -> Vec<Event> {
if self.construct(cast.target).is_ko() {
self.add_resolution(&cast, &Event::TargetKo { construct: cast.target });
return self;
return events;
}
if let Some(immunity) = self.construct(cast.target).immune(cast.skill) {
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() {
@ -538,24 +534,24 @@ impl Game {
// both reflecting, show it and bail
if self.construct(cast.source).affected(Effect::Reflect) {
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() {
let events = match action {
let new_events = match action {
Action::Cast => vec![self.cast(cast)],
Action::Hit => vec![self.hit(cast)],
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)
},
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)
},
@ -563,19 +559,37 @@ impl Game {
Action::Remove { construct, effect } => self.effect_remove(construct, effect),
Action::RemoveAll { construct } => self.remove_all(construct),
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
// for chronological ordering it is added to the resolution list
// before extra processing on it begins
// this event is now considered to have happened
// for chronological ordering it is added to the resolution list
// before extra processing on it begins
for event in new_events {
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 {
@ -584,23 +598,6 @@ impl Game {
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 {
values.iter()
.fold(0, |total, value| {
@ -679,6 +676,10 @@ impl Game {
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
fn remove_all(&mut self, construct: Uuid) -> Vec<Event> {
self.construct_by_id(construct).unwrap().remove_all()
@ -844,6 +845,7 @@ pub enum Action {
Remove { construct: Uuid, effect: Effect },
RemoveAll { construct: Uuid },
IncreaseCooldowns { construct: Uuid, turns: usize },
SetEffectMeta { construct: Uuid, values: Vec<Value>, effect: Effect },
}
#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)]
@ -886,6 +888,7 @@ pub enum Event {
Damage { construct: Uuid, amount: usize, mitigation: usize, colour: Colour, display: EventConstruct },
Effect { construct: Uuid, effect: Effect, duration: u8, 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 },
Inversion { construct: Uuid },
@ -964,6 +967,7 @@ impl Event {
Event::TargetKo { construct: _ } |
Event::Disable { construct: _, effects: _ } |
Event::Immune { construct: _, effects: _ } |
Event::Meta { construct: _, effect: _, meta: _ } |
Event::Forfeit() => Animation::Skip,
}
}
@ -1677,7 +1681,7 @@ mod tests {
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::Bash));
game.resolve(Cast::new(source, player_id, target, Skill::Bash), vec![]);
}
#[test]
@ -1687,7 +1691,7 @@ mod tests {
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::Slay));
game.resolve(Cast::new(source, player_id, target, Skill::Slay), vec![]);
let last = game.resolutions.len() - 1;
let resolutions = &game.resolutions[last];
@ -1712,9 +1716,9 @@ mod tests {
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::Strike));
game.resolve(Cast::new(source, player_id, target, Skill::Invert));
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), vec![]);
game.resolve(Cast::new(source, player_id, target, Skill::Strike), vec![]);
let last = game.resolutions.len() - 1;
let resolutions = &game.resolutions[last];
@ -1739,7 +1743,7 @@ mod tests {
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::Siphon));
game.resolve(Cast::new(source, player_id, target, Skill::Siphon), vec![]);
let last = game.resolutions.len() - 1;
let resolutions = &game.resolutions[last];
@ -1767,12 +1771,12 @@ mod tests {
game = game.resolve_phase_start();
// 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 resolutions = &game.resolutions[last];
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,
}).count();
@ -1786,8 +1790,8 @@ mod tests {
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::Reflect));
game.resolve(Cast::new(source, player_id, target, Skill::Blast));
game.resolve(Cast::new(source, player_id, target, Skill::Reflect), vec![]);
game.resolve(Cast::new(source, player_id, target, Skill::Blast), vec![]);
let last = game.resolutions.len() - 1;
let resolutions = &game.resolutions[last];
@ -1798,4 +1802,35 @@ mod tests {
_ => 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,
},
Action::Effect {
construct: self.target,
effect: ConstructEffect { effect: Effect::Absorption, duration: 3, meta: None },
construct: self.source,
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![
@ -166,7 +171,12 @@ impl Cast {
},
Action::Effect {
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![
@ -176,7 +186,12 @@ impl Cast {
},
Action::Effect {
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 }],
},
],