mnml/server/src/skill.rs
2019-03-24 23:01:59 +11:00

1242 lines
37 KiB
Rust

use rand::{thread_rng, Rng};
use uuid::Uuid;
use cryp::{Cryp, CrypEffect, Stat};
use vbox::{Var};
use game::{Game};
#[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 finalise(&mut self, game: &mut Game) -> &mut Cast {
let mut results = vec![];
let mut source = game.cryp_by_id(self.source_cryp_id).unwrap().clone();
self.resolution.speed = source.skill_speed(self.skill);
let targets = match source.skill_is_aoe(self.skill) {
true => game.cryp_aoe_targets(self.target_cryp_id),
false => vec![self.target_cryp_id],
};
for target_id in targets {
// let mut source = game.cryp_by_id(self.source_cryp_id).unwrap();
let mut target = game.cryp_by_id(target_id).unwrap();
results.append(&mut self.skill.resolve(&mut source, target));
}
self.resolution.results = results;
self
}
pub fn used_cooldown(&self) -> bool {
return self.skill.base_cd().is_some();
}
}
pub type Disable = Vec<Effect>;
pub type Immunity = Vec<Effect>;
#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)]
pub enum ResolutionResult {
TargetKo,
Disable { disable: Disable },
Immunity { immunity: Immunity },
Damage { amount: u64, mitigation: u64, category: Category },
Healing { amount: u64, overhealing: u64 },
Recharge { red: u64, blue: u64 },
Inversion { healing: u64, damage: u64, recharge: u64, category: Category },
Effect { effect: Effect, duration: u8 },
Removal { effect: Effect },
Evasion { skill: Skill, evasion_rating: u64 },
}
#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)]
pub struct Resolution {
pub skill: Skill,
pub speed: u64,
pub results: Vec<ResolutionResult>,
}
impl Resolution {
fn new(skill: Skill) -> Resolution {
Resolution { skill, results: vec![], 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,
Clutch,
Reflect,
Empower,
Invert,
Strangle,
Strangling,
// 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::Strangle => skill != Skill::StrangleTick,
Effect::Strangling => true,
Effect::Banish => true,
_ => false,
}
}
pub fn disables_skill(&self, skill: Skill) -> bool {
match self {
Effect::Stun => true,
Effect::Hex => true,
Effect::Banish => true,
Effect::Strangle => true,
Effect::Strangling => skill != Skill::StrangleTick,
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::Clutch => Category::RedBuff,
Effect::Empower => Category::RedBuff,
Effect::Strangle => Category::RedDebuff,
Effect::Strangling => 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::Reflect => 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::Clutch => 1,
Effect::Reflect => 1,
Effect::Strangle => 2,
Effect::Strangling => 2,
Effect::Vulnerable => 2,
Effect::Snare => 2,
Effect::Empower => 2,
Effect::Invert => 1,
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,
Clutch,
Taunt,
Toxic,
Invert,
Strangle,
StrangleTick,
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(2),
Skill::StrangleTick => None,
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,
Skill::Strangle => Category::Red,
Skill::StrangleTick => Category::Red,
// -----------------
// Nature
// -----------------
Skill::Block => Category::Red, // reduce damage
Skill::Parry => Category::Red, // avoid all damage
Skill::Snare => Category::Red,
Skill::Clutch => 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::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 {
// -----------------
// Test
// -----------------
Skill::TestTouch => 10,
Skill::TestStun => 5,
Skill::TestBlock => 10,
Skill::TestParry => 10,
Skill::TestSiphon => 10,
Skill::Strike => u8::max_value(),
Skill::SiphonTick => Var::from(Skill::Siphon).speed(),
Skill::DecayTick => Var::from(Skill::Decay).speed(),
Skill::TriageTick => Var::from(Skill::Triage).speed(),
Skill::StrangleTick => Var::from(Skill::Strangle).speed(),
_ => Var::from(*self).speed(),
}
}
pub fn aoe(&self) -> bool {
match self {
Skill::Ruin => true,
_ => false,
}
}
pub fn resolve(&self, source: &mut Cryp, target: &mut Cryp) -> Vec<ResolutionResult> {
let mut rng = thread_rng();
let _base: u64 = rng.gen();
let mut results = vec![];
if let Some(disable) = source.disabled(*self) {
results.push(ResolutionResult::Disable { disable });
return results;
}
// match self.category() == Category::Red {
// true => {
// if let Some(evasion) = target.evade(*self) {
// results.push(evasion);
// return resolution;
// }
// },
// false => (),
// }
if target.is_ko() {
results.push(ResolutionResult::TargetKo);
return results;
}
if target.is_reflecting() {
// guard against overflow
if source.is_reflecting() {
return results;
}
return self.resolve(target, source);
}
match self {
Skill::Amplify => amplify(source, target, results), // increase magic damage
Skill::Attack => attack(source, target, results),
Skill::Banish => banish(source, target, results), // TODO prevent all actions
Skill::Blast => blast(source, target, results),
Skill::Block => block(source, target, results),
Skill::Curse => curse(source, target, results),
Skill::Decay => decay(source, target, results), // dot
Skill::DecayTick => decay_tick(source, target, results), // dot
Skill::Empower => empower(source, target, results), // increased phys damage
Skill::Haste => haste(source, target, results), // speed slow
Skill::Heal => heal(source, target, results),
Skill::Hex => hex(source, target, results), // todo prevent casting
Skill::Invert => invert(source, target, results), // todo prevent casting
Skill::Parry => parry(source, target, results),
Skill::Purge => purge(source, target, results), // dispel all buffs
Skill::Purify => purify(source, target, results), // dispel all debuffs
Skill::Recharge => recharge(source, target, results), // target is immune to magic damage and fx
Skill::Shield => shield(source, target, results), // target is immune to magic damage and fx
Skill::Silence => silence(source, target, results), // target cannot cast spells
Skill::Siphon => siphon(source, target, results),
Skill::SiphonTick => siphon_tick(source, target, results), // hot
Skill::Slow => slow(source, target, results), // speed slow
Skill::Snare => snare(source, target, results), // TODO prevent physical moves
Skill::Strike => strike(source, target, results),
Skill::Stun => stun(source, target, results),
Skill::Throw => throw(source, target, results), // no damage stun, adds vulnerable
Skill::Triage => triage(source, target, results), // hot
Skill::TriageTick => triage_tick(source, target, results), // hot
Skill::Clutch => clutch(source, target, results),
Skill::Strangle => strangle(source, target, results),
Skill::StrangleTick => strangle_tick(source, target, results),
Skill::Reflect => reflect(source, target, results),
Skill::Ruin => unimplemented!(),
Skill::Slay => unimplemented!(),
Skill::Taunt => unimplemented!(),
Skill::Toxic => unimplemented!(),
// -----------------
// Test
// -----------------
Skill::TestTouch => results,
Skill::TestStun => stun(source, target, results),
Skill::TestBlock => block(source, target, results),
Skill::TestParry => parry(source, target, results),
Skill::TestSiphon => siphon(source, target, results),
}
}
pub fn self_targeting(&self) -> bool {
match self {
Skill::Block => true,
Skill::Parry => true,
Skill::Clutch => 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 results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let amount = cryp.red_damage();
results.push(target.deal_red_damage(Skill::Attack, amount));
return results;
}
fn stun(_cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let effect = CrypEffect { effect: Effect::Stun, duration: Effect::Stun.duration(), tick: None };
results.push(target.add_effect(Skill::Stun, effect));
return results;
}
fn clutch(_cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let effect = CrypEffect { effect: Effect::Clutch, duration: Effect::Clutch.duration(), tick: None };
results.push(target.add_effect(Skill::Clutch, effect));
return results;
}
fn throw(_cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let stun = CrypEffect { effect: Effect::Stun, duration: Effect::Stun.duration(), tick: None };
let vulnerable = CrypEffect { effect: Effect::Vulnerable, duration: Effect::Vulnerable.duration(), tick: None };
results.push(target.add_effect(Skill::Throw, stun));
results.push(target.add_effect(Skill::Throw, vulnerable));
return results;
}
fn strangle(cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let target_stun = CrypEffect {
effect: Effect::Strangle,
duration: Effect::Strangle.duration(),
tick: Some(Cast::new_tick(cryp, target, Skill::StrangleTick))
};
let attacker_immunity = CrypEffect { effect: Effect::Strangling, duration: Effect::Strangling.duration(), tick: None };
results.push(target.add_effect(Skill::Strangle, target_stun));
results.push(cryp.add_effect(Skill::Strangle, attacker_immunity));
return strangle_tick(cryp, target, results);
}
fn strangle_tick(cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let amount = cryp.red_damage();
results.push(target.deal_red_damage(Skill::StrangleTick, amount));
// remove immunity if target ko
if target.is_ko() {
let i = cryp.effects
.iter()
.position(|e| e.effect == Effect::Strangling)
.expect("no strangling on cryp");
cryp.effects.remove(i);
}
return results;
}
fn block(_cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let block = CrypEffect { effect: Effect::Block, duration: Effect::Block.duration(), tick: None };
results.push(target.add_effect(Skill::Block, block));
return results;
}
fn parry(_cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let effect = CrypEffect { effect: Effect::Parry, duration: Effect::Parry.duration(), tick: None };
results.push(target.add_effect(Skill::Parry, effect));
return results;
}
fn snare(_cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let snare = CrypEffect { effect: Effect::Snare, duration: Effect::Snare.duration(), tick: None };
results.push(target.add_effect(Skill::Snare, snare));
return results;
}
fn empower(_cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let empower = CrypEffect { effect: Effect::Empower, duration: Effect::Empower.duration(), tick: None };
results.push(target.add_effect(Skill::Empower, empower));
return results;
}
fn heal(cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let amount = cryp.green_damage();
results.push(target.deal_green_damage(Skill::Heal, amount));
return results;
}
fn triage(cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let effect = CrypEffect {
effect: Effect::Triage,
duration: Effect::Triage.duration(),
tick: Some(Cast::new_tick(cryp, target, Skill::TriageTick)),
};
results.push(target.add_effect(Skill::Triage, effect));
return triage_tick(cryp, target, results);
}
fn triage_tick(cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let amount = cryp.blue_damage().wrapping_div(2);
results.push(target.deal_green_damage(Skill::TriageTick, amount));
return results;
}
fn blast(cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let amount = cryp.blue_damage();
results.push(target.deal_blue_damage(Skill::Blast, amount));
return results;
}
fn amplify(_cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let amplify = CrypEffect { effect: Effect::Amplify, duration: Effect::Amplify.duration(), tick: None };
results.push(target.add_effect(Skill::Amplify, amplify));
return results;;
}
fn haste(_cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let effect = CrypEffect { effect: Effect::Haste, duration: Effect::Haste.duration(), tick: None };
results.push(target.add_effect(Skill::Haste, effect));
return results;;
}
fn slow(_cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let effect = CrypEffect { effect: Effect::Slow, duration: Effect::Slow.duration(), tick: None };
results.push(target.add_effect(Skill::Slow, effect));
return results;;
}
fn decay(cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let decay = CrypEffect {
effect: Effect::Decay,
duration: Effect::Decay.duration(),
tick: Some(Cast::new_tick(cryp, target, Skill::DecayTick)),
};
results.push(target.add_effect(Skill::Decay, decay));
return decay_tick(cryp, target, results);
}
fn decay_tick(cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let amount = cryp.blue_damage();
results.push(target.deal_blue_damage(Skill::DecayTick, amount));
return results;
}
fn hex(_cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let hex = CrypEffect { effect: Effect::Hex, duration: Effect::Hex.duration(), tick: None };
results.push(target.add_effect(Skill::Hex, hex));
return results;;
}
fn curse(_cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let curse = CrypEffect { effect: Effect::Curse, duration: Effect::Curse.duration(), tick: None };
results.push(target.add_effect(Skill::Curse, curse));
return results;;
}
fn invert(_cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let effect = CrypEffect { effect: Effect::Invert, duration: Effect::Invert.duration(), tick: None };
results.push(target.add_effect(Skill::Invert, effect));
return results;;
}
fn reflect(_cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let effect = CrypEffect { effect: Effect::Reflect, duration: Effect::Reflect.duration(), tick: None };
results.push(target.add_effect(Skill::Reflect, effect));
return results;;
}
fn recharge(_cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
results.push(target.recharge());
return results;
}
fn siphon(cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let siphon = CrypEffect {
effect: Effect::Siphon,
duration: Effect::Siphon.duration(),
tick: Some(Cast::new_tick(cryp, target, Skill::SiphonTick)),
};
results.push(target.add_effect(Skill::Siphon, siphon));
return siphon_tick(cryp, target, results);
}
fn siphon_tick(cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let amount = cryp.blue_damage();
let siphon_damage = target.deal_blue_damage(Skill::SiphonTick, amount);
results.push(siphon_damage.clone());
match siphon_damage {
ResolutionResult::Damage { amount, mitigation: _, category: _, } => {
results.push(cryp.deal_green_damage(Skill::Heal, amount));
},
_ => panic!("siphon tick damage not dealt {:?}", siphon_damage),
}
return results;
}
fn shield(_cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let shield = CrypEffect { effect: Effect::Shield, duration: Effect::Shield.duration(), tick: None };
results.push(target.add_effect(Skill::Shield, shield));
return results;
}
fn silence(_cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let silence = CrypEffect { effect: Effect::Silence, duration: Effect::Silence.duration(), tick: None };
results.push(target.add_effect(Skill::Silence, silence));
return results;
}
fn purge(_cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
for (i, ce) in target.effects.clone().iter_mut().enumerate() {
if ce.effect.category() == Category::BlueBuff {
target.effects.remove(i);
results.push(ResolutionResult::Removal { effect: ce.effect });
}
}
return results;
}
fn purify(_cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
for (i, ce) in target.effects.clone().iter_mut().enumerate() {
if ce.effect.category() == Category::BlueDebuff {
target.effects.remove(i);
results.push(ResolutionResult::Removal { effect: ce.effect });
}
}
return results;
}
fn banish(_cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let banish = CrypEffect { effect: Effect::Banish, duration: Effect::Banish.duration(), tick: None };
results.push(target.add_effect(Skill::Banish, banish));
return results;
}
fn strike(cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let _amount = cryp.red_damage();
results.push(target.deal_red_damage(Skill::Attack, u64::max_value()));
return results;
}
#[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, vec![]);
}
#[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, vec![]);
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, vec![]);
assert!(y.effects.iter().any(|e| e.effect == Effect::Block));
let results = attack(&mut x, &mut y, vec![]);
match results[0] {
ResolutionResult::Damage { amount, mitigation: _, category: _ } => assert_eq!(amount, 50),
_ => panic!("not damage"),
};
}
#[test]
fn clutch_test() {
let mut x = Cryp::new()
.named(&"muji".to_string());
let mut y = Cryp::new()
.named(&"camel".to_string());
x.red_damage.force(u64::max_value());
clutch(&mut y.clone(), &mut y, vec![]);
assert!(y.is_clutch());
let results = attack(&mut x, &mut y, vec![]);
assert!(y.hp() == 1);
match results[0] {
ResolutionResult::Damage { amount, mitigation: _, category: _ } => assert_eq!(amount, 1023),
_ => 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, vec![]);
assert!(y.is_inverted());
// heal should deal green damage
heal(&mut x, &mut y, vec![]);
assert!(y.hp() == 768);
// attack should heal and recharge red shield
let results = attack(&mut x, &mut y, vec![]);
assert!(y.hp() == 1024);
match results[0] {
ResolutionResult::Inversion { damage: _, healing: _, recharge, category: _ } => assert_eq!(recharge, 64),
_ => panic!("not inversion"),
};
}
#[test]
fn reflect_test() {
let mut x = Cryp::new()
.named(&"muji".to_string());
let mut y = Cryp::new()
.named(&"camel".to_string());
reflect(&mut y.clone(), &mut y, vec![]);
assert!(y.is_reflecting());
let results = Skill::Attack.resolve(&mut x, &mut y);
assert!(x.hp() == 768);
match results[0] {
ResolutionResult::Damage { amount, mitigation: _, category: _ } => assert_eq!(amount, 256),
_ => panic!("not damage"),
};
}
#[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 results = triage(&mut x, &mut y, vec![]);
assert!(y.effects.iter().any(|e| e.effect == Effect::Triage));
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 results = recharge(&mut x, &mut y, vec![]);
match results[0] {
ResolutionResult::Recharge { red, blue } => {
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, vec![]);
assert!(x.effects.iter().any(|e| e.effect == Effect::Silence));
assert!(x.disabled(Skill::Silence).is_some());
}
#[test]
fn amplify_test() {
let mut x = Cryp::new()
.named(&"muji".to_string());
x.blue_damage.force(50);
amplify(&mut x.clone(), &mut x, vec![]);
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, vec![]);
assert!(x.effects.iter().any(|e| e.effect == Effect::Decay));
purify(&mut x.clone(), &mut x, vec![]);
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,
// }