You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
806 lines
27 KiB
806 lines
27 KiB
import { nextTick, watch, reactive, ref, computed, onMounted, onUnmounted } from "vue";
|
|
import { getQuery, setKirby } from "../../kirby.js";
|
|
import { state } from "../../stateMgr.js";
|
|
import { handleActive, ArrowVerticalKeyHandler, ArrowHorizontalKeyHandler, NumberKeyHandler } from "../../handlers.js";
|
|
import { overlayAndGet, powerStateMachine, overlayAndPop, popLastElem } from "../../componentPromise.js";
|
|
|
|
import { getGameProps, initGame, initStats, storeVisit, formatDate, removeLastVisit, extension, getFinalWinner } from "./logic.js";
|
|
|
|
const html = (v) => { return v[0] };
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Components
|
|
//////////////////////////in//////////////////////////////////////////////////////
|
|
|
|
const pregame = {
|
|
props: ['page','active', 'stack'],
|
|
setup(props, context) {
|
|
handleActive(props, [ArrowVerticalKeyHandler]);
|
|
|
|
const selectPlayer = async (i) => {
|
|
if (props.active) {
|
|
const [result, error] = await overlayAndPop("d-playerSelect", { players: props.page.participants, class:"overlay"}, props.stack);
|
|
if (error === undefined) {
|
|
props.page.players[i] = result;
|
|
}
|
|
}
|
|
}
|
|
|
|
const selectX01 = async () => {
|
|
if (props.active) {
|
|
const [result, error] = await overlayAndPop("d-select", { options: [ "301", "501" ], class:"overlay"}, props.stack);
|
|
if (error === undefined) {
|
|
props.page.modus = result;
|
|
}
|
|
}
|
|
}
|
|
|
|
const selectIn = async () => {
|
|
if (props.active) {
|
|
const [result, error] = await overlayAndPop("d-select", { options: [ "Straight", "Double" ], class:"overlay"}, props.stack);
|
|
if (error === undefined) {
|
|
props.page.in = result;
|
|
}
|
|
}
|
|
}
|
|
|
|
return { selectPlayer, selectX01, selectIn }
|
|
},
|
|
template: html`
|
|
<nav class="gamesetup" :disabled="!active">
|
|
<h1>Game Setup</h1>
|
|
<div class="menu">
|
|
<!-- <div><span class="label">Name:</span><d-inputElem v-index="active" v-model="game.title"></d-inputElem></div> -->
|
|
<div><span class="label">Player 1:</span><d-playerElem v-index="active" @click="selectPlayer(0)" :player="page.players[0]"></d-playerElem></div>
|
|
<div><span class="label">Player 2:</span><d-playerElem v-index="active" @click="selectPlayer(1)" :player="page.players[1]"></d-playerElem></div>
|
|
<div><span class="label">X01:</span><d-plainElem v-index="active" @click="selectX01()" :text="page.modus"></d-plainElem></div>
|
|
<div><span class="label">In:</span><d-plainElem v-index="active" @click="selectIn()" :text="page.in"></d-plainElem></div>
|
|
<!-- <div><span class="label">Best of Sets:</span><d-inputElem v-index="active" v-model="page.sets"></d-inputElem></div> -->
|
|
<div><span class="label">Best of Legs (per Set):</span><d-inputElem v-index="active" v-model="page.legs"></d-inputElem></div>
|
|
<div><d-plainElem text="Start" v-index="active" @click="$emit('resolve', page)"></d-plainElem></div>
|
|
</div>
|
|
</nav>
|
|
`
|
|
}
|
|
|
|
export const bullselect = {
|
|
props: ['players','active', 'stack'],
|
|
setup(props, { emit }) {
|
|
handleActive(props, [ArrowHorizontalKeyHandler, NumberKeyHandler]);
|
|
|
|
const children = computed(() => {
|
|
const items = [];
|
|
for (let i in props.players){
|
|
const player = props.players[i];
|
|
items.push({
|
|
component: "d-squareElem",
|
|
props: {
|
|
text: player.forename + " " + player.surname,
|
|
icon: player.img ? player.img : '/assets/img/placeholder_person.png'
|
|
},
|
|
onClick: () => {
|
|
emit("resolve", player);
|
|
}
|
|
})
|
|
}
|
|
return items;
|
|
});
|
|
|
|
return { children }
|
|
},
|
|
template: html`
|
|
<div class="gamesetup">
|
|
<h1 class="bull">Who won bull?</h1>
|
|
<d-list :withshortkey="true" :elements="children" type="horizontal" />
|
|
</div>
|
|
`
|
|
}
|
|
|
|
|
|
|
|
const score = {
|
|
props: ['page', 'justlegs', 'current_set_points', 'current_leg_points'],
|
|
setup(props) {
|
|
return { }
|
|
},
|
|
template: html`
|
|
<div class="score">
|
|
<div v-if="!justlegs" class="sets">
|
|
<h2 v-for="(idx) in [0,1]">{{ current_set_points[idx] }}</h2>
|
|
<div class="info">
|
|
<h3>Best of {{ page.sets }}</h3>
|
|
<h2>Sets</h2>
|
|
</div>
|
|
</div>
|
|
<div class="legs">
|
|
<h2 v-for="(idx) in [0,1]">{{ current_leg_points[idx] }}</h2>
|
|
<div class="info">
|
|
<h3>Best of {{ page.legs }}</h3>
|
|
<h2>Legs</h2>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`
|
|
}
|
|
|
|
const player = {
|
|
props: ['page', 'id', 'current_stat'],
|
|
setup(props) {
|
|
const addStats = (stat1, stat2) => {
|
|
let res = {
|
|
average: [stat1.average[0]+stat2.average[0], stat1.average[1]+stat2.average[1]],
|
|
first9: [stat1.first9[0]+stat2.first9[0], stat1.first9[1]+stat2.first9[1]],
|
|
"60+": stat1["60+"]+stat2["60+"],
|
|
"100+": stat1["100+"]+stat2["100+"],
|
|
"140+": stat1["140+"]+stat2["140+"],
|
|
"180": stat1["180"]+stat2["180"],
|
|
"checkouts": [stat1.checkouts[0]+stat2.checkouts[0], stat1.checkouts[1]+stat2.checkouts[1]],
|
|
"checkoutPoints": [...stat1.checkoutPoints, ...stat2.checkoutPoints]
|
|
}
|
|
if ("checkins" in stat1 && "checkins" in stat2){
|
|
res["checkins"] = [stat1.checkins[0]+stat2.checkins[0], stat1.checkins[1]+stat2.checkins[1]];
|
|
res["checkinPoints"] = [...stat1.checkinPoints, ...stat2.checkinPoints];
|
|
}
|
|
|
|
return res;
|
|
}
|
|
const inspect = !!props.page.enddate;
|
|
const tourStats = computed(() => {
|
|
if (props.page.tournamentStats.length != 2){
|
|
return [
|
|
props.page.stats.stats[0],
|
|
props.page.stats.stats[1]
|
|
];
|
|
}
|
|
if (inspect) {
|
|
return [
|
|
props.page.tournamentStats[0],
|
|
props.page.tournamentStats[1]
|
|
];
|
|
}
|
|
return [
|
|
addStats(props.page.tournamentStats[0], props.page.stats.stats[0]),
|
|
addStats(props.page.tournamentStats[1], props.page.stats.stats[1])
|
|
];
|
|
});
|
|
const getAverage = (avg) => {
|
|
return avg && avg[1] != 0 ? ((3*avg[0])/avg[1]).toFixed(1) : "-";
|
|
}
|
|
const getCheckout = (checkout) => {
|
|
return checkout && checkout[1] != 0 ? Math.round(1000*checkout[0]/checkout[1])/10 : "- "
|
|
}
|
|
const getMax = (checkouts) => {
|
|
if (checkouts && checkouts.length != 0) {
|
|
return Math.max(...checkouts);
|
|
}
|
|
return "-"
|
|
}
|
|
const player = computed(() => props.page.players[props.id])
|
|
return { tourStats, getAverage, getCheckout, getMax, player }
|
|
},
|
|
template: html`
|
|
<div class="player">
|
|
<img style="width: 100%" :src="player.img ? player.img : '/assets/img/placeholder_person.png'">
|
|
<h2 class="name">{{ player.forename }} {{ player.surname }}</h2>
|
|
<h3 class="nickname">{{ player.nickname }}</h3>
|
|
<div class="stats">
|
|
<div class="row header">
|
|
<div>Stat</div><div>Tnm</div><div>Match</div><div>Leg</div>
|
|
</div>
|
|
<div class="row">
|
|
<div>Avg:</div><div>
|
|
{{ getAverage(tourStats[id].average) }}
|
|
</div><div>
|
|
{{ getAverage(page.stats.stats[id].average) }}
|
|
</div><div>
|
|
{{ getAverage(current_stat.stats[id].average) }}
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div>First 9:</div><div>
|
|
{{ getAverage(tourStats[id].first9) }}
|
|
</div><div>
|
|
{{ getAverage(page.stats.stats[id].first9) }}
|
|
</div><div>
|
|
{{ getAverage(current_stat.stats[id].first9) }}
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div>60+:</div><div>{{ tourStats[id]["60+"] }}</div><div>{{ page.stats.stats[id]["60+"] }}</div><div>{{ current_stat.stats[id]["60+"] }}</div>
|
|
</div>
|
|
<div class="row">
|
|
<div>100+:</div><div>{{ tourStats[id]["100+"] }}</div><div>{{ page.stats.stats[id]["100+"] }}</div><div>{{ current_stat.stats[id]["100+"] }}</div>
|
|
</div>
|
|
<div class="row">
|
|
<div>140+:</div><div>{{ tourStats[id]["140+"] }}</div><div>{{ page.stats.stats[id]["140+"] }}</div><div>{{ current_stat.stats[id]["140+"] }}</div>
|
|
</div>
|
|
<div class="row">
|
|
<div>180:</div><div>{{ tourStats[id]["180"] }}</div><div>{{ page.stats.stats[id]["180"] }}</div><div>{{ current_stat.stats[id]["180"] }}</div>
|
|
</div>
|
|
<div class="row" v-if="page.in == 'Double'">
|
|
<div>Ch. I. %:</div><div>{{ getCheckout(tourStats[id].checkins) }}%</div><div>{{ getCheckout(page.stats.stats[id].checkins) }}%</div><div>{{ getCheckout(current_stat.stats[id].checkins) }}%</div>
|
|
</div>
|
|
<div class="row" v-if="page.in == 'Double'">
|
|
<div>Best Ch.:</div><div>{{ getMax(tourStats[id].checkinPoints) }}</div><div>{{ getMax(page.stats.stats[id].checkinPoints) }}</div><div>{{ getMax(current_stat.stats[id].checkinPoints) }}</div>
|
|
</div>
|
|
<div class="row">
|
|
<div>Ch. O. %:</div><div>{{ getCheckout(tourStats[id].checkouts) }}%</div><div>{{ getCheckout(page.stats.stats[id].checkouts) }}%</div><div></div>
|
|
</div>
|
|
<div class="row">
|
|
<div>Best Ch.:</div><div>{{ getMax(tourStats[id].checkoutPoints) }}</div><div>{{ getMax(page.stats.stats[id].checkoutPoints) }}</div><div></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`
|
|
}
|
|
|
|
|
|
const game = {
|
|
props: ['active', 'stack', 'players','current_leg', 'max'],
|
|
setup(props, context) {
|
|
const visits = computed(() => props.current_leg ? props.current_leg.visits:undefined );
|
|
const getPlayerVisits = (uuid) => {
|
|
const vs = visits.value.filter((v) => v.player == uuid);
|
|
if (vs.length < 9) {
|
|
for (var i = vs.length; i < 9; i++) {
|
|
vs.push({ "sum": "", "toGo":["",""]})
|
|
}
|
|
} else if (vs.length*2 < visits.value.length) {
|
|
|
|
vs.push({ "sum": "", "toGo":["",""]});
|
|
}
|
|
return vs;
|
|
}
|
|
return { getPlayerVisits }
|
|
},
|
|
template: html`
|
|
<div class="game">
|
|
<div class="headding">
|
|
<div class="headding points player1">Points</div>
|
|
<div class="headding toGo player1">ToGo</div>
|
|
<div class="headding rounds">Round</div>
|
|
<div class="headding points player2">Points</div>
|
|
<div class="headding toGo player2">ToGo</div>
|
|
</div>
|
|
<div class="body">
|
|
<div class="points player1"></div>
|
|
<div class="toGo player1">{{ max }}</div>
|
|
<div class="rounds">0</div>
|
|
<div class="points player2"></div>
|
|
<div class="toGo player2">{{ max }}</div>
|
|
<template v-for="j in [1,2]" v-if="players">
|
|
<template v-for="visit, i in getPlayerVisits(players[j-1].uuid)">
|
|
<template v-if="visit.toGo === undefined">
|
|
<div :class="'points player'+j+' input'">
|
|
<slot />
|
|
</div>
|
|
<div :class="'toGo player'+j"></div>
|
|
</template>
|
|
<template v-if="visit.toGo !== undefined">
|
|
<div :class="'points player'+j">{{ visit.sum }}</div>
|
|
<div :class="'toGo player'+j">{{ visit.toGo[j-1] }}</div>
|
|
</template>
|
|
<div class="rounds" v-if="j == 1">{{ (i+1)*3 }}</div>
|
|
</template>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
`
|
|
}
|
|
|
|
const gameinput = {
|
|
props: ['input','active', 'stack'],
|
|
setup(props, context) {
|
|
handleActive(props, []);
|
|
const check_remove = (event) => {
|
|
if (!event.repeat && event.target.value.length < 1) {
|
|
event.preventDefault();
|
|
context.emit('reject', -2);
|
|
}
|
|
}
|
|
const keyhandler = (e) => {
|
|
if (e.key == "F1" || e.keyCode == 112) {
|
|
e.preventDefault();
|
|
context.emit('resolve', "60");
|
|
} else if (e.key == "F2" || e.keyCode == 113) {
|
|
e.preventDefault();
|
|
context.emit('resolve', "45");
|
|
} else if (e.key == "F3" || e.keyCode == 114) {
|
|
e.preventDefault();
|
|
context.emit('resolve', "41");
|
|
} else if (e.key == "F4" || e.keyCode == 115) {
|
|
e.preventDefault();
|
|
context.emit('resolve', "26");
|
|
} else if (e.key == "/" || e.keyCode == 111 || e.key == "r") {
|
|
e.preventDefault();
|
|
context.emit('resolve', -1*parseFloat(e.target.value));
|
|
}
|
|
|
|
}
|
|
return { check_remove, keyhandler }
|
|
},
|
|
template: html`
|
|
<input v-index="active" :value="input" @keydown="keyhandler" @keydown.enter.stop="$emit('resolve', $event.target.value)" @keydown.backspace="check_remove($event)">
|
|
`
|
|
}
|
|
|
|
|
|
const xoi = {
|
|
props: ['page', 'active', 'stack', 'inspect', 'watch'],
|
|
components: {
|
|
"d-player": player,
|
|
"d-score": score,
|
|
"d-game": game
|
|
},
|
|
setup(props, context) {
|
|
const gamestack = reactive([]);
|
|
const inspectstack = reactive([]);
|
|
let current_set, current_leg;
|
|
const set_id = ref(props.page.game.sets.length - 1);
|
|
const leg_id = ref(props.page.game.sets[set_id.value].legs.length - 1);
|
|
let current_stat;
|
|
if (props.inspect) {
|
|
current_set = computed(() => props.page.game.sets[set_id.value]);
|
|
current_leg = computed(() => current_set.value.legs[leg_id.value]);
|
|
current_stat = computed( () => props.page.stats?.sets[set_id.value].legs[leg_id.value]);
|
|
}
|
|
if (props.watch) {
|
|
setInterval(async () => {
|
|
let gs = await refreshGame(props.page.id);
|
|
props.page.game = gs.game;
|
|
props.page.stats = gs.stats;
|
|
}, 2000);
|
|
}
|
|
const computedProps = getGameProps(props.page, current_set, current_leg);
|
|
if (!props.inspect) {
|
|
current_stat = computed( () => props.page.stats?.sets[props.page.stats.sets.length-1].legs[computedProps.current_set.value.legs.length-1]);
|
|
}
|
|
|
|
const mounted = onMounted(async () => {
|
|
if (props.inspect || props.watch) {
|
|
} else {
|
|
const winner = await gameHandler(gamestack, props.stack, props.page, computedProps)
|
|
context.emit('resolve', winner);
|
|
}
|
|
})
|
|
return { ...computedProps, set_id, leg_id, gamestack, inspectstack, current_stat }
|
|
},
|
|
template: html`
|
|
<div class="xoi">
|
|
<div class="bigToGo one" :class="{'active' : current_player==0 }">{{ current_toGo[0] }}</div>
|
|
<d-score :page="page" :justlegs="page && page.sets == 1" :current_set_points="current_set_points" :current_leg_points="current_leg_points"></d-score>
|
|
<div class="bigToGo two" :class="{'active' : current_player==1 }">{{ current_toGo[1] }}</div>
|
|
<d-player class="player1" :page="page" :id="0" :current_stat="current_stat"></d-player>
|
|
<d-game :active="active" :stack="stack" :players="page.players" :max="page.modus" :current_leg="current_leg" >
|
|
<d-renderer :stack="gamestack"></d-renderer>
|
|
</d-game>
|
|
<d-player class="player2" :page="page" :id="1" :current_stat="current_stat"></d-player>
|
|
<div class="nav" v-if="inspect">
|
|
<span class="label" v-if="page.sets != 1">Set:</span><d-plainElem :text="set_id+1" v-if="page.sets != 1" v-index="active" @click="set_id = (set_id+1)%page.game.sets.length; leg_id=0"></d-plainElem><span class="label">Leg:</span><d-plainElem style="padding:0.2em 0.5em" :text="leg_id+1" v-index="active" v-autofocus="true" @click="leg_id = (leg_id+1)%current_set.legs.length"></d-plainElem>
|
|
</div>
|
|
<div class="nav" v-if="!!!inspect">
|
|
<span>Shortkeys:</span><span>60 (F1)</span><span>45 (F2)</span><span>41 (F3)</span><span>26 (F4)</span>
|
|
</div>
|
|
</div>
|
|
`
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Dialogs
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
const numDartsDialog = () => {
|
|
const buttons = [];
|
|
for (var i = 1; i <= 3; i++) {
|
|
buttons.push({
|
|
"component": "d-plainElem",
|
|
"props" : {
|
|
"text": `${i}`
|
|
},
|
|
"result" : i
|
|
})
|
|
}
|
|
return {
|
|
"withshortkey": true,
|
|
"title": "Congratulations!",
|
|
"text": `How many darts did you need?`,
|
|
"buttons": buttons
|
|
};
|
|
}
|
|
|
|
const numCheckoutTriesDialog = (numDarts, start=1) => {
|
|
const buttons = [];
|
|
for (var i = start; i <= numDarts; i++) {
|
|
buttons.push({
|
|
"component": "d-plainElem",
|
|
"props" : {
|
|
"text": `${i}`,
|
|
"autofocus": i == 0,
|
|
"data-tabindex": i
|
|
},
|
|
"result" : i
|
|
})
|
|
}
|
|
return {
|
|
"withshortkey": true,
|
|
"title": "Checkout Tries",
|
|
"text": `How many tries on a Checkout?` ,
|
|
"buttons": buttons
|
|
};
|
|
}
|
|
|
|
const numCheckinTriesDialog = (numDarts, start=1) => {
|
|
const buttons = [];
|
|
for (var i = start; i <= numDarts; i++) {
|
|
buttons.push({
|
|
"component": "d-plainElem",
|
|
"props" : {
|
|
"text": `${i}`,
|
|
"autofocus": i == 0,
|
|
"data-tabindex": i
|
|
},
|
|
"result" : i
|
|
})
|
|
}
|
|
return {
|
|
"withshortkey": true,
|
|
"title": "Checkin Tries",
|
|
"text": `How many tries on a Checkin?` ,
|
|
"buttons": buttons
|
|
};
|
|
}
|
|
|
|
const impossibleDialog = (sum) => {
|
|
return {
|
|
"withshortkey": true,
|
|
"title": "Impossible",
|
|
"text": `A score of ${sum} is not possible`,
|
|
"buttons": [{
|
|
"component": "d-plainElem",
|
|
"props" : {
|
|
"text": "ok"
|
|
},
|
|
"result" : "ok"
|
|
}]}
|
|
}
|
|
|
|
const gameOverDialog = (winner, points, breaks) => {
|
|
return {
|
|
"withshortkey": true,
|
|
"title": `Game Over`,
|
|
"text": `${winner != "DRAW" ? "The winner is": ""} ${winner} with ${points[0]}-${points[1]} (Breaks: ${breaks[0]}-${breaks[1]})`,
|
|
"buttons": [
|
|
{
|
|
"component": "d-plainElem",
|
|
"props" : {
|
|
"text": "End Game"
|
|
},
|
|
"result" : "end"
|
|
},{
|
|
"component": "d-plainElem",
|
|
"props" : {
|
|
"text": "Continue"
|
|
},
|
|
"result" : "continue"
|
|
}]
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// API
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function refreshGame(id){
|
|
return getQuery(`site.find('${id}')`, {
|
|
select: {
|
|
game: "page.rounds.parseJSON",
|
|
stats: "page.stats.parseJSON"
|
|
}
|
|
})
|
|
}
|
|
|
|
function getGame(id){
|
|
return getQuery(`site.find('${id}')`, {
|
|
select: {
|
|
title: "page.title",
|
|
id: "page.id",
|
|
modus: "page.max.toInt",
|
|
in: "page.in",
|
|
out: "page.out",
|
|
game: "page.rounds.parseJSON",
|
|
stats: "page.stats.parseJSON",
|
|
sets: "page.sets.toInt",
|
|
legs: "page.legs.toInt",
|
|
startdate: "page.Startdate",
|
|
enddate: "page.Enddate",
|
|
tournamentStats: "page.tournamentStats",
|
|
participants: {
|
|
query: "page.parent.participants.toPages.sortBy('forename')",
|
|
select:{
|
|
forename: "page.forename",
|
|
surname: "page.surname",
|
|
nickname: "page.nickname",
|
|
uuid: "page.uuid",
|
|
img: "page.pic.toFile?.url"
|
|
}
|
|
},
|
|
players: {
|
|
query: "page.players.toPages",
|
|
select:{
|
|
forename: "page.forename",
|
|
surname: "page.surname",
|
|
nickname: "page.nickname",
|
|
uuid: "page.uuid",
|
|
img: "page.pic.toFile?.thumbnail(350).url"
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
function savePregame(page){
|
|
return setKirby(page.id, {
|
|
sets: page.sets,
|
|
legs: page.legs,
|
|
max: page.modus,
|
|
in: page.in,
|
|
players: page.players.map((p) => p.uuid),
|
|
startdate: page.startdate,
|
|
rounds: page.game ? JSON.stringify(page.game) : "",
|
|
stats: page.stats ? JSON.stringify(page.stats) : ""
|
|
});
|
|
}
|
|
|
|
function saveGame(page){
|
|
return setKirby(page.id, {
|
|
rounds: page.game ? JSON.stringify(page.game) : "",
|
|
stats: page.stats ? JSON.stringify(page.stats) : "",
|
|
enddate: page.enddate,
|
|
});
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// State Machine
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Checkout Pipeline
|
|
const checkoutPipeline = (stack) => {
|
|
return {
|
|
0: async (sum, reGet) => {
|
|
// Ask for num Darts
|
|
const [numDarts, error] = await overlayAndGet("d-dialog", numDartsDialog(), stack, reGet);
|
|
if (error != undefined) {
|
|
popLastElem(stack);
|
|
return [undefined, [-1, -1]];
|
|
}
|
|
return [1, [numDarts, 1]];
|
|
},
|
|
1: async ([numDarts, start], reGet) => {
|
|
// Ask for checkoutTries
|
|
if (numDarts <= start) {
|
|
// Store with one checkout try
|
|
popLastElem(stack);
|
|
return [undefined, [1, 1]];
|
|
}
|
|
const [tries, error] = await overlayAndPop("d-dialog", numCheckoutTriesDialog(numDarts, start), stack);
|
|
if (error != undefined) {
|
|
if (start == 0) {
|
|
return [undefined, [-1, -1]];
|
|
}
|
|
return [-1, undefined];
|
|
}
|
|
if (start == 1) {
|
|
popLastElem(stack);
|
|
}
|
|
return [undefined, [numDarts, tries]];
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
// Game State Machine
|
|
const gameStateMachine = (gamestack, stack, page, computedProps) => {
|
|
return {
|
|
0: async (input, reGet) => {
|
|
// Dispatcher
|
|
if (page.stats.winner){
|
|
return [undefined, page.stats.winner];
|
|
}
|
|
// Check for Game State
|
|
return [1, undefined];
|
|
},
|
|
1: async (input, reGet) => {
|
|
// Normal Game Loop
|
|
// Get Game Input
|
|
let [visit, error] = await overlayAndPop("d-gameinput", { input: input }, gamestack);
|
|
|
|
if (parseFloat(visit) < 0 ){
|
|
visit = ""+(computedProps.current_toGo.value[computedProps.current_player.value * 1] - parseFloat(visit)*-1);
|
|
}
|
|
// back/delete last throw
|
|
if (error != undefined) {
|
|
const val = removeLastVisit(page);
|
|
saveGame(page);
|
|
if (val == undefined) return [1, val];
|
|
return [1, val.join(",")];
|
|
}
|
|
|
|
// Validate throw
|
|
let numDarts, tries;
|
|
const [ret, sum] = computedProps.checkVisit(visit);
|
|
if (ret == -1) {
|
|
// Impossible
|
|
const [_, error] = await overlayAndPop("d-dialog", impossibleDialog(sum), stack);
|
|
return [1, visit];
|
|
} else if (ret == -2) {
|
|
// Bust TODO
|
|
const [_, error] = await overlayAndPop("d-dialog", impossibleDialog(sum), stack);
|
|
return [1, visit];
|
|
} else if (ret == 3) {
|
|
// Checkins
|
|
const [ret, error] = await overlayAndPop("d-dialog", numCheckinTriesDialog(3), stack);
|
|
if (error) {
|
|
return [1, visit];
|
|
}
|
|
storeVisit(page, visit.split(","), sum, 3, 0, ret);
|
|
saveGame(page);
|
|
return [1, undefined]
|
|
} else if (ret == 2) {
|
|
// Normal
|
|
storeVisit(page, visit.split(","), sum, 3, 0);
|
|
saveGame(page);
|
|
return [1, undefined]
|
|
}
|
|
// checkout:
|
|
if (ret == 0) {
|
|
// Checkout: Ask for num Darts
|
|
[numDarts, tries] = await powerStateMachine(checkoutPipeline(stack), stack, /*initState=*/0, /*initInput=*/sum);
|
|
} else if (ret == 1) {
|
|
// <=50: Ask for checkout tries
|
|
[numDarts, tries] = await powerStateMachine(checkoutPipeline(stack), stack, /*initState=*/1, /*initInput=*/[3,0]);
|
|
}
|
|
|
|
if (numDarts == -1) {
|
|
// Error/Back
|
|
return [1, visit];
|
|
} else {
|
|
const points = storeVisit(page, visit.split(","), sum, numDarts, tries);
|
|
if (points){
|
|
// Game Over
|
|
const winner = getFinalWinner(page);
|
|
let name = "DRAW"
|
|
if (winner != -2){
|
|
name = page.players[winner].forename;
|
|
}
|
|
const [answer, error] = await overlayAndPop("d-dialog", gameOverDialog(name, points[1], computedProps.breaks.value), stack);
|
|
if (answer == "end") {
|
|
saveGame(page);
|
|
return [undefined, page.stats.winner];
|
|
} else {
|
|
extension(page);
|
|
saveGame(page);
|
|
}
|
|
} else {
|
|
saveGame(page);
|
|
}
|
|
return [1, undefined];
|
|
}
|
|
},
|
|
};
|
|
}
|
|
|
|
export const gameHandler = async (gamestack, stack, page, computedProps) => {
|
|
const sm = gameStateMachine(gamestack, stack, page, computedProps);
|
|
return powerStateMachine(sm, gamestack);
|
|
}
|
|
|
|
function getUrlParam(name) {
|
|
var url_string = window.location;
|
|
let url = new URL(url_string);
|
|
let params = new URLSearchParams(url.search);
|
|
return params.get(name);
|
|
}
|
|
|
|
// General State Machine
|
|
const stateMachine = (stack, page) => {
|
|
return {
|
|
0: async (input, reGet) => {
|
|
// Dispatcher
|
|
// Check if in watchmode
|
|
// Check Game State:
|
|
if (page.players.length != 2 || page.startdate === undefined || page.startdate === ""){
|
|
// Pre Game
|
|
return [1, page];
|
|
} else if (page.enddate === undefined || page.enddate === ""){
|
|
if (page.game === "" || page.game === undefined || page.game === null) {
|
|
// Who won Bull?
|
|
return [2, page];
|
|
} else {
|
|
// In Game
|
|
if (getUrlParam("w") != null){
|
|
// Watch Game
|
|
return [5, undefined];
|
|
}
|
|
return [3, page];
|
|
}
|
|
} else {
|
|
// Post Game
|
|
return [4, undefined];
|
|
}
|
|
},
|
|
1: async (page, reGet) => {
|
|
// Pre Game
|
|
const [result, error] = await overlayAndPop("d-pregame", { page: page}, stack);
|
|
if (error != undefined) {
|
|
var re = /^https?:\/\/[^/]+/i;
|
|
window.setTimeout(() => {
|
|
window.location.href = re.exec(window.location.href)[0];
|
|
return false;
|
|
},1);
|
|
return [undefined, undefined];
|
|
}
|
|
page.startdate = formatDate(new Date(Date.now()));
|
|
// Update game in database
|
|
const ret = await savePregame(page);
|
|
if (ret.status != "ok") {
|
|
console.error("Error save page:", ret.status, ret.error);
|
|
}
|
|
return [0, page];
|
|
},
|
|
2: async (page, reGet) => {
|
|
// Ask for Bull
|
|
const [result, error] = await overlayAndPop("d-bullselect", { players: page.players}, stack);
|
|
if (error != undefined) {
|
|
return [1, page];
|
|
}
|
|
// reorderPlayer
|
|
if (result !== page.players[0]){
|
|
page.players = [page.players[1], page.players[0]];
|
|
}
|
|
// Setup Game
|
|
initGame(page);
|
|
initStats(page);
|
|
const ret = await savePregame(page);
|
|
return [3, page];
|
|
},
|
|
3: async (page, reGet) => {
|
|
// In Game
|
|
const [result, error] = await overlayAndPop("d-xoi", { page: page, inspect: false }, stack);
|
|
return [4, result];
|
|
},
|
|
4: async (winnerUUID, reGet) => {
|
|
const [res, e] = await overlayAndPop("d-xoi", { page: page, inspect: true }, stack);
|
|
var re = /^https?:\/\/[^/]+/i;
|
|
window.setTimeout(() => {
|
|
window.location.href = re.exec(window.location.href)[0];
|
|
return false;
|
|
},1);
|
|
return [4, res];
|
|
},
|
|
5: async (_, reGet) => {
|
|
const [res, e] = await overlayAndPop("d-xoi", { page: page, watch: true }, stack);
|
|
var re = /^https?:\/\/[^/]+/i;
|
|
window.setTimeout(() => {
|
|
window.location.href = re.exec(window.location.href)[0];
|
|
return false;
|
|
},1);
|
|
return [5, res];
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Exports
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
export const initXoiView = (app) => {
|
|
app.component('d-pregame', pregame).component('d-xoi', xoi).component('d-bullselect', bullselect).component('d-gameinput', gameinput)
|
|
}
|
|
|
|
export const xoiHandler = async (stack, id) => {
|
|
const page = reactive(await getGame(id));
|
|
const sm = stateMachine(stack, page);
|
|
await powerStateMachine(sm, stack);
|
|
}
|
|
|