feat: add label random and animations to web interface

This commit is contained in:
2026-08-30 23:10:23 +03:00
parent 5cb4d8e9f6
commit c0c3c73c7a
8 changed files with 598 additions and 21 deletions
+44 -1
View File
@@ -43,7 +43,50 @@
};
}
const api = { createTargetActivator };
function detectFeedback(previous, next, viewer) {
if (!previous || !next || previous.gameId !== next.gameId || next.version <= previous.version) return undefined;
const player = viewer === 'player1' ? 0 : viewer === 'player2' ? 1 : -1;
if (previous.phase !== next.phase && next.phase === 'finished') {
if (player < 0) return { type: 'finish' };
return { type: next.winner === player ? 'victory' : 'defeat' };
}
if (previous.phase !== 'in_progress' && next.phase === 'in_progress') return { type: 'start' };
if (!Array.isArray(previous.boards) || !Array.isArray(next.boards)) return undefined;
let best;
for (let board = 0; board < 2; board += 1) {
if (typeof previous.boards[board] !== 'string' || typeof next.boards[board] !== 'string') continue;
for (let cell = 0; cell < 100; cell += 1) {
const before = previous.boards[board][cell];
const after = next.boards[board][cell];
if (before === after) continue;
const perspective = player < 0 ? 'watch' : board === player ? 'damage' : 'attack';
if (after === '4') best = { type: perspective === 'damage' ? 'sunk-damage' : perspective === 'attack' ? 'sunk' : 'watch-sunk' };
else if (!best && after === '3') best = { type: perspective === 'damage' ? 'damage' : perspective === 'attack' ? 'hit' : 'watch-hit' };
else if (!best && after === '2') best = { type: perspective === 'attack' ? 'miss' : perspective === 'damage' ? 'dodged' : 'watch-miss' };
}
}
if (best) return best;
if (player >= 0 && previous.turn !== next.turn && next.turn === viewer) return { type: 'turn' };
return undefined;
}
function createReactionPicker(catalog, random = Math.random) {
const previousByType = new Map();
return type => {
const variants = catalog[type];
if (!Array.isArray(variants) || variants.length === 0) return undefined;
if (variants.length === 1) return variants[0];
const previous = previousByType.get(type);
const candidateCount = previous === undefined ? variants.length : variants.length - 1;
let index = Math.min(candidateCount - 1, Math.max(0, Math.floor(random() * candidateCount)));
if (previous !== undefined && index >= previous) index += 1;
previousByType.set(type, index);
return variants[index];
};
}
const api = { createTargetActivator, detectFeedback, createReactionPicker };
if (typeof module !== 'undefined' && module.exports) module.exports = api;
else globalThis.BattleshipTargetInteraction = api;
})();