diff --git a/client/src/scenes/item.list.js b/client/src/scenes/item.list.js old mode 100644 new mode 100755 index 3cc88380..557698fa --- a/client/src/scenes/item.list.js +++ b/client/src/scenes/item.list.js @@ -152,9 +152,9 @@ class ItemList extends Phaser.Scene { drawInventory(graphics); drawVbox(graphics); - this.add.text(X + ITEM_WIDTH * 0.5, Y + ITEM_HEIGHT * 0.5, `Varibox - $${vbox.balance} available`, TEXT.HEADER); - this.add.text(X + ITEM_WIDTH * 0.5, Y + ITEM_HEIGHT * 11, 'Inventory', TEXT.HEADER); - this.add.text(X + ITEM_WIDTH * 0.5, Y + ITEM_HEIGHT * 18, 'Combiner', TEXT.HEADER); + this.add.text(X + ITEM_WIDTH * 0.5, Y + ITEM_HEIGHT * 0.5, `vBox - ${vbox.bits}b`, TEXT.HEADER); + this.add.text(X + ITEM_WIDTH * 0.5, Y + ITEM_HEIGHT * 11, 'inventory', TEXT.HEADER); + this.add.text(X + ITEM_WIDTH * 0.5, Y + ITEM_HEIGHT * 18, 'iCombinator', TEXT.HEADER); this.add.text(X + ITEM_WIDTH * 0.5, Y + ITEM_HEIGHT * 23, `Wins: ${player.score.wins}`, TEXT.HEADER); this.add.text(X + ITEM_WIDTH * 0.5, Y + ITEM_HEIGHT * 24, `Losses: ${player.score.losses}`, TEXT.HEADER); diff --git a/server/WORKLOG.md b/server/WORKLOG.md index b6a7f6a4..f0e32ed4 100644 --- a/server/WORKLOG.md +++ b/server/WORKLOG.md @@ -9,11 +9,14 @@ # WORK WORK ## NOW -cost system for items - be able to reclaim skills / specs from cryp +be able to reclaim skills / specs from cryp combo specs randomise skill speed +scoreboard + +constants + round system for games join instance is pve? diff --git a/server/src/instance.rs b/server/src/instance.rs index bba1b5ed..dcb0eb16 100644 --- a/server/src/instance.rs +++ b/server/src/instance.rs @@ -58,7 +58,7 @@ impl Instance { self.players = iter::repeat_with(|| { let bot_id = Uuid::new_v4(); let cryps = instance_mobs(bot_id); - Player::new(bot_id, self.id, cryps).set_bot(true) + Player::new(bot_id, self.id, &bot_id.to_string(), cryps).set_bot(true) }) .take(15) .collect::>(); @@ -212,7 +212,7 @@ impl Instance { .iter() .filter(|p| round.player_ids.contains(&p.id) && p.bot) .count() == 2 { - println!("should play a game between {:?}", round.player_ids); + // println!("should play a game between {:?}", round.player_ids); let a = self.players.clone().into_iter().find(|p| p.id == round.player_ids[0]).unwrap(); let b = self.players.clone().into_iter().find(|p| p.id == round.player_ids[1]).unwrap(); @@ -400,7 +400,7 @@ pub fn instance_join(params: InstanceJoinParams, tx: &mut Transaction, account: return Err(format_err!("incorrect team size. ({:})", 3)); } - let mut player = Player::new(account.id, instance.id, cryps); + let mut player = Player::new(account.id, instance.id, &account.name, cryps); player.vbox.fill(); let player = player_create(tx, player, account)?; @@ -515,7 +515,7 @@ mod tests { let player_id = Uuid::new_v4(); let cryps = instance_mobs(player_id); - let player = Player::new(player_id, instance.id, cryps).set_bot(true); + let player = Player::new(player_id, instance.id, &"test".to_string(), cryps).set_bot(true); instance.add_player(player); assert!(instance.can_start()); @@ -529,7 +529,7 @@ mod tests { assert!(instance.vbox_phase_finished()); instance.games_phase_start(); - println!("{:#?}", instance); + // println!("{:#?}", instance); assert!(instance.games_phase_finished()); } diff --git a/server/src/player.rs b/server/src/player.rs index c2efdb9d..898a8d3c 100644 --- a/server/src/player.rs +++ b/server/src/player.rs @@ -24,7 +24,7 @@ pub struct Player { pub id: Uuid, pub instance: Uuid, pub account: Uuid, - // name: String, + pub name: String, pub vbox: Vbox, pub score: Score, pub cryps: Vec, @@ -33,11 +33,12 @@ pub struct Player { } impl Player { - pub fn new(account: Uuid, instance: Uuid, cryps: Vec) -> Player { + pub fn new(account: Uuid, instance: Uuid, name: &String, cryps: Vec) -> Player { Player { id: Uuid::new_v4(), account, instance, + name: name.clone(), vbox: Vbox::new(account, instance), score: Score { wins: 0, losses: 0 }, cryps, @@ -179,7 +180,7 @@ pub fn player_cryps_set(params: PlayerCrypsSetParams, tx: &mut Transaction, acco player_update(tx, p, false) }, Err(_) => { - return player_create(tx, Player::new(account.id, Uuid::nil(), cryps), &account) + return player_create(tx, Player::new(account.id, Uuid::nil(), &account.name, cryps), &account) } } } diff --git a/server/src/vbox.rs b/server/src/vbox.rs index 6e6db1db..dc9921ef 100644 --- a/server/src/vbox.rs +++ b/server/src/vbox.rs @@ -17,7 +17,7 @@ use skill::{Skill}; use spec::{Spec}; use player::{Player, player_get, player_update}; -#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +#[derive(Debug,Copy,Clone,Serialize,Deserialize,PartialEq,PartialOrd,Ord,Eq)] pub enum Var { // colours Blue, @@ -81,18 +81,28 @@ enum VarEffect { } impl Var { - fn is_base(&self) -> bool { + fn cost(&self) -> u16 { match self { - Var::Attack | - Var::Block | - Var::Stun | - Var::Debuff | - Var::Buff => true, + Var::Red => 1, + Var::Green => 1, + Var::Blue => 1, - Var::Hp | - Var::Damage => true, + Var::Attack => 2, + Var::Block => 2, + Var::Buff => 2, + Var::Debuff => 2, + Var::Stun => 2, - _ => false, + Var::Damage => 3, + Var::Hp => 3, + Var::Speed => 3, + + _ => { + let combos = get_combos(); + let combo = combos.iter().find(|c| c.1 == *self) + .unwrap_or_else(|| panic!("unable to find components for {:?}", self)); + return combo.0.iter().fold(0, |acc, c| acc + c.cost()); + }, } } @@ -157,20 +167,67 @@ impl Var { } } -#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] -enum ColourCode { - RR, - GG, - BB, - RG, - BR, - GB, +fn get_combos() -> Vec<(Vec, Var)> { + let mut combinations = vec![ + (vec![Var::Attack, Var::Red, Var::Red] , Var::Strike ), + (vec![Var::Attack, Var::Green, Var::Green] , Var::Heal ), + (vec![Var::Attack, Var::Blue, Var::Blue] , Var::Blast ), + (vec![Var::Attack, Var::Red, Var::Green] , Var::Strike ), + (vec![Var::Attack, Var::Green, Var::Blue] , Var::Heal ), + (vec![Var::Attack, Var::Red, Var::Blue] , Var::Blast ), + + (vec![Var::Block, Var::Red, Var::Red] , Var::Parry ), + (vec![Var::Block, Var::Green, Var::Green] , Var::Reflect ), + (vec![Var::Block, Var::Blue, Var::Blue] , Var::Toxic ), + (vec![Var::Block, Var::Red, Var::Green] , Var::Taunt ), + (vec![Var::Block, Var::Green, Var::Blue] , Var::Shield ), + // (vec![Var::Block, Var::Red, Var::Blue] , Var::Blast), + + (vec![Var::Buff, Var::Red, Var::Red] , Var::Empower), + (vec![Var::Buff, Var::Green, Var::Green] , Var::Triage), + (vec![Var::Buff, Var::Blue, Var::Blue] , Var::Amplify), + (vec![Var::Buff, Var::Red, Var::Green] , Var::Clutch), + // (vec![Var::Buff, Var::Green, Var::Blue] , Var::Heal), + (vec![Var::Buff, Var::Red, Var::Blue] , Var::Haste), + + (vec![Var::Debuff, Var::Red, Var::Red] , Var::Snare), + (vec![Var::Debuff, Var::Green, Var::Green] , Var::Purge), + (vec![Var::Debuff, Var::Blue, Var::Blue] , Var::Curse), + // (vec![Var::Debuff, Var::Red, Var::Green] , Var::Siphon), + (vec![Var::Debuff, Var::Green, Var::Blue] , Var::Siphon), + (vec![Var::Debuff, Var::Red, Var::Blue] , Var::Slow), + + (vec![Var::Stun, Var::Red, Var::Red] , Var::Strangle), + (vec![Var::Stun, Var::Green, Var::Green] , Var::Throw), + (vec![Var::Stun, Var::Blue, Var::Blue] , Var::Ruin), + // (vec![Var::Stun, Var::Red, Var::Green] , Var::Strike), + (vec![Var::Stun, Var::Green, Var::Blue] , Var::Silence), + (vec![Var::Stun, Var::Red, Var::Blue] , Var::Hex), + + (vec![Var::Damage, Var::Red, Var::Red] , Var::RedDamageI), + (vec![Var::Damage, Var::Green, Var::Green] , Var::GreenDamageI), + (vec![Var::Damage, Var::Blue, Var::Blue] , Var::BlueDamageI), + // (vec![Var::Damage, Var::Red, Var::Green] , Var::Strike), + // (vec![Var::Damage, Var::Green, Var::Blue] , Var::Silence), + // (vec![Var::Damage, Var::Red, Var::Blue] , Var::Hex), + + (vec![Var::Hp, Var::Red, Var::Red] , Var::RedShieldI), + (vec![Var::Hp, Var::Green, Var::Green] , Var::LifeI), + (vec![Var::Hp, Var::Blue, Var::Blue] , Var::BlueShieldI), + (vec![Var::Hp, Var::Red, Var::Green] , Var::LRSI), + (vec![Var::Hp, Var::Green, Var::Blue] , Var::LBSI), + (vec![Var::Hp, Var::Red, Var::Blue] , Var::RBSI), + ]; + + combinations.iter_mut().for_each(|set| set.0.sort_unstable()); + + return combinations; } #[derive(Debug,Clone,Serialize,Deserialize)] pub struct Vbox { pub id: Uuid, - pub balance: u16, + pub bits: u16, pub free: Vec>, pub bound: Vec, pub instance: Uuid, @@ -197,7 +254,7 @@ impl Vbox { instance: instance_id, free: vec![], bound: starting_items, - balance: 9, + bits: 18, }; vbox.fill(); @@ -205,6 +262,21 @@ impl Vbox { 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), @@ -248,10 +320,16 @@ impl Vbox { 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) @@ -259,8 +337,10 @@ impl Vbox { pub fn drop(&mut self, i: usize) -> Result<&mut Vbox, Error> { self.bound.get(i).ok_or(format_err!("no var at index {:?}", i))?; - self.bound.remove(i); - // balance update + let dropped = self.bound.remove(i); + let refund = dropped.cost(); + println!("refunding {:?} for {:?}", refund, dropped); + self.balance_add(refund); Ok(self) } @@ -275,8 +355,8 @@ impl Vbox { // have to sort the indices and keep track of the iteration // because when removing the elements the array shifts - indices.sort(); - let mut vars = indices + indices.sort_unstable(); + let mut input = indices .iter() .enumerate() .map(|(i, index)| { @@ -284,107 +364,23 @@ impl Vbox { }) .collect::>(); - let base_index = vars - .iter() - .position(|v| v.is_base()) - .ok_or(err_msg("no base item selected"))?; + // 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.0 == input).ok_or(err_msg("not a combo"))?; - let base = vars.remove(base_index); - - // fold colours into RGB - let colours = vars - .iter() - .fold([0, 0, 0], |mut acc, c| { - match c { - Var::Red => acc[0] += 1, - Var::Green => acc[1] += 1, - Var::Blue => acc[2] += 1, - _ => (), - }; - acc - }); - - let colour_code = match colours { - [2,0,0] => ColourCode::RR, - [0,2,0] => ColourCode::GG, - [0,0,2] => ColourCode::BB, - [1,1,0] => ColourCode::RG, - [0,1,1] => ColourCode::GB, - [1,0,1] => ColourCode::BR, - _ => return Err(err_msg("not a combo")), - }; - - let new = match base { - Var::Attack => match colour_code { - ColourCode::RR => Var::Strike, - ColourCode::GG => Var::Heal, - ColourCode::BB => Var::Blast, - ColourCode::RG => Var::Slay, // - ColourCode::GB => return Err(err_msg("unhandled skill combo")), - ColourCode::BR => Var::Banish, // - }, - Var::Block => match colour_code { - ColourCode::RR => Var::Parry, - ColourCode::GG => Var::Reflect, - ColourCode::BB => Var::Toxic, - ColourCode::RG => Var::Taunt, - ColourCode::GB => Var::Shield, - ColourCode::BR => return Err(err_msg("unhandled skill combo")), - }, - Var::Buff => match colour_code { - ColourCode::RR => Var::Empower, - ColourCode::GG => Var::Triage, - ColourCode::BB => Var::Amplify, - ColourCode::RG => Var::Clutch, - ColourCode::GB => return Err(err_msg("unhandled skill combo")), - ColourCode::BR => Var::Haste, - }, - Var::Debuff => match colour_code { - ColourCode::RR => Var::Snare, - ColourCode::GG => Var::Purge, - ColourCode::BB => Var::Curse, - ColourCode::RG => return Err(err_msg("unhandled skill combo")), - ColourCode::GB => Var::Siphon, - ColourCode::BR => Var::Slow, - }, - Var::Stun => match colour_code { - ColourCode::RR => Var::Strangle, - ColourCode::GG => Var::Throw, - ColourCode::BB => Var::Ruin, - ColourCode::RG => return Err(err_msg("unhandled skill combo")), - ColourCode::GB => Var::Silence, - ColourCode::BR => Var::Hex, - }, - - // SPECS - Var::Damage => match colour_code { - ColourCode::RR => Var::RedDamageI, - ColourCode::GG => Var::GreenDamageI, - ColourCode::BB => Var::BlueDamageI, - ColourCode::RG => return Err(err_msg("unhandled skill combo")), - ColourCode::GB => return Err(err_msg("unhandled skill combo")), - ColourCode::BR => return Err(err_msg("unhandled skill combo")), - }, - Var::Hp => match colour_code { - ColourCode::RR => Var::RedShieldI, - ColourCode::GG => Var::LifeI, - ColourCode::BB => Var::BlueShieldI, - ColourCode::RG => Var::LRSI, - ColourCode::GB => Var::LBSI, - ColourCode::BR => Var::RBSI, - }, - - _ => panic!("wrong base {:?}", base), - }; - - self.bound.push(new); + self.bound.push(combo.1); Ok(self) } } +const DISCARD_COST: u16 = 5; + pub fn vbox_discard(params: VboxDiscardParams, tx: &mut Transaction, account: &Account) -> Result { 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); } @@ -447,4 +443,25 @@ mod tests { 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.0 == input); + assert!(combo.is_some()); + } + + #[test] + fn drop_test() { + let mut vbox = Vbox::new(Uuid::new_v4(), Uuid::new_v4()); + vbox.bound = vec![Var::Strike]; + vbox.drop(0).unwrap(); + assert_eq!(vbox.bits, 22); + } + } \ No newline at end of file