diff --git a/client/assets/icons/colour.lines.svg b/client/assets/icons/colour.lines.svg
new file mode 100644
index 00000000..99cb59ad
--- /dev/null
+++ b/client/assets/icons/colour.lines.svg
@@ -0,0 +1,15 @@
+
+
+
+
diff --git a/client/assets/icons/cross.svg b/client/assets/icons/cross.svg
new file mode 100644
index 00000000..cceb5eb6
--- /dev/null
+++ b/client/assets/icons/cross.svg
@@ -0,0 +1,13 @@
+
+
+
+
diff --git a/client/assets/icons/hourglass.lines.svg b/client/assets/icons/hourglass.lines.svg
new file mode 100644
index 00000000..dfeaea61
--- /dev/null
+++ b/client/assets/icons/hourglass.lines.svg
@@ -0,0 +1,18 @@
+
+
+
+
diff --git a/client/cryps.css b/client/cryps.css
index e6cd22ed..f0033438 100644
--- a/client/cryps.css
+++ b/client/cryps.css
@@ -412,8 +412,8 @@ header {
}
.vbox-table td svg {
- stroke-width: 1.5px;
- height: 95%;
+ stroke-width: 2px;
+ height: 96%;
vertical-align: text-bottom;
}
diff --git a/client/src/components/constants.jsx b/client/src/components/constants.jsx
index 1d5efddd..23f51b3f 100644
--- a/client/src/components/constants.jsx
+++ b/client/src/components/constants.jsx
@@ -229,7 +229,7 @@ module.exports = {
upgrades: 'combine with 2 red / blue / green',
},
- LifeI: {
+ GreenLifeI: {
description: 'Increases life',
colours: ['green'],
thresholds: [5, 10, 20],
diff --git a/client/src/components/svgs/colour.jsx b/client/src/components/svgs/colour.jsx
index 02114b34..b8130546 100644
--- a/client/src/components/svgs/colour.jsx
+++ b/client/src/components/svgs/colour.jsx
@@ -1,31 +1,31 @@
const preact = require('preact');
-module.exports = function triangle(classes) {
+module.exports = function vboxColour(classes) {
return (
);
-}
\ No newline at end of file
+};
diff --git a/client/src/utils.jsx b/client/src/utils.jsx
index 2a5a6535..4d8cff06 100644
--- a/client/src/utils.jsx
+++ b/client/src/utils.jsx
@@ -92,9 +92,9 @@ const SPECS = {
};
const COLOUR_ICONS = {
- red: { colour: 'red', caption: 'red', svg: shapes.hexagon },
- blue: { colour: 'blue', caption: 'blue', svg: shapes.hexagon },
- green: { colour: 'green', caption: 'green', svg: shapes.hexagon },
+ red: { colour: 'red', caption: 'red', svg: shapes.square },
+ blue: { colour: 'blue', caption: 'blue', svg: shapes.square },
+ green: { colour: 'green', caption: 'green', svg: shapes.square },
};
module.exports = {
diff --git a/server/src/game.rs b/server/src/game.rs
index a9480710..44fa0673 100644
--- a/server/src/game.rs
+++ b/server/src/game.rs
@@ -270,7 +270,13 @@ impl Game {
}
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");
+ match self.add_skill(team_id, mob_id, target_id, s) {
+ Ok(_) => (),
+ Err(e) => {
+ println!("{:?}", self.cryp_by_id(mob_id));
+ panic!("{:?} unable to add pve mob skill {:?}", e, s);
+ },
+ }
}
self
diff --git a/server/src/instance.rs b/server/src/instance.rs
index 4cf3ddfc..c6a2bbec 100644
--- a/server/src/instance.rs
+++ b/server/src/instance.rs
@@ -59,7 +59,9 @@ 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, &bot_id.to_string(), cryps).set_bot(true)
+ let mut p = Player::new(bot_id, self.id, &bot_id.to_string(), cryps).set_bot(true);
+ p.autobuy();
+ p
})
.take(15)
.collect::>();
@@ -199,16 +201,7 @@ impl Instance {
fn bot_vbox_phase(&mut self) -> &mut Instance {
for bot in self.players.iter_mut().filter(|p| p.bot) {
bot.vbox.fill();
-
- // paths for what to do
- // use a spec
- // craft a spec
- // craft a skill
- // check for cryps w/ no skills
- // create a skill()
- // find base
- // pick 2 random colours
-
+ bot.autobuy();
bot.set_ready(true);
}
@@ -585,6 +578,7 @@ mod tests {
let player_account = Uuid::new_v4();
let cryps = instance_mobs(player_account);
let mut player = Player::new(player_account, instance.id, &"test".to_string(), cryps).set_bot(true);
+ player.autobuy();
instance.add_player(player.clone());
assert!(instance.can_start());
@@ -605,4 +599,13 @@ mod tests {
assert_eq!(instance.rounds.len(), 3);
}
+
+ #[test]
+ fn instance_bot_vbox_test() {
+ let mut instance = Instance::new();
+ let player_account = Uuid::new_v4();
+ let cryps = instance_mobs(player_account);
+ let mut player = Player::new(player_account, instance.id, &"test".to_string(), cryps).set_bot(true);
+ }
+
}
\ No newline at end of file
diff --git a/server/src/mob.rs b/server/src/mob.rs
index 96823ecb..95a63ac4 100644
--- a/server/src/mob.rs
+++ b/server/src/mob.rs
@@ -33,8 +33,8 @@ pub fn generate_mob() -> Cryp {
pub fn instance_mobs(team_id: Uuid) -> Vec {
iter::repeat_with(||
generate_mob()
- .set_account(team_id)
- .learn(Skill::Attack))
+ .set_account(team_id))
+ // .learn(Skill::Attack))
.take(3)
.collect::>()
}
diff --git a/server/src/player.rs b/server/src/player.rs
index 73360727..d0e3f4f2 100644
--- a/server/src/player.rs
+++ b/server/src/player.rs
@@ -1,4 +1,5 @@
use uuid::Uuid;
+use rand::prelude::*;
use serde_cbor::{from_slice, to_vec};
@@ -8,11 +9,13 @@ use failure::Error;
use failure::err_msg;
use account::Account;
-use cryp::{Cryp, cryp_get};
-use vbox::{Vbox};
+use cryp::{Cryp, Colours, cryp_get};
+use vbox::{Vbox, Var, VarEffect};
use rpc::{PlayerStateParams, PlayerCrypsSetParams};
use instance::{instance_get, instance_update};
+const DISCARD_COST: u16 = 5;
+
#[derive(Debug,Clone,Copy,Serialize,Deserialize)]
pub struct Score {
pub wins: u8,
@@ -74,6 +77,193 @@ impl Player {
pub fn cryp_get(&mut self, id: Uuid) -> Result<&mut Cryp, Error> {
self.cryps.iter_mut().find(|c| c.id == id).ok_or(err_msg("cryp not found"))
}
+
+ pub fn autobuy(&mut self) -> &mut Player {
+ let mut rng = thread_rng();
+
+ // first check if any cryps have no skills
+ // if there is one find an item in vbox that gives a skill
+ while let Some(c) = self.cryps.iter().position(|c| c.skills.len() == 0) {
+ if let Some(s) = self.vbox.bound.iter().position(|v| v.into_skill().is_some()) {
+ let cryp_id = self.cryps[c].id;
+ self.vbox_apply(s, cryp_id).expect("could not apply");
+ continue;
+ }
+ println!("no skills available...");
+ }
+
+ // now keep buying and applying items cause whynot
+ // inb4 montecarlo gan
+
+ loop {
+ let (target_cryp_i, target_cryp_id) = match self.cryps.iter().any(|c| c.skills.len() < 3) {
+ true => {
+ let mut target_cryp_i = 0;
+ for (j, c) in self.cryps.iter().enumerate() {
+ if c.skills.len() < self.cryps[target_cryp_i].skills.len() {
+ target_cryp_i = j;
+ }
+ }
+ (target_cryp_i, self.cryps[target_cryp_i].id)
+ },
+ false => {
+ let i = rng.gen_range(0, 3);
+ (i, self.cryps[i].id)
+ },
+ };
+
+ let needs_skills = self.cryps[target_cryp_i].skills.len() < 3;
+ let group_i = match needs_skills {
+ true => 1,
+ false => 2,
+ };
+
+ if self.vbox.bound.len() < 3 {
+ if (needs_skills && self.vbox.bits < 4) || self.vbox.bits < 5 {
+ // println!("insufficient balance");
+ return self;
+ }
+
+ // get 2 colours and something else
+ if self.vbox.free[0].len() < 2 {
+ return self;
+ }
+ self.vbox_accept(0, 0).expect("could't accept colour 0");
+ self.vbox_accept(0, 0).expect("could't accept colour 1");
+ self.vbox_accept(group_i, 0).expect("could't accept group 0");
+ }
+
+ // println!("{:?}", self.vbox.bound);
+
+ let skills = [Var::Attack, Var::Block, Var::Buff, Var::Debuff, Var::Stun];
+ let combo_i = match group_i {
+ 1 => self.vbox.bound.iter().position(|v| skills.contains(v)).expect("no skill found"),
+ 2 => self.vbox.bound.iter().position(|v| v.into_spec().is_some()).expect("no spec found"),
+ _ => panic!("unknown group_i"),
+ };
+
+ // first 2 colours can be whatever
+ match self.vbox_combine(vec![0, 1, combo_i]) {
+ Ok(_) => {
+ // println!("refined {:?}", self.vbox.bound[self.vbox.bound.len() - 1]);
+ }
+ Err(e) => println!("{:?}", e),
+ };
+
+ let var_i = self.vbox.bound.len() - 1;
+ match self.vbox_apply(var_i, target_cryp_id) {
+ Ok(_) => {
+ // println!("{:?} improved", self.cryps[target_cryp_i].name);
+ },
+ Err(e) => println!("{:?}", e),
+ }
+ }
+ }
+
+ pub fn vbox_discard(&mut self) -> Result<&mut Player, Error> {
+ self.vbox.balance_sub(DISCARD_COST)?;
+ self.vbox.fill();
+ Ok(self)
+ }
+
+ pub fn vbox_accept(&mut self, group: usize, index: usize) -> Result<&mut Player, Error> {
+ self.vbox.accept(group, index)?;
+ Ok(self)
+ }
+
+ pub fn vbox_combine(&mut self, indices: Vec) -> Result<&mut Player, Error> {
+ self.vbox.combine(indices)?;
+ Ok(self)
+ }
+
+ pub fn vbox_reclaim(&mut self, index: usize) -> Result<&mut Player, Error> {
+ self.vbox.reclaim(index)?;
+ Ok(self)
+ }
+
+ pub fn vbox_apply(&mut self, index: usize, cryp_id: Uuid) -> Result<&mut Player, Error> {
+ let var = self.vbox.bound.remove(index);
+
+ match var.effect() {
+ Some(VarEffect::Skill) => {
+ let skill = var.into_skill().ok_or(format_err!("var {:?} has no associated skill", var))?;
+ let cryp = self.cryp_get(cryp_id)?;
+ // done here because i teach them a tonne of skills for tests
+ let max_skills = 3;
+ if cryp.skills.len() >= max_skills {
+ return Err(format_err!("cryp at max skills ({:?})", max_skills));
+ }
+
+ if cryp.knows(skill) {
+ return Err(format_err!("cryp already knows skill ({:?})" , skill));
+ }
+
+ cryp.learn_mut(skill);
+ },
+ Some(VarEffect::Spec) => {
+ let spec = var.into_spec().ok_or(format_err!("var {:?} has no associated spec", var))?;
+ let cryp = self.cryp_get(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 = self.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 self.cryps.iter_mut() {
+ cryp.apply_modifiers(&team_colours);
+ }
+
+ Ok(self)
+ }
+
+ pub fn vbox_unequip(&mut self, target: Var, cryp_id: Uuid) -> Result<&mut Player, Error> {
+ if self.vbox.bound.len() >= 9 {
+ return Err(err_msg("too many vars bound"));
+ }
+
+ match target.effect() {
+ Some(VarEffect::Skill) => {
+ let skill = target.into_skill().ok_or(format_err!("var {:?} has no associated skill", target))?;
+ let cryp = self.cryp_get(cryp_id)?;
+ cryp.forget(skill)?;
+ },
+ Some(VarEffect::Spec) => {
+ let spec = target.into_spec().ok_or(format_err!("var {:?} has no associated spec", target))?;
+ let cryp = self.cryp_get(cryp_id)?;
+ cryp.spec_remove(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 = self.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 self.cryps.iter_mut() {
+ cryp.apply_modifiers(&team_colours);
+ }
+
+ self.vbox.bound.push(target);
+ self.vbox.bound.sort_unstable();
+
+ Ok(self)
+ }
}
pub fn player_get(tx: &mut Transaction, account_id: Uuid, instance_id: Uuid) -> Result {
@@ -191,3 +381,19 @@ pub fn player_mm_cryps_set(params: PlayerCrypsSetParams, tx: &mut Transaction, a
}
}
}
+
+#[cfg(test)]
+mod tests {
+ use mob::instance_mobs;
+ use super::*;
+
+ #[test]
+ fn player_bot_vbox_test() {
+ let player_account = Uuid::new_v4();
+ let cryps = instance_mobs(player_account);
+ let mut player = Player::new(player_account, Uuid::new_v4(), &"test".to_string(), cryps).set_bot(true);
+
+ player.autobuy();
+ println!("{:#?}", player);
+ }
+}
\ No newline at end of file
diff --git a/server/src/skill.rs b/server/src/skill.rs
index e5745a9d..160446ce 100644
--- a/server/src/skill.rs
+++ b/server/src/skill.rs
@@ -1075,23 +1075,23 @@ fn silence(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Re
}
fn purge(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
- for (i, ce) in target.effects.clone().iter_mut().enumerate() {
- if [Category::BlueBuff, Category::RedBuff].contains(&ce.effect.category()) {
- target.effects.remove(i);
+ while let Some(i) = target.effects
+ .iter()
+ .position(|ce| [Category::BlueBuff, Category::RedBuff].contains(&ce.effect.category())) {
+ let ce = target.effects.remove(i);
results.push(Resolution::new(source, target).event(Event::Removal { effect: ce.effect }));
}
- }
return results;
}
fn purify(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
- for (i, ce) in target.effects.clone().iter_mut().enumerate() {
- if [Category::BlueDebuff, Category::RedDebuff].contains(&ce.effect.category()) {
- target.effects.remove(i);
+ while let Some(i) = target.effects
+ .iter()
+ .position(|ce| [Category::BlueDebuff, Category::RedDebuff].contains(&ce.effect.category())) {
+ let ce = target.effects.remove(i);
results.push(Resolution::new(source, target).event(Event::Removal { effect: ce.effect }));
}
- }
return results;
}
diff --git a/server/src/vbox.rs b/server/src/vbox.rs
index de93bc17..2605dafd 100644
--- a/server/src/vbox.rs
+++ b/server/src/vbox.rs
@@ -101,7 +101,7 @@ pub enum Var {
TestSiphon,
}
-enum VarEffect {
+pub enum VarEffect {
Skill,
Spec,
}
@@ -167,7 +167,7 @@ impl Var {
}
}
- fn effect(&self) -> Option {
+ pub fn effect(&self) -> Option {
if let Some(_skill) = self.into_skill() {
return Some(VarEffect::Skill);
}
@@ -177,7 +177,7 @@ impl Var {
return None;
}
- fn into_skill(&self) -> Option {
+ pub fn into_skill(&self) -> Option {
match self {
Var::Attack => Some(Skill::Attack),
Var::Amplify => Some(Skill::Amplify),
@@ -215,7 +215,7 @@ impl Var {
}
}
- fn into_spec(&self) -> Option {
+ pub fn into_spec(&self) -> Option {
match *self {
Var::Speed => Some(Spec::Speed),
Var::RedSpeedI => Some(Spec::RedSpeedI),
@@ -255,6 +255,7 @@ impl From for Var {
Skill::Blast => Var::Blast,
Skill::Block => Var::Block,
Skill::Curse => Var::Curse,
+ Skill::Clutch => Var::Clutch,
Skill::Decay => Var::Decay,
Skill::Empower => Var::Empower,
Skill::Haste => Var::Haste,
@@ -363,7 +364,7 @@ fn get_combos() -> Vec {
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::Attack, Var::Red, Var::Blue], var: Var::Blast }, // AAAAAAAAAAAAAAAAAA
Combo { units: vec![Var::Damage, Var::Red, Var::Red], var: Var::RedDamageI },
Combo { units: vec![Var::Damage, Var::Green, Var::Green], var: Var::GreenDamageI },
@@ -495,6 +496,7 @@ impl Vbox {
// actually move
self.bound.push(self.free[i].remove(j));
+ self.bound.sort_unstable();
Ok(self)
}
@@ -535,118 +537,45 @@ impl Vbox {
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)
}
}
-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();
+ player.vbox_discard()?;
return player_update(tx, player, false);
}
pub fn vbox_accept(params: VboxAcceptParams, tx: &mut Transaction, account: &Account) -> Result {
let mut player = player_get(tx, account.id, params.instance_id)?;
- player.vbox.accept(params.group, params.index)?;
+ 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 {
let mut player = player_get(tx, account.id, params.instance_id)?;
- player.vbox.combine(params.indices)?;
+ player.vbox_combine(params.indices)?;
return player_update(tx, player, false);
}
pub fn vbox_reclaim(params: VboxReclaimParams, tx: &mut Transaction, account: &Account) -> Result {
let mut player = player_get(tx, account.id, params.instance_id)?;
- player.vbox.reclaim(params.index)?;
+ player.vbox_reclaim(params.index)?;
return player_update(tx, player, false);
}
pub fn vbox_apply(params: VboxApplyParams, tx: &mut Transaction, account: &Account) -> Result {
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 = 3;
- 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);
- }
-
+ player.vbox_apply(params.index, params.cryp_id)?;
return player_update(tx, player, false);
}
pub fn vbox_unequip(params: VboxUnequipParams, tx: &mut Transaction, account: &Account) -> Result {
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")),
- }
-
- // 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);
- }
-
- player.vbox.bound.push(params.target);
+ player.vbox_unequip(params.target, params.cryp_id)?;
return player_update(tx, player, false);
}