1176 lines
35 KiB
Rust
1176 lines
35 KiB
Rust
use rand::{thread_rng, Rng};
|
|
use uuid::Uuid;
|
|
|
|
use cryp::{Cryp, CrypEffect, Stat};
|
|
|
|
#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)]
|
|
pub struct Cast {
|
|
pub id: Uuid,
|
|
pub skill: Skill,
|
|
pub source_team_id: Uuid,
|
|
pub source_cryp_id: Uuid,
|
|
pub target_cryp_id: Uuid,
|
|
pub resolution: Resolution,
|
|
}
|
|
|
|
impl Cast {
|
|
pub fn new(source_cryp_id: Uuid, source_team_id: Uuid, target_cryp_id: Uuid, skill: Skill) -> Cast {
|
|
return Cast {
|
|
id: Uuid::new_v4(),
|
|
source_cryp_id,
|
|
source_team_id,
|
|
target_cryp_id,
|
|
skill,
|
|
resolution: Resolution::new(skill),
|
|
};
|
|
}
|
|
|
|
pub fn new_tick(source: &mut Cryp, target: &mut Cryp, skill: Skill) -> Cast {
|
|
Cast::new(source.id, source.account, target.id, skill)
|
|
}
|
|
|
|
pub fn set_resolution(&mut self, cryp: &mut Cryp, target: &mut Cryp) -> &mut Cast {
|
|
self.resolution = self.skill.resolve(cryp, target);
|
|
self
|
|
}
|
|
|
|
pub fn used_cooldown(&self) -> bool {
|
|
return self.skill.base_cd().is_some();
|
|
}
|
|
}
|
|
|
|
|
|
#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)]
|
|
pub struct Immunity {
|
|
pub immune: bool,
|
|
pub effects: Vec<Effect>
|
|
}
|
|
|
|
#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)]
|
|
pub struct Disable {
|
|
pub disabled: bool,
|
|
pub effects: Vec<Effect>
|
|
}
|
|
|
|
impl Disable {
|
|
fn new() -> Disable {
|
|
Disable { disabled: false, effects: vec![] }
|
|
}
|
|
}
|
|
|
|
#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)]
|
|
pub enum ResolutionResult {
|
|
Damage { amount: u64, mitigation: u64, category: Category , immunity: Immunity },
|
|
Healing { amount: u64, overhealing: u64, immunity: Immunity },
|
|
Recharge { red: u64, blue: u64, immunity: Immunity },
|
|
Inversion { healing: u64, damage: u64, recharge: u64, category: Category, immunity: Immunity },
|
|
Effect { effect: Effect, duration: u8, immunity: Immunity },
|
|
Removal { effect: Effect, immunity: Immunity },
|
|
Evasion { skill: Skill, evasion_rating: u64 },
|
|
}
|
|
|
|
#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)]
|
|
pub struct Resolution {
|
|
pub skill: Skill,
|
|
pub disable: Disable,
|
|
pub speed: u64,
|
|
pub results: Vec<ResolutionResult>,
|
|
}
|
|
|
|
impl Resolution {
|
|
fn new(skill: Skill) -> Resolution {
|
|
Resolution { skill, results: vec![], disable: Disable::new(), speed: 0 }
|
|
}
|
|
}
|
|
|
|
pub type Cooldown = Option<u8>;
|
|
|
|
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
|
|
pub enum Effect {
|
|
// physical
|
|
Stun,
|
|
Parry,
|
|
Block,
|
|
Bleed,
|
|
Leech,
|
|
Airborne,
|
|
Untouchable,
|
|
Deadly,
|
|
Vulnerable,
|
|
Fury,
|
|
Blind,
|
|
Snare,
|
|
|
|
Empower,
|
|
|
|
Invert,
|
|
|
|
// magic
|
|
Hex,
|
|
Curse,
|
|
Banish,
|
|
Slow,
|
|
Haste,
|
|
Enslave,
|
|
Mesmerise,
|
|
Amplify,
|
|
Silence,
|
|
|
|
// magic immunity
|
|
Shield,
|
|
|
|
// effects over time
|
|
Triage,
|
|
Decay,
|
|
Regen,
|
|
Siphon,
|
|
|
|
SpeedSiphon,
|
|
SpeedIncrease,
|
|
|
|
Ko,
|
|
}
|
|
|
|
impl Effect {
|
|
pub fn immune(&self, skill: Skill) -> bool {
|
|
match self {
|
|
Effect::Parry => match skill.category() {
|
|
Category::Blue => false,
|
|
Category::Red => true,
|
|
_ => false,
|
|
},
|
|
Effect::Shield => match skill.category() {
|
|
Category::Blue => true,
|
|
Category::Red => false,
|
|
_ => false,
|
|
},
|
|
Effect::Banish => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn disables_skill(&self, skill: Skill) -> bool {
|
|
match self {
|
|
Effect::Stun => true,
|
|
Effect::Hex => true,
|
|
Effect::Banish => true,
|
|
Effect::Silence => match skill.category() {
|
|
Category::Blue => true,
|
|
Category::Red => false,
|
|
_ => false,
|
|
},
|
|
Effect::Snare => match skill.category() {
|
|
Category::Blue => false,
|
|
Category::Red => true,
|
|
_ => false,
|
|
},
|
|
|
|
Effect::Ko => match skill.category() {
|
|
Category::BlueTick => false,
|
|
_ => true,
|
|
},
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn modifications(&self) -> Vec<Stat> {
|
|
match self {
|
|
Effect::Empower => vec![Stat::RedDamage],
|
|
Effect::Vulnerable => vec![Stat::RedDamageTaken],
|
|
Effect::Block => vec![Stat::RedDamageTaken],
|
|
|
|
Effect::Amplify => vec![Stat::BlueDamage],
|
|
Effect::Curse => vec![Stat::BlueDamageTaken],
|
|
|
|
Effect::Haste => vec![Stat::Speed],
|
|
Effect::Slow => vec![Stat::Speed],
|
|
|
|
_ => vec![],
|
|
}
|
|
}
|
|
|
|
// maybe increase by rng
|
|
// roll little endian bits
|
|
// and OR with base stat
|
|
pub fn apply(&self, value: u64) -> u64 {
|
|
match self {
|
|
Effect::Empower => value << 1,
|
|
Effect::Vulnerable => value << 1,
|
|
Effect::Block => value >> 1,
|
|
|
|
Effect::Amplify => value << 1,
|
|
Effect::Curse => value << 1,
|
|
|
|
Effect::Haste => value << 1,
|
|
Effect::Slow => value >> 1,
|
|
|
|
_ => {
|
|
println!("{:?} does not have a mod effect", self);
|
|
return value;
|
|
},
|
|
}
|
|
}
|
|
|
|
pub fn category(&self) -> Category {
|
|
match self {
|
|
// physical
|
|
Effect::Stun => Category::RedDebuff,
|
|
Effect::Block => Category::RedBuff,
|
|
Effect::Parry => Category::RedBuff,
|
|
Effect::Bleed => Category::RedDebuff,
|
|
Effect::Leech => Category::RedDebuff,
|
|
Effect::Airborne => Category::RedDebuff,
|
|
Effect::Untouchable => Category::RedBuff,
|
|
Effect::Deadly => Category::RedBuff,
|
|
Effect::Vulnerable => Category::RedDebuff,
|
|
Effect::Fury => Category::RedBuff,
|
|
Effect::Blind => Category::RedDebuff,
|
|
Effect::Snare => Category::RedDebuff,
|
|
|
|
Effect::Empower => Category::RedBuff,
|
|
|
|
// magic
|
|
Effect::Hex => Category::BlueDebuff,
|
|
Effect::Curse => Category::BlueDebuff,
|
|
Effect::Banish => Category::BlueDebuff, // todo randomise
|
|
Effect::Slow => Category::BlueDebuff,
|
|
Effect::Haste => Category::BlueBuff,
|
|
Effect::Enslave => Category::BlueDebuff,
|
|
Effect::Mesmerise => Category::BlueDebuff,
|
|
Effect::Amplify => Category::BlueBuff,
|
|
Effect::Silence => Category::BlueDebuff,
|
|
|
|
// magic immunity
|
|
Effect::Shield => Category::BlueBuff,
|
|
Effect::Invert => Category::GreenBuff,
|
|
|
|
// effects over time
|
|
Effect::Triage => Category::BlueBuff,
|
|
Effect::Decay => Category::BlueDebuff,
|
|
Effect::Regen => Category::BlueBuff,
|
|
Effect::Siphon => Category::BlueDebuff,
|
|
|
|
Effect::SpeedSiphon => Category::BlueDebuff,
|
|
Effect::SpeedIncrease => Category::BlueBuff,
|
|
|
|
Effect::Ko => Category::Ko,
|
|
}
|
|
}
|
|
|
|
pub fn duration(&self) -> u8 {
|
|
match self {
|
|
Effect::Stun => 2,
|
|
Effect::Block => 1,
|
|
Effect::Parry => 1,
|
|
|
|
Effect::Vulnerable => 2,
|
|
Effect::Snare => 2,
|
|
|
|
Effect::Empower => 2,
|
|
|
|
Effect::Hex => 2,
|
|
Effect::Curse => 2,
|
|
Effect::Banish => 1,
|
|
|
|
Effect::Slow => 2,
|
|
Effect::Haste => 2,
|
|
|
|
Effect::Amplify => 2,
|
|
Effect::Silence => 2,
|
|
|
|
Effect::Shield => 2,
|
|
|
|
Effect::Triage => 3,
|
|
Effect::Decay => 3,
|
|
Effect::Siphon => 2,
|
|
|
|
_ => {
|
|
println!("{:?} does not have a duration", self);
|
|
return 1;
|
|
},
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
|
|
pub enum Category {
|
|
Red,
|
|
RedDamage,
|
|
RedDebuff,
|
|
RedBuff,
|
|
RedTick,
|
|
Blue,
|
|
BlueDamage,
|
|
BlueDebuff,
|
|
BlueBuff,
|
|
BlueTick,
|
|
Green,
|
|
GreenDamage,
|
|
GreenBuff,
|
|
Ko,
|
|
}
|
|
|
|
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
|
|
pub enum Skill {
|
|
Attack,
|
|
|
|
// -----------------
|
|
// Nature
|
|
// -----------------
|
|
Block, // reduce damage
|
|
Parry, // avoid all damage
|
|
Snare,
|
|
|
|
Recharge,
|
|
Reflect,
|
|
Ruin,
|
|
Slay,
|
|
Strangle,
|
|
Clutch,
|
|
Taunt,
|
|
Toxic,
|
|
Invert,
|
|
|
|
Strike,
|
|
Stun,
|
|
// Evade, // actively evade
|
|
|
|
// -----------------
|
|
// Nonviolence
|
|
// -----------------
|
|
Heal,
|
|
Triage, // hot
|
|
TriageTick,
|
|
Throw, // no damage stun, adds vulnerable
|
|
|
|
// Sleep,
|
|
// Nightmare,
|
|
|
|
// -------------------
|
|
// Destruction
|
|
// -------------------
|
|
Blast,
|
|
Amplify,
|
|
Decay, // dot
|
|
DecayTick, // dot
|
|
Siphon,
|
|
SiphonTick,
|
|
Curse,
|
|
|
|
// -----------------
|
|
// Purity
|
|
// -----------------
|
|
Empower,
|
|
Shield,
|
|
Silence,
|
|
Purify,
|
|
Purge,
|
|
|
|
// -----------------
|
|
// Chaos
|
|
// -----------------
|
|
Banish,
|
|
Hex,
|
|
Haste,
|
|
Slow,
|
|
|
|
// used by tests, no cd, no damage
|
|
TestTouch,
|
|
TestStun,
|
|
TestBlock,
|
|
TestParry,
|
|
TestSiphon,
|
|
}
|
|
|
|
impl Skill {
|
|
pub fn base_cd(&self) -> Cooldown {
|
|
match self {
|
|
Skill::Attack => None,
|
|
Skill::Strike => None,
|
|
Skill::Block => None, // reduce damage
|
|
Skill::Parry => None, // avoid all damage
|
|
Skill::Snare => Some(1),
|
|
Skill::Stun => Some(1),
|
|
Skill::Heal => None,
|
|
Skill::Triage => None, // hot
|
|
Skill::TriageTick => None,
|
|
Skill::Throw => Some(1), // no damage stun, adds vulnerable
|
|
Skill::Blast => None,
|
|
Skill::Amplify => Some(1),
|
|
Skill::Invert => Some(1),
|
|
Skill::Decay => None, // dot
|
|
Skill::DecayTick => None,
|
|
Skill::Siphon => Some(1),
|
|
Skill::SiphonTick => None,
|
|
Skill::Curse => Some(1),
|
|
Skill::Empower => Some(1),
|
|
Skill::Shield => None,
|
|
Skill::Silence => Some(1),
|
|
Skill::Purify => None,
|
|
Skill::Purge => None,
|
|
Skill::Banish => Some(1),
|
|
Skill::Hex => None,
|
|
Skill::Haste => None,
|
|
Skill::Slow => None,
|
|
Skill::Reflect => Some(2),
|
|
Skill::Recharge => Some(2),
|
|
Skill::Ruin => Some(1),
|
|
Skill::Slay => None,
|
|
Skill::Strangle => Some(1),
|
|
Skill::Clutch => Some(2),
|
|
Skill::Taunt => Some(1),
|
|
Skill::Toxic => Some(1),
|
|
|
|
// -----------------
|
|
// Test
|
|
// -----------------
|
|
Skill::TestTouch => None,
|
|
Skill::TestStun => None,
|
|
Skill::TestBlock => None,
|
|
Skill::TestSiphon => None,
|
|
Skill::TestParry => None,
|
|
}
|
|
}
|
|
|
|
pub fn category(&self) -> Category {
|
|
match self {
|
|
Skill::Attack => Category::Red,
|
|
Skill::Strike => Category::Red,
|
|
|
|
// -----------------
|
|
// Nature
|
|
// -----------------
|
|
Skill::Block => Category::Red, // reduce damage
|
|
Skill::Parry => Category::Red, // avoid all damage
|
|
Skill::Snare => Category::Red,
|
|
|
|
|
|
|
|
Skill::Stun => Category::Red,
|
|
|
|
// -----------------
|
|
// Technology
|
|
// -----------------
|
|
|
|
// -----------------
|
|
// Preservation
|
|
// -----------------
|
|
Skill::Heal => Category::Red,
|
|
Skill::Triage => Category::Blue, // hot
|
|
Skill::TriageTick => Category::BlueTick, // hot
|
|
Skill::Throw => Category::Red, // no damage stun, adds vulnerable
|
|
|
|
// -----------------
|
|
// Destruction
|
|
// -----------------
|
|
Skill::Invert => Category::Green,
|
|
Skill::Blast => Category::Blue,
|
|
Skill::Amplify => Category::Blue,
|
|
Skill::Decay => Category::Blue, // dot
|
|
Skill::DecayTick => Category::BlueTick, // hot
|
|
Skill::Siphon => Category::Blue,
|
|
Skill::SiphonTick => Category::BlueTick, // hot
|
|
Skill::Curse => Category::Blue,
|
|
|
|
// -----------------
|
|
// Purity
|
|
// -----------------
|
|
Skill::Empower => Category::Red,
|
|
Skill::Shield => Category::Blue,
|
|
Skill::Silence => Category::Blue,
|
|
Skill::Purify => Category::Blue,
|
|
Skill::Purge => Category::Blue,
|
|
|
|
// -----------------
|
|
// Chaos
|
|
// -----------------
|
|
Skill::Banish => Category::Blue,
|
|
Skill::Hex => Category::Blue,
|
|
// Skill::Lag => 2, //
|
|
Skill::Haste => Category::Blue,
|
|
Skill::Slow => Category::Blue,
|
|
|
|
|
|
// WRONG
|
|
Skill::Recharge => Category::Blue,
|
|
Skill::Reflect => Category::Blue,
|
|
Skill::Ruin => Category::Blue,
|
|
Skill::Slay => Category::Blue,
|
|
Skill::Strangle => Category::Blue,
|
|
Skill::Clutch => Category::Blue,
|
|
Skill::Taunt => Category::Blue,
|
|
Skill::Toxic => Category::Blue,
|
|
|
|
|
|
// -----------------
|
|
// Test
|
|
// -----------------
|
|
Skill::TestTouch => Category::Red,
|
|
Skill::TestStun => Category::Red,
|
|
Skill::TestParry => Category::Red,
|
|
Skill::TestBlock => Category::Red,
|
|
Skill::TestSiphon => Category::Blue,
|
|
}
|
|
}
|
|
|
|
pub fn ko_castable(&self) -> bool {
|
|
match self {
|
|
Skill::TriageTick => true,
|
|
Skill::DecayTick => true,
|
|
Skill::SiphonTick => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn speed(&self) -> u8 {
|
|
match self {
|
|
|
|
// defensive block
|
|
Skill::Block => 10, // reduce damage
|
|
Skill::Parry => 10, // avoid all damage
|
|
Skill::Snare => 10,
|
|
Skill::Shield => 10, // avoid magic damage,
|
|
|
|
// fast phys combat
|
|
Skill::Attack => 5,
|
|
Skill::Strike => 10,
|
|
Skill::Banish => 5,
|
|
Skill::Blast => 5,
|
|
Skill::Decay => 5, // dot
|
|
|
|
// magic combat trickery
|
|
Skill::Invert => 3, // hot
|
|
Skill::Triage => 3, // hot
|
|
Skill::Slow => 3,
|
|
Skill::Amplify => 3,
|
|
Skill::Curse => 3,
|
|
Skill::Empower => 3,
|
|
Skill::Haste => 3,
|
|
|
|
// general combat
|
|
Skill::DecayTick => 2, // hot
|
|
Skill::Siphon => 2,
|
|
Skill::SiphonTick => 2, // hot
|
|
Skill::Hex => 2,
|
|
Skill::Silence => 2,
|
|
Skill::Stun => 2,
|
|
Skill::Throw => 2, // no damage stun, adds vulnerable
|
|
Skill::TriageTick => 2, // hot
|
|
|
|
Skill::Heal => 1,
|
|
Skill::Purify => 1,
|
|
Skill::Purge => 1,
|
|
|
|
Skill::Recharge => 1,
|
|
Skill::Reflect => 1,
|
|
Skill::Ruin => 1,
|
|
Skill::Slay => 1,
|
|
Skill::Strangle => 1,
|
|
Skill::Clutch => 1,
|
|
Skill::Taunt => 1,
|
|
Skill::Toxic => 1,
|
|
|
|
|
|
// unimplemented
|
|
// Skill::Lag => 2, //
|
|
|
|
// -----------------
|
|
// Test
|
|
// -----------------
|
|
Skill::TestTouch => 10,
|
|
Skill::TestStun => 5,
|
|
Skill::TestBlock => 10,
|
|
Skill::TestParry => 10,
|
|
Skill::TestSiphon => 10,
|
|
}
|
|
}
|
|
|
|
pub fn resolve(&self, source: &mut Cryp, target: &mut Cryp) -> Resolution {
|
|
let mut rng = thread_rng();
|
|
let _base: u64 = rng.gen();
|
|
|
|
let speed = source.skill_speed(*self);
|
|
|
|
let mut resolution = Resolution { skill: *self, results: vec![], disable: source.disabled(*self), speed };
|
|
|
|
if target.is_ko() {
|
|
return resolution;
|
|
}
|
|
|
|
if resolution.disable.disabled {
|
|
return resolution;
|
|
}
|
|
|
|
match self.category() == Category::Red {
|
|
true => {
|
|
if let Some(evasion) = target.evade(*self) {
|
|
resolution.results.push(evasion);
|
|
return resolution;
|
|
}
|
|
},
|
|
false => (),
|
|
}
|
|
|
|
match self {
|
|
Skill::Amplify => amplify(source, target, resolution), // increase magic damage
|
|
Skill::Attack => attack(source, target, resolution),
|
|
Skill::Banish => banish(source, target, resolution), // TODO prevent all actions
|
|
Skill::Blast => blast(source, target, resolution),
|
|
Skill::Block => block(source, target, resolution),
|
|
Skill::Curse => curse(source, target, resolution),
|
|
Skill::Decay => decay(source, target, resolution), // dot
|
|
Skill::DecayTick => decay_tick(source, target, resolution), // dot
|
|
Skill::Empower => empower(source, target, resolution), // increased phys damage
|
|
Skill::Haste => haste(source, target, resolution), // speed slow
|
|
Skill::Heal => heal(source, target, resolution),
|
|
Skill::Hex => hex(source, target, resolution), // todo prevent casting
|
|
Skill::Invert => invert(source, target, resolution), // todo prevent casting
|
|
Skill::Parry => parry(source, target, resolution),
|
|
Skill::Purge => purge(source, target, resolution), // dispel all buffs
|
|
Skill::Purify => purify(source, target, resolution), // dispel all debuffs
|
|
Skill::Recharge => recharge(source, target, resolution), // target is immune to magic damage and fx
|
|
Skill::Shield => shield(source, target, resolution), // target is immune to magic damage and fx
|
|
Skill::Silence => silence(source, target, resolution), // target cannot cast spells
|
|
Skill::Siphon => siphon(source, target, resolution),
|
|
Skill::SiphonTick => siphon_tick(source, target, resolution), // hot
|
|
Skill::Slow => slow(source, target, resolution), // speed slow
|
|
Skill::Snare => snare(source, target, resolution), // TODO prevent physical moves
|
|
Skill::Strike => strike(source, target, resolution),
|
|
Skill::Stun => stun(source, target, resolution),
|
|
Skill::Throw => throw(source, target, resolution), // no damage stun, adds vulnerable
|
|
Skill::Triage => triage(source, target, resolution), // hot
|
|
Skill::TriageTick => triage_tick(source, target, resolution), // hot
|
|
|
|
Skill::Reflect => unimplemented!(),
|
|
Skill::Ruin => unimplemented!(),
|
|
Skill::Slay => unimplemented!(),
|
|
Skill::Strangle => unimplemented!(),
|
|
Skill::Clutch => unimplemented!(),
|
|
Skill::Taunt => unimplemented!(),
|
|
Skill::Toxic => unimplemented!(),
|
|
|
|
// -----------------
|
|
// Test
|
|
// -----------------
|
|
Skill::TestTouch => Resolution { skill: Skill::TestTouch, results: vec![], disable: Disable::new(), speed: 0 },
|
|
Skill::TestStun => stun(source, target, resolution),
|
|
Skill::TestBlock => block(source, target, resolution),
|
|
Skill::TestParry => parry(source, target, resolution),
|
|
Skill::TestSiphon => siphon(source, target, resolution),
|
|
}
|
|
}
|
|
|
|
pub fn self_targeting(&self) -> bool {
|
|
match self {
|
|
Skill::Block => true,
|
|
Skill::Parry => true,
|
|
Skill::TestBlock => true,
|
|
Skill::TestParry => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn defensive(&self) -> bool {
|
|
match self {
|
|
Skill::Heal |
|
|
Skill::Triage |
|
|
Skill::Empower |
|
|
Skill::Purify |
|
|
Skill::Parry |
|
|
Skill::Block => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn attack(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution {
|
|
let amount = cryp.red_damage();
|
|
resolution.results.push(target.deal_red_damage(Skill::Attack, amount));
|
|
return resolution;
|
|
}
|
|
|
|
fn stun(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution {
|
|
let effect = CrypEffect { effect: Effect::Stun, duration: Effect::Stun.duration(), tick: None };
|
|
resolution.results.push(target.add_effect(Skill::Stun, effect));
|
|
return resolution;
|
|
}
|
|
|
|
fn throw(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution {
|
|
let stun = CrypEffect { effect: Effect::Stun, duration: Effect::Stun.duration(), tick: None };
|
|
let vulnerable = CrypEffect { effect: Effect::Vulnerable, duration: Effect::Vulnerable.duration(), tick: None };
|
|
|
|
resolution.results.push(target.add_effect(Skill::Throw, stun));
|
|
resolution.results.push(target.add_effect(Skill::Throw, vulnerable));
|
|
|
|
return resolution;
|
|
}
|
|
|
|
|
|
fn block(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution {
|
|
let block = CrypEffect { effect: Effect::Block, duration: Effect::Block.duration(), tick: None };
|
|
resolution.results.push(target.add_effect(Skill::Block, block));
|
|
return resolution;
|
|
}
|
|
|
|
fn parry(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution {
|
|
let effect = CrypEffect { effect: Effect::Parry, duration: Effect::Parry.duration(), tick: None };
|
|
resolution.results.push(target.add_effect(Skill::Parry, effect));
|
|
return resolution;
|
|
}
|
|
|
|
|
|
fn snare(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution {
|
|
let snare = CrypEffect { effect: Effect::Snare, duration: Effect::Snare.duration(), tick: None };
|
|
resolution.results.push(target.add_effect(Skill::Snare, snare));
|
|
return resolution;
|
|
}
|
|
|
|
fn empower(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution {
|
|
let empower = CrypEffect { effect: Effect::Empower, duration: Effect::Empower.duration(), tick: None };
|
|
resolution.results.push(target.add_effect(Skill::Empower, empower));
|
|
return resolution;
|
|
}
|
|
|
|
fn heal(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution {
|
|
let amount = cryp.green_damage();
|
|
resolution.results.push(target.deal_green_damage(Skill::Heal, amount));
|
|
return resolution;
|
|
}
|
|
|
|
fn triage(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution {
|
|
let effect = CrypEffect {
|
|
effect: Effect::Triage,
|
|
duration: Effect::Triage.duration(),
|
|
tick: Some(Cast::new_tick(cryp, target, Skill::TriageTick)),
|
|
};
|
|
target.add_effect(Skill::Triage, effect);
|
|
return resolution;
|
|
}
|
|
|
|
fn triage_tick(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution {
|
|
let amount = cryp.blue_damage().wrapping_div(2);
|
|
resolution.results.push(target.deal_green_damage(Skill::TriageTick, amount));
|
|
return resolution;
|
|
}
|
|
|
|
fn blast(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution {
|
|
let amount = cryp.blue_damage();
|
|
resolution.results.push(target.deal_blue_damage(Skill::Blast, amount));
|
|
return resolution;
|
|
}
|
|
|
|
fn amplify(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution {
|
|
let amplify = CrypEffect { effect: Effect::Amplify, duration: Effect::Amplify.duration(), tick: None };
|
|
resolution.results.push(target.add_effect(Skill::Amplify, amplify));
|
|
return resolution;;
|
|
}
|
|
|
|
fn haste(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution {
|
|
let effect = CrypEffect { effect: Effect::Haste, duration: Effect::Haste.duration(), tick: None };
|
|
resolution.results.push(target.add_effect(Skill::Haste, effect));
|
|
return resolution;;
|
|
}
|
|
|
|
fn slow(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution {
|
|
let effect = CrypEffect { effect: Effect::Slow, duration: Effect::Slow.duration(), tick: None };
|
|
resolution.results.push(target.add_effect(Skill::Slow, effect));
|
|
return resolution;;
|
|
}
|
|
|
|
fn decay(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution {
|
|
let decay = CrypEffect {
|
|
effect: Effect::Decay,
|
|
duration: Effect::Decay.duration(),
|
|
tick: Some(Cast::new_tick(cryp, target, Skill::DecayTick)),
|
|
};
|
|
resolution.results.push(target.add_effect(Skill::Decay, decay));
|
|
return resolution;
|
|
}
|
|
|
|
fn decay_tick(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution {
|
|
let amount = cryp.blue_damage();
|
|
resolution.results.push(target.deal_blue_damage(Skill::DecayTick, amount));
|
|
return resolution;
|
|
}
|
|
|
|
fn hex(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution {
|
|
let hex = CrypEffect { effect: Effect::Hex, duration: Effect::Hex.duration(), tick: None };
|
|
resolution.results.push(target.add_effect(Skill::Hex, hex));
|
|
return resolution;;
|
|
}
|
|
|
|
fn curse(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution {
|
|
let curse = CrypEffect { effect: Effect::Curse, duration: Effect::Curse.duration(), tick: None };
|
|
resolution.results.push(target.add_effect(Skill::Curse, curse));
|
|
return resolution;;
|
|
}
|
|
|
|
fn invert(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution {
|
|
let effect = CrypEffect { effect: Effect::Invert, duration: Effect::Invert.duration(), tick: None };
|
|
resolution.results.push(target.add_effect(Skill::Invert, effect));
|
|
return resolution;;
|
|
}
|
|
|
|
fn recharge(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution {
|
|
resolution.results.push(target.recharge());
|
|
return resolution;
|
|
}
|
|
|
|
fn siphon(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution {
|
|
let siphon = CrypEffect {
|
|
effect: Effect::Siphon,
|
|
duration: Effect::Siphon.duration(),
|
|
tick: Some(Cast::new_tick(cryp, target, Skill::SiphonTick)),
|
|
};
|
|
resolution.results.push(target.add_effect(Skill::Siphon, siphon));
|
|
return resolution;;
|
|
}
|
|
|
|
fn siphon_tick(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution {
|
|
let amount = cryp.blue_damage();
|
|
let siphon_damage = target.deal_blue_damage(Skill::SiphonTick, amount);
|
|
resolution.results.push(siphon_damage.clone());
|
|
|
|
match siphon_damage {
|
|
ResolutionResult::Damage { amount, mitigation: _, category: _, immunity } => {
|
|
if !immunity.immune {
|
|
resolution.results.push(cryp.deal_green_damage(Skill::Heal, amount));
|
|
}
|
|
},
|
|
_ => panic!("siphon tick damage not dealt {:?}", siphon_damage),
|
|
}
|
|
|
|
return resolution;
|
|
}
|
|
|
|
fn shield(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution {
|
|
let shield = CrypEffect { effect: Effect::Shield, duration: Effect::Shield.duration(), tick: None };
|
|
resolution.results.push(target.add_effect(Skill::Shield, shield));
|
|
return resolution;
|
|
}
|
|
|
|
fn silence(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution {
|
|
let silence = CrypEffect { effect: Effect::Silence, duration: Effect::Silence.duration(), tick: None };
|
|
resolution.results.push(target.add_effect(Skill::Silence, silence));
|
|
return resolution;
|
|
}
|
|
|
|
fn purge(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution {
|
|
let immunity = target.immune(Skill::Purge);
|
|
let immune = immunity.immune;
|
|
|
|
if !immune {
|
|
for (i, ce) in target.effects.clone().iter_mut().enumerate() {
|
|
if ce.effect.category() == Category::BlueBuff {
|
|
target.effects.remove(i);
|
|
resolution.results.push(ResolutionResult::Removal { effect: ce.effect, immunity: immunity.clone() });
|
|
}
|
|
}
|
|
}
|
|
|
|
return resolution;
|
|
}
|
|
|
|
fn purify(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution {
|
|
let immunity = target.immune(Skill::Purify);
|
|
let immune = immunity.immune;
|
|
|
|
if !immune {
|
|
for (i, ce) in target.effects.clone().iter_mut().enumerate() {
|
|
if ce.effect.category() == Category::BlueDebuff {
|
|
target.effects.remove(i);
|
|
resolution.results.push(ResolutionResult::Removal { effect: ce.effect, immunity: immunity.clone() });
|
|
}
|
|
}
|
|
}
|
|
|
|
return resolution;
|
|
}
|
|
|
|
fn banish(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution {
|
|
let banish = CrypEffect { effect: Effect::Banish, duration: Effect::Banish.duration(), tick: None };
|
|
resolution.results.push(target.add_effect(Skill::Banish, banish));
|
|
return resolution;
|
|
}
|
|
|
|
fn strike(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution {
|
|
let _amount = cryp.red_damage();
|
|
resolution.results.push(target.deal_red_damage(Skill::Attack, u64::max_value()));
|
|
return resolution;
|
|
}
|
|
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use skill::*;
|
|
|
|
#[test]
|
|
fn heal_test() {
|
|
let mut x = Cryp::new()
|
|
.named(&"muji".to_string())
|
|
.learn(Skill::Heal);
|
|
|
|
let mut y = Cryp::new()
|
|
.named(&"camel".to_string())
|
|
.learn(Skill::Heal);
|
|
|
|
x.deal_red_damage(Skill::Attack, 5);
|
|
|
|
heal(&mut y, &mut x, Resolution::new(Skill::Heal));
|
|
}
|
|
|
|
#[test]
|
|
fn decay_test() {
|
|
let mut x = Cryp::new()
|
|
.named(&"muji".to_string());
|
|
|
|
let mut y = Cryp::new()
|
|
.named(&"camel".to_string());
|
|
|
|
let mut log = vec![];
|
|
decay(&mut x, &mut y, Resolution::new(Skill::Triage));
|
|
|
|
assert!(y.effects.iter().any(|e| e.effect == Effect::Decay));
|
|
|
|
y.reduce_effect_durations(&mut log);
|
|
let _decay = y.effects.iter().find(|e| e.effect == Effect::Decay);
|
|
// assert!(y.hp() == y.hp().saturating_sub(decay.unwrap().tick.unwrap().amount));
|
|
}
|
|
|
|
#[test]
|
|
fn block_test() {
|
|
let mut x = Cryp::new()
|
|
.named(&"muji".to_string());
|
|
|
|
let mut y = Cryp::new()
|
|
.named(&"camel".to_string());
|
|
|
|
// ensure it doesn't have 0 pd
|
|
x.red_damage.force(100);
|
|
y.hp.force(500);
|
|
|
|
block(&mut y.clone(), &mut y, Resolution::new(Skill::Block));
|
|
assert!(y.effects.iter().any(|e| e.effect == Effect::Block));
|
|
|
|
let res = attack(&mut x, &mut y, Resolution::new(Skill::Attack));
|
|
|
|
match res.results[0] {
|
|
ResolutionResult::Damage { amount, mitigation: _, category: _, immunity: _ } => assert_eq!(amount, 50),
|
|
_ => panic!("not damage"),
|
|
};
|
|
}
|
|
|
|
#[test]
|
|
fn invert_test() {
|
|
let mut x = Cryp::new()
|
|
.named(&"muji".to_string());
|
|
|
|
let mut y = Cryp::new()
|
|
.named(&"camel".to_string());
|
|
|
|
// give red shield but reduce to 0
|
|
y.red_shield.force(64);
|
|
y.red_shield.reduce(64);
|
|
x.red_damage.force(256 + 64);
|
|
|
|
invert(&mut y.clone(), &mut y, Resolution::new(Skill::Invert));
|
|
assert!(y.is_inverted());
|
|
|
|
// heal should deal green damage
|
|
heal(&mut x, &mut y, Resolution::new(Skill::Heal));
|
|
assert!(y.hp() == 768);
|
|
|
|
// attack should heal and recharge red shield
|
|
let res = attack(&mut x, &mut y, Resolution::new(Skill::Attack));
|
|
assert!(y.hp() == 1024);
|
|
|
|
match res.results[0] {
|
|
ResolutionResult::Inversion { damage: _, healing: _, recharge, category: _, immunity: _ } => assert_eq!(recharge, 64),
|
|
_ => panic!("not inversion"),
|
|
};
|
|
}
|
|
|
|
|
|
#[test]
|
|
fn triage_test() {
|
|
let mut x = Cryp::new()
|
|
.named(&"muji".to_string());
|
|
|
|
let mut y = Cryp::new()
|
|
.named(&"pretaliation".to_string());
|
|
|
|
// ensure it doesn't have 0 sd
|
|
x.blue_damage.force(50);
|
|
|
|
// remove all mitigation
|
|
y.red_shield.force(0);
|
|
y.blue_shield.force(0);
|
|
|
|
y.deal_red_damage(Skill::Attack, 5);
|
|
let prev_hp = y.hp();
|
|
|
|
let res = Resolution::new(Skill::Triage);
|
|
|
|
triage(&mut x, &mut y, res);
|
|
|
|
assert!(y.effects.iter().any(|e| e.effect == Effect::Triage));
|
|
|
|
let res = Resolution::new(Skill::TriageTick);
|
|
triage_tick(&mut x, &mut y, res);
|
|
|
|
assert!(y.hp() > prev_hp);
|
|
}
|
|
|
|
#[test]
|
|
fn recharge_test() {
|
|
let mut x = Cryp::new()
|
|
.named(&"muji".to_string());
|
|
|
|
let mut y = Cryp::new()
|
|
.named(&"pretaliation".to_string());
|
|
|
|
y.red_shield.force(50);
|
|
y.blue_shield.force(50);
|
|
|
|
y.deal_red_damage(Skill::Attack, 5);
|
|
y.deal_blue_damage(Skill::Blast, 5);
|
|
|
|
let mut res = Resolution::new(Skill::Recharge);
|
|
res = recharge(&mut x, &mut y, res);
|
|
|
|
match res.results[0] {
|
|
ResolutionResult::Recharge { red, blue, immunity: _ } => {
|
|
assert!(red == 5);
|
|
assert!(blue == 5);
|
|
}
|
|
_ => panic!("result was not recharge"),
|
|
}
|
|
}
|
|
|
|
|
|
#[test]
|
|
fn silence_test() {
|
|
let mut x = Cryp::new()
|
|
.named(&"muji".to_string());
|
|
|
|
silence(&mut x.clone(), &mut x, Resolution::new(Skill::Silence));
|
|
assert!(x.effects.iter().any(|e| e.effect == Effect::Silence));
|
|
assert!(x.disabled(Skill::Silence).disabled);
|
|
}
|
|
|
|
#[test]
|
|
fn amplify_test() {
|
|
let mut x = Cryp::new()
|
|
.named(&"muji".to_string());
|
|
|
|
x.blue_damage.force(50);
|
|
|
|
amplify(&mut x.clone(), &mut x, Resolution::new(Skill::Amplify));
|
|
assert!(x.effects.iter().any(|e| e.effect == Effect::Amplify));
|
|
assert_eq!(x.blue_damage(), 100);
|
|
}
|
|
|
|
#[test]
|
|
fn purify_test() {
|
|
let mut x = Cryp::new()
|
|
.named(&"muji".to_string());
|
|
|
|
decay(&mut x.clone(), &mut x, Resolution::new(Skill::Decay));
|
|
assert!(x.effects.iter().any(|e| e.effect == Effect::Decay));
|
|
|
|
purify(&mut x.clone(), &mut x, Resolution::new(Skill::Purify));
|
|
assert!(!x.effects.iter().any(|e| e.effect == Effect::Decay));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// pub enum Skill {
|
|
// Attack,
|
|
|
|
// // -----------------
|
|
// // Nature
|
|
// // -----------------
|
|
// Block, // reduce damage
|
|
// Parry, // avoid all damage
|
|
// Snare,
|
|
|
|
// Paralyse,
|
|
|
|
// Strangle, // physical dot and disable
|
|
// Strike,
|
|
// Stun,
|
|
// // Evade, // actively evade
|
|
|
|
|
|
// // -----------------
|
|
// // Technology
|
|
// // -----------------
|
|
// Replicate,
|
|
// Swarm,
|
|
// Orbit,
|
|
// Repair,
|
|
// Scan, // track?
|
|
|
|
// // -----------------
|
|
// // Nonviolence
|
|
// // -----------------
|
|
// Heal,
|
|
// Triage, // hot
|
|
// TriageTick,
|
|
// Throw, // no damage stun, adds vulnerable
|
|
// Charm,
|
|
// Calm,
|
|
// Rez,
|
|
|
|
// // Sleep,
|
|
// // Nightmare,
|
|
|
|
// // -------------------
|
|
// // Destruction
|
|
// // -------------------
|
|
// Blast,
|
|
// Amplify,
|
|
// Decay, // dot
|
|
// DecayTick, // dot
|
|
// Siphon,
|
|
// SiphonTick,
|
|
// Curse,
|
|
// Plague, // aoe dot
|
|
// Ruin, // aoe
|
|
|
|
// // -----------------
|
|
// // Purity
|
|
// // -----------------
|
|
// Empower,
|
|
// Slay,
|
|
// Shield,
|
|
// Silence,
|
|
// Inquiry,
|
|
// Purify,
|
|
// Purge,
|
|
// // Precision,
|
|
|
|
// // -----------------
|
|
// // Chaos
|
|
// // -----------------
|
|
// Banish,
|
|
// Hex,
|
|
// Fear,
|
|
// Taunt,
|
|
// Pause, // speed slow
|
|
// Haste,
|
|
// Slow,
|
|
|
|
// // used by tests, no cd, no damage
|
|
// TestTouch,
|
|
// TestStun,
|
|
// TestBlock,
|
|
// TestParry,
|
|
// TestSiphon,
|
|
// }
|