mirror of
https://github.com/AndrioCelos/TableturfBattleApp.git
synced 2026-08-20 18:04:00 -05:00
Add more hints to the game page
This commit is contained in:
@@ -241,6 +241,7 @@
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div id="passHint" hidden>Select a card to discard.</div>
|
||||
<div id="handContainer"></div>
|
||||
|
||||
<div id="playRow">
|
||||
@@ -372,6 +373,8 @@
|
||||
<div class="playContainer" data-index="2"></div>
|
||||
<div class="playContainer" data-index="0"></div>
|
||||
|
||||
<div id="playHint" hidden></div>
|
||||
|
||||
<div id="testPlacementList"></div>
|
||||
</div>
|
||||
<div id="deckListPage" hidden>
|
||||
|
||||
@@ -124,14 +124,14 @@ class Board {
|
||||
var x2 = x + dx;
|
||||
var y2 = y + dy;
|
||||
if (x2 < 0 || x2 >= this.grid.length || y2 < 0 || y2 >= this.grid[x2].length)
|
||||
return "It cannot overlap a wall or out of bounds."; // Out of bounds.
|
||||
return 'It cannot be over a wall.'; // Out of bounds.
|
||||
const space = this.grid[x2][y2];
|
||||
if (space == Space.Wall || space == Space.OutOfBounds)
|
||||
return "It cannot overlap a wall or out of bounds.";
|
||||
return 'It cannot be over a wall.';
|
||||
if (space >= Space.SpecialInactive1)
|
||||
return "It cannot overlap a special space."
|
||||
return `It cannot be over${' <div class="playHintSpecial" aria-label="Special space"> </div>'.repeat(Math.max(currentGame?.players?.length ?? 0, 2))}.`;
|
||||
if (space != Space.Empty && !isSpecialAttack)
|
||||
return "It cannot overlap existing ink without a special attack.";
|
||||
return 'It cannot be over inked spaces.';
|
||||
if (!isAnchored) {
|
||||
// A normal play must be adjacent to ink of the player's colour.
|
||||
// A special attack must be adjacent to a special space of theirs.
|
||||
@@ -152,7 +152,7 @@ class Board {
|
||||
}
|
||||
}
|
||||
}
|
||||
return isAnchored ? null : (isSpecialAttack ? "It must be next to one of your special spaces." : "It must be next to your turf.");
|
||||
return isAnchored ? null : (isSpecialAttack ? 'It\'s not next to <div class="playHintSpecial"> </div>.' : 'It\'s not touching your colour of ink.');
|
||||
}
|
||||
|
||||
refreshHighlight() {
|
||||
|
||||
@@ -6,6 +6,7 @@ const handButtons: CardButton[] = [ ];
|
||||
|
||||
const passButton = CheckButton.fromId('passButton');
|
||||
const specialButton = CheckButton.fromId('specialButton');
|
||||
const passHint = document.getElementById('passHint')!;
|
||||
const gameButtonsContainer = document.getElementById('gameButtonsContainer')!;
|
||||
const rotateLeftButton = document.getElementById('rotateLeftButton') as HTMLButtonElement;
|
||||
const rotateRightButton = document.getElementById('rotateRightButton') as HTMLButtonElement;
|
||||
@@ -22,6 +23,9 @@ const replayPreviousButton = document.getElementById('replayPreviousButton')!;
|
||||
const flipButton = document.getElementById('flipButton') as HTMLButtonElement;
|
||||
let replayAnimationAbortController: AbortController | null = null;
|
||||
|
||||
const playHint = document.getElementById('playHint')!;
|
||||
let playHintTimeout: number | null = null;
|
||||
|
||||
const shareReplayLinkButton = document.getElementById('shareReplayLinkButton') as HTMLButtonElement;
|
||||
let canShareReplay = false;
|
||||
|
||||
@@ -449,6 +453,30 @@ function updateStats(playerIndex: number, scores: number[]) {
|
||||
playerBars[playerIndex].statPassesElement.innerText = currentGame.players[playerIndex].passes.toString();
|
||||
}
|
||||
|
||||
function clearPlayHint() {
|
||||
playHint.hidden = true;
|
||||
if (playHintTimeout) {
|
||||
clearTimeout(playHintTimeout);
|
||||
playHintTimeout = null;
|
||||
}
|
||||
}
|
||||
function showPlayHint(html: string) {
|
||||
clearPlayHint();
|
||||
playHint.innerHTML = html;
|
||||
playHint.className = '';
|
||||
playHint.hidden = false;
|
||||
}
|
||||
function showPlayHintError(html: string) {
|
||||
playHint.className = !playHint.hidden && html == playHint.innerHTML ? 'playError repeated' : 'playError';
|
||||
if (playHintTimeout) {
|
||||
clearTimeout(playHintTimeout!);
|
||||
resetAnimation(playHint);
|
||||
}
|
||||
playHint.innerHTML = html;
|
||||
playHint.hidden = false;
|
||||
playHintTimeout = setTimeout(() => playHint.hidden = true, 5000);
|
||||
}
|
||||
|
||||
/** Shows the ready indication for the specified player. */
|
||||
function showReady(playerIndex: number) {
|
||||
const el = document.createElement('div');
|
||||
@@ -499,6 +527,8 @@ function lockGamePage() {
|
||||
for (const el of handButtons) el.enabled = false;
|
||||
passButton.enabled = false;
|
||||
specialButton.enabled = false;
|
||||
passHint.hidden = true;
|
||||
clearPlayHint();
|
||||
}
|
||||
|
||||
async function playInkAnimations(data: {
|
||||
@@ -756,7 +786,9 @@ function redrawButton_click(e: MouseEvent) {
|
||||
|
||||
function passButton_input() {
|
||||
board.autoHighlight = !passButton.checked;
|
||||
passHint.hidden = !passButton.checked;
|
||||
if (passButton.checked) {
|
||||
clearPlayHint();
|
||||
specialButton.checked = false;
|
||||
board.cardPlaying = null;
|
||||
board.specialAttack = false;
|
||||
@@ -776,6 +808,8 @@ passButton.input.addEventListener('input', passButton_input);
|
||||
function specialButton_input() {
|
||||
board.specialAttack = specialButton.checked;
|
||||
if (specialButton.checked) {
|
||||
passHint.hidden = true;
|
||||
showPlayHint('Unleash it next to <div class="playHintSpecial"> </div>!');
|
||||
passButton.checked = false;
|
||||
board.autoHighlight = true;
|
||||
for (let i = 0; i < 4; i++) {
|
||||
@@ -784,6 +818,7 @@ function specialButton_input() {
|
||||
handButtons[i].checked = false;
|
||||
}
|
||||
} else {
|
||||
clearPlayHint();
|
||||
for (let i = 0; i < 4; i++) {
|
||||
handButtons[i].enabled = canPlayCard[i];
|
||||
if (!canPlayCard[i])
|
||||
@@ -817,7 +852,7 @@ board.onsubmit = (x, y) => {
|
||||
return;
|
||||
const message = board.checkMoveLegality(currentGame.me.playerIndex, board.cardPlaying, x, y, board.cardRotation, board.specialAttack);
|
||||
if (message != null) {
|
||||
alert(message);
|
||||
showPlayHintError(message);
|
||||
return;
|
||||
}
|
||||
if (testMode) {
|
||||
|
||||
@@ -76,14 +76,8 @@ class TimeLabel {
|
||||
private updateWarningState() {
|
||||
if (!this.faded && this.timeLeft != null && this.timeLeft <= 10) {
|
||||
this.contentElement.classList.add('warning');
|
||||
this.resetAnimation();
|
||||
resetAnimation(this.contentElement);
|
||||
} else
|
||||
this.contentElement.classList.remove('warning');
|
||||
}
|
||||
|
||||
private resetAnimation() {
|
||||
this.contentElement.style.animation = 'none';
|
||||
this.contentElement.offsetHeight; // Trigger a reflow.
|
||||
this.contentElement.style.animation = '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -628,6 +628,12 @@ function clearChildren(el: Element) {
|
||||
while (el2 = el.firstChild)
|
||||
el.removeChild(el2);
|
||||
}
|
||||
function resetAnimation(el: HTMLElement) {
|
||||
el.style.animation = 'none';
|
||||
el.offsetHeight; // Trigger a reflow.
|
||||
el.style.animation = '';
|
||||
}
|
||||
|
||||
|
||||
document.getElementById('noJSPage')!.innerText = 'Loading client...';
|
||||
|
||||
|
||||
@@ -353,14 +353,25 @@ dialog::backdrop {
|
||||
row-gap: 4px;
|
||||
}
|
||||
|
||||
.cardSpecialPoint {
|
||||
.cardSpecialPoint, .playHintSpecial {
|
||||
display: inline-block;
|
||||
color: transparent;
|
||||
background: var(--player-special-colour);
|
||||
width: 1ch;
|
||||
height: 1ch;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.cardSpecialPoint {
|
||||
margin-right: 0.3em;
|
||||
}
|
||||
.playHintSpecial {
|
||||
width: 1.5ch;
|
||||
height: 1.5ch;
|
||||
}
|
||||
.playHintSpecial:nth-of-type(1):not(:last-of-type) { background: var(--special-colour-1); }
|
||||
.playHintSpecial:nth-of-type(2) { background: var(--special-colour-2); }
|
||||
.playHintSpecial:nth-of-type(3) { background: var(--special-colour-3); }
|
||||
.playHintSpecial:nth-of-type(4) { background: var(--special-colour-4); }
|
||||
|
||||
.card::before {
|
||||
/* If it exists */
|
||||
@@ -553,6 +564,14 @@ dialog::backdrop {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#passHint:not([hidden]) {
|
||||
position: absolute;
|
||||
margin: 0 0 35em 1em;
|
||||
padding: 0.5em 1em;
|
||||
background: #80808080;
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
|
||||
#playRow:not([hidden]) {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
@@ -706,7 +725,7 @@ label[for="flipBox"] {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.playContainer { text-align: center; }
|
||||
.playContainer { text-align: center; margin: 8em 0; }
|
||||
|
||||
.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; }
|
||||
@@ -722,6 +741,40 @@ label[for="flipBox"] {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#playHint {
|
||||
grid-column: -3 / -1; grid-row: -2 / -1; align-self: end;
|
||||
justify-self: end;
|
||||
margin: 0 3em 1.5em 0;
|
||||
font-size: larger;
|
||||
padding: 0 0.5em;
|
||||
background: #00000080;
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
#playHint.playError {
|
||||
animation: 1s infinite playHintError;
|
||||
}
|
||||
#playHint.playError.repeated {
|
||||
animation: 1s infinite playHintError, 0.5s linear 1 playHintShake;
|
||||
}
|
||||
@keyframes playHintError {
|
||||
from { background: #ff0000ff; }
|
||||
50% { background: #ff000080; }
|
||||
to { background: #ff0000ff; }
|
||||
}
|
||||
@keyframes playHintShake {
|
||||
0% { transform: translate(1px, 1px); }
|
||||
10% { transform: translate(-1px, -2px); }
|
||||
20% { transform: translate(-3px, 0px); }
|
||||
30% { transform: translate(3px, 2px); }
|
||||
40% { transform: translate(1px, -1px); }
|
||||
50% { transform: translate(-1px, 2px); }
|
||||
60% { transform: translate(-3px, 1px); }
|
||||
70% { transform: translate(3px, 1px); }
|
||||
80% { transform: translate(-1px, -1px); }
|
||||
90% { transform: translate(1px, 2px); }
|
||||
100% { transform: translate(0px, 0px); }
|
||||
}
|
||||
|
||||
#testPlacementList {
|
||||
display: none;
|
||||
}
|
||||
@@ -1331,6 +1384,10 @@ label[for="flipBox"] {
|
||||
min-height: 12em;
|
||||
}
|
||||
|
||||
#passHint:not([hidden]) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#scoreSection:not([hidden]) {
|
||||
grid-column: 1;
|
||||
grid-row: 2;
|
||||
@@ -1338,7 +1395,7 @@ label[for="flipBox"] {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
#turnNumberContainer {
|
||||
#timeSection {
|
||||
grid-column: 1;
|
||||
grid-row: 2;
|
||||
margin: 0;
|
||||
@@ -1404,6 +1461,12 @@ label[for="flipBox"] {
|
||||
margin-top: 5em;
|
||||
}
|
||||
|
||||
#playHint {
|
||||
grid-column: 1 / -1;
|
||||
grid-row: 2;
|
||||
margin: 0 1em 0 0;
|
||||
}
|
||||
|
||||
/* Deck editor - Mobile layout */
|
||||
|
||||
:is(#deckListPage, #deckEditPage):not([hidden]) {
|
||||
|
||||
Reference in New Issue
Block a user