v{VERSION}
-
use the buttons on the right to join an instance.
+
Use the buttons on the right to join an instance.
- select PVP to play against other players.
- click LEARN to practice the game without time controls.
+ Select PVP to play against other players.
+ Select INVITE then click COPY LINK to generate an instance invitation for a friend.
+ Click LEARN to practice the game without time controls.
- if you enjoy the game please support its development by subscribing or purchasing credits.
+ If you enjoy the game please support its development by subscribing or purchasing credits.
glhf
--ntr & mashy
diff --git a/client/src/events.jsx b/client/src/events.jsx
index 3e0943fd..184f4ad7 100644
--- a/client/src/events.jsx
+++ b/client/src/events.jsx
@@ -1,3 +1,5 @@
+const querystring = require('query-string');
+
const eachSeries = require('async/eachSeries');
const sample = require('lodash/sample');
@@ -168,9 +170,20 @@ function registerEvents(store) {
return store.dispatch(actions.setInstances(v));
}
+ function setInvite(code) {
+ if (!code) return store.dispatch(actions.setInvite(null));
+
+ navigator.clipboard.writeText(code).then(() => {
+ notify(`your invite code ${code} was copied to the clipboard.`);
+ }, () => {});
+
+ return store.dispatch(actions.setInvite(code));
+ }
+
function setInstance(v) {
const { account, instance, ws } = store.getState();
if (v) {
+ setInvite(null);
const player = v.players.find(p => p.id === account.id);
store.dispatch(actions.setPlayer(player));
@@ -272,6 +285,16 @@ function registerEvents(store) {
} */
// setup / localstorage
+ function urlHashChange() {
+ const { ws } = store.getState();
+ const cmds = querystring.parse(location.hash);
+
+ if (cmds.join) ws.sendInstanceJoin(cmds.join);
+ return true;
+ }
+
+ window.addEventListener('hashchange', urlHashChange, false);
+
return {
clearCombiner,
clearConstructRename,
@@ -289,12 +312,15 @@ function registerEvents(store) {
setEmail,
setInstance,
setItemInfo,
+ setInvite,
setPing,
setShop,
setTeam,
setSubscription,
setWs,
+ urlHashChange,
+
notify,
};
}
diff --git a/client/src/reducers.jsx b/client/src/reducers.jsx
index cfc6dad5..349d6e91 100644
--- a/client/src/reducers.jsx
+++ b/client/src/reducers.jsx
@@ -31,6 +31,7 @@ module.exports = {
constructRename: createReducer(null, 'SET_CONSTRUCT_RENAME'),
game: createReducer(null, 'SET_GAME'),
email: createReducer(null, 'SET_EMAIL'),
+ invite: createReducer(null, 'SET_INVITE'),
info: createReducer(null, 'SET_INFO'),
instance: createReducer(null, 'SET_INSTANCE'),
instances: createReducer([], 'SET_INSTANCES'),
diff --git a/client/src/socket.jsx b/client/src/socket.jsx
index 74140922..1c6f6f37 100644
--- a/client/src/socket.jsx
+++ b/client/src/socket.jsx
@@ -126,6 +126,14 @@ function createSocket(events) {
send(['InstanceQueue', {}]);
}
+ function sendInstanceInvite() {
+ send(['InstanceInvite', {}]);
+ }
+
+ function sendInstanceJoin(code) {
+ send(['InstanceJoin', { code }]);
+ }
+
function sendInstanceReady(instanceId) {
send(['InstanceReady', { instance_id: instanceId }]);
}
@@ -239,6 +247,9 @@ function createSocket(events) {
QueueRequested: () => events.notify('pvp queue request received'),
QueueJoined: () => events.notify('you have joined the pvp queue'),
+ InviteRequested: () => events.notify('pvp queue request received'),
+ Invite: code => events.setInvite(code),
+ Joining: () => events.notify('searching for instance...'),
Error: errHandler,
};
@@ -284,6 +295,8 @@ function createSocket(events) {
sendPing();
sendItemInfo();
+ events.urlHashChange();
+
return true;
}
@@ -318,6 +331,7 @@ function createSocket(events) {
ws.addEventListener('message', onMessage);
ws.addEventListener('error', onError);
ws.addEventListener('close', onClose);
+
return ws;
}
@@ -338,6 +352,8 @@ function createSocket(events) {
sendInstancePractice,
sendInstanceQueue,
sendInstanceState,
+ sendInstanceInvite,
+ sendInstanceJoin,
sendVboxAccept,
sendVboxApply,
diff --git a/ops/package.json b/ops/package.json
index 642d45e1..5314c598 100755
--- a/ops/package.json
+++ b/ops/package.json
@@ -1,6 +1,6 @@
{
"name": "mnml-ops",
- "version": "1.4.4",
+ "version": "1.4.5",
"description": "",
"main": "index.js",
"scripts": {
diff --git a/server/Cargo.toml b/server/Cargo.toml
index 67096284..d3970fb5 100644
--- a/server/Cargo.toml
+++ b/server/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "mnml"
-version = "1.4.4"
+version = "1.4.5"
authors = ["ntr
"]
[dependencies]
diff --git a/server/src/construct.rs b/server/src/construct.rs
index 2d0e4fe8..80b4d7bb 100644
--- a/server/src/construct.rs
+++ b/server/src/construct.rs
@@ -46,7 +46,6 @@ impl Colours {
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
pub struct ConstructSkill {
pub skill: Skill,
- pub self_targeting: bool,
pub cd: Cooldown,
// used for Uon client
pub disabled: bool,
@@ -56,7 +55,6 @@ impl ConstructSkill {
pub fn new(skill: Skill) -> ConstructSkill {
ConstructSkill {
skill,
- self_targeting: skill.self_targeting(),
cd: skill.base_cd(),
disabled: false,
}
diff --git a/server/src/events.rs b/server/src/events.rs
index da9fe692..ad3d898f 100644
--- a/server/src/events.rs
+++ b/server/src/events.rs
@@ -12,6 +12,7 @@ use account;
use account::Account;
use game;
use instance;
+use names;
use pg::{Db, PgPool};
use rpc::RpcMessage;
@@ -54,6 +55,9 @@ pub enum Event {
// client events
Queue(Id),
+ Invite(Id),
+ Join(Id, String),
+ Joined(Id),
}
struct WsClient {
@@ -62,6 +66,7 @@ struct WsClient {
tx: Sender,
subs: HashSet,
pvp: bool,
+ invite: Option,
}
impl Events {
@@ -115,7 +120,7 @@ impl Events {
None => None,
};
- let client = WsClient { id, tx, account: account_id, subs: HashSet::new(), pvp: false };
+ let client = WsClient { id, tx, account: account_id, subs: HashSet::new(), pvp: false, invite: None };
self.clients.insert(id, client);
info!("clients={:?}", self.clients.len());
@@ -223,6 +228,59 @@ impl Events {
info!("joined game queue id={:?} account={:?}", requester.id, requester.account);
return Ok(());
},
+
+ Event::Invite(id) => {
+ // check whether request is valid
+ let c = self.clients.get_mut(&id)
+ .ok_or(format_err!("connection not found id={:?}", id))?;
+
+ if let None = c.account {
+ return Err(err_msg("cannot join pvp queue anonymously"));
+ }
+
+ let code = names::name().split_whitespace().collect::>().join("-");
+ info!("pvp invite request id={:?} account={:?} code={:?}", c.id, c.account, code);
+ c.invite = Some(code.clone());
+ c.tx.send(RpcMessage::Invite(code))?;
+ return Ok(());
+ },
+
+ Event::Join(id, code) => {
+ // check whether request is valid
+ let c = self.clients.get(&id)
+ .ok_or(format_err!("connection not found id={:?}", id))?;
+
+ if let None = c.account {
+ return Err(err_msg("cannot join pvp queue anonymously"));
+ }
+
+ info!("pvp join request id={:?} account={:?} code={:?}", c.id, c.account, code);
+
+ let inv = self.clients.iter()
+ .filter(|(_id, c)| c.invite.is_some())
+ .find(|(_id, c)| match c.invite {
+ Some(ref c) => *c == code,
+ None => false,
+ })
+ .map(|(_id, c)| PvpRequest { id: c.id, account: c.account.unwrap(), tx: c.tx.clone() })
+ .ok_or(format_err!("invite not found code={:?}", code))?;
+
+ let join = PvpRequest { id: c.id, account: c.account.unwrap(), tx: c.tx.clone() };
+
+ self.warden.send(GameEvent::Match((join, inv)))?;
+ return Ok(());
+ },
+
+ Event::Joined(id) => {
+ // check whether request is valid
+ let c = self.clients.get_mut(&id)
+ .ok_or(format_err!("connection not found id={:?}", id))?;
+
+ c.pvp = false;
+ c.invite = None;
+ return Ok(());
+ },
+
}
}
}
diff --git a/server/src/game.rs b/server/src/game.rs
index caeb1e61..97354d86 100644
--- a/server/src/game.rs
+++ b/server/src/game.rs
@@ -236,7 +236,7 @@ impl Game {
target = find_target();
}
- pve_skills.push((mobs.id, mob.id, Some(target.id), s));
+ pve_skills.push((mobs.id, mob.id, target.id, s));
},
None => continue,
};
@@ -258,7 +258,7 @@ impl Game {
self
}
- fn add_skill(&mut self, player_id: Uuid, source_construct_id: Uuid, target_construct_id: Option, skill: Skill) -> Result<&mut Game, Error> {
+ fn add_skill(&mut self, player_id: Uuid, source_construct_id: Uuid, target_construct_id: Uuid, skill: Skill) -> Result<&mut Game, Error> {
// check player in game
self.player_by_id(player_id)?;
@@ -266,17 +266,9 @@ impl Game {
return Err(err_msg("game not in skill phase"));
}
- let final_target_id = match skill.self_targeting() {
- true => source_construct_id,
- false => match target_construct_id {
- Some(t) => t,
- None => return Err(err_msg("skill requires a target")),
- }
- };
-
// target checks
{
- let target = match self.construct_by_id(final_target_id) {
+ let target = match self.construct_by_id(target_construct_id) {
Some(c) => c,
None => return Err(err_msg("target construct not in game")),
};
@@ -318,7 +310,7 @@ impl Game {
self.stack.remove(s);
}
- let skill = Cast::new(source_construct_id, player_id, final_target_id, skill);
+ let skill = Cast::new(source_construct_id, player_id, target_construct_id, skill);
self.stack.push(skill);
return Ok(self);
@@ -887,7 +879,7 @@ fn game_json_file_write(g: &Game) -> Result {
Ok(dest)
}
-pub fn game_skill(tx: &mut Transaction, account: &Account, game_id: Uuid, construct_id: Uuid, target_construct_id: Option, skill: Skill) -> Result {
+pub fn game_skill(tx: &mut Transaction, account: &Account, game_id: Uuid, construct_id: Uuid, target_construct_id: Uuid, skill: Skill) -> Result {
let mut game = game_get(tx, game_id)?;
game.add_skill(account.id, construct_id, target_construct_id, skill)?;
@@ -1039,8 +1031,8 @@ mod tests {
let x_construct = x_player.constructs[0].clone();
let y_construct = y_player.constructs[0].clone();
- game.add_skill(x_player.id, x_construct.id, Some(y_construct.id), Skill::Attack).unwrap();
- game.add_skill(y_player.id, y_construct.id, Some(x_construct.id), Skill::Attack).unwrap();
+ game.add_skill(x_player.id, x_construct.id, y_construct.id, Skill::Attack).unwrap();
+ game.add_skill(y_player.id, y_construct.id, x_construct.id, Skill::Attack).unwrap();
game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap();
@@ -1068,8 +1060,8 @@ mod tests {
game.construct_by_id(x_construct.id).unwrap().reduce_cooldowns();
}
- game.add_skill(x_player.id, x_construct.id, Some(y_construct.id), Skill::Stun).unwrap();
- game.add_skill(y_player.id, y_construct.id, Some(x_construct.id), Skill::Attack).unwrap();
+ game.add_skill(x_player.id, x_construct.id, y_construct.id, Skill::Stun).unwrap();
+ game.add_skill(y_player.id, y_construct.id, x_construct.id, Skill::Attack).unwrap();
game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap();
@@ -1105,8 +1097,8 @@ mod tests {
// remove all mitigation
game.player_by_id(x_player.id).unwrap().construct_by_id(x_construct.id).unwrap().red_life.force(0);
- game.add_skill(x_player.id, x_construct.id, Some(y_construct.id), Skill::Stun).unwrap();
- game.add_skill(y_player.id, y_construct.id, Some(x_construct.id), Skill::Attack).unwrap();
+ game.add_skill(x_player.id, x_construct.id, y_construct.id, Skill::Stun).unwrap();
+ game.add_skill(y_player.id, y_construct.id, x_construct.id, Skill::Attack).unwrap();
game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap();
@@ -1135,8 +1127,8 @@ mod tests {
assert!(game.player_by_id(y_player.id).unwrap().constructs[0].skill_on_cd(Skill::Stun).is_some());
assert!(game.player_by_id(x_player.id).unwrap().constructs[0].skill_on_cd(Skill::Block).is_none());
- game.add_skill(x_player.id, x_construct.id, Some(y_construct.id), Skill::Attack).unwrap();
- game.add_skill(y_player.id, y_construct.id, Some(x_construct.id), Skill::Attack).unwrap();
+ game.add_skill(x_player.id, x_construct.id, y_construct.id, Skill::Attack).unwrap();
+ game.add_skill(y_player.id, y_construct.id, x_construct.id, Skill::Attack).unwrap();
game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap();
@@ -1149,8 +1141,8 @@ mod tests {
// second round
// now we block and it should go back on cd
- // game.add_skill(x_player.id, x_construct.id, Some(y_construct.id), Skill::Stun).unwrap();
- game.add_skill(y_player.id, y_construct.id, Some(x_construct.id), Skill::Attack).unwrap();
+ // game.add_skill(x_player.id, x_construct.id, y_construct.id, Skill::Stun).unwrap();
+ game.add_skill(y_player.id, y_construct.id, x_construct.id, Skill::Attack).unwrap();
game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap();
@@ -1179,8 +1171,8 @@ mod tests {
game.construct_by_id(x_construct.id).unwrap().reduce_cooldowns();
}
- game.add_skill(x_player.id, x_construct.id, None, Skill::Counter).unwrap();
- game.add_skill(y_player.id, y_construct.id, Some(x_construct.id), Skill::Stun).unwrap();
+ game.add_skill(x_player.id, x_construct.id, x_construct.id, Skill::Counter).unwrap();
+ game.add_skill(y_player.id, y_construct.id, x_construct.id, Skill::Stun).unwrap();
game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap();
@@ -1214,14 +1206,14 @@ mod tests {
}
// apply buff
- game.add_skill(x_player.id, x_construct.id, Some(x_construct.id), Skill::Electrify).unwrap();
+ game.add_skill(x_player.id, x_construct.id, x_construct.id, Skill::Electrify).unwrap();
game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap();
game = game.resolve_phase_start();
assert!(game.construct_by_id(x_construct.id).unwrap().affected(Effect::Electric));
// attack and receive debuff
- game.add_skill(y_player.id, y_construct.id, Some(x_construct.id), Skill::Attack).unwrap();
+ game.add_skill(y_player.id, y_construct.id, x_construct.id, Skill::Attack).unwrap();
game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap();
game = game.resolve_phase_start();
@@ -1246,7 +1238,7 @@ mod tests {
}
// apply buff
- game.add_skill(x_player.id, x_construct.id, Some(y_construct.id), Skill::Link).unwrap();
+ game.add_skill(x_player.id, x_construct.id, y_construct.id, Skill::Link).unwrap();
game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap();
game = game.resolve_phase_start();
@@ -1265,7 +1257,7 @@ mod tests {
}
// attack and receive link hit
- game.add_skill(y_player.id, y_construct.id, Some(x_construct.id), Skill::Attack).unwrap();
+ game.add_skill(y_player.id, y_construct.id, x_construct.id, Skill::Attack).unwrap();
game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap();
game = game.resolve_phase_start();
@@ -1296,14 +1288,14 @@ mod tests {
// }
// // apply buff
- // game.add_skill(x_player.id, x_construct.id, Some(x_construct.id), Skill::Absorb).unwrap();
+ // game.add_skill(x_player.id, x_construct.id, x_construct.id, Skill::Absorb).unwrap();
// game.player_ready(x_player.id).unwrap();
// game.player_ready(y_player.id).unwrap();
// game = game.resolve_phase_start();
// assert!(game.construct_by_id(x_construct.id).unwrap().affected(Effect::Absorb));
// // attack and receive debuff
- // game.add_skill(y_player.id, y_construct.id, Some(x_construct.id), Skill::TestAttack).unwrap();
+ // game.add_skill(y_player.id, y_construct.id, x_construct.id, Skill::TestAttack).unwrap();
// game.player_ready(x_player.id).unwrap();
// game.player_ready(y_player.id).unwrap();
// game = game.resolve_phase_start();
@@ -1330,10 +1322,10 @@ mod tests {
game.construct_by_id(x_construct.id).unwrap().reduce_cooldowns();
}
- game.add_skill(i_player.id, i_construct.id, Some(x_construct.id), Skill::Attack).unwrap();
- game.add_skill(i_player.id, j_construct.id, Some(x_construct.id), Skill::Attack).unwrap();
- game.add_skill(x_player.id, x_construct.id, Some(i_construct.id), Skill::Ruin).unwrap();
- game.add_skill(x_player.id, y_construct.id, Some(i_construct.id), Skill::Attack).unwrap();
+ game.add_skill(i_player.id, i_construct.id, x_construct.id, Skill::Attack).unwrap();
+ game.add_skill(i_player.id, j_construct.id, x_construct.id, Skill::Attack).unwrap();
+ game.add_skill(x_player.id, x_construct.id, i_construct.id, Skill::Ruin).unwrap();
+ game.add_skill(x_player.id, y_construct.id, i_construct.id, Skill::Attack).unwrap();
game.player_ready(i_player.id).unwrap();
game.player_ready(x_player.id).unwrap();
@@ -1380,10 +1372,10 @@ mod tests {
game.construct_by_id(x_construct.id).unwrap().reduce_cooldowns();
}
- game.add_skill(i_player.id, i_construct.id, Some(x_construct.id), Skill::Attack).unwrap();
- game.add_skill(i_player.id, j_construct.id, Some(x_construct.id), Skill::Attack).unwrap();
- game.add_skill(x_player.id, x_construct.id, Some(i_construct.id), Skill::Intercept).unwrap();
- game.add_skill(x_player.id, y_construct.id, Some(i_construct.id), Skill::Attack).unwrap();
+ game.add_skill(i_player.id, i_construct.id, x_construct.id, Skill::Attack).unwrap();
+ game.add_skill(i_player.id, j_construct.id, x_construct.id, Skill::Attack).unwrap();
+ game.add_skill(x_player.id, x_construct.id, i_construct.id, Skill::Intercept).unwrap();
+ game.add_skill(x_player.id, y_construct.id, i_construct.id, Skill::Attack).unwrap();
game.player_ready(i_player.id).unwrap();
game.player_ready(x_player.id).unwrap();
@@ -1411,10 +1403,10 @@ mod tests {
let x_construct = x_player.constructs[0].clone();
let y_construct = x_player.constructs[1].clone();
- game.add_skill(i_player.id, i_construct.id, Some(x_construct.id), Skill::Attack).unwrap()
- .add_skill(i_player.id, j_construct.id, Some(x_construct.id), Skill::Attack).unwrap()
- .add_skill(x_player.id, x_construct.id, Some(i_construct.id), Skill::Attack).unwrap()
- .add_skill(x_player.id, y_construct.id, Some(i_construct.id), Skill::Attack).unwrap()
+ game.add_skill(i_player.id, i_construct.id, x_construct.id, Skill::Attack).unwrap()
+ .add_skill(i_player.id, j_construct.id, x_construct.id, Skill::Attack).unwrap()
+ .add_skill(x_player.id, x_construct.id, i_construct.id, Skill::Attack).unwrap()
+ .add_skill(x_player.id, y_construct.id, i_construct.id, Skill::Attack).unwrap()
.player_ready(i_player.id).unwrap()
.player_ready(x_player.id).unwrap();
@@ -1430,10 +1422,10 @@ mod tests {
assert!(game.player_by_id(x_player.id).unwrap().skills_required() == 2);
// add some more skills
- game.add_skill(i_player.id, j_construct.id, Some(x_construct.id), Skill::Attack).unwrap();
- game.add_skill(x_player.id, x_construct.id, Some(j_construct.id), Skill::Attack).unwrap();
- game.add_skill(x_player.id, y_construct.id, Some(j_construct.id), Skill::Attack).unwrap();
- assert!(game.add_skill(x_player.id, x_construct.id, Some(i_construct.id), Skill::Attack).is_err());
+ game.add_skill(i_player.id, j_construct.id, x_construct.id, Skill::Attack).unwrap();
+ game.add_skill(x_player.id, x_construct.id, j_construct.id, Skill::Attack).unwrap();
+ game.add_skill(x_player.id, y_construct.id, j_construct.id, Skill::Attack).unwrap();
+ assert!(game.add_skill(x_player.id, x_construct.id, i_construct.id, Skill::Attack).is_err());
game.player_ready(i_player.id).unwrap();
game.player_ready(x_player.id).unwrap();
@@ -1475,7 +1467,7 @@ mod tests {
}
// apply buff
- game.add_skill(x_player.id, x_construct.id, Some(y_construct.id), Skill::Decay).unwrap();
+ game.add_skill(x_player.id, x_construct.id, y_construct.id, Skill::Decay).unwrap();
game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap();
game = game.resolve_phase_start();
@@ -1490,7 +1482,7 @@ mod tests {
game.resolved.clear();
// remove
- game.add_skill(y_player.id, y_construct.id, Some(y_construct.id), Skill::Purify).unwrap();
+ game.add_skill(y_player.id, y_construct.id, y_construct.id, Skill::Purify).unwrap();
game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap();
game = game.resolve_phase_start();
@@ -1503,14 +1495,14 @@ mod tests {
}
};
- game.add_skill(y_player.id, x_construct.id, Some(y_construct.id), Skill::Siphon).unwrap();
+ game.add_skill(y_player.id, x_construct.id, y_construct.id, Skill::Siphon).unwrap();
game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap();
game = game.resolve_phase_start();
game.resolved.clear();
- game.add_skill(y_player.id, y_construct.id, Some(y_construct.id), Skill::Purify).unwrap();
+ game.add_skill(y_player.id, y_construct.id, y_construct.id, Skill::Purify).unwrap();
game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap();
game = game.resolve_phase_start();
diff --git a/server/src/img.rs b/server/src/img.rs
index e3584c20..3fc746b3 100644
--- a/server/src/img.rs
+++ b/server/src/img.rs
@@ -117,7 +117,7 @@ pub fn shapes_write(id: Uuid) -> Result {
(ConstructShapes::Circle, 10),
(ConstructShapes::Line, 10),
(ConstructShapes::V, 10),
- // (ConstructShapes::Tri, 3),
+ (ConstructShapes::Tri, 10),
// (ConstructShapes::Plus, 5),
(ConstructShapes::Blank, 1),
];
@@ -155,6 +155,9 @@ pub fn shapes_write(id: Uuid) -> Result {
let size = rng.gen_range(20.0, 50.0);
write!(&mut svg, "",
fill = colour, x = size / 2.0, y = size / 2.0, width = size, height = size, x_t = x_translate, y_t = y_translate, rotation = rotation)?;
+ if scalar == 0.0 && rng.gen_bool(0.5) {
+ continue;
+ }
write!(&mut svg, "",
fill = colour, x = size / 2.0, y = size / 2.0, width = size, height = size, x_t = -x_translate, y_t = -y_translate, rotation = rotation)?;
},
@@ -163,6 +166,9 @@ pub fn shapes_write(id: Uuid) -> Result {
let b = rng.gen_range(20.0, 50.0);
write!(&mut svg, "",
fill = colour, x = -b / 2.0, y = h / 2.0, x0 = -b / 2.0, y0 = -h / 2.0, x1 = 0, y1 = b / 2.0, x2 = b / 2.0, y2 = -h / 2.0, rotation = rotation, x_translate = x_translate, y_translate = y_translate)?;
+ if scalar == 0.0 && rng.gen_bool(0.5) {
+ continue;
+ }
write!(&mut svg, "",
fill = colour, x = -b / 2.0, y = h / 2.0, x0 = -b / 2.0, y0 = -h / 2.0, x1 = 0, y1 = b / 2.0, x2 = b / 2.0, y2 = -h / 2.0, rotation = rotation + 180, x_translate = -x_translate, y_translate = -y_translate)?;
},
@@ -178,6 +184,9 @@ pub fn shapes_write(id: Uuid) -> Result {
let height = rng.gen_range(20.0, 50.0);
write!(&mut svg, "",
fill = colour, x = width / 2.0, y = height / 2.0, width = width, height = height, x_t = x_translate, y_t = y_translate, rotation = rotation)?;
+ if scalar == 0.0 && rng.gen_bool(0.5) {
+ continue;
+ }
write!(&mut svg, "",
fill = colour, x = width / 2.0, y = height / 2.0, width = width, height = height, x_t = -x_translate, y_t = -y_translate, rotation = rotation)?;
},
@@ -188,11 +197,30 @@ pub fn shapes_write(id: Uuid) -> Result {
write!(&mut svg, "",
fill = colour, width = width, x = -b / 2.0, y = h / 2.0, x0 = -b / 2.0, y0 = -h / 2.0, x1 = 0, y1 = b / 2.0, x2 = b / 2.0, y2 = -h / 2.0, rotation = rotation, x_translate = x_translate, y_translate = y_translate)?;
+ if scalar == 0.0 && rng.gen_bool(0.5) {
+ continue;
+ }
write!(&mut svg, "",
fill = colour, width = width, x = -b / 2.0, y = h / 2.0, x0 = -b / 2.0, y0 = -h / 2.0, x1 = 0, y1 = b / 2.0, x2 = b / 2.0, y2 = -h / 2.0, rotation = rotation + 180, x_translate = -x_translate, y_translate = -y_translate)?;
},
ConstructShapes::Tri => {
+ let width = rng.gen_range(2.0, 4.0);
+ let length = rng.gen_range(12.5, 25.0);
+ let x0 = (0.0 as f64).cos() * length;
+ let y0 = (0.0 as f64).sin() * length;
+ let x1 = ((f64::consts::PI * 2.0) / 3.0).cos() * length;
+ let y1 = ((f64::consts::PI * 2.0) / 3.0).sin() * length;
+ let x2 = ((f64::consts::PI * 4.0) / 3.0).cos() * length;
+ let y2 = ((f64::consts::PI * 4.0) / 3.0).sin() * length;
+
+ write!(&mut svg, "",
+ fill = colour, width = width, x0 = x0, y0 = y0, x1 = x1, y1 = y1, x2 = x2, y2 = y2, rotation = rotation, x_translate = x_translate, y_translate = y_translate)?;
+ if scalar == 0.0 && rng.gen_bool(0.5) {
+ continue;
+ }
+ write!(&mut svg, "",
+ fill = colour, width = width, x0 = x0, y0 = y0, x1 = x1, y1 = y1, x2 = x2, y2 = y2, rotation = rotation, x_translate = -x_translate, y_translate = -y_translate)?;
},
ConstructShapes::Plus => {
diff --git a/server/src/rpc.rs b/server/src/rpc.rs
index d3955988..be3071e8 100644
--- a/server/src/rpc.rs
+++ b/server/src/rpc.rs
@@ -62,6 +62,10 @@ pub enum RpcMessage {
QueueJoined(()),
QueueCancelled(()),
+ InviteRequested(()),
+ Invite(String),
+ Joining(()),
+
Error(String),
}
@@ -78,7 +82,7 @@ pub enum RpcRequest {
GameState { id: Uuid },
GameReady { id: Uuid },
- GameSkill { game_id: Uuid, construct_id: Uuid, target_construct_id: Option, skill: Skill },
+ GameSkill { game_id: Uuid, construct_id: Uuid, target_construct_id: Uuid, skill: Skill },
GameSkillClear { game_id: Uuid },
AccountState {},
@@ -91,6 +95,8 @@ pub enum RpcRequest {
SubscriptionState {},
EmailState {},
+ InstanceInvite {},
+ InstanceJoin { code: String },
InstanceQueue {},
InstancePractice {},
InstanceAbandon { instance_id: Uuid },
@@ -136,99 +142,108 @@ impl Connection {
None => return Err(err_msg("auth required")),
};
- // evented but authorization required
- match v {
- RpcRequest::InstanceQueue {} => {
- self.events.send(Event::Queue(self.id))?;
- return Ok(RpcMessage::QueueRequested(()));
- },
- _ => (),
- };
-
- // all good, let's make a tx and process
- let mut tx = db.transaction()?;
-
let request = v.clone();
let response = match v {
- RpcRequest::AccountState {} =>
- Ok(RpcMessage::AccountState(account.clone())),
- RpcRequest::AccountConstructs {} =>
- Ok(RpcMessage::AccountConstructs(account::constructs(&mut tx, &account)?)),
- RpcRequest::AccountInstances {} =>
- Ok(RpcMessage::AccountInstances(account::account_instances(&mut tx, account)?)),
- RpcRequest::AccountSetTeam { ids } =>
- Ok(RpcMessage::AccountTeam(account::set_team(&mut tx, &account, ids)?)),
+ // evented but authorization required
+ RpcRequest::InstanceQueue {} => {
+ self.events.send(Event::Queue(self.id))?;
+ Ok(RpcMessage::QueueRequested(()))
+ },
+ RpcRequest::InstanceInvite {} => {
+ self.events.send(Event::Invite(self.id))?;
+ Ok(RpcMessage::InviteRequested(()))
+ },
+ RpcRequest::InstanceJoin { code } => {
+ self.events.send(Event::Join(self.id, code))?;
+ Ok(RpcMessage::Joining(()))
+ },
+ _ => {
+ // all good, let's make a tx and process
+ let mut tx = db.transaction()?;
- RpcRequest::EmailState {} =>
- Ok(RpcMessage::EmailState(mail::select_account(&db, account.id)?)),
+ let res = match v {
+ RpcRequest::AccountState {} =>
+ Ok(RpcMessage::AccountState(account.clone())),
+ RpcRequest::AccountConstructs {} =>
+ Ok(RpcMessage::AccountConstructs(account::constructs(&mut tx, &account)?)),
+ RpcRequest::AccountInstances {} =>
+ Ok(RpcMessage::AccountInstances(account::account_instances(&mut tx, account)?)),
+ RpcRequest::AccountSetTeam { ids } =>
+ Ok(RpcMessage::AccountTeam(account::set_team(&mut tx, &account, ids)?)),
- RpcRequest::SubscriptionState {} =>
- Ok(RpcMessage::SubscriptionState(payments::account_subscription(&db, &self.stripe, &account)?)),
+ RpcRequest::EmailState {} =>
+ Ok(RpcMessage::EmailState(mail::select_account(&db, account.id)?)),
- // RpcRequest::AccountShop {} =>
- // Ok(RpcMessage::AccountShop(mtx::account_shop(&mut tx, &account)?)),
+ RpcRequest::SubscriptionState {} =>
+ Ok(RpcMessage::SubscriptionState(payments::account_subscription(&db, &self.stripe, &account)?)),
- // RpcRequest::ConstructDelete" => handle_construct_delete(data, &mut tx, account),
+ // RpcRequest::AccountShop {} =>
+ // Ok(RpcMessage::AccountShop(mtx::account_shop(&mut tx, &account)?)),
- RpcRequest::GameState { id } =>
- Ok(RpcMessage::GameState(game_state(&mut tx, account, id)?)),
+ // RpcRequest::ConstructDelete" => handle_construct_delete(data, &mut tx, account),
- RpcRequest::GameSkill { game_id, construct_id, target_construct_id, skill } =>
- Ok(RpcMessage::GameState(game_skill(&mut tx, account, game_id, construct_id, target_construct_id, skill)?)),
+ RpcRequest::GameState { id } =>
+ Ok(RpcMessage::GameState(game_state(&mut tx, account, id)?)),
- RpcRequest::GameSkillClear { game_id } =>
- Ok(RpcMessage::GameState(game_skill_clear(&mut tx, account, game_id)?)),
+ RpcRequest::GameSkill { game_id, construct_id, target_construct_id, skill } =>
+ Ok(RpcMessage::GameState(game_skill(&mut tx, account, game_id, construct_id, target_construct_id, skill)?)),
- RpcRequest::GameReady { id } =>
- Ok(RpcMessage::GameState(game_ready(&mut tx, account, id)?)),
+ RpcRequest::GameSkillClear { game_id } =>
+ Ok(RpcMessage::GameState(game_skill_clear(&mut tx, account, game_id)?)),
- RpcRequest::InstancePractice {} =>
- Ok(RpcMessage::InstanceState(instance_practice(&mut tx, account)?)),
+ RpcRequest::GameReady { id } =>
+ Ok(RpcMessage::GameState(game_ready(&mut tx, account, id)?)),
- // these two can return GameState or InstanceState
- RpcRequest::InstanceReady { instance_id } =>
- Ok(instance_ready(&mut tx, account, instance_id)?),
- RpcRequest::InstanceState { instance_id } =>
- Ok(instance_state(&mut tx, instance_id)?),
- RpcRequest::InstanceAbandon { instance_id } =>
- Ok(instance_abandon(&mut tx, account, instance_id)?),
+ RpcRequest::InstancePractice {} =>
+ Ok(RpcMessage::InstanceState(instance_practice(&mut tx, account)?)),
- RpcRequest::VboxAccept { instance_id, group, index } =>
- Ok(RpcMessage::InstanceState(vbox_accept(&mut tx, account, instance_id, group, index)?)),
+ // these two can return GameState or InstanceState
+ RpcRequest::InstanceReady { instance_id } =>
+ Ok(instance_ready(&mut tx, account, instance_id)?),
+ RpcRequest::InstanceState { instance_id } =>
+ Ok(instance_state(&mut tx, instance_id)?),
+ RpcRequest::InstanceAbandon { instance_id } =>
+ Ok(instance_abandon(&mut tx, account, instance_id)?),
- RpcRequest::VboxApply { instance_id, construct_id, index } =>
- Ok(RpcMessage::InstanceState(vbox_apply(&mut tx, account, instance_id, construct_id, index)?)),
+ RpcRequest::VboxAccept { instance_id, group, index } =>
+ Ok(RpcMessage::InstanceState(vbox_accept(&mut tx, account, instance_id, group, index)?)),
- RpcRequest::VboxCombine { instance_id, indices } =>
- Ok(RpcMessage::InstanceState(vbox_combine(&mut tx, account, instance_id, indices)?)),
+ RpcRequest::VboxApply { instance_id, construct_id, index } =>
+ Ok(RpcMessage::InstanceState(vbox_apply(&mut tx, account, instance_id, construct_id, index)?)),
- RpcRequest::VboxDiscard { instance_id } =>
- Ok(RpcMessage::InstanceState(vbox_discard(&mut tx, account, instance_id)?)),
+ RpcRequest::VboxCombine { instance_id, indices } =>
+ Ok(RpcMessage::InstanceState(vbox_combine(&mut tx, account, instance_id, indices)?)),
- RpcRequest::VboxReclaim { instance_id, index } =>
- Ok(RpcMessage::InstanceState(vbox_reclaim(&mut tx, account, instance_id, index)?)),
+ RpcRequest::VboxDiscard { instance_id } =>
+ Ok(RpcMessage::InstanceState(vbox_discard(&mut tx, account, instance_id)?)),
- RpcRequest::VboxUnequip { instance_id, construct_id, target } =>
- Ok(RpcMessage::InstanceState(vbox_unequip(&mut tx, account, instance_id, construct_id, target)?)),
+ RpcRequest::VboxReclaim { instance_id, index } =>
+ Ok(RpcMessage::InstanceState(vbox_reclaim(&mut tx, account, instance_id, index)?)),
- RpcRequest::MtxConstructSpawn {} =>
- Ok(RpcMessage::ConstructSpawn(mtx::new_construct(&mut tx, account)?)),
+ RpcRequest::VboxUnequip { instance_id, construct_id, target } =>
+ Ok(RpcMessage::InstanceState(vbox_unequip(&mut tx, account, instance_id, construct_id, target)?)),
- RpcRequest::MtxConstructApply { mtx, construct_id, name } =>
- Ok(RpcMessage::AccountTeam(mtx::apply(&mut tx, account, mtx, construct_id, name)?)),
+ RpcRequest::MtxConstructSpawn {} =>
+ Ok(RpcMessage::ConstructSpawn(mtx::new_construct(&mut tx, account)?)),
- RpcRequest::MtxBuy { mtx } =>
- Ok(RpcMessage::AccountShop(mtx::buy(&mut tx, account, mtx)?)),
+ RpcRequest::MtxConstructApply { mtx, construct_id, name } =>
+ Ok(RpcMessage::AccountTeam(mtx::apply(&mut tx, account, mtx, construct_id, name)?)),
- RpcRequest::SubscriptionEnding { ending } =>
- Ok(RpcMessage::SubscriptionState(payments::subscription_ending(&mut tx, &self.stripe, account, ending)?)),
+ RpcRequest::MtxBuy { mtx } =>
+ Ok(RpcMessage::AccountShop(mtx::buy(&mut tx, account, mtx)?)),
- _ => Err(format_err!("unknown request request={:?}", request)),
+ RpcRequest::SubscriptionEnding { ending } =>
+ Ok(RpcMessage::SubscriptionState(payments::subscription_ending(&mut tx, &self.stripe, account, ending)?)),
+
+ _ => Err(format_err!("unknown request request={:?}", request)),
+ };
+
+ tx.commit()?;
+ res
+ }
};
- tx.commit()?;
-
info!("request={:?} account={:?} duration={:?}", request, account.name, begin.elapsed());
return response;
diff --git a/server/src/skill.rs b/server/src/skill.rs
index d71ac47e..52ef3092 100644
--- a/server/src/skill.rs
+++ b/server/src/skill.rs
@@ -1213,20 +1213,6 @@ impl Skill {
}
}
- pub fn self_targeting(&self) -> bool {
- match self {
- Skill::Block |
- Skill::Sustain|
- Skill::SustainPlus |
- Skill::SustainPlusPlus |
- Skill::Counter|
- Skill::CounterPlus |
- Skill::CounterPlusPlus => true,
-
- _ => false,
- }
- }
-
pub fn defensive(&self) -> bool {
let mut rng = thread_rng();
diff --git a/server/src/warden.rs b/server/src/warden.rs
index 9d58e678..262f6187 100644
--- a/server/src/warden.rs
+++ b/server/src/warden.rs
@@ -90,6 +90,10 @@ impl Warden {
fn on_match(&mut self, pair: Pair) -> Result<(), Error> {
info!("received pair={:?}", pair);
+ // clear pvp status
+ self.events.send(Event::Joined(pair.0.id))?;
+ self.events.send(Event::Joined(pair.1.id))?;
+
let db = self.pool.get()?;
let mut tx = db.transaction()?;