Versione del gioco (src/version.js, 0.1.0): immagini, musiche e voci con ?v=<versione> e cache lunga in nginx; scripts/bump.sh e make bump per le versioni nuove

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ks2K99uqCZjAxvy7Rdj1R
This commit is contained in:
2026-10-25 10:10:51 +01:00
co-authored by Claude Opus 5.5
parent 4467909237
commit 12c6357cbc
12 changed files with 98 additions and 19 deletions
+3 -1
View File
@@ -2,6 +2,8 @@
// Quale brano suona lo decidono i dati (story.music e scene.music). Si prova a partire subito; se il browser non lo
// permette prima di un gesto del giocatore, il brano riparte al primo clic o tasto (unlock).
import { versioned } from '../version.js';
// Il brano del momento: titolo, fine, oppure quello della scena (o quello comune del gioco). Sotto il cartello
// d'apertura nessuno: lì suona la sua musichetta, una volta sola (main.js).
// Quale brano: quello della scena se ce l'ha; altrimenti, all'aperto, quello della fase del giorno (la sera ha il suo);
@@ -57,7 +59,7 @@ export class Music {
}
start(src) {
const audio = new Audio(src);
const audio = new Audio(versioned(src));
audio.loop = true;
audio.volume = 0;
audio.play().catch(() => { this.blocked = audio; }); // senza permesso si riprova al primo gesto
+3 -2
View File
@@ -2,6 +2,7 @@
// Le forme provvisorie (`art`) verranno sostituite da sfondi e sprite in pixel art.
import { check } from './game.js';
import { ambientEffects, ambientHash } from './ambient.js';
import { versioned } from '../version.js';
export const WIDTH = 640, HEIGHT = 360;
function drawShape(ctx, s) {
@@ -64,7 +65,7 @@ const loadImage = src => new Promise(resolve => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = () => resolve(null);
img.src = src;
img.src = versioned(src);
});
export const hasBackground = src => backgrounds.has(src);
@@ -96,7 +97,7 @@ export function loadBackground(src, now = Date.now()) {
export async function loadAssets(story, first = story.start.scene, { together = 3 } = {}) {
const people = Object.entries(story.characters).filter(([, c]) => c.sprite).map(async ([id, c]) => {
try {
const atlas = await (await fetch(c.sprite)).json();
const atlas = await (await fetch(versioned(c.sprite))).json();
const img = await loadImage(new URL(atlas.image, new URL(c.sprite, location.href)).href);
if (img) sprites.set(id, { atlas, img });
return !!img;
+2 -1
View File
@@ -2,6 +2,7 @@
// Senza assets/voices/index.json, o per una battuta nuova non ancora generata, il gioco resta muto e va avanti.
import { ENGINE_LINES } from './game.js';
import { versioned } from '../version.js';
export const voiceKey = (who, text) => `${who}|${text}`;
@@ -46,7 +47,7 @@ export class Voices {
this.stop();
const entry = this.index[voiceKey(who, text)];
if (!entry || muted || this.volume <= 0) return false;
this.audio = new Audio(this.base + entry.file);
this.audio = new Audio(versioned(this.base + entry.file));
this.audio.volume = this.volume;
this.audio.play().catch(() => {});
return true;
+6 -5
View File
@@ -6,6 +6,7 @@ import { render, loadAssets, hasBackground, WIDTH, HEIGHT } from './engine/rende
import { readSave, writeSave, clearSave, readAudio, writeAudio, saveFile, saveFileName, readSaveFile } from './engine/storage.js';
import { Music, trackFor } from './engine/music.js';
import { Voices } from './engine/voices.js';
import { versioned } from './version.js';
const $ = id => document.getElementById(id);
const canvas = $('scene'), ctx = canvas.getContext('2d');
@@ -40,7 +41,7 @@ $('voice-volume').addEventListener('input', e => { voices.setVolume(e.target.val
showSound();
window.music = music; // per i test nel browser
fetch('assets/voices/index.json').then(r => r.ok ? r.json() : {}).then(index => { voices.index = index; }).catch(() => {});
fetch(versioned('assets/voices/index.json')).then(r => r.ok ? r.json() : {}).then(index => { voices.index = index; }).catch(() => {});
window.voices = voices;
let voiced = null;
const say = (who, text) => voices.play(who, text, music.muted);
@@ -156,7 +157,7 @@ function rollCredits() {
rolling = requestAnimationFrame(step);
}
function showFace({ name, face }) {
$('face').querySelector('img').src = face;
$('face').querySelector('img').src = versioned(face);
$('face').querySelector('img').classList.toggle('palio', face.endsWith('palio.png'));
$('face').querySelector('figcaption').textContent = name;
$('face').hidden = false;
@@ -185,7 +186,7 @@ function openMap() {
b.classList.toggle('here', p.here);
const img = document.createElement('img');
img.alt = '';
if (p.mini) img.src = p.mini;
if (p.mini) img.src = versioned(p.mini);
const name = document.createElement('span');
name.textContent = p.name;
b.append(img, name);
@@ -432,7 +433,7 @@ function updateInventory() {
let icon;
if (item.icon?.src) {
icon = document.createElement('img');
icon.src = item.icon.src;
icon.src = versioned(item.icon.src);
icon.alt = '';
} else {
icon = document.createElement('i');
@@ -595,7 +596,7 @@ function showSplash() {
reveal();
};
// La musichetta: senza un gesto del giocatore il browser può non lasciarla partire, e allora il cartello resta muto.
const jingle = story.music?.splash ? new Audio(story.music.splash) : null;
const jingle = story.music?.splash ? new Audio(versioned(story.music.splash)) : null;
if (jingle) {
jingle.volume = music.muted ? 0 : music.volume;
jingle.play().catch(() => {});
+6
View File
@@ -0,0 +1,6 @@
// La versione del gioco: la cambia scripts/bump.sh, insieme a package.json.
export const VERSION = '0.1.0';
// Immagini, musiche e voci con ?v=<versione>: a ogni versione l'indirizzo cambia e il browser le riscarica,
// mentre fra una versione e l'altra le tiene in cache a lungo (nginx.conf).
export const versioned = url => /^(data|blob):/.test(url) ? url : `${url}${url.includes('?') ? '&' : '?'}v=${VERSION}`;