clientside rounds changes
This commit is contained in:
parent
1f5fba80f1
commit
6e513e3da7
@ -446,7 +446,7 @@ header {
|
||||
flex: 1 1 50%;
|
||||
}
|
||||
|
||||
.create-form input {
|
||||
.create-form .login-input {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
|
||||
@ -23,8 +23,8 @@ document.fonts.load('16pt "Jura"').then(() => {
|
||||
setupKeys(store);
|
||||
|
||||
const ws = createSocket(events);
|
||||
store.dispatch(actions.setWs(ws));
|
||||
ws.connect();
|
||||
events.setWs(ws);
|
||||
|
||||
const App = () => (
|
||||
<Provider store={store}>
|
||||
|
||||
@ -65,7 +65,7 @@ function Instance(args) {
|
||||
<tr key={i}
|
||||
className={p.ready ? 'ready' : ''}>
|
||||
<td>{p.name}</td>
|
||||
<td>{p.score.wins} / {p.score.losses}</td>
|
||||
<td>{p.wins} / {p.losses}</td>
|
||||
<td>{pText}</td>
|
||||
</tr>
|
||||
);
|
||||
|
||||
@ -6,9 +6,9 @@ const addState = connect(
|
||||
function receiveState(state) {
|
||||
const { ws, team } = state;
|
||||
|
||||
function sendInstanceNew(sConstructs, name, players) {
|
||||
function sendInstanceNew(sConstructs, name, pve) {
|
||||
if (sConstructs.length) {
|
||||
return ws.sendInstanceNew(sConstructs, name, players);
|
||||
return ws.sendInstanceNew(sConstructs, name, pve);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -24,19 +24,19 @@ class InstanceCreateForm extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = { players: 2, name: '' };
|
||||
this.state = { pve: false, name: '' };
|
||||
|
||||
const { sendInstanceNew } = props;
|
||||
|
||||
this.sendInstanceNew = sendInstanceNew.bind(this);
|
||||
|
||||
this.nameInput = this.nameInput.bind(this);
|
||||
this.playersChange = this.playersChange.bind(this);
|
||||
this.pveChange = this.pveChange.bind(this);
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
}
|
||||
|
||||
playersChange(event) {
|
||||
this.setState({ players: Number(event.target.value) });
|
||||
pveChange() {
|
||||
this.setState({ pve: !this.state.pve });
|
||||
}
|
||||
|
||||
nameInput(event) {
|
||||
@ -45,8 +45,8 @@ class InstanceCreateForm extends Component {
|
||||
|
||||
handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
this.sendInstanceNew(this.props.team, this.state.name, this.state.players);
|
||||
this.setState({ name: '', players: 2 });
|
||||
this.sendInstanceNew(this.props.team, this.state.name, this.state.pve);
|
||||
this.setState({ name: '', pve: false });
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -62,21 +62,17 @@ class InstanceCreateForm extends Component {
|
||||
type="text"
|
||||
disabled={disabled}
|
||||
value={this.state.name}
|
||||
placeholder="name"
|
||||
placeholder="game name"
|
||||
onInput={this.nameInput}
|
||||
/>
|
||||
<label htmlFor="playerSelect">players</label>
|
||||
<select id="playerSelect"
|
||||
<label htmlFor="pveSelect">vs CPU</label>
|
||||
<input id="pveSelect"
|
||||
type="checkbox"
|
||||
disabled={disabled}
|
||||
value={this.state.players}
|
||||
onChange={this.playersChange}
|
||||
checked={this.state.pve}
|
||||
onChange={this.pveChange}
|
||||
>
|
||||
<option value={1}>pve</option>
|
||||
<option value={2}>2</option>
|
||||
<option value={4}>4</option>
|
||||
<option value={8}>8</option>
|
||||
<option value={16}>16</option>
|
||||
</select>
|
||||
</input>
|
||||
</form>
|
||||
<button
|
||||
onClick={this.handleSubmit}
|
||||
|
||||
@ -56,7 +56,7 @@ function List(args) {
|
||||
const instancePanels = instanceList.map(instance => {
|
||||
const player = instance.players.find(p => p.id === account.id);
|
||||
const scoreText = player
|
||||
? `${player.score.wins} : ${player.score.losses}`
|
||||
? `${player.wins} : ${player.losses}`
|
||||
: '';
|
||||
|
||||
function instanceClick() {
|
||||
|
||||
@ -125,8 +125,8 @@ function createSocket(events) {
|
||||
send({ method: 'instance_join', params: { instance_id: instanceId, construct_ids: constructs } });
|
||||
}
|
||||
|
||||
function sendInstanceNew(constructs, name, players) {
|
||||
send({ method: 'instance_new', params: { construct_ids: constructs, name, players } });
|
||||
function sendInstanceNew(constructs, name, pve) {
|
||||
send({ method: 'instance_new', params: { construct_ids: constructs, name, pve } });
|
||||
}
|
||||
|
||||
function sendInstanceReady(instanceId) {
|
||||
@ -284,6 +284,7 @@ function createSocket(events) {
|
||||
if (account) {
|
||||
events.setAccount(account);
|
||||
sendAccountInstances();
|
||||
sendInstanceList();
|
||||
sendAccountConstructs();
|
||||
}
|
||||
|
||||
|
||||
@ -38,18 +38,23 @@ enum Format {
|
||||
|
||||
#[derive(Debug,Clone,Serialize,Deserialize)]
|
||||
struct Round {
|
||||
player_ids: Vec<Uuid>,
|
||||
game_id: Option<Uuid>,
|
||||
finished: bool,
|
||||
}
|
||||
|
||||
impl Round {
|
||||
fn new() -> Round {
|
||||
Round { game_id: None, finished: false }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug,Clone,Serialize,Deserialize)]
|
||||
pub struct Instance {
|
||||
id: Uuid,
|
||||
pub name: String,
|
||||
|
||||
players: Vec<Player>,
|
||||
rounds: Vec<Vec<Round>>,
|
||||
rounds: Vec<Round>,
|
||||
|
||||
open: bool,
|
||||
max_players: usize,
|
||||
@ -72,13 +77,13 @@ impl Instance {
|
||||
phase: InstancePhase::Lobby,
|
||||
open: true,
|
||||
max_players: 2,
|
||||
max_rounds: 16,
|
||||
max_rounds: 5,
|
||||
name: String::new(),
|
||||
password: None,
|
||||
phase_end: Utc::now(),
|
||||
phase_start: Utc::now(),
|
||||
|
||||
format: Format::RoundRobin,
|
||||
format: Format::Standard,
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,7 +95,7 @@ impl Instance {
|
||||
phase: InstancePhase::InProgress,
|
||||
open: false,
|
||||
max_players: 0,
|
||||
max_rounds: 1,
|
||||
max_rounds: 5,
|
||||
name: "Global Matchmaking".to_string(),
|
||||
password: None,
|
||||
phase_start: Utc::now(),
|
||||
@ -108,7 +113,6 @@ impl Instance {
|
||||
self.players
|
||||
.iter()
|
||||
.filter(|p| !p.ready)
|
||||
.filter(|p| self.current_game_id(p.id).is_none())
|
||||
.map(|p| p.id)
|
||||
.collect::<Vec<Uuid>>()
|
||||
}
|
||||
@ -131,15 +135,6 @@ impl Instance {
|
||||
(self, new_games)
|
||||
}
|
||||
|
||||
fn set_max_players(mut self, max: usize) -> Result<Instance, Error> {
|
||||
if max > 16 || max % 2 != 0 {
|
||||
return Err(err_msg("max players must be divisible by 2 and less than 16"));
|
||||
}
|
||||
|
||||
self.max_players = max;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn set_name(mut self, name: String) -> Result<Instance, Error> {
|
||||
if name.len() == 0 {
|
||||
return Err(err_msg("name must have a length"));
|
||||
@ -156,15 +151,17 @@ impl Instance {
|
||||
|
||||
fn add_bots(mut self) -> Instance {
|
||||
self.open = false;
|
||||
self.players = iter::repeat_with(|| {
|
||||
iter::repeat_with(|| {
|
||||
let bot_id = Uuid::new_v4();
|
||||
let constructs = instance_mobs(bot_id);
|
||||
let mut p = Player::new(bot_id, &name(), constructs).set_bot(true);
|
||||
p.set_ready(true);
|
||||
p
|
||||
})
|
||||
.take(15)
|
||||
.collect::<Vec<Player>>();
|
||||
// .take(self.max_players - self.players.len())
|
||||
.take(1)
|
||||
.for_each(|p| self.players.push(p));
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
@ -223,52 +220,47 @@ impl Instance {
|
||||
// create a game object if both players are ready
|
||||
// this should only happen once
|
||||
|
||||
let all_ready = self.round_ready_check(player_id);
|
||||
let all_ready = self.round_ready_check();
|
||||
|
||||
if !all_ready {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let game = self.create_round_game(player_id);
|
||||
let game = self.create_round_game();
|
||||
|
||||
{
|
||||
let round_num = self.rounds.len() - 1;
|
||||
let current_round = self.rounds[round_num]
|
||||
.iter_mut()
|
||||
.find(|g| g.player_ids.contains(&player_id))
|
||||
.unwrap();
|
||||
let current_round = self.rounds
|
||||
.last_mut()
|
||||
.expect("instance does not have any rounds");
|
||||
|
||||
current_round.game_id = Some(game.id);
|
||||
}
|
||||
current_round.game_id = Some(game.id);
|
||||
|
||||
return Ok(Some(game));
|
||||
|
||||
}
|
||||
|
||||
fn round_ready_check(&mut self, player_id: Uuid) -> bool {
|
||||
let current_round = self.current_round(player_id);
|
||||
fn round_ready_check(&mut self) -> bool {
|
||||
self.players
|
||||
.iter()
|
||||
.filter(|p| current_round.player_ids.contains(&p.id))
|
||||
.all(|p| p.ready)
|
||||
}
|
||||
|
||||
// maybe just embed the games in the instance
|
||||
// but seems hella inefficient
|
||||
fn create_round_game(&self, player_id: Uuid) -> Game {
|
||||
let current_round = self.current_round(player_id);
|
||||
fn create_round_game(&mut self) -> Game {
|
||||
let current_round = self.rounds
|
||||
.last_mut()
|
||||
.expect("instance does not have any rounds");
|
||||
|
||||
|
||||
let mut game = Game::new();
|
||||
current_round.game_id = Some(game.id);
|
||||
|
||||
game
|
||||
.set_player_num(2)
|
||||
.set_player_constructs(3)
|
||||
.set_instance(self.id);
|
||||
|
||||
// create the initiators player
|
||||
for player in self.players
|
||||
.clone()
|
||||
.into_iter()
|
||||
.filter(|p| current_round.player_ids.contains(&p.id)) {
|
||||
for player in self.players.clone().into_iter() {
|
||||
game.player_add(player).unwrap();
|
||||
}
|
||||
|
||||
@ -303,7 +295,7 @@ impl Instance {
|
||||
p.vbox.fill();
|
||||
});
|
||||
|
||||
self.generate_rounds();
|
||||
self.rounds.push(Round::new());
|
||||
self.bot_round_actions();
|
||||
|
||||
self
|
||||
@ -313,7 +305,9 @@ impl Instance {
|
||||
match self.format {
|
||||
// bo5 standard
|
||||
// OR condition is for forfeitures
|
||||
Format::Standard => self.players.iter().any(|p| p.score.wins > 2) || self.rounds.len() == 5,
|
||||
Format::Standard =>
|
||||
self.players.iter().any(|p| p.wins as usize >= self.max_rounds / 2 + 1)
|
||||
|| self.rounds.len() == self.max_rounds,
|
||||
|
||||
// everybody plays each other once
|
||||
Format::RoundRobin => self.rounds.len() == self.players.len() - 1,
|
||||
@ -353,54 +347,14 @@ impl Instance {
|
||||
self
|
||||
}
|
||||
|
||||
fn generate_rounds(&mut self) -> &mut Instance {
|
||||
let round_num = self.rounds.len();
|
||||
let mut matched_players = self.players
|
||||
.iter()
|
||||
.map(|p| p.id)
|
||||
.collect::<Vec<Uuid>>();
|
||||
|
||||
let np = matched_players.len();
|
||||
|
||||
if round_num > 0 {
|
||||
matched_players.rotate_right(round_num % np);
|
||||
matched_players.swap(0,1);
|
||||
}
|
||||
|
||||
// only set up for even player numbers atm
|
||||
// no byes
|
||||
let current_round = matched_players[0..(np / 2)]
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, id)| Round {
|
||||
player_ids: vec![*id, matched_players[np - (i + 1)]],
|
||||
game_id: None,
|
||||
finished: false,
|
||||
})
|
||||
.collect::<Vec<Round>>();
|
||||
|
||||
self.rounds.push(current_round);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
fn current_round(&self, player_id: Uuid) -> &Round {
|
||||
let round_num = self.rounds.len() - 1;
|
||||
|
||||
let current_round = self.rounds[round_num]
|
||||
.iter()
|
||||
.find(|g| g.player_ids.contains(&player_id))
|
||||
.unwrap();
|
||||
|
||||
current_round
|
||||
}
|
||||
|
||||
fn current_game_id(&self, player_id: Uuid) -> Option<Uuid> {
|
||||
fn current_game_id(&self) -> Option<Uuid> {
|
||||
if self.phase == InstancePhase::Lobby {
|
||||
return None;
|
||||
}
|
||||
|
||||
let current_round = self.current_round(player_id);
|
||||
let current_round = self.rounds
|
||||
.last()
|
||||
.expect("instance does not have any rounds");
|
||||
|
||||
if current_round.finished || current_round.game_id.is_none() {
|
||||
return None;
|
||||
@ -410,13 +364,17 @@ impl Instance {
|
||||
}
|
||||
|
||||
fn game_finished(&mut self, game: &Game) -> Result<&mut Instance, Error> {
|
||||
let round_num = self.rounds.len() - 1;
|
||||
self.rounds[round_num]
|
||||
.iter_mut()
|
||||
.filter(|r| r.game_id.is_some())
|
||||
.find(|r| r.game_id.unwrap() == game.id)
|
||||
.ok_or(err_msg("could not find matchup in current round"))?
|
||||
.finished = true;
|
||||
{
|
||||
let current_round = self.rounds
|
||||
.last_mut()
|
||||
.expect("instance does not have any rounds");
|
||||
|
||||
if current_round.game_id.unwrap() != game.id {
|
||||
return Err(err_msg("instance does not have a round for this game"));
|
||||
}
|
||||
|
||||
current_round.finished = true;
|
||||
}
|
||||
|
||||
// if you don't win, you lose
|
||||
// ties can happen if both players forfeit
|
||||
@ -446,7 +404,7 @@ impl Instance {
|
||||
|
||||
fn all_games_finished(&self) -> bool {
|
||||
match self.rounds.last() {
|
||||
Some(r) => r.iter().all(|g| g.finished),
|
||||
Some(r) => r.finished,
|
||||
None => true,
|
||||
}
|
||||
}
|
||||
@ -460,10 +418,15 @@ impl Instance {
|
||||
}
|
||||
|
||||
pub fn vbox_action_allowed(&self, account: Uuid) -> Result<(), Error> {
|
||||
if self.players.iter().find(|p| p.id == account).is_none() {
|
||||
return Err(err_msg("player not in this instance"));
|
||||
}
|
||||
|
||||
if self.phase == InstancePhase::Lobby {
|
||||
return Err(err_msg("game not yet started"));
|
||||
}
|
||||
if self.current_game_id(account).is_some() {
|
||||
|
||||
if self.current_game_id().is_some() {
|
||||
return Err(err_msg("you cannot perform vbox actions while in a game"));
|
||||
}
|
||||
|
||||
@ -653,20 +616,13 @@ pub fn instances_need_upkeep(tx: &mut Transaction) -> Result<Vec<Instance>, Erro
|
||||
}
|
||||
|
||||
pub fn instance_new(params: InstanceLobbyParams, tx: &mut Transaction, account: &Account) -> Result<Instance, Error> {
|
||||
let mut instance = match params.players {
|
||||
1 => Instance::new()
|
||||
.set_max_players(16)?
|
||||
let mut instance = match params.pve {
|
||||
true => Instance::new()
|
||||
.set_name(params.name)?
|
||||
.add_bots(),
|
||||
|
||||
2 => Instance::new()
|
||||
.set_max_players(params.players)?
|
||||
false => Instance::new()
|
||||
.set_name(params.name)?
|
||||
.set_format(Format::Standard),
|
||||
|
||||
_ => Instance::new()
|
||||
.set_max_players(params.players)?
|
||||
.set_name(params.name)?,
|
||||
};
|
||||
|
||||
instance = instance_create(tx, instance)?;
|
||||
@ -705,10 +661,10 @@ pub fn instance_ready(params: InstanceReadyParams, tx: &mut Transaction, account
|
||||
instance_update(tx, instance)
|
||||
}
|
||||
|
||||
pub fn instance_state(params: InstanceStateParams, tx: &mut Transaction, account: &Account) -> Result<RpcResult, Error> {
|
||||
pub fn instance_state(params: InstanceStateParams, tx: &mut Transaction, _account: &Account) -> Result<RpcResult, Error> {
|
||||
let instance = instance_get(tx, params.instance_id)?;
|
||||
|
||||
if let Some(game_id) = instance.current_game_id(account.id) {
|
||||
if let Some(game_id) = instance.current_game_id() {
|
||||
let game = game_get(tx, game_id)?;
|
||||
|
||||
// return the game until it's finished
|
||||
@ -736,24 +692,19 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn instance_pve_test() {
|
||||
let mut instance = Instance::new()
|
||||
.set_max_players(16).expect("unable to set max players")
|
||||
.set_format(Format::RoundRobin)
|
||||
.add_bots();
|
||||
let mut instance = Instance::new().add_bots();
|
||||
|
||||
let player_account = Uuid::new_v4();
|
||||
let constructs = instance_mobs(player_account);
|
||||
let player = Player::new(player_account, &"test".to_string(), constructs).set_bot(true);
|
||||
let player_id = player.id;
|
||||
|
||||
instance.add_player(player).expect("could not add player");
|
||||
|
||||
assert_eq!(instance.phase, InstancePhase::Lobby);
|
||||
instance.player_ready(player_id).unwrap();
|
||||
instance.player_ready(player_account).unwrap();
|
||||
|
||||
assert_eq!(instance.phase, InstancePhase::Finished);
|
||||
assert_eq!(instance.rounds[0].len(), 8);
|
||||
assert_eq!(instance.rounds.len(), 15);
|
||||
assert!(instance.players.iter().any(|p| p.wins as usize == instance.max_rounds / 2 + 1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -766,9 +717,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn instance_start_test() {
|
||||
let mut instance = Instance::new()
|
||||
.set_max_players(2)
|
||||
.expect("could not create instance");
|
||||
let mut instance = Instance::new();
|
||||
|
||||
assert_eq!(instance.max_players, 2);
|
||||
|
||||
@ -807,9 +756,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn instance_upkeep_test() {
|
||||
let mut instance = Instance::new()
|
||||
.set_max_players(2).expect("could not create instance")
|
||||
.set_format(Format::Standard);
|
||||
let mut instance = Instance::new();
|
||||
|
||||
let player_account = Uuid::new_v4();
|
||||
let constructs = instance_mobs(player_account);
|
||||
|
||||
@ -14,22 +14,17 @@ use skill::{Effect};
|
||||
|
||||
const DISCARD_COST: u16 = 5;
|
||||
|
||||
#[derive(Debug,Clone,Copy,Serialize,Deserialize)]
|
||||
pub struct Score {
|
||||
pub wins: u8,
|
||||
pub losses: u8,
|
||||
}
|
||||
|
||||
#[derive(Debug,Clone,Serialize,Deserialize)]
|
||||
pub struct Player {
|
||||
pub id: Uuid,
|
||||
pub name: String,
|
||||
pub vbox: Vbox,
|
||||
pub score: Score,
|
||||
pub constructs: Vec<Construct>,
|
||||
pub bot: bool,
|
||||
pub ready: bool,
|
||||
pub warnings: u8,
|
||||
pub wins: u8,
|
||||
pub losses: u8,
|
||||
}
|
||||
|
||||
impl Player {
|
||||
@ -38,7 +33,8 @@ impl Player {
|
||||
id: account,
|
||||
name: name.clone(),
|
||||
vbox: Vbox::new(),
|
||||
score: Score { wins: 0, losses: 0 },
|
||||
wins: 0,
|
||||
losses: 0,
|
||||
constructs,
|
||||
bot: false,
|
||||
ready: false,
|
||||
@ -69,13 +65,13 @@ impl Player {
|
||||
}
|
||||
|
||||
pub fn add_win(&mut self) -> &mut Player {
|
||||
self.score.wins += 1;
|
||||
self.wins += 1;
|
||||
self.vbox.balance_add(12);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_loss(&mut self) -> &mut Player {
|
||||
self.score.losses += 1;
|
||||
self.losses += 1;
|
||||
self.vbox.balance_add(9);
|
||||
self
|
||||
}
|
||||
|
||||
@ -17,7 +17,6 @@ use game::{Game, game_state, game_skill, game_ready};
|
||||
use account::{Account, account_create, account_login, account_from_token, account_constructs, account_instances};
|
||||
use skill::{Skill};
|
||||
use spec::{Spec};
|
||||
use player::{Score};
|
||||
use instance::{Instance, instance_state, instance_list, instance_new, instance_ready, instance_join};
|
||||
use vbox::{vbox_accept, vbox_apply, vbox_discard, vbox_combine, vbox_reclaim, vbox_unequip};
|
||||
use item::{Item, ItemInfoCtr, item_info};
|
||||
@ -389,9 +388,6 @@ pub enum RpcResult {
|
||||
ConstructList(Vec<Construct>),
|
||||
GameState(Game),
|
||||
ItemInfo(ItemInfoCtr),
|
||||
InstanceScores(Vec<(String, Score)>),
|
||||
// ZoneState(Zone),
|
||||
// ZoneClose(()),
|
||||
|
||||
InstanceList(Vec<Instance>),
|
||||
InstanceState(Instance),
|
||||
@ -528,7 +524,7 @@ struct InstanceLobbyMsg {
|
||||
pub struct InstanceLobbyParams {
|
||||
pub construct_ids: Vec<Uuid>,
|
||||
pub name: String,
|
||||
pub players: usize,
|
||||
pub pve: bool,
|
||||
pub password: Option<String>,
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user