Flip board view for players 2 and 4

This commit is contained in:
Andrio Celos
2022-12-26 19:13:27 +11:00
parent 94739183ac
commit 9dd44b8351
5 changed files with 115 additions and 51 deletions

View File

@@ -85,7 +85,7 @@
<input type="radio" id="stageRandomButton" name="stage" value="random" checked/> Random
</label>
</div>
<p id="stageListLoadingSection"><div class="lds-ripple"><div></div><div></div></div> Loading stages...</p>
<div id="stageListLoadingSection"><div class="lds-ripple"><div></div><div></div></div> Loading stages...</div>
</form>
</section>
<div id="lobbyDeckSection" hidden>
@@ -155,14 +155,16 @@
<a id="resultLeaveButton" href="#">Leave game</a>
</div>
<div id="replayControls" hidden>
<input type="checkbox" id="replayFlipBox" autocomplete="off"/>
<label for="replayFlipBox">Flip board</label>
<button id="replayPreviousButton">Previous turn</button>
<button id="replayNextButton">Next turn</button>
<a id="replayLeaveButton" href="#">Exit replay</a>
</div>
<div class="playerBar" data-index="0">
<div class="playerBar" data-index="2">
<div class="result"></div>
<div class="name">Player 1</div>
<div class="name">Player 3</div>
<div class="specialPoints">&nbsp;</div>
<div class="playerStats">
<div class="statSpecialPoints">
@@ -175,9 +177,9 @@
</div>
</div>
</div>
<div class="playerBar" data-index="2">
<div class="playerBar" data-index="0">
<div class="result"></div>
<div class="name">Player 3</div>
<div class="name">Player 1</div>
<div class="specialPoints">&nbsp;</div>
<div class="playerStats">
<div class="statSpecialPoints">
@@ -205,12 +207,10 @@
<div id="turnNumberLabel">12</div>
</div>
<div id="scoreSection">
<div id="pointsContainers">
<div class="pointsContainer" data-index="2"><span class="points">69</span><span class="pointsToContainer">&rarrhk; <span class="pointsTo">60</span></span><span class="pointsDelta">+1</span></div>
<div class="pointsContainer" data-index="1"><span class="points">69</span><span class="pointsToContainer">&rarrhk; <span class="pointsTo">60</span></span><span class="pointsDelta">+1</span></div>
<div class="pointsContainer" data-index="0"><span class="points">69</span><span class="pointsToContainer">&rarrhk; <span class="pointsTo">60</span></span><span class="pointsDelta">+1</span></div>
<div class="pointsContainer" data-index="3"><span class="points">69</span><span class="pointsToContainer">&rarrhk; <span class="pointsTo">60</span></span><span class="pointsDelta">+1</span></div>
</div>
<div class="pointsContainer" data-index="1"><span class="points">69</span><span class="pointsToContainer">&rarrhk; <span class="pointsTo">60</span></span><span class="pointsDelta">+1</span></div>
<div class="pointsContainer" data-index="3"><span class="points">69</span><span class="pointsToContainer">&rarrhk; <span class="pointsTo">60</span></span><span class="pointsDelta">+1</span></div>
<div class="pointsContainer" data-index="2"><span class="points">69</span><span class="pointsToContainer">&rarrhk; <span class="pointsTo">60</span></span><span class="pointsDelta">+1</span></div>
<div class="pointsContainer" data-index="0"><span class="points">69</span><span class="pointsToContainer">&rarrhk; <span class="pointsTo">60</span></span><span class="pointsDelta">+1</span></div>
</div>
<div id="boardSection">
<table id="gameBoard" tabindex="0"></table>
@@ -231,8 +231,8 @@
<div class="playContainer" data-index="1"></div>
<div class="playContainer" data-index="3"></div>
<div class="playContainer" data-index="0"></div>
<div class="playContainer" data-index="2"></div>
<div class="playContainer" data-index="0"></div>
</div>
<div id="deckListPage" hidden>
<section id="deckEditordeckListPage">

View File

@@ -10,6 +10,7 @@ class Board {
autoHighlight = true;
touchscreenMode = false;
flip = false;
highlightX = NaN;
highlightY = NaN;
@@ -38,16 +39,16 @@ class Board {
this.moveHighlight((x, y, r) => [x, y, r - 1], false);
break;
case 'ArrowUp':
this.moveHighlight((x, y, r) => [x, y - 1, r], true);
this.moveHighlight((x, y, r) => [x, y + (this.flip ? 1 : -1), r], true);
break;
case 'ArrowDown':
this.moveHighlight((x, y, r) => [x, y + 1, r], true);
this.moveHighlight((x, y, r) => [x, y + (this.flip ? -1 : 1), r], true);
break;
case 'ArrowLeft':
this.moveHighlight((x, y, r) => [x - 1, y, r], true);
this.moveHighlight((x, y, r) => [x + (this.flip ? 1 : -1), y, r], true);
break;
case 'ArrowRight':
this.moveHighlight((x, y, r) => [x + 1, y, r], true);
this.moveHighlight((x, y, r) => [x + (this.flip ? -1 : 1), y, r], true);
break;
case 'Enter': case ' ':
if (this.onclick)
@@ -67,9 +68,9 @@ class Board {
const touch = e.changedTouches[0];
if (isNaN(this.highlightX) || isNaN(this.highlightY) || isNaN(this.cardRotation)) {
const startSpace = this.startSpaces[this.playerIndex];
this.highlightX = startSpace.x - 3;
this.highlightY = startSpace.y - 3;
this.cardRotation = 0;
this.highlightX = startSpace.x - (this.flip ? 4 : 3);
this.highlightY = startSpace.y - (this.flip ? 4 : 3);
this.cardRotation = this.flip ? 2 : 0;
this.refreshHighlight();
}
this.touch = [touch.identifier, touch.pageX, touch.pageY, this.highlightX, this.highlightY];
@@ -105,7 +106,7 @@ class Board {
if (this.playerIndex == null) return;
if (this.highlightedCells.length == 0 || isNaN(this.highlightX) || isNaN(this.highlightY) || isNaN(this.cardRotation)) {
const startSpace = this.startSpaces[this.playerIndex];
[this.highlightX, this.highlightY, this.cardRotation] = [startSpace.x - 3, startSpace.y - 3, 0];
[this.highlightX, this.highlightY, this.cardRotation] = [startSpace.x - (this.flip ? 4 : 3), startSpace.y - (this.flip ? 4 : 3), 0];
} else {
let [x, y, r] = move(this.highlightX, this.highlightY, this.cardRotation);
let clampedPosition = clamp
@@ -193,44 +194,33 @@ class Board {
this.highlightedCells.splice(0);
}
resize(grid: Space[][]) {
if (grid.length == 0)
throw new Error('Grid must not be empty.');
this.grid = grid || this.grid;
resize(grid?: Space[][]) {
if (grid) this.grid = grid;
clearChildren(this.table);
this.cells.splice(0);
this.highlightedCells.splice(0);
const boardWidth = grid.length;
const boardHeight = grid[0].length;
const boardWidth = this.grid.length;
const boardHeight = this.grid[0].length;
this.table.style.setProperty('--board-width', boardWidth.toString());
this.table.style.setProperty('--board-height', boardHeight.toString());
const trs: HTMLTableRowElement[] = [ ];
for (let y = 0; y < boardHeight; y++) {
const tr = document.createElement('tr');
trs.push(tr);
this.table.appendChild(tr);
}
for (let x = 0; x < boardWidth; x++) {
const col: HTMLTableCellElement[] = [ ];
for (let y = 0; y < grid[x].length; y++) {
for (let y = 0; y < this.grid[x].length; y++) {
const td = document.createElement('td');
td.dataset.x = x.toString();
td.dataset.y = y.toString();
trs[y].appendChild(td);
col.push(td);
td.addEventListener('mousemove', e => {
if (e.buttons == 0)
this.touchscreenMode = false;
if (!this.touchscreenMode) {
if (this.autoHighlight && this.cardPlaying != null) {
const x = parseInt((e.target as HTMLTableCellElement).dataset.x!) - 3;
const y = parseInt((e.target as HTMLTableCellElement).dataset.y!) - 3;
const x = parseInt((e.target as HTMLTableCellElement).dataset.x!) - (this.flip ? 4 : 3);
const y = parseInt((e.target as HTMLTableCellElement).dataset.y!) - (this.flip ? 4 : 3);
if (x != this.highlightX || y != this.highlightY) {
this.highlightX = x;
this.highlightY = y;
@@ -244,8 +234,8 @@ class Board {
if (this.touchscreenMode) {
this.onclick(this.highlightX, this.highlightY);
} else {
const x = parseInt((e.target as HTMLTableCellElement).dataset.x!) - 3;
const y = parseInt((e.target as HTMLTableCellElement).dataset.y!) - 3;
const x = parseInt((e.target as HTMLTableCellElement).dataset.x!) - (this.flip ? 4 : 3);
const y = parseInt((e.target as HTMLTableCellElement).dataset.y!) - (this.flip ? 4 : 3);
this.onclick(x, y);
}
}
@@ -262,6 +252,26 @@ class Board {
this.cells.push(col);
}
const trs: HTMLTableRowElement[] = [ ];
for (let y = 0; y < boardHeight; y++) {
const tr = document.createElement('tr');
trs.push(tr);
this.table.appendChild(tr);
}
if (this.flip) trs.reverse();
for (let y = 0; y < boardHeight; y++) {
if (this.flip) {
for (let x = boardWidth - 1; x >= 0; x--) {
trs[y].appendChild(this.cells[x][y]);
}
} else {
for (let x = 0; x < boardWidth; x++) {
trs[y].appendChild(this.cells[x][y]);
}
}
}
this.refresh();
}

View File

@@ -21,6 +21,7 @@ const resultElement = document.getElementById('result')!;
const replayControls = document.getElementById('replayControls')!;
const replayNextButton = document.getElementById('replayNextButton')!;
const replayPreviousButton = document.getElementById('replayPreviousButton')!;
const replayFlipBox = document.getElementById('replayFlipBox') as HTMLInputElement;
let replayAnimationAbortController: AbortController | null = null;
const shareReplayLinkButton = document.getElementById('shareReplayLinkButton') as HTMLButtonElement;
@@ -185,6 +186,23 @@ replayPreviousButton.addEventListener('click', _ => {
}
});
replayFlipBox.addEventListener('input', _ => {
if (currentGame == null || currentReplay == null) return;
if (replayAnimationAbortController) {
replayAnimationAbortController.abort();
replayAnimationAbortController = null;
clearPlayContainers();
turnNumberLabel.setTurnNumber(currentGame.turnNumber);
for (let i = 0; i < currentGame.players.length; i++) {
updateStats(i);
}
}
board.flip = replayFlipBox.checked;
if (board.flip) gamePage.classList.add('boardFlipped');
else gamePage.classList.remove('boardFlipped');
board.resize();
});
function loadPlayers(players: Player[]) {
gamePage.dataset.players = players.length.toString();
for (let i = 0; i < players.length; i++) {
@@ -404,10 +422,10 @@ function updateHand(playerData: PlayerData) {
} 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.highlightX = board.startSpaces[board.playerIndex!].x - (board.flip ? 4 : 3);
board.highlightY = board.startSpaces[board.playerIndex!].y - (board.flip ? 4 : 3);
}
board.cardRotation = 0;
board.cardRotation = board.flip ? 2 : 0;
board.refreshHighlight();
board.table.focus();
}

View File

@@ -88,6 +88,9 @@ function onGameStateChange(game: any, playerData: PlayerData | null) {
throw new Error('currentGame is null');
clearPlayContainers();
if (game.board) {
board.flip = playerData != null && playerData.playerIndex % 2 != 0;
if (board.flip) gamePage.classList.add('boardFlipped');
else gamePage.classList.remove('boardFlipped');
board.resize(game.board);
board.startSpaces = game.startSpaces;
board.refresh();

View File

@@ -450,14 +450,21 @@ dialog::backdrop {
grid-row: player-row-3;
}
#gamePage.boardFlipped .playerBar[data-index="0"] { grid-row: player-row-1; }
#gamePage.boardFlipped .playerBar[data-index="1"] { grid-row: player-row-0; }
#gamePage.boardFlipped .playerBar[data-index="2"] { grid-row: player-row-3; }
#gamePage.boardFlipped .playerBar[data-index="3"] { grid-row: player-row-2; }
.playerBar {
grid-column: 1 / span 2;
margin: 0 10px;
border-left: 8px solid var(--colour);
padding-left: 10px;
}
#gamePage:not([data-players="2"]) .playerBar:is([data-index="1"], [data-index="2"]) {
#gamePage:not(.boardFlipped):not([data-players="2"]) .playerBar:is([data-index="1"], [data-index="2"]),
#gamePage.boardFlipped:not([data-players="2"]) .playerBar:is([data-index="0"], [data-index="3"]) {
text-align: right;
border-left: none;
border-right: 8px solid var(--colour);
padding-right: 10px;
}
@@ -466,10 +473,17 @@ dialog::backdrop {
grid-column: score-column;
grid-row: 2 / -2;
display: flex;
flex-flow: column;
justify-content: center;
align-items: center;
gap: 2em;
}
#gamePage.boardFlipped .pointsContainer[data-index="2"] { order: 0; }
#gamePage.boardFlipped .pointsContainer[data-index="0"] { order: 1; }
#gamePage.boardFlipped .pointsContainer[data-index="3"] { order: 2; }
#gamePage.boardFlipped .pointsContainer[data-index="1"] { order: 3; }
#playControls, #resultContainer, #replayControls {
grid-column: 1 / span 2;
grid-row: hand-row;
@@ -557,11 +571,16 @@ dialog::backdrop {
.playContainer { text-align: center; }
.playContainer[data-index="0"] { grid-column: play-column-1; grid-row: 2 / -1; align-self: end; }
.playContainer[data-index="1"] { grid-column: play-column-2; grid-row: 1 / 4; align-self: start; }
.playContainer[data-index="2"] { grid-column: play-column-2; grid-row: 2 / -1; align-self: end; }
.playContainer[data-index="1"] { grid-column: play-column-2; grid-row: 1 / 4; align-self: start; }
.playContainer[data-index="0"] { grid-column: play-column-1; grid-row: 2 / -1; align-self: end; }
.playContainer[data-index="3"] { grid-column: play-column-1; grid-row: 1 / 4; align-self: start; }
#gamePage.boardFlipped .playContainer[data-index="3"] { grid-column: play-column-2; grid-row: 2 / -1; align-self: end; }
#gamePage.boardFlipped .playContainer[data-index="0"] { grid-column: play-column-2; grid-row: 1 / 4; align-self: start; }
#gamePage.boardFlipped .playContainer[data-index="1"] { grid-column: play-column-1; grid-row: 2 / -1; align-self: end; }
#gamePage.boardFlipped .playContainer[data-index="2"] { grid-column: play-column-1; grid-row: 1 / 4; align-self: start; }
.playContainer .card {
margin: 0;
}
@@ -734,7 +753,6 @@ dialog::backdrop {
.pointsContainer {
color: var(--colour);
margin: 2em 0;
}
.pointsDelta {
@@ -1040,6 +1058,11 @@ dialog::backdrop {
.playerBar[data-index="2"] { grid-column: 1 / span 4; grid-row: 3; }
.playerBar[data-index="3"] { grid-column: 1 / span 4; grid-row: 1; }
#gamePage.boardFlipped .playerBar[data-index="3"] { grid-column: 1 / span 4; grid-row: 3; }
#gamePage.boardFlipped .playerBar[data-index="2"] { grid-column: 1 / span 4; grid-row: 1; }
#gamePage.boardFlipped .playerBar[data-index="1"] { grid-column: 1 / span 4; grid-row: 3; }
#gamePage.boardFlipped .playerBar[data-index="0"] { grid-column: 1 / span 4; grid-row: 1; }
#boardSection {
grid-column: 2 / -1;
grid-row: 2;
@@ -1096,17 +1119,27 @@ dialog::backdrop {
width: 30vw;
}
.playContainer:is([data-index="0"], [data-index="1"], [data-index="2"], [data-index="3"]) {
#gamePage:is(.boardFlipped, *) .playContainer:is([data-index="0"], [data-index="1"], [data-index="2"], [data-index="3"]) {
font-size: x-small;
grid-column: 1 / -1;
left: auto;
top: auto;
z-index: -1;
display: flex;
}
.playContainer[data-index="0"] { grid-row: 2 / span 2; justify-content: start; margin: 0 0 2em 1em; }
.playContainer[data-index="1"] { grid-row: 1 / span 2; justify-content: end; margin: 2em 1em 0 0; }
#gamePage[data-players="2"] .playContainer[data-index="0"],
.playContainer[data-index="1"] { grid-row: 1 / span 2; justify-content: end; margin: 4em 1em 0 0; }
.playContainer[data-index="2"] { grid-row: 2 / span 2; justify-content: end; margin: 0 1em 2em 0; }
.playContainer[data-index="3"] { grid-row: 1 / span 2; justify-content: start; margin: 2em 0 0 1em; }
.playContainer[data-index="3"] { grid-row: 1 / span 2; justify-content: start; margin: 4em 0 0 1em; }
#gamePage[data-players="2"] .playContainer[data-index="0"] { justify-content: end; margin: 0 1em 0 0; }
#gamePage[data-players="2"] .playContainer[data-index="1"] { justify-content: end; margin: 0 1em 0 0; }
#gamePage.boardFlipped .playContainer[data-index="1"] { grid-row: 2 / span 2; justify-content: start; margin: 0 0 2em 1em; }
#gamePage.boardFlipped .playContainer[data-index="0"] { grid-row: 1 / span 2; justify-content: end; margin: 2em 1em 0 0; }
#gamePage.boardFlipped .playContainer[data-index="3"] { grid-row: 2 / span 2; justify-content: end; margin: 0 1em 2em 0; }
#gamePage.boardFlipped .playContainer[data-index="2"] { grid-row: 1 / span 2; justify-content: start; margin: 2em 0 0 1em; }
#gamePage.boardFlipped[data-players="2"] .playContainer[data-index="0"] { justify-content: end; margin: 0 1em 0 0; }
#gamePage.boardFlipped[data-players="2"] .playContainer[data-index="1"] { justify-content: end; margin: 0 1em 0 0; }
#gameButtonsContainer {
grid-column: 1;