feat: create a large randomized library of playful game sounds and audio combos
This commit is contained in:
+11
-8
@@ -21,6 +21,7 @@
|
||||
setEnabled: async () => false, setVolume: () => {}, setReduced: () => {}, play: () => false, setPageHidden: () => {}, cleanup: () => {}
|
||||
};
|
||||
const audio = globalThis.BattleshipAudio?.createAudioEngine?.() || silentAudio;
|
||||
const sounds = globalThis.BattleshipSounds?.createSoundDirector?.({ engine: audio }) || { play: () => false, reset: () => {} };
|
||||
let token = localStorage.getItem(storage.token) || '';
|
||||
let role = '';
|
||||
let info;
|
||||
@@ -176,7 +177,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
function showFeedback(event) {
|
||||
function showFeedback(event, version, summary = false) {
|
||||
const content = pickReaction(event?.type);
|
||||
if (!content) return;
|
||||
let { motion, burst } = content;
|
||||
@@ -195,6 +196,7 @@
|
||||
void document.body.offsetWidth;
|
||||
document.body.classList.add(kind === 'damage' ? 'fx-damage' : 'fx-hit');
|
||||
if (navigator.vibrate) navigator.vibrate(vibration);
|
||||
sounds.play(event.type, { version, combo: hitStreak, reduced: audio.getPreferences().reduced, summary });
|
||||
effectTimer = window.setTimeout(() => {
|
||||
ui.effects.hidden = true; document.body.classList.remove('fx-hit', 'fx-damage');
|
||||
}, kind === 'victory' ? 2200 : combo ? 1800 : 1350);
|
||||
@@ -218,7 +220,7 @@
|
||||
clearRecoveryTimers();
|
||||
if (socket) { socket.onclose = null; socket.close(); socket = undefined; }
|
||||
selectedTarget = undefined; firePending = false; cumulativeStatistics = undefined; statisticsGameId = undefined;
|
||||
activeBoard = 'own'; hitStreak = 0; state = undefined; role = ''; info = undefined; targetActivator = undefined; audio.cleanup();
|
||||
activeBoard = 'own'; hitStreak = 0; state = undefined; role = ''; info = undefined; targetActivator = undefined; sounds.reset(); audio.cleanup();
|
||||
document.body.classList.remove('fx-hit', 'fx-damage'); ui.effects.hidden = true; notify('');
|
||||
const soundKeys = new Set(['battleship.soundEnabled', 'battleship.soundVolume', 'battleship.soundReduced']);
|
||||
Object.keys(localStorage).filter(key => key.startsWith('battleship.') && !soundKeys.has(key)).forEach(key => localStorage.removeItem(key));
|
||||
@@ -246,7 +248,7 @@
|
||||
}
|
||||
|
||||
function openRecovery() {
|
||||
if (!token) return; recoveryOpener = document.activeElement; ui.recoveryDialog.hidden = false; ui.recoveryButton.setAttribute('aria-expanded', 'true');
|
||||
if (!token) return; sounds.play('recovery-open', { reduced: audio.getPreferences().reduced }); recoveryOpener = document.activeElement; ui.recoveryDialog.hidden = false; ui.recoveryButton.setAttribute('aria-expanded', 'true');
|
||||
ui.recoveryChoices.hidden = false; ui.recoveryConfirmation.hidden = true; ui.recoveryGlobal.hidden = !isPlayer(); ui.recoveryClose.focus();
|
||||
}
|
||||
|
||||
@@ -263,7 +265,7 @@
|
||||
if (action === 'leave') await request('/api/session/leave', { method: 'POST', headers: headers(), body: JSON.stringify({ token, gameId: state?.gameId || 0 }) });
|
||||
else if (action === 'profile') await request('/api/session/profile-reset', { method: 'POST', headers: headers(), body: JSON.stringify({ token, gameId: state?.gameId || 0 }) });
|
||||
else await request('/api/game/reset', { method: 'POST', headers: headers(), body: JSON.stringify({ token, gameId: state?.gameId || 0 }) });
|
||||
clearLocalProfile(action !== 'leave');
|
||||
sounds.play(`recovery-${action}`, { reduced: audio.getPreferences().reduced }); clearLocalProfile(action !== 'leave');
|
||||
} catch (error) { recoveryPending = false; ui.recoveryConfirm.disabled = false; ui.recoveryLive.textContent = 'Не получилось. Можно повторить или отменить.'; notify(error.message, true); }
|
||||
}
|
||||
|
||||
@@ -373,7 +375,7 @@
|
||||
selectedTarget = selectedTarget && payload.boards[opponentIndex()][selectedTarget.y * 10 + selectedTarget.x] === '0' ? selectedTarget : undefined;
|
||||
document.title = `Морской бой — версия ${payload.version}`;
|
||||
render();
|
||||
showFeedback(globalThis.BattleshipTargetInteraction.detectFeedback(previousState, payload, role));
|
||||
showFeedback(globalThis.BattleshipTargetInteraction.detectFeedback(previousState, payload, role), payload.version, hadGap);
|
||||
}
|
||||
|
||||
function isPlayer() { return role === 'player1' || role === 'player2'; }
|
||||
@@ -401,6 +403,7 @@
|
||||
renderGame();
|
||||
try {
|
||||
await post('/api/game/shot', { token, gameId: state.gameId, ...target });
|
||||
sounds.play('shot', { reduced: audio.getPreferences().reduced });
|
||||
return true;
|
||||
} catch (_) {
|
||||
return false;
|
||||
@@ -414,7 +417,7 @@
|
||||
if (!targetActivator) {
|
||||
targetActivator = globalThis.BattleshipTargetInteraction.createTargetActivator({
|
||||
canFire: canFireTarget,
|
||||
select: nextTarget => { selectedTarget = nextTarget; renderGame(); },
|
||||
select: nextTarget => { selectedTarget = nextTarget; sounds.play('select', { reduced: audio.getPreferences().reduced }); renderGame(); },
|
||||
fire: fireTarget,
|
||||
});
|
||||
}
|
||||
@@ -710,7 +713,7 @@
|
||||
ui.fire.addEventListener('click', () => { if (selectedTarget) fireTarget(selectedTarget); });
|
||||
ui.cancel.addEventListener('click', () => { selectedTarget = undefined; renderGame(); });
|
||||
ui.abort.addEventListener('click', () => post('/api/game/abort', { token, gameId: state.gameId }));
|
||||
ui.rematch.addEventListener('click', () => post('/api/game/rematch', { token, gameId: state.gameId }));
|
||||
ui.rematch.addEventListener('click', () => { post('/api/game/rematch', { token, gameId: state.gameId }).then(() => sounds.play('rematch', { reduced: audio.getPreferences().reduced })).catch(() => {}); });
|
||||
el('retry-button').addEventListener('click', () => { refreshInfo(); pollState(); connectSocket(); });
|
||||
ui.recoveryButton.addEventListener('click', openRecovery);
|
||||
ui.soundToggle.addEventListener('click', async () => {
|
||||
@@ -723,7 +726,7 @@
|
||||
ui.soundReduced.addEventListener('change', () => { audio.setReduced(ui.soundReduced.checked); syncSoundControls(); });
|
||||
ui.recoverySoundMute.addEventListener('click', async () => { await audio.setEnabled(false); syncSoundControls(); });
|
||||
ui.recoveryClose.addEventListener('click', closeRecovery);
|
||||
ui.recoveryCancel.addEventListener('click', () => { recoveryAction = undefined; ui.recoveryConfirmation.hidden = true; ui.recoveryChoices.hidden = false; ui.recoveryClose.focus(); });
|
||||
ui.recoveryCancel.addEventListener('click', () => { sounds.play('recovery-cancel', { reduced: audio.getPreferences().reduced }); recoveryAction = undefined; ui.recoveryConfirmation.hidden = true; ui.recoveryChoices.hidden = false; ui.recoveryClose.focus(); });
|
||||
ui.recoveryChoices.addEventListener('click', event => {
|
||||
const choice = event.target.closest('[data-recovery-action]');
|
||||
if (choice && !choice.hidden) showRecoveryConfirmation(choice.dataset.recoveryAction);
|
||||
|
||||
Reference in New Issue
Block a user