2269 lines
81 KiB
Rust
2269 lines
81 KiB
Rust
use rand::{thread_rng, Rng};
|
|
use uuid::Uuid;
|
|
|
|
use util::{IntPct};
|
|
use construct::{Construct, ConstructEffect, EffectMeta, Stat};
|
|
use item::{Item};
|
|
|
|
use game::{Game};
|
|
|
|
pub fn resolution_steps(cast: &Cast, game: &mut Game) -> Resolutions {
|
|
let mut resolutions = vec![];
|
|
|
|
resolutions = pre_resolve(cast, game, resolutions);
|
|
|
|
return resolutions;
|
|
}
|
|
|
|
pub fn pre_resolve(cast: &Cast, game: &mut Game, mut resolutions: Resolutions) -> Resolutions {
|
|
let skill = cast.skill;
|
|
let source = game.construct_by_id(cast.source_construct_id).unwrap().clone();
|
|
let targets = game.get_targets(cast.skill, &source, cast.target_construct_id);
|
|
|
|
if skill.aoe() { // Send an aoe skill event for anims
|
|
resolutions.push(Resolution::new(&source,
|
|
&game.construct_by_id(cast.target_construct_id).unwrap().clone()).event(Event::Skill { skill }));
|
|
}
|
|
|
|
for target_id in targets {
|
|
// we clone the current state of the target and source
|
|
// so we can modify them during the resolution
|
|
// no more than 1 mutable ref allowed on game
|
|
let mut source = game.construct_by_id(cast.source_construct_id).unwrap().clone();
|
|
let mut target = game.construct_by_id(target_id).unwrap().clone();
|
|
|
|
// bail out on ticks that have been removed
|
|
if skill.is_tick() && target.effects.iter().find(|ce| match ce.tick {
|
|
Some(t) => t.id == cast.id,
|
|
None => false,
|
|
}).is_none() {
|
|
continue;
|
|
}
|
|
|
|
resolutions = resolve(cast.skill, &mut source, &mut target, resolutions);
|
|
|
|
// save the changes to the game
|
|
game.update_construct(&mut source);
|
|
game.update_construct(&mut target);
|
|
|
|
// do additional steps
|
|
resolutions = post_resolve(cast.skill, game, resolutions);
|
|
}
|
|
|
|
return resolutions;
|
|
}
|
|
|
|
pub fn resolve(skill: Skill, source: &mut Construct, target: &mut Construct, mut resolutions: Vec<Resolution>) -> Resolutions {
|
|
if let Some(disable) = source.disabled(skill) {
|
|
resolutions.push(Resolution::new(source, target).event(Event::Disable { disable, skill }));
|
|
return resolutions;
|
|
}
|
|
|
|
if target.is_ko() {
|
|
resolutions.push(Resolution::new(source, target).event(Event::TargetKo { skill }));
|
|
return resolutions;
|
|
}
|
|
|
|
if target.affected(Effect::Reflect) {
|
|
// guard against overflow
|
|
if source.affected(Effect::Reflect) {
|
|
return resolutions;
|
|
}
|
|
resolutions.push(Resolution::new(source, target).event(Event::Reflection { skill }));
|
|
return resolve(skill, target, source, resolutions);
|
|
}
|
|
|
|
if source.affected(Effect::Haste) {
|
|
match skill {
|
|
Skill::Attack |
|
|
Skill::SlayI |
|
|
Skill::ChaosI |
|
|
Skill::StrikeI => {
|
|
let amount = source.speed().pct(Skill::HasteStrike.multiplier());
|
|
target.deal_red_damage(Skill::HasteStrike, amount)
|
|
.into_iter()
|
|
.for_each(|e| resolutions.push(Resolution::new(source, target).event(e)));
|
|
},
|
|
_ => (),
|
|
}
|
|
}
|
|
|
|
if source.affected(Effect::Impurity) {
|
|
match skill {
|
|
Skill::BlastI |
|
|
Skill::ChaosI |
|
|
Skill::SiphonI => {
|
|
let amount = source.green_power().pct(Skill::ImpureBlast.multiplier());
|
|
target.deal_blue_damage(Skill::ImpureBlast, amount)
|
|
.into_iter()
|
|
.for_each(|e| resolutions.push(Resolution::new(source, target).event(e)));
|
|
},
|
|
_ => (),
|
|
}
|
|
}
|
|
|
|
// match self.category() == EffectCategory::Red {
|
|
// true => {
|
|
// if let Some(evasion) = target.evade(*self) {
|
|
// resolutions.push(evasion);
|
|
// return Event;
|
|
// }
|
|
// },
|
|
// false => (),
|
|
// }
|
|
|
|
resolutions = match skill {
|
|
Skill::AmplifyI |
|
|
Skill::AmplifyII |
|
|
Skill::AmplifyIII => amplify(source, target, resolutions, skill),
|
|
|
|
Skill::BanishI |
|
|
Skill::BanishII |
|
|
Skill::BanishIII => banish(source, target, resolutions, skill), // TODO prevent all actions
|
|
|
|
Skill::BlastI |
|
|
Skill::BlastII |
|
|
Skill::BlastIII => blast(source, target, resolutions, skill),
|
|
|
|
Skill::ChaosI |
|
|
Skill::ChaosII |
|
|
Skill::ChaosIII => chaos(source, target, resolutions, skill),
|
|
|
|
Skill::ClutchI |
|
|
Skill::ClutchII |
|
|
Skill::ClutchIII => clutch(source, target, resolutions, skill),
|
|
|
|
Skill::CorruptI |
|
|
Skill::CorruptII |
|
|
Skill::CorruptIII => corrupt(source, target, resolutions, skill),
|
|
Skill::CorruptionTickI |
|
|
Skill::CorruptionTickII |
|
|
Skill::CorruptionTickIII => corruption_tick(source, target, resolutions, skill),
|
|
|
|
Skill::CurseI |
|
|
Skill::CurseII |
|
|
Skill::CurseIII => curse(source, target, resolutions, skill),
|
|
|
|
Skill::DecayI |
|
|
Skill::DecayII |
|
|
Skill::DecayIII => decay(source, target, resolutions, skill), // dot
|
|
Skill::DecayTickI |
|
|
Skill::DecayTickII |
|
|
Skill::DecayTickIII => decay_tick(source, target, resolutions, skill), // dot
|
|
|
|
Skill::HasteI |
|
|
Skill::HasteII |
|
|
Skill::HasteIII => haste(source, target, resolutions, skill), // speed slow
|
|
|
|
Skill::HealI |
|
|
Skill::HealII |
|
|
Skill::HealIII => heal(source, target, resolutions, skill),
|
|
|
|
Skill::HexI |
|
|
Skill::HexII |
|
|
Skill::HexIII => hex(source, target, resolutions, skill),
|
|
|
|
Skill::HostilityI |
|
|
Skill::HostilityII |
|
|
Skill::HostilityIII => hostility(source, target, resolutions, skill),
|
|
|
|
Skill::ImpurityI |
|
|
Skill::ImpurityII |
|
|
Skill::ImpurityIII => impurity(source, target, resolutions, skill),
|
|
|
|
Skill::InvertI |
|
|
Skill::InvertII |
|
|
Skill::InvertIII => invert(source, target, resolutions, skill),
|
|
|
|
Skill::ParryI |
|
|
Skill::ParryII |
|
|
Skill::ParryIII => parry(source, target, resolutions, skill),
|
|
|
|
Skill::PurgeI |
|
|
Skill::PurgeII |
|
|
Skill::PurgeIII => purge(source, target, resolutions, skill), // dispel all buffs
|
|
|
|
Skill::PurifyI |
|
|
Skill::PurifyII |
|
|
Skill::PurifyIII => purify(source, target, resolutions, skill),
|
|
|
|
Skill::RechargeI |
|
|
Skill::RechargeII |
|
|
Skill::RechargeIII => recharge(source, target, resolutions, skill),
|
|
|
|
Skill::ReflectI |
|
|
Skill::ReflectII |
|
|
Skill::ReflectIII => reflect(source, target, resolutions, skill),
|
|
|
|
Skill::RuinI |
|
|
Skill::RuinII |
|
|
Skill::RuinIII => ruin(source, target, resolutions, skill),
|
|
|
|
Skill::ScatterI |
|
|
Skill::ScatterII |
|
|
Skill::ScatterIII => scatter(source, target, resolutions, skill), // target is immune to magic damage and fx
|
|
|
|
Skill::SilenceI |
|
|
Skill::SilenceII |
|
|
Skill::SilenceIII => silence(source, target, resolutions, skill), // target cannot cast spells
|
|
|
|
Skill::SiphonI |
|
|
Skill::SiphonII |
|
|
Skill::SiphonIII => siphon(source, target, resolutions, skill), // dot
|
|
Skill::SiphonTickI |
|
|
Skill::SiphonTickII |
|
|
Skill::SiphonTickIII => siphon_tick(source, target, resolutions, skill), // dot
|
|
|
|
Skill::SlayI |
|
|
Skill::SlayII |
|
|
Skill::SlayIII => slay(source, target, resolutions, skill), // hybrid dmg self heal
|
|
|
|
Skill::SleepI |
|
|
Skill::SleepII |
|
|
Skill::SleepIII => sleep(source, target, resolutions, skill), // heal stun
|
|
|
|
Skill::SnareI |
|
|
Skill::SnareII |
|
|
Skill::SnareIII => snare(source, target, resolutions, skill),
|
|
|
|
Skill::StrangleI |
|
|
Skill::StrangleII |
|
|
Skill::StrangleIII => strangle(source, target, resolutions, skill),
|
|
Skill::StrangleTickI |
|
|
Skill::StrangleTickII |
|
|
Skill::StrangleTickIII => strangle_tick(source, target, resolutions, skill),
|
|
|
|
Skill::StrikeI |
|
|
Skill::StrikeII |
|
|
Skill::StrikeIII => strike(source, target, resolutions, skill),
|
|
|
|
Skill::TauntI |
|
|
Skill::TauntII |
|
|
Skill::TauntIII => taunt(source, target, resolutions, skill),
|
|
|
|
Skill::ThrowI |
|
|
Skill::ThrowII |
|
|
Skill::ThrowIII => throw(source, target, resolutions, skill), // no damage stun, adds vulnerable
|
|
|
|
Skill::TriageI |
|
|
Skill::TriageII |
|
|
Skill::TriageIII => triage(source, target, resolutions, skill), // hot
|
|
|
|
Skill::TriageTickI |
|
|
Skill::TriageTickII |
|
|
Skill::TriageTickIII => triage_tick(source, target, resolutions, skill), // hot
|
|
|
|
// Base Skills
|
|
Skill::Attack => attack(source, target, resolutions, skill),
|
|
Skill::Block => block(source, target, resolutions, skill),
|
|
Skill::Buff => buff(source, target, resolutions, skill),
|
|
Skill::Debuff => debuff(source, target, resolutions, skill), // speed slow
|
|
Skill::Stun => stun(source, target, resolutions, skill),
|
|
|
|
//Triggered
|
|
Skill::CorruptionI |
|
|
Skill::CorruptionII |
|
|
Skill::CorruptionIII => panic!("should only trigger from corrupt hit"),
|
|
Skill::HasteStrike => panic!("should only trigger from haste"),
|
|
Skill::HatredI |
|
|
Skill::HatredII |
|
|
Skill::HatredIII => panic!("should only trigger from hatred"),
|
|
Skill::ImpureBlast => panic!("should only trigger from impurity"),
|
|
Skill::RiposteI |
|
|
Skill::RiposteII |
|
|
Skill::RiposteIII => panic!("should only trigger from parry"),
|
|
|
|
|
|
// Not used
|
|
// Skill::Injure => injure(source, target, resolutions, skill),
|
|
};
|
|
|
|
return resolutions;
|
|
}
|
|
|
|
fn post_resolve(_skill: Skill, game: &mut Game, mut resolutions: Resolutions) -> Resolutions {
|
|
for Resolution { source, target, event, stages: _ } in resolutions.clone() {
|
|
let mut source = game.construct_by_id(source.id).unwrap().clone();
|
|
let mut target = game.construct_by_id(target.id).unwrap().clone();
|
|
|
|
match event {
|
|
Event::Damage { amount, skill, mitigation: _, colour: _ } => {
|
|
if target.affected(Effect::Corrupt) {
|
|
let ConstructEffect { effect: _, duration: _, meta, tick: _ } = target.effects.iter()
|
|
.find(|e| e.effect == Effect::Corrupt).unwrap().clone();
|
|
match meta {
|
|
Some(EffectMeta::Skill(s)) => {
|
|
resolutions = corruption(&mut target, &mut source, resolutions, s);
|
|
},
|
|
_ => panic!("no corrupt skill"),
|
|
};
|
|
}
|
|
|
|
if target.affected(Effect::Hostility) {
|
|
let ConstructEffect { effect: _, duration: _, meta, tick: _ } = target.effects.iter()
|
|
.find(|e| e.effect == Effect::Hostility).unwrap().clone();
|
|
match meta {
|
|
Some(EffectMeta::Skill(s)) => {
|
|
resolutions = hatred(&mut target, &mut source, resolutions, skill, amount, s);
|
|
},
|
|
_ => panic!("no hatred skill"),
|
|
};
|
|
}
|
|
|
|
// beware that scatter doesn't cause any damage
|
|
// because then applying it will proc this
|
|
if target.affected(Effect::Scatter) {
|
|
resolutions = scatter_hit(&source, &target, resolutions, game, event)
|
|
}
|
|
},
|
|
|
|
Event::Immunity { skill: _, immunity } => match immunity.contains(&Effect::Parry) {
|
|
true => {
|
|
let ConstructEffect { effect: _, duration: _, meta, tick: _ } = target.effects.iter()
|
|
.find(|e| e.effect == Effect::Parry).unwrap().clone();
|
|
match meta {
|
|
Some(EffectMeta::Skill(s)) => {
|
|
resolutions = riposte(&mut target, &mut source, resolutions, s);
|
|
},
|
|
_ => panic!("no parry skill"),
|
|
};
|
|
|
|
},
|
|
false => (),
|
|
},
|
|
_ => (),
|
|
};
|
|
|
|
game.update_construct(&mut source);
|
|
game.update_construct(&mut target);
|
|
};
|
|
|
|
return resolutions;
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
|
|
pub struct Cast {
|
|
pub id: Uuid,
|
|
pub source_player_id: Uuid,
|
|
pub source_construct_id: Uuid,
|
|
pub target_construct_id: Uuid,
|
|
pub skill: Skill,
|
|
pub speed: u64,
|
|
}
|
|
|
|
impl Cast {
|
|
pub fn new(source_construct_id: Uuid, source_player_id: Uuid, target_construct_id: Uuid, skill: Skill) -> Cast {
|
|
return Cast {
|
|
id: Uuid::new_v4(),
|
|
source_construct_id,
|
|
source_player_id,
|
|
target_construct_id,
|
|
skill,
|
|
speed: 0,
|
|
};
|
|
}
|
|
|
|
pub fn new_tick(source: &mut Construct, target: &mut Construct, skill: Skill) -> Cast {
|
|
Cast {
|
|
id: Uuid::new_v4(),
|
|
source_construct_id: source.id,
|
|
source_player_id: source.account,
|
|
target_construct_id: target.id,
|
|
skill,
|
|
speed: 0,
|
|
}
|
|
}
|
|
|
|
pub fn used_cooldown(&self) -> bool {
|
|
return self.skill.base_cd().is_some();
|
|
}
|
|
}
|
|
|
|
pub type Disable = Vec<Effect>;
|
|
pub type Immunity = Vec<Effect>;
|
|
|
|
// used to show the progress of a construct
|
|
// while the resolutions are animating
|
|
#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)]
|
|
pub struct EventConstruct {
|
|
pub id: Uuid,
|
|
pub red: u64,
|
|
pub green: u64,
|
|
pub blue: u64,
|
|
}
|
|
|
|
#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)]
|
|
pub enum LogStages {
|
|
AllStages, // 0 Anim Anim Anim
|
|
StartEnd, // 1 Anim Anim Skip
|
|
StartPost, // 2 Anim Skip Anim
|
|
StartOnly, // 3 Anim Skip Skip
|
|
EndPost, // 4 Skip Anim Anim
|
|
EndOnly, // 5 Skip Anim Skip
|
|
PostOnly, // 6 Skip Skip Anim
|
|
None, // 7 Skip Skip Skip
|
|
}
|
|
|
|
#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)]
|
|
pub struct Resolution {
|
|
pub source: EventConstruct,
|
|
pub target: EventConstruct,
|
|
pub event: Event,
|
|
pub stages: u8,
|
|
}
|
|
|
|
impl Resolution {
|
|
fn new(source: &Construct, target: &Construct) -> Resolution {
|
|
Resolution {
|
|
source: EventConstruct {
|
|
id: source.id,
|
|
red: source.red_life(),
|
|
green: source.green_life(),
|
|
blue: source.blue_life(),
|
|
},
|
|
target: EventConstruct {
|
|
id: target.id,
|
|
red: target.red_life(),
|
|
green: target.green_life(),
|
|
blue: target.blue_life(),
|
|
},
|
|
event: Event::Incomplete,
|
|
stages: LogStages::AllStages as u8,
|
|
}
|
|
}
|
|
|
|
fn event(mut self, e: Event) -> Resolution {
|
|
self.event = e;
|
|
self
|
|
}
|
|
|
|
fn stages(mut self, s: LogStages) -> Resolution {
|
|
self.stages = s as u8;
|
|
self
|
|
}
|
|
}
|
|
|
|
|
|
#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)]
|
|
pub enum Event {
|
|
Disable { skill: Skill, disable: Disable },
|
|
Immunity { skill: Skill, immunity: Immunity },
|
|
Damage { skill: Skill, amount: u64, mitigation: u64, colour: Colour },
|
|
Healing { skill: Skill, amount: u64, overhealing: u64 },
|
|
Recharge { skill: Skill, red: u64, blue: u64 },
|
|
Inversion { skill: Skill },
|
|
Reflection { skill: Skill },
|
|
Skill { skill: Skill },
|
|
Effect { skill: Skill, effect: Effect, duration: u8, construct_effects: Vec<ConstructEffect> },
|
|
Removal { effect: Effect, construct_effects: Vec<ConstructEffect> },
|
|
TargetKo { skill: Skill },
|
|
// skill not necessary but makes it neater as all events are arrays in js
|
|
Ko (),
|
|
Forfeit (),
|
|
Incomplete,
|
|
// not used
|
|
Evasion { skill: Skill, evasion_rating: u64 },
|
|
}
|
|
|
|
type Resolutions = Vec<Resolution>;
|
|
pub type Cooldown = Option<u8>;
|
|
|
|
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
|
|
enum EffectCategory {
|
|
Buff,
|
|
Debuff,
|
|
|
|
Constant,
|
|
|
|
Ko,
|
|
}
|
|
|
|
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
|
|
pub enum Effect {
|
|
Amplify,
|
|
Banish,
|
|
Block,
|
|
Buff,
|
|
Clutch,
|
|
Curse,
|
|
Haste,
|
|
Hex,
|
|
Impurity,
|
|
Invert,
|
|
Parry,
|
|
Reflect,
|
|
Slow,
|
|
Snare,
|
|
Strangle,
|
|
Strangling,
|
|
Stun,
|
|
Taunt,
|
|
Vulnerable,
|
|
Silence,
|
|
Wither, // Reduce green dmg (healing) taken
|
|
|
|
// corrupt is the buff that applies
|
|
// corruption the dmg debuff
|
|
Corrupt,
|
|
Corruption,
|
|
|
|
// hostility is the buff
|
|
// hatred is the increased damage
|
|
Hostility,
|
|
Hatred,
|
|
|
|
// magic immunity
|
|
Scatter,
|
|
|
|
// effects over time
|
|
Triage,
|
|
Decay,
|
|
Regen,
|
|
Siphon,
|
|
|
|
Injured,
|
|
// Airborne,
|
|
// Boost
|
|
// Bleed,
|
|
// Blind,
|
|
// Deadly,
|
|
// Enslave,
|
|
// Fury,
|
|
// Injured,
|
|
// Leech,
|
|
// Mesmerise,
|
|
// Untouchable,
|
|
// SpeedSiphon,
|
|
// SpeedIncrease,
|
|
|
|
Ko,
|
|
}
|
|
|
|
impl Effect {
|
|
pub fn immune(&self, skill: Skill) -> bool {
|
|
match self {
|
|
Effect::Parry => match skill {
|
|
Skill::Attack => true,
|
|
Skill::Stun => true,
|
|
_ => skill.colours().contains(&Colour::Red)
|
|
},
|
|
Effect::Banish => true,
|
|
Effect::Clutch => [
|
|
Skill::Stun,
|
|
Skill::HexI,
|
|
Skill::HexII,
|
|
Skill::HexIII,
|
|
Skill::SilenceI,
|
|
Skill::SilenceII,
|
|
Skill::SilenceIII,
|
|
Skill::RuinI,
|
|
Skill::RuinII,
|
|
Skill::RuinIII,
|
|
Skill::StrangleI,
|
|
Skill::StrangleII,
|
|
Skill::StrangleIII,
|
|
Skill::SnareI,
|
|
Skill::SnareII,
|
|
Skill::SnareIII
|
|
].contains(&skill),
|
|
Effect::Injured => skill.colours().contains(&Colour::Green),
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn disables_skill(&self, skill: Skill) -> bool {
|
|
if skill.is_tick() {
|
|
return false;
|
|
}
|
|
|
|
match self {
|
|
Effect::Stun => true,
|
|
Effect::Hex => true,
|
|
Effect::Banish => true,
|
|
Effect::Strangle => true,
|
|
Effect::Strangling => match skill {
|
|
Skill::StrangleTickI |
|
|
Skill::StrangleTickII |
|
|
Skill::StrangleTickIII => false,
|
|
_ => true,
|
|
},
|
|
Effect::Silence => skill.colours().contains(&Colour::Blue),
|
|
Effect::Snare => skill.colours().contains(&Colour::Red),
|
|
Effect::Ko => skill.ko_castable(),
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn modifications(&self) -> Vec<Stat> {
|
|
match self {
|
|
Effect::Vulnerable => vec![Stat::RedDamageTaken],
|
|
Effect::Block => vec![Stat::RedDamageTaken],
|
|
Effect::Buff => vec![Stat::RedPower, Stat::Speed],
|
|
|
|
Effect::Hatred => vec![Stat::RedPower, Stat::BluePower],
|
|
|
|
Effect::Amplify => vec![Stat::RedPower, Stat::BluePower],
|
|
Effect::Curse => vec![Stat::BlueDamageTaken],
|
|
|
|
Effect::Impurity => vec![Stat::GreenPower],
|
|
Effect::Wither => vec![Stat::GreenDamageTaken],
|
|
|
|
Effect::Haste => vec![Stat::Speed],
|
|
Effect::Slow => vec![Stat::Speed],
|
|
|
|
Effect::Scatter => vec![Stat::BlueDamageTaken, Stat::GreenDamageTaken, Stat::RedDamageTaken],
|
|
|
|
_ => vec![],
|
|
}
|
|
}
|
|
|
|
pub fn apply(&self, value: u64, meta: Option<EffectMeta>) -> u64 {
|
|
match self {
|
|
Effect::Amplify |
|
|
Effect::Vulnerable |
|
|
Effect::Block |
|
|
Effect::Buff |
|
|
Effect::Curse |
|
|
Effect::Haste |
|
|
Effect::Slow |
|
|
Effect::Impurity |
|
|
Effect::Wither => value.pct(match meta {
|
|
Some(EffectMeta::Multiplier(d)) => d,
|
|
_ => 100,
|
|
}),
|
|
|
|
Effect::Scatter => value >> 1,
|
|
|
|
Effect::Hatred => value + match meta {
|
|
Some(EffectMeta::AddedDamage(d)) => d,
|
|
_ => panic!("hatred meta not damage"),
|
|
},
|
|
|
|
_ => {
|
|
info!("{:?} does not have a mod effect", self);
|
|
return value;
|
|
},
|
|
}
|
|
}
|
|
|
|
fn category(&self) -> EffectCategory {
|
|
match self {
|
|
// physical
|
|
Effect::Stun => EffectCategory::Debuff,
|
|
Effect::Block => EffectCategory::Buff,
|
|
Effect::Buff => EffectCategory::Buff,
|
|
Effect::Parry => EffectCategory::Buff,
|
|
Effect::Vulnerable => EffectCategory::Debuff,
|
|
Effect::Snare => EffectCategory::Debuff,
|
|
Effect::Clutch => EffectCategory::Buff,
|
|
Effect::Taunt => EffectCategory::Buff,
|
|
|
|
// magic
|
|
Effect::Hex => EffectCategory::Debuff,
|
|
Effect::Curse => EffectCategory::Debuff,
|
|
Effect::Banish => EffectCategory::Debuff, // todo randomise
|
|
// Effect::Banish => rng.gen_bool(0.5),
|
|
|
|
Effect::Slow => EffectCategory::Debuff,
|
|
Effect::Haste => EffectCategory::Buff,
|
|
Effect::Hatred => EffectCategory::Buff,
|
|
Effect::Reflect => EffectCategory::Buff,
|
|
Effect::Amplify => EffectCategory::Buff,
|
|
Effect::Silence => EffectCategory::Debuff,
|
|
Effect::Wither => EffectCategory::Debuff,
|
|
|
|
Effect::Corrupt => EffectCategory::Buff,
|
|
Effect::Corruption => EffectCategory::Debuff,
|
|
|
|
Effect::Hostility => EffectCategory::Buff,
|
|
|
|
// magic
|
|
Effect::Impurity => EffectCategory::Buff,
|
|
Effect::Scatter => EffectCategory::Buff,
|
|
Effect::Invert => EffectCategory::Buff,
|
|
|
|
// effects over time
|
|
Effect::Triage => EffectCategory::Buff,
|
|
Effect::Decay => EffectCategory::Debuff,
|
|
Effect::Regen => EffectCategory::Buff,
|
|
Effect::Siphon => EffectCategory::Debuff,
|
|
|
|
// can't be purged or purified
|
|
Effect::Strangle => EffectCategory::Constant,
|
|
Effect::Strangling => EffectCategory::Constant,
|
|
|
|
// not in game
|
|
Effect::Injured => EffectCategory::Debuff,
|
|
|
|
Effect::Ko => EffectCategory::Ko,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
|
|
pub enum Colour {
|
|
Red,
|
|
Blue,
|
|
Green,
|
|
}
|
|
|
|
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
|
|
pub enum Skill {
|
|
Attack,
|
|
Debuff,
|
|
Buff,
|
|
Block, // reduce damage
|
|
Stun,
|
|
|
|
// Boost -- sounds nice
|
|
// Evade, // actively evade
|
|
// Nightmare,
|
|
// Sleep,
|
|
AmplifyI,
|
|
AmplifyII,
|
|
AmplifyIII,
|
|
|
|
BanishI,
|
|
BanishII,
|
|
BanishIII,
|
|
|
|
BlastI,
|
|
BlastII,
|
|
BlastIII,
|
|
|
|
ChaosI,
|
|
ChaosII,
|
|
ChaosIII,
|
|
|
|
ClutchI,
|
|
ClutchII,
|
|
ClutchIII,
|
|
|
|
CorruptI,
|
|
CorruptII,
|
|
CorruptIII,
|
|
CorruptionI,
|
|
CorruptionII,
|
|
CorruptionIII,
|
|
CorruptionTickI,
|
|
CorruptionTickII,
|
|
CorruptionTickIII,
|
|
|
|
CurseI,
|
|
CurseII,
|
|
CurseIII,
|
|
|
|
DecayI, // dot
|
|
DecayII,
|
|
DecayIII,
|
|
DecayTickI, // dot
|
|
DecayTickII,
|
|
DecayTickIII,
|
|
|
|
HasteI,
|
|
HasteII,
|
|
HasteIII,
|
|
HasteStrike,
|
|
|
|
HealI,
|
|
HealII,
|
|
HealIII,
|
|
|
|
HexI,
|
|
HexII,
|
|
HexIII,
|
|
|
|
HatredI,
|
|
HatredII,
|
|
HatredIII,
|
|
HostilityI,
|
|
HostilityII,
|
|
HostilityIII,
|
|
ImpureBlast,
|
|
|
|
ImpurityI,
|
|
ImpurityII,
|
|
ImpurityIII,
|
|
// Injure,
|
|
|
|
InvertI,
|
|
InvertII,
|
|
InvertIII,
|
|
|
|
ParryI, // avoid all damage
|
|
ParryII,
|
|
ParryIII,
|
|
PurgeI,
|
|
PurgeII,
|
|
PurgeIII,
|
|
|
|
PurifyI,
|
|
PurifyII,
|
|
PurifyIII,
|
|
|
|
RechargeI,
|
|
RechargeII,
|
|
RechargeIII,
|
|
|
|
ReflectI,
|
|
ReflectII,
|
|
ReflectIII,
|
|
|
|
RiposteI,
|
|
RiposteII,
|
|
RiposteIII,
|
|
|
|
RuinI,
|
|
RuinII,
|
|
RuinIII,
|
|
|
|
ScatterI,
|
|
ScatterII,
|
|
ScatterIII,
|
|
|
|
SilenceI,
|
|
SilenceII,
|
|
SilenceIII,
|
|
|
|
SiphonI,
|
|
SiphonII,
|
|
SiphonIII,
|
|
SiphonTickI,
|
|
SiphonTickII,
|
|
SiphonTickIII,
|
|
|
|
SlayI,
|
|
SlayII,
|
|
SlayIII,
|
|
|
|
SleepI,
|
|
SleepII,
|
|
SleepIII,
|
|
|
|
SnareI,
|
|
SnareII,
|
|
SnareIII,
|
|
|
|
StrangleI,
|
|
StrangleII,
|
|
StrangleIII,
|
|
StrangleTickI,
|
|
StrangleTickII,
|
|
StrangleTickIII,
|
|
|
|
StrikeI,
|
|
StrikeII,
|
|
StrikeIII,
|
|
|
|
TauntI,
|
|
TauntII,
|
|
TauntIII,
|
|
|
|
ThrowI, // no damage stun, adds vulnerable
|
|
ThrowII,
|
|
ThrowIII,
|
|
|
|
TriageI, // hot
|
|
TriageII,
|
|
TriageIII,
|
|
|
|
TriageTickI,
|
|
TriageTickII,
|
|
TriageTickIII,
|
|
}
|
|
|
|
impl Skill {
|
|
pub fn multiplier(&self) -> u64 {
|
|
match self {
|
|
// Attack Base
|
|
Skill::Attack => 80, // Base
|
|
|
|
Skill::BlastI => 110, // BB
|
|
Skill::BlastII => 130, // BB
|
|
Skill::BlastIII => 150, // BB
|
|
|
|
Skill::ChaosI => 40, // BR
|
|
Skill::ChaosII => 50, // BR
|
|
Skill::ChaosIII => 60, // BR
|
|
|
|
Skill::HealI => 130, //GG
|
|
Skill::HealII => 160, //GG
|
|
Skill::HealIII => 200, //GG
|
|
Skill::SiphonTickI => 40, // GB
|
|
Skill::SiphonTickII => 70,
|
|
Skill::SiphonTickIII => 110,
|
|
|
|
Skill::SlayI => 70, // RG
|
|
Skill::SlayII => 90,
|
|
Skill::SlayIII => 120,
|
|
|
|
Skill::StrikeI => 90, //RR
|
|
Skill::StrikeII => 110,
|
|
Skill::StrikeIII => 140,
|
|
|
|
// Block Base
|
|
Skill::CorruptionTickI => 80,
|
|
Skill::CorruptionTickII => 100,
|
|
Skill::CorruptionTickIII => 130,
|
|
|
|
Skill::ParryI => 110,
|
|
Skill::ParryII => 145,
|
|
Skill::ParryIII => 200,
|
|
Skill::RiposteI => 70,
|
|
Skill::RiposteII => 95,
|
|
Skill::RiposteIII => 120,
|
|
|
|
Skill::PurifyI => 45, //Green dmg (heal)
|
|
Skill::PurifyII => 70,
|
|
Skill::PurifyIII => 105,
|
|
|
|
Skill::ReflectI => 45, //restore blue life (heal)
|
|
Skill::ReflectII => 70,
|
|
Skill::ReflectIII => 100,
|
|
|
|
Skill::RechargeI => 85, //restore red and blue life (heal)
|
|
Skill::RechargeII => 130,
|
|
Skill::RechargeIII => 200,
|
|
|
|
// Stun Base
|
|
Skill::SleepI => 240, //Green dmg (heal)
|
|
Skill::SleepII => 300,
|
|
Skill::SleepIII => 400,
|
|
Skill::StrangleTickI => 65,
|
|
Skill::StrangleTickII => 95,
|
|
Skill::StrangleTickIII => 140,
|
|
|
|
// Debuff Base
|
|
Skill::DecayTickI => 25,
|
|
Skill::DecayTickII => 45,
|
|
Skill::DecayTickIII => 70,
|
|
Skill::SilenceI => 55, // Deals more per blue skill on target
|
|
Skill::SilenceII => 80,
|
|
Skill::SilenceIII => 110,
|
|
Skill::SnareI => 40, // Deals more per red skill on target
|
|
Skill::SnareII => 65,
|
|
Skill::SnareIII => 100,
|
|
|
|
// Buff base
|
|
Skill::ImpureBlast => 25,
|
|
Skill::HasteStrike => 30,
|
|
Skill::ScatterI => 140,
|
|
Skill::ScatterII => 200,
|
|
Skill::ScatterIII => 300,
|
|
Skill::TauntI => 80,
|
|
Skill::TauntII => 110,
|
|
Skill::TauntIII => 150,
|
|
Skill::TriageTickI => 75,
|
|
Skill::TriageTickII => 110,
|
|
Skill::TriageTickIII => 140,
|
|
|
|
_ => 100,
|
|
}
|
|
}
|
|
|
|
pub fn effect(&self) -> Vec<ConstructEffect> {
|
|
match self {
|
|
// Modifiers
|
|
Skill::AmplifyI => vec![ConstructEffect {effect: Effect::Amplify, duration: 2,
|
|
meta: Some(EffectMeta::Multiplier(150)), tick: None}],
|
|
Skill::AmplifyII => vec![ConstructEffect {effect: Effect::Amplify, duration: 3,
|
|
meta: Some(EffectMeta::Multiplier(175)), tick: None}],
|
|
Skill::AmplifyIII => vec![ConstructEffect {effect: Effect::Amplify, duration: 4,
|
|
meta: Some(EffectMeta::Multiplier(200)), tick: None}],
|
|
|
|
Skill::BanishI => vec![ConstructEffect {effect: Effect::Banish, duration: 1,meta: None, tick: None}],
|
|
Skill::BanishII => vec![ConstructEffect {effect: Effect::Banish, duration: 2,meta: None, tick: None}],
|
|
Skill::BanishIII => vec![ConstructEffect {effect: Effect::Banish, duration: 3,meta: None, tick: None}],
|
|
Skill::Block => vec![ConstructEffect {effect: Effect::Block, duration: 1,
|
|
meta: Some(EffectMeta::Multiplier(50)), tick: None}],
|
|
Skill::Buff => vec![ConstructEffect {effect: Effect::Buff, duration: 2,
|
|
meta: Some(EffectMeta::Multiplier(125)), tick: None }],
|
|
|
|
Skill::CorruptI => vec![ConstructEffect {effect: Effect::Corrupt, duration: 2,
|
|
meta: Some(EffectMeta::Skill(Skill::CorruptionI)), tick: None}],
|
|
Skill::CorruptII => vec![ConstructEffect {effect: Effect::Corrupt, duration: 3,
|
|
meta: Some(EffectMeta::Skill(Skill::CorruptionII)), tick: None}],
|
|
Skill::CorruptIII => vec![ConstructEffect {effect: Effect::Corrupt, duration: 4,
|
|
meta: Some(EffectMeta::Skill(Skill::CorruptionIII)), tick: None}],
|
|
Skill::CorruptionI => vec![ConstructEffect {effect: Effect::Corruption, duration: 3,
|
|
meta: Some(EffectMeta::Skill(Skill::CorruptionTickI)), tick: None}],
|
|
Skill::CorruptionII => vec![ConstructEffect {effect: Effect::Corruption, duration: 4,
|
|
meta: Some(EffectMeta::Skill(Skill::CorruptionTickII)), tick: None}],
|
|
Skill::CorruptionIII => vec![ConstructEffect {effect: Effect::Corruption, duration: 5,
|
|
meta: Some(EffectMeta::Skill(Skill::CorruptionTickIII)), tick: None}],
|
|
|
|
Skill::ClutchI => vec![ConstructEffect {effect: Effect::Clutch, duration: 1, meta: None, tick: None }],
|
|
Skill::ClutchII => vec![ConstructEffect {effect: Effect::Clutch, duration: 2, meta: None, tick: None }],
|
|
Skill::ClutchIII => vec![ConstructEffect {effect: Effect::Clutch, duration: 3, meta: None, tick: None }],
|
|
|
|
Skill::CurseI => vec![ConstructEffect {effect: Effect::Curse, duration: 2,
|
|
meta: Some(EffectMeta::Multiplier(150)), tick: None}],
|
|
Skill::CurseII => vec![ConstructEffect {effect: Effect::Curse, duration: 2,
|
|
meta: Some(EffectMeta::Multiplier(200)), tick: None}],
|
|
Skill::CurseIII => vec![ConstructEffect {effect: Effect::Curse, duration: 3,
|
|
meta: Some(EffectMeta::Multiplier(250)), tick: None}],
|
|
|
|
Skill::Debuff => vec![ConstructEffect {effect: Effect::Slow, duration: 3,
|
|
meta: Some(EffectMeta::Multiplier(50)), tick: None }],
|
|
|
|
Skill::DecayI => vec![ConstructEffect {effect: Effect::Wither, duration: 3, meta: Some(EffectMeta::Multiplier(50)), tick: None },
|
|
ConstructEffect {effect: Effect::Decay, duration: 3,
|
|
meta: Some(EffectMeta::Skill(Skill::DecayTickI)), tick: None}],
|
|
Skill::DecayII => vec![ConstructEffect {effect: Effect::Wither, duration: 3, meta: Some(EffectMeta::Multiplier(35)), tick: None },
|
|
ConstructEffect {effect: Effect::Decay, duration: 3,
|
|
meta: Some(EffectMeta::Skill(Skill::DecayTickII)), tick: None}],
|
|
Skill::DecayIII => vec![ConstructEffect {effect: Effect::Wither, duration: 4, meta: Some(EffectMeta::Multiplier(20)), tick: None },
|
|
ConstructEffect {effect: Effect::Decay, duration: 4,
|
|
meta: Some(EffectMeta::Skill(Skill::DecayTickIII)), tick: None}],
|
|
|
|
Skill::HasteI => vec![ConstructEffect {effect: Effect::Haste, duration: 2,
|
|
meta: Some(EffectMeta::Multiplier(150)), tick: None }],
|
|
Skill::HasteII => vec![ConstructEffect {effect: Effect::Haste, duration: 3,
|
|
meta: Some(EffectMeta::Multiplier(175)), tick: None }],
|
|
Skill::HasteIII => vec![ConstructEffect {effect: Effect::Haste, duration: 4,
|
|
meta: Some(EffectMeta::Multiplier(225)), tick: None }],
|
|
Skill::HexI => vec![ConstructEffect {effect: Effect::Hex, duration: 2, meta: None, tick: None}],
|
|
Skill::HexII => vec![ConstructEffect {effect: Effect::Hex, duration: 3, meta: None, tick: None}],
|
|
Skill::HexIII => vec![ConstructEffect {effect: Effect::Hex, duration: 4, meta: None, tick: None}],
|
|
|
|
Skill::HostilityI => vec![ConstructEffect {effect: Effect::Hostility, duration: 2,
|
|
meta: Some(EffectMeta::Skill(Skill::HatredI)), tick: None}],
|
|
Skill::HostilityII => vec![ConstructEffect {effect: Effect::Hostility, duration: 3,
|
|
meta: Some(EffectMeta::Skill(Skill::HatredII)), tick: None}],
|
|
Skill::HostilityIII => vec![ConstructEffect {effect: Effect::Hostility, duration: 4,
|
|
meta: Some(EffectMeta::Skill(Skill::HatredIII)), tick: None}],
|
|
|
|
Skill::HatredI => vec![ConstructEffect {effect: Effect::Hatred, duration: 5, meta: None, tick: None}],
|
|
Skill::HatredII => vec![ConstructEffect {effect: Effect::Hatred, duration: 7, meta: None, tick: None}],
|
|
Skill::HatredIII => vec![ConstructEffect {effect: Effect::Hatred, duration: 9, meta: None, tick: None}],
|
|
|
|
Skill::ImpurityI => vec![ConstructEffect {effect: Effect::Impurity, duration: 2,
|
|
meta: Some(EffectMeta::Multiplier(150)), tick: None }],
|
|
Skill::ImpurityII => vec![ConstructEffect {effect: Effect::Impurity, duration: 3,
|
|
meta: Some(EffectMeta::Multiplier(175)), tick: None }],
|
|
Skill::ImpurityIII => vec![ConstructEffect {effect: Effect::Impurity, duration: 4,
|
|
meta: Some(EffectMeta::Multiplier(225)), tick: None }],
|
|
|
|
Skill::InvertI => vec![ConstructEffect {effect: Effect::Invert, duration: 2, meta: None, tick: None}],
|
|
Skill::InvertII => vec![ConstructEffect {effect: Effect::Invert, duration: 3, meta: None, tick: None}],
|
|
Skill::InvertIII => vec![ConstructEffect {effect: Effect::Invert, duration: 4, meta: None, tick: None}],
|
|
|
|
Skill::ParryI => vec![ConstructEffect {effect: Effect::Parry, duration: 2,
|
|
meta: Some(EffectMeta::Skill(Skill::RiposteI)), tick: None}],
|
|
Skill::ParryII => vec![ConstructEffect {effect: Effect::Parry, duration: 2,
|
|
meta: Some(EffectMeta::Skill(Skill::RiposteII)), tick: None}],
|
|
Skill::ParryIII => vec![ConstructEffect {effect: Effect::Parry, duration: 2,
|
|
meta: Some(EffectMeta::Skill(Skill::RiposteIII)), tick: None}],
|
|
|
|
Skill::ReflectI => vec![ConstructEffect {effect: Effect::Reflect, duration: 1, meta: None, tick: None }],
|
|
Skill::ReflectII => vec![ConstructEffect {effect: Effect::Reflect, duration: 2, meta: None, tick: None }],
|
|
Skill::ReflectIII => vec![ConstructEffect {effect: Effect::Reflect, duration: 3, meta: None, tick: None }],
|
|
|
|
Skill::ThrowI => vec![ConstructEffect {effect: Effect::Stun, duration: 1, meta: None, tick: None},
|
|
ConstructEffect {effect: Effect::Vulnerable, duration: 3, meta: Some(EffectMeta::Multiplier(150)), tick: None}],
|
|
Skill::ThrowII => vec![ConstructEffect {effect: Effect::Stun, duration: 1, meta: None, tick: None},
|
|
ConstructEffect {effect: Effect::Vulnerable, duration: 4, meta: Some(EffectMeta::Multiplier(200)), tick: None}],
|
|
Skill::ThrowIII => vec![ConstructEffect {effect: Effect::Stun, duration: 2, meta: None, tick: None},
|
|
ConstructEffect {effect: Effect::Vulnerable, duration: 4, meta: Some(EffectMeta::Multiplier(250)), tick: None}],
|
|
|
|
Skill::RuinI => vec![ConstructEffect {effect: Effect::Stun, duration: 1, meta: None, tick: None}],
|
|
Skill::RuinII => vec![ConstructEffect {effect: Effect::Stun, duration: 1, meta: None, tick: None}],
|
|
Skill::RuinIII => vec![ConstructEffect {effect: Effect::Stun, duration: 2, meta: None, tick: None}],
|
|
|
|
Skill::ScatterI => vec![ConstructEffect {effect: Effect::Scatter, duration: 2, meta: None, tick: None}],
|
|
Skill::ScatterII => vec![ConstructEffect {effect: Effect::Scatter, duration: 3, meta: None, tick: None}],
|
|
Skill::ScatterIII => vec![ConstructEffect {effect: Effect::Scatter, duration: 4, meta: None, tick: None}],
|
|
|
|
Skill::SilenceI => vec![ConstructEffect {effect: Effect::Silence, duration: 2, meta: None, tick: None}],
|
|
Skill::SilenceII => vec![ConstructEffect {effect: Effect::Silence, duration: 3, meta: None, tick: None}],
|
|
Skill::SilenceIII => vec![ConstructEffect {effect: Effect::Silence, duration: 4, meta: None, tick: None}],
|
|
|
|
Skill::SiphonI => vec![ConstructEffect {effect: Effect::Siphon, duration: 2,
|
|
meta: Some(EffectMeta::Skill(Skill::SiphonTickI)), tick: None}],
|
|
Skill::SiphonII => vec![ConstructEffect {effect: Effect::Siphon, duration: 3,
|
|
meta: Some(EffectMeta::Skill(Skill::SiphonTickII)), tick: None}],
|
|
Skill::SiphonIII => vec![ConstructEffect {effect: Effect::Siphon, duration: 4,
|
|
meta: Some(EffectMeta::Skill(Skill::SiphonTickIII)), tick: None}],
|
|
|
|
Skill::SleepI => vec![ConstructEffect {effect: Effect::Stun, duration: 2, meta: None, tick: None}],
|
|
Skill::SleepII => vec![ConstructEffect {effect: Effect::Stun, duration: 3, meta: None, tick: None}],
|
|
Skill::SleepIII => vec![ConstructEffect {effect: Effect::Stun, duration: 4, meta: None, tick: None}],
|
|
|
|
Skill::SnareI => vec![ConstructEffect {effect: Effect::Snare, duration: 2, meta: None, tick: None}],
|
|
Skill::SnareII => vec![ConstructEffect {effect: Effect::Snare, duration: 3, meta: None, tick: None}],
|
|
Skill::SnareIII => vec![ConstructEffect {effect: Effect::Snare, duration: 4, meta: None, tick: None}],
|
|
|
|
Skill::StrangleI => vec![ConstructEffect {effect: Effect::Strangle, duration: 2,
|
|
meta: Some(EffectMeta::Skill(Skill::StrangleTickI)), tick: None}],
|
|
Skill::StrangleII => vec![ConstructEffect {effect: Effect::Strangle, duration: 2,
|
|
meta: Some(EffectMeta::Skill(Skill::StrangleTickII)), tick: None}],
|
|
Skill::StrangleIII => vec![ConstructEffect {effect: Effect::Strangle, duration: 2,
|
|
meta: Some(EffectMeta::Skill(Skill::StrangleTickIII)), tick: None}],
|
|
Skill::Stun => vec![ConstructEffect {effect: Effect::Stun, duration: 2, meta: None, tick: None}],
|
|
|
|
Skill::TauntI => vec![ConstructEffect {effect: Effect::Taunt, duration: 2, meta: None, tick: None}],
|
|
Skill::TauntII => vec![ConstructEffect {effect: Effect::Taunt, duration: 3, meta: None, tick: None}],
|
|
Skill::TauntIII => vec![ConstructEffect {effect: Effect::Taunt, duration: 4, meta: None, tick: None}],
|
|
|
|
Skill::TriageI => vec![ConstructEffect {effect: Effect::Triage, duration: 2,
|
|
meta: Some(EffectMeta::Skill(Skill::TriageTickI)), tick: None}],
|
|
Skill::TriageII => vec![ConstructEffect {effect: Effect::Triage, duration: 3,
|
|
meta: Some(EffectMeta::Skill(Skill::TriageTickII)), tick: None}],
|
|
Skill::TriageIII => vec![ConstructEffect {effect: Effect::Triage, duration: 4,
|
|
meta: Some(EffectMeta::Skill(Skill::TriageTickIII)), tick: None}],
|
|
//Unused
|
|
// Skill::Injure => vec![ConstructEffect {effect: Effect::Injured, duration: 2, meta: None, tick: None }],
|
|
|
|
_ => {
|
|
panic!("{:?} no skill effect", self);
|
|
},
|
|
}
|
|
}
|
|
|
|
pub fn base_cd(&self) -> Cooldown {
|
|
match self {
|
|
Skill::Attack => None,
|
|
Skill::Debuff => Some(1),
|
|
Skill::Buff => None,
|
|
|
|
Skill::StrikeI => None,
|
|
Skill::StrikeII => None,
|
|
Skill::StrikeIII => None,
|
|
Skill::Block => None, // reduce damage
|
|
Skill::ParryI |
|
|
Skill::ParryII |
|
|
Skill::ParryIII => Some(2), // avoid all damage
|
|
|
|
Skill::SnareI => Some(2),
|
|
Skill::SnareII => Some(2),
|
|
Skill::SnareIII => Some(2),
|
|
Skill::Stun => Some(2),
|
|
|
|
Skill::HealI => None,
|
|
Skill::HealII => None,
|
|
Skill::HealIII => None,
|
|
|
|
Skill::TriageI => None, // hot
|
|
Skill::TriageII => None, // hot
|
|
Skill::TriageIII => None, // hot
|
|
|
|
Skill::ThrowI => Some(1), // no damage stun, adds vulnerable
|
|
Skill::ThrowII => Some(1),
|
|
Skill::ThrowIII => Some(1),
|
|
|
|
Skill::BlastI => None,
|
|
Skill::BlastII => None,
|
|
Skill::BlastIII => None,
|
|
|
|
Skill::ChaosI => None,
|
|
Skill::ChaosII => None,
|
|
Skill::ChaosIII => None,
|
|
|
|
Skill::AmplifyI => Some(1),
|
|
Skill::AmplifyII => Some(1),
|
|
Skill::AmplifyIII => Some(1),
|
|
Skill::ImpurityI |
|
|
Skill::ImpurityII |
|
|
Skill::ImpurityIII => Some(3),
|
|
|
|
Skill::InvertI => Some(2),
|
|
Skill::InvertII => Some(2),
|
|
Skill::InvertIII => Some(2),
|
|
Skill::DecayI => Some(1), // dot
|
|
Skill::DecayII => Some(1),
|
|
Skill::DecayIII => Some(1),
|
|
Skill::SiphonI |
|
|
Skill::SiphonII |
|
|
Skill::SiphonIII => None,
|
|
|
|
Skill::CurseI => Some(1),
|
|
Skill::CurseII => Some(1),
|
|
Skill::CurseIII => Some(1),
|
|
|
|
Skill::ScatterI => Some(2),
|
|
Skill::ScatterII => Some(2),
|
|
Skill::ScatterIII => Some(2),
|
|
|
|
Skill::SilenceI => Some(3),
|
|
Skill::SilenceII => Some(2),
|
|
Skill::SilenceIII => Some(2),
|
|
|
|
Skill::PurifyI => None,
|
|
Skill::PurifyII => None,
|
|
Skill::PurifyIII => None,
|
|
|
|
Skill::PurgeI => None,
|
|
Skill::PurgeII => None,
|
|
Skill::PurgeIII => None,
|
|
|
|
Skill::BanishI => Some(1),
|
|
Skill::BanishII => Some(1),
|
|
Skill::BanishIII => Some(1),
|
|
|
|
Skill::HexI => Some(1),
|
|
Skill::HexII => Some(2),
|
|
Skill::HexIII => Some(2),
|
|
|
|
Skill::HasteI => Some(2),
|
|
Skill::HasteII => Some(2),
|
|
Skill::HasteIII => Some(2),
|
|
|
|
Skill::ReflectI => Some(2),
|
|
Skill::ReflectII => Some(2),
|
|
Skill::ReflectIII => Some(2),
|
|
|
|
Skill::RechargeI => Some(2),
|
|
Skill::RechargeII => Some(2),
|
|
Skill::RechargeIII => Some(2),
|
|
|
|
Skill::RuinI => Some(3),
|
|
Skill::RuinII => Some(2),
|
|
Skill::RuinIII => Some(2),
|
|
|
|
Skill::SlayI => None,
|
|
Skill::SlayII => None,
|
|
Skill::SlayIII => None,
|
|
|
|
Skill::SleepI => Some(3),
|
|
Skill::SleepII => Some(3),
|
|
Skill::SleepIII => Some(3),
|
|
|
|
Skill::StrangleI => Some(2),
|
|
Skill::StrangleII => Some(2),
|
|
Skill::StrangleIII => Some(2),
|
|
|
|
Skill::ClutchI => Some(1),
|
|
Skill::ClutchII => Some(2),
|
|
Skill::ClutchIII => Some(3),
|
|
|
|
Skill::TauntI => Some(2),
|
|
Skill::TauntII => Some(2),
|
|
Skill::TauntIII => Some(2),
|
|
// Skill::Injure => Some(2),
|
|
|
|
Skill::CorruptI =>Some(1),
|
|
Skill::CorruptII =>Some(1),
|
|
Skill::CorruptIII =>Some(1),
|
|
|
|
|
|
Skill::HostilityI |
|
|
Skill::HostilityII |
|
|
Skill::HostilityIII => Some(1),
|
|
|
|
//-----------
|
|
// Never cast directly
|
|
//---------
|
|
// Trigger
|
|
Skill::ImpureBlast |
|
|
Skill::HasteStrike |
|
|
Skill::RiposteI |
|
|
Skill::RiposteII |
|
|
Skill::RiposteIII | // parry
|
|
Skill::CorruptionI |
|
|
Skill::CorruptionII |
|
|
Skill::CorruptionIII |
|
|
Skill::HatredI |
|
|
Skill::HatredII |
|
|
Skill::HatredIII |
|
|
// Ticks
|
|
Skill::CorruptionTickI |
|
|
Skill::CorruptionTickII |
|
|
Skill::CorruptionTickIII |
|
|
Skill::DecayTickI |
|
|
Skill::DecayTickII |
|
|
Skill::DecayTickIII |
|
|
Skill::SiphonTickI |
|
|
Skill::SiphonTickII |
|
|
Skill::SiphonTickIII |
|
|
Skill::StrangleTickI |
|
|
Skill::StrangleTickII |
|
|
Skill::StrangleTickIII |
|
|
Skill::TriageTickI |
|
|
Skill::TriageTickII |
|
|
Skill::TriageTickIII => None,
|
|
}
|
|
}
|
|
|
|
pub fn ko_castable(&self) -> bool {
|
|
match self {
|
|
Skill::CorruptionTickI |
|
|
Skill::CorruptionTickII |
|
|
Skill::CorruptionTickIII |
|
|
Skill::DecayTickI |
|
|
Skill::DecayTickII |
|
|
Skill::DecayTickIII |
|
|
Skill::SiphonTickI |
|
|
Skill::SiphonTickII |
|
|
Skill::SiphonTickIII |
|
|
|
|
Skill::TriageTickI |
|
|
Skill::TriageTickII |
|
|
Skill::TriageTickIII => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn is_tick(&self) -> bool {
|
|
match self {
|
|
Skill::CorruptionTickI |
|
|
Skill::CorruptionTickII |
|
|
Skill::CorruptionTickIII |
|
|
Skill::DecayTickI |
|
|
Skill::DecayTickII |
|
|
Skill::DecayTickIII |
|
|
Skill::SiphonTickI |
|
|
Skill::SiphonTickII |
|
|
Skill::SiphonTickIII |
|
|
Skill::StrangleTickI |
|
|
Skill::StrangleTickII |
|
|
Skill::StrangleTickIII |
|
|
|
|
Skill::TriageTickI |
|
|
Skill::TriageTickII |
|
|
Skill::TriageTickIII => true,
|
|
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn speed(&self) -> u64 {
|
|
match self {
|
|
Skill::StrikeI => Item::from(Skill::StrikeI).speed().pct(150),
|
|
Skill::StrikeII => Skill::StrikeI.speed(),
|
|
Skill::StrikeIII => Skill::StrikeI.speed(),
|
|
|
|
Skill::SiphonTickI |
|
|
Skill::SiphonTickII |
|
|
Skill::SiphonTickIII => Skill::SiphonI.speed(),
|
|
Skill::DecayTickI |
|
|
Skill::DecayTickII |
|
|
Skill::DecayTickIII => Skill::DecayI.speed(),
|
|
|
|
Skill::TriageTickI |
|
|
Skill::TriageTickII |
|
|
Skill::TriageTickIII => Skill::TriageI.speed(),
|
|
|
|
Skill::StrangleTickI |
|
|
Skill::StrangleTickII |
|
|
Skill::StrangleTickIII => Skill::StrangleI.speed(),
|
|
Skill::CorruptionTickI |
|
|
Skill::CorruptionTickII |
|
|
Skill::CorruptionTickIII => Skill::CorruptI.speed(),
|
|
|
|
_ => Item::from(*self).speed(),
|
|
}
|
|
}
|
|
|
|
pub fn aoe(&self) -> bool {
|
|
match self {
|
|
Skill::RuinI |
|
|
Skill::RuinII |
|
|
Skill::RuinIII => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn self_targeting(&self) -> bool {
|
|
match self {
|
|
Skill::Block |
|
|
Skill::CorruptI |
|
|
Skill::CorruptII |
|
|
Skill::CorruptIII |
|
|
Skill::ClutchI |
|
|
Skill::ClutchII |
|
|
Skill::ClutchIII |
|
|
Skill::ParryI |
|
|
Skill::ParryII |
|
|
Skill::ParryIII => true,
|
|
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn defensive(&self) -> bool {
|
|
let mut rng = thread_rng();
|
|
|
|
match self {
|
|
Skill::AmplifyI |
|
|
Skill::AmplifyII |
|
|
Skill::AmplifyIII |
|
|
Skill::Block |
|
|
Skill::ClutchI |
|
|
Skill::ClutchII |
|
|
Skill::ClutchIII |
|
|
Skill::CorruptI |
|
|
Skill::CorruptII |
|
|
Skill::CorruptIII |
|
|
Skill::HasteI |
|
|
Skill::HasteII |
|
|
Skill::HasteIII |
|
|
Skill::HealI |
|
|
Skill::HealII |
|
|
Skill::HealIII |
|
|
Skill::HostilityI |
|
|
Skill::HostilityII |
|
|
Skill::HostilityIII |
|
|
Skill::InvertI |
|
|
Skill::InvertII |
|
|
Skill::InvertIII |
|
|
Skill::ParryI |
|
|
Skill::ParryII |
|
|
Skill::ParryIII |
|
|
Skill::PurifyI |
|
|
Skill::PurifyII |
|
|
Skill::PurifyIII |
|
|
Skill::RechargeI |
|
|
Skill::RechargeII |
|
|
Skill::RechargeIII |
|
|
Skill::ReflectI |
|
|
Skill::ReflectII |
|
|
Skill::ReflectIII |
|
|
Skill::ScatterI |
|
|
Skill::ScatterII |
|
|
Skill::ScatterIII |
|
|
Skill::TriageI |
|
|
Skill::TriageII |
|
|
Skill::TriageIII => true,
|
|
|
|
Skill::BanishI |
|
|
Skill::BanishII |
|
|
Skill::BanishIII => rng.gen_bool(0.5),
|
|
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
fn components(&self) -> Vec<Item> {
|
|
let mut components = Item::from(*self).components();
|
|
components.sort_unstable();
|
|
return components;
|
|
}
|
|
|
|
fn colours(&self) -> Vec<Colour> {
|
|
let mut components = self.components();
|
|
let colour_items = [Item::Red, Item::Green, Item::Blue];
|
|
components.dedup();
|
|
return components.iter()
|
|
.filter(|i| colour_items.contains(i))
|
|
.map(|i| i.into_colour())
|
|
.collect::<Vec<Colour>>();
|
|
}
|
|
|
|
fn base(&self) -> Skill {
|
|
let bases = [Item::Attack, Item::Stun, Item::Buff, Item::Debuff, Item::Block];
|
|
match self.components()
|
|
.iter()
|
|
.find(|i| bases.contains(i)) {
|
|
Some(i) => i.into_skill().unwrap(),
|
|
None => panic!("{:?} has no base item", self),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn attack(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
let amount = source.red_power().pct(skill.multiplier());
|
|
target.deal_red_damage(skill, amount)
|
|
.into_iter()
|
|
.for_each(|e| results.push(Resolution::new(source, target).event(e)));
|
|
|
|
return results;
|
|
}
|
|
|
|
fn strike(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
let amount = source.red_power().pct(skill.multiplier());
|
|
target.deal_red_damage(skill, amount)
|
|
.into_iter()
|
|
.for_each(|e| results.push(Resolution::new(source, target).event(e)));
|
|
|
|
return results;
|
|
}
|
|
|
|
/*fn injure(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
let amount = source.red_power().pct(skill.multiplier());
|
|
target.deal_red_damage(skill, amount)
|
|
.into_iter()
|
|
.for_each(|e| results.push(Resolution::new(source, target).event(e)));
|
|
|
|
skill.effect().into_iter()
|
|
.for_each(|e| (results.push(Resolution::new(source, target).event(target.add_effect(skill, e)))));
|
|
|
|
return results;
|
|
}
|
|
*/
|
|
fn stun(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
skill.effect().into_iter()
|
|
.for_each(|e| (results.push(Resolution::new(source, target).event(target.add_effect(skill, e)))));
|
|
|
|
return results;
|
|
}
|
|
|
|
fn sleep(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
skill.effect().into_iter()
|
|
.for_each(|e| (results.push(Resolution::new(source, target).event(target.add_effect(skill, e)))));
|
|
|
|
let amount = source.green_power().pct(skill.multiplier());
|
|
target.deal_green_damage(skill, amount)
|
|
.into_iter()
|
|
.for_each(|e| results.push(Resolution::new(source, target).event(e).stages(LogStages::PostOnly)));
|
|
|
|
return results;
|
|
}
|
|
|
|
fn clutch(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
skill.effect().into_iter()
|
|
.for_each(|e| (results.push(Resolution::new(source, target).event(target.add_effect(skill, e)))));
|
|
return results;
|
|
}
|
|
|
|
fn taunt(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
let red_amount = source.red_power().pct(skill.multiplier());
|
|
results.push(Resolution::new(source, target).event(target.recharge(skill, red_amount, 0)));
|
|
|
|
let taunt = skill.effect()[0];
|
|
results.push(Resolution::new(source, target).event(target.add_effect(skill, taunt)).stages(LogStages::PostOnly));
|
|
return results;
|
|
}
|
|
|
|
fn throw(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
let stun = skill.effect()[0];
|
|
results.push(Resolution::new(source, target).event(target.add_effect(skill, stun)));
|
|
let vuln = skill.effect()[1];
|
|
results.push(Resolution::new(source, target).event(target.add_effect(skill, vuln)).stages(LogStages::PostOnly));
|
|
|
|
return results;
|
|
}
|
|
|
|
fn strangle(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
let ConstructEffect { effect, duration, meta, tick: _ } = skill.effect()[0];
|
|
let tick_skill = match meta {
|
|
Some(EffectMeta::Skill(s)) => s,
|
|
_ => panic!("no strangle tick skill"),
|
|
};
|
|
let strangle = ConstructEffect::new(effect, duration).set_tick(Cast::new_tick(source, target, tick_skill));
|
|
results.push(Resolution::new(source, target).event(target.add_effect(skill, strangle)));
|
|
|
|
let attacker_strangle = ConstructEffect::new(Effect::Strangling, duration);
|
|
results.push(Resolution::new(source, source).event(source.add_effect(skill, attacker_strangle)).stages(LogStages::PostOnly));
|
|
return strangle_tick(source, target, results, tick_skill);
|
|
}
|
|
|
|
fn strangle_tick(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
let amount = source.red_power().pct(skill.multiplier());
|
|
target.deal_red_damage(skill, amount)
|
|
.into_iter()
|
|
.for_each(|e| results.push(Resolution::new(source, target).event(e).stages(LogStages::EndPost)));
|
|
|
|
// remove immunity if target ko
|
|
if target.is_ko() && !source.is_ko() {
|
|
if let Some(i) = source.effects
|
|
.iter()
|
|
.position(|e| e.effect == Effect::Strangling) {
|
|
source.effects.remove(i);
|
|
results.push(Resolution::new(source, source)
|
|
.event(Event::Removal { effect: Effect::Strangling, construct_effects: target.effects.clone() })
|
|
.stages(LogStages::PostOnly));
|
|
}
|
|
else {
|
|
error!("{:?}", results);
|
|
println!("{:?}", results);
|
|
panic!("no strangling on source");
|
|
}
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
fn block(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
results.push(Resolution::new(source, target)
|
|
.event(target.add_effect(skill, skill.effect()[0]))
|
|
.stages(LogStages::StartEnd));
|
|
return results;
|
|
}
|
|
|
|
fn buff(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
results.push(Resolution::new(source, target)
|
|
.event(target.add_effect(skill, skill.effect()[0])));
|
|
return results;
|
|
}
|
|
|
|
fn parry(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
let red_amount = source.red_power().pct(skill.multiplier());
|
|
results.push(Resolution::new(source, target)
|
|
.event(target.recharge(skill, red_amount, 0))
|
|
.stages(LogStages::StartEnd));
|
|
|
|
results.push(Resolution::new(source, target)
|
|
.event(target.add_effect(skill, skill.effect()[0]))
|
|
.stages(LogStages::PostOnly));
|
|
|
|
return results;
|
|
}
|
|
|
|
fn riposte(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
let amount = source.red_power().pct(skill.multiplier());
|
|
target.deal_red_damage(skill, amount)
|
|
.into_iter()
|
|
.for_each(|e| results.push(Resolution::new(source, target).event(e).stages(LogStages::StartPost)));
|
|
|
|
return results;
|
|
}
|
|
|
|
fn snare(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
skill.effect().into_iter()
|
|
.for_each(|e| (results.push(Resolution::new(source, target).event(target.add_effect(skill, e)))));
|
|
|
|
let s_multi = target.skills
|
|
.iter()
|
|
.fold(100, |acc, cs| match cs.skill.colours().contains(&Colour::Red) {
|
|
true => acc + 35,
|
|
false => acc,
|
|
});
|
|
|
|
let amount = source.red_power().pct(skill.multiplier()).pct(s_multi);
|
|
target.deal_red_damage(skill, amount)
|
|
.into_iter()
|
|
.for_each(|e| results.push(Resolution::new(source, target).event(e).stages(LogStages::PostOnly)));
|
|
|
|
|
|
return results;
|
|
}
|
|
|
|
fn slay(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
let amount = source.red_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: _ } => {
|
|
results.push(Resolution::new(source, target).event(e));
|
|
let heal = source.deal_green_damage(skill, amount);
|
|
for h in heal {
|
|
results.push(Resolution::new(source, source).event(h).stages(LogStages::PostOnly));
|
|
};
|
|
},
|
|
_ => results.push(Resolution::new(source, target).event(e)),
|
|
}
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
fn heal(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
let amount = source.green_power().pct(skill.multiplier());
|
|
target.deal_green_damage(skill, amount)
|
|
.into_iter()
|
|
.for_each(|e| results.push(Resolution::new(source, target).event(e)));
|
|
return results;
|
|
}
|
|
|
|
fn triage(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
let ConstructEffect { effect, duration, meta, tick: _ } = skill.effect()[0];
|
|
let tick_skill = match meta {
|
|
Some(EffectMeta::Skill(s)) => s,
|
|
_ => panic!("no triage tick skill"),
|
|
};
|
|
let triage = ConstructEffect::new(effect, duration).set_tick(Cast::new_tick(source, target, tick_skill));
|
|
results.push(Resolution::new(source, target).event(target.add_effect(skill, triage)));
|
|
return triage_tick(source, target, results, tick_skill);
|
|
}
|
|
|
|
fn triage_tick(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
let amount = source.green_power().pct(skill.multiplier());
|
|
target.deal_green_damage(skill, amount)
|
|
.into_iter()
|
|
.for_each(|e| results.push(Resolution::new(source, target).event(e).stages(LogStages::EndPost)));
|
|
return results;
|
|
}
|
|
|
|
fn chaos(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
let mut rng = thread_rng();
|
|
let b_rng: u64 = rng.gen_range(100, 130);
|
|
let amount = source.blue_power().pct(skill.multiplier()).pct(b_rng);
|
|
target.deal_blue_damage(skill, amount)
|
|
.into_iter()
|
|
.for_each(|e| results.push(Resolution::new(source, target).event(e)));
|
|
let r_rng: u64 = rng.gen_range(100, 130);
|
|
let amount = source.red_power().pct(skill.multiplier()).pct(r_rng);
|
|
target.deal_red_damage(skill, amount)
|
|
.into_iter()
|
|
.for_each(|e| results.push(Resolution::new(source, target).event(e).stages(LogStages::PostOnly)));
|
|
return results;
|
|
}
|
|
|
|
fn blast(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
let amount = source.blue_power().pct(skill.multiplier());
|
|
target.deal_blue_damage(skill, amount)
|
|
.into_iter()
|
|
.for_each(|e| results.push(Resolution::new(source, target).event(e)));
|
|
return results;
|
|
}
|
|
|
|
fn amplify(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
results.push(Resolution::new(source, target).event(target.add_effect(skill, skill.effect()[0])));
|
|
return results;;
|
|
}
|
|
|
|
fn haste(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
results.push(Resolution::new(source, target).event(target.add_effect(skill, skill.effect()[0])));
|
|
return results;;
|
|
}
|
|
|
|
fn debuff(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
results.push(Resolution::new(source, target).event(target.add_effect(skill, skill.effect()[0])));
|
|
return results;;
|
|
}
|
|
|
|
fn decay(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
|
|
let wither = skill.effect()[0];
|
|
results.push(Resolution::new(source, target).event(target.add_effect(skill, wither)));
|
|
|
|
let ConstructEffect { effect, duration, meta, tick: _ } = skill.effect()[1];
|
|
let tick_skill = match meta {
|
|
Some(EffectMeta::Skill(s)) => s,
|
|
_ => panic!("no decay tick skill"),
|
|
};
|
|
let decay = ConstructEffect::new(effect, duration).set_tick(Cast::new_tick(source, target, tick_skill));
|
|
results.push(Resolution::new(source, target)
|
|
.event(target.add_effect(skill, decay))
|
|
.stages(LogStages::PostOnly));
|
|
|
|
return decay_tick(source, target, results, tick_skill);
|
|
}
|
|
|
|
fn decay_tick(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
let amount = source.blue_power().pct(skill.multiplier());
|
|
target.deal_blue_damage(skill, amount)
|
|
.into_iter()
|
|
.for_each(|e| results.push(Resolution::new(source, target).event(e).stages(LogStages::EndPost)));
|
|
return results;
|
|
}
|
|
|
|
// corrupt is the buff effect
|
|
// when attacked it runs corruption and applies a debuff
|
|
fn corrupt(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
let corrupt = skill.effect()[0];
|
|
results.push(Resolution::new(source, target).event(target.add_effect(skill, corrupt)));
|
|
return results;;
|
|
}
|
|
|
|
fn corruption(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
|
|
let ConstructEffect { effect, duration, meta, tick: _ } = skill.effect()[0];
|
|
let tick_skill = match meta {
|
|
Some(EffectMeta::Skill(s)) => s,
|
|
_ => panic!("no corruption tick skill"),
|
|
};
|
|
let corruption = ConstructEffect::new(effect, duration).set_tick(Cast::new_tick(source, target, tick_skill));
|
|
results.push(Resolution::new(source, target)
|
|
.event(target.add_effect(skill, corruption))
|
|
.stages(LogStages::StartPost));
|
|
return corruption_tick(source, target, results, tick_skill);
|
|
}
|
|
|
|
fn corruption_tick(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
let amount = source.blue_power().pct(skill.multiplier());
|
|
target.deal_blue_damage(skill, amount)
|
|
.into_iter()
|
|
.for_each(|e| results.push(Resolution::new(source, target).event(e).stages(LogStages::EndPost)));
|
|
return results;
|
|
}
|
|
|
|
fn ruin(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
results.push(Resolution::new(source, target)
|
|
.event(target.add_effect(skill, skill.effect()[0]))
|
|
.stages(LogStages::PostOnly));
|
|
return results;;
|
|
}
|
|
|
|
|
|
fn hex(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
results.push(Resolution::new(source, target).event(target.add_effect(skill, skill.effect()[0])));
|
|
return results;;
|
|
}
|
|
|
|
fn hostility(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
results.push(Resolution::new(source, target).event(target.add_effect(skill, skill.effect()[0])));
|
|
return results;;
|
|
}
|
|
|
|
fn hatred(source: &mut Construct, target: &mut Construct, mut results: Resolutions, reflect_skill: Skill, amount: u64, skill: Skill) -> Resolutions {
|
|
let hatred = skill.effect()[0].set_meta(EffectMeta::AddedDamage(amount));
|
|
results.push(Resolution::new(source, target)
|
|
.event(target.add_effect(reflect_skill, hatred))
|
|
.stages(LogStages::PostOnly));
|
|
return results;;
|
|
}
|
|
|
|
fn curse(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
results.push(Resolution::new(source, target).event(target.add_effect(skill, skill.effect()[0])));
|
|
return results;;
|
|
}
|
|
|
|
fn impurity(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
results.push(Resolution::new(source, target).event(target.add_effect(skill, skill.effect()[0])));
|
|
return results;;
|
|
}
|
|
|
|
fn invert(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
results.push(Resolution::new(source, target).event(target.add_effect(skill, skill.effect()[0])));
|
|
return results;;
|
|
}
|
|
|
|
fn reflect(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
results.push(Resolution::new(source, target).event(target.add_effect(skill, skill.effect()[0])));
|
|
|
|
let blue_amount = source.blue_power().pct(skill.multiplier());
|
|
results.push(Resolution::new(source, target)
|
|
.event(target.recharge(skill, 0, blue_amount))
|
|
.stages(LogStages::PostOnly));
|
|
|
|
return results;;
|
|
}
|
|
|
|
fn recharge(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
let red_amount = source.red_power().pct(skill.multiplier());
|
|
let blue_amount = source.blue_power().pct(skill.multiplier());
|
|
|
|
results.push(Resolution::new(source, target).event(target.recharge(skill, red_amount, blue_amount)));
|
|
return results;
|
|
}
|
|
|
|
fn siphon(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
let ConstructEffect { effect, duration, meta, tick: _ } = skill.effect()[0];
|
|
let tick_skill = match meta {
|
|
Some(EffectMeta::Skill(s)) => s,
|
|
_ => panic!("no siphon tick skill"),
|
|
};
|
|
let siphon = ConstructEffect::new(effect, duration).set_tick(Cast::new_tick(source, target, tick_skill));
|
|
results.push(Resolution::new(source, target).event(target.add_effect(skill, siphon)));
|
|
|
|
return siphon_tick(source, target, results, tick_skill);
|
|
}
|
|
|
|
fn siphon_tick(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
let amount = source.blue_power().pct(skill.multiplier());
|
|
let siphon_events = target.deal_blue_damage(skill, amount);
|
|
|
|
for e in siphon_events {
|
|
match e {
|
|
Event::Damage { amount, mitigation: _, colour: _, skill: _ } => {
|
|
results.push(Resolution::new(source, target).event(e).stages(LogStages::EndPost));
|
|
let heal = source.deal_green_damage(skill, amount);
|
|
for h in heal {
|
|
results.push(Resolution::new(source, source).event(h).stages(LogStages::PostOnly));
|
|
};
|
|
},
|
|
_ => results.push(Resolution::new(source, target).event(e).stages(LogStages::EndPost)),
|
|
}
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
fn scatter(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
let blue_amount = source.blue_power().pct(skill.multiplier());
|
|
results.push(Resolution::new(source, target).event(target.recharge(skill, 0, blue_amount)));
|
|
|
|
let scatter = skill.effect()[0].set_meta(EffectMeta::ScatterTarget(target.id));
|
|
results.push(Resolution::new(source, target).event(source.add_effect(skill, scatter)).stages(LogStages::PostOnly));
|
|
|
|
return results;
|
|
}
|
|
|
|
fn scatter_hit(source: &Construct, target: &Construct, mut results: Resolutions, game: &mut Game, event: Event) -> Resolutions {
|
|
match event {
|
|
Event::Damage { amount, skill, mitigation: _, colour } => {
|
|
let scatter = target.effects.iter().find(|e| e.effect == Effect::Scatter).unwrap();
|
|
|
|
if let Some(EffectMeta::ScatterTarget(scatter_target_id)) = scatter.meta {
|
|
let mut scatter_target = game.construct_by_id(scatter_target_id).unwrap();
|
|
|
|
let res = match colour {
|
|
Colour::Red => scatter_target.deal_red_damage(skill, amount),
|
|
Colour::Blue => scatter_target.deal_blue_damage(skill, amount),
|
|
Colour::Green => scatter_target.deal_green_damage(skill, amount),
|
|
};
|
|
|
|
results.push(Resolution::new(target, scatter_target).event(Event::Skill { skill: Skill::ScatterI }));
|
|
res.into_iter().for_each(|e| results.push(Resolution::new(&source, &scatter_target)
|
|
.event(e).stages(LogStages::EndPost)));
|
|
} else {
|
|
panic!("not a scatter target {:?}", scatter);
|
|
}
|
|
|
|
return results;
|
|
},
|
|
_ => panic!("{:?} scatter hit not damage event", event),
|
|
}
|
|
}
|
|
|
|
fn silence(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
results.push(Resolution::new(source, target).event(target.add_effect(skill, skill.effect()[0])));
|
|
|
|
let s_multi = target.skills
|
|
.iter()
|
|
.fold(100, |acc, cs| match cs.skill.colours().contains(&Colour::Blue) {
|
|
true => acc + 45,
|
|
false => acc,
|
|
});
|
|
|
|
let amount = source.blue_power().pct(skill.multiplier()).pct(s_multi);
|
|
target.deal_blue_damage(skill, amount)
|
|
.into_iter()
|
|
.for_each(|e| results.push(Resolution::new(source, target).event(e).stages(LogStages::PostOnly)));
|
|
|
|
return results;
|
|
}
|
|
|
|
fn purge(source: &mut Construct, target: &mut Construct, mut results: Resolutions, _skill: Skill) -> Resolutions {
|
|
while let Some(i) = target.effects
|
|
.iter()
|
|
.position(|ce| ce.effect.category() == EffectCategory::Buff) {
|
|
let ce = target.effects.remove(i);
|
|
results.push(Resolution::new(source, target)
|
|
.event(Event::Removal { effect: ce.effect, construct_effects: target.effects.clone() }));
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
fn purify(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
results.push(Resolution::new(source, target).event(Event::Skill { skill }));
|
|
let amount = source.green_power().pct(skill.multiplier());
|
|
while let Some(i) = target.effects
|
|
.iter()
|
|
.position(|ce| ce.effect.category() == EffectCategory::Debuff) {
|
|
let ce = target.effects.remove(i);
|
|
results.push(Resolution::new(source, target)
|
|
.event(Event::Removal { effect: ce.effect, construct_effects: target.effects.clone() })
|
|
.stages(LogStages::PostOnly));
|
|
target.deal_green_damage(skill, amount)
|
|
.into_iter()
|
|
.for_each(|e| results.push(Resolution::new(source, target).event(e).stages(LogStages::PostOnly)));
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
fn banish(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
results.push(Resolution::new(source, target).event(target.add_effect(skill, skill.effect()[0])));
|
|
return results;
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use skill::*;
|
|
|
|
#[test]
|
|
fn heal_test() {
|
|
let mut x = Construct::new()
|
|
.named(&"muji".to_string())
|
|
.learn(Skill::HealI);
|
|
|
|
let mut y = Construct::new()
|
|
.named(&"camel".to_string())
|
|
.learn(Skill::HealI);
|
|
|
|
x.deal_red_damage(Skill::Attack, 5);
|
|
|
|
heal(&mut y, &mut x, vec![], Skill::HealI);
|
|
}
|
|
|
|
#[test]
|
|
fn decay_test() {
|
|
let mut x = Construct::new()
|
|
.named(&"muji".to_string());
|
|
|
|
let mut y = Construct::new()
|
|
.named(&"camel".to_string());
|
|
|
|
decay(&mut x, &mut y, vec![], Skill::DecayI);
|
|
|
|
assert!(y.effects.iter().any(|e| e.effect == Effect::Decay));
|
|
|
|
y.reduce_effect_durations();
|
|
let _decay = y.effects.iter().find(|e| e.effect == Effect::Decay);
|
|
// assert!(y.green_life() == y.green_life().saturating_sub(decay.unwrap().tick.unwrap().amount));
|
|
}
|
|
|
|
#[test]
|
|
fn block_test() {
|
|
let mut x = Construct::new()
|
|
.named(&"muji".to_string());
|
|
|
|
let mut y = Construct::new()
|
|
.named(&"camel".to_string());
|
|
|
|
// ensure it doesn't have 0 pd
|
|
x.red_power.force(100);
|
|
y.green_life.force(500);
|
|
|
|
block(&mut y.clone(), &mut y, vec![], Skill::Block);
|
|
assert!(y.effects.iter().any(|e| e.effect == Effect::Block));
|
|
|
|
let mut results = attack(&mut x, &mut y, vec![], Skill::Attack);
|
|
|
|
let Resolution { source: _, target: _, event, stages: _ } = results.remove(0);
|
|
match event {
|
|
Event::Damage { amount, mitigation: _, colour: _, skill: _ } =>
|
|
assert!(amount < x.red_power().pct(Skill::Attack.multiplier())),
|
|
_ => panic!("not damage"),
|
|
};
|
|
}
|
|
|
|
#[test]
|
|
fn clutch_test() {
|
|
let mut x = Construct::new()
|
|
.named(&"muji".to_string());
|
|
|
|
let mut y = Construct::new()
|
|
.named(&"camel".to_string());
|
|
|
|
x.red_power.force(10000000000000); // multiplication of int max will cause overflow
|
|
|
|
clutch(&mut y.clone(), &mut y, vec![], Skill::ClutchI);
|
|
assert!(y.affected(Effect::Clutch));
|
|
|
|
let mut results = hex(&mut x, &mut y, vec![], Skill::HexI);
|
|
let Resolution { source: _, target: _, event, stages: _ } = results.remove(0);
|
|
match event {
|
|
Event::Immunity { skill: _, immunity } => assert!(immunity.contains(&Effect::Clutch)),
|
|
_ => panic!("not immune cluthc"),
|
|
};
|
|
|
|
let mut results = attack(&mut x, &mut y, vec![], Skill::Attack);
|
|
assert!(y.green_life() == 1);
|
|
|
|
let Resolution { source: _, target: _, event, stages: _ } = results.remove(0);
|
|
match event {
|
|
Event::Damage { amount, mitigation: _, colour: _, skill: _ } => assert_eq!(amount, 1023),
|
|
_ => panic!("not damage"),
|
|
};
|
|
}
|
|
|
|
/* #[test]
|
|
fn injure_test() {
|
|
let mut x = Construct::new()
|
|
.named(&"muji".to_string());
|
|
|
|
let mut y = Construct::new()
|
|
.named(&"camel".to_string());
|
|
|
|
resolve(Skill::Injure, &mut x, &mut y, vec![]);
|
|
assert!(y.immune(Skill::HealI).is_some());
|
|
// resolutions = heal(&mut y.clone(), &mut y, resolutions);
|
|
}
|
|
*/
|
|
#[test]
|
|
fn invert_test() {
|
|
let mut x = Construct::new()
|
|
.named(&"muji".to_string());
|
|
|
|
let mut y = Construct::new()
|
|
.named(&"camel".to_string());
|
|
|
|
// give red shield but reduce to 0
|
|
y.red_life.force(64);
|
|
y.red_life.reduce(64);
|
|
x.red_power.force(512);
|
|
invert(&mut y.clone(), &mut y, vec![], Skill::InvertI);
|
|
assert!(y.affected(Effect::Invert));
|
|
|
|
// heal should deal green damage
|
|
heal(&mut x, &mut y, vec![], Skill::HealI);
|
|
assert!(y.green_life() < 1024);
|
|
|
|
// attack should heal and recharge red shield
|
|
let mut results = attack(&mut x, &mut y, vec![], Skill::Attack);
|
|
|
|
match results.remove(0).event {
|
|
Event::Inversion { skill } => assert_eq!(skill, Skill::Attack),
|
|
_ => panic!("not inversion"),
|
|
};
|
|
|
|
match results.remove(0).event {
|
|
Event::Healing { skill: _, overhealing: _, amount } => assert!(amount > 0),
|
|
_ => panic!("not healing from inversion"),
|
|
};
|
|
|
|
match results.remove(0).event {
|
|
Event::Recharge { skill: _, red, blue: _ } => assert!(red > 0),
|
|
_ => panic!("not recharge from inversion"),
|
|
};
|
|
}
|
|
|
|
#[test]
|
|
fn reflect_test() {
|
|
let mut x = Construct::new()
|
|
.named(&"muji".to_string());
|
|
|
|
let mut y = Construct::new()
|
|
.named(&"camel".to_string());
|
|
|
|
reflect(&mut y.clone(), &mut y, vec![], Skill::ReflectI);
|
|
assert!(y.affected(Effect::Reflect));
|
|
|
|
let mut results = vec![];
|
|
results = resolve(Skill::Attack, &mut x, &mut y, results);
|
|
|
|
assert!(x.green_life() < 1024);
|
|
|
|
let Resolution { source: _, target: _, event, stages: _ } = results.remove(0);
|
|
match event {
|
|
Event::Reflection { skill } => assert_eq!(skill, Skill::Attack),
|
|
_ => panic!("not reflection"),
|
|
};
|
|
|
|
let Resolution { source: _, target: _, event, stages: _ } = results.remove(0);
|
|
match event {
|
|
Event::Damage { amount, mitigation: _, colour: _, skill: _ } => assert!(amount > 0),
|
|
_ => panic!("not damage"),
|
|
};
|
|
}
|
|
|
|
#[test]
|
|
fn siphon_test() {
|
|
let mut x = Construct::new()
|
|
.named(&"muji".to_string());
|
|
|
|
let mut y = Construct::new()
|
|
.named(&"camel".to_string());
|
|
|
|
x.green_life.reduce(512);
|
|
|
|
let mut results = resolve(Skill::SiphonI, &mut x, &mut y, vec![]);
|
|
|
|
assert!(y.affected(Effect::Siphon));
|
|
assert!(x.green_life() == (512 + 256.pct(Skill::SiphonTickI.multiplier())));
|
|
|
|
let Resolution { source: _, target: _, event, stages: _ } = results.remove(0);
|
|
match event {
|
|
Event::Effect { effect, skill: _, duration: _, construct_effects: _ } => assert_eq!(effect, Effect::Siphon),
|
|
_ => panic!("not siphon"),
|
|
};
|
|
|
|
let Resolution { source: _, target: _, event, stages: _ } = results.remove(0);
|
|
match event {
|
|
Event::Damage { amount, skill: _, mitigation: _, colour: _} => assert_eq!(amount, 256.pct(Skill::SiphonTickI.multiplier())),
|
|
_ => panic!("not damage siphon"),
|
|
};
|
|
|
|
let Resolution { source: _, target, event, stages: _ } = results.remove(0);
|
|
match event {
|
|
Event::Healing { amount, skill: _, overhealing: _ } => {
|
|
assert_eq!(amount, 256.pct(Skill::SiphonTickI.multiplier()));
|
|
assert_eq!(target.id, x.id);
|
|
},
|
|
_ => panic!("not healing"),
|
|
};
|
|
}
|
|
|
|
#[test]
|
|
fn triage_test() {
|
|
let mut x = Construct::new()
|
|
.named(&"muji".to_string());
|
|
|
|
let mut y = Construct::new()
|
|
.named(&"pretaliation".to_string());
|
|
|
|
// ensure it doesn't have 0 sd
|
|
x.blue_power.force(50);
|
|
|
|
// remove all mitigation
|
|
y.red_life.force(0);
|
|
y.blue_life.force(0);
|
|
|
|
y.deal_red_damage(Skill::Attack, 5);
|
|
let prev_hp = y.green_life();
|
|
|
|
triage(&mut x, &mut y, vec![], Skill::TriageI);
|
|
|
|
assert!(y.effects.iter().any(|e| e.effect == Effect::Triage));
|
|
assert!(y.green_life() > prev_hp);
|
|
}
|
|
|
|
#[test]
|
|
fn recharge_test() {
|
|
let mut x = Construct::new()
|
|
.named(&"muji".to_string());
|
|
|
|
let mut y = Construct::new()
|
|
.named(&"pretaliation".to_string());
|
|
|
|
y.red_life.force(50);
|
|
y.blue_life.force(50);
|
|
|
|
y.deal_red_damage(Skill::Attack, 5);
|
|
y.deal_blue_damage(Skill::BlastI, 5);
|
|
|
|
let mut results = recharge(&mut x, &mut y, vec![], Skill::RechargeI);
|
|
|
|
let Resolution { source: _, target: _, event, stages: _ } = results.remove(0);
|
|
match event {
|
|
Event::Recharge { red, blue, skill: _ } => {
|
|
assert!(red == 5);
|
|
assert!(blue == 5);
|
|
}
|
|
_ => panic!("result was not recharge"),
|
|
}
|
|
}
|
|
|
|
|
|
#[test]
|
|
fn silence_test() {
|
|
let mut x = Construct::new()
|
|
.named(&"muji".to_string());
|
|
|
|
silence(&mut x.clone(), &mut x, vec![], Skill::SilenceI);
|
|
assert!(x.effects.iter().any(|e| e.effect == Effect::Silence));
|
|
assert!(x.disabled(Skill::SilenceI).is_some());
|
|
}
|
|
|
|
#[test]
|
|
fn amplify_test() {
|
|
let mut x = Construct::new()
|
|
.named(&"muji".to_string());
|
|
|
|
x.blue_power.force(50);
|
|
|
|
amplify(&mut x.clone(), &mut x, vec![], Skill::AmplifyI);
|
|
assert!(x.effects.iter().any(|e| e.effect == Effect::Amplify));
|
|
assert_eq!(x.blue_power(), 75);
|
|
}
|
|
|
|
#[test]
|
|
fn purify_test() {
|
|
let mut x = Construct::new()
|
|
.named(&"muji".to_string());
|
|
|
|
decay(&mut x.clone(), &mut x, vec![], Skill::DecayI);
|
|
assert!(x.effects.iter().any(|e| e.effect == Effect::Decay));
|
|
|
|
purify(&mut x.clone(), &mut x, vec![], Skill::PurifyI);
|
|
assert!(!x.effects.iter().any(|e| e.effect == Effect::Decay));
|
|
}
|
|
|
|
#[test]
|
|
fn strangle_test() {
|
|
let mut x = Construct::new()
|
|
.named(&"muji".to_string());
|
|
|
|
let mut y = Construct::new()
|
|
.named(&"pretaliation".to_string());
|
|
|
|
strangle(&mut x, &mut y, vec![], Skill::StrangleI);
|
|
assert!(y.effects.iter().any(|e| e.effect == Effect::Strangle));
|
|
assert!(x.effects.iter().any(|e| e.effect == Effect::Strangling));
|
|
|
|
// ensure can't be removed
|
|
purify(&mut x, &mut y, vec![], Skill::PurifyI);
|
|
assert!(y.effects.iter().any(|e| e.effect == Effect::Strangle));
|
|
purge(&mut x.clone(), &mut x, vec![], Skill::PurgeI);
|
|
assert!(x.effects.iter().any(|e| e.effect == Effect::Strangling));
|
|
}
|
|
|
|
}
|