actions wip

This commit is contained in:
ntr 2019-12-04 18:11:06 +10:00
parent 488dc21182
commit 9b5261c95a
6 changed files with 220 additions and 235 deletions

View File

@ -2,4 +2,7 @@
remove immunity
aoe event
cooldown events leak skills
hit event
reqs moving eventconstruct into specific events
cooldown checking -> go through round and find all casters

View File

@ -4,7 +4,7 @@ use rand::prelude::*;
use failure::Error;
use failure::err_msg;
use skill::{Skill, Cast, Immunity, Disable, Event, EventVariant};
use skill::{Skill, Cast, Disable, Event, EventVariant};
use game::{Colour};
use effect::{Cooldown, Effect};
use spec::{Spec};
@ -341,24 +341,6 @@ impl Construct {
self
}
pub fn immune(&self, skill: Skill) -> Option<Immunity> {
// also checked in resolve stage so shouldn't happen really
if self.is_ko() {
return Some(vec![Effect::Ko]);
}
let immunities = self.effects.iter()
.filter(|e| e.effect.immune(skill))
.map(|e| e.effect)
.collect::<Vec<Effect>>();
if immunities.len() > 0 {
return Some(immunities);
}
None
}
pub fn disabled(&self, skill: Skill) -> Option<Disable> {
if self.is_ko() && !skill.ko_castable() {
return Some(vec![Effect::Ko]);
@ -433,6 +415,29 @@ impl Construct {
self
}
pub fn increase_cooldowns(&mut self, turns: usize) -> Vec<Event> {
let mut events = vec![];
let state = self.clone();
for skill in self.skills.iter_mut() {
if skill.skill.base_cd().is_some() { // if has a cooldown
match skill.cd {
Some(cd) => {
skill.cd = Some(cd.saturating_add(turns));
events.push(Event::new(EventVariant::CooldownIncrease { turns, skill: skill.skill }, &state))
},
None => {
skill.cd = Some(turns);
events.push(Event::new(EventVariant::CooldownIncrease { turns, skill: skill.skill }, &state))
},
}
}
}
return events;
}
pub fn reduce_cooldowns(&mut self) -> &mut Construct {
for skill in self.skills.iter_mut() {
// if used cooldown
@ -543,16 +548,10 @@ impl Construct {
}
}
pub fn recharge(&mut self, skill: Skill, red_amount: usize, blue_amount: usize) -> Vec<Event> {
pub fn recharge(&mut self, red_amount: usize, blue_amount: usize) -> Vec<Event> {
let mut events = vec![];
// Should red type immunity block recharge???
if let Some(immunity) = self.immune(skill) {
if !self.is_ko() {
events.push(Event::new(EventVariant::Immunity { skill, immunity }, self));
}
return events;
}
if self.is_ko() { return events; }
match self.affected(Effect::Invert) {
false => {
@ -568,7 +567,7 @@ impl Construct {
let blue = new_blue_life - current_blue_life;
if red != 0 || blue != 0 {
events.push(Event::new(EventVariant::Recharge { red, blue, skill }, self));
events.push(Event::new(EventVariant::Recharge { red, blue }, self));
}
},
true => {
@ -595,7 +594,6 @@ impl Construct {
let red_damage_amount = red_current_green_life - self.green_life();
events.push(Event::new(EventVariant::Damage {
skill,
amount: red_damage_amount,
mitigation: red_mitigation,
colour: Colour::Red
@ -624,7 +622,6 @@ impl Construct {
let blue_damage_amount = blue_current_green_life - self.green_life();
events.push(Event::new(EventVariant::Damage {
skill,
amount: blue_damage_amount,
mitigation: blue_mitigation,
colour: Colour::Blue
@ -635,14 +632,9 @@ impl Construct {
return events;
}
pub fn deal_green_damage(&mut self, skill: Skill, amount: usize) -> Vec<Event> {
pub fn deal_green_damage(&mut self, amount: usize) -> Vec<Event> {
let mut events = vec![];
if let Some(immunity) = self.immune(skill) {
if !self.is_ko() {
events.push(Event::new(EventVariant::Immunity { skill, immunity }, self));
}
return events;
}
if self.is_ko() { return events; }
let mods = self.effects.iter()
.filter(|e| e.effect.modifications().contains(&Stat::GreenDamageTaken))
@ -662,7 +654,6 @@ impl Construct {
let overhealing = modified_power - healing;
events.push(Event::new(EventVariant::Healing {
skill,
amount: healing,
overhealing,
}, self));
@ -676,7 +667,6 @@ impl Construct {
let delta = current_green_life - self.green_life();
events.push(Event::new(EventVariant::Damage {
skill,
amount: delta,
mitigation: 0,
colour: Colour::Green,
@ -687,15 +677,10 @@ impl Construct {
return events;
}
pub fn deal_red_damage(&mut self, skill: Skill, amount: usize) -> Vec<Event> {
pub fn deal_red_damage(&mut self, amount: usize) -> Vec<Event> {
let mut events = vec![];
if let Some(immunity) = self.immune(skill) {
if !self.is_ko() {
events.push(Event::new(EventVariant::Immunity { skill, immunity }, self));
}
return events;
}
if self.is_ko() { return events; }
let mods = self.effects.iter()
.filter(|e| e.effect.modifications().contains(&Stat::RedDamageTaken))
@ -724,7 +709,6 @@ impl Construct {
events.push(Event::new(
EventVariant::Damage {
skill,
amount: delta,
mitigation,
colour: Colour::Red,
@ -748,7 +732,6 @@ impl Construct {
if healing > 0 {
events.push(Event::new(
EventVariant::Healing{
skill,
amount: healing,
overhealing: overhealing - recharge,
}, self
@ -756,7 +739,7 @@ impl Construct {
}
if recharge > 0 {
events.push(Event::new(EventVariant::Recharge { red: recharge, blue: 0, skill }, self));
events.push(Event::new(EventVariant::Recharge { red: recharge, blue: 0 }, self));
}
}
};
@ -764,15 +747,10 @@ impl Construct {
return events;
}
pub fn deal_blue_damage(&mut self, skill: Skill, amount: usize) -> Vec<Event> {
pub fn deal_blue_damage(&mut self, amount: usize) -> Vec<Event> {
let mut events = vec![];
if let Some(immunity) = self.immune(skill) {
if !self.is_ko() {
events.push(Event::new(EventVariant::Immunity { skill, immunity }, self));
}
return events;
}
if self.is_ko() { return events; }
let mods = self.effects.iter()
.filter(|e| e.effect.modifications().contains(&Stat::BlueDamageTaken))
@ -796,7 +774,6 @@ impl Construct {
let delta = current_green_life - self.green_life();
events.push(Event::new(EventVariant::Damage {
skill,
amount: delta,
mitigation,
colour: Colour::Blue,
@ -818,7 +795,6 @@ impl Construct {
if healing > 0 {
events.push(
Event::new(EventVariant::Healing {
skill,
amount: healing,
overhealing,
},
@ -827,7 +803,7 @@ impl Construct {
}
if recharge > 0 {
events.push(Event::new(EventVariant::Recharge { red: 0, blue: recharge, skill }, self));
events.push(Event::new(EventVariant::Recharge { red: 0, blue: recharge }, self));
}
}
};
@ -835,16 +811,10 @@ impl Construct {
return events;
}
pub fn add_effect(&mut self, skill: Skill, effect: ConstructEffect) -> Vec<Event> {
if let Some(immunity) = self.immune(skill) {
return vec![
Event::new(
EventVariant::Immunity{
skill,
immunity,
}, self)
];
}
pub fn add_effect(&mut self, effect: ConstructEffect) -> Vec<Event> {
if self.is_ko() { return vec![] }
if self.affected(Effect::Banish) { panic!("banish immunity not fixt yet") }
if let Some(p) = self.effects.iter().position(|ce| ce.effect == effect.effect) {
// duplicate effect
@ -863,7 +833,6 @@ impl Construct {
effect: effect.effect,
duration: effect.duration,
construct_effects: self.effects.clone(),
skill,
}, self
);

View File

@ -3,7 +3,7 @@ use game::{Colour};
use skill::{Skill};
use util::{IntPct};
pub type Cooldown = Option<u8>;
pub type Cooldown = Option<usize>;
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
pub enum Effect {

View File

@ -11,7 +11,7 @@ use failure::err_msg;
use construct::{Construct, ConstructEffect, Stat};
use skill::{Skill, Cast, Event, resolve};
use effect::{Effect};
use player::{Player};
use instance::{TimeControl};
@ -40,9 +40,9 @@ pub enum Colour {
#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)]
pub enum Action {
Damage { construct: Uuid, skill: Skill, values: Vec<Value>, colour: Colour },
Effect { construct: Uuid, skill: Skill, effect: ConstructEffect },
IncreaseCooldowns { construct: Uuid, skill: Skill, turns: usize },
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 },
}
@ -498,31 +498,31 @@ impl Game {
pub fn actions(&mut self, actions: Vec<Action>) -> &mut Game {
for action in actions {
match action {
Action::Damage { construct, skill, values, colour } => self.damage(construct, skill, values, colour),
Action::Effect { construct, skill, effect } => self.effect(construct, skill, effect),
Action::IncreaseCooldowns { construct, skill, turns } => self.increase_cooldowns(construct, skill, turns),
Action::Damage { construct, values, colour } => self.damage(construct, values, colour),
Action::Effect { construct, effect } => self.effect(construct, effect),
Action::IncreaseCooldowns { construct, turns } => self.increase_cooldowns(construct, turns),
};
}
self
}
fn damage(&mut self, construct: Uuid, skill: Skill, values: Vec<Value>, colour: Colour) -> &mut Game {
fn damage(&mut self, construct: Uuid, values: Vec<Value>, colour: Colour) -> &mut Game {
let resolutions = match colour {
_ => self.construct_by_id(construct).unwrap().deal_red_damage(skill, 128) // fixme unwrap
_ => self.construct_by_id(construct).unwrap().deal_red_damage(128) // fixme unwrap
};
self.resolution_add(resolutions);
self
}
fn effect(&mut self, construct: Uuid, skill: Skill, effect: ConstructEffect) -> &mut Game {
let resolutions = self.construct_by_id(construct).unwrap().add_effect(skill, effect);
fn effect(&mut self, construct: Uuid, effect: ConstructEffect) -> &mut Game {
let resolutions = self.construct_by_id(construct).unwrap().add_effect(effect);
self.resolution_add(resolutions);
self
}
fn increase_cooldowns(&mut self, construct: Uuid, skill: Skill, turns: usize) -> &mut Game {
fn increase_cooldowns(&mut self, construct: Uuid, turns: usize) -> &mut Game {
let resolutions = self.construct_by_id(construct).unwrap().increase_cooldowns(turns);
self.resolution_add(resolutions);
self
@ -1339,5 +1339,19 @@ mod tests {
let target = game.players[1].constructs[0].id;
game.add_skill(player_id, source, target, Skill::Attack).unwrap();
game = game.resolve_phase_start();
println!("{:?}", game);
}
#[test]
fn bash_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.actions(Cast::new(source, player_id, target, Skill::Bash).actions());
println!("{:?}", game);
}
}

View File

@ -739,7 +739,7 @@ impl Item {
Item::Sustain|
Item::SustainPlus |
Item::SustainPlusPlus => format!(
"Construct cannot be KO'd while active and provides immunity to disables. Lasts {:?}T.
"Construct cannot be KO'd while active. Lasts {:?}T.
Recharges target RedLife based on {:?}% RedPower.",
self.into_skill().unwrap().effect()[0].get_duration(),
self.into_skill().unwrap().multiplier()),

View File

@ -62,7 +62,9 @@ pub fn resolve(game: &mut Game, cast: Cast) {
// }
for cast in casts {
game.actions(cast_actions(cast));
let mut actions = vec![]; // fixme hit event
actions.append(&mut cast.actions());
game.actions(actions);
}
}
@ -103,10 +105,123 @@ impl Cast {
pub fn used_cooldown(&self) -> bool {
return self.skill.base_cd().is_some();
}
pub fn actions(&self) -> Vec<Action> {
match self.skill {
Skill::Amplify => vec![
Action::Effect {
construct: self.target,
effect: ConstructEffect { effect: Effect::Amplify, duration: 2, meta: Some(EffectMeta::Multiplier(150)), tick: None },
},
],
Skill::AmplifyPlus => vec![
Action::Effect {
construct: self.target,
effect: ConstructEffect { effect: Effect::Amplify, duration: 3, meta: Some(EffectMeta::Multiplier(175)), tick: None },
},
],
Skill::AmplifyPlusPlus => vec![
Action::Effect {
construct: self.target,
effect: ConstructEffect { effect: Effect::Amplify, duration: 4, meta: Some(EffectMeta::Multiplier(200)), tick: None },
},
],
Skill::Attack => vec![
Action::Damage {
construct: self.target,
colour: Colour::Red,
values: vec![Value::Stat { construct: self.source, stat: Stat::RedPower, mult: self.skill.multiplier() }],
},
],
Skill::Banish => vec![
Action::Effect {
construct: self.target,
effect: ConstructEffect { effect: Effect::Banish, duration: 2, meta: None, tick: None }
}
],
Skill::BanishPlus => vec![
Action::Effect {
construct: self.target,
effect: ConstructEffect { effect: Effect::Banish, duration: 2, meta: None, tick: None }
}
],
Skill::BanishPlusPlus => vec![
Action::Effect {
construct: self.target,
effect: ConstructEffect { effect: Effect::Banish, duration: 2, meta: None, tick: None }
}
],
Skill::Bash => vec![
Action::Damage {
construct: self.target,
colour: Colour::Red,
values: vec![
Value::Stat { construct: self.source, stat: Stat::RedPower, mult: self.skill.multiplier() },
Value::Cooldowns { construct: self.source, mult: 45 },
],
},
Action::Effect {
construct: self.target,
effect: ConstructEffect {effect: Effect::Stun, duration: 2, meta: Some(EffectMeta::Skill(Skill::Bash)), tick: None }
},
Action::IncreaseCooldowns {
construct: self.target,
turns: 1,
},
],
// fn bash(source: &mut Construct, target: &mut Construct, skill: Skill) {
// skill.effect().into_iter()
// .for_each(|e| (game.event(Event::new(source, target).event(target.add_effect(skill, e)))));
// if resolutions.iter().any(|r| match r.event {
// Event::Effect { effect, skill: effect_skill, duration: _, construct_effects: _ }
// => effect == Effect::Stun && skill == effect_skill,
// _ => false,
// }) {
// let mut cds = 0;
// for cs in target.skills.iter_mut() {
// if cs.skill.base_cd().is_some() {
// cs.cd = match cs.cd {
// None => Some(1),
// Some(i) => Some(i + 1),
// };
// cds += 1;
// }
// }
// let amount = source.red_power().pct(skill.multiplier().pct(100 + 45usize.saturating_mul(cds)));
// target.deal_red_damage(skill, amount)
// .into_iter()
// .for_each(|e| game.event(Event::new(source, target).event(e).stages(EventStages::PostOnly)));
// }
// }
Skill::Strike |
Skill::StrikePlus |
Skill::StrikePlusPlus => vec![
Action::Damage {
construct: self.target,
colour: Colour::Red,
values: vec![Value::Stat { construct: self.source, stat: Stat::RedPower, mult: self.skill.multiplier() }],
},
],
_ => unimplemented!()
}
}
}
pub type Disable = Vec<Effect>;
pub type Immunity = Vec<Effect>;
#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)]
pub struct Event {
@ -136,17 +251,17 @@ impl Event {
#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)]
pub enum EventVariant {
Disable { skill: Skill, disable: Disable },
Immunity { skill: Skill, immunity: Immunity },
Damage { skill: Skill, amount: usize, mitigation: usize, colour: Colour },
Healing { skill: Skill, amount: usize, overhealing: usize },
Recharge { skill: Skill, red: usize, blue: usize },
Hit { skill: Skill },
Disable { disable: Disable },
Damage { amount: usize, mitigation: usize, colour: Colour },
Healing { amount: usize, overhealing: usize },
Recharge { red: usize, blue: usize },
Inversion { skill: Skill },
Reflection { skill: Skill },
AoeSkill { skill: Skill },
Skill { skill: Skill },
Effect { skill: Skill, effect: Effect, duration: u8, construct_effects: Vec<ConstructEffect> },
Removal { skill: Skill, effect: Option<Effect>, construct_effects: Vec<ConstructEffect> },
Effect { effect: Effect, duration: u8, construct_effects: Vec<ConstructEffect> },
Removal { effect: Option<Effect>, construct_effects: Vec<ConstructEffect> },
TargetKo { skill: Skill },
// skill not necessary but makes it neater as all events are arrays in js
Ko (),
@ -154,34 +269,35 @@ pub enum EventVariant {
Incomplete (),
// not used
Evasion { skill: Skill, evasion_rating: usize },
CooldownIncrease { turns: usize, skill: Skill },
CooldownDecrease { turns: usize, skill: Skill },
}
impl EventVariant {
fn stages(&self) -> EventStages {
match self {
EventVariant::Disable { skill, disable}
EventVariant::Disable { disable: _}
=> EventStages::PostOnly,
EventVariant::Immunity { skill, immunity}
EventVariant::Damage { amount: _, mitigation: _, colour: _}
=> EventStages::PostOnly,
EventVariant::Damage { skill, amount, mitigation, colour}
EventVariant::Healing { amount: _, overhealing: _}
=> EventStages::PostOnly,
EventVariant::Healing { skill, amount, overhealing}
EventVariant::Recharge { red: _, blue: _}
=> EventStages::PostOnly,
EventVariant::Recharge { skill, red, blue}
EventVariant::Inversion { skill: _ }
=> EventStages::PostOnly,
EventVariant::Inversion { skill }
EventVariant::Reflection { skill: _ }
=> EventStages::PostOnly,
EventVariant::Reflection { skill }
EventVariant::AoeSkill { skill: _ }
=> EventStages::PostOnly,
EventVariant::AoeSkill { skill }
EventVariant::Skill { skill: _ }
=> EventStages::PostOnly,
EventVariant::Skill { skill }
EventVariant::Effect { effect: _, duration: _, construct_effects: _ }
=> EventStages::PostOnly,
EventVariant::Effect { skill, effect, duration, construct_effects }
EventVariant::Removal { effect: _, construct_effects: _ }
=> EventStages::PostOnly,
EventVariant::Removal { skill, effect, construct_effects }
=> EventStages::PostOnly,
EventVariant::TargetKo { skill }
EventVariant::TargetKo { skill: _ }
=> EventStages::PostOnly,
EventVariant::Ko ()
=> EventStages::PostOnly,
@ -189,7 +305,15 @@ impl EventVariant {
=> EventStages::PostOnly,
EventVariant::Incomplete ()
=> EventStages::PostOnly,
EventVariant::Evasion { skill, evasion_rating }
EventVariant::Evasion { skill: _, evasion_rating: _ }
=> EventStages::PostOnly,
EventVariant::CooldownDecrease { skill: _, turns: _ }
=> EventStages::PostOnly,
EventVariant::CooldownIncrease { skill: _, turns: _ }
=> EventStages::PostOnly,
EventVariant::Hit { skill: _ }
=> EventStages::PostOnly,
}
}
@ -1065,130 +1189,6 @@ impl Skill {
}
}
pub fn cast_actions(cast: Cast) -> Vec<Action> {
match cast.skill {
Skill::Amplify => vec![
Action::Effect {
construct: cast.target,
skill: cast.skill,
effect: ConstructEffect { effect: Effect::Amplify, duration: 2, meta: Some(EffectMeta::Multiplier(150)), tick: None },
},
],
Skill::AmplifyPlus => vec![
Action::Effect {
construct: cast.target,
skill: cast.skill,
effect: ConstructEffect { effect: Effect::Amplify, duration: 3, meta: Some(EffectMeta::Multiplier(175)), tick: None },
},
],
Skill::AmplifyPlusPlus => vec![
Action::Effect {
construct: cast.target,
skill: cast.skill,
effect: ConstructEffect { effect: Effect::Amplify, duration: 4, meta: Some(EffectMeta::Multiplier(200)), tick: None },
},
],
Skill::Attack => vec![
Action::Damage {
construct: cast.target,
skill: cast.skill,
colour: Colour::Red,
values: vec![Value::Stat { construct: cast.source, stat: Stat::RedPower, mult: cast.skill.multiplier() }],
},
],
Skill::Banish => vec![
Action::Effect {
construct: cast.target,
skill: cast.skill,
effect: ConstructEffect { effect: Effect::Banish, duration: 2, meta: None, tick: None }
}
],
Skill::BanishPlus => vec![
Action::Effect {
construct: cast.target,
skill: cast.skill,
effect: ConstructEffect { effect: Effect::Banish, duration: 2, meta: None, tick: None }
}
],
Skill::BanishPlusPlus => vec![
Action::Effect {
construct: cast.target,
skill: cast.skill,
effect: ConstructEffect { effect: Effect::Banish, duration: 2, meta: None, tick: None }
}
],
Skill::Bash => vec![
Action::Damage {
construct: cast.target,
skill: cast.skill,
colour: Colour::Red,
values: vec![
Value::Stat { construct: cast.source, stat: Stat::RedPower, mult: cast.skill.multiplier() },
Value::Cooldowns { construct: cast.source, mult: 45 },
],
},
Action::Effect {
construct: cast.target,
skill: cast.skill,
effect: ConstructEffect {effect: Effect::Stun, duration: 2, meta: Some(EffectMeta::Skill(Skill::Bash)), tick: None }
},
Action::IncreaseCooldowns {
construct: cast.target,
skill: cast.skill,
turns: 1,
},
],
// fn bash(source: &mut Construct, target: &mut Construct, skill: Skill) {
// skill.effect().into_iter()
// .for_each(|e| (game.event(Event::new(source, target).event(target.add_effect(skill, e)))));
// if resolutions.iter().any(|r| match r.event {
// Event::Effect { effect, skill: effect_skill, duration: _, construct_effects: _ }
// => effect == Effect::Stun && skill == effect_skill,
// _ => false,
// }) {
// let mut cds = 0;
// for cs in target.skills.iter_mut() {
// if cs.skill.base_cd().is_some() {
// cs.cd = match cs.cd {
// None => Some(1),
// Some(i) => Some(i + 1),
// };
// cds += 1;
// }
// }
// let amount = source.red_power().pct(skill.multiplier().pct(100 + 45usize.saturating_mul(cds)));
// target.deal_red_damage(skill, amount)
// .into_iter()
// .for_each(|e| game.event(Event::new(source, target).event(e).stages(EventStages::PostOnly)));
// }
// }
Skill::Strike |
Skill::StrikePlus |
Skill::StrikePlusPlus => vec![
Action::Damage {
construct: cast.target,
skill: cast.skill,
colour: Colour::Red,
values: vec![Value::Stat { construct: cast.source, stat: Stat::RedPower, mult: cast.skill.multiplier() }],
},
],
_ => unimplemented!()
}
}
// fn sleep(source: &mut Construct, target: &mut Construct, skill: Skill) {
// skill.effect().into_iter()
// .for_each(|e| (game.event(Event::new(source, target).event(target.add_effect(skill, e)))));
@ -1635,10 +1635,9 @@ mod tests {
#[test]
fn attack_actions_test() {
let cast = Cast::new(Uuid::new_v4(), Uuid::new_v4(), Uuid::new_v4(), Skill::Attack);
let mut actions = cast_actions(cast);
match actions.remove(0) {
Action::Damage { construct: _, skill, values: _, colour } => {
assert_eq!(skill, Skill::Attack);
let mut actions = cast.actions();
match actions[0] {
Action::Damage { construct: _, values: _, colour } => {
assert_eq!(colour, Colour::Red);
},
_ => panic!("{:?}", actions),