fix(gameplay): auto-erase tiles on invalid/duplicate submissions and show real-time already-found status

This commit is contained in:
2026-10-23 23:00:11 -04:00
parent 5fcd68c976
commit ac7e457e5a
4 changed files with 26 additions and 9 deletions

View File

@@ -13,6 +13,8 @@ export function createWordBuilder({ onTileRemoved, onTilesReordered, onSubmitWor
const hasTiles = tileCount > 0;
const tileObjects = state.activeTileIds.map(id => state.boardTiles.find(t => t.id === id)).filter(Boolean);
const pts = tileCount === 4 ? 8 : tileCount === 3 ? 4 : tileCount === 2 ? 2 : 1;
const constructedWord = tileObjects.map(t => t.text.toLowerCase()).join('');
const isAlreadyFound = hasTiles && state.foundWords.has(constructedWord);
wrapper.innerHTML = `
<div class="word-builder ${hasTiles ? 'has-tiles' : ''}" id="active-word-builder">
@@ -35,10 +37,10 @@ export function createWordBuilder({ onTileRemoved, onTilesReordered, onSubmitWor
<button class="btn-icon-tray" id="btn-builder-backspace" title="Remove last tile" ${!hasTiles ? 'style="opacity:0.25;pointer-events:none;"' : ''}>
⌫
</button>
<button class="btn-submit-tray" id="btn-builder-submit" ${!hasTiles ? 'disabled' : ''} title="Submit word (Enter)">
<button class="btn-submit-tray ${isAlreadyFound ? 'is-duplicate' : ''}" id="btn-builder-submit" ${!hasTiles ? 'disabled' : ''} title="${isAlreadyFound ? 'Word already found' : 'Submit word (Enter)'}">
<span class="submit-icon">✓</span>
<span class="submit-text">ENTER</span>
${hasTiles ? `<span class="submit-pts">+${pts}</span>` : ''}
<span class="submit-text">${isAlreadyFound ? 'FOUND' : 'ENTER'}</span>
${hasTiles ? `<span class="submit-pts ${isAlreadyFound ? 'already-found' : ''}">${isAlreadyFound ? 'Found' : `+${pts}`}</span>` : ''}
</button>
</div>
</div>

View File

@@ -174,8 +174,14 @@ async function initApp() {
const result = gameState.submitWord();
if (!result.success) {
sound.playError();
wordBuilder.triggerShake();
if (result.reason !== 'empty') {
sound.playError();
wordBuilder.triggerShake();
// Automatically erase selected tiles after shake so player doesn't have to keep hitting backspace/x
setTimeout(() => {
gameState.clearActive();
}, 400);
}
return;
}

View File

@@ -337,9 +337,7 @@ class GameStateManager {
// Check if already found
if (this.foundWords.has(constructedWord)) {
this.notifyToast(`"${constructedWord.toUpperCase()}" already found!`, 'warning');
this.activeTileIds = [];
this.notify();
return { success: false, reason: 'duplicate' };
return { success: false, reason: 'duplicate', word: constructedWord };
}
// Look up in current puzzle's valid words list
@@ -347,7 +345,7 @@ class GameStateManager {
if (!match) {
this.notifyToast(`"${constructedWord.toUpperCase()}" is not in the word list`, 'error');
return { success: false, reason: 'invalid' };
return { success: false, reason: 'invalid', word: constructedWord };
}
// Valid word!

View File

@@ -186,6 +186,17 @@
margin-left: 2px;
}
.btn-submit-tray.is-duplicate {
background: linear-gradient(135deg, #f59e0b, #d97706);
box-shadow: 0 2px 8px rgba(245, 158, 11, 0.35);
}
.submit-pts.already-found {
background: rgba(0, 0, 0, 0.22);
color: #ffffff;
font-weight: 800;
}
@media (max-width: 420px) {
.word-builder {
padding: 3px 6px;