686 lines
23 KiB
Rust
686 lines
23 KiB
Rust
use std::iter;
|
|
use uuid::Uuid;
|
|
|
|
// reclaims
|
|
use rand::prelude::*;
|
|
use rand::{thread_rng};
|
|
use rand::distributions::{WeightedIndex};
|
|
|
|
use postgres::transaction::Transaction;
|
|
|
|
use failure::Error;
|
|
use failure::err_msg;
|
|
|
|
use account::Account;
|
|
use rpc::{VboxAcceptParams, VboxDiscardParams, VboxCombineParams, VboxApplyParams, VboxReclaimParams, VboxUnequipParams};
|
|
use skill::{Skill};
|
|
use spec::{Spec};
|
|
use player::{Player, player_get, player_update};
|
|
use cryp::{Colours};
|
|
|
|
#[derive(Debug,Copy,Clone,Serialize,Deserialize,PartialEq,PartialOrd,Ord,Eq)]
|
|
pub enum Var {
|
|
// colours
|
|
Blue,
|
|
Green,
|
|
Red,
|
|
|
|
// base skills
|
|
Attack,
|
|
Block,
|
|
Stun,
|
|
Buff,
|
|
Debuff,
|
|
|
|
// specs
|
|
// Base
|
|
Damage,
|
|
Hp,
|
|
Speed,
|
|
|
|
// Shields Upgrades
|
|
LifeI,
|
|
RedShieldI,
|
|
BlueShieldI,
|
|
LRSI,
|
|
LBSI,
|
|
RBSI,
|
|
|
|
// Damage Upgrades
|
|
RedDamageI,
|
|
BlueDamageI,
|
|
GreenDamageI,
|
|
GRDI,
|
|
GBDI,
|
|
RBDI,
|
|
|
|
// Speed Upgrades
|
|
RedSpeedI,
|
|
BlueSpeedI,
|
|
GreenSpeedI,
|
|
GRSpeedI,
|
|
GBSpeedI,
|
|
RBSpeedI,
|
|
|
|
Amplify,
|
|
Banish,
|
|
Blast,
|
|
Curse,
|
|
Decay,
|
|
Empower,
|
|
Hostility,
|
|
Haste,
|
|
Heal,
|
|
Hex,
|
|
Invert,
|
|
Parry,
|
|
Purge,
|
|
Purify,
|
|
Reflect,
|
|
Recharge,
|
|
Ruin,
|
|
Shield,
|
|
Silence,
|
|
Slay,
|
|
Slow,
|
|
Snare,
|
|
Strangle,
|
|
Strike,
|
|
Siphon,
|
|
Clutch,
|
|
Taunt,
|
|
Throw,
|
|
Corrupt,
|
|
Triage,
|
|
|
|
|
|
TestTouch,
|
|
TestStun,
|
|
TestBlock,
|
|
TestParry,
|
|
TestSiphon,
|
|
}
|
|
|
|
enum VarEffect {
|
|
Skill,
|
|
Spec,
|
|
}
|
|
|
|
impl Var {
|
|
pub fn colours(&self, count: &mut Colours) {
|
|
let combos = get_combos();
|
|
let combo = combos.iter().find(|c| c.var == *self);
|
|
match combo {
|
|
Some(c) => c.units.iter().for_each(|unit| match unit {
|
|
Var::Red => count.red += 1,
|
|
Var::Blue => count.blue += 1,
|
|
Var::Green => count.green += 1,
|
|
_ => (),
|
|
}),
|
|
None => (),
|
|
}
|
|
}
|
|
|
|
fn cost(&self) -> u16 {
|
|
match self {
|
|
Var::Red => 1,
|
|
Var::Green => 1,
|
|
Var::Blue => 1,
|
|
|
|
Var::Attack => 2,
|
|
Var::Block => 2,
|
|
Var::Buff => 2,
|
|
Var::Debuff => 2,
|
|
Var::Stun => 2,
|
|
|
|
Var::Damage => 3,
|
|
Var::Hp => 3,
|
|
Var::Speed => 3,
|
|
|
|
_ => {
|
|
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.cost());
|
|
},
|
|
}
|
|
}
|
|
|
|
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> {
|
|
if let Some(_skill) = self.into_skill() {
|
|
return Some(VarEffect::Skill);
|
|
}
|
|
if let Some(_spec) = self.into_spec() {
|
|
return Some(VarEffect::Spec);
|
|
}
|
|
return None;
|
|
}
|
|
|
|
fn into_skill(&self) -> Option<Skill> {
|
|
match self {
|
|
Var::Attack => Some(Skill::Attack),
|
|
Var::Amplify => Some(Skill::Amplify),
|
|
Var::Banish => Some(Skill::Banish),
|
|
Var::Blast => Some(Skill::Blast),
|
|
Var::Block => Some(Skill::Block),
|
|
Var::Curse => Some(Skill::Curse),
|
|
Var::Decay => Some(Skill::Decay),
|
|
Var::Empower => Some(Skill::Empower),
|
|
Var::Haste => Some(Skill::Haste),
|
|
Var::Heal => Some(Skill::Heal),
|
|
Var::Hex => Some(Skill::Hex),
|
|
Var::Hostility => Some(Skill::Hostility),
|
|
Var::Invert => Some(Skill::Invert),
|
|
Var::Parry => Some(Skill::Parry),
|
|
Var::Purge => Some(Skill::Purge),
|
|
Var::Purify => Some(Skill::Purify),
|
|
Var::Recharge => Some(Skill::Recharge),
|
|
Var::Reflect => Some(Skill::Reflect),
|
|
Var::Ruin => Some(Skill::Ruin),
|
|
Var::Shield => Some(Skill::Shield),
|
|
Var::Silence => Some(Skill::Silence),
|
|
// Var::Slay => Some(Skill::Slay),
|
|
Var::Slow => Some(Skill::Slow),
|
|
Var::Siphon => Some(Skill::Siphon),
|
|
Var::Snare => Some(Skill::Snare),
|
|
Var::Strangle => Some(Skill::Strangle),
|
|
Var::Strike => Some(Skill::Strike),
|
|
Var::Clutch => Some(Skill::Clutch),
|
|
Var::Taunt => Some(Skill::Taunt),
|
|
Var::Throw => Some(Skill::Throw),
|
|
Var::Corrupt => Some(Skill::Corrupt),
|
|
Var::Triage => Some(Skill::Triage),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn into_spec(&self) -> Option<Spec> {
|
|
match *self {
|
|
Var::Speed => Some(Spec::Speed),
|
|
Var::RedSpeedI => Some(Spec::RedSpeedI),
|
|
Var::BlueSpeedI => Some(Spec::BlueSpeedI),
|
|
Var::GreenSpeedI => Some(Spec::GreenSpeedI),
|
|
Var::GRSpeedI => Some(Spec::GRSpeedI),
|
|
Var::GBSpeedI => Some(Spec::GBSpeedI),
|
|
Var::RBSpeedI => Some(Spec::RBSpeedI),
|
|
|
|
Var::Damage => Some(Spec::Damage),
|
|
Var::RedDamageI => Some(Spec::RedDamageI),
|
|
Var::BlueDamageI => Some(Spec::BlueDamageI),
|
|
Var::GreenDamageI => Some(Spec::GreenDamageI),
|
|
Var::GRDI => Some(Spec::GRDI),
|
|
Var::GBDI => Some(Spec::GBDI),
|
|
Var::RBDI => Some(Spec::RBDI),
|
|
|
|
Var::Hp => Some(Spec::Hp),
|
|
Var::LifeI => Some(Spec::LifeI),
|
|
Var::LRSI => Some(Spec::LRSI),
|
|
Var::LBSI => Some(Spec::LBSI),
|
|
Var::RBSI => Some(Spec::RBSI),
|
|
Var::RedShieldI => Some(Spec::RedShieldI),
|
|
Var::BlueShieldI => Some(Spec::BlueShieldI),
|
|
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Skill> for Var {
|
|
fn from(skill: Skill) -> Var {
|
|
match skill {
|
|
Skill::Amplify => Var::Amplify,
|
|
Skill::Attack => Var::Attack,
|
|
Skill::Banish => Var::Banish,
|
|
Skill::Blast => Var::Blast,
|
|
Skill::Block => Var::Block,
|
|
Skill::Curse => Var::Curse,
|
|
Skill::Decay => Var::Decay,
|
|
Skill::Empower => Var::Empower,
|
|
Skill::Haste => Var::Haste,
|
|
Skill::Hostility => Var::Hostility,
|
|
Skill::Heal => Var::Heal,
|
|
Skill::Hex => Var::Hex,
|
|
Skill::Invert => Var::Invert,
|
|
Skill::Parry => Var::Parry,
|
|
Skill::Purge => Var::Purge,
|
|
Skill::Purify => Var::Purify,
|
|
Skill::Recharge => Var::Recharge,
|
|
Skill::Reflect => Var::Reflect,
|
|
Skill::Ruin => Var::Ruin,
|
|
Skill::Shield => Var::Shield,
|
|
Skill::Silence => Var::Silence,
|
|
Skill::Siphon => Var::Siphon,
|
|
Skill::Slow => Var::Slow,
|
|
Skill::Snare => Var::Snare,
|
|
Skill::Strangle => Var::Strangle,
|
|
Skill::Strike => Var::Strike,
|
|
Skill::Stun => Var::Stun,
|
|
Skill::Taunt => Var::Taunt,
|
|
Skill::Throw => Var::Throw,
|
|
Skill::Triage => Var::Triage,
|
|
Skill::Corrupt => Var::Corrupt,
|
|
|
|
Skill::TestTouch => Var::TestTouch,
|
|
Skill::TestStun => Var::TestStun,
|
|
Skill::TestBlock => Var::TestBlock,
|
|
Skill::TestParry => Var::TestParry,
|
|
Skill::TestSiphon => Var::TestSiphon,
|
|
_ => panic!("{:?} not implemented as a var", skill),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Spec> for Var {
|
|
fn from(spec: Spec) -> Var {
|
|
match spec {
|
|
Spec::Speed => Var::Speed,
|
|
Spec::RedSpeedI => Var::RedSpeedI,
|
|
Spec::BlueSpeedI => Var::BlueSpeedI,
|
|
Spec::GreenSpeedI => Var::GreenSpeedI,
|
|
Spec::GRSpeedI => Var::GRSpeedI,
|
|
Spec::GBSpeedI => Var::GBSpeedI,
|
|
Spec::RBSpeedI => Var::RBSpeedI,
|
|
|
|
Spec::Damage => Var::Damage,
|
|
Spec::RedDamageI => Var::RedDamageI,
|
|
Spec::BlueDamageI => Var::BlueDamageI,
|
|
Spec::GreenDamageI => Var::GreenDamageI,
|
|
Spec::GRDI => Var::GRDI,
|
|
Spec::GBDI => Var::GBDI,
|
|
Spec::RBDI => Var::RBDI,
|
|
|
|
Spec::Hp => Var::Hp,
|
|
Spec::LifeI => Var::LifeI,
|
|
Spec::LRSI => Var::LRSI,
|
|
Spec::LBSI => Var::LBSI,
|
|
Spec::RBSI => Var::RBSI,
|
|
Spec::RedShieldI => Var::RedShieldI,
|
|
Spec::BlueShieldI => Var::BlueShieldI,
|
|
// _ => panic!("{:?} not implemented as a var", spec),
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
struct Combo {
|
|
var: Var,
|
|
units: Vec<Var>,
|
|
}
|
|
|
|
fn get_combos() -> Vec<Combo> {
|
|
let mut combinations = vec![
|
|
Combo { units: vec![Var::Buff, Var::Red, Var::Red], var: Var::Empower },
|
|
Combo { units: vec![Var::Buff, Var::Green, Var::Green], var: Var::Triage },
|
|
Combo { units: vec![Var::Buff, Var::Blue, Var::Blue], var: Var::Hostility },
|
|
Combo { units: vec![Var::Buff, Var::Red, Var::Green], var: Var::Clutch },
|
|
Combo { units: vec![Var::Buff, Var::Green, Var::Blue], var: Var::Amplify },
|
|
Combo { units: vec![Var::Buff, Var::Red, Var::Blue], var: Var::Haste },
|
|
|
|
Combo { units: vec![Var::Debuff, Var::Red, Var::Red], var: Var::Snare },
|
|
Combo { units: vec![Var::Debuff, Var::Green, Var::Green], var: Var::Purge },
|
|
Combo { units: vec![Var::Debuff, Var::Blue, Var::Blue], var: Var::Curse },
|
|
Combo { units: vec![Var::Debuff, Var::Red, Var::Green], var: Var::Slow },
|
|
Combo { units: vec![Var::Debuff, Var::Green, Var::Blue], var: Var::Siphon },
|
|
Combo { units: vec![Var::Debuff, Var::Red, Var::Blue], var: Var::Invert },
|
|
|
|
Combo { units: vec![Var::Block, Var::Red, Var::Red], var: Var::Parry },
|
|
Combo { units: vec![Var::Block, Var::Green, Var::Green], var: Var::Reflect },
|
|
Combo { units: vec![Var::Block, Var::Blue, Var::Blue], var: Var::Corrupt },
|
|
Combo { units: vec![Var::Block, Var::Red, Var::Green], var: Var::Taunt },
|
|
Combo { units: vec![Var::Block, Var::Green, Var::Blue], var: Var::Shield },
|
|
Combo { units: vec![Var::Block, Var::Red, Var::Blue], var: Var::Recharge },
|
|
|
|
Combo { units: vec![Var::Stun, Var::Red, Var::Red], var: Var::Strangle },
|
|
Combo { units: vec![Var::Stun, Var::Green, Var::Green], var: Var::Throw },
|
|
Combo { units: vec![Var::Stun, Var::Blue, Var::Blue], var: Var::Ruin },
|
|
Combo { units: vec![Var::Stun, Var::Red, Var::Green], var: Var::Banish },
|
|
Combo { units: vec![Var::Stun, Var::Green, Var::Blue], var: Var::Silence },
|
|
Combo { units: vec![Var::Stun, Var::Red, Var::Blue], var: Var::Hex },
|
|
|
|
Combo { units: vec![Var::Attack, Var::Red, Var::Red], var: Var::Strike },
|
|
Combo { units: vec![Var::Attack, Var::Green, Var::Green], var: Var::Heal },
|
|
Combo { units: vec![Var::Attack, Var::Blue, Var::Blue], var: Var::Blast },
|
|
Combo { units: vec![Var::Attack, Var::Red, Var::Green], var: Var::Purify },
|
|
Combo { units: vec![Var::Attack, Var::Green, Var::Blue], var: Var::Decay },
|
|
// Combo { units: vec![Var::Attack, Var::Red, Var::Blue], var: Var::Blast },
|
|
|
|
Combo { units: vec![Var::Damage, Var::Red, Var::Red], var: Var::RedDamageI },
|
|
Combo { units: vec![Var::Damage, Var::Green, Var::Green], var: Var::GreenDamageI },
|
|
Combo { units: vec![Var::Damage, Var::Blue, Var::Blue], var: Var::BlueDamageI },
|
|
Combo { units: vec![Var::Damage, Var::Red, Var::Green], var: Var::GRDI },
|
|
Combo { units: vec![Var::Damage, Var::Green, Var::Blue], var: Var::GBDI },
|
|
Combo { units: vec![Var::Damage, Var::Red, Var::Blue], var: Var::RBDI },
|
|
|
|
Combo { units: vec![Var::Hp, Var::Red, Var::Red], var: Var::RedShieldI },
|
|
Combo { units: vec![Var::Hp, Var::Green, Var::Green], var: Var::LifeI },
|
|
Combo { units: vec![Var::Hp, Var::Blue, Var::Blue], var: Var::BlueShieldI },
|
|
Combo { units: vec![Var::Hp, Var::Red, Var::Green], var: Var::LRSI },
|
|
Combo { units: vec![Var::Hp, Var::Green, Var::Blue], var: Var::LBSI },
|
|
Combo { units: vec![Var::Hp, Var::Red, Var::Blue], var: Var::RBSI },
|
|
|
|
Combo { units: vec![Var::Speed, Var::Red, Var::Red], var: Var::RedSpeedI },
|
|
Combo { units: vec![Var::Speed, Var::Green, Var::Green], var: Var::GreenSpeedI },
|
|
Combo { units: vec![Var::Speed, Var::Blue, Var::Blue], var: Var::BlueSpeedI },
|
|
Combo { units: vec![Var::Speed, Var::Red, Var::Green], var: Var::GRSpeedI },
|
|
Combo { units: vec![Var::Speed, Var::Green, Var::Blue], var: Var::GBSpeedI },
|
|
Combo { units: vec![Var::Speed, Var::Red, Var::Blue], var: Var::RBSpeedI },
|
|
|
|
];
|
|
|
|
combinations.iter_mut().for_each(|set| set.units.sort_unstable());
|
|
|
|
return combinations;
|
|
}
|
|
|
|
#[derive(Debug,Clone,Serialize,Deserialize)]
|
|
pub struct Vbox {
|
|
pub id: Uuid,
|
|
pub bits: u16,
|
|
pub free: Vec<Vec<Var>>,
|
|
pub bound: Vec<Var>,
|
|
pub instance: Uuid,
|
|
pub account: Uuid,
|
|
}
|
|
|
|
impl Vbox {
|
|
pub fn new(account_id: Uuid, instance_id: Uuid) -> Vbox {
|
|
// line them up with the columns of the vbox
|
|
let starting_items = vec![
|
|
Var::Red,
|
|
Var::Attack,
|
|
Var::Hp,
|
|
Var::Green,
|
|
Var::Attack,
|
|
Var::Speed,
|
|
Var::Blue,
|
|
Var::Attack,
|
|
Var::Damage,
|
|
];
|
|
|
|
let mut vbox = Vbox {
|
|
id: Uuid::new_v4(),
|
|
account: account_id,
|
|
instance: instance_id,
|
|
free: vec![],
|
|
bound: starting_items,
|
|
bits: 18,
|
|
};
|
|
|
|
vbox.fill();
|
|
|
|
return vbox;
|
|
}
|
|
|
|
pub fn balance_sub(&mut self, amount: u16) -> Result<&mut Vbox, Error> {
|
|
let new_balance = self.bits
|
|
.checked_sub(amount)
|
|
.ok_or(format_err!("insufficient balance: {:?}", self.bits))?;
|
|
|
|
self.bits = new_balance;
|
|
|
|
Ok(self)
|
|
}
|
|
|
|
pub fn balance_add(&mut self, amount: u16) -> &mut Vbox {
|
|
self.bits = self.bits.saturating_add(amount);
|
|
self
|
|
}
|
|
|
|
pub fn fill(&mut self) -> &mut Vbox {
|
|
let colours = vec![
|
|
(Var::Red, 1),
|
|
(Var::Green, 1),
|
|
(Var::Blue, 1),
|
|
];
|
|
|
|
let skills = vec![
|
|
(Var::Attack, 1),
|
|
(Var::Block, 1),
|
|
(Var::Buff, 1),
|
|
(Var::Debuff, 1),
|
|
(Var::Stun, 1),
|
|
];
|
|
|
|
let specs = vec![
|
|
(Var::Damage, 1),
|
|
(Var::Hp, 1),
|
|
(Var::Speed, 1),
|
|
];
|
|
|
|
let mut rng = thread_rng();
|
|
|
|
self.free = [&colours, &skills, &specs].iter()
|
|
.map(|vars| {
|
|
let dist = WeightedIndex::new(vars.iter().map(|item| item.1)).unwrap();
|
|
|
|
iter::repeat_with(|| {
|
|
vars[dist.sample(&mut rng)].0
|
|
})
|
|
.take(6)
|
|
.collect::<Vec<Var>>()
|
|
})
|
|
.collect::<Vec<Vec<Var>>>();
|
|
|
|
self
|
|
}
|
|
|
|
pub fn accept(&mut self, i: usize, j: usize) -> Result<&mut Vbox, Error> {
|
|
if self.bound.len() >= 9 {
|
|
return Err(err_msg("too many vars bound"));
|
|
}
|
|
|
|
// check item exists
|
|
self.free
|
|
.get(i).ok_or(format_err!("no var group at index {:?}", i))?
|
|
.get(j).ok_or(format_err!("no var at index {:?}", j))?;
|
|
|
|
// check can purchase
|
|
let cost = self.free[i][j].cost();
|
|
self.balance_sub(cost)?;
|
|
|
|
// actually move
|
|
self.bound.push(self.free[i].remove(j));
|
|
|
|
Ok(self)
|
|
}
|
|
|
|
pub fn reclaim(&mut self, i: usize) -> Result<&mut Vbox, Error> {
|
|
self.bound.get(i).ok_or(format_err!("no var at index {:?}", i))?;
|
|
let reclaimed = self.bound.remove(i);
|
|
let refund = reclaimed.cost();
|
|
// println!("reclaiming {:?} for {:?}", refund, reclaimed);
|
|
self.balance_add(refund);
|
|
Ok(self)
|
|
}
|
|
|
|
pub fn combine(&mut self, mut indices: Vec<usize>) -> Result<&mut Vbox, Error> {
|
|
if indices.len() != 3 {
|
|
return Err(err_msg("exactly 3 indices required"));
|
|
}
|
|
|
|
if !indices.iter().all(|i| self.bound.get(*i).is_some()) {
|
|
return Err(err_msg("var missing index"));
|
|
}
|
|
|
|
// have to sort the indices and keep track of the iteration
|
|
// because when removing the elements the array shifts
|
|
indices.sort_unstable();
|
|
let mut input = indices
|
|
.iter()
|
|
.enumerate()
|
|
.map(|(i, index)| {
|
|
self.bound.remove(*index - i)
|
|
})
|
|
.collect::<Vec<Var>>();
|
|
|
|
// sort the input to align with the combinations
|
|
// combos are sorted when created
|
|
input.sort_unstable();
|
|
let combos = get_combos();
|
|
let combo = combos.iter().find(|c| c.units == input).ok_or(err_msg("not a combo"))?;
|
|
|
|
self.bound.push(combo.var);
|
|
|
|
Ok(self)
|
|
}
|
|
}
|
|
|
|
const DISCARD_COST: u16 = 5;
|
|
|
|
pub fn vbox_discard(params: VboxDiscardParams, tx: &mut Transaction, account: &Account) -> Result<Player, Error> {
|
|
let mut player = player_get(tx, account.id, params.instance_id)?;
|
|
player.vbox.balance_sub(DISCARD_COST)?;
|
|
player.vbox.fill();
|
|
return player_update(tx, player, false);
|
|
}
|
|
|
|
pub fn vbox_accept(params: VboxAcceptParams, tx: &mut Transaction, account: &Account) -> Result<Player, Error> {
|
|
let mut player = player_get(tx, account.id, params.instance_id)?;
|
|
player.vbox.accept(params.group, params.index)?;
|
|
return player_update(tx, player, false);
|
|
}
|
|
|
|
pub fn vbox_combine(params: VboxCombineParams, tx: &mut Transaction, account: &Account) -> Result<Player, Error> {
|
|
let mut player = player_get(tx, account.id, params.instance_id)?;
|
|
player.vbox.combine(params.indices)?;
|
|
return player_update(tx, player, false);
|
|
}
|
|
|
|
pub fn vbox_reclaim(params: VboxReclaimParams, tx: &mut Transaction, account: &Account) -> Result<Player, Error> {
|
|
let mut player = player_get(tx, account.id, params.instance_id)?;
|
|
player.vbox.reclaim(params.index)?;
|
|
return player_update(tx, player, false);
|
|
}
|
|
|
|
pub fn vbox_apply(params: VboxApplyParams, tx: &mut Transaction, account: &Account) -> Result<Player, Error> {
|
|
let mut player = player_get(tx, account.id, params.instance_id)?;
|
|
let var = player.vbox.bound.remove(params.index);
|
|
|
|
match var.effect() {
|
|
Some(VarEffect::Skill) => {
|
|
let skill = var.into_skill().ok_or(format_err!("var {:?} has no associated skill", var))?;
|
|
let cryp = player.cryp_get(params.cryp_id)?;
|
|
// done here because i teach them a tonne of skills for tests
|
|
let max_skills = 4;
|
|
if cryp.skills.len() >= max_skills {
|
|
return Err(format_err!("cryp at max skills ({:?})", max_skills));
|
|
}
|
|
|
|
cryp.learn_mut(skill);
|
|
},
|
|
Some(VarEffect::Spec) => {
|
|
let spec = var.into_spec().ok_or(format_err!("var {:?} has no associated spec", var))?;
|
|
let cryp = player.cryp_get(params.cryp_id)?;
|
|
cryp.spec_add(spec)?;
|
|
|
|
},
|
|
None => return Err(err_msg("var has no effect on cryps")),
|
|
}
|
|
|
|
// now the var has been applied
|
|
// recalculate the stats of the whole team
|
|
let team_colours = player.cryps.iter().fold(Colours::new(), |tc, c| {
|
|
Colours {
|
|
red: tc.red + c.colours.red,
|
|
green: tc.green + c.colours.green,
|
|
blue: tc.blue + c.colours.blue
|
|
}
|
|
});
|
|
|
|
for cryp in player.cryps.iter_mut() {
|
|
cryp.apply_modifiers(&team_colours);
|
|
}
|
|
|
|
return player_update(tx, player, false);
|
|
}
|
|
|
|
pub fn vbox_unequip(params: VboxUnequipParams, tx: &mut Transaction, account: &Account) -> Result<Player, Error> {
|
|
let mut player = player_get(tx, account.id, params.instance_id)?;
|
|
|
|
if player.vbox.bound.len() >= 9 {
|
|
return Err(err_msg("too many vars bound"));
|
|
}
|
|
|
|
println!("{:?}", params);
|
|
|
|
match params.target.effect() {
|
|
Some(VarEffect::Skill) => {
|
|
let skill = params.target.into_skill().ok_or(format_err!("var {:?} has no associated skill", params.target))?;
|
|
let cryp = player.cryp_get(params.cryp_id)?;
|
|
cryp.forget(skill)?;
|
|
},
|
|
Some(VarEffect::Spec) => {
|
|
let spec = params.target.into_spec().ok_or(format_err!("var {:?} has no associated spec", params.target))?;
|
|
let cryp = player.cryp_get(params.cryp_id)?;
|
|
cryp.spec_remove(spec)?;
|
|
},
|
|
None => return Err(err_msg("var has no effect on cryps")),
|
|
}
|
|
|
|
player.vbox.bound.push(params.target);
|
|
return player_update(tx, player, false);
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn combine_test() {
|
|
let mut vbox = Vbox::new(Uuid::new_v4(), Uuid::new_v4());
|
|
vbox.bound = vec![Var::Attack, Var::Green, Var::Green];
|
|
vbox.combine(vec![1,2,0]).unwrap();
|
|
assert_eq!(vbox.bound[0], Var::Heal);
|
|
}
|
|
|
|
#[test]
|
|
fn combos_test() {
|
|
let mut input = vec![Var::Green, Var::Attack, Var::Green];
|
|
let combos = get_combos();
|
|
|
|
// sort input so they align
|
|
input.sort_unstable();
|
|
|
|
let combo = combos.iter().find(|c| c.units == input);
|
|
assert!(combo.is_some());
|
|
}
|
|
|
|
#[test]
|
|
fn reclaim_test() {
|
|
let mut vbox = Vbox::new(Uuid::new_v4(), Uuid::new_v4());
|
|
vbox.bound = vec![Var::Strike];
|
|
vbox.reclaim(0).unwrap();
|
|
assert_eq!(vbox.bits, 22);
|
|
}
|
|
|
|
#[test]
|
|
fn colours_count_test() {
|
|
let strike = Var::Strike;
|
|
|
|
let mut count = Colours::new();
|
|
strike.colours(&mut count);
|
|
assert_eq!(count.red, 2);
|
|
}
|
|
|
|
} |