player box

This commit is contained in:
ntr 2019-09-08 19:48:48 +10:00
parent a794705fbe
commit ac3801ae38
8 changed files with 195 additions and 94 deletions

View File

@ -0,0 +1,37 @@
@import 'colours.less';
.player-box {
display: grid;
grid-template-areas:
"score"
"img"
"ctrl";
&.top {
grid-template-areas:
"ctrl"
"img"
"score";
.img {
display: flex;
flex-flow: column;
justify-content: flex-end;
}
}
grid-template-rows: min-content 1fr min-content;
.score {
grid-area: score;
}
.img {
grid-area: img;
}
.ctrl {
grid-area: ctrl;
}
}

View File

@ -7,6 +7,7 @@ require('./assets/styles/controls.less');
require('./assets/styles/instance.less'); require('./assets/styles/instance.less');
require('./assets/styles/vbox.less'); require('./assets/styles/vbox.less');
require('./assets/styles/game.less'); require('./assets/styles/game.less');
require('./assets/styles/player.less');
require('./assets/styles/styles.mobile.css'); require('./assets/styles/styles.mobile.css');
require('./assets/styles/instance.mobile.css'); require('./assets/styles/instance.mobile.css');

View File

@ -3,8 +3,7 @@ const { connect } = require('preact-redux');
const actions = require('../actions'); const actions = require('../actions');
// dunno if this is a good idea const PlayerBox = require('./player.box');
// bashing together instance and game to save having two panels
const addState = connect( const addState = connect(
function receiveState(state) { function receiveState(state) {
@ -44,35 +43,6 @@ const addState = connect(
} }
); );
function scoreboard(game, player, isOpponent) {
const text = game.phase === 'Finished'
? player.wins > game.rounds / 2 && 'Winner'
: '';
const classes =`scoreboard ${player.ready ? 'ready' : ''}`;
const body = isOpponent
? (
<tbody>
<tr><td>{player.name}</td></tr>
<tr><td>{player.wins} / {player.losses}</td></tr>
<tr><td>{player.ready ? 'ready' : ''}</td></tr>
</tbody>
) : (
<tbody>
<tr><td>{player.ready ? 'ready' : ''}</td></tr>
<tr><td>{player.wins} / {player.losses}</td></tr>
<tr><td>{player.name}</td></tr>
</tbody>
);
return (
<table class={classes}>
{body}
</table>
);
}
function Controls(args) { function Controls(args) {
const { const {
account, account,
@ -126,9 +96,9 @@ function Controls(args) {
<aside class="controls"> <aside class="controls">
{timer} {timer}
<div class="controls"> <div class="controls">
{scoreboard(game, opponent, true)} <PlayerBox player={opponent}/>
{game.phase === 'Finish' ? quitBtn : readyBtn} {game.phase === 'Finish' ? quitBtn : readyBtn}
{scoreboard(game, player)} <PlayerBox player={player} isPlayer={true}/>
</div> </div>
</aside> </aside>
); );

View File

@ -1,6 +1,10 @@
const preact = require('preact'); const preact = require('preact');
const { connect } = require('preact-redux'); const { connect } = require('preact-redux');
const actions = require('../actions');
const PlayerBox = require('./player.box');
const addState = connect( const addState = connect(
function receiveState(state) { function receiveState(state) {
const { const {
@ -23,42 +27,23 @@ const addState = connect(
animating, animating,
}; };
}, },
function receiveDispatch(dispatch) {
function leave() {
dispatch(actions.setNav('play'));
dispatch(actions.setGame(null));
dispatch(actions.setInstance(null));
}
return { leave };
}
); );
function scoreboard(instance, player, isOpponent) {
const text = instance.phase === 'Finished'
? player.wins > instance.rounds / 2 && 'Winner'
: '';
const classes =`scoreboard ${player.ready ? 'ready' : ''}`;
const body = isOpponent
? (
<tbody>
<tr><td>{player.name}</td></tr>
<tr><td>{player.wins} / {player.losses}</td></tr>
<tr><td>{player.ready ? 'ready' : ''}</td></tr>
</tbody>
) : (
<tbody>
<tr><td>{player.ready ? 'ready' : ''}</td></tr>
<tr><td>{player.wins} / {player.losses}</td></tr>
<tr><td>{player.name}</td></tr>
</tbody>
);
return (
<table class={classes}>
{body}
</table>
);
}
function Controls(args) { function Controls(args) {
const { const {
account, account,
instance, instance,
sendReady, sendReady,
leave,
} = args; } = args;
if (!instance) return false; if (!instance) return false;
@ -96,9 +81,9 @@ function Controls(args) {
<aside> <aside>
{timer} {timer}
<div class="controls"> <div class="controls">
{scoreboard(instance, opponent, true)} <PlayerBox player={opponent} />
<button class="ready" onClick={() => sendReady()}>Ready</button> <button class="ready" onClick={() => sendReady()}>Ready</button>
{scoreboard(instance, player)} <PlayerBox player={player} isPlayer={true} leave={leave}/>
</div> </div>
</aside> </aside>
); );

View File

@ -0,0 +1,47 @@
const preact = require('preact');
function Scoreboard(args) {
const {
isPlayer,
ready,
player,
leave,
} = args;
let scoreText = () => {
if (player.score === 'Zero') return '▫▫▫▫';
if (player.score === 'One') return '■▫▫▫';
if (player.score === 'Two') return '■■▫▫';
if (player.score === 'Three') return '■■■▫';
if (player.score === 'Adv') return '■■■+';
if (player.score === 'Win') return '■■■■';
return '';
};
if (!isPlayer) {
return (
<div class={`player-box top ${player.ready ? 'ready' : ''}`}>
<div class="ctrl">&nbsp;</div>
<div class="score">{scoreText()}</div>
<div class="img">
<div>{player.name}</div>
</div>
</div>
);
}
return (
<div class={`player-box bottom ${player.ready ? 'ready' : ''}`}>
<div class="score">{scoreText()}</div>
<div class="img">
<div>{player.name}</div>
</div>
<div class="ctrl">
{leave ? <button onClick={leave}>Leave</button> : null}
</div>
</div>
);
}
module.exports = Scoreboard;

View File

@ -14,7 +14,7 @@ use chrono::Duration;
use account::Account; use account::Account;
use account; use account;
use events::{EventsTx, Event}; use events::{EventsTx, Event};
use player::{Player, player_create}; use player::{Player, Score, player_create};
use construct::{Construct, construct_get}; use construct::{Construct, construct_get};
use mob::{bot_player, instance_mobs}; use mob::{bot_player, instance_mobs};
use game::{Game, Phase, game_get, game_write}; use game::{Game, Phase, game_get, game_write};
@ -318,14 +318,14 @@ impl Instance {
// tennis // tennis
for player in self.players.iter() { for player in self.players.iter() {
if player.wins >= 4 && player.wins >= player.losses + 2 { if player.score == Score::Win {
self.winner = Some(player.id); self.winner = Some(player.id);
return true; return true;
} }
} }
// both players afk // both players afk
if self.players.iter().all(|p| p.wins == 0) { if self.players.iter().all(|p| p.score == Score::Zero) {
return true; return true;
} }
@ -403,12 +403,25 @@ impl Instance {
None => return Ok(self.finish()), None => return Ok(self.finish()),
}; };
for player in game.players.iter() { {
let mut player = self.account_player(player.id)?; let loser = self.players.iter()
match player.id == winner_id { .find(|p| p.id != winner_id)
true => player.add_win(), .map(|p| p.score)
false => player.add_loss(), .unwrap();
};
let winner = self.players.iter_mut()
.find(|p| p.id == winner_id)
.unwrap();
winner.score = winner.score.add_win(&loser);
}
{
let loser = self.players.iter_mut()
.find(|p| p.id != winner_id)
.unwrap();
loser.score = loser.score.add_loss();
} }
if self.all_games_finished() { if self.all_games_finished() {

View File

@ -14,6 +14,43 @@ use effect::{Effect};
const DISCARD_COST: u16 = 2; const DISCARD_COST: u16 = 2;
#[derive(Debug,Copy,Clone,Serialize,Deserialize,Eq,PartialEq)]
pub enum Score {
Zero,
One,
Two,
Three,
Adv,
Win,
Lose,
}
impl Score {
pub fn add_win(self, opp: &Score) -> Score {
match self {
Score::Zero => Score::One,
Score::One => Score::Two,
Score::Two => Score::Three,
Score::Three => match opp {
Score::Adv => Score::Three,
Score::Three => Score::Adv,
_ => Score::Win,
}
Score::Adv => Score::Win,
_ => panic!("faulty score increment {:?}", self),
}
}
pub fn add_loss(self) -> Score {
match self {
Score::Adv => Score::Three,
_ => self,
}
}
}
#[derive(Debug,Clone,Serialize,Deserialize)] #[derive(Debug,Clone,Serialize,Deserialize)]
pub struct Player { pub struct Player {
pub id: Uuid, pub id: Uuid,
@ -23,8 +60,7 @@ pub struct Player {
pub bot: bool, pub bot: bool,
pub ready: bool, pub ready: bool,
pub warnings: u8, pub warnings: u8,
pub wins: u8, pub score: Score,
pub losses: u8,
} }
impl Player { impl Player {
@ -33,12 +69,11 @@ impl Player {
id: account, id: account,
name: name.clone(), name: name.clone(),
vbox: Vbox::new(), vbox: Vbox::new(),
wins: 0,
losses: 0,
constructs, constructs,
bot: false, bot: false,
ready: false, ready: false,
warnings: 0, warnings: 0,
score: Score::Zero,
} }
} }
@ -64,21 +99,6 @@ impl Player {
self self
} }
pub fn add_win(&mut self) -> &mut Player {
self.wins += 1;
let win_bonus = 12 + 6 * (self.wins + self.losses);
self.vbox.balance_add(win_bonus.into());
self
}
pub fn add_loss(&mut self) -> &mut Player {
self.losses += 1;
let loss_bonus = 12 + 6 * (self.wins + self.losses);
self.vbox.balance_add(loss_bonus.into());
self
}
pub fn construct_get(&mut self, id: Uuid) -> Result<&mut Construct, Error> { pub fn construct_get(&mut self, id: Uuid) -> Result<&mut Construct, Error> {
self.constructs.iter_mut().find(|c| c.id == id).ok_or(err_msg("construct not found")) self.constructs.iter_mut().find(|c| c.id == id).ok_or(err_msg("construct not found"))
} }
@ -336,4 +356,32 @@ mod tests {
assert!(player.constructs.iter().all(|c| c.skills.len() >= 1)); assert!(player.constructs.iter().all(|c| c.skills.len() >= 1));
} }
#[test]
fn player_score_test() {
let player_account = Uuid::new_v4();
let constructs = instance_mobs(player_account);
let mut player = Player::new(player_account, &"test".to_string(), constructs).set_bot(true);
player.score = player.score.add_win(&Score::Zero);
player.score = player.score.add_win(&Score::Zero);
player.score = player.score.add_win(&Score::Zero);
assert_eq!(player.score, Score::Three); // 40 / 0
player.score = player.score.add_loss(); // adv -> deuce
assert_eq!(player.score, Score::Three);
player.score = player.score.add_loss(); // adv -> deuce
assert_eq!(player.score, Score::Three);
player.score = player.score.add_win(&Score::Adv); // opp adv -> stays deuce
assert_eq!(player.score, Score::Three);
player.score = player.score.add_win(&Score::Three);
assert_eq!(player.score, Score::Adv);
player.score = player.score.add_win(&Score::Three);
assert_eq!(player.score, Score::Win);
}
} }

View File

@ -2113,8 +2113,8 @@ mod tests {
.learn(Skill::HealPlus); .learn(Skill::HealPlus);
purge(&mut x, &mut y, vec![], Skill::Purge); purge(&mut x, &mut y, vec![], Skill::Purge);
// 2 turns at lvl 1 // current turn + 2 turns at lvl 1
assert!(y.effects.iter().any(|e| e.effect == Effect::Purge && e.duration == 2)); assert!(y.effects.iter().any(|e| e.effect == Effect::Purge && e.duration == 3));
assert!(y.disabled(Skill::Heal).is_some()); assert!(y.disabled(Skill::Heal).is_some());
} }
} }