This commit is contained in:
ntr 2018-10-22 20:00:51 +11:00
parent 89ffc2fca3
commit af9f2c479d
11 changed files with 263 additions and 54 deletions

View File

@ -3,6 +3,7 @@ const preact = require('preact');
const ItemListContainer = require('./item.list.container');
const CrypSpawnContainer = require('./cryp.spawn.container');
const GameJoinButton = require('./game.join.button');
const CrypListContainer = require('./cryp.list.container');
const GameContainer = require('./game.container');
@ -12,6 +13,7 @@ function renderBody() {
<div className="column is-4">
<div className="column">
<CrypSpawnContainer />
<GameJoinButton />
</div>
<div className="column">
<div className="columns">

View File

@ -9,6 +9,10 @@ const addState = connect(
return ws.sendGamePve(crypId);
}
function sendGamePvp(crypIds) {
return ws.sendGamePvp(crypIds);
}
function sendItemUse(targetId) {
if (activeItem) {
return ws.sendItemUse(activeItem, targetId);
@ -16,7 +20,7 @@ const addState = connect(
return false;
}
return { cryps, sendGamePve, activeItem, sendItemUse };
return { cryps, sendGamePve, sendGamePvp, activeItem, sendItemUse };
}
);

View File

@ -3,7 +3,7 @@ const preact = require('preact');
const { stringSort } = require('./../utils');
const nameSort = stringSort('name');
function CrypList({ cryps, activeItem, sendGamePve, sendItemUse }) {
function CrypList({ cryps, activeItem, sendGamePve, sendGamePvp, sendItemUse }) {
if (!cryps) return <div>not ready</div>;
const crypPanels = cryps.sort(nameSort).map(cryp => (
@ -38,6 +38,15 @@ function CrypList({ cryps, activeItem, sendGamePve, sendItemUse }) {
onClick={() => sendGamePve(cryp.id)}>
Start PVE
</button>
<button
className="button is-dark"
type="submit"
disabled={cryp.hp.value === 0}
onClick={() => sendGamePvp([cryp.id])}>
PVP
</button>
</div>
));
return (

View File

@ -0,0 +1,42 @@
const preact = require('preact');
const { connect } = require('preact-redux');
const addState = connect(
function receiveState(state) {
const { ws, cryps } = state;
function sendGameJoin(gameId) {
return ws.sendGameJoin(gameId, [cryps[0].id]);
}
return { account: state.account, sendGameJoin };
}
);
function GameJoinButton({ account, sendGameJoin }) {
let gameId = '';
if (!account) return <div>...</div>;
return (
<div className="columns">
<div className="column">
<input
className="input"
type="text"
placeholder="gameId"
onChange={e => (gameId = e.target.value)}
/>
</div>
<div className="column is-4">
<button
className="button is-dark is-fullwidth"
type="submit"
onClick={() => sendGameJoin(gameId)}>
Join Game
</button>
</div>
</div>
);
}
module.exports = addState(GameJoinButton);

View File

@ -126,8 +126,9 @@ function GamePanel(props) {
<div className="tile is-ancestor">
<div className="tile is-parent is-vertical">
<div className="tile is-parent">{otherTeams.map(OpponentTeam)}</div>
<div className="tile">
<div className="title is-1">{game.phase}</div>
<div className="tile is-child">
<div className="title">{game.phase}</div>
<div className="subtitle">{game.id}</div>
</div>
<div className="tile">
{incoming}

View File

@ -24,7 +24,7 @@ function createSocket(store) {
// Connection opened
ws.addEventListener('open', function wsOpen(event) {
send({ method: 'account_login', params: { name: 'ntr', password: 'grepgrepgrep' } });
// send({ method: 'account_login', params: { name: 'ntr', password: 'grepgrepgrep' } });
});
// Listen for messages
@ -113,8 +113,19 @@ function createSocket(store) {
send({ method: 'game_pve', params: { id } });
}
function sendGamePvp(crypIds) {
send({ method: 'game_pvp', params: { cryp_ids: crypIds } });
}
function sendGameJoin(gameId, crypIds) {
send({ method: 'game_join', params: { game_id: gameId, cryp_ids: crypIds } });
}
function sendGameSkill(gameId, crypId, targetTeamId, skill) {
send({ method: 'game_skill', params: { game_id: gameId, cryp_id: crypId, target_team_id: targetTeamId, skill } });
send({
method: 'game_skill',
params: { game_id: gameId, cryp_id: crypId, target_team_id: targetTeamId, skill }
});
store.dispatch(actions.setActiveSkill(null));
}
@ -164,6 +175,8 @@ function createSocket(store) {
sendAccountLogin,
sendAccountRegister,
sendGamePve,
sendGamePvp,
sendGameJoin,
sendGameSkill,
sendGameTarget,
sendCrypSpawn,

View File

@ -30,8 +30,6 @@
* teach cyps skills
* can you attack yourself?
* fetch existing battles
* check for cryp skill already used
* check for cryp skill ownership
* check for game participation
* write players row for every team+cryp added
* return results<>

View File

@ -219,7 +219,7 @@ pub fn cryp_get(tx: &mut Transaction, id: Uuid, account_id: Uuid) -> Result<Cryp
let result = tx
.query(query, &[&id, &account_id])?;
let result = result.iter().next().ok_or(err_msg("cryp not found"))?;
let result = result.iter().next().ok_or(format_err!("cryp {:} not found", id))?;
let cryp_bytes: Vec<u8> = result.get(0);
let cryp = from_slice::<Cryp>(&cryp_bytes)?;

View File

@ -8,8 +8,8 @@ use failure::Error;
use failure::err_msg;
use account::Account;
use rpc::{GameSkillParams, GamePveParams, GameTargetParams};
use cryp::{Cryp, CrypStat, Stat};
use rpc::{GameSkillParams, GamePveParams, GamePvpParams, GameTargetParams, GameJoinParams};
use cryp::{Cryp, cryp_get};
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
pub struct Roll {
@ -142,7 +142,7 @@ pub struct Game {
}
impl Game {
pub fn new() -> Game {
fn new() -> Game {
return Game {
id: Uuid::new_v4(),
team_size: 0,
@ -154,38 +154,40 @@ impl Game {
};
}
pub fn set_team_num(&mut self, size: usize) -> &mut Game {
fn set_team_num(&mut self, size: usize) -> &mut Game {
self.team_num = size;
self
}
pub fn set_team_size(&mut self, size: usize) -> &mut Game {
fn set_team_size(&mut self, size: usize) -> &mut Game {
self.team_size = size;
self
}
pub fn set_pve(&mut self, pve: bool) -> &mut Game {
fn set_pve(&mut self, pve: bool) -> &mut Game {
self.is_pve = pve;
self
}
pub fn add_team(&mut self, team: Team) -> &mut Game {
// check team not already in
fn add_team(&mut self, team: Team) -> Result<&mut Game, Error> {
if self.teams.len() == self.team_num {
panic!("maximum number of teams");
return Err(err_msg("maximum number of teams"));
}
self.teams.push(team);
self
Ok(self)
}
// handle missing team properly
pub fn team_by_id(&mut self, id: Uuid) -> &mut Team {
fn team_by_id(&mut self, id: Uuid) -> &mut Team {
match self.teams.iter_mut().find(|t| t.id == id) {
Some(t) => t,
None => panic!("id not in game {:?}", id),
}
}
pub fn cryp_by_id(&mut self, id: Uuid) -> &mut Cryp {
fn cryp_by_id(&mut self, id: Uuid) -> &mut Cryp {
for team in self.teams.iter_mut() {
let in_team = team.cryp_by_id(id);
if in_team.is_some() {
@ -195,16 +197,16 @@ impl Game {
panic!("cryp not in game");
}
fn update_team(&mut self, updated: Team) -> &mut Game {
match self.teams.iter().position(|t| t.id == updated.id) {
Some(index) => {
self.teams.remove(index);
self.teams.push(updated);
self
}
None => panic!("team not in game"),
}
}
// fn update_team(&mut self, updated: Team) -> &mut Game {
// match self.teams.iter().position(|t| t.id == updated.id) {
// Some(index) => {
// self.teams.remove(index);
// self.teams.push(updated);
// self
// }
// None => panic!("team not in game"),
// }
// }
fn update_cryp(&mut self, updated: Cryp) -> &mut Game {
for team in self.teams.iter_mut() {
@ -217,16 +219,16 @@ impl Game {
self
}
pub fn can_start(&self) -> bool {
fn can_start(&self) -> bool {
self.teams.len() == self.team_num
}
pub fn start(&mut self) -> &mut Game {
fn start(&mut self) -> &mut Game {
self.skill_phase_start();
self
}
pub fn skill_phase_start(&mut self) -> &mut Game {
fn skill_phase_start(&mut self) -> &mut Game {
if ![Phase::Start, Phase::Damage].contains(&self.phase) {
panic!("game not in damage or start phase");
}
@ -261,7 +263,7 @@ impl Game {
// skills can target any team, but we have to check if the caller is the owner of the cryp
// and that the cryp has the skill they are trying to add
pub fn add_skill(&mut self, team_id: Uuid, cryp_id: Uuid, target_team_id: Uuid, skill: Skill) -> Result<Uuid, Error> {
fn add_skill(&mut self, team_id: Uuid, cryp_id: Uuid, target_team_id: Uuid, skill: Skill) -> Result<Uuid, Error> {
if self.phase != Phase::Skill {
return Err(err_msg("game not in skill phase"));
}
@ -291,12 +293,12 @@ impl Game {
return Ok(skill.id);
}
pub fn skill_phase_finished(&self) -> bool {
fn skill_phase_finished(&self) -> bool {
self.teams.iter().all(|t| t.skills.len() == self.team_size)
}
// move all skills into their target team's targets list
pub fn target_phase_start(&mut self) -> &mut Game {
fn target_phase_start(&mut self) -> &mut Game {
if self.phase != Phase::Skill {
panic!("game not in skill phase");
}
@ -335,7 +337,7 @@ impl Game {
// targets can only be added by the owner of the team
pub fn add_target(&mut self, team_id: Uuid, cryp_id: Uuid, skill_id: Uuid) -> Result<&mut GameSkill, Error> {
fn add_target(&mut self, team_id: Uuid, cryp_id: Uuid, skill_id: Uuid) -> Result<&mut GameSkill, Error> {
// whose team is this?
let team = self.team_by_id(team_id);
@ -350,13 +352,13 @@ impl Game {
Ok(skill.set_target(cryp_id))
}
pub fn target_phase_finished(&self) -> bool {
fn target_phase_finished(&self) -> bool {
self.teams.iter().all(|t| t.incoming.iter().all(|i| i.target_cryp_id.is_some()))
}
// requires no input
// just do it
pub fn damage_phase_start(&mut self) -> &mut Game {
fn damage_phase_start(&mut self) -> &mut Game {
if self.phase != Phase::Target {
panic!("game not in target phase");
}
@ -392,7 +394,7 @@ impl Game {
self
}
pub fn is_finished(&self) -> bool {
fn is_finished(&self) -> bool {
self.teams.iter().any(|t| t.cryps.iter().all(|c| c.is_ko()))
}
@ -433,7 +435,9 @@ pub fn game_skill(params: GameSkillParams, tx: &mut Transaction, account: &Accou
game.target_phase_start();
}
return game_write(game, tx);
game_update(&game, tx)?;
Ok(game)
}
pub fn game_target(params: GameTargetParams, tx: &mut Transaction, account: &Account) -> Result<Game, Error> {
@ -461,7 +465,9 @@ pub fn game_target(params: GameTargetParams, tx: &mut Transaction, account: &Acc
game.damage_phase_start();
}
return game_write(game, tx);
game_update(&game, tx)?;
Ok(game)
}
pub fn game_new(game: &Game, tx: &mut Transaction) -> Result<(), Error> {
@ -476,13 +482,35 @@ pub fn game_new(game: &Game, tx: &mut Transaction) -> Result<(), Error> {
let result = tx
.query(query, &[&game.id, &game_bytes])?;
let _returned = result.iter().next().expect("no row returned");
result.iter().next().ok_or(format_err!("no game written"))?;
println!("{:?} wrote game", game.id);
return Ok(());
}
pub fn game_get(tx: &mut Transaction, id: Uuid) -> Result<Game, Error> {
let query = "
SELECT *
FROM games
WHERE id = $1
";
let result = tx
.query(query, &[&id])?;
let returned = match result.iter().next() {
Some(row) => row,
None => return Err(err_msg("game not found")),
};
// tells from_slice to cast into a cryp
let game_bytes: Vec<u8> = returned.get("data");
let game = from_slice::<Game>(&game_bytes)?;
return Ok(game);
}
/// write a row for every cryp in a team when added to a battle
pub fn players_write(team: &Team, game_id: Uuid, tx: &mut Transaction) -> Result<(), Error> {
// pve
@ -508,7 +536,7 @@ pub fn players_write(team: &Team, game_id: Uuid, tx: &mut Transaction) -> Result
return Ok(());
}
pub fn game_write(game: Game, tx: &mut Transaction) -> Result<Game, Error> {
pub fn game_update(game: &Game, tx: &mut Transaction) -> Result<(), Error> {
let game_bytes = to_vec(&game)?;
let query = "
@ -521,11 +549,11 @@ pub fn game_write(game: Game, tx: &mut Transaction) -> Result<Game, Error> {
let result = tx
.query(query, &[&game_bytes, &game.id])?;
let returned = result.iter().next().expect("no row returned");
result.iter().next().ok_or(format_err!("game {:?} could not be written", game))?;
println!("{:?} wrote game", game.id);
return Ok(game);
return Ok(());
}
fn generate_mob(plr: &Cryp) -> Cryp {
@ -589,8 +617,8 @@ pub fn game_pve(params: GamePveParams, tx: &mut Transaction, account: &Account)
.set_cryps(vec![mob]);
game
.add_team(plr_team)
.add_team(mob_team);
.add_team(plr_team)?
.add_team(mob_team)?;
game.start();
@ -601,6 +629,59 @@ pub fn game_pve(params: GamePveParams, tx: &mut Transaction, account: &Account)
Ok(game)
}
pub fn game_pvp(params: GamePvpParams, tx: &mut Transaction, account: &Account) -> Result<Game, Error> {
let cryps = params.cryp_ids
.iter()
.map(|id| cryp_get(tx, *id, account.id))
.collect::<Result<Vec<Cryp>, Error>>()?;
// create the game
let mut game = Game::new();
let game_id = game.id;
game
.set_pve(false)
.set_team_num(2)
.set_team_size(1);
// create the initiators team
let mut team = Team::new(account.id);
team.set_cryps(cryps);
game.add_team(team)?;
// persist
game_new(&game, tx)?;
players_write(&game.team_by_id(account.id), game_id, tx)?;
Ok(game)
}
pub fn game_join(params: GameJoinParams, tx: &mut Transaction, account: &Account) -> Result<Game, Error> {
let mut game = game_get(tx, params.game_id)?;
let game_id = game.id;
let cryps = params.cryp_ids
.iter()
.map(|id| cryp_get(tx, *id, account.id))
.collect::<Result<Vec<Cryp>, Error>>()?;
let mut team = Team::new(account.id);
team.set_cryps(cryps);
game.add_team(team)?;
if game.can_start() {
game.start();
}
game_update(&game, tx)?;
players_write(&game.team_by_id(account.id), game_id, tx)?;
Ok(game)
}
#[cfg(test)]
mod tests {
use game::*;
@ -640,8 +721,8 @@ mod tests {
.set_cryps(vec![y]);
game
.add_team(x_team)
.add_team(y_team);
.add_team(x_team).unwrap()
.add_team(y_team).unwrap();
assert!(game.can_start());
@ -656,8 +737,8 @@ mod tests {
println!("{:?}", game);
game.add_target(x_team_id, x_id, y_attack_id.unwrap());
game.add_target(y_team_id, y_id, x_attack_id.unwrap());
game.add_target(x_team_id, x_id, y_attack_id.unwrap()).unwrap();
game.add_target(y_team_id, y_id, x_attack_id.unwrap()).unwrap();
assert!(game.target_phase_finished());

View File

@ -14,7 +14,7 @@ extern crate serde_cbor;
#[macro_use]
extern crate serde_derive;
extern crate failure;
#[macro_use] extern crate failure;
// #[macro_use] extern crate failure_derive;
mod cryp;

View File

@ -11,7 +11,7 @@ use failure::err_msg;
use net::Db;
use cryp::{Cryp, cryp_spawn};
use game::{Game, Skill, game_pve, game_skill, game_target};
use game::{Game, Skill, game_pve, game_pvp, game_join, game_skill, game_target};
use account::{Account, account_create, account_login, account_from_token, account_cryps};
use item::{Item, items_list, item_use};
@ -39,6 +39,8 @@ impl Rpc {
let response = match v.method.as_ref() {
"cryp_spawn" => Rpc::cryp_spawn(data, &mut tx, account, client),
"game_pve" => Rpc::game_pve(data, &mut tx, account, client),
"game_pvp" => Rpc::game_pvp(data, &mut tx, account, client),
"game_join" => Rpc::game_join(data, &mut tx, account, client),
"game_skill" => Rpc::game_skill(data, &mut tx, account, client),
"game_target" => Rpc::game_target(data, &mut tx, account, client),
"account_create" => Rpc::account_create(data, &mut tx, account, client),
@ -87,6 +89,40 @@ impl Rpc {
return Ok(game_response);
}
fn game_pvp(data: Vec<u8>, tx: &mut Transaction, account: Option<Account>, _client: &mut WebSocket<TcpStream>) -> Result<RpcResponse, Error> {
let a = match account {
Some(a) => a,
None => return Err(err_msg("auth required")),
};
let msg = from_slice::<GamePvpMsg>(&data).or(Err(err_msg("invalid params")))?;
let game_response = RpcResponse {
method: "game_state".to_string(),
params: RpcResult::GameState(game_pvp(msg.params, tx, &a)?)
};
return Ok(game_response);
}
fn game_join(data: Vec<u8>, tx: &mut Transaction, account: Option<Account>, _client: &mut WebSocket<TcpStream>) -> Result<RpcResponse, Error> {
let a = match account {
Some(a) => a,
None => return Err(err_msg("auth required")),
};
let msg = from_slice::<GameJoinMsg>(&data).or(Err(err_msg("invalid params")))?;
let game_response = RpcResponse {
method: "game_state".to_string(),
params: RpcResult::GameState(game_join(msg.params, tx, &a)?)
};
return Ok(game_response);
}
fn game_skill(data: Vec<u8>, tx: &mut Transaction, account: Option<Account>, _client: &mut WebSocket<TcpStream>) -> Result<RpcResponse, Error> {
let a = match account {
Some(a) => a,
@ -250,6 +286,29 @@ pub struct GamePveParams {
pub id: Uuid,
}
#[derive(Debug,Clone,Serialize,Deserialize)]
struct GamePvpMsg {
method: String,
params: GamePvpParams,
}
#[derive(Debug,Clone,Serialize,Deserialize)]
pub struct GamePvpParams {
pub cryp_ids: Vec<Uuid>,
}
#[derive(Debug,Clone,Serialize,Deserialize)]
struct GameJoinMsg {
method: String,
params: GameJoinParams,
}
#[derive(Debug,Clone,Serialize,Deserialize)]
pub struct GameJoinParams {
pub game_id: Uuid,
pub cryp_ids: Vec<Uuid>,
}
#[derive(Debug,Clone,Serialize,Deserialize)]
struct GameTargetMsg {
method: String,