Merge branch 'master' of ssh://cryps.gg:40022/~/cryps

This commit is contained in:
Mashy 2019-03-25 10:43:27 +10:00
commit 9a81a68a48
7 changed files with 421 additions and 502 deletions

View File

@ -46,7 +46,10 @@ function calculateTweenParams(sourceSpawn, targetSpawn, account) {
function animatePhase(scene, game, resolution, cb) { function animatePhase(scene, game, resolution, cb) {
// return early for disabled skills // return early for disabled skills
if (resolution.resolution.disable.disabled) return cb(); const { resolution: { results } } = resolution;
if (results.length === 0) return cb();
if (results[0][0] === 'Disable') return cb();
const group = scene.scene.get('CombatCryps').cryps; const group = scene.scene.get('CombatCryps').cryps;
const animations = new CombatAnimations(scene); const animations = new CombatAnimations(scene);
const account = scene.registry.get('account'); const account = scene.registry.get('account');
@ -65,10 +68,6 @@ function animatePhase(scene, game, resolution, cb) {
}; };
const castLocation = castParams(); const castLocation = castParams();
const { resolution: { results } } = resolution;
if (results.length === 0) return cb();
// Move cryps into position // Move cryps into position
if (moveSourceBattle) scene.tweens.add(moveSourceBattle); if (moveSourceBattle) scene.tweens.add(moveSourceBattle);
scene.tweens.add(moveTargetBattle); scene.tweens.add(moveTargetBattle);

View File

@ -16,21 +16,19 @@
# WORK WORK # WORK WORK
## NOW ## NOW
cryp vbox cryp vbox
ensure all skills impl ensure all skills impl
ez ez
Skill::Slay -> red attack with bonus somethingorother for blue / maim no healing Skill::Slay -> red attack with bonus somethingorother for blue / maim no healing
Hatred -> damage received converted into bonus dmg
aoe aoe
Skill::Ruin -> aoe stun Skill::Ruin -> aoe stun
on attack on attack
Skill::Reflect -> reflect incoming attacks back to opponent
Skill::Taunt -> redirect incomnig attacks to self Skill::Taunt -> redirect incomnig attacks to self
Skill::Toxic -> apply debuff to attackers Skill::Toxic -> apply debuff to attackers
colour speeds
update speed of rest of stack on cryp speed change
include target name in effect resolution include target name in effect resolution
eg strangle applies buff to self eg strangle applies buff to self
@ -55,7 +53,6 @@ change to ownership pattern
deal_damage(colour) deal_damage(colour)
resolutions.push(skill()).flatten() resolutions.push(skill()).flatten()
overkill in logs overkill in logs
immunity resolution type
* confirm cryp without skill ready * confirm cryp without skill ready
* iconography * iconography

View File

@ -258,9 +258,10 @@ impl Cryp {
self.hp.value == 0 self.hp.value == 0
} }
pub fn immune(&self, skill: Skill) -> Immunity { pub fn immune(&self, skill: Skill) -> Option<Immunity> {
// also checked in resolve stage so shouldn't happen really
if self.is_ko() { if self.is_ko() {
return Immunity { immune: true, effects: vec![Effect::Ko]}; return Some(vec![Effect::Ko]);
} }
let immunities = self.effects.iter() let immunities = self.effects.iter()
@ -269,15 +270,15 @@ impl Cryp {
.collect::<Vec<Effect>>(); .collect::<Vec<Effect>>();
if immunities.len() > 0 { if immunities.len() > 0 {
return Immunity { immune: true, effects: immunities}; return Some(immunities);
} }
return Immunity { immune: false, effects: vec![]}; None
} }
pub fn disabled(&self, skill: Skill) -> Disable { pub fn disabled(&self, skill: Skill) -> Option<Disable> {
if self.is_ko() && !skill.ko_castable() { if self.is_ko() && !skill.ko_castable() {
return Disable { disabled: true, effects: vec![Effect::Ko]}; return Some(vec![Effect::Ko]);
} }
let disables = self.effects.iter() let disables = self.effects.iter()
@ -286,10 +287,10 @@ impl Cryp {
.collect::<Vec<Effect>>(); .collect::<Vec<Effect>>();
if disables.len() > 0 { if disables.len() > 0 {
return Disable { disabled: true, effects: disables}; return Some(disables);
} }
return Disable { disabled: false, effects: vec![]}; None
} }
@ -312,7 +313,7 @@ impl Cryp {
pub fn available_skills(&self) -> Vec<&CrypSkill> { pub fn available_skills(&self) -> Vec<&CrypSkill> {
self.skills.iter() self.skills.iter()
.filter(|s| s.cd.is_none()) .filter(|s| s.cd.is_none())
.filter(|s| !self.disabled(s.skill).disabled) .filter(|s| self.disabled(s.skill).is_none())
.collect() .collect()
} }
@ -387,11 +388,6 @@ impl Cryp {
self self
} }
// pub fn rez(&mut self) -> &mut Cryp {
// self.hp.set(self.hp.base);
// self
// }
// Stats // Stats
pub fn red_damage(&self) -> u64 { pub fn red_damage(&self) -> u64 {
let red_damage_mods = self.effects.iter() let red_damage_mods = self.effects.iter()
@ -423,11 +419,15 @@ impl Cryp {
return modified_green_damage; return modified_green_damage;
} }
pub fn skill_speed(&self, s: Skill) -> u64 { pub fn skill_speed(&self, s: Skill) -> u64 {
self.speed().saturating_mul(s.speed() as u64) self.speed().saturating_mul(s.speed() as u64)
} }
// todo complete with specs
pub fn skill_is_aoe(&self, s: Skill) -> bool {
s.aoe()
}
pub fn speed(&self) -> u64 { pub fn speed(&self) -> u64 {
let speed_mods = self.effects.iter() let speed_mods = self.effects.iter()
.filter(|e| e.effect.modifications().contains(&Stat::Speed)) .filter(|e| e.effect.modifications().contains(&Stat::Speed))
@ -450,14 +450,9 @@ impl Cryp {
} }
pub fn recharge(&mut self) -> ResolutionResult { pub fn recharge(&mut self) -> ResolutionResult {
let immunity = self.immune(Skill::Recharge); if let Some(immunity) = self.immune(Skill::Recharge) {
let immune = immunity.immune; return ResolutionResult::Immunity {
immunity,
if immune {
ResolutionResult::Recharge {
red: 0,
blue: 0,
immunity: immunity.clone(),
}; };
} }
@ -467,18 +462,13 @@ impl Cryp {
let blue = self.blue_shield.max.saturating_sub(self.blue_shield.value); let blue = self.blue_shield.max.saturating_sub(self.blue_shield.value);
self.blue_shield.value = self.blue_shield.max; self.blue_shield.value = self.blue_shield.max;
ResolutionResult::Recharge { red, blue, immunity } ResolutionResult::Recharge { red, blue }
} }
pub fn deal_green_damage(&mut self, skill: Skill, amount: u64) -> ResolutionResult { pub fn deal_green_damage(&mut self, skill: Skill, amount: u64) -> ResolutionResult {
let immunity = self.immune(skill); if let Some(immunity) = self.immune(skill) {
let immune = immunity.immune; return ResolutionResult::Immunity {
immunity,
if immune {
return ResolutionResult::Healing {
amount: 0,
overhealing: 0,
immunity: immunity.clone(),
}; };
} }
@ -503,7 +493,6 @@ impl Cryp {
return ResolutionResult::Healing { return ResolutionResult::Healing {
amount: healing, amount: healing,
overhealing, overhealing,
immunity,
}; };
}, },
true => { true => {
@ -517,21 +506,14 @@ impl Cryp {
healing: 0, healing: 0,
recharge: 0, recharge: 0,
category: Category::GreenDamage, category: Category::GreenDamage,
immunity,
}; };
} }
} }
} }
pub fn deal_red_damage(&mut self, skill: Skill, amount: u64) -> ResolutionResult { pub fn deal_red_damage(&mut self, skill: Skill, amount: u64) -> ResolutionResult {
let immunity = self.immune(skill); if let Some(immunity) = self.immune(skill) {
let immune = immunity.immune; return ResolutionResult::Immunity {
if immune {
return ResolutionResult::Damage {
amount: 0,
mitigation: 0,
category: Category::RedDamage,
immunity, immunity,
}; };
} }
@ -566,7 +548,6 @@ impl Cryp {
amount: delta, amount: delta,
mitigation, mitigation,
category: Category::RedDamage, category: Category::RedDamage,
immunity,
}; };
}, },
true => { true => {
@ -584,7 +565,6 @@ impl Cryp {
damage: 0, damage: 0,
healing, healing,
recharge, recharge,
immunity,
category: Category::RedDamage, category: Category::RedDamage,
}; };
} }
@ -592,14 +572,8 @@ impl Cryp {
} }
pub fn deal_blue_damage(&mut self, skill: Skill, amount: u64) -> ResolutionResult { pub fn deal_blue_damage(&mut self, skill: Skill, amount: u64) -> ResolutionResult {
let immunity = self.immune(skill); if let Some(immunity) = self.immune(skill) {
let immune = immunity.immune; return ResolutionResult::Immunity {
if immune {
return ResolutionResult::Damage {
amount: 0,
mitigation: 0,
category: Category::BlueDamage,
immunity, immunity,
}; };
} }
@ -627,7 +601,6 @@ impl Cryp {
amount: delta, amount: delta,
mitigation, mitigation,
category: Category::BlueDamage, category: Category::BlueDamage,
immunity,
}; };
}, },
true => { true => {
@ -645,7 +618,6 @@ impl Cryp {
damage: 0, damage: 0,
healing, healing,
recharge, recharge,
immunity,
category: Category::BlueDamage, category: Category::BlueDamage,
}; };
} }
@ -653,20 +625,20 @@ impl Cryp {
} }
pub fn add_effect(&mut self, skill: Skill, effect: CrypEffect) -> ResolutionResult { pub fn add_effect(&mut self, skill: Skill, effect: CrypEffect) -> ResolutionResult {
let immunity = self.immune(skill); if let Some(immunity) = self.immune(skill) {
let immune = immunity.immune; return ResolutionResult::Immunity {
immunity,
};
}
// todo modified durations cause of buffs // todo modified durations cause of buffs
let result = ResolutionResult::Effect { let result = ResolutionResult::Effect {
effect: effect.effect, effect: effect.effect,
duration: effect.effect.duration(), duration: effect.effect.duration(),
immunity,
}; };
if !immune {
// println!("{:?} {:?} adding effect", self.name, effect.effect); // println!("{:?} {:?} adding effect", self.name, effect.effect);
self.effects.push(effect); self.effects.push(effect);
}
return result; return result;
} }

View File

@ -21,7 +21,7 @@ pub struct Team {
pub id: Uuid, pub id: Uuid,
pub player: Option<Uuid>, pub player: Option<Uuid>,
pub bot: bool, pub bot: bool,
cryps: Vec<Cryp>, pub cryps: Vec<Cryp>,
} }
impl Team { impl Team {
@ -154,14 +154,14 @@ impl Game {
} }
// handle missing team properly // handle missing team properly
fn team_by_id(&mut self, id: Uuid) -> &mut Team { pub fn team_by_id(&mut self, id: Uuid) -> &mut Team {
match self.teams.iter_mut().find(|t| t.id == id) { match self.teams.iter_mut().find(|t| t.id == id) {
Some(t) => t, Some(t) => t,
None => panic!("id not in game {:}", id), None => panic!("id not in game {:}", id),
} }
} }
fn cryp_by_id(&mut self, id: Uuid) -> Option<&mut Cryp> { pub fn cryp_by_id(&mut self, id: Uuid) -> Option<&mut Cryp> {
match self.teams.iter_mut().find(|t| t.cryps.iter().any(|c| c.id == id)) { match self.teams.iter_mut().find(|t| t.cryps.iter().any(|c| c.id == id)) {
Some(team) => team.cryps.iter_mut().find(|c| c.id == id), Some(team) => team.cryps.iter_mut().find(|c| c.id == id),
None => None, None => None,
@ -177,6 +177,16 @@ impl Game {
.collect::<Vec<Cryp>>() .collect::<Vec<Cryp>>()
} }
pub fn cryp_aoe_targets(&self, cryp_id: Uuid) -> Vec<Uuid> {
self.teams.iter()
.find(|t| t.cryps.iter().any(|c| c.id == cryp_id))
.unwrap()
.cryps
.iter()
.map(|c| c.id)
.collect()
}
fn update_cryp(&mut self, cryp: &mut Cryp) -> &mut Game { fn update_cryp(&mut self, cryp: &mut Cryp) -> &mut Game {
match self.teams.iter_mut().find(|t| t.cryps.iter().any(|c| c.id == cryp.id)) { match self.teams.iter_mut().find(|t| t.cryps.iter().any(|c| c.id == cryp.id)) {
Some(team) => { Some(team) => {
@ -196,14 +206,12 @@ impl Game {
&& self.teams.iter().all(|t| t.cryps.len() == self.team_size) && self.teams.iter().all(|t| t.cryps.len() == self.team_size)
} }
pub fn start(&mut self) -> &mut Game { pub fn start(mut self) -> Game {
self.log.push("Game starting...".to_string()); self.log.push("Game starting...".to_string());
self.skill_phase_start()
self.skill_phase_start();
self
} }
fn skill_phase_start(&mut self) -> &mut Game { fn skill_phase_start(mut self) -> Game {
self.log.push("<Skill Phase>".to_string()); self.log.push("<Skill Phase>".to_string());
if ![Phase::Start, Phase::Resolve].contains(&self.phase) { if ![Phase::Start, Phase::Resolve].contains(&self.phase) {
@ -212,23 +220,21 @@ impl Game {
self.phase = Phase::Skill; self.phase = Phase::Skill;
self.stack.clear();
self.pve_add_skills(); self.pve_add_skills();
if self.skill_phase_finished() { if self.skill_phase_finished() {
self.resolve_phase_start(); return self.resolve_phase_start()
} }
self self
} }
fn pve_add_skills(&mut self) -> &mut Game { fn pve_add_skills(&mut self) -> &mut Game {
for mobs in self.teams let mut pve_skills = vec![];
.clone()
.into_iter()
.filter(|t| t.bot) {
let player_team = self.teams.iter().find(|t| t.id != mobs.id).unwrap().clone(); for mobs in self.teams
.iter()
.filter(|t| t.bot) {
let player_team = self.teams.iter().find(|t| t.id != mobs.id).unwrap();
for mob in mobs.cryps.iter() { for mob in mobs.cryps.iter() {
let skill = mob.mob_select_skill(); let skill = mob.mob_select_skill();
@ -252,16 +258,17 @@ impl Game {
target = find_target(); target = find_target();
} }
match self.add_skill(mobs.id, mob.id, Some(target.id), s) { pve_skills.push((mobs.id, mob.id, Some(target.id), s));
Ok(_) => (),
Err(e) => println!("{:?} could not add pve skill", e),
}
// println!("{:?}", cast);
}, },
None => continue, None => continue,
}; };
} }
} }
for (team_id, mob_id, target_id, s) in pve_skills {
self.add_skill(team_id, mob_id, target_id, s).expect("unable to add pve mob skill");
}
self self
} }
@ -312,9 +319,8 @@ impl Game {
} }
// check here as well so uncastable spells don't go on the stack // check here as well so uncastable spells don't go on the stack
let check = cryp.disabled(skill); if let Some(disable) = cryp.disabled(skill) {
if check.disabled { return Err(format_err!("skill disabled {:?}", disable));
return Err(format_err!("skill disabled {:?}", check.effects));
} }
} }
@ -343,7 +349,7 @@ impl Game {
// requires no input // requires no input
// just do it // just do it
fn resolve_phase_start(&mut self) -> &mut Game { fn resolve_phase_start(mut self) -> Game {
if self.phase != Phase::Skill { if self.phase != Phase::Skill {
panic!("game not in skill phase"); panic!("game not in skill phase");
} }
@ -355,73 +361,56 @@ impl Game {
self.resolve_skills() self.resolve_skills()
} }
fn log_resolution(&mut self, source: &mut Cryp, target: &mut Cryp, cast: &Cast) -> &mut Game { fn log_resolution(&mut self, cast: &Cast) -> &mut Game {
match cast.resolution.disable.disabled { let source = self.cryp_by_id(cast.source_cryp_id).unwrap().clone();
true => { let target = self.cryp_by_id(cast.target_cryp_id).unwrap().clone();
self.log.push(format!("{:} {:?} {:} disabled {:?}", source.name, cast.skill, target.name, cast.resolution.disable.effects));
return self;
},
false => (),
};
for result in cast.resolution.results.iter() { for result in cast.resolution.results.iter() {
match result { match result {
ResolutionResult::Damage { amount, mitigation, category: _, immunity } => { ResolutionResult::Disable { disable } =>
match immunity.immune { self.log.push(format!("{:} {:?} {:} disabled {:?}",
true => self.log.push(format!("[{:}] {:} {:?} {:} immune {:?}", source.name, cast.skill, target.name, disable)),
cast.resolution.speed, source.name, cast.skill, target.name, immunity.effects)),
false => self.log.push(format!("[{:}] {:} {:?} {:} {:} ({:} mitigated)", ResolutionResult::Immunity { immunity } =>
self.log.push(format!("[{:}] {:} {:?} {:} immune {:?}",
cast.resolution.speed, source.name, cast.skill, target.name, immunity)),
ResolutionResult::TargetKo =>
self.log.push(format!("[{:}] {:} {:?} {:} - KO",
cast.resolution.speed, source.name, cast.skill, target.name)),
ResolutionResult::Damage { amount, mitigation, category: _ } =>
self.log.push(format!("[{:}] {:} {:?} {:} {:} ({:} mitigated)",
cast.resolution.speed, source.name, cast.skill, target.name, amount, mitigation)), cast.resolution.speed, source.name, cast.skill, target.name, amount, mitigation)),
}
}, ResolutionResult::Healing { amount, overhealing } =>
ResolutionResult::Healing { amount, overhealing, immunity } => { self.log.push(format!("[{:}] {:} {:?} {:} {:} ({:}OH)",
match immunity.immune {
true => self.log.push(format!("[{:}] {:} {:?} {:} immune {:?}",
cast.resolution.speed, source.name, cast.skill, target.name, immunity.effects)),
false => self.log.push(format!("[{:}] {:} {:?} {:} {:} ({:}OH)",
cast.resolution.speed, source.name, cast.skill, target.name, amount, overhealing)), cast.resolution.speed, source.name, cast.skill, target.name, amount, overhealing)),
}
}, ResolutionResult::Inversion { healing, damage, recharge, category: _ } => {
ResolutionResult::Inversion { healing, damage, recharge, category: _, immunity } => { match *healing > 0 {
match immunity.immune {
true => self.log.push(format!("[{:}] {:} {:?} {:} immune {:?}",
cast.resolution.speed, source.name, cast.skill, target.name, immunity.effects)),
false => match *healing > 0 {
true => self.log.push(format!("[{:}] {:} {:?} {:} INVERTED {:} ({:} recharge)", true => self.log.push(format!("[{:}] {:} {:?} {:} INVERTED {:} ({:} recharge)",
cast.resolution.speed, source.name, cast.skill, target.name, healing, recharge)), cast.resolution.speed, source.name, cast.skill, target.name, healing, recharge)),
false => self.log.push(format!("[{:}] {:} {:?} {:} INVERTED {:}", false => self.log.push(format!("[{:}] {:} {:?} {:} INVERTED {:}",
cast.resolution.speed, source.name, cast.skill, target.name, damage)), cast.resolution.speed, source.name, cast.skill, target.name, damage)),
} }
}
}, },
ResolutionResult::Effect { effect, duration, immunity } => {
match immunity.immune { ResolutionResult::Effect { effect, duration } =>
true => self.log.push(format!("[{:}] {:} {:?} {:} immune {:?}", self.log.push(format!("[{:}] {:} {:?} {:} {:?} {:}T",
cast.resolution.speed, source.name, cast.skill, target.name, immunity.effects)),
false => self.log.push(format!("[{:}] {:} {:?} {:} {:?} {:}T",
cast.resolution.speed, source.name, cast.skill, target.name, effect, duration)), cast.resolution.speed, source.name, cast.skill, target.name, effect, duration)),
}
}, ResolutionResult::Removal { effect } =>
ResolutionResult::Removal { effect, immunity } => { self.log.push(format!("[{:}] {:?} removed {:} {:?}",
match immunity.immune {
true => self.log.push(format!("[{:}] {:} {:?} {:} immune {:?}",
cast.resolution.speed, source.name, cast.skill, target.name, immunity.effects)),
false => self.log.push(format!("[{:}] {:?} removed {:} {:?}",
cast.resolution.speed, source.name, target.name, effect)), cast.resolution.speed, source.name, target.name, effect)),
}
}, ResolutionResult::Recharge { red, blue } =>
ResolutionResult::Recharge { red, blue, immunity } => { self.log.push(format!("[{:}] {:} {:?} {:} {:}R {:}B",
match immunity.immune {
true => self.log.push(format!("[{:}] {:} {:?} {:} immune {:?}",
cast.resolution.speed, source.name, cast.skill, target.name, immunity.effects)),
false => self.log.push(format!("[{:}] {:} {:?} {:} {:}R {:}B",
cast.resolution.speed, source.name, cast.skill, target.name, red, blue)), cast.resolution.speed, source.name, cast.skill, target.name, red, blue)),
}
}, ResolutionResult::Evasion { skill: _, evasion_rating } =>
ResolutionResult::Evasion { skill: _, evasion_rating } => {
self.log.push(format!("[{:}] {:} {:?} {:} evaded ({:}%)", self.log.push(format!("[{:}] {:} {:?} {:} evaded ({:}%)",
cast.resolution.speed, source.name, cast.skill, target.name, evasion_rating)); cast.resolution.speed, source.name, cast.skill, target.name, evasion_rating)),
},
} }
} }
@ -436,12 +425,11 @@ impl Game {
}); });
self.stack = sorted; self.stack = sorted;
self.stack.reverse();
self self
} }
fn resolve_skills(&mut self) -> &mut Game { fn resolve_skills(mut self) -> Game {
if self.phase != Phase::Resolve { if self.phase != Phase::Resolve {
panic!("game not in Resolve phase"); panic!("game not in Resolve phase");
} }
@ -461,44 +449,43 @@ impl Game {
self.stack_sort_speed(); self.stack_sort_speed();
// update the stack with the resolved skills // temp vec of this round's resolving skills
for mut skill in self.stack.clone() { // because need to check cooldown use before pushing them into the complete list
// println!("{:} resolving ", skill); let mut resolving = vec![];
let mut source = self.cryp_by_id(skill.source_cryp_id).unwrap().clone();
let mut target = self.cryp_by_id(skill.target_cryp_id).unwrap().clone();
skill.set_resolution(&mut source, &mut target); while let Some(mut cast) = self.stack.pop() {
// println!("{:} resolving ", cast);
self.log_resolution(&mut source, &mut target, &skill); cast.finalise(&mut self);
self.resolved.push(skill); self.log_resolution(&cast);
if target.is_ko() && !target.ko_logged { resolving.push(cast);
self.log.push(format!("{:} KO", target.name));
target.effects.clear();
target.ko_logged = true;
}
if source.is_ko() && !source.ko_logged { // if target.is_ko() && !target.ko_logged {
self.log.push(format!("{:} KO", source.name)); // self.log.push(format!("{:} KO", target.name));
source.effects.clear(); // target.effects.clear();
source.ko_logged = true; // target.ko_logged = true;
} // }
self.update_cryp(&mut source); // if source.is_ko() && !source.ko_logged {
self.update_cryp(&mut target); // self.log.push(format!("{:} KO", source.name));
// source.effects.clear();
// source.ko_logged = true;
// }
// find a way to // self.update_cryp(&mut source);
// resort the stack after each cast because // self.update_cryp(&mut target);
// the cryp speed may have changed
// self.stack_sort_speed(); self.stack_sort_speed();
}; };
// println!("{:#?}", self.resolved); // println!("{:#?}", self.resolving);
// now Resolve has all been assigned // now Resolve has all been assigned
// handle cooldowns and statuses // handle cooldowns and statuses
self.progress_durations(); self.progress_durations(&resolving);
self.resolved.append(&mut resolving);
if self.finished() { if self.finished() {
return self.finish() return self.finish()
@ -507,7 +494,7 @@ impl Game {
self.skill_phase_start() self.skill_phase_start()
} }
fn progress_durations(&mut self) -> &mut Game { fn progress_durations(&mut self, resolved: &Vec<Cast>) -> &mut Game {
for mut cryp in self.all_cryps() { for mut cryp in self.all_cryps() {
// println!("progressing durations for {:}", cryp.name); // println!("progressing durations for {:}", cryp.name);
@ -518,7 +505,7 @@ impl Game {
// only reduce cooldowns if no cd was used // only reduce cooldowns if no cd was used
// have to borrow self for the skill check // have to borrow self for the skill check
{ {
if let Some(skill) = self.stack.iter_mut().find(|s| s.source_cryp_id == cryp.id) { if let Some(skill) = resolved.iter().find(|s| s.source_cryp_id == cryp.id) {
if skill.used_cooldown() { if skill.used_cooldown() {
cryp.skill_set_cd(skill.skill); cryp.skill_set_cd(skill.skill);
} else { } else {
@ -545,10 +532,9 @@ impl Game {
self.teams.iter().find(|t| t.cryps.iter().any(|c| !c.is_ko())) self.teams.iter().find(|t| t.cryps.iter().any(|c| !c.is_ko()))
} }
fn finish(&mut self) -> &mut Game { fn finish(mut self) -> Game {
self.phase = Phase::Finish; self.phase = Phase::Finish;
self.log.push(format!("Game finished.")); self.log.push(format!("Game finished."));
self.stack.clear();
{ {
let winner = self.teams.iter().find(|t| t.cryps.iter().any(|c| !c.is_ko())); let winner = self.teams.iter().find(|t| t.cryps.iter().any(|c| !c.is_ko()));
@ -588,7 +574,7 @@ pub fn game_skill(params: GameSkillParams, tx: &mut Transaction, account: &Accou
game.add_skill(account.id, params.cryp_id, params.target_cryp_id, params.skill)?; game.add_skill(account.id, params.cryp_id, params.target_cryp_id, params.skill)?;
if game.skill_phase_finished() { if game.skill_phase_finished() {
game.resolve_phase_start(); game = game.resolve_phase_start();
} }
game_update(&game, tx)?; game_update(&game, tx)?;
@ -864,7 +850,7 @@ pub fn game_instance_join(tx: &mut Transaction, player: Player, game_id: Uuid) -
game.team_add(team)?; game.team_add(team)?;
if game.can_start() { if game.can_start() {
game.start(); game = game.start();
} }
println!("{:?} game joined", game.id); println!("{:?} game joined", game.id);
@ -926,9 +912,7 @@ mod tests {
assert!(game.can_start()); assert!(game.can_start());
game.start(); return game.start();
return game;
} }
fn create_2v2_test_game() -> Game { fn create_2v2_test_game() -> Game {
@ -974,9 +958,7 @@ mod tests {
assert!(game.can_start()); assert!(game.can_start());
game.start(); return game.start();
return game;
} }
#[test] #[test]
@ -994,7 +976,7 @@ mod tests {
assert!(game.skill_phase_finished()); assert!(game.skill_phase_finished());
game.resolve_phase_start(); game = game.resolve_phase_start();
assert!([Phase::Skill, Phase::Finish].contains(&game.phase)); assert!([Phase::Skill, Phase::Finish].contains(&game.phase));
@ -1015,7 +997,7 @@ mod tests {
game.add_skill(y_team.id, y_cryp.id, Some(x_cryp.id), Skill::TestTouch).unwrap(); game.add_skill(y_team.id, y_cryp.id, Some(x_cryp.id), Skill::TestTouch).unwrap();
assert!(game.skill_phase_finished()); assert!(game.skill_phase_finished());
game.resolve_phase_start(); game = game.resolve_phase_start();
// should auto progress back to skill phase // should auto progress back to skill phase
assert!(game.phase == Phase::Skill); assert!(game.phase == Phase::Skill);
@ -1045,7 +1027,7 @@ mod tests {
game.add_skill(y_team.id, y_cryp.id, Some(x_cryp.id), Skill::Attack).unwrap(); game.add_skill(y_team.id, y_cryp.id, Some(x_cryp.id), Skill::Attack).unwrap();
assert!(game.skill_phase_finished()); assert!(game.skill_phase_finished());
game.resolve_phase_start(); game = game.resolve_phase_start();
assert!(!game.team_by_id(y_team.id).cryps[0].is_stunned()); assert!(!game.team_by_id(y_team.id).cryps[0].is_stunned());
assert!(game.phase == Phase::Finish); assert!(game.phase == Phase::Finish);
@ -1071,7 +1053,7 @@ mod tests {
let _x_stun_id = game.add_skill(x_team.id, x_cryp.id, Some(y_cryp.id), Skill::TestTouch).unwrap(); let _x_stun_id = game.add_skill(x_team.id, x_cryp.id, Some(y_cryp.id), Skill::TestTouch).unwrap();
game.add_skill(y_team.id, y_cryp.id, Some(x_cryp.id), Skill::TestTouch).unwrap(); game.add_skill(y_team.id, y_cryp.id, Some(x_cryp.id), Skill::TestTouch).unwrap();
game.resolve_phase_start(); game = game.resolve_phase_start();
// should auto progress back to skill phase // should auto progress back to skill phase
assert!(game.phase == Phase::Skill); assert!(game.phase == Phase::Skill);
@ -1082,7 +1064,7 @@ mod tests {
let _x_block_id = game.add_skill(x_team.id, x_cryp.id, Some(y_cryp.id), Skill::Stun).unwrap(); let _x_block_id = game.add_skill(x_team.id, x_cryp.id, Some(y_cryp.id), Skill::Stun).unwrap();
let _y_touch_id = game.add_skill(y_team.id, y_cryp.id, Some(x_cryp.id), Skill::TestTouch).unwrap(); let _y_touch_id = game.add_skill(y_team.id, y_cryp.id, Some(x_cryp.id), Skill::TestTouch).unwrap();
game.resolve_phase_start(); game = game.resolve_phase_start();
assert!(game.team_by_id(x_team.id).cryps[0].skill_on_cd(Skill::Stun).is_some()); assert!(game.team_by_id(x_team.id).cryps[0].skill_on_cd(Skill::Stun).is_some());
assert!(game.team_by_id(y_team.id).cryps[0].skill_on_cd(Skill::Block).is_none()); assert!(game.team_by_id(y_team.id).cryps[0].skill_on_cd(Skill::Block).is_none());
@ -1101,7 +1083,7 @@ mod tests {
let _x_block_id = game.add_skill(x_team.id, x_cryp.id, None, Skill::TestParry).unwrap(); let _x_block_id = game.add_skill(x_team.id, x_cryp.id, None, Skill::TestParry).unwrap();
game.add_skill(y_team.id, y_cryp.id, Some(x_cryp.id), Skill::TestStun).unwrap(); game.add_skill(y_team.id, y_cryp.id, Some(x_cryp.id), Skill::TestStun).unwrap();
game.resolve_phase_start(); game = game.resolve_phase_start();
// should not be stunned because of parry // should not be stunned because of parry
assert!(game.team_by_id(x_team.id).cryps[0].is_stunned() == false); assert!(game.team_by_id(x_team.id).cryps[0].is_stunned() == false);
@ -1120,12 +1102,12 @@ mod tests {
let _x_siphon_id = game.add_skill(x_team.id, x_cryp.id, Some(y_cryp.id), Skill::TestSiphon).unwrap(); let _x_siphon_id = game.add_skill(x_team.id, x_cryp.id, Some(y_cryp.id), Skill::TestSiphon).unwrap();
let _y_touch_id = game.add_skill(y_team.id, y_cryp.id, Some(x_cryp.id), Skill::TestTouch).unwrap(); let _y_touch_id = game.add_skill(y_team.id, y_cryp.id, Some(x_cryp.id), Skill::TestTouch).unwrap();
game.resolve_phase_start(); game = game.resolve_phase_start();
game.add_skill(x_team.id, x_cryp.id, None, Skill::TestBlock).unwrap(); game.add_skill(x_team.id, x_cryp.id, None, Skill::TestBlock).unwrap();
game.add_skill(y_team.id, y_cryp.id, None, Skill::TestBlock).unwrap(); game.add_skill(y_team.id, y_cryp.id, None, Skill::TestBlock).unwrap();
game.resolve_phase_start(); game = game.resolve_phase_start();
assert!(game.resolved.iter().any(|r| r.skill == Skill::SiphonTick)); assert!(game.resolved.iter().any(|r| r.skill == Skill::SiphonTick));
} }
@ -1148,7 +1130,7 @@ mod tests {
game.add_skill(x_team.id, y_cryp.id, Some(i_cryp.id), Skill::TestTouch).unwrap(); game.add_skill(x_team.id, y_cryp.id, Some(i_cryp.id), Skill::TestTouch).unwrap();
assert!(game.skill_phase_finished()); assert!(game.skill_phase_finished());
game.resolve_phase_start(); game = game.resolve_phase_start();
assert!([Phase::Skill, Phase::Finish].contains(&game.phase)); assert!([Phase::Skill, Phase::Finish].contains(&game.phase));
@ -1165,7 +1147,7 @@ mod tests {
assert!(game.add_skill(x_team.id, x_cryp.id, Some(i_cryp.id), Skill::TestTouch).is_err()); assert!(game.add_skill(x_team.id, x_cryp.id, Some(i_cryp.id), Skill::TestTouch).is_err());
assert!(game.skill_phase_finished()); assert!(game.skill_phase_finished());
game.resolve_phase_start(); game = game.resolve_phase_start();
assert!(game.team_by_id(i_team.id).skills_required() == 1); assert!(game.team_by_id(i_team.id).skills_required() == 1);
assert!(game.team_by_id(x_team.id).skills_required() == 2); assert!(game.team_by_id(x_team.id).skills_required() == 2);

View File

@ -133,7 +133,7 @@ impl Instance {
.team_add(plr_team)? .team_add(plr_team)?
.team_add(bot_team)?; .team_add(bot_team)?;
game.start(); game = game.start();
Ok(game) Ok(game)
} }
@ -253,7 +253,7 @@ impl Instance {
.team_add(a_team).unwrap() .team_add(a_team).unwrap()
.team_add(b_team).unwrap(); .team_add(b_team).unwrap();
game.start(); game = game.start();
assert!(game.finished()); assert!(game.finished());
let winner = game.winner().unwrap(); let winner = game.winner().unwrap();

View File

@ -2,6 +2,8 @@ use rand::{thread_rng, Rng};
use uuid::Uuid; use uuid::Uuid;
use cryp::{Cryp, CrypEffect, Stat}; use cryp::{Cryp, CrypEffect, Stat};
use vbox::{Var};
use game::{Game};
#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] #[derive(Debug,Clone,PartialEq,Serialize,Deserialize)]
pub struct Cast { pub struct Cast {
@ -29,8 +31,23 @@ impl Cast {
Cast::new(source.id, source.account, target.id, skill) Cast::new(source.id, source.account, target.id, skill)
} }
pub fn set_resolution(&mut self, cryp: &mut Cryp, target: &mut Cryp) -> &mut Cast { pub fn finalise(&mut self, game: &mut Game) -> &mut Cast {
self.resolution = self.skill.resolve(cryp, target); 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 self
} }
@ -39,47 +56,33 @@ impl Cast {
} }
} }
pub type Disable = Vec<Effect>;
#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] pub type Immunity = Vec<Effect>;
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)] #[derive(Debug,Clone,PartialEq,Serialize,Deserialize)]
pub enum ResolutionResult { pub enum ResolutionResult {
Damage { amount: u64, mitigation: u64, category: Category , immunity: Immunity }, TargetKo,
Healing { amount: u64, overhealing: u64, immunity: Immunity }, Disable { disable: Disable },
Recharge { red: u64, blue: u64, immunity: Immunity }, Immunity { immunity: Immunity },
Inversion { healing: u64, damage: u64, recharge: u64, category: Category, immunity: Immunity }, Damage { amount: u64, mitigation: u64, category: Category },
Effect { effect: Effect, duration: u8, immunity: Immunity }, Healing { amount: u64, overhealing: u64 },
Removal { effect: Effect, immunity: Immunity }, 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 }, Evasion { skill: Skill, evasion_rating: u64 },
} }
#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] #[derive(Debug,Clone,PartialEq,Serialize,Deserialize)]
pub struct Resolution { pub struct Resolution {
pub skill: Skill, pub skill: Skill,
pub disable: Disable,
pub speed: u64, pub speed: u64,
pub results: Vec<ResolutionResult>, pub results: Vec<ResolutionResult>,
} }
impl Resolution { impl Resolution {
fn new(skill: Skill) -> Resolution { fn new(skill: Skill) -> Resolution {
Resolution { skill, results: vec![], disable: Disable::new(), speed: 0 } Resolution { skill, results: vec![], speed: 0 }
} }
} }
@ -549,57 +552,6 @@ impl Skill {
pub fn speed(&self) -> u8 { pub fn speed(&self) -> u8 {
match self { 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 => 5,
Skill::Ruin => 1,
Skill::Slay => 1,
Skill::Strangle => 5,
Skill::StrangleTick => 5,
Skill::Clutch => 1,
Skill::Taunt => 1,
Skill::Toxic => 1,
// unimplemented
// Skill::Lag => 2, //
// ----------------- // -----------------
// Test // Test
// ----------------- // -----------------
@ -608,78 +560,93 @@ impl Skill {
Skill::TestBlock => 10, Skill::TestBlock => 10,
Skill::TestParry => 10, Skill::TestParry => 10,
Skill::TestSiphon => 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 resolve(&self, source: &mut Cryp, target: &mut Cryp) -> Resolution { 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 mut rng = thread_rng();
let _base: u64 = rng.gen(); let _base: u64 = rng.gen();
let speed = source.skill_speed(*self); let mut results = vec![];
let mut resolution = Resolution { skill: *self, results: vec![], disable: source.disabled(*self), speed }; 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() { if target.is_ko() {
return resolution; results.push(ResolutionResult::TargetKo);
} return results;
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 => (),
} }
if target.is_reflecting() { if target.is_reflecting() {
// guard against overflow // guard against overflow
if source.is_reflecting() { if source.is_reflecting() {
return resolution; return results;
} }
let mut copy = source.clone(); return self.resolve(target, source);
return self.resolve(&mut copy, source);
} }
match self { match self {
Skill::Amplify => amplify(source, target, resolution), // increase magic damage Skill::Amplify => amplify(source, target, results), // increase magic damage
Skill::Attack => attack(source, target, resolution), Skill::Attack => attack(source, target, results),
Skill::Banish => banish(source, target, resolution), // TODO prevent all actions Skill::Banish => banish(source, target, results), // TODO prevent all actions
Skill::Blast => blast(source, target, resolution), Skill::Blast => blast(source, target, results),
Skill::Block => block(source, target, resolution), Skill::Block => block(source, target, results),
Skill::Curse => curse(source, target, resolution), Skill::Curse => curse(source, target, results),
Skill::Decay => decay(source, target, resolution), // dot Skill::Decay => decay(source, target, results), // dot
Skill::DecayTick => decay_tick(source, target, resolution), // dot Skill::DecayTick => decay_tick(source, target, results), // dot
Skill::Empower => empower(source, target, resolution), // increased phys damage Skill::Empower => empower(source, target, results), // increased phys damage
Skill::Haste => haste(source, target, resolution), // speed slow Skill::Haste => haste(source, target, results), // speed slow
Skill::Heal => heal(source, target, resolution), Skill::Heal => heal(source, target, results),
Skill::Hex => hex(source, target, resolution), // todo prevent casting Skill::Hex => hex(source, target, results), // todo prevent casting
Skill::Invert => invert(source, target, resolution), // todo prevent casting Skill::Invert => invert(source, target, results), // todo prevent casting
Skill::Parry => parry(source, target, resolution), Skill::Parry => parry(source, target, results),
Skill::Purge => purge(source, target, resolution), // dispel all buffs Skill::Purge => purge(source, target, results), // dispel all buffs
Skill::Purify => purify(source, target, resolution), // dispel all debuffs Skill::Purify => purify(source, target, results), // dispel all debuffs
Skill::Recharge => recharge(source, target, resolution), // target is immune to magic damage and fx Skill::Recharge => recharge(source, target, results), // target is immune to magic damage and fx
Skill::Shield => shield(source, target, resolution), // 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, resolution), // target cannot cast spells Skill::Silence => silence(source, target, results), // target cannot cast spells
Skill::Siphon => siphon(source, target, resolution), Skill::Siphon => siphon(source, target, results),
Skill::SiphonTick => siphon_tick(source, target, resolution), // hot Skill::SiphonTick => siphon_tick(source, target, results), // hot
Skill::Slow => slow(source, target, resolution), // speed slow Skill::Slow => slow(source, target, results), // speed slow
Skill::Snare => snare(source, target, resolution), // TODO prevent physical moves Skill::Snare => snare(source, target, results), // TODO prevent physical moves
Skill::Strike => strike(source, target, resolution), Skill::Strike => strike(source, target, results),
Skill::Stun => stun(source, target, resolution), Skill::Stun => stun(source, target, results),
Skill::Throw => throw(source, target, resolution), // no damage stun, adds vulnerable Skill::Throw => throw(source, target, results), // no damage stun, adds vulnerable
Skill::Triage => triage(source, target, resolution), // hot Skill::Triage => triage(source, target, results), // hot
Skill::TriageTick => triage_tick(source, target, resolution), // hot Skill::TriageTick => triage_tick(source, target, results), // hot
Skill::Clutch => clutch(source, target, resolution), Skill::Clutch => clutch(source, target, results),
Skill::Strangle => strangle(source, target, resolution), Skill::Strangle => strangle(source, target, results),
Skill::StrangleTick => strangle_tick(source, target, resolution), Skill::StrangleTick => strangle_tick(source, target, results),
Skill::Reflect => reflect(source, target, resolution), Skill::Reflect => reflect(source, target, results),
Skill::Ruin => unimplemented!(), Skill::Ruin => unimplemented!(),
Skill::Slay => unimplemented!(), Skill::Slay => unimplemented!(),
Skill::Taunt => unimplemented!(), Skill::Taunt => unimplemented!(),
@ -688,11 +655,11 @@ impl Skill {
// ----------------- // -----------------
// Test // Test
// ----------------- // -----------------
Skill::TestTouch => Resolution { skill: Skill::TestTouch, results: vec![], disable: Disable::new(), speed: 0 }, Skill::TestTouch => results,
Skill::TestStun => stun(source, target, resolution), Skill::TestStun => stun(source, target, results),
Skill::TestBlock => block(source, target, resolution), Skill::TestBlock => block(source, target, results),
Skill::TestParry => parry(source, target, resolution), Skill::TestParry => parry(source, target, results),
Skill::TestSiphon => siphon(source, target, resolution), Skill::TestSiphon => siphon(source, target, results),
} }
} }
@ -720,35 +687,35 @@ impl Skill {
} }
} }
fn attack(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { fn attack(cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let amount = cryp.red_damage(); let amount = cryp.red_damage();
resolution.results.push(target.deal_red_damage(Skill::Attack, amount)); results.push(target.deal_red_damage(Skill::Attack, amount));
return resolution; return results;
} }
fn stun(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { 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 }; let effect = CrypEffect { effect: Effect::Stun, duration: Effect::Stun.duration(), tick: None };
resolution.results.push(target.add_effect(Skill::Stun, effect)); results.push(target.add_effect(Skill::Stun, effect));
return resolution; return results;
} }
fn clutch(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { 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 }; let effect = CrypEffect { effect: Effect::Clutch, duration: Effect::Clutch.duration(), tick: None };
resolution.results.push(target.add_effect(Skill::Clutch, effect)); results.push(target.add_effect(Skill::Clutch, effect));
return resolution; return results;
} }
fn throw(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { 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 stun = CrypEffect { effect: Effect::Stun, duration: Effect::Stun.duration(), tick: None };
let vulnerable = CrypEffect { effect: Effect::Vulnerable, duration: Effect::Vulnerable.duration(), tick: None }; let vulnerable = CrypEffect { effect: Effect::Vulnerable, duration: Effect::Vulnerable.duration(), tick: None };
resolution.results.push(target.add_effect(Skill::Throw, stun)); results.push(target.add_effect(Skill::Throw, stun));
resolution.results.push(target.add_effect(Skill::Throw, vulnerable)); results.push(target.add_effect(Skill::Throw, vulnerable));
return resolution; return results;
} }
fn strangle(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { fn strangle(cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let target_stun = CrypEffect { let target_stun = CrypEffect {
effect: Effect::Strangle, effect: Effect::Strangle,
duration: Effect::Strangle.duration(), duration: Effect::Strangle.duration(),
@ -756,14 +723,14 @@ fn strangle(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> R
}; };
let attacker_immunity = CrypEffect { effect: Effect::Strangling, duration: Effect::Strangling.duration(), tick: None }; let attacker_immunity = CrypEffect { effect: Effect::Strangling, duration: Effect::Strangling.duration(), tick: None };
resolution.results.push(target.add_effect(Skill::Strangle, target_stun)); results.push(target.add_effect(Skill::Strangle, target_stun));
resolution.results.push(cryp.add_effect(Skill::Strangle, attacker_immunity)); results.push(cryp.add_effect(Skill::Strangle, attacker_immunity));
return strangle_tick(cryp, target, resolution); return strangle_tick(cryp, target, results);
} }
fn strangle_tick(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { fn strangle_tick(cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let amount = cryp.red_damage(); let amount = cryp.red_damage();
resolution.results.push(target.deal_red_damage(Skill::StrangleTick, amount)); results.push(target.deal_red_damage(Skill::StrangleTick, amount));
// remove immunity if target ko // remove immunity if target ko
if target.is_ko() { if target.is_ko() {
@ -774,206 +741,194 @@ fn strangle_tick(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution)
cryp.effects.remove(i); cryp.effects.remove(i);
} }
return resolution; return results;
} }
fn block(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { 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 }; let block = CrypEffect { effect: Effect::Block, duration: Effect::Block.duration(), tick: None };
resolution.results.push(target.add_effect(Skill::Block, block)); results.push(target.add_effect(Skill::Block, block));
return resolution; return results;
} }
fn parry(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { 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 }; let effect = CrypEffect { effect: Effect::Parry, duration: Effect::Parry.duration(), tick: None };
resolution.results.push(target.add_effect(Skill::Parry, effect)); results.push(target.add_effect(Skill::Parry, effect));
return resolution; return results;
} }
fn snare(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { 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 }; let snare = CrypEffect { effect: Effect::Snare, duration: Effect::Snare.duration(), tick: None };
resolution.results.push(target.add_effect(Skill::Snare, snare)); results.push(target.add_effect(Skill::Snare, snare));
return resolution; return results;
} }
fn empower(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { 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 }; let empower = CrypEffect { effect: Effect::Empower, duration: Effect::Empower.duration(), tick: None };
resolution.results.push(target.add_effect(Skill::Empower, empower)); results.push(target.add_effect(Skill::Empower, empower));
return resolution; return results;
} }
fn heal(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { fn heal(cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let amount = cryp.green_damage(); let amount = cryp.green_damage();
resolution.results.push(target.deal_green_damage(Skill::Heal, amount)); results.push(target.deal_green_damage(Skill::Heal, amount));
return resolution; return results;
} }
fn triage(cryp: &mut Cryp, target: &mut Cryp, resolution: Resolution) -> Resolution { fn triage(cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let effect = CrypEffect { let effect = CrypEffect {
effect: Effect::Triage, effect: Effect::Triage,
duration: Effect::Triage.duration(), duration: Effect::Triage.duration(),
tick: Some(Cast::new_tick(cryp, target, Skill::TriageTick)), tick: Some(Cast::new_tick(cryp, target, Skill::TriageTick)),
}; };
target.add_effect(Skill::Triage, effect); results.push(target.add_effect(Skill::Triage, effect));
return triage_tick(cryp, target, resolution); return triage_tick(cryp, target, results);
} }
fn triage_tick(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { fn triage_tick(cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let amount = cryp.blue_damage().wrapping_div(2); let amount = cryp.blue_damage().wrapping_div(2);
resolution.results.push(target.deal_green_damage(Skill::TriageTick, amount)); results.push(target.deal_green_damage(Skill::TriageTick, amount));
return resolution; return results;
} }
fn blast(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { fn blast(cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let amount = cryp.blue_damage(); let amount = cryp.blue_damage();
resolution.results.push(target.deal_blue_damage(Skill::Blast, amount)); results.push(target.deal_blue_damage(Skill::Blast, amount));
return resolution; return results;
} }
fn amplify(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { 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 }; let amplify = CrypEffect { effect: Effect::Amplify, duration: Effect::Amplify.duration(), tick: None };
resolution.results.push(target.add_effect(Skill::Amplify, amplify)); results.push(target.add_effect(Skill::Amplify, amplify));
return resolution;; return results;;
} }
fn haste(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { 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 }; let effect = CrypEffect { effect: Effect::Haste, duration: Effect::Haste.duration(), tick: None };
resolution.results.push(target.add_effect(Skill::Haste, effect)); results.push(target.add_effect(Skill::Haste, effect));
return resolution;; return results;;
} }
fn slow(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { 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 }; let effect = CrypEffect { effect: Effect::Slow, duration: Effect::Slow.duration(), tick: None };
resolution.results.push(target.add_effect(Skill::Slow, effect)); results.push(target.add_effect(Skill::Slow, effect));
return resolution;; return results;;
} }
fn decay(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { fn decay(cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let decay = CrypEffect { let decay = CrypEffect {
effect: Effect::Decay, effect: Effect::Decay,
duration: Effect::Decay.duration(), duration: Effect::Decay.duration(),
tick: Some(Cast::new_tick(cryp, target, Skill::DecayTick)), tick: Some(Cast::new_tick(cryp, target, Skill::DecayTick)),
}; };
resolution.results.push(target.add_effect(Skill::Decay, decay)); results.push(target.add_effect(Skill::Decay, decay));
return decay_tick(cryp, target, resolution); return decay_tick(cryp, target, results);
} }
fn decay_tick(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { fn decay_tick(cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let amount = cryp.blue_damage(); let amount = cryp.blue_damage();
resolution.results.push(target.deal_blue_damage(Skill::DecayTick, amount)); results.push(target.deal_blue_damage(Skill::DecayTick, amount));
return resolution; return results;
} }
fn hex(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { 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 }; let hex = CrypEffect { effect: Effect::Hex, duration: Effect::Hex.duration(), tick: None };
resolution.results.push(target.add_effect(Skill::Hex, hex)); results.push(target.add_effect(Skill::Hex, hex));
return resolution;; return results;;
} }
fn curse(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { 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 }; let curse = CrypEffect { effect: Effect::Curse, duration: Effect::Curse.duration(), tick: None };
resolution.results.push(target.add_effect(Skill::Curse, curse)); results.push(target.add_effect(Skill::Curse, curse));
return resolution;; return results;;
} }
fn invert(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { 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 }; let effect = CrypEffect { effect: Effect::Invert, duration: Effect::Invert.duration(), tick: None };
resolution.results.push(target.add_effect(Skill::Invert, effect)); results.push(target.add_effect(Skill::Invert, effect));
return resolution;; return results;;
} }
fn reflect(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { 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 }; let effect = CrypEffect { effect: Effect::Reflect, duration: Effect::Reflect.duration(), tick: None };
resolution.results.push(target.add_effect(Skill::Reflect, effect)); results.push(target.add_effect(Skill::Reflect, effect));
return resolution;; return results;;
} }
fn recharge(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { fn recharge(_cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
resolution.results.push(target.recharge()); results.push(target.recharge());
return resolution; return results;
} }
fn siphon(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { fn siphon(cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let siphon = CrypEffect { let siphon = CrypEffect {
effect: Effect::Siphon, effect: Effect::Siphon,
duration: Effect::Siphon.duration(), duration: Effect::Siphon.duration(),
tick: Some(Cast::new_tick(cryp, target, Skill::SiphonTick)), tick: Some(Cast::new_tick(cryp, target, Skill::SiphonTick)),
}; };
resolution.results.push(target.add_effect(Skill::Siphon, siphon)); results.push(target.add_effect(Skill::Siphon, siphon));
return siphon_tick(cryp, target, resolution); return siphon_tick(cryp, target, results);
} }
fn siphon_tick(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { fn siphon_tick(cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let amount = cryp.blue_damage(); let amount = cryp.blue_damage();
let siphon_damage = target.deal_blue_damage(Skill::SiphonTick, amount); let siphon_damage = target.deal_blue_damage(Skill::SiphonTick, amount);
resolution.results.push(siphon_damage.clone()); results.push(siphon_damage.clone());
match siphon_damage { match siphon_damage {
ResolutionResult::Damage { amount, mitigation: _, category: _, immunity } => { ResolutionResult::Damage { amount, mitigation: _, category: _, } => {
if !immunity.immune { results.push(cryp.deal_green_damage(Skill::Heal, amount));
resolution.results.push(cryp.deal_green_damage(Skill::Heal, amount));
}
}, },
_ => panic!("siphon tick damage not dealt {:?}", siphon_damage), _ => panic!("siphon tick damage not dealt {:?}", siphon_damage),
} }
return resolution; return results;
} }
fn shield(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { 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 }; let shield = CrypEffect { effect: Effect::Shield, duration: Effect::Shield.duration(), tick: None };
resolution.results.push(target.add_effect(Skill::Shield, shield)); results.push(target.add_effect(Skill::Shield, shield));
return resolution; return results;
} }
fn silence(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { 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 }; let silence = CrypEffect { effect: Effect::Silence, duration: Effect::Silence.duration(), tick: None };
resolution.results.push(target.add_effect(Skill::Silence, silence)); results.push(target.add_effect(Skill::Silence, silence));
return resolution; return results;
} }
fn purge(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { fn purge(_cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let immunity = target.immune(Skill::Purge);
let immune = immunity.immune;
if !immune {
for (i, ce) in target.effects.clone().iter_mut().enumerate() { for (i, ce) in target.effects.clone().iter_mut().enumerate() {
if ce.effect.category() == Category::BlueBuff { if ce.effect.category() == Category::BlueBuff {
target.effects.remove(i); target.effects.remove(i);
resolution.results.push(ResolutionResult::Removal { effect: ce.effect, immunity: immunity.clone() }); results.push(ResolutionResult::Removal { effect: ce.effect });
}
} }
} }
return resolution; return results;
} }
fn purify(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { fn purify(_cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let immunity = target.immune(Skill::Purify);
let immune = immunity.immune;
if !immune {
for (i, ce) in target.effects.clone().iter_mut().enumerate() { for (i, ce) in target.effects.clone().iter_mut().enumerate() {
if ce.effect.category() == Category::BlueDebuff { if ce.effect.category() == Category::BlueDebuff {
target.effects.remove(i); target.effects.remove(i);
resolution.results.push(ResolutionResult::Removal { effect: ce.effect, immunity: immunity.clone() }); results.push(ResolutionResult::Removal { effect: ce.effect });
}
} }
} }
return resolution; return results;
} }
fn banish(_cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { 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 }; let banish = CrypEffect { effect: Effect::Banish, duration: Effect::Banish.duration(), tick: None };
resolution.results.push(target.add_effect(Skill::Banish, banish)); results.push(target.add_effect(Skill::Banish, banish));
return resolution; return results;
} }
fn strike(cryp: &mut Cryp, target: &mut Cryp, mut resolution: Resolution) -> Resolution { fn strike(cryp: &mut Cryp, target: &mut Cryp, mut results: Vec<ResolutionResult>) -> Vec<ResolutionResult> {
let _amount = cryp.red_damage(); let _amount = cryp.red_damage();
resolution.results.push(target.deal_red_damage(Skill::Attack, u64::max_value())); results.push(target.deal_red_damage(Skill::Attack, u64::max_value()));
return resolution; return results;
} }
@ -993,7 +948,7 @@ mod tests {
x.deal_red_damage(Skill::Attack, 5); x.deal_red_damage(Skill::Attack, 5);
heal(&mut y, &mut x, Resolution::new(Skill::Heal)); heal(&mut y, &mut x, vec![]);
} }
#[test] #[test]
@ -1005,7 +960,7 @@ mod tests {
.named(&"camel".to_string()); .named(&"camel".to_string());
let mut log = vec![]; let mut log = vec![];
decay(&mut x, &mut y, Resolution::new(Skill::Triage)); decay(&mut x, &mut y, vec![]);
assert!(y.effects.iter().any(|e| e.effect == Effect::Decay)); assert!(y.effects.iter().any(|e| e.effect == Effect::Decay));
@ -1026,13 +981,13 @@ mod tests {
x.red_damage.force(100); x.red_damage.force(100);
y.hp.force(500); y.hp.force(500);
block(&mut y.clone(), &mut y, Resolution::new(Skill::Block)); block(&mut y.clone(), &mut y, vec![]);
assert!(y.effects.iter().any(|e| e.effect == Effect::Block)); assert!(y.effects.iter().any(|e| e.effect == Effect::Block));
let res = attack(&mut x, &mut y, Resolution::new(Skill::Attack)); let results = attack(&mut x, &mut y, vec![]);
match res.results[0] { match results[0] {
ResolutionResult::Damage { amount, mitigation: _, category: _, immunity: _ } => assert_eq!(amount, 50), ResolutionResult::Damage { amount, mitigation: _, category: _ } => assert_eq!(amount, 50),
_ => panic!("not damage"), _ => panic!("not damage"),
}; };
} }
@ -1047,14 +1002,14 @@ mod tests {
x.red_damage.force(u64::max_value()); x.red_damage.force(u64::max_value());
clutch(&mut y.clone(), &mut y, Resolution::new(Skill::Clutch)); clutch(&mut y.clone(), &mut y, vec![]);
assert!(y.is_clutch()); assert!(y.is_clutch());
let res = attack(&mut x, &mut y, Resolution::new(Skill::Attack)); let results = attack(&mut x, &mut y, vec![]);
assert!(y.hp() == 1); assert!(y.hp() == 1);
match res.results[0] { match results[0] {
ResolutionResult::Damage { amount, mitigation: _, category: _, immunity: _ } => assert_eq!(amount, 1023), ResolutionResult::Damage { amount, mitigation: _, category: _ } => assert_eq!(amount, 1023),
_ => panic!("not damage"), _ => panic!("not damage"),
}; };
} }
@ -1072,19 +1027,19 @@ mod tests {
y.red_shield.reduce(64); y.red_shield.reduce(64);
x.red_damage.force(256 + 64); x.red_damage.force(256 + 64);
invert(&mut y.clone(), &mut y, Resolution::new(Skill::Invert)); invert(&mut y.clone(), &mut y, vec![]);
assert!(y.is_inverted()); assert!(y.is_inverted());
// heal should deal green damage // heal should deal green damage
heal(&mut x, &mut y, Resolution::new(Skill::Heal)); heal(&mut x, &mut y, vec![]);
assert!(y.hp() == 768); assert!(y.hp() == 768);
// attack should heal and recharge red shield // attack should heal and recharge red shield
let res = attack(&mut x, &mut y, Resolution::new(Skill::Attack)); let results = attack(&mut x, &mut y, vec![]);
assert!(y.hp() == 1024); assert!(y.hp() == 1024);
match res.results[0] { match results[0] {
ResolutionResult::Inversion { damage: _, healing: _, recharge, category: _, immunity: _ } => assert_eq!(recharge, 64), ResolutionResult::Inversion { damage: _, healing: _, recharge, category: _ } => assert_eq!(recharge, 64),
_ => panic!("not inversion"), _ => panic!("not inversion"),
}; };
} }
@ -1097,17 +1052,15 @@ mod tests {
let mut y = Cryp::new() let mut y = Cryp::new()
.named(&"camel".to_string()); .named(&"camel".to_string());
reflect(&mut y.clone(), &mut y, Resolution::new(Skill::Reflect)); reflect(&mut y.clone(), &mut y, vec![]);
assert!(y.is_reflecting()); assert!(y.is_reflecting());
// heal should deal green damage let results = Skill::Attack.resolve(&mut x, &mut y);
let mut cast = Cast::new(Uuid::nil(), Uuid::nil(), Uuid::nil(), Skill::Attack);
cast.set_resolution(&mut x, &mut y);
assert!(x.hp() == 768); assert!(x.hp() == 768);
match cast.resolution.results[0] { match results[0] {
ResolutionResult::Damage { amount, mitigation: _, category: _, immunity: _ } => assert_eq!(amount, 256), ResolutionResult::Damage { amount, mitigation: _, category: _ } => assert_eq!(amount, 256),
_ => panic!("not damage"), _ => panic!("not damage"),
}; };
} }
@ -1130,15 +1083,9 @@ mod tests {
y.deal_red_damage(Skill::Attack, 5); y.deal_red_damage(Skill::Attack, 5);
let prev_hp = y.hp(); let prev_hp = y.hp();
let res = Resolution::new(Skill::Triage); let results = triage(&mut x, &mut y, vec![]);
triage(&mut x, &mut y, res);
assert!(y.effects.iter().any(|e| e.effect == Effect::Triage)); 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); assert!(y.hp() > prev_hp);
} }
@ -1156,11 +1103,10 @@ mod tests {
y.deal_red_damage(Skill::Attack, 5); y.deal_red_damage(Skill::Attack, 5);
y.deal_blue_damage(Skill::Blast, 5); y.deal_blue_damage(Skill::Blast, 5);
let mut res = Resolution::new(Skill::Recharge); let results = recharge(&mut x, &mut y, vec![]);
res = recharge(&mut x, &mut y, res);
match res.results[0] { match results[0] {
ResolutionResult::Recharge { red, blue, immunity: _ } => { ResolutionResult::Recharge { red, blue } => {
assert!(red == 5); assert!(red == 5);
assert!(blue == 5); assert!(blue == 5);
} }
@ -1174,9 +1120,9 @@ mod tests {
let mut x = Cryp::new() let mut x = Cryp::new()
.named(&"muji".to_string()); .named(&"muji".to_string());
silence(&mut x.clone(), &mut x, Resolution::new(Skill::Silence)); silence(&mut x.clone(), &mut x, vec![]);
assert!(x.effects.iter().any(|e| e.effect == Effect::Silence)); assert!(x.effects.iter().any(|e| e.effect == Effect::Silence));
assert!(x.disabled(Skill::Silence).disabled); assert!(x.disabled(Skill::Silence).is_some());
} }
#[test] #[test]
@ -1186,7 +1132,7 @@ mod tests {
x.blue_damage.force(50); x.blue_damage.force(50);
amplify(&mut x.clone(), &mut x, Resolution::new(Skill::Amplify)); amplify(&mut x.clone(), &mut x, vec![]);
assert!(x.effects.iter().any(|e| e.effect == Effect::Amplify)); assert!(x.effects.iter().any(|e| e.effect == Effect::Amplify));
assert_eq!(x.blue_damage(), 100); assert_eq!(x.blue_damage(), 100);
} }
@ -1196,10 +1142,10 @@ mod tests {
let mut x = Cryp::new() let mut x = Cryp::new()
.named(&"muji".to_string()); .named(&"muji".to_string());
decay(&mut x.clone(), &mut x, Resolution::new(Skill::Decay)); decay(&mut x.clone(), &mut x, vec![]);
assert!(x.effects.iter().any(|e| e.effect == Effect::Decay)); assert!(x.effects.iter().any(|e| e.effect == Effect::Decay));
purify(&mut x.clone(), &mut x, Resolution::new(Skill::Purify)); purify(&mut x.clone(), &mut x, vec![]);
assert!(!x.effects.iter().any(|e| e.effect == Effect::Decay)); assert!(!x.effects.iter().any(|e| e.effect == Effect::Decay));
} }
} }

View File

@ -145,6 +145,27 @@ impl Var {
} }
} }
pub fn speed(&self) -> u8 {
match self {
Var::Red => 3,
Var::Green => 2,
Var::Blue => 1,
Var::Attack => 1,
Var::Stun => 2,
Var::Block => 3,
Var::Buff => 4,
Var::Debuff => 4,
_ => {
let combos = get_combos();
let combo = combos.iter().find(|c| c.var == *self)
.unwrap_or_else(|| panic!("unable to find components for {:?}", self));
return combo.units.iter().fold(0, |acc, c| acc + c.speed());
},
}
}
fn effect(&self) -> Option<VarEffect> { fn effect(&self) -> Option<VarEffect> {
if let Some(_skill) = self.into_skill() { if let Some(_skill) = self.into_skill() {
return Some(VarEffect::Skill); return Some(VarEffect::Skill);
@ -163,6 +184,7 @@ impl Var {
Var::Blast => Some(Skill::Blast), Var::Blast => Some(Skill::Blast),
Var::Block => Some(Skill::Block), Var::Block => Some(Skill::Block),
Var::Curse => Some(Skill::Curse), Var::Curse => Some(Skill::Curse),
Var::Decay => Some(Skill::Decay),
Var::Empower => Some(Skill::Empower), Var::Empower => Some(Skill::Empower),
Var::Haste => Some(Skill::Haste), Var::Haste => Some(Skill::Haste),
Var::Heal => Some(Skill::Heal), Var::Heal => Some(Skill::Heal),
@ -250,6 +272,7 @@ impl From<Skill> for Var {
Skill::Triage => Var::Triage, Skill::Triage => Var::Triage,
Skill::Decay => Var::Decay, Skill::Decay => Var::Decay,
Skill::Reflect => Var::Reflect, Skill::Reflect => Var::Reflect,
Skill::Recharge => Var::Recharge,
Skill::TestTouch => Var::TestTouch, Skill::TestTouch => Var::TestTouch,
Skill::TestStun => Var::TestStun, Skill::TestStun => Var::TestStun,
@ -479,7 +502,7 @@ impl Vbox {
self.bound.get(i).ok_or(format_err!("no var at index {:?}", i))?; self.bound.get(i).ok_or(format_err!("no var at index {:?}", i))?;
let reclaimed = self.bound.remove(i); let reclaimed = self.bound.remove(i);
let refund = reclaimed.cost(); let refund = reclaimed.cost();
println!("reclaiming {:?} for {:?}", refund, reclaimed); // println!("reclaiming {:?} for {:?}", refund, reclaimed);
self.balance_add(refund); self.balance_add(refund);
Ok(self) Ok(self)
} }