Skip to content

Commit

Permalink
Added support for max_rounds for X01
Browse files Browse the repository at this point in the history
  • Loading branch information
thordy committed Nov 30, 2024
1 parent 9f2d5eb commit a07ed5f
Show file tree
Hide file tree
Showing 13 changed files with 266 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ A preview of major changes can be found in the Wiki ([Latest Changes](https://gi
- New set of larger Compact buttons
- Ability to configure: "Announcement Volume", "Auto Busting" and "Auto Leg Finish" from frontend
- Holding score buttons to score same dart three times
- Support `Max Rounds` to play for `x01`
- Lots of new Badges

#### Changed
Expand Down
3 changes: 2 additions & 1 deletion routes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ router.get('/', function (req, res, next) {
outshots: outshots.data,
lives: [{ id: 1, name: 1 }, { id: 3, name: 3 }, { id: 5, name: 5 }, { id: 7, name: 7 }, { id: 10, name: 10 }],
points_to_win: [{ id: 1, name: 1 }, { id: 2, name: 2 }, { id: 3, name: 3 }, { id: 4, name: 4 }, { id: 5, name: 5 }],
max_rounds: [{ id: -1, name: 'Unlimited' }, { id: 3, name: 3 }, { id: 5, name: 5 }, { id: 7, name: 7 }, { id: 10, name: 10 }, { id: 15, name: 15 }],
max_rounds_170: [{ id: -1, name: '-' }, { id: 3, name: 3 }, { id: 5, name: 5 }, { id: 7, name: 7 }, { id: 10, name: 10 }, { id: 15, name: 15 }],
max_rounds_x01: [{ id: -1, name: '-' }, { id: 10, name: 10 }, { id: 12, name: 12 }, { id: 16, name: 16 }, { id: 20, name: 20 }, { id: 30, name: 30 }],
venues: venues.data,
stakes: oweTypes.data,
offices: offices.data,
Expand Down
54 changes: 54 additions & 0 deletions routes/legs.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,60 @@ router.put('/:id/order', function (req, res, next) {
});
});

/** Method to finish a leg */
router.put('/:id/finish', function (req, res, next) {
let legId = req.params.id;
axios.put(`${req.app.locals.kcapp.api}/leg/${legId}/finish`, req.body)
.then(response => {
axios.all([
axios.get(`${req.app.locals.kcapp.api}/leg/${legId}`),
axios.get(`${req.app.locals.kcapp.api}/leg/${legId}/players`),
axios.get(`${req.app.locals.kcapp.api}/statistics/global/fnc`)
]).then(axios.spread((legData, playersData, globalData) => {
const leg = legData.data;
const players = playersData.data;
const currentPlayer = _.findWhere(players, { is_current_player: true });
const globalstat = globalData.data[0];

axios.get(`${req.app.locals.kcapp.api}/match/${leg.match_id}`)
.then((response) => {
const match = response.data;

const winnerPlayer = _.findWhere(players, { player_id: leg.winner_player_id });
//announceLegFinished(winnerPlayer, match)

if (!match.is_finished) {
this.socketHandler.setupLegsNamespace(match.current_leg_id);

// Forward all spectating clients to next leg
this.socketHandler.emitMessage(`/legs/${legId}`, 'new_leg', { match: match, leg: leg });
}
this.socketHandler.emitMessage(`/active`, 'leg_finished', { match: match, leg: leg });
this.socketHandler.emitMessage(`/legs/${legId}`, 'score_update', { leg: leg, players: players, match: match });
this.socketHandler.emitMessage(`/legs/${legId}`,'leg_finished', { leg: leg, match: match });

setTimeout(() => {
// Remove the namespace in a bit, once announcements are finished
this.socketHandler.removeNamespace(legId);
}, 15000);
res.status(200).send({ leg_id: match.current_leg_id, match: match }).end();
}).catch(error => {
const message = `${error.message}(${error})`;
debug(`[${legId}] Error when getting match: ${message}`);
next(error);
});
})).catch(error => {
const message = `${error.message} (${error})`;
debug(`[${legId}] Error when getting leg: ${message}`);
next(error);
});
}).catch(error => {
debug(`[${legId}] Unable to finish leg: ${error}`);
next(error);
});
});


/** Method to undo leg finish */
router.put('/:id/undo', function (req, res, next) {
axios.put(`${req.app.locals.kcapp.api}/leg/${req.params.id}/undo`)
Expand Down
16 changes: 15 additions & 1 deletion src/components/scorecard/components/x01.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@ exports.isBust = (player, dart, totalScore, leg) => {
return currentScore < 2;
}

exports.isMaxRound = (player, dartsThrown, leg, players) => {
if (player.player_id === players[players.length - 1].player_id &&
dartsThrown > 3 &&
leg.parameters.max_rounds && leg.parameters.max_rounds === leg.round) {
return true;
}
return false;
}

exports.isCheckout = (player, dart, totalScore, leg) => {
let currentScore = player.current_score - dart.getValue();
if (player.player.options && !player.player.options.subtract_per_dart) {
Expand Down Expand Up @@ -65,6 +74,7 @@ exports.confirmThrow = function (external) {

const isCheckout = module.exports.isCheckout(this.state.player, dart, this.state.totalScore, this.state.leg);
const isBust = module.exports.isBust(this.state.player, dart, this.state.totalScore, this.state.leg);
const isMaxRound = module.exports.isMaxRound(this.state.player, this.state.currentDart, this.state.leg, this.input.players);
if (isCheckout) {
submitting = true;
}
Expand All @@ -89,6 +99,11 @@ exports.confirmThrow = function (external) {
this.emit('player-busted', true);
}
}
else if (isMaxRound) {
alertify.notify(`Maximum numbers of rounds reached.`, 'warning');
this.emit('max-rounds-reached', true);
}

if (!this.state.player.player.options || this.state.player.player.options.subtract_per_dart) {
this.state.player.current_score -= scored;
}
Expand All @@ -98,4 +113,3 @@ exports.confirmThrow = function (external) {
}
return submitting;
}

Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ module.exports = {
stake: null,
venue_id: null
},
maxRounds: input.max_rounds_x01,
playerId: "",
socket: {},
demo_mode: false,
Expand Down Expand Up @@ -104,7 +105,7 @@ module.exports = {
for (const file of data.audios) {
audioPlayers.push(new Audio(file.file));
}

for (let i = 0; i < audioPlayers.length; i++) {
const current = audioPlayers[i];
const next = audioPlayers[i + 1];
Expand Down Expand Up @@ -314,6 +315,13 @@ module.exports = {
}
this.state.options.starting_score = scoreComponent.state.index


if (this.state.options.game_type === types.X01) {
this.state.maxRounds = this.input.max_rounds_x01;
} else if (this.state.options.game_type === types.ONESEVENTY) {
this.state.maxRounds = this.input.max_rounds_170;
}

let selectedPlayers = this.getComponents('players');
for (let i = 0; i < selectedPlayers.length; i++) {
selectedPlayers[i].handleTypeChange(this.state.options.game_type);
Expand Down
10 changes: 7 additions & 3 deletions src/pages/index/components/new-game-form/new-game-form.marko
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,20 @@ $ const types = require('../../../../components/scorecard/components/match_types
<div class="block-container-with-header">
<game-option-selector values=input.types options=state.options key="game-type" label="Type" attribute="game_type" defaultValue=state.options.game_type on-value-changed('onGameTypeChanged')/>
<game-option-selector values=input.scores options=state.options key="starting-score" label="Starting Score" attribute="starting_score" defaultValue=state.options.starting_score/>
<if(state.options.game_type == types.X01 || state.options.game_type == types.X01HANDICAP || state.options.game_type == types.TIC_TAC_TOE)>
<if(state.options.game_type == types.X01 || state.options.game_type == types.X01HANDICAP)>
<game-option-selector values=input.outshots options=state.options key="outshot-type" label="Outshot Type" attribute="outshot_type" defaultValue=types.OUTSHOT_DOUBLE/>
<game-option-selector values=state.maxRounds options=state.options key="max-rounds-x01" label="Max Rounds" attribute="max_rounds" defaultValue=-1/>
</if>
<if(state.options.game_type == types.TIC_TAC_TOE)>
<game-option-selector values=input.outshots options=state.options key="outshot-type" label="Outshot Type" attribute="outshot_type" defaultValue=types.OUTSHOT_DOUBLE/>
</if>
<if(state.options.game_type == types.KNOCKOUT)>
<game-option-selector values=input.lives options=state.options key="starting-lives" label="Starting Lives" attribute="starting_lives" defaultValue=5/>
</if>
<if(state.options.game_type == types.ONESEVENTY)>
<game-option-selector values=input.points_to_win options=state.options key="points-to-win" label="Points To Win" attribute="points_to_win" defaultValue=1/>
<game-option-selector values=input.max_rounds options=state.options key="max-rounds" label="Max Rounds" attribute="max_rounds" defaultValue=-1/>
</if>
<game-option-selector values=state.maxRounds options=state.options key="max-rounds-oneseventy" label="Max Rounds" attribute="max_rounds" defaultValue=-1/>
</if>
<game-option-selector values=input.modes options=state.options key="game-mode" label="Mode" attribute="game_mode" defaultValue=state.options.game_mode/>
<game-option-selector values=input.stakes options=state.options key="stake" label="Stake" attribute="stake" addNull=true defaultAttrName="item"/>
<game-option-selector values=state.venues options=state.options key="venue" label="Venue" attribute="venue_id" addNull=true defaultValue=state.options.venue_id/>
Expand Down
4 changes: 3 additions & 1 deletion src/pages/index/index-template.marko
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import Layout from "../layout.marko"
<${Layout}>
<@body>
<new-game-form players=input.players modes=input.modes scores=input.scores types=input.types
outshots=input.outshots lives=input.lives points_to_win=input.points_to_win max_rounds=input.max_rounds
outshots=input.outshots lives=input.lives points_to_win=input.points_to_win
max_rounds_170=input.max_rounds_170
max_rounds_x01=input.max_rounds_x01
venues=input.venues stakes=input.stakes offices=input.offices presets=input.presets/>
</@body>
</>
4 changes: 4 additions & 0 deletions src/pages/leg/components/leg/leg.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,10 @@ module.exports = {
}
},

onMaxRoundsReached(component) {
$("#pick-winner-modal").modal("show");
},

onUndoThrow() {
if (this.state.leg.visits.length > 0) {
this.state.submitting = true;
Expand Down
3 changes: 3 additions & 0 deletions src/pages/leg/components/leg/leg.marko
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ $ const types = require('../../../../components/scorecard/components/match_types
on-possible-throw("onPossibleThrow")
on-score-change("onScoreChange")
on-player-busted("onPlayerBusted")
on-max-rounds-reached("onMaxRoundsReached")
on-leg-finished("onLegFinished")
on-undo-throw("onUndoThrow")/>
</tr>
Expand All @@ -35,6 +36,7 @@ $ const types = require('../../../../components/scorecard/components/match_types
on-possible-throw("onPossibleThrow")
on-score-change("onScoreChange")
on-player-busted("onPlayerBusted")
on-max-rounds-reached("onMaxRoundsReached")
on-leg-finished("onLegFinished")
on-undo-throw("onUndoThrow")/>
</for>
Expand All @@ -50,6 +52,7 @@ $ const types = require('../../../../components/scorecard/components/match_types
</if>
</div>
<player-order players=state.players match=input.match legId=state.leg.id on-warmup-started("onWarmupStarted") on-smartboard-reconnect('onSmartboardReconnect')/>
<pick-winner-modal players=state.players match=input.match legId=state.leg.id/>
<qr-code-modal/>
<confirm-checkout on-confirm-checkout("onConfirmCheckout")/>
<if(state.enableButtonInput)>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
const _ = require("underscore");
const axios = require('axios');

module.exports = {
onCreate(input) {
this.state = {
legId: input.legId,
players: input.players,
winner: undefined,
submitting: false
}
},

onMount() {
const modal = document.getElementById('pick-winner-modal');
modal.addEventListener("keydown", this.onKeyDown.bind(this), false);
modal.addEventListener("keypress", this.onKeyPress.bind(this), false);
},
onKeyPress(e) {
e.stopPropagation();
},
onKeyDown(e) {
if (e.key === 'Enter') {
if (this.state.submitting) {
e.preventDefault();
e.stopPropagation();
return;
}
this.confirmWinner();

// We don`t want to submit darts in the background
e.preventDefault();
} else if (e.key == 'ESC') {
// Don`t allow closing this modal
e.preventDefault();
} else {
const player = this.input.players[parseInt(e.key) - 1];
if (player) {
const exist = _.findWhere(this.state.players, { player_id: player.player_id });
if (exist) {
this.addPlayer(player);
} else {
this.removePlayer(player);
}
}
}
e.stopPropagation();
},

playerSelected(playerId) {
const player = _.findWhere(this.input.players, { player_id: playerId });
this.addPlayer(player);
},

addPlayer(player) {
this.state.players = _.reject(this.input.players, (p) => p.player_id === player.player_id);
this.setStateDirty('players');

this.state.winner = player;
this.setStateDirty('winner');
},

removePlayer(player) {
this.state.winner = undefined;
this.setStateDirty('winner');

this.state.players.push(player);
this.setStateDirty('players');
},

confirmWinner(event) {
this.state.submitting = true;
if (!this.state.winner) {
alert("Please select a winner");
this.state.submitting = false;
return;
}
axios.put(`${window.location.origin}/legs/${this.state.legId}/finish`, { winner_id: this.state.winner.player_id })
.then(response => {
const match = response.data.match;
if (match.is_finished) {
location.href = `/matches/${match.id}/result?finished=true`;
} else {
location.href = `/legs/${response.data.leg_id}`;
}
}).catch(error => {
this.state.submitting = false;
alert('Error choosing winner. Please reload');
console.log(error);
location.reload();
});
}
};
53 changes: 53 additions & 0 deletions src/pages/leg/components/pick-winner-modal/pick-winner-modal.marko
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<div id="pick-winner-modal" class="modal fade modal-right" tabindex="-1" role="dialog" aria-labelledby="pick-winner-modal-label" aria-hidden="true" data-backdrop="static" data-keyboard="false">
<div class="modal-dialog modal-dialog-centered" role="document" style="height:100%;">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Select Winner</h4>
</div>
<div class="modal-body">
<h5>Players</h5>
<div class="table-responsive no-border" style="min-height: 75px;">
<table class="uv-table-players" id="table-players-select-order" style="table-layout: fixed;">
<tbody>
<tr>
<for|player| of=state.players>
<td class="block-container-blue text-center" data-player-id=player.player_id on-click("playerSelected", player.player_id)>
<div>
<label class="key-small">${player.order}</label>
</div>
<label id=`player-name-${player.player_id}` class="label-player-name">${player.player.name}</label>
</td>
</for>
</tr>
</tbody>
</table>
</div>
<h5>Winner</h5>
<div class="table-responsive no-border" style="min-height: 75px;">
<table class="uv-table-players" id="table-players-selected-order" style="table-layout: fixed;">
<tbody>
<tr>
<if(state.winner)>
<td class="block-container-blue text-center" data-player-id=state.winner.player_id on-click("removePlayer", state.winner)>
<div class="text-center">
<label class="key-small">${state.winner.order}</label>
</div>
<label id=`player-name-${state.winner.player_id}` class="label-player-name">${state.winner.player.name}</label>
</td>
</if>
</tr>
</tbody>
</table>
</div>
</div>
<div class="modal-footer text-center">
<button class="btn btn-primary" type="button" on-click("confirmWinner")>
<div class="text-center">
<label class="key-small">ENTER</label>
<label class="pl-10">Finish Match</label>
</div>
</button>
</div>
</div>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.key-small {
padding-left: 8px;
padding-right: 8px;
height: 25px;
background-color: #eeeff1;
color: #aaaaaa;
font-weight: normal;
line-height: 25px;
border-radius: 5px;
box-shadow: 0px 1px 0px 2px #999;
}

body {
// Opening modal adds padding for some reason
padding-right: 0px !important;
}
Loading

0 comments on commit a07ed5f

Please sign in to comment.