Merge branch 'release/1.4.0'
This commit is contained in:
commit
f41c22af16
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mnml-client",
|
"name": "mnml-client",
|
||||||
"version": "1.3.2",
|
"version": "1.4.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
37
client/assets/styles/player.less
Normal file
37
client/assets/styles/player.less
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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');
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mnml-client",
|
"name": "mnml-client",
|
||||||
"version": "1.3.2",
|
"version": "1.4.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -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() {
|
||||||
function scoreboard(instance, player, isOpponent) {
|
dispatch(actions.setNav('play'));
|
||||||
const text = instance.phase === 'Finished'
|
dispatch(actions.setGame(null));
|
||||||
? player.wins > instance.rounds / 2 && 'Winner'
|
dispatch(actions.setInstance(null));
|
||||||
: '';
|
|
||||||
|
|
||||||
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>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return { leave };
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
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>
|
||||||
);
|
);
|
||||||
|
|||||||
47
client/src/components/player.box.jsx
Normal file
47
client/src/components/player.box.jsx
Normal 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"> </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;
|
||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mnml-ops",
|
"name": "mnml-ops",
|
||||||
"version": "1.3.2",
|
"version": "1.4.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "mnml"
|
name = "mnml"
|
||||||
version = "1.3.2"
|
version = "1.4.0"
|
||||||
authors = ["ntr <ntr@smokestack.io>"]
|
authors = ["ntr <ntr@smokestack.io>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@ -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() {
|
||||||
|
|||||||
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user