2200 lines
82 KiB
Rust
2200 lines
82 KiB
Rust
use rand::{thread_rng, Rng};
|
|
use uuid::Uuid;
|
|
|
|
use util::{IntPct};
|
|
use construct::{Construct, ConstructEffect, EffectMeta};
|
|
use item::{Item};
|
|
|
|
use game::{Game};
|
|
use effect::{Effect, Colour, Cooldown};
|
|
|
|
pub fn dev_resolve(a_id: Uuid, b_id: Uuid, skill: Skill) -> Resolutions {
|
|
let mut resolutions = vec![];
|
|
|
|
let mut a = Construct::new();
|
|
a.id = a_id;
|
|
let mut b = Construct::new();
|
|
b.id = b_id;
|
|
if skill.aoe() { // Send an aoe skill event for anims
|
|
resolutions.push(Resolution::new(&a, &b).event(Event::AoeSkill { skill }).stages(EventStages::StartEnd));
|
|
}
|
|
return resolve(skill, &mut a, &mut b, resolutions);
|
|
}
|
|
|
|
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::AoeSkill { skill }).stages(EventStages::StartEnd));
|
|
}
|
|
|
|
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 }).stages(EventStages::PostOnly));
|
|
return resolutions;
|
|
}
|
|
|
|
if target.is_ko() {
|
|
// resolutions.push(Resolution::new(source, target).event(Event::TargetKo { skill }).stages(EventStages::PostOnly));
|
|
return resolutions;
|
|
}
|
|
|
|
if target.affected(Effect::Reflect) && skill.colours().contains(&Colour::Blue) && !skill.is_tick() {
|
|
// guard against overflow
|
|
if source.affected(Effect::Reflect) {
|
|
return resolutions;
|
|
}
|
|
resolutions.push(Resolution::new(source, target).event(Event::Reflection { skill }));
|
|
return resolve(skill, &mut source.clone(), source, resolutions);
|
|
}
|
|
|
|
if source.affected(Effect::Haste) {
|
|
match skill {
|
|
Skill::Slay |
|
|
Skill::SlayPlus |
|
|
Skill::SlayPlusPlus |
|
|
Skill::Chaos |
|
|
Skill::ChaosPlus |
|
|
Skill::ChaosPlusPlus |
|
|
Skill::Strike |
|
|
Skill::StrikePlus |
|
|
Skill::StrikePlusPlus => {
|
|
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::Hybrid) {
|
|
match skill {
|
|
Skill::Blast|
|
|
Skill::BlastPlus |
|
|
Skill::BlastPlusPlus |
|
|
Skill::Chaos |
|
|
Skill::ChaosPlus |
|
|
Skill::ChaosPlusPlus |
|
|
Skill::Siphon |
|
|
Skill::SiphonPlus |
|
|
Skill::SiphonPlusPlus => {
|
|
let amount = source.green_power().pct(Skill::HybridBlast.multiplier());
|
|
target.deal_blue_damage(Skill::HybridBlast, 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::Amplify|
|
|
Skill::AmplifyPlus |
|
|
Skill::AmplifyPlusPlus => amplify(source, target, resolutions, skill),
|
|
|
|
Skill::Banish|
|
|
Skill::BanishPlus |
|
|
Skill::BanishPlusPlus => banish(source, target, resolutions, skill),
|
|
|
|
Skill::Bash|
|
|
Skill::BashPlus |
|
|
Skill::BashPlusPlus => bash(source, target, resolutions, skill),
|
|
|
|
Skill::Blast|
|
|
Skill::BlastPlus |
|
|
Skill::BlastPlusPlus => blast(source, target, resolutions, skill),
|
|
|
|
Skill::Chaos|
|
|
Skill::ChaosPlus |
|
|
Skill::ChaosPlusPlus => chaos(source, target, resolutions, skill),
|
|
|
|
Skill::Sustain|
|
|
Skill::SustainPlus |
|
|
Skill::SustainPlusPlus => sustain(source, target, resolutions, skill),
|
|
|
|
Skill::Electrify|
|
|
Skill::ElectrifyPlus |
|
|
Skill::ElectrifyPlusPlus => electrify(source, target, resolutions, skill),
|
|
Skill::ElectrocuteTick|
|
|
Skill::ElectrocuteTickPlus |
|
|
Skill::ElectrocuteTickPlusPlus => electrocute_tick(source, target, resolutions, skill),
|
|
|
|
Skill::Curse|
|
|
Skill::CursePlus |
|
|
Skill::CursePlusPlus => curse(source, target, resolutions, skill),
|
|
|
|
Skill::Decay|
|
|
Skill::DecayPlus |
|
|
Skill::DecayPlusPlus => decay(source, target, resolutions, skill), // dot
|
|
Skill::DecayTick|
|
|
Skill::DecayTickPlus |
|
|
Skill::DecayTickPlusPlus => decay_tick(source, target, resolutions, skill), // dot
|
|
|
|
Skill::Haste|
|
|
Skill::HastePlus |
|
|
Skill::HastePlusPlus => haste(source, target, resolutions, skill), // speed slow
|
|
|
|
Skill::Heal|
|
|
Skill::HealPlus |
|
|
Skill::HealPlusPlus => heal(source, target, resolutions, skill),
|
|
|
|
Skill::Absorb|
|
|
Skill::AbsorbPlus |
|
|
Skill::AbsorbPlusPlus => absorb(source, target, resolutions, skill),
|
|
|
|
Skill::Hybrid|
|
|
Skill::HybridPlus |
|
|
Skill::HybridPlusPlus => hybrid(source, target, resolutions, skill),
|
|
|
|
Skill::Invert|
|
|
Skill::InvertPlus |
|
|
Skill::InvertPlusPlus => invert(source, target, resolutions, skill),
|
|
|
|
Skill::Counter|
|
|
Skill::CounterPlus |
|
|
Skill::CounterPlusPlus => counter(source, target, resolutions, skill),
|
|
|
|
Skill::Purge|
|
|
Skill::PurgePlus |
|
|
Skill::PurgePlusPlus => purge(source, target, resolutions, skill), // dispel all buffs
|
|
|
|
Skill::Purify|
|
|
Skill::PurifyPlus |
|
|
Skill::PurifyPlusPlus => purify(source, target, resolutions, skill),
|
|
|
|
Skill::Recharge|
|
|
Skill::RechargePlus |
|
|
Skill::RechargePlusPlus => recharge(source, target, resolutions, skill),
|
|
|
|
Skill::Reflect|
|
|
Skill::ReflectPlus |
|
|
Skill::ReflectPlusPlus => reflect(source, target, resolutions, skill),
|
|
|
|
Skill::Ruin|
|
|
Skill::RuinPlus |
|
|
Skill::RuinPlusPlus => ruin(source, target, resolutions, skill),
|
|
|
|
Skill::Link|
|
|
Skill::LinkPlus |
|
|
Skill::LinkPlusPlus => link(source, target, resolutions, skill), // target is immune to magic damage and fx
|
|
|
|
Skill::Silence|
|
|
Skill::SilencePlus |
|
|
Skill::SilencePlusPlus => silence(source, target, resolutions, skill), // target cannot cast spells
|
|
|
|
Skill::Siphon|
|
|
Skill::SiphonPlus |
|
|
Skill::SiphonPlusPlus => siphon(source, target, resolutions, skill), // dot
|
|
Skill::SiphonTick|
|
|
Skill::SiphonTickPlus |
|
|
Skill::SiphonTickPlusPlus => siphon_tick(source, target, resolutions, skill), // dot
|
|
|
|
Skill::Slay|
|
|
Skill::SlayPlus |
|
|
Skill::SlayPlusPlus => slay(source, target, resolutions, skill), // hybrid dmg self heal
|
|
|
|
Skill::Sleep|
|
|
Skill::SleepPlus |
|
|
Skill::SleepPlusPlus => sleep(source, target, resolutions, skill), // heal stun
|
|
|
|
Skill::Restrict|
|
|
Skill::RestrictPlus |
|
|
Skill::RestrictPlusPlus => restrict(source, target, resolutions, skill),
|
|
|
|
Skill::Strike|
|
|
Skill::StrikePlus |
|
|
Skill::StrikePlusPlus => strike(source, target, resolutions, skill),
|
|
|
|
Skill::Intercept|
|
|
Skill::InterceptPlus |
|
|
Skill::InterceptPlusPlus => intercept(source, target, resolutions, skill),
|
|
|
|
Skill::Break|
|
|
Skill::BreakPlus |
|
|
Skill::BreakPlusPlus => break_(source, target, resolutions, skill), // no damage stun, adds vulnerable
|
|
|
|
Skill::Triage|
|
|
Skill::TriagePlus |
|
|
Skill::TriagePlusPlus => triage(source, target, resolutions, skill), // hot
|
|
|
|
Skill::TriageTick|
|
|
Skill::TriageTickPlus |
|
|
Skill::TriageTickPlusPlus => 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::Electrocute |
|
|
Skill::ElectrocutePlus |
|
|
Skill::ElectrocutePlusPlus => panic!("should only trigger from electrify hit"),
|
|
Skill::HasteStrike => panic!("should only trigger from haste"),
|
|
Skill::Absorption|
|
|
Skill::AbsorptionPlus |
|
|
Skill::AbsorptionPlusPlus => panic!("should only trigger from absorb"),
|
|
Skill::HybridBlast => panic!("should only trigger from hybrid"),
|
|
Skill::CounterAttack|
|
|
Skill::CounterAttackPlus |
|
|
Skill::CounterAttackPlusPlus => panic!("should only trigger from counter"),
|
|
|
|
|
|
// Not used
|
|
};
|
|
|
|
return resolutions;
|
|
}
|
|
|
|
fn post_resolve(_skill: Skill, game: &mut Game, mut resolutions: Resolutions) -> Resolutions {
|
|
for Resolution { source, target: event_target, event, stages: _ } in resolutions.clone() {
|
|
let mut source = game.construct_by_id(source.id).unwrap().clone();
|
|
let mut target = game.construct_by_id(event_target.id).unwrap().clone();
|
|
|
|
match event {
|
|
Event::Damage { amount, skill, mitigation, colour: c } => {
|
|
if target.affected(Effect::Electric) && !skill.is_tick() {
|
|
let ConstructEffect { effect: _, duration: _, meta, tick: _ } = target.effects.iter()
|
|
.find(|e| e.effect == Effect::Electric).unwrap().clone();
|
|
match meta {
|
|
Some(EffectMeta::Skill(s)) => {
|
|
// Gurad against reflect overflow
|
|
if !(source.affected(Effect::Reflect) && target.affected(Effect::Reflect)) {
|
|
// Check reflect don't bother if electrocute is procing on death
|
|
if source.affected(Effect::Reflect) && !target.is_ko() {
|
|
resolutions.push(Resolution::new(&target, &source)
|
|
.event(Event::Reflection { skill: s }).stages(EventStages::EndPost));
|
|
resolutions = electrocute(&mut source, &mut target, resolutions, s);
|
|
} else {
|
|
resolutions = electrocute(&mut target, &mut source, resolutions, s);
|
|
}
|
|
}
|
|
},
|
|
_ => panic!("no electrify skill"),
|
|
};
|
|
}
|
|
|
|
if target.affected(Effect::Absorb) && !target.is_ko() {
|
|
let ConstructEffect { effect: _, duration: _, meta, tick: _ } = target.effects.iter()
|
|
.find(|e| e.effect == Effect::Absorb).unwrap().clone();
|
|
match meta {
|
|
Some(EffectMeta::Skill(s)) => {
|
|
resolutions = absorption(&mut source, &mut target, resolutions, skill, amount + mitigation, s);
|
|
},
|
|
_ => panic!("no absorb skill"),
|
|
};
|
|
}
|
|
if c == Colour::Red {
|
|
if target.affected(Effect::Counter) && !target.is_ko() {
|
|
let ConstructEffect { effect: _, duration: _, meta, tick: _ } = target.effects.iter()
|
|
.find(|e| e.effect == Effect::Counter).unwrap().clone();
|
|
match meta {
|
|
Some(EffectMeta::Skill(s)) => {
|
|
resolutions = counter_attack(&mut target, &mut source, resolutions, s);
|
|
},
|
|
_ => panic!("no counter skill"),
|
|
};
|
|
}
|
|
}
|
|
},
|
|
_ => (),
|
|
};
|
|
|
|
if target.is_ko() && event_target.green == 0 {
|
|
// Check target is actually ko if sending ko event
|
|
target.effects.clear();
|
|
resolutions.push(Resolution::new(&source, &target).event(Event::Ko()).stages(EventStages::PostOnly));
|
|
}
|
|
|
|
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: source.skill_speed(skill),
|
|
}
|
|
}
|
|
|
|
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 EventStages {
|
|
#[serde(rename = "START_SKILL END_SKILL POST_SKILL")]
|
|
AllStages, // Anim Anim Anim
|
|
#[serde(rename = "START_SKILL END_SKILL")]
|
|
StartEnd, // Anim Anim Skip
|
|
#[serde(rename = "START_SKILL POST_SKILL")]
|
|
StartPost, // Anim Skip Anim
|
|
#[serde(rename = "START_SKILL")]
|
|
StartOnly, // Anim Skip Skip
|
|
#[serde(rename = "END_SKILL POST_SKILL")]
|
|
EndPost, // Skip Anim Anim
|
|
#[serde(rename = "END_SKILL")]
|
|
EndOnly, // Skip Anim Skip
|
|
#[serde(rename = "POST_SKILL")]
|
|
PostOnly, // Skip Skip Anim
|
|
}
|
|
|
|
#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)]
|
|
pub struct Resolution {
|
|
pub source: EventConstruct,
|
|
pub target: EventConstruct,
|
|
pub event: Event,
|
|
pub stages: EventStages,
|
|
}
|
|
|
|
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: EventStages::AllStages,
|
|
}
|
|
}
|
|
|
|
fn event(mut self, e: Event) -> Resolution {
|
|
self.event = e;
|
|
self
|
|
}
|
|
|
|
fn stages(mut self, s: EventStages) -> Resolution {
|
|
self.stages = s;
|
|
self
|
|
}
|
|
|
|
pub fn get_delay(self) -> i64 {
|
|
let source_duration = 1000; // Time for SOURCE ONLY
|
|
let target_delay = 500; // Used for Source + Target
|
|
let target_duration = 1500; // Time for TARGET ONLY
|
|
let post_skill = 1000; // Time for all POST
|
|
let source_and_target_total = target_delay + target_duration; // SOURCE + TARGET time
|
|
|
|
match self.stages {
|
|
EventStages::AllStages => source_and_target_total + post_skill, // Anim Anim Anim
|
|
EventStages::StartEnd => source_and_target_total, // Anim Anim Skip
|
|
EventStages::StartPost => source_duration + post_skill, // Anim Skip Anim
|
|
EventStages::StartOnly => source_duration, // Anim Skip Skip
|
|
EventStages::EndPost => target_duration + post_skill, // Skip Anim Anim
|
|
EventStages::EndOnly => target_duration, // Skip Anim Skip
|
|
EventStages::PostOnly => post_skill, // Skip Skip Anim
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#[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 },
|
|
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> },
|
|
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 },
|
|
}
|
|
|
|
pub type Resolutions = Vec<Resolution>;
|
|
|
|
#[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,
|
|
|
|
Amplify,
|
|
#[serde(rename = "Amplify+")]
|
|
AmplifyPlus,
|
|
#[serde(rename = "Amplify++")]
|
|
AmplifyPlusPlus,
|
|
|
|
Absorb,
|
|
#[serde(rename = "Absorb+")]
|
|
AbsorbPlus,
|
|
#[serde(rename = "Absorb++")]
|
|
AbsorbPlusPlus,
|
|
|
|
Banish,
|
|
#[serde(rename = "Banish+")]
|
|
BanishPlus,
|
|
#[serde(rename = "Banish++")]
|
|
BanishPlusPlus,
|
|
|
|
Bash,
|
|
#[serde(rename = "Bash+")]
|
|
BashPlus,
|
|
#[serde(rename = "Bash++")]
|
|
BashPlusPlus,
|
|
|
|
Blast,
|
|
#[serde(rename = "Blast+")]
|
|
BlastPlus,
|
|
#[serde(rename = "Blast++")]
|
|
BlastPlusPlus,
|
|
|
|
Chaos,
|
|
#[serde(rename = "Chaos+")]
|
|
ChaosPlus,
|
|
#[serde(rename = "Chaos++")]
|
|
ChaosPlusPlus,
|
|
|
|
Sustain,
|
|
#[serde(rename = "Sustain+")]
|
|
SustainPlus,
|
|
#[serde(rename = "Sustain++")]
|
|
SustainPlusPlus,
|
|
|
|
Electrify,
|
|
#[serde(rename = "Electrify+")]
|
|
ElectrifyPlus,
|
|
#[serde(rename = "Electrify++")]
|
|
ElectrifyPlusPlus,
|
|
|
|
Curse,
|
|
#[serde(rename = "Curse+")]
|
|
CursePlus,
|
|
#[serde(rename = "Curse++")]
|
|
CursePlusPlus,
|
|
|
|
Decay,
|
|
#[serde(rename = "Decay+")]
|
|
DecayPlus,
|
|
#[serde(rename = "Decay++")]
|
|
DecayPlusPlus,
|
|
|
|
Haste,
|
|
#[serde(rename = "Haste+")]
|
|
HastePlus,
|
|
#[serde(rename = "Haste++")]
|
|
HastePlusPlus,
|
|
|
|
Heal,
|
|
#[serde(rename = "Heal+")]
|
|
HealPlus,
|
|
#[serde(rename = "Heal++")]
|
|
HealPlusPlus,
|
|
|
|
Hybrid,
|
|
#[serde(rename = "Hybrid+")]
|
|
HybridPlus,
|
|
#[serde(rename = "Hybrid++")]
|
|
HybridPlusPlus,
|
|
|
|
Invert,
|
|
#[serde(rename = "Invert+")]
|
|
InvertPlus,
|
|
#[serde(rename = "Invert++")]
|
|
InvertPlusPlus,
|
|
|
|
Counter,
|
|
#[serde(rename = "Counter+")]
|
|
CounterPlus,
|
|
#[serde(rename = "Counter++")]
|
|
CounterPlusPlus,
|
|
|
|
Purge,
|
|
#[serde(rename = "Purge+")]
|
|
PurgePlus,
|
|
#[serde(rename = "Purge++")]
|
|
PurgePlusPlus,
|
|
|
|
Purify,
|
|
#[serde(rename = "Purify+")]
|
|
PurifyPlus,
|
|
#[serde(rename = "Purify++")]
|
|
PurifyPlusPlus,
|
|
|
|
Reflect,
|
|
#[serde(rename = "Reflect+")]
|
|
ReflectPlus,
|
|
#[serde(rename = "Reflect++")]
|
|
ReflectPlusPlus,
|
|
|
|
Recharge,
|
|
#[serde(rename = "Recharge+")]
|
|
RechargePlus,
|
|
#[serde(rename = "Recharge++")]
|
|
RechargePlusPlus,
|
|
|
|
Ruin,
|
|
#[serde(rename = "Ruin+")]
|
|
RuinPlus,
|
|
#[serde(rename = "Ruin++")]
|
|
RuinPlusPlus,
|
|
|
|
Link,
|
|
#[serde(rename = "Link+")]
|
|
LinkPlus,
|
|
#[serde(rename = "Link++")]
|
|
LinkPlusPlus,
|
|
|
|
Silence,
|
|
#[serde(rename = "Silence+")]
|
|
SilencePlus,
|
|
#[serde(rename = "Silence++")]
|
|
SilencePlusPlus,
|
|
|
|
Slay,
|
|
#[serde(rename = "Slay+")]
|
|
SlayPlus,
|
|
#[serde(rename = "Slay++")]
|
|
SlayPlusPlus,
|
|
|
|
Sleep,
|
|
#[serde(rename = "Sleep+")]
|
|
SleepPlus,
|
|
#[serde(rename = "Sleep++")]
|
|
SleepPlusPlus,
|
|
|
|
Restrict,
|
|
#[serde(rename = "Restrict+")]
|
|
RestrictPlus,
|
|
#[serde(rename = "Restrict++")]
|
|
RestrictPlusPlus,
|
|
|
|
Strike,
|
|
#[serde(rename = "Strike+")]
|
|
StrikePlus,
|
|
#[serde(rename = "Strike++")]
|
|
StrikePlusPlus,
|
|
|
|
Siphon,
|
|
#[serde(rename = "Siphon+")]
|
|
SiphonPlus,
|
|
#[serde(rename = "Siphon++")]
|
|
SiphonPlusPlus,
|
|
|
|
Intercept,
|
|
#[serde(rename = "Intercept+")]
|
|
InterceptPlus,
|
|
#[serde(rename = "Intercept++")]
|
|
InterceptPlusPlus,
|
|
|
|
Break,
|
|
#[serde(rename = "Break+")]
|
|
BreakPlus,
|
|
#[serde(rename = "Break++")]
|
|
BreakPlusPlus,
|
|
|
|
Triage,
|
|
#[serde(rename = "Triage+")]
|
|
TriagePlus,
|
|
#[serde(rename = "Triage++")]
|
|
TriagePlusPlus,
|
|
|
|
Absorption,
|
|
#[serde(rename = "Absorption+")]
|
|
AbsorptionPlus,
|
|
#[serde(rename = "Absorption++")]
|
|
AbsorptionPlusPlus,
|
|
|
|
CounterAttack,
|
|
#[serde(rename = "CounterAttack+")]
|
|
CounterAttackPlus,
|
|
#[serde(rename = "CounterAttack++")]
|
|
CounterAttackPlusPlus,
|
|
|
|
Electrocute,
|
|
#[serde(rename = "Electrocute+")]
|
|
ElectrocutePlus,
|
|
#[serde(rename = "Electrocute++")]
|
|
ElectrocutePlusPlus,
|
|
ElectrocuteTick,
|
|
#[serde(rename = "ElectrocuteTick+")]
|
|
ElectrocuteTickPlus,
|
|
#[serde(rename = "ElectrocuteTick++")]
|
|
ElectrocuteTickPlusPlus,
|
|
|
|
DecayTick, // dot
|
|
#[serde(rename = "DecayTick+")]
|
|
DecayTickPlus,
|
|
#[serde(rename = "DecayTick++")]
|
|
DecayTickPlusPlus,
|
|
|
|
HasteStrike,
|
|
HybridBlast,
|
|
|
|
SiphonTick,
|
|
#[serde(rename = "SiphonTick+")]
|
|
SiphonTickPlus,
|
|
#[serde(rename = "SiphonTick++")]
|
|
SiphonTickPlusPlus,
|
|
|
|
TriageTick,
|
|
#[serde(rename = "TriageTick+")]
|
|
TriageTickPlus,
|
|
#[serde(rename = "TriageTick++")]
|
|
TriageTickPlusPlus,
|
|
}
|
|
|
|
impl Skill {
|
|
pub fn multiplier(&self) -> u64 {
|
|
match self {
|
|
// Attack Base
|
|
Skill::Attack => 80, // Base
|
|
|
|
Skill::Blast => 105, // BB
|
|
Skill::BlastPlus => 140, // BB
|
|
Skill::BlastPlusPlus => 200, // BB
|
|
|
|
Skill::Chaos => 40, // BR
|
|
Skill::ChaosPlus => 65, // BR
|
|
Skill::ChaosPlusPlus => 90, // BR
|
|
|
|
Skill::Heal => 125, //GG
|
|
Skill::HealPlus => 185, //GG
|
|
Skill::HealPlusPlus => 270, //GG
|
|
|
|
Skill::SiphonTick => 25, // GB
|
|
Skill::SiphonTickPlus => 30,
|
|
Skill::SiphonTickPlusPlus => 40,
|
|
|
|
Skill::Slay => 45, // RG
|
|
Skill::SlayPlus => 65,
|
|
Skill::SlayPlusPlus => 100,
|
|
|
|
Skill::Strike => 90, //RR
|
|
Skill::StrikePlus => 140,
|
|
Skill::StrikePlusPlus => 200,
|
|
|
|
// Block Base
|
|
Skill::ElectrocuteTick => 80,
|
|
Skill::ElectrocuteTickPlus => 100,
|
|
Skill::ElectrocuteTickPlusPlus => 130,
|
|
|
|
Skill::CounterAttack => 120,
|
|
Skill::CounterAttackPlus => 160,
|
|
Skill::CounterAttackPlusPlus => 230,
|
|
|
|
Skill::Purify => 45, //Green dmg (heal)
|
|
Skill::PurifyPlus => 70,
|
|
Skill::PurifyPlusPlus => 105,
|
|
|
|
Skill::Reflect => 45, //Recharge blue life (heal)
|
|
Skill::ReflectPlus => 70,
|
|
Skill::ReflectPlusPlus => 100,
|
|
|
|
Skill::Recharge => 70, //Recharge red and blue life (heal)
|
|
Skill::RechargePlus => 110,
|
|
Skill::RechargePlusPlus => 170,
|
|
|
|
Skill::Sustain => 120, // Recharge red life (heal)
|
|
Skill::SustainPlus => 150,
|
|
Skill::SustainPlusPlus => 230,
|
|
|
|
// Stun Base
|
|
Skill::Sleep => 200, //Green dmg (heal)
|
|
Skill::SleepPlus => 290,
|
|
Skill::SleepPlusPlus => 400,
|
|
|
|
Skill::Banish => 40, //Green dmg (heal)
|
|
Skill::BanishPlus => 75,
|
|
Skill::BanishPlusPlus => 125,
|
|
|
|
Skill::Bash => 45,
|
|
Skill::BashPlus => 65,
|
|
Skill::BashPlusPlus => 100,
|
|
|
|
Skill::Link => 25,
|
|
Skill::LinkPlus => 40,
|
|
Skill::LinkPlusPlus => 70,
|
|
|
|
Skill::Ruin => 40,
|
|
Skill::RuinPlus => 70,
|
|
Skill::RuinPlusPlus => 100,
|
|
|
|
// Debuff Base
|
|
Skill::DecayTick => 33,
|
|
Skill::DecayTickPlus => 45,
|
|
Skill::DecayTickPlusPlus => 70,
|
|
|
|
Skill::Silence => 55, // Deals more per blue skill on target
|
|
Skill::SilencePlus => 80,
|
|
Skill::SilencePlusPlus => 110,
|
|
|
|
Skill::Restrict => 40, // Deals more per red skill on target
|
|
Skill::RestrictPlus => 65,
|
|
Skill::RestrictPlusPlus => 100,
|
|
|
|
// Buff base
|
|
Skill::HybridBlast => 50,
|
|
|
|
Skill::HasteStrike => 60,
|
|
|
|
Skill::Absorb=> 95,
|
|
Skill::AbsorbPlus => 120,
|
|
Skill::AbsorbPlusPlus => 155,
|
|
|
|
Skill::Intercept => 80,
|
|
Skill::InterceptPlus => 110,
|
|
Skill::InterceptPlusPlus => 150,
|
|
|
|
Skill::TriageTick => 75,
|
|
Skill::TriageTickPlus => 110,
|
|
Skill::TriageTickPlusPlus => 140,
|
|
|
|
_ => 100,
|
|
}
|
|
}
|
|
|
|
pub fn effect(&self) -> Vec<ConstructEffect> {
|
|
match self {
|
|
// Modifiers
|
|
Skill::Amplify => vec![ConstructEffect {effect: Effect::Amplify, duration: 2,
|
|
meta: Some(EffectMeta::Multiplier(150)), tick: None}],
|
|
Skill::AmplifyPlus => vec![ConstructEffect {effect: Effect::Amplify, duration: 3,
|
|
meta: Some(EffectMeta::Multiplier(175)), tick: None}],
|
|
Skill::AmplifyPlusPlus => vec![ConstructEffect {effect: Effect::Amplify, duration: 4,
|
|
meta: Some(EffectMeta::Multiplier(200)), tick: None}],
|
|
|
|
Skill::Banish => vec![ConstructEffect {effect: Effect::Banish, duration: 2, meta: None, tick: None}],
|
|
Skill::BanishPlus => vec![ConstructEffect {effect: Effect::Banish, duration: 2, meta: None, tick: None}],
|
|
Skill::BanishPlusPlus => vec![ConstructEffect {effect: Effect::Banish, duration: 2, meta: None, tick: None}],
|
|
Skill::Block => vec![ConstructEffect {effect: Effect::Block, duration: 1,
|
|
meta: Some(EffectMeta::Multiplier(35)), tick: None}],
|
|
Skill::Buff => vec![ConstructEffect {effect: Effect::Buff, duration: 3,
|
|
meta: Some(EffectMeta::Multiplier(130)), tick: None }],
|
|
|
|
Skill::Electrify => vec![ConstructEffect {effect: Effect::Electric, duration: 1,
|
|
meta: Some(EffectMeta::Skill(Skill::Electrocute)), tick: None}],
|
|
Skill::ElectrifyPlus => vec![ConstructEffect {effect: Effect::Electric, duration: 1,
|
|
meta: Some(EffectMeta::Skill(Skill::ElectrocutePlus)), tick: None}],
|
|
Skill::ElectrifyPlusPlus => vec![ConstructEffect {effect: Effect::Electric, duration: 1,
|
|
meta: Some(EffectMeta::Skill(Skill::ElectrocutePlusPlus)), tick: None}],
|
|
Skill::Electrocute => vec![ConstructEffect {effect: Effect::Electrocute, duration: 2,
|
|
meta: Some(EffectMeta::Skill(Skill::ElectrocuteTick)), tick: None}],
|
|
Skill::ElectrocutePlus => vec![ConstructEffect {effect: Effect::Electrocute, duration: 3,
|
|
meta: Some(EffectMeta::Skill(Skill::ElectrocuteTickPlus)), tick: None}],
|
|
Skill::ElectrocutePlusPlus => vec![ConstructEffect {effect: Effect::Electrocute, duration: 4,
|
|
meta: Some(EffectMeta::Skill(Skill::ElectrocuteTickPlusPlus)), tick: None}],
|
|
|
|
Skill::Sustain => vec![ConstructEffect {effect: Effect::Sustain, duration: 1, meta: None, tick: None }],
|
|
Skill::SustainPlus => vec![ConstructEffect {effect: Effect::Sustain, duration: 1, meta: None, tick: None }],
|
|
Skill::SustainPlusPlus => vec![ConstructEffect {effect: Effect::Sustain, duration: 1, meta: None, tick: None }],
|
|
|
|
Skill::Curse => vec![ConstructEffect {effect: Effect::Curse, duration: 2,
|
|
meta: Some(EffectMeta::Multiplier(150)), tick: None}],
|
|
Skill::CursePlus => vec![ConstructEffect {effect: Effect::Curse, duration: 2,
|
|
meta: Some(EffectMeta::Multiplier(200)), tick: None}],
|
|
Skill::CursePlusPlus => 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::Decay => 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::DecayTick)), tick: None}],
|
|
Skill::DecayPlus => 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::DecayTickPlus)), tick: None}],
|
|
Skill::DecayPlusPlus => 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::DecayTickPlusPlus)), tick: None}],
|
|
|
|
Skill::Haste => vec![ConstructEffect {effect: Effect::Haste, duration: 3,
|
|
meta: Some(EffectMeta::Multiplier(150)), tick: None }],
|
|
Skill::HastePlus => vec![ConstructEffect {effect: Effect::Haste, duration: 4,
|
|
meta: Some(EffectMeta::Multiplier(175)), tick: None }],
|
|
Skill::HastePlusPlus => vec![ConstructEffect {effect: Effect::Haste, duration: 5,
|
|
meta: Some(EffectMeta::Multiplier(225)), tick: None }],
|
|
|
|
Skill::Absorb => vec![ConstructEffect {effect: Effect::Absorb, duration: 1,
|
|
meta: Some(EffectMeta::Skill(Skill::Absorption)), tick: None}],
|
|
Skill::AbsorbPlus => vec![ConstructEffect {effect: Effect::Absorb, duration: 1,
|
|
meta: Some(EffectMeta::Skill(Skill::AbsorptionPlus)), tick: None}],
|
|
Skill::AbsorbPlusPlus => vec![ConstructEffect {effect: Effect::Absorb, duration: 1,
|
|
meta: Some(EffectMeta::Skill(Skill::AbsorptionPlusPlus)), tick: None}],
|
|
|
|
Skill::Absorption => vec![ConstructEffect {effect: Effect::Absorption, duration: 3, meta: None, tick: None}],
|
|
Skill::AbsorptionPlus => vec![ConstructEffect {effect: Effect::Absorption, duration: 5, meta: None, tick: None}],
|
|
Skill::AbsorptionPlusPlus => vec![ConstructEffect {effect: Effect::Absorption, duration: 7, meta: None, tick: None}],
|
|
|
|
Skill::Hybrid => vec![ConstructEffect {effect: Effect::Hybrid, duration: 3,
|
|
meta: Some(EffectMeta::Multiplier(150)), tick: None }],
|
|
Skill::HybridPlus => vec![ConstructEffect {effect: Effect::Hybrid, duration: 4,
|
|
meta: Some(EffectMeta::Multiplier(175)), tick: None }],
|
|
Skill::HybridPlusPlus => vec![ConstructEffect {effect: Effect::Hybrid, duration: 5,
|
|
meta: Some(EffectMeta::Multiplier(225)), tick: None }],
|
|
|
|
Skill::Invert => vec![ConstructEffect {effect: Effect::Invert, duration: 2, meta: None, tick: None}],
|
|
Skill::InvertPlus => vec![ConstructEffect {effect: Effect::Invert, duration: 3, meta: None, tick: None}],
|
|
Skill::InvertPlusPlus => vec![ConstructEffect {effect: Effect::Invert, duration: 4, meta: None, tick: None}],
|
|
|
|
Skill::Counter => vec![ConstructEffect {effect: Effect::Counter, duration: 1,
|
|
meta: Some(EffectMeta::Skill(Skill::CounterAttack)), tick: None}],
|
|
Skill::CounterPlus => vec![ConstructEffect {effect: Effect::Counter, duration: 1,
|
|
meta: Some(EffectMeta::Skill(Skill::CounterAttackPlus)), tick: None}],
|
|
Skill::CounterPlusPlus => vec![ConstructEffect {effect: Effect::Counter, duration: 1,
|
|
meta: Some(EffectMeta::Skill(Skill::CounterAttackPlusPlus)), tick: None}],
|
|
|
|
Skill::Reflect => vec![ConstructEffect {effect: Effect::Reflect, duration: 1, meta: None, tick: None }],
|
|
Skill::ReflectPlus => vec![ConstructEffect {effect: Effect::Reflect, duration: 1, meta: None, tick: None }],
|
|
Skill::ReflectPlusPlus => vec![ConstructEffect {effect: Effect::Reflect, duration: 1, meta: None, tick: None }],
|
|
|
|
Skill::Break => 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::BreakPlus => 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::BreakPlusPlus => 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::Ruin => vec![ConstructEffect {effect: Effect::Stun, duration: 1, meta: None, tick: None}],
|
|
Skill::RuinPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 1, meta: None, tick: None}],
|
|
Skill::RuinPlusPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 1, meta: None, tick: None}],
|
|
|
|
Skill::Purge => vec![ConstructEffect {effect: Effect::Purge, duration: 2, meta: None, tick: None}],
|
|
Skill::PurgePlus => vec![ConstructEffect {effect: Effect::Purge, duration: 3, meta: None, tick: None}],
|
|
Skill::PurgePlusPlus => vec![ConstructEffect {effect: Effect::Purge, duration: 4, meta: None, tick: None}],
|
|
|
|
Skill::Link => vec![ConstructEffect {effect: Effect::Stun, duration: 1, meta: None, tick: None}],
|
|
Skill::LinkPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 1, meta: None, tick: None}],
|
|
Skill::LinkPlusPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 1, meta: None, tick: None}],
|
|
|
|
Skill::Silence => vec![ConstructEffect {effect: Effect::Silence, duration: 2, meta: None, tick: None}],
|
|
Skill::SilencePlus => vec![ConstructEffect {effect: Effect::Silence, duration: 2, meta: None, tick: None}],
|
|
Skill::SilencePlusPlus => vec![ConstructEffect {effect: Effect::Silence, duration: 2, meta: None, tick: None}],
|
|
|
|
Skill::Siphon => vec![ConstructEffect {effect: Effect::Siphon, duration: 2,
|
|
meta: Some(EffectMeta::Skill(Skill::SiphonTick)), tick: None}],
|
|
Skill::SiphonPlus => vec![ConstructEffect {effect: Effect::Siphon, duration: 3,
|
|
meta: Some(EffectMeta::Skill(Skill::SiphonTickPlus)), tick: None}],
|
|
Skill::SiphonPlusPlus => vec![ConstructEffect {effect: Effect::Siphon, duration: 4,
|
|
meta: Some(EffectMeta::Skill(Skill::SiphonTickPlusPlus)), tick: None}],
|
|
|
|
Skill::Sleep => vec![ConstructEffect {effect: Effect::Stun, duration: 2, meta: None, tick: None}],
|
|
Skill::SleepPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 3, meta: None, tick: None}],
|
|
Skill::SleepPlusPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 4, meta: None, tick: None}],
|
|
|
|
Skill::Restrict => vec![ConstructEffect {effect: Effect::Restrict, duration: 2, meta: None, tick: None}],
|
|
Skill::RestrictPlus => vec![ConstructEffect {effect: Effect::Restrict, duration: 2, meta: None, tick: None}],
|
|
Skill::RestrictPlusPlus => vec![ConstructEffect {effect: Effect::Restrict, duration: 2, meta: None, tick: None}],
|
|
|
|
Skill::Bash => vec![ConstructEffect {effect: Effect::Stun, duration: 2,
|
|
meta: Some(EffectMeta::Skill(Skill::Bash)), tick: None}],
|
|
Skill::BashPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 2,
|
|
meta: Some(EffectMeta::Skill(Skill::BashPlus)), tick: None}],
|
|
Skill::BashPlusPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 2,
|
|
meta: Some(EffectMeta::Skill(Skill::BashPlusPlus)), tick: None}],
|
|
Skill::Stun => vec![ConstructEffect {effect: Effect::Stun, duration: 2, meta: None, tick: None}],
|
|
|
|
Skill::Intercept => vec![ConstructEffect {effect: Effect::Intercept, duration: 1, meta: None, tick: None}],
|
|
Skill::InterceptPlus => vec![ConstructEffect {effect: Effect::Intercept, duration: 1, meta: None, tick: None}],
|
|
Skill::InterceptPlusPlus => vec![ConstructEffect {effect: Effect::Intercept, duration: 1, meta: None, tick: None}],
|
|
|
|
Skill::Triage => vec![ConstructEffect {effect: Effect::Triage, duration: 2,
|
|
meta: Some(EffectMeta::Skill(Skill::TriageTick)), tick: None}],
|
|
Skill::TriagePlus => vec![ConstructEffect {effect: Effect::Triage, duration: 3,
|
|
meta: Some(EffectMeta::Skill(Skill::TriageTickPlus)), tick: None}],
|
|
Skill::TriagePlusPlus => vec![ConstructEffect {effect: Effect::Triage, duration: 4,
|
|
meta: Some(EffectMeta::Skill(Skill::TriageTickPlusPlus)), tick: None}],
|
|
|
|
Skill::Purify => vec![ConstructEffect { effect: Effect::Purified, duration: 2,
|
|
meta: Some(EffectMeta::Multiplier(150)), tick: None}],
|
|
Skill::PurifyPlus => vec![ConstructEffect { effect: Effect::Purified, duration: 2,
|
|
meta: Some(EffectMeta::Multiplier(175)), tick: None}],
|
|
Skill::PurifyPlusPlus => vec![ConstructEffect { effect: Effect::Purified, duration: 2,
|
|
meta: Some(EffectMeta::Multiplier(200)), tick: None}],
|
|
|
|
_ => {
|
|
panic!("{:?} no skill effect", self);
|
|
},
|
|
}
|
|
}
|
|
|
|
pub fn base_cd(&self) -> Cooldown {
|
|
match self {
|
|
Skill::Attack => None,
|
|
Skill::Block => None, // reduce damage
|
|
Skill::Buff => None,
|
|
Skill::Debuff => Some(1),
|
|
Skill::Stun => Some(2),
|
|
|
|
Skill::Strike=> None,
|
|
Skill::StrikePlus => None,
|
|
Skill::StrikePlusPlus => None,
|
|
|
|
Skill::Counter|
|
|
Skill::CounterPlus |
|
|
Skill::CounterPlusPlus => None, // avoid all damage
|
|
|
|
Skill::Restrict |
|
|
Skill::RestrictPlus |
|
|
Skill::RestrictPlusPlus => Some(2),
|
|
|
|
Skill::Bash |
|
|
Skill::BashPlus |
|
|
Skill::BashPlusPlus => Some(2),
|
|
|
|
Skill::Heal=> None,
|
|
Skill::HealPlus => None,
|
|
Skill::HealPlusPlus => None,
|
|
|
|
Skill::Triage=> None, // hot
|
|
Skill::TriagePlus => None, // hot
|
|
Skill::TriagePlusPlus => None, // hot
|
|
|
|
Skill::Break | // no damage stun, adds vulnerable
|
|
Skill::BreakPlus |
|
|
Skill::BreakPlusPlus => Some(1),
|
|
|
|
Skill::Blast |
|
|
Skill::BlastPlus |
|
|
Skill::BlastPlusPlus => None,
|
|
|
|
Skill::Chaos |
|
|
Skill::ChaosPlus |
|
|
Skill::ChaosPlusPlus => None,
|
|
|
|
Skill::Amplify |
|
|
Skill::AmplifyPlus |
|
|
Skill::AmplifyPlusPlus => Some(1),
|
|
|
|
Skill::Hybrid |
|
|
Skill::HybridPlus |
|
|
Skill::HybridPlusPlus => Some(1),
|
|
|
|
Skill::Invert |
|
|
Skill::InvertPlus |
|
|
Skill::InvertPlusPlus => Some(2),
|
|
|
|
Skill::Decay => None, // dot
|
|
Skill::DecayPlus => None,
|
|
Skill::DecayPlusPlus => None,
|
|
|
|
Skill::Siphon|
|
|
Skill::SiphonPlus |
|
|
Skill::SiphonPlusPlus => None,
|
|
|
|
Skill::Curse |
|
|
Skill::CursePlus |
|
|
Skill::CursePlusPlus => Some(1),
|
|
|
|
Skill::Link |
|
|
Skill::LinkPlus |
|
|
Skill::LinkPlusPlus => Some(1),
|
|
|
|
Skill::Silence |
|
|
Skill::SilencePlus |
|
|
Skill::SilencePlusPlus => Some(2),
|
|
|
|
Skill::Purify |
|
|
Skill::PurifyPlus |
|
|
Skill::PurifyPlusPlus => None,
|
|
|
|
Skill::Purge |
|
|
Skill::PurgePlus |
|
|
Skill::PurgePlusPlus => Some(1),
|
|
|
|
Skill::Banish |
|
|
Skill::BanishPlus |
|
|
Skill::BanishPlusPlus => Some(1),
|
|
|
|
Skill::Haste |
|
|
Skill::HastePlus |
|
|
Skill::HastePlusPlus => Some(1),
|
|
|
|
Skill::Reflect |
|
|
Skill::ReflectPlus |
|
|
Skill::ReflectPlusPlus => None,
|
|
|
|
Skill::Recharge |
|
|
Skill::RechargePlus |
|
|
Skill::RechargePlusPlus => None,
|
|
|
|
Skill::Ruin |
|
|
Skill::RuinPlus |
|
|
Skill::RuinPlusPlus => Some(2),
|
|
|
|
Skill::Slay=> None,
|
|
Skill::SlayPlus => None,
|
|
Skill::SlayPlusPlus => None,
|
|
|
|
Skill::Sleep |
|
|
Skill::SleepPlus |
|
|
Skill::SleepPlusPlus => Some(2),
|
|
|
|
Skill::Sustain |
|
|
Skill::SustainPlus |
|
|
Skill::SustainPlusPlus => Some(1),
|
|
|
|
Skill::Intercept => Some(1),
|
|
Skill::InterceptPlus => Some(1),
|
|
Skill::InterceptPlusPlus => Some(1),
|
|
|
|
Skill::Electrify |
|
|
Skill::ElectrifyPlus |
|
|
Skill::ElectrifyPlusPlus => None,
|
|
|
|
Skill::Absorb |
|
|
Skill::AbsorbPlus |
|
|
Skill::AbsorbPlusPlus => Some(1),
|
|
|
|
//-----------
|
|
// Never cast directly
|
|
//---------
|
|
// Trigger
|
|
Skill::HybridBlast |
|
|
Skill::HasteStrike |
|
|
Skill::CounterAttack|
|
|
Skill::CounterAttackPlus |
|
|
Skill::CounterAttackPlusPlus | // counter
|
|
Skill::Electrocute|
|
|
Skill::ElectrocutePlus |
|
|
Skill::ElectrocutePlusPlus |
|
|
Skill::Absorption|
|
|
Skill::AbsorptionPlus |
|
|
Skill::AbsorptionPlusPlus |
|
|
// Ticks
|
|
Skill::ElectrocuteTick|
|
|
Skill::ElectrocuteTickPlus |
|
|
Skill::ElectrocuteTickPlusPlus |
|
|
Skill::DecayTick|
|
|
Skill::DecayTickPlus |
|
|
Skill::DecayTickPlusPlus |
|
|
Skill::SiphonTick|
|
|
Skill::SiphonTickPlus |
|
|
Skill::SiphonTickPlusPlus |
|
|
Skill::TriageTick|
|
|
Skill::TriageTickPlus |
|
|
Skill::TriageTickPlusPlus => None,
|
|
}
|
|
}
|
|
|
|
pub fn ko_castable(&self) -> bool {
|
|
match self {
|
|
Skill::ElectrocuteTick |
|
|
Skill::ElectrocuteTickPlus |
|
|
Skill::ElectrocuteTickPlusPlus |
|
|
Skill::DecayTick |
|
|
Skill::DecayTickPlus |
|
|
Skill::DecayTickPlusPlus |
|
|
Skill::SiphonTick |
|
|
Skill::SiphonTickPlus |
|
|
Skill::SiphonTickPlusPlus |
|
|
|
|
Skill::TriageTick |
|
|
Skill::TriageTickPlus |
|
|
Skill::TriageTickPlusPlus => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn is_tick(&self) -> bool {
|
|
match self {
|
|
Skill::ElectrocuteTick |
|
|
Skill::ElectrocuteTickPlus |
|
|
Skill::ElectrocuteTickPlusPlus |
|
|
Skill::DecayTick |
|
|
Skill::DecayTickPlus |
|
|
Skill::DecayTickPlusPlus |
|
|
Skill::SiphonTick |
|
|
Skill::SiphonTickPlus |
|
|
Skill::SiphonTickPlusPlus |
|
|
Skill::TriageTick |
|
|
Skill::TriageTickPlus |
|
|
Skill::TriageTickPlusPlus => true,
|
|
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn speed(&self) -> u64 {
|
|
match self {
|
|
Skill::SiphonTick |
|
|
Skill::SiphonTickPlus |
|
|
Skill::SiphonTickPlusPlus => Skill::Siphon.speed(),
|
|
|
|
Skill::DecayTick |
|
|
Skill::DecayTickPlus |
|
|
Skill::DecayTickPlusPlus => Skill::Decay.speed(),
|
|
|
|
Skill::TriageTick |
|
|
Skill::TriageTickPlus |
|
|
Skill::TriageTickPlusPlus => Skill::Triage.speed(),
|
|
|
|
Skill::ElectrocuteTick |
|
|
Skill::ElectrocuteTickPlus |
|
|
Skill::ElectrocuteTickPlusPlus => Skill::Electrify.speed(),
|
|
|
|
_ => Item::from(*self).speed(),
|
|
}
|
|
}
|
|
|
|
pub fn aoe(&self) -> bool {
|
|
match self {
|
|
Skill::Ruin |
|
|
Skill::RuinPlus |
|
|
Skill::RuinPlusPlus => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn defensive(&self) -> bool {
|
|
let mut rng = thread_rng();
|
|
|
|
match self {
|
|
Skill::Amplify|
|
|
Skill::AmplifyPlus |
|
|
Skill::AmplifyPlusPlus |
|
|
Skill::Block |
|
|
Skill::Sustain |
|
|
Skill::SustainPlus |
|
|
Skill::SustainPlusPlus |
|
|
Skill::Electrify |
|
|
Skill::ElectrifyPlus |
|
|
Skill::ElectrifyPlusPlus |
|
|
Skill::Haste |
|
|
Skill::HastePlus |
|
|
Skill::HastePlusPlus |
|
|
Skill::Heal |
|
|
Skill::HealPlus |
|
|
Skill::HealPlusPlus |
|
|
Skill::Absorb |
|
|
Skill::AbsorbPlus |
|
|
Skill::AbsorbPlusPlus |
|
|
Skill::Invert |
|
|
Skill::InvertPlus |
|
|
Skill::InvertPlusPlus |
|
|
Skill::Intercept |
|
|
Skill::InterceptPlus |
|
|
Skill::InterceptPlusPlus |
|
|
Skill::Counter |
|
|
Skill::CounterPlus |
|
|
Skill::CounterPlusPlus |
|
|
Skill::Purify |
|
|
Skill::PurifyPlus |
|
|
Skill::PurifyPlusPlus |
|
|
Skill::Recharge |
|
|
Skill::RechargePlus |
|
|
Skill::RechargePlusPlus |
|
|
Skill::Reflect |
|
|
Skill::ReflectPlus |
|
|
Skill::ReflectPlusPlus |
|
|
Skill::Triage |
|
|
Skill::TriagePlus |
|
|
Skill::TriagePlusPlus => true,
|
|
|
|
Skill::Banish |
|
|
Skill::BanishPlus |
|
|
Skill::BanishPlusPlus => rng.gen_bool(0.5),
|
|
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
fn components(&self) -> Vec<Item> {
|
|
let mut components = Item::from(*self).components();
|
|
components.sort_unstable();
|
|
return components;
|
|
}
|
|
|
|
pub 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 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 bash(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)))));
|
|
|
|
if results.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 + 45u64.saturating_mul(cds)));
|
|
target.deal_red_damage(skill, amount)
|
|
.into_iter()
|
|
.for_each(|e| results.push(Resolution::new(source, target).event(e).stages(EventStages::PostOnly)));
|
|
}
|
|
|
|
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(EventStages::PostOnly)));
|
|
|
|
return results;
|
|
}
|
|
|
|
fn sustain(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 red_amount = source.red_power().pct(skill.multiplier());
|
|
target.recharge(skill, red_amount, 0)
|
|
.into_iter()
|
|
.for_each(|e| results.push(Resolution::new(source, target).event(e).stages(EventStages::PostOnly)));
|
|
|
|
return results;
|
|
}
|
|
|
|
fn intercept(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
let intercept = skill.effect()[0];
|
|
results.push(Resolution::new(source, target).event(target.add_effect(skill, intercept)));
|
|
|
|
let red_amount = source.red_power().pct(skill.multiplier());
|
|
target.recharge(skill, red_amount, 0)
|
|
.into_iter()
|
|
.for_each(|e| results.push(Resolution::new(source, target).event(e).stages(EventStages::PostOnly)));
|
|
|
|
return results;
|
|
}
|
|
|
|
fn break_(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(EventStages::PostOnly));
|
|
|
|
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])));
|
|
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 counter(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 counter_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 restrict(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(EventStages::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()) + source.green_power().pct(skill.multiplier());
|
|
let slay_events = target.deal_red_damage(skill, amount);
|
|
|
|
for e in slay_events {
|
|
match e {
|
|
Event::Damage { amount, mitigation: _, colour: _, skill: _ } => {
|
|
results.push(Resolution::new(source, target).event(e));
|
|
let heal = source.deal_green_damage(skill, amount.pct(50));
|
|
for h in heal {
|
|
results.push(Resolution::new(source, source).event(h).stages(EventStages::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 skip_tick = target.effects.iter().any(|e| {
|
|
match e.effect {
|
|
Effect::Triage => source.skill_speed(skill) <= e.tick.unwrap().speed,
|
|
_ => false,
|
|
}
|
|
});
|
|
|
|
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)));
|
|
|
|
match skip_tick {
|
|
true => return results,
|
|
false => 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(EventStages::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(EventStages::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 skip_tick = target.effects.iter().any(|e| {
|
|
match e.effect {
|
|
Effect::Decay => source.skill_speed(skill) <= e.tick.unwrap().speed,
|
|
_ => false,
|
|
}
|
|
});
|
|
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(EventStages::PostOnly));
|
|
|
|
match skip_tick {
|
|
true => return results,
|
|
false => 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(EventStages::EndPost)));
|
|
return results;
|
|
}
|
|
|
|
// electrify is the buff effect
|
|
// when attacked it runs electrocute and applies a debuff
|
|
fn electrify(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
let electrify = skill.effect()[0];
|
|
results.push(Resolution::new(source, target).event(target.add_effect(skill, electrify)));
|
|
return results;;
|
|
}
|
|
|
|
fn electrocute(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
// Remove electric buff, no need to display if construct is dead
|
|
if !source.is_ko() {
|
|
let electric = source.effects.iter().position(|e| e.effect == Effect::Electric);
|
|
match electric {
|
|
Some(eff) => {
|
|
let ce = source.effects.remove(eff);
|
|
results.push(Resolution::new(source, source)
|
|
.event(Event::Removal { skill, effect: Some(ce.effect), construct_effects: source.effects.clone() })
|
|
.stages(EventStages::PostOnly));
|
|
}
|
|
None => ()
|
|
}
|
|
}
|
|
|
|
let ConstructEffect { effect, duration, meta, tick: _ } = skill.effect()[0];
|
|
let tick_skill = match meta {
|
|
Some(EffectMeta::Skill(s)) => s,
|
|
_ => panic!("no electrocute tick skill"),
|
|
};
|
|
|
|
let skip_tick = target.effects.iter().any(|e| {
|
|
match e.effect {
|
|
Effect::Electrocute => source.skill_speed(skill) <= e.tick.unwrap().speed,
|
|
_ => false,
|
|
}
|
|
});
|
|
let electrocute = ConstructEffect::new(effect, duration).set_tick(Cast::new_tick(source, target, tick_skill));
|
|
results.push(Resolution::new(source, target)
|
|
.event(target.add_effect(skill, electrocute))
|
|
.stages(EventStages::PostOnly));
|
|
|
|
|
|
match skip_tick {
|
|
true => return results,
|
|
false => return electrocute_tick(source, target, results, tick_skill)
|
|
}
|
|
}
|
|
|
|
fn electrocute_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(EventStages::EndPost)));
|
|
return results;
|
|
}
|
|
|
|
fn ruin(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(EventStages::PostOnly)));
|
|
|
|
results.push(Resolution::new(source, target)
|
|
.event(target.add_effect(skill, skill.effect()[0]))
|
|
.stages(EventStages::PostOnly));
|
|
return results;;
|
|
}
|
|
|
|
fn absorb(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());
|
|
target.recharge(skill, 0, blue_amount)
|
|
.into_iter()
|
|
.for_each(|e| results.push(Resolution::new(source, target).event(e).stages(EventStages::PostOnly)));
|
|
return results;;
|
|
}
|
|
|
|
fn absorption(source: &mut Construct, target: &mut Construct, mut results: Resolutions, reflect_skill: Skill, amount: u64, skill: Skill) -> Resolutions {
|
|
let absorb = skill.effect()[0].set_meta(EffectMeta::AddedDamage(amount));
|
|
|
|
results.push(Resolution::new(source, target)
|
|
.event(target.add_effect(reflect_skill, absorb))
|
|
.stages(EventStages::PostOnly));
|
|
|
|
let absorb_index = target.effects.iter().position(|e| e.effect == Effect::Absorb).expect("No absorb");
|
|
let ce = target.effects.remove(absorb_index);
|
|
|
|
results.push(Resolution::new(source, target)
|
|
.event(Event::Removal { skill, effect: Some(ce.effect), construct_effects: target.effects.clone() })
|
|
.stages(EventStages::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 hybrid(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());
|
|
target.recharge(skill, 0, blue_amount)
|
|
.into_iter()
|
|
.for_each(|e| results.push(Resolution::new(source, target).event(e).stages(EventStages::PostOnly)));
|
|
|
|
return results;;
|
|
}
|
|
|
|
fn recharge(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
results.push(Resolution::new(source, target).event(Event::Skill { skill }).stages(EventStages::StartEnd));
|
|
let red_amount = source.red_power().pct(skill.multiplier());
|
|
let blue_amount = source.blue_power().pct(skill.multiplier());
|
|
target.recharge(skill, red_amount, blue_amount)
|
|
.into_iter()
|
|
.for_each(|e| results.push(Resolution::new(source, target).event(e).stages(EventStages::PostOnly)));
|
|
|
|
return results;
|
|
}
|
|
|
|
fn siphon(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
|
|
let skip_tick = target.effects.iter().any(|e| {
|
|
match e.effect {
|
|
Effect::Siphon => source.skill_speed(skill) <= e.tick.unwrap().speed,
|
|
_ => false,
|
|
}
|
|
});
|
|
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)));
|
|
|
|
match skip_tick {
|
|
true => return results,
|
|
false => 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()) + source.green_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(EventStages::EndPost));
|
|
let heal = source.deal_green_damage(skill, amount);
|
|
for h in heal {
|
|
results.push(Resolution::new(source, source).event(h).stages(EventStages::PostOnly));
|
|
};
|
|
},
|
|
_ => results.push(Resolution::new(source, target).event(e).stages(EventStages::EndPost)),
|
|
}
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
fn link(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 amount = source.blue_power().pct(skill.multiplier().saturating_mul(target.effects.len() as u64));
|
|
target.deal_blue_damage(skill, amount)
|
|
.into_iter()
|
|
.for_each(|e| results.push(Resolution::new(source, target).event(e).stages(EventStages::PostOnly)));
|
|
|
|
return results;
|
|
}
|
|
|
|
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(EventStages::PostOnly)));
|
|
|
|
return results;
|
|
}
|
|
|
|
fn purge(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
results.push(Resolution::new(source, target).event(Event::Skill { skill }).stages(EventStages::StartEnd));
|
|
if target.effects.len() > 0 {
|
|
target.effects.clear();
|
|
results.push(Resolution::new(source, target)
|
|
.event(Event::Removal { skill, effect: None, construct_effects: target.effects.clone() })
|
|
.stages(EventStages::PostOnly));
|
|
}
|
|
|
|
let effect = skill.effect()[0];
|
|
results.push(Resolution::new(source, target).event(target.add_effect(skill, effect)).stages(EventStages::PostOnly));
|
|
|
|
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 }).stages(EventStages::StartEnd));
|
|
if target.effects.len() > 0 {
|
|
let amount = source.green_power().pct(skill.multiplier().saturating_mul(target.effects.len() as u64));
|
|
target.effects.clear();
|
|
results.push(Resolution::new(source, target)
|
|
.event(Event::Removal { skill, effect: None, construct_effects: target.effects.clone() })
|
|
.stages(EventStages::PostOnly));
|
|
target.deal_green_damage(skill, amount)
|
|
.into_iter()
|
|
.for_each(|e| results.push(Resolution::new(source, target).event(e).stages(EventStages::PostOnly)));
|
|
}
|
|
let effect = skill.effect()[0];
|
|
results.push(Resolution::new(source, target).event(target.add_effect(skill, effect)).stages(EventStages::PostOnly));
|
|
|
|
return results;
|
|
}
|
|
|
|
fn banish(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
|
results.push(Resolution::new(source, target).event(Event::Skill { skill }).stages(EventStages::StartEnd));
|
|
|
|
let red_damage = target.red_life().pct(skill.multiplier());
|
|
let blue_damage = target.blue_life().pct(skill.multiplier());
|
|
|
|
if red_damage > 0 {
|
|
target.deal_red_damage(skill, red_damage)
|
|
.into_iter()
|
|
.for_each(|e| results.push(Resolution::new(source, target).event(e).stages(EventStages::PostOnly)));
|
|
}
|
|
|
|
if blue_damage > 0 {
|
|
target.deal_blue_damage(skill, blue_damage)
|
|
.into_iter()
|
|
.for_each(|e| results.push(Resolution::new(source, target).event(e).stages(EventStages::PostOnly)));
|
|
}
|
|
|
|
results.push(Resolution::new(source, target).event(target.add_effect(skill, skill.effect()[0])).stages(EventStages::PostOnly));
|
|
return results;
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use skill::*;
|
|
|
|
#[test]
|
|
fn heal_test() {
|
|
let mut x = Construct::new()
|
|
.named(&"muji".to_string())
|
|
.learn(Skill::Heal);
|
|
|
|
let mut y = Construct::new()
|
|
.named(&"camel".to_string())
|
|
.learn(Skill::Heal);
|
|
|
|
x.deal_red_damage(Skill::Attack, 5);
|
|
|
|
heal(&mut y, &mut x, vec![], Skill::Heal);
|
|
}
|
|
|
|
#[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::Decay);
|
|
|
|
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 sustain_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
|
|
y.green_life.force(1024); // make tests more flexible if we change stats
|
|
|
|
sustain(&mut y.clone(), &mut y, vec![], Skill::Sustain);
|
|
assert!(y.affected(Effect::Sustain));
|
|
|
|
let mut results = ruin(&mut x, &mut y, vec![], Skill::Ruin);
|
|
let Resolution { source: _, target: _, event, stages: _ } = results.remove(0);
|
|
match event {
|
|
Event::Immunity { skill: _, immunity } => assert!(immunity.contains(&Effect::Sustain)),
|
|
_ => 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 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::Invert);
|
|
assert!(y.affected(Effect::Invert));
|
|
|
|
// heal should deal green damage
|
|
heal(&mut x, &mut y, vec![], Skill::Heal);
|
|
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::Reflect);
|
|
assert!(y.affected(Effect::Reflect));
|
|
|
|
let mut results = vec![];
|
|
results = resolve(Skill::Blast, &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::Blast),
|
|
_ => 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.blue_power.force(256);
|
|
x.green_power.force(220);
|
|
x.green_life.force(1024);
|
|
y.blue_life.force(0);
|
|
x.green_life.reduce(512);
|
|
|
|
let mut results = resolve(Skill::Siphon, &mut x, &mut y, vec![]);
|
|
|
|
assert!(y.affected(Effect::Siphon));
|
|
assert!(x.green_life() == (512 + 256.pct(Skill::SiphonTick.multiplier()) + 220.pct(Skill::SiphonTick.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::SiphonTick.multiplier())
|
|
+ 220.pct(Skill::SiphonTick.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::SiphonTick.multiplier()) + 220.pct(Skill::SiphonTick.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::Triage);
|
|
|
|
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::Blast, 5);
|
|
|
|
let mut results = recharge(&mut x, &mut y, vec![], Skill::Recharge);
|
|
|
|
results.remove(0);
|
|
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::Silence);
|
|
assert!(x.effects.iter().any(|e| e.effect == Effect::Silence));
|
|
assert!(x.disabled(Skill::Silence).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::Amplify);
|
|
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::Decay);
|
|
assert!(x.effects.iter().any(|e| e.effect == Effect::Decay));
|
|
|
|
purify(&mut x.clone(), &mut x, vec![], Skill::Purify);
|
|
assert!(!x.effects.iter().any(|e| e.effect == Effect::Decay));
|
|
}
|
|
|
|
#[test]
|
|
fn bash_test() {
|
|
let mut x = Construct::new()
|
|
.named(&"muji".to_string());
|
|
|
|
let mut y = Construct::new()
|
|
.named(&"pretaliation".to_string())
|
|
.learn(Skill::Stun);
|
|
|
|
let stun_cd = y.skills.iter().find(|cs| cs.skill == Skill::Stun).unwrap().cd.unwrap();
|
|
|
|
bash(&mut x, &mut y, vec![], Skill::Bash);
|
|
assert!(!x.effects.iter().any(|e| e.effect == Effect::Stun));
|
|
assert!(y.skills.iter().any(|cs| cs.skill == Skill::Stun && cs.cd.unwrap() == stun_cd + 1));
|
|
}
|
|
|
|
#[test]
|
|
fn purge_test() {
|
|
let mut x = Construct::new()
|
|
.named(&"muji".to_string());
|
|
|
|
let mut y = Construct::new()
|
|
.named(&"pretaliation".to_string())
|
|
.learn(Skill::Heal)
|
|
.learn(Skill::HealPlus);
|
|
|
|
purge(&mut x, &mut y, vec![], Skill::Purge);
|
|
// 2 turns at lvl 1
|
|
assert!(y.effects.iter().any(|e| e.effect == Effect::Purge && e.duration == 2));
|
|
assert!(y.disabled(Skill::Heal).is_some());
|
|
}
|
|
}
|