mnml/server/src/vbox.rs
2019-05-14 10:58:37 +10:00

683 lines
22 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 instance::{Instance, instance_get, instance_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,
Life,
Speed,
// Lifes Upgrades
GreenLifeI,
RedLifeI,
BlueLifeI,
GRLI,
GBLI,
RBLI,
// Damage Upgrades
RedDamageI,
BlueDamageI,
GreenDamageI,
GRDI,
GBDI,
RBDI,
// Speed Upgrades
RedSpeedI,
BlueSpeedI,
GreenSpeedI,
GRSpeedI,
GBSpeedI,
RBSpeedI,
Amplify,
Banish,
Blast,
Chaos,
Clutch,
Corrupt,
Curse,
Decay,
Hostility,
Haste,
Heal,
Hex,
Impurity,
Invert,
Parry,
Purge,
Purify,
Reflect,
Recharge,
Ruin,
Scatter,
Silence,
Slay,
Sleep,
Snare,
Strangle,
Strike,
StrikeII,
StrikeIII,
Siphon,
Taunt,
Throw,
Triage,
TestTouch,
TestStun,
TestBlock,
TestParry,
TestSiphon,
}
pub 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,
_ => {
let mut combo_count = Colours::new();
unit.colours(&mut combo_count);
count.red += combo_count.red;
count.blue += combo_count.blue;
count.green += combo_count.green;
}
}),
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::Life => 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());
},
}
}
pub 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;
}
pub 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::Chaos => Some(Skill::Chaos),
Var::Curse => Some(Skill::Curse),
Var::Debuff => Some(Skill::Debuff),
Var::Decay => Some(Skill::Decay),
Var::Haste => Some(Skill::Haste),
Var::Heal => Some(Skill::Heal),
Var::Hex => Some(Skill::Hex),
Var::Hostility => Some(Skill::Hostility),
Var::Impurity => Some(Skill::Impurity),
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::Scatter => Some(Skill::Scatter),
Var::Silence => Some(Skill::Silence),
Var::Slay => Some(Skill::Slay),
Var::Sleep => Some(Skill::Sleep),
Var::Siphon => Some(Skill::Siphon),
Var::Snare => Some(Skill::Snare),
Var::Strangle => Some(Skill::Strangle),
Var::Stun => Some(Skill::Stun),
Var::Strike => Some(Skill::Strike),
Var::StrikeII => Some(Skill::StrikeII),
Var::StrikeIII => Some(Skill::StrikeIII),
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,
}
}
pub 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::Life => Some(Spec::Life),
Var::GRLI => Some(Spec::GRLI),
Var::GBLI => Some(Spec::GBLI),
Var::RBLI => Some(Spec::RBLI),
Var::GreenLifeI => Some(Spec::GreenLifeI),
Var::RedLifeI => Some(Spec::RedLifeI),
Var::BlueLifeI => Some(Spec::BlueLifeI),
_ => 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::Chaos => Var::Chaos,
Skill::Curse => Var::Curse,
Skill::Clutch => Var::Clutch,
Skill::Decay => Var::Decay,
Skill::Debuff => Var::Debuff,
Skill::Haste => Var::Haste,
Skill::Hostility => Var::Hostility,
Skill::Heal => Var::Heal,
Skill::Hex => Var::Hex,
Skill::Impurity => Var::Impurity,
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::Scatter => Var::Scatter,
Skill::Silence => Var::Silence,
Skill::Siphon => Var::Siphon,
Skill::Slay => Var::Slay,
Skill::Sleep => Var::Sleep,
Skill::Snare => Var::Snare,
Skill::Strangle => Var::Strangle,
Skill::Strike => Var::Strike,
Skill::StrikeII => Var::StrikeII,
Skill::StrikeIII => Var::StrikeIII,
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::Life => Var::Life,
Spec::GRLI => Var::GRLI,
Spec::GBLI => Var::GBLI,
Spec::RBLI => Var::RBLI,
Spec::GreenLifeI => Var::GreenLifeI,
Spec::RedLifeI => Var::RedLifeI,
Spec::BlueLifeI => Var::BlueLifeI,
// _ => panic!("{:?} not implemented as a var", spec),
}
}
}
#[derive(Debug,Clone,Serialize,Deserialize)]
pub 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::Taunt },
Combo { units: vec![Var::Buff, Var::Green, Var::Green], var: Var::Triage },
Combo { units: vec![Var::Buff, Var::Blue, Var::Blue], var: Var::Scatter }, //To be impl
Combo { units: vec![Var::Buff, Var::Red, Var::Green], var: Var::Haste },
Combo { units: vec![Var::Buff, Var::Green, Var::Blue], var: Var::Impurity },
Combo { units: vec![Var::Buff, Var::Red, Var::Blue], var: Var::Amplify }, // Some flavour
Combo { units: vec![Var::Debuff, Var::Red, Var::Red], var: Var::Snare },
Combo { units: vec![Var::Debuff, Var::Green, Var::Green], var: Var::Purge }, // Needs flavour
Combo { units: vec![Var::Debuff, Var::Blue, Var::Blue], var: Var::Silence },
Combo { units: vec![Var::Debuff, Var::Red, Var::Green], var: Var::Curse }, // To be reworked
Combo { units: vec![Var::Debuff, Var::Green, Var::Blue], var: Var::Decay },
Combo { units: vec![Var::Debuff, Var::Red, Var::Blue], var: Var::Invert },
Combo { units: vec![Var::Block, Var::Red, Var::Red], var: Var::Parry }, // Add red recharge
Combo { units: vec![Var::Block, Var::Green, Var::Green], var: Var::Purify },
Combo { units: vec![Var::Block, Var::Blue, Var::Blue], var: Var::Corrupt },
Combo { units: vec![Var::Block, Var::Red, Var::Green], var: Var::Clutch },
Combo { units: vec![Var::Block, Var::Green, Var::Blue], var: Var::Reflect },
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::Sleep },
Combo { units: vec![Var::Stun, Var::Blue, Var::Blue], var: Var::Ruin },
Combo { units: vec![Var::Stun, Var::Red, Var::Green], var: Var::Throw },
Combo { units: vec![Var::Stun, Var::Green, Var::Blue], var: Var::Hex },
Combo { units: vec![Var::Stun, Var::Red, Var::Blue], var: Var::Banish },
Combo { units: vec![Var::Attack, Var::Red, Var::Red], var: Var::Strike },
Combo { units: vec![Var::Strike, Var::Strike, Var::Strike], var: Var::StrikeII },
Combo { units: vec![Var::StrikeII, Var::StrikeII, Var::StrikeII], var: Var::StrikeIII },
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::Slay },
Combo { units: vec![Var::Attack, Var::Green, Var::Blue], var: Var::Siphon },
Combo { units: vec![Var::Attack, Var::Red, Var::Blue], var: Var::Chaos },
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::Life, Var::Red, Var::Red], var: Var::RedLifeI },
Combo { units: vec![Var::Life, Var::Green, Var::Green], var: Var::GreenLifeI },
Combo { units: vec![Var::Life, Var::Blue, Var::Blue], var: Var::BlueLifeI },
Combo { units: vec![Var::Life, Var::Red, Var::Green], var: Var::GRLI },
Combo { units: vec![Var::Life, Var::Green, Var::Blue], var: Var::GBLI },
Combo { units: vec![Var::Life, Var::Red, Var::Blue], var: Var::RBLI },
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 VarInfo {
pub v: Var,
pub spec: bool,
pub skill: bool,
}
#[derive(Debug,Clone,Serialize,Deserialize)]
pub struct VboxInfo {
pub combos: Vec<Combo>,
pub vars: Vec<VarInfo>,
}
pub fn vbox_info() -> VboxInfo {
let combos = get_combos();
let mut vars = combos
.into_iter()
.flat_map(|mut c| {
c.units.push(c.var);
c.units
})
.collect::<Vec<Var>>();
vars.sort_unstable();
vars.dedup();
let vars = vars
.into_iter()
.map(|v| VarInfo {
v,
spec: v.into_spec().is_some(),
skill: v.into_skill().is_some(),
})
.collect::<Vec<VarInfo>>();
let combos = get_combos();
return VboxInfo {
combos,
vars,
};
}
#[derive(Debug,Clone,Serialize,Deserialize)]
pub struct Vbox {
pub bits: u16,
pub free: Vec<Vec<Var>>,
pub bound: Vec<Var>,
}
impl Vbox {
pub fn new() -> Vbox {
let starting_items = vec![
Var::Attack,
Var::Attack,
Var::Attack,
];
Vbox {
free: vec![vec![], vec![], vec![]],
bound: starting_items,
bits: 18,
}
}
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::Life, 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));
// self.bound.sort_unstable();
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.saturating_sub(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);
self.bound.sort_unstable();
Ok(self)
}
}
pub fn vbox_discard(params: VboxDiscardParams, tx: &mut Transaction, account: &Account) -> Result<Instance, Error> {
let instance = instance_get(tx, params.instance_id)?
.vbox_discard(account.id)?;
return instance_update(tx, instance);
}
pub fn vbox_accept(params: VboxAcceptParams, tx: &mut Transaction, account: &Account) -> Result<Instance, Error> {
let instance = instance_get(tx, params.instance_id)?
.vbox_accept(account.id, params.group, params.index)?;
return instance_update(tx, instance);
}
pub fn vbox_combine(params: VboxCombineParams, tx: &mut Transaction, account: &Account) -> Result<Instance, Error> {
let instance = instance_get(tx, params.instance_id)?
.vbox_combine(account.id, params.indices)?;
return instance_update(tx, instance);
}
pub fn vbox_reclaim(params: VboxReclaimParams, tx: &mut Transaction, account: &Account) -> Result<Instance, Error> {
let instance = instance_get(tx, params.instance_id)?
.vbox_reclaim(account.id, params.index)?;
return instance_update(tx, instance);
}
pub fn vbox_apply(params: VboxApplyParams, tx: &mut Transaction, account: &Account) -> Result<Instance, Error> {
let instance = instance_get(tx, params.instance_id)?
.vbox_apply(account.id, params.index, params.cryp_id)?;
return instance_update(tx, instance);
}
pub fn vbox_unequip(params: VboxUnequipParams, tx: &mut Transaction, account: &Account) -> Result<Instance, Error> {
let instance = instance_get(tx, params.instance_id)?
.vbox_unequip(account.id, params.target, params.cryp_id)?;
return instance_update(tx, instance);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn combine_test() {
let mut vbox = Vbox::new();
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();
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);
}
// #[test]
// fn vbox_info_test() {
// println!("{:#?}", vbox_info());
// }
}