This commit is contained in:
ntr 2019-04-25 19:34:53 +10:00
parent 2f4944db10
commit f37cb4a7e4
16 changed files with 417 additions and 245 deletions

View File

@ -340,6 +340,11 @@ header {
margin: 0; margin: 0;
} }
.instance-ui-btn[disabled] {
color: #333;
border-color: #333;
}
.vbox-btn:active, .vbox-btn:hover, .vbox-btn:focus { .vbox-btn:active, .vbox-btn:hover, .vbox-btn:focus {
color: black; color: black;
} }

View File

@ -57,6 +57,7 @@ function GamePanel(props) {
<div className="instance-hdr"> <div className="instance-hdr">
<button <button
className="game-back-btn instance-btn instance-ui-btn left" className="game-back-btn instance-btn instance-ui-btn left"
disabled={game.phase !== 'Finish'}
onClick={backClick}> onClick={backClick}>
Back Back
</button> </button>

View File

@ -10,12 +10,13 @@ function Info(args) {
info, info,
sendUnequip, sendUnequip,
instance, instance,
player,
setInfo, setInfo,
} = args; } = args;
function infoVar([type, value]) { function infoVar([type, value]) {
let red = 0; let blue = 0; let green = 0; let red = 0; let blue = 0; let green = 0;
instance.cryps.forEach(cryp => { player.cryps.forEach(cryp => {
red += cryp.colours.red; red += cryp.colours.red;
blue += cryp.colours.blue; blue += cryp.colours.blue;
green += cryp.colours.green; green += cryp.colours.green;
@ -152,18 +153,37 @@ function Info(args) {
); );
} }
function scoreBoard() {
const players = instance.players.map((p, i) =>
<tr key={i} >
<td>{p.name}</td>
<td>{p.score.wins} / {p.score.losses}</td>
<td>{p.ready ? 'ready' : ''}</td>
</tr>
);
return (
<table className="vbox-table">
<tbody>
{players}
</tbody>
</table>
);
}
const infoCryp = activeCryp const infoCryp = activeCryp
? infoCrypElement(instance.cryps.find(c => c.id === activeCryp.id)) ? infoCrypElement(player.cryps.find(c => c.id === activeCryp.id))
: null; : null;
const otherInfo = info.length const otherInfo = info.length
? infoVar(info) ? infoVar(info)
: null; : null;
const instanceInfoClass = `instance-info ${activeCryp ? '' : 'hidden'}`; const instanceInfoClass = `instance-info ${info.length ? '' : 'hidden'}`;
return ( return (
<div className={instanceInfoClass} > <div className={instanceInfoClass} >
{scoreBoard()}
{infoCryp} {infoCryp}
{otherInfo} {otherInfo}
</div> </div>

View File

@ -10,10 +10,11 @@ const addState = connect(
info, info,
ws, ws,
instance, instance,
player,
} = state; } = state;
function sendUnequip(crypId, item) { function sendUnequip(crypId, item) {
return ws.sendVboxUnequip(instance.instance, crypId, item); return ws.sendVboxUnequip(instance.id, crypId, item);
} }
return { return {
@ -21,6 +22,7 @@ const addState = connect(
info, info,
sendUnequip, sendUnequip,
instance, instance,
player,
}; };
}, },

View File

@ -1,120 +1,16 @@
const preact = require('preact'); const preact = require('preact');
// const key = require('keymaster');
const range = require('lodash/range');
const mapValues = require('lodash/mapValues');
const VboxContainer = require('./vbox.container'); const VboxContainer = require('./vbox.container');
const InfoContainer = require('./info.container'); const InfoContainer = require('./info.container');
const molecule = require('./molecule'); const InstanceCrypsContainer = require('./instance.cryps');
const { SPECS } = require('../utils');
function Cryp(props) {
const {
cryp,
sendVboxApply,
setInfo,
activeVar,
setActiveCryp,
} = props;
const skills = range(0, 3).map(i => {
const skill = cryp.skills[i];
const s = skill
? skill.skill
: (<span>&nbsp;</span>);
function skillClick() {
if (!skill) return false;
setInfo('skill', { skill: skill.skill, cryp });
return setActiveCryp(cryp);
}
return <button key={i} className="cryp-skill-btn right" onClick={skillClick} >{s}</button>;
});
// needed for ondrop to fire
function onDragOver(e) {
e.preventDefault();
return false;
}
function onDrop(e) {
e.stopPropagation();
e.preventDefault();
const item = parseInt(e.dataTransfer.getData('text'), 10);
return sendVboxApply(cryp.id, item);
}
function onClick(e) {
e.stopPropagation();
e.preventDefault();
if (activeVar !== null) return sendVboxApply(cryp.id, activeVar);
setInfo(null);
return setActiveCryp(cryp);
}
const specs = cryp.specs.map((s, i) => {
function specClick() {
setActiveCryp(cryp);
setInfo('spec', { spec: s, cryp });
}
return (
<figure key={i} onClick={specClick}>
{SPECS[s].svg(`stat-icon ${SPECS[s].colour}`)}
<figcaption>{SPECS[s].caption}</figcaption>
</figure>
);
});
const cTotal = cryp.colours.red + cryp.colours.blue + cryp.colours.green;
const colours = mapValues(cryp.colours, c => {
if (cTotal === 0) return 245;
return Math.floor(c / cTotal * 255);
});
const alpha = cTotal === 0 ? 1 : 0.75;
const thickness = total => {
if (total < 3) return 1;
if (total < 6) return 2;
if (total < 9) return 3;
return 4;
};
const border = { border: `${thickness(cTotal)}px solid rgba(${colours.red}, ${colours.green}, ${colours.blue}, ${alpha})` };
return (
<div
key={cryp.id}
className="cryp-box"
onDragOver={onDragOver}
onDrop={onDrop}
style={border}
>
<div className="cryp-box-top">
<figure className="img" onClick={onClick}>
{molecule()}
<figcaption>{cryp.name}</figcaption>
</figure>
<div className="skills">
{skills}
</div>
</div>
<div className="stats">
{specs}
</div>
</div>
);
}
function InstanceComponent(args) { function InstanceComponent(args) {
const { const {
account,
instance, instance,
player,
quit, quit,
// clearInfo, // clearInfo,
sendInstanceReady, sendInstanceReady,
sendVboxApply,
setInfo,
activeVar, activeVar,
activeCryp, activeCryp,
setActiveVar, setActiveVar,
@ -123,12 +19,6 @@ function InstanceComponent(args) {
if (!instance) return <div>...</div>; if (!instance) return <div>...</div>;
const player = instance.players.find(p => p.account === account.id);
const cryps = player.cryps.map((c, i) => Cryp({
cryp: c, sendVboxApply, setInfo, activeVar, setActiveCryp,
}));
function showVbox(e) { function showVbox(e) {
setActiveVar(null); setActiveVar(null);
setActiveCryp(null); setActiveCryp(null);
@ -162,8 +52,6 @@ function InstanceComponent(args) {
? vboxBtn ? vboxBtn
: teamBtn; : teamBtn;
const crypListClass = `cryp-list ${infoSelected ? '' : 'hidden'}`;
const menuBtn = ( const menuBtn = (
<button <button
className="instance-btn instance-ui-btn menu-btn left" className="instance-btn instance-ui-btn menu-btn left"
@ -172,6 +60,17 @@ function InstanceComponent(args) {
</button> </button>
); );
const readyBtn = (
<button
className="instance-btn instance-ui-btn ready-btn"
onClick={() => sendInstanceReady()}>
Ready
</button>
);
const actionBtn = player
? readyBtn
: null;
return ( return (
<main className="instance" > <main className="instance" >
@ -181,16 +80,10 @@ function InstanceComponent(args) {
<div className="spacer"> <div className="spacer">
<div>&nbsp;</div> <div>&nbsp;</div>
</div> </div>
<button {actionBtn}
className="instance-btn instance-ui-btn ready-btn"
onClick={() => sendInstanceReady()}>
Ready
</button>
</div> </div>
<VboxContainer /> <VboxContainer />
<div className={crypListClass}> <InstanceCrypsContainer />
{cryps}
</div>
<InfoContainer /> <InfoContainer />
</main> </main>
); );

View File

@ -6,7 +6,7 @@ const Instance = require('./instance.component');
const addState = connect( const addState = connect(
function receiveState(state) { function receiveState(state) {
const { ws, instance, account, activeVar, activeCryp } = state; const { ws, instance, player, account, activeVar, activeCryp } = state;
function sendInstanceReady() { function sendInstanceReady() {
return ws.sendInstanceReady(instance.id); return ws.sendInstanceReady(instance.id);
@ -16,7 +16,7 @@ const addState = connect(
return ws.sendVboxApply(instance.id, crypId, i); return ws.sendVboxApply(instance.id, crypId, i);
} }
return { instance, account, sendInstanceReady, sendVboxApply, activeVar, activeCryp }; return { instance, player, account, sendInstanceReady, sendVboxApply, activeVar, activeCryp };
}, },
function receiveDispatch(dispatch) { function receiveDispatch(dispatch) {

View File

@ -0,0 +1,178 @@
const { connect } = require('preact-redux');
const preact = require('preact');
const range = require('lodash/range');
const mapValues = require('lodash/mapValues');
const molecule = require('./molecule');
const { SPECS } = require('../utils');
const actions = require('../actions');
const SkillBtn = require('./skill.btn');
const addState = connect(
function receiveState(state) {
const { ws, instance, player, account, activeVar, activeCryp } = state;
function sendInstanceReady() {
return ws.sendInstanceReady(instance.id);
}
function sendVboxApply(crypId, i) {
return ws.sendVboxApply(instance.id, crypId, i);
}
return { instance, player, account, sendInstanceReady, sendVboxApply, activeVar, activeCryp };
},
function receiveDispatch(dispatch) {
function quit() {
dispatch(actions.setInstance(null));
}
function setInfo(item, value) {
dispatch(actions.setInfo([item, value]));
}
function setActiveVar(value) {
dispatch(actions.setActiveVar(value));
}
function setActiveCryp(value) {
dispatch(actions.setActiveCryp(value));
}
function clearInfo() {
return dispatch(actions.setInfo([]));
}
return { quit, clearInfo, setInfo, setActiveVar, setActiveCryp };
}
);
function Cryp(props) {
const {
cryp,
sendVboxApply,
setInfo,
activeVar,
setActiveCryp,
} = props;
const skills = range(0, 3).map(i => {
const skill = cryp.skills[i];
const s = skill
? skill.skill
: (<span>&nbsp;</span>);
function skillClick() {
if (!skill) return false;
setInfo('skill', { skill: skill.skill, cryp });
return setActiveCryp(cryp);
}
return <button key={i} className="cryp-skill-btn right" onClick={skillClick} >{s}</button>;
});
// needed for ondrop to fire
function onDragOver(e) {
e.preventDefault();
return false;
}
function onDrop(e) {
e.stopPropagation();
e.preventDefault();
const item = parseInt(e.dataTransfer.getData('text'), 10);
return sendVboxApply(cryp.id, item);
}
function onClick(e) {
e.stopPropagation();
e.preventDefault();
if (activeVar !== null) return sendVboxApply(cryp.id, activeVar);
setInfo(null);
return setActiveCryp(cryp);
}
const specs = cryp.specs.map((s, i) => {
function specClick() {
setActiveCryp(cryp);
setInfo('spec', { spec: s, cryp });
}
return (
<figure key={i} onClick={specClick}>
{SPECS[s].svg(`stat-icon ${SPECS[s].colour}`)}
<figcaption>{SPECS[s].caption}</figcaption>
</figure>
);
});
const cTotal = cryp.colours.red + cryp.colours.blue + cryp.colours.green;
const colours = mapValues(cryp.colours, c => {
if (cTotal === 0) return 245;
return Math.floor(c / cTotal * 255);
});
const alpha = cTotal === 0 ? 1 : 0.75;
const thickness = total => {
if (total < 3) return 1;
if (total < 6) return 2;
if (total < 9) return 3;
return 4;
};
const border = { border: `${thickness(cTotal)}px solid rgba(${colours.red}, ${colours.green}, ${colours.blue}, ${alpha})` };
return (
<div
key={cryp.id}
className="cryp-box"
onDragOver={onDragOver}
onDrop={onDrop}
style={border}
>
<div className="cryp-box-top">
<figure className="img" onClick={onClick}>
{molecule()}
<figcaption>{cryp.name}</figcaption>
</figure>
<div className="skills">
{skills}
</div>
</div>
<div className="stats">
{specs}
</div>
</div>
);
}
function InstanceCryps(props) {
const {
player,
activeCryp,
activeVar,
// clearInfo,
setInfo,
setActiveCryp,
sendVboxApply,
} = props;
if (!player) return false;
const infoSelected = activeVar !== null || activeCryp;
const crypListClass = `cryp-list ${infoSelected ? '' : 'hidden'}`;
const cryps = player.cryps.map((c, i) => Cryp({
cryp: c, sendVboxApply, setInfo, activeVar, setActiveCryp,
}));
return (
<div className={crypListClass}>
{cryps}
</div>
);
}
module.exports = addState(InstanceCryps);

View File

@ -24,6 +24,7 @@ function Menu(args) {
sendInstanceState, sendInstanceState,
sendPlayerMmCrypsSet, sendPlayerMmCrypsSet,
sendInstanceJoin, sendInstanceJoin,
sendInstanceNew,
sendCrypSpawn, sendCrypSpawn,
instances, instances,
} = args; } = args;
@ -32,14 +33,22 @@ function Menu(args) {
if (!instances) return <div>...</div>; if (!instances) return <div>...</div>;
const instancePanels = instances.map(instance => { const instancePanels = instances.map(instance => {
const player = instance.players.find(p => p.account === account.id); const player = instance.players.find(p => p.id === account.id);
const name = `${instance.name} | ${player.score.wins} : ${player.score.losses}`; const scoreText = player
? `| ${player.score.wins} : ${player.score.losses}`
: '';
const name = `${instance.name} ${scoreText}`;
function instanceClick() {
if (!player) return sendInstanceJoin(instance);
return sendInstanceState(instance);
}
return ( return (
<button <button
className={'menu-instance-btn right'} className={'menu-instance-btn right'}
key={instance.id} key={instance.id}
onClick={() => sendInstanceState(instance)}> onClick={instanceClick}>
{name} {name}
</button> </button>
); );
@ -59,8 +68,8 @@ function Menu(args) {
<button <button
className={`menu-instance-btn right ${instanceJoinHidden ? 'hidden' : ''}`} className={`menu-instance-btn right ${instanceJoinHidden ? 'hidden' : ''}`}
disabled={instanceJoinHidden} disabled={instanceJoinHidden}
onClick={() => sendInstanceJoin()}> onClick={() => sendInstanceNew()}>
Join New Instance Create New Instance
</button> </button>
); );

View File

@ -7,9 +7,16 @@ const addState = connect(
function receiveState(state) { function receiveState(state) {
const { ws, cryps, selectedCryps, instances, account } = state; const { ws, cryps, selectedCryps, instances, account } = state;
function sendInstanceJoin() { function sendInstanceJoin(instance) {
if (selectedCryps.length) { if (selectedCryps.length) {
return ws.sendInstanceLobby(selectedCryps); return ws.sendInstanceJoin(instance.id, selectedCryps);
}
return false;
}
function sendInstanceNew() {
if (selectedCryps.length) {
return ws.sendInstanceNew(selectedCryps);
} }
return false; return false;
} }
@ -34,6 +41,7 @@ const addState = connect(
cryps, cryps,
selectedCryps, selectedCryps,
sendInstanceJoin, sendInstanceJoin,
sendInstanceNew,
sendInstanceState, sendInstanceState,
sendCrypSpawn, sendCrypSpawn,
sendPlayerMmCrypsSet, sendPlayerMmCrypsSet,

View File

@ -93,9 +93,11 @@ function registerEvents(store) {
} }
function setInstance(v) { function setInstance(v) {
const { account } = store.getState(); const { account, ws } = store.getState();
const player = v.players.find(p => p.account === account.id); const player = v.players.find(p => p.id === account.id);
store.dispatch(actions.setPlayer(player)); if (player) store.dispatch(actions.setPlayer(player));
if (!v) ws.clearInstanceStateTimeout();
return store.dispatch(actions.setInstance(v)); return store.dispatch(actions.setInstance(v));
} }

View File

@ -7,6 +7,7 @@ function setupKeys(store) {
key('esc', () => store.dispatch(actions.setCombiner([null, null, null]))); key('esc', () => store.dispatch(actions.setCombiner([null, null, null])));
key('esc', () => store.dispatch(actions.setReclaiming(false))); key('esc', () => store.dispatch(actions.setReclaiming(false)));
key('esc', () => store.dispatch(actions.setActiveSkill(null))); key('esc', () => store.dispatch(actions.setActiveSkill(null)));
key('esc', () => store.dispatch(actions.setActiveCryp(null)));
} }
module.exports = setupKeys; module.exports = setupKeys;

View File

@ -144,12 +144,12 @@ function createSocket(events) {
send({ method: 'zone_close', params: { zone_id: zoneId } }); send({ method: 'zone_close', params: { zone_id: zoneId } });
} }
function sendInstanceJoin(cryps) { function sendInstanceJoin(instanceId, cryps) {
send({ method: 'instance_join', params: { cryp_ids: cryps, pve: true } }); send({ method: 'instance_join', params: { instance_id: instanceId, cryp_ids: cryps } });
} }
function sendInstanceLobby(cryps) { function sendInstanceNew(cryps) {
send({ method: 'instance_lobby', params: { cryp_ids: cryps, name: 'dota apem', players: 2 } }); send({ method: 'instance_new', params: { cryp_ids: cryps, name: 'dota pros onli', players: 2 } });
} }
function sendInstanceReady(instanceId) { function sendInstanceReady(instanceId) {
@ -187,7 +187,7 @@ function createSocket(events) {
let gameStateTimeout; let gameStateTimeout;
function gameState(response) { function gameState(response) {
const [structName, game] = response; const [structName, game] = response;
clearInterval(gameStateTimeout); clearTimeout(gameStateTimeout);
gameStateTimeout = setTimeout(() => sendGameState(game.id), 1000); gameStateTimeout = setTimeout(() => sendGameState(game.id), 1000);
events.setGame(game); events.setGame(game);
} }
@ -205,11 +205,19 @@ function createSocket(events) {
events.setZone(zone); events.setZone(zone);
} }
let instanceStateTimeout;
function instanceState(response) { function instanceState(response) {
const [structName, i] = response; const [structName, i] = response;
clearTimeout(instanceStateTimeout);
instanceStateTimeout = setTimeout(() => sendInstanceState(i.id), 1000);
events.setInstance(i); events.setInstance(i);
} }
function clearInstanceStateTimeout() {
clearTimeout(instanceStateTimeout);
}
function instanceScores(response) { function instanceScores(response) {
const [structName, scores] = response; const [structName, scores] = response;
events.setScores(scores); events.setScores(scores);
@ -334,7 +342,7 @@ function createSocket(events) {
sendZoneClose, sendZoneClose,
sendInstanceJoin, sendInstanceJoin,
sendInstanceReady, sendInstanceReady,
sendInstanceLobby, sendInstanceNew,
sendInstanceScores, sendInstanceScores,
sendPlayerMmCrypsSet, sendPlayerMmCrypsSet,
sendInstanceState, sendInstanceState,

View File

@ -860,7 +860,7 @@ pub fn game_update(game: &Game, tx: &mut Transaction) -> Result<(), Error> {
// Ok(game) // Ok(game)
// } // }
pub fn game_instance_new(tx: &mut Transaction, player: Player, game_id: Uuid) -> Result<Game, Error> { pub fn game_instance_new(tx: &mut Transaction, players: Vec<Player>, game_id: Uuid, instance_id: Uuid) -> Result<Game, Error> {
// create the game // create the game
let mut game = Game::new(); let mut game = Game::new();
game.id = game_id; game.id = game_id;
@ -868,14 +868,19 @@ pub fn game_instance_new(tx: &mut Transaction, player: Player, game_id: Uuid) ->
game game
.set_team_num(2) .set_team_num(2)
.set_team_size(3) .set_team_size(3)
.set_instance(player.instance) .set_instance(instance_id)
.set_mode(GameMode::Pvp); .set_mode(GameMode::Pvp);
// create the initiators team // create the initiators team
let mut team = Team::new(player.account); for player in players {
team.set_cryps(player.cryps); let mut team = Team::new(player.id);
team.set_cryps(player.cryps);
game.team_add(team)?;
}
game.team_add(team)?; if game.can_start() {
game = game.start();
}
// persist // persist
game_write(&game, tx)?; game_write(&game, tx)?;
@ -886,7 +891,7 @@ pub fn game_instance_new(tx: &mut Transaction, player: Player, game_id: Uuid) ->
pub fn game_instance_join(tx: &mut Transaction, player: Player, game_id: Uuid) -> Result<Game, Error> { pub fn game_instance_join(tx: &mut Transaction, player: Player, game_id: Uuid) -> Result<Game, Error> {
let mut game = game_get(tx, game_id)?; let mut game = game_get(tx, game_id)?;
let mut team = Team::new(player.account); let mut team = Team::new(player.id);
team.set_cryps(player.cryps); team.set_cryps(player.cryps);
game.team_add(team)?; game.team_add(team)?;

View File

@ -14,14 +14,14 @@ use account::Account;
use player::{Player, Score, player_create, player_get, player_update}; use player::{Player, Score, player_create, player_get, player_update};
use cryp::{Cryp, cryp_get}; use cryp::{Cryp, cryp_get};
use mob::{instance_mobs}; use mob::{instance_mobs};
use game::{Game, Team, game_get, game_write, game_instance_new, game_instance_join, game_global_get, game_global_set}; use game::{Game, Phase, Team, game_get, game_write, game_instance_new, game_instance_join, game_global_get, game_global_set};
use vbox::{Var}; use vbox::{Var};
use rpc::{RpcResult};
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
enum InstancePhase { enum InstancePhase {
Open, Lobby,
Vbox, InProgress,
Games,
Finished, Finished,
} }
@ -50,7 +50,7 @@ impl Instance {
id: Uuid::new_v4(), id: Uuid::new_v4(),
players: vec![], players: vec![],
rounds: vec![], rounds: vec![],
phase: InstancePhase::Open, phase: InstancePhase::Lobby,
open: true, open: true,
max_players: 2, max_players: 2,
name: String::new(), name: String::new(),
@ -63,7 +63,7 @@ impl Instance {
id: Uuid::nil(), id: Uuid::nil(),
players: vec![player], players: vec![player],
rounds: vec![], rounds: vec![],
phase: InstancePhase::Vbox, phase: InstancePhase::InProgress,
open: false, open: false,
max_players: 0, max_players: 0,
name: "Global Matchmaking".to_string(), name: "Global Matchmaking".to_string(),
@ -103,13 +103,18 @@ impl Instance {
self self
} }
fn add_player(&mut self, player: Player) -> &mut Instance { fn add_player(&mut self, player: Player) -> Result<&mut Instance, Error> {
match self.players.iter().find(|p| p.id == player.id) {
Some(p) => return Err(err_msg("already joined")),
None => (),
};
self.players.push(player); self.players.push(player);
self Ok(self)
} }
pub fn player_update(mut self, player: Player, ignore_phase: bool) -> Result<Instance, Error> { pub fn player_update(mut self, player: Player, ignore_phase: bool) -> Result<Instance, Error> {
if !ignore_phase && self.phase != InstancePhase::Vbox { if !ignore_phase && self.phase != InstancePhase::InProgress {
return Err(format_err!("instance not in vbox phase ({:?})", self.phase)); return Err(format_err!("instance not in vbox phase ({:?})", self.phase));
} }
@ -124,7 +129,7 @@ impl Instance {
} }
fn player_ready(&mut self, player_id: Uuid) -> Result<&mut Instance, Error> { fn player_ready(&mut self, player_id: Uuid) -> Result<&mut Instance, Error> {
if ![InstancePhase::Vbox, InstancePhase::Open].contains(&self.phase) { if ![InstancePhase::InProgress, InstancePhase::Lobby].contains(&self.phase) {
return Err(err_msg("instance not in start or vbox phase")); return Err(err_msg("instance not in start or vbox phase"));
} }
@ -135,27 +140,17 @@ impl Instance {
self.players[i].set_ready(true); self.players[i].set_ready(true);
match self.phase { if self.phase == InstancePhase::Lobby && self.can_start() {
InstancePhase::Open => { self.start();
if self.can_start() {
self.start();
}
},
InstancePhase::Vbox => {
if self.all_ready() {
self.games_phase_start();
}
},
_ => panic!("unhandled ready phase"),
} }
Ok(self) Ok(self)
} }
fn player_has_pve_game(&self, player: &Player) -> bool { fn player_has_pve_game(&self, player_id: Uuid) -> bool {
let opponent_id = self.current_round(&player).player_ids let opponent_id = self.current_round(player_id).player_ids
.iter() .iter()
.find(|p| **p != player.id) .find(|p| **p != player_id)
.expect("unable to find opponent"); .expect("unable to find opponent");
return self.players return self.players
@ -165,11 +160,11 @@ impl Instance {
.bot; .bot;
} }
fn bot_vs_player_game(&self, player: &Player) -> Result<Game, Error> { fn bot_vs_player_game(&self, player_id: Uuid) -> Result<Game, Error> {
let current_round = self.current_round(player); let current_round = self.current_round(player_id);
let bot_id = current_round.player_ids.iter().find(|id| **id != player.id).unwrap(); let bot_id = current_round.player_ids.iter().find(|id| **id != player_id).unwrap();
let plr = self.players.clone().into_iter().find(|p| p.id == player.id).unwrap(); let plr = self.players.clone().into_iter().find(|p| p.id == player_id).unwrap();
let bot = self.players.clone().into_iter().find(|p| p.id == *bot_id).unwrap(); let bot = self.players.clone().into_iter().find(|p| p.id == *bot_id).unwrap();
let mut game = Game::new(); let mut game = Game::new();
@ -180,10 +175,10 @@ impl Instance {
.set_instance(self.id); .set_instance(self.id);
// add the players // add the players
let mut plr_team = Team::new(plr.account); let mut plr_team = Team::new(plr.id);
plr_team.set_cryps(plr.cryps); plr_team.set_cryps(plr.cryps);
let mut bot_team = Team::new(bot.account); let mut bot_team = Team::new(bot.id);
bot_team.set_cryps(bot.cryps); bot_team.set_cryps(bot.cryps);
bot_team.set_bot(); bot_team.set_bot();
@ -203,18 +198,20 @@ impl Instance {
fn start(&mut self) -> &mut Instance { fn start(&mut self) -> &mut Instance {
// self.players.sort_unstable_by_key(|p| p.id); // self.players.sort_unstable_by_key(|p| p.id);
self.open = false; self.open = false;
self.vbox_phase_start() self.next_round()
} }
fn vbox_phase_start(&mut self) -> &mut Instance { fn next_round(&mut self) -> &mut Instance {
self.phase = InstancePhase::Vbox; self.phase = InstancePhase::InProgress;
self.players.iter_mut().for_each(|p| { self.players.iter_mut().for_each(|p| {
p.ready = false; p.ready = false;
p.vbox.fill(); p.vbox.fill();
}); });
self.generate_rounds(); self.generate_rounds();
self.bot_vbox_phase(); self.bot_vbox_phase();
self.bot_games_phase();
self self
} }
@ -223,21 +220,6 @@ impl Instance {
self.players.iter().all(|p| p.ready) self.players.iter().all(|p| p.ready)
} }
// requires no input
// just do it
fn games_phase_start(&mut self) -> &mut Instance {
if self.phase != InstancePhase::Vbox {
panic!("instance not in vbox phase");
}
assert!(self.all_ready());
self.phase = InstancePhase::Games;
self.bot_games_phase();
self
}
fn game_finished(&mut self, game: &Game) -> Result<&mut Instance, Error> { fn game_finished(&mut self, game: &Game) -> Result<&mut Instance, Error> {
let round_num = self.rounds.len() - 1; let round_num = self.rounds.len() - 1;
self.rounds[round_num] self.rounds[round_num]
@ -249,7 +231,7 @@ impl Instance {
Ok(self) Ok(self)
} }
fn games_phase_finished(&self) -> bool { fn all_games_finished(&self) -> bool {
match self.rounds.last() { match self.rounds.last() {
Some(r) => r.iter().all(|g| g.finished), Some(r) => r.iter().all(|g| g.finished),
None => true, None => true,
@ -267,8 +249,8 @@ impl Instance {
} }
fn bot_games_phase(&mut self) -> &mut Instance { fn bot_games_phase(&mut self) -> &mut Instance {
if self.phase != InstancePhase::Games { if self.phase != InstancePhase::InProgress {
panic!("instance not in games phase"); panic!("instance not in progress phase");
} }
let r = self.rounds.len() - 1; let r = self.rounds.len() - 1;
@ -277,7 +259,7 @@ impl Instance {
for mut round in self.rounds[r].iter_mut() { for mut round in self.rounds[r].iter_mut() {
if self.players if self.players
.iter() .iter()
.filter(|p| round.player_ids.contains(&p.id) && p.bot) .filter(|p| round.player_ids.contains(&p.id) && p.bot && p.ready)
.count() == 2 { .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 a = self.players.clone().into_iter().find(|p| p.id == round.player_ids[0]).unwrap();
@ -291,11 +273,11 @@ impl Instance {
.set_team_size(3); .set_team_size(3);
// add the players // add the players
let mut a_team = Team::new(a.account); let mut a_team = Team::new(a.id);
a_team.set_cryps(a.cryps); a_team.set_cryps(a.cryps);
a_team.set_bot(); a_team.set_bot();
let mut b_team = Team::new(b.account); let mut b_team = Team::new(b.id);
b_team.set_cryps(b.cryps); b_team.set_cryps(b.cryps);
b_team.set_bot(); b_team.set_bot();
@ -314,7 +296,7 @@ impl Instance {
round.finished = true; round.finished = true;
for team in game.teams.iter() { for team in game.teams.iter() {
let mut player = self.players.iter_mut().find(|p| p.account == team.id).unwrap(); let mut player = self.players.iter_mut().find(|p| p.id == team.id).unwrap();
match team.id == winner.id { match team.id == winner.id {
true => player.add_win(), true => player.add_win(),
false => player.add_loss(), false => player.add_loss(),
@ -358,17 +340,35 @@ impl Instance {
self self
} }
fn current_round(&self, player: &Player) -> &Round { fn current_round(&self, player_id: Uuid) -> &Round {
let round_num = self.rounds.len() - 1; let round_num = self.rounds.len() - 1;
let current_round = self.rounds[round_num] let current_round = self.rounds[round_num]
.iter() .iter()
.find(|g| g.player_ids.contains(&player.id)) .find(|g| g.player_ids.contains(&player_id))
.unwrap(); .unwrap();
current_round current_round
} }
fn current_game(&mut self, player_id: Uuid) -> Option<Uuid> {
if self.phase == InstancePhase::Lobby {
return None;
}
let current_round = self.current_round(player_id);
let can_start = self.players
.iter()
.filter(|p| current_round.player_ids.contains(&p.id))
.all(|p| p.ready);
match can_start {
true => Some(current_round.game_id),
false => None,
}
}
fn scores(&self) -> Vec<(String, Score)> { fn scores(&self) -> Vec<(String, Score)> {
let mut scores = self.players.iter() let mut scores = self.players.iter()
.map(|p| (p.name.clone(), p.score)) .map(|p| (p.name.clone(), p.score))
@ -383,7 +383,7 @@ impl Instance {
fn account_player(&mut self, account: Uuid) -> Result<&mut Player, Error> { fn account_player(&mut self, account: Uuid) -> Result<&mut Player, Error> {
self.players self.players
.iter_mut() .iter_mut()
.find(|p| p.account == account) .find(|p| p.id == account)
.ok_or(err_msg("account not in instance")) .ok_or(err_msg("account not in instance"))
} }
@ -522,7 +522,7 @@ pub fn instance_get_open(tx: &mut Transaction) -> Result<Instance, Error> {
return Ok(instance); return Ok(instance);
} }
pub fn instance_lobby(params: InstanceLobbyParams, tx: &mut Transaction, account: &Account) -> Result<Instance, Error> { pub fn instance_new(params: InstanceLobbyParams, tx: &mut Transaction, account: &Account) -> Result<Instance, Error> {
let mut instance = Instance::new() let mut instance = Instance::new()
.set_max_players(params.players)? .set_max_players(params.players)?
.set_name(params.name)?; .set_name(params.name)?;
@ -545,8 +545,9 @@ pub fn instance_join(params: InstanceJoinParams, tx: &mut Transaction, account:
return Err(format_err!("incorrect team size. ({:})", 3)); return Err(format_err!("incorrect team size. ({:})", 3));
} }
let player = Player::new(account.id, instance.id, &account.name, cryps); let player = player_create(tx, Player::new(account.id, instance.id, &account.name, cryps), account)?;
instance.add_player(player);
instance.add_player(player)?;
instance_update(tx, instance) instance_update(tx, instance)
} }
@ -560,11 +561,11 @@ pub fn instance_ready_global(tx: &mut Transaction, _account: &Account, player: P
match game_instance_join(tx, player.clone(), g.id) { match game_instance_join(tx, player.clone(), g.id) {
Ok(g) => g, Ok(g) => g,
// if fails make a new one // if fails make a new one
Err(_e) => game_instance_new(tx, player, Uuid::new_v4())?, Err(_e) => game_instance_new(tx, vec![player], Uuid::new_v4(), Uuid::nil())?,
} }
}, },
// if not found make a new one // if not found make a new one
Err(_) => game_instance_new(tx, player, Uuid::new_v4())?, Err(_) => game_instance_new(tx, vec![player], Uuid::new_v4(), Uuid::nil())?,
}; };
// set the current game // set the current game
@ -582,11 +583,48 @@ pub fn instance_ready(params: InstanceReadyParams, tx: &mut Transaction, account
let player_id = instance.account_player(account.id)?.id; let player_id = instance.account_player(account.id)?.id;
instance.player_ready(player_id)?; instance.player_ready(player_id)?;
if let Some(game_id) = instance.current_game(player_id) {
match instance.player_has_pve_game(player_id) {
true => match game_get(tx, game_id) {
Ok(g) => g,
Err(_) => {
let game = instance.bot_vs_player_game(player_id)?;
game_write(&game, tx)?;
game
},
},
false => {
let opponent_id = *instance
.current_round(account.id)
.player_ids
.iter()
.find(|p| **p != account.id)
.expect("could not find opponent");
let a = instance.account_player(account.id)?.clone();
let b = instance.account_player(opponent_id)?.clone();
let teams = vec![a, b];
game_instance_new(tx, teams, game_id, instance.id)?
}
};
}
instance_update(tx, instance) instance_update(tx, instance)
} }
pub fn instance_state(params: InstanceStateParams, tx: &mut Transaction, account: &Account) -> Result<Instance, Error> { pub fn instance_state(params: InstanceStateParams, tx: &mut Transaction, account: &Account) -> Result<RpcResult, Error> {
instance_get(tx, params.instance_id) let mut instance = instance_get(tx, params.instance_id)?;
if let Some(game_id) = instance.current_game(account.id) {
let game = game_get(tx, game_id)?;
if game.phase != Phase::Finish {
return Ok(RpcResult::GameState(game))
}
}
Ok(RpcResult::InstanceState(instance))
} }
// pub fn instance_ready(params: InstanceReadyParams, tx: &mut Transaction, account: &Account) -> Result<Game, Error> { // pub fn instance_ready(params: InstanceReadyParams, tx: &mut Transaction, account: &Account) -> Result<Game, Error> {
@ -602,10 +640,10 @@ pub fn instance_state(params: InstanceStateParams, tx: &mut Transaction, account
// // send game state // // send game state
// match instance.player_ready(player.id) { // match instance.player_ready(player.id) {
// Ok(_) => (), // Ok(_) => (),
// Err(_) => return game_get(tx, instance.current_round(&player).game_id), // Err(_) => return game_get(tx, instance.current_round(player.id).game_id),
// }; // };
// let game_id = instance.current_round(&player).game_id; // let game_id = instance.current_round(player.id).game_id;
// let game = match instance.player_has_pve_game(&player) { // let game = match instance.player_has_pve_game(&player) {
// true => match game_get(tx, game_id) { // true => match game_get(tx, game_id) {
@ -678,8 +716,8 @@ pub fn instance_state(params: InstanceStateParams, tx: &mut Transaction, account
// // now modify the players and write them all // // now modify the players and write them all
// // each player update will also update the instance in db // // each player update will also update the instance in db
// if instance.games_phase_finished() { // if instance.all_games_finished() {
// instance.vbox_phase_start(); // instance.next_round();
// let instance = instance_update(tx, instance)?; // let instance = instance_update(tx, instance)?;
// for player in instance.players // for player in instance.players
@ -711,22 +749,22 @@ mod tests {
let player = Player::new(player_account, instance.id, &"test".to_string(), cryps).set_bot(true); let player = Player::new(player_account, instance.id, &"test".to_string(), cryps).set_bot(true);
let player_id = player.id; let player_id = player.id;
instance.add_player(player); instance.add_player(player).expect("could not add player");
assert_eq!(instance.phase, InstancePhase::Open); assert_eq!(instance.phase, InstancePhase::Lobby);
instance.player_ready(player_id).unwrap(); instance.player_ready(player_id).unwrap();
assert_eq!(instance.phase, InstancePhase::Vbox); assert_eq!(instance.phase, InstancePhase::InProgress);
assert_eq!(instance.rounds[0].len(), 8); assert_eq!(instance.rounds[0].len(), 8);
instance.player_ready(player_id).unwrap(); instance.player_ready(player_id).unwrap();
assert!(instance.games_phase_finished()); assert!(instance.all_games_finished());
instance.vbox_phase_start(); instance.next_round();
instance.player_ready(player_id).unwrap(); instance.player_ready(player_id).unwrap();
instance.vbox_phase_start(); instance.next_round();
instance.player_ready(player_id).unwrap(); instance.player_ready(player_id).unwrap();
@ -754,7 +792,7 @@ mod tests {
let player = Player::new(player_account, instance.id, &"a".to_string(), cryps); let player = Player::new(player_account, instance.id, &"a".to_string(), cryps);
let a_id = player.id; let a_id = player.id;
instance.add_player(player.clone()); instance.add_player(player).expect("could not add player");
assert!(!instance.can_start()); assert!(!instance.can_start());
let player_account = Uuid::new_v4(); let player_account = Uuid::new_v4();
@ -762,14 +800,14 @@ mod tests {
let player = Player::new(player_account, instance.id, &"b".to_string(), cryps); let player = Player::new(player_account, instance.id, &"b".to_string(), cryps);
let b_id = player.id; let b_id = player.id;
instance.add_player(player); instance.add_player(player).expect("could not add player");
assert_eq!(instance.phase, InstancePhase::Open); assert_eq!(instance.phase, InstancePhase::Lobby);
instance.player_ready(a_id).expect("a ready"); instance.player_ready(a_id).expect("a ready");
assert!(!instance.can_start()); assert!(!instance.can_start());
instance.player_ready(b_id).expect("b ready"); instance.player_ready(b_id).expect("b ready");
assert_eq!(instance.phase, InstancePhase::Vbox); assert_eq!(instance.phase, InstancePhase::InProgress);
assert!(!instance.can_start()); assert!(!instance.can_start());
} }

View File

@ -26,7 +26,6 @@ pub struct Score {
pub struct Player { pub struct Player {
pub id: Uuid, pub id: Uuid,
pub instance: Uuid, pub instance: Uuid,
pub account: Uuid,
pub name: String, pub name: String,
pub vbox: Vbox, pub vbox: Vbox,
pub score: Score, pub score: Score,
@ -38,8 +37,7 @@ pub struct Player {
impl Player { impl Player {
pub fn new(account: Uuid, instance: Uuid, name: &String, cryps: Vec<Cryp>) -> Player { pub fn new(account: Uuid, instance: Uuid, name: &String, cryps: Vec<Cryp>) -> Player {
Player { Player {
id: Uuid::new_v4(), id: account,
account,
instance, instance,
name: name.clone(), name: name.clone(),
vbox: Vbox::new(account, instance), vbox: Vbox::new(account, instance),
@ -296,7 +294,7 @@ pub fn player_create(tx: &mut Transaction, player: Player, account: &Account) ->
"; ";
let result = tx let result = tx
.query(query, &[&player.id, &player.instance, &account.id, &player_bytes])?; .query(query, &[&Uuid::new_v4(), &player.instance, &account.id, &player_bytes])?;
let _returned = result.iter().next().expect("no row written"); let _returned = result.iter().next().expect("no row written");

View File

@ -22,7 +22,7 @@ use skill::{Skill};
// use zone::{Zone, zone_create, zone_join, zone_close}; // use zone::{Zone, zone_create, zone_join, zone_close};
use spec::{Spec}; use spec::{Spec};
use player::{Score, player_mm_cryps_set, Player}; use player::{Score, player_mm_cryps_set, Player};
use instance::{Instance, instance_state, instance_lobby, instance_ready, instance_join, instance_scores}; use instance::{Instance, instance_state, instance_new, instance_ready, instance_join, instance_scores};
use vbox::{Var, vbox_accept, vbox_apply, vbox_discard, vbox_combine, vbox_reclaim, vbox_unequip}; use vbox::{Var, vbox_accept, vbox_apply, vbox_discard, vbox_combine, vbox_reclaim, vbox_unequip};
pub struct Rpc; pub struct Rpc;
@ -74,7 +74,7 @@ impl Rpc {
"instance_join" => Rpc::instance_join(data, &mut tx, account.unwrap(), client), "instance_join" => Rpc::instance_join(data, &mut tx, account.unwrap(), client),
"instance_ready" => Rpc::instance_ready(data, &mut tx, account.unwrap(), client), "instance_ready" => Rpc::instance_ready(data, &mut tx, account.unwrap(), client),
"instance_lobby" => Rpc::instance_lobby(data, &mut tx, account.unwrap(), client), "instance_new" => Rpc::instance_new(data, &mut tx, account.unwrap(), client),
"instance_scores" => Rpc::instance_scores(data, &mut tx, account.unwrap(), client), "instance_scores" => Rpc::instance_scores(data, &mut tx, account.unwrap(), client),
"instance_state" => Rpc::instance_state(data, &mut tx, account.unwrap(), client), "instance_state" => Rpc::instance_state(data, &mut tx, account.unwrap(), client),
@ -217,12 +217,12 @@ impl Rpc {
}) })
} }
fn instance_lobby(data: Vec<u8>, tx: &mut Transaction, account: Account, _client: &mut WebSocket<TcpStream>) -> Result<RpcResponse, Error> { fn instance_new(data: Vec<u8>, tx: &mut Transaction, account: Account, _client: &mut WebSocket<TcpStream>) -> Result<RpcResponse, Error> {
let msg = from_slice::<InstanceLobbyMsg>(&data).or(Err(err_msg("invalid params")))?; let msg = from_slice::<InstanceLobbyMsg>(&data).or(Err(err_msg("invalid params")))?;
let response = RpcResponse { let response = RpcResponse {
method: "instance_state".to_string(), method: "instance_state".to_string(),
params: RpcResult::InstanceState(instance_lobby(msg.params, tx, &account)?) params: RpcResult::InstanceState(instance_new(msg.params, tx, &account)?)
}; };
return Ok(response); return Ok(response);
@ -274,13 +274,17 @@ impl Rpc {
fn instance_state(data: Vec<u8>, tx: &mut Transaction, account: Account, _client: &mut WebSocket<TcpStream>) -> Result<RpcResponse, Error> { fn instance_state(data: Vec<u8>, tx: &mut Transaction, account: Account, _client: &mut WebSocket<TcpStream>) -> Result<RpcResponse, Error> {
let msg = from_slice::<InstanceStateMsg>(&data).or(Err(err_msg("invalid params")))?; let msg = from_slice::<InstanceStateMsg>(&data).or(Err(err_msg("invalid params")))?;
match instance_state(msg.params, tx, &account)? {
let response = RpcResponse { RpcResult::GameState(p) => Ok(RpcResponse {
method: "instance_state".to_string(), method: "game_state".to_string(),
params: RpcResult::InstanceState(instance_state(msg.params, tx, &account)?) params: RpcResult::GameState(p),
}; }),
RpcResult::InstanceState(p) => Ok(RpcResponse {
return Ok(response); method: "instance_state".to_string(),
params: RpcResult::InstanceState(p),
}),
_ => Err(err_msg("unhandled instance state"))
}
} }
fn player_mm_cryps_set(data: Vec<u8>, tx: &mut Transaction, account: Account, _client: &mut WebSocket<TcpStream>) -> Result<RpcResponse, Error> { fn player_mm_cryps_set(data: Vec<u8>, tx: &mut Transaction, account: Account, _client: &mut WebSocket<TcpStream>) -> Result<RpcResponse, Error> {