diff --git a/TableturfBattleClient/index.html b/TableturfBattleClient/index.html
index 8b682bd..bb16e4a 100644
--- a/TableturfBattleClient/index.html
+++ b/TableturfBattleClient/index.html
@@ -182,6 +182,15 @@
+
Turns left
12
diff --git a/TableturfBattleClient/src/Pages/GamePage.ts b/TableturfBattleClient/src/Pages/GamePage.ts
index 7fa63c6..a7f292a 100644
--- a/TableturfBattleClient/src/Pages/GamePage.ts
+++ b/TableturfBattleClient/src/Pages/GamePage.ts
@@ -22,6 +22,11 @@ const resultElement = document.getElementById('result')!;
const playerBars = Array.from(document.getElementsByClassName('playerBar'), el => new PlayerBar(el as HTMLDivElement));
playerBars.sort((a, b) => a.playerIndex - b.playerIndex);
+const showDeckContainer = document.getElementById('showDeckContainer')!;
+const showDeckListElement = document.getElementById('showDeckList')!;
+const showDeckButtons: CardButton[] = [ ];
+const showDeckCloseButton = document.getElementById('showDeckCloseButton') as HTMLButtonElement;
+
const playContainers = Array.from(document.getElementsByClassName('playContainer')) as HTMLDivElement[];
playContainers.sort((a, b) => parseInt(a.dataset.index || '0') - parseInt(b.dataset.index || '0'));
@@ -189,80 +194,106 @@ function showResult() {
}
}
-function updateHand(cards: any[]) {
+function clearShowDeck() {
+ showDeckContainer.hidden = true;
+ clearChildren(showDeckListElement);
+ showDeckButtons.splice(0);
+}
+
+function updateHand(playerData: PlayerData) {
for (const button of handButtons) {
handContainer.removeChild(button.element);
}
handButtons.splice(0);
- if (!currentGame?.me) return;
- currentGame.me.hand = cards.map(Card.fromJson);
- if (cards) {
- for (let i = 0; i < currentGame.me.hand.length; i++) {
- const card = currentGame.me.hand[i];
- const button = new CardButton('radio', card);
- button.inputElement.name = 'handCard';
- handButtons.push(button);
- button.inputElement.addEventListener('input', e => {
- if (button.checked) {
- for (const button2 of handButtons) {
- if (button2 != button)
- button2.element.classList.remove('checked');
- }
- if (passButton.checked) {
- if (canPlay) {
- canPlay = false;
- // Send the play to the server.
- let req = new XMLHttpRequest();
- req.open('POST', `${config.apiBaseUrl}/games/${currentGame!.id}/play`);
- req.addEventListener('error', () => communicationError());
- let data = new URLSearchParams();
- data.append('clientToken', clientToken);
- data.append('cardNumber', card.number.toString());
- data.append('isPass', 'true');
- req.send(data.toString());
+ if (showDeckButtons.length == 0) {
+ for (const card of playerData.deck!) {
+ const button = new CardButton('checkbox', card);
+ button.inputElement.hidden = true;
+ showDeckButtons.push(button);
- board.autoHighlight = false;
- }
- } else {
- board.cardPlaying = card;
- if (isNaN(board.highlightX) || isNaN(board.highlightY)) {
- board.highlightX = board.startSpaces[board.playerIndex!].x - 3;
- board.highlightY = board.startSpaces[board.playerIndex!].y - 3;
- }
- board.cardRotation = 0;
- board.refreshHighlight();
- board.table.focus();
- }
- }
- });
- button.inputElement.addEventListener('keydown', e => {
- switch (e.key) {
- case 'ArrowUp':
- if (i >= 2)
- handButtons[i - 2].inputElement.focus();
- e.preventDefault();
- break;
- case 'ArrowDown':
- if (i < 2)
- handButtons[i + 2].inputElement.focus();
- e.preventDefault();
- break;
- case 'ArrowLeft':
- if (i % 2 != 0)
- handButtons[i - 1].inputElement.focus();
- e.preventDefault();
- break;
- case 'ArrowRight':
- if (i % 2 == 0)
- handButtons[i + 1].inputElement.focus();
- e.preventDefault();
- break;
- }
- });
- handContainer.appendChild(button.element);
+ const li = document.createElement('li');
+ li.appendChild(button.element);
+ showDeckListElement.appendChild(li);
}
}
+
+ for (const button of showDeckButtons) {
+ const li = button.element.parentElement!;
+ if (playerData.hand!.find(c => c.number == button.card.number))
+ li.className = 'inHand';
+ else if (playerData.cardsUsed.includes(button.card.number))
+ li.className = 'used';
+ else
+ li.className = 'unused';
+ }
+
+ if (!currentGame?.me) return;
+ currentGame.me.hand = playerData.hand!.map(Card.fromJson);
+ for (let i = 0; i < currentGame.me.hand.length; i++) {
+ const card = currentGame.me.hand[i];
+ const button = new CardButton('radio', card);
+ button.inputElement.name = 'handCard';
+ handButtons.push(button);
+ button.inputElement.addEventListener('input', e => {
+ if (button.checked) {
+ for (const button2 of handButtons) {
+ if (button2 != button)
+ button2.element.classList.remove('checked');
+ }
+ if (passButton.checked) {
+ if (canPlay) {
+ canPlay = false;
+ // Send the play to the server.
+ let req = new XMLHttpRequest();
+ req.open('POST', `${config.apiBaseUrl}/games/${currentGame!.id}/play`);
+ req.addEventListener('error', () => communicationError());
+ let data = new URLSearchParams();
+ data.append('clientToken', clientToken);
+ data.append('cardNumber', card.number.toString());
+ data.append('isPass', 'true');
+ req.send(data.toString());
+
+ board.autoHighlight = false;
+ }
+ } else {
+ board.cardPlaying = card;
+ if (isNaN(board.highlightX) || isNaN(board.highlightY)) {
+ board.highlightX = board.startSpaces[board.playerIndex!].x - 3;
+ board.highlightY = board.startSpaces[board.playerIndex!].y - 3;
+ }
+ board.cardRotation = 0;
+ board.refreshHighlight();
+ board.table.focus();
+ }
+ }
+ });
+ button.inputElement.addEventListener('keydown', e => {
+ switch (e.key) {
+ case 'ArrowUp':
+ if (i >= 2)
+ handButtons[i - 2].inputElement.focus();
+ e.preventDefault();
+ break;
+ case 'ArrowDown':
+ if (i < 2)
+ handButtons[i + 2].inputElement.focus();
+ e.preventDefault();
+ break;
+ case 'ArrowLeft':
+ if (i % 2 != 0)
+ handButtons[i - 1].inputElement.focus();
+ e.preventDefault();
+ break;
+ case 'ArrowRight':
+ if (i % 2 == 0)
+ handButtons[i + 1].inputElement.focus();
+ e.preventDefault();
+ break;
+ }
+ });
+ handContainer.appendChild(button.element);
+ }
}
(document.getElementById('redrawNoButton') as HTMLButtonElement).addEventListener('click', redrawButton_click);
@@ -385,6 +416,18 @@ function focusFirstEnabledHandCard() {
const firstEnabledButton = handButtons.find(b => b.enabled);
if (firstEnabledButton)
firstEnabledButton.inputElement.focus();
+ else
+ passButton.input.focus();
+}
+
+function toggleShowDeck() {
+ if (showDeckContainer.hidden) {
+ showDeckContainer.hidden = false;
+ showDeckCloseButton.focus();
+ } else {
+ showDeckContainer.hidden = true;
+ focusFirstEnabledHandCard();
+ }
}
rotateLeftButton.addEventListener('click', () => {
@@ -395,6 +438,8 @@ rotateRightButton.addEventListener('click', () => {
board.cardRotation++;
board.refreshHighlight();
});
+gameDeckButton.addEventListener('click', toggleShowDeck);
+showDeckCloseButton.addEventListener('click', () => showDeckContainer.hidden = true);
document.addEventListener('keydown', e => {
if (!pages.get('game')!.hidden) {
@@ -415,6 +460,10 @@ document.addEventListener('keydown', e => {
}
e.preventDefault();
break;
+ case 'd':
+ if (!gameButtonsContainer.hidden) toggleShowDeck();
+ e.preventDefault();
+ break;
}
}
});
diff --git a/TableturfBattleClient/src/Pages/PreGamePage.ts b/TableturfBattleClient/src/Pages/PreGamePage.ts
index 35ba565..3067848 100644
--- a/TableturfBattleClient/src/Pages/PreGamePage.ts
+++ b/TableturfBattleClient/src/Pages/PreGamePage.ts
@@ -129,6 +129,8 @@ function getGameInfo(gameID: string, myPlayerIndex: number | null) {
board.playerIndex = myPlayerIndex;
initLobbyPage(window.location.toString());
+ showDeckButtons.splice(0);
+ clearShowDeck();
myPlayerIndex = setupWebSocket(gameID, myPlayerIndex);
}
diff --git a/TableturfBattleClient/src/app.ts b/TableturfBattleClient/src/app.ts
index 6bd27c7..73b43a1 100644
--- a/TableturfBattleClient/src/app.ts
+++ b/TableturfBattleClient/src/app.ts
@@ -104,7 +104,7 @@ function onGameStateChange(game: any, playerData: any) {
case GameState.Ongoing:
case GameState.Ended:
if (playerData)
- updateHand(playerData.hand);
+ updateHand(playerData);
board.autoHighlight = false;
redrawModal.hidden = true;
showPage('game');
@@ -294,11 +294,13 @@ function setupWebSocket(gameID: string, myPlayerIndex: number | null) {
(async () => {
await playInkAnimations(payload.data, anySpecialAttacks);
- updateHand(payload.playerData.hand);
+ updateHand(payload.playerData);
turnNumberLabel.setTurnNumber(payload.data.game.turnNumber);
clearPlayContainers();
if (payload.event == 'gameEnd') {
gameButtonsContainer.hidden = true;
+ passButton.enabled = false;
+ specialButton.enabled = false;
gamePage.classList.add('gameEnded');
showResult();
} else {
diff --git a/TableturfBattleClient/tableturf.css b/TableturfBattleClient/tableturf.css
index a86f943..9396202 100644
--- a/TableturfBattleClient/tableturf.css
+++ b/TableturfBattleClient/tableturf.css
@@ -471,6 +471,56 @@ dialog::backdrop {
#playControls > label:is(:active, .checked) { background: var(--special-accent-colour-1); }
#playControls > label.disabled { background: grey; }
+#showDeckContainer {
+ grid-column: 1 / span 3;
+ grid-row: 2 / -2;
+ background: black;
+ z-index: 2;
+}
+#showDeck {
+ display: flex;
+ flex-flow: column;
+ height: 100%;
+ text-align: center;
+}
+#showDeckList {
+ display: grid;
+ grid-template-columns: auto auto auto;
+ list-style-type: none;
+ font-size: smaller;
+ overflow-y: auto;
+ padding: 0;
+ margin: 0;
+ justify-items: center;
+}
+#showDeckList > li {
+ margin: 0.25em;
+ border-radius: 0.5em;
+ text-align: center;
+}
+#showDeckList > li.inHand {
+ outline: 0.25em solid;
+}
+#showDeckList > li.used {
+ opacity: 0.35;
+}
+#showDeckCloseButton {
+ border: none;
+ font-family: inherit;
+ color: inherit;
+ width: 40%;
+ grid-row: button-row;
+ margin: 1em;
+ padding: 0 0.5em;
+ text-align: center;
+ background: var(--primary-colour-1);
+ text-shadow: 0 0 4px black;
+ border-radius: 0.5em;
+}
+#showDeckCloseButton:hover { background: var(--special-colour-1); }
+#showDeckCloseButton:focus-within { outline: 2px solid var(--special-accent-colour-2); }
+#showDeckCloseButton:active { background: var(--special-accent-colour-1); }
+
.playContainer { text-align: center; }
.playContainer[data-index="0"] { grid-column: play-column-1; grid-row: 2 / -1; align-self: end; }
@@ -510,9 +560,6 @@ dialog::backdrop {
opacity: 1;
color: var(--primary-colour-1);
}
-#gameDeckButton {
- display: none;
-}
#boardSection {
grid-column: board-column;
@@ -1007,6 +1054,14 @@ dialog::backdrop {
overflow: scroll visible;
}
+ #showDeckContainer {
+ grid-column: 1 / -1;
+ grid-row: 2 / -1;
+ }
+ #showDeckList .card {
+ width: 30vw;
+ }
+
.playContainer:is([data-index="0"], [data-index="1"], [data-index="2"], [data-index="3"]) {
font-size: x-small;
grid-column: 1 / -1;