Il viale alberato della Provinciale e la Carza verso La Luna; la mappa si cerca scrivendo

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ApqNBvNbNCwXqR2RLo8QJh
This commit is contained in:
2026-10-24 18:47:01 +02:00
co-authored by Claude Opus 5.5
parent d89092303a
commit 3e99fb094c
22 changed files with 155 additions and 26 deletions
+8
View File
@@ -50,6 +50,14 @@ export function shuffledCredits(credits, random = Math.random) {
});
}
// La ricerca della mappa: ogni parola scritta deve stare nel nome del posto, senza badare a maiuscole, accenti e
// apostrofi («camp» trova Campomigliaio, «de medici» trova Via de' Medici).
const plain = text => text.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/[^a-z0-9]+/g, ' ').trim();
export function matchPlace(name, query) {
const words = plain(query).split(' ').filter(Boolean), hay = plain(name);
return words.every(w => hay.includes(w));
}
export const sayTime = text => Math.max(1.6, 0.9 + text.length * 0.055);
export function newState(story) {
+32 -2
View File
@@ -1,6 +1,6 @@
// Avvio nel browser: collega motore, disegno e interfaccia HTML (verbi, inventario, dialoghi, fumetti).
import { story } from './story/story.js';
import { Game, VERBS, VERB_LABELS, checkSave, introText, shuffledCredits } from './engine/game.js';
import { Game, VERBS, VERB_LABELS, checkSave, introText, shuffledCredits, matchPlace } from './engine/game.js';
import { Command } from './engine/command.js';
import { render, loadAssets, hasBackground, WIDTH, HEIGHT } from './engine/render.js';
import { readSave, writeSave, clearSave, readAudio, writeAudio, saveFile, saveFileName, readSaveFile } from './engine/storage.js';
@@ -143,16 +143,41 @@ function openMap() {
}));
return row;
}));
$('map-search').value = '';
filterMap();
$('map').hidden = false;
drawRoads();
// Col mouse si può scrivere subito; sul telefono la tastiera si apre solo toccando la casella.
if (matchMedia('(pointer: fine)').matches) $('map-search').focus({ preventScroll: true });
// Sul telefono la mappa scorre: si parte col posto dove si è, al centro.
board.querySelector('.here')?.scrollIntoView({ block: 'center', inline: 'center' });
}
// La ricerca: mentre si scrive restano solo i posti che corrispondono; Invio porta al primo.
function filterMap() {
const query = $('map-search').value;
let shown = 0;
for (const row of $('map-board').querySelectorAll('.map-band')) {
let inRow = 0;
for (const b of row.children) inRow += (b.hidden = !matchPlace(b.title, query)) ? 0 : 1;
row.hidden = !inRow;
shown += inRow;
}
$('map-none').hidden = shown > 0;
$('map-none').textContent = `Nessun posto con «${query.trim()}»`;
drawRoads();
}
$('map-search').addEventListener('input', filterMap);
$('map-search').addEventListener('keydown', e => {
if (e.key !== 'Enter') return;
const first = [...$('map-board').querySelectorAll('button')].find(b => !b.hidden && !b.classList.contains('here'));
if (first) first.click();
});
// Le strade fra le schede, misurate dopo l'impaginazione (e di nuovo se la finestra cambia misura).
function drawRoads() {
const board = $('map-board'), svg = $('map-roads'), origin = board.getBoundingClientRect();
const center = id => {
const r = board.querySelector(`[data-scene="${id}"] img`)?.getBoundingClientRect();
const card = board.querySelector(`[data-scene="${id}"]`);
const r = card && !card.hidden && !card.parentElement.hidden && card.querySelector('img').getBoundingClientRect();
return r && [r.left + r.width / 2 - origin.left, r.top + r.height / 2 - origin.top];
};
svg.setAttribute('viewBox', `0 0 ${board.scrollWidth} ${board.scrollHeight}`);
@@ -383,6 +408,11 @@ function updateChoices() {
window.addEventListener('keydown', e => {
if (e.target === $('volume') || e.target === $('voice-volume')) return;
// Nella ricerca della mappa le lettere si scrivono; Esc prima svuota la casella, poi chiude.
if (e.target === $('map-search')) {
if (e.key === 'Escape') { if (e.target.value) { e.target.value = ''; filterMap(); } else $('map').hidden = true; }
return;
}
if (debugKey(e)) return;
if (!$('credits').hidden) { if (e.key === 'Escape') $('credits').hidden = true; return; }
if (!$('pause').hidden) { if (e.key === 'Escape') closePause(); return; }
+47 -11
View File
@@ -1032,7 +1032,7 @@ export const story = {
id: 'alla_scuola', name: 'più avanti, la stradina della scuola', box: [170, 196, 80, 40], at: [208, 226], exit: { scene: 'scuola', at: [600, 320], face: 'left' },
},
{
id: 'la_luna', name: 'a sinistra, verso La Luna e Campomigliaio', box: [0, 216, 44, 56], at: [12, 248], exit: { scene: 'verso_luna', at: [150, 350], face: 'up' },
id: 'la_luna', name: 'a sinistra, verso La Luna e Campomigliaio', box: [0, 216, 44, 56], at: [12, 248], exit: { scene: 'provinciale_viale', at: [300, 350], face: 'up' },
},
{
id: 'al_forno', name: 'laggiù, verso il forno', box: [280, 196, 80, 40], at: [318, 224], exit: { scene: 'forno', at: [12, 300], face: 'right' },
@@ -1042,17 +1042,53 @@ export const story = {
},
],
},
// La Provinciale lungo la Sieve, uscendo dal paese verso La Luna (foto di Andrea, 24 ottobre).
// La Provinciale dopo il bivio della stazione, fra gli alberi, prima della Carza (foto di Andrea, 24 ottobre).
provinciale_viale: {
name: 'Via Provinciale, il viale alberato',
map: [72, 68],
background: 'assets/scenes/provinciale-viale.png',
walk: [[0, 360], [0, 330], [400, 232], [462, 232], [620, 360]],
depth: [[232, 0.4], [360, 1.7]],
entry: [300, 350],
hotspots: [
{
id: 'verso_la_sieve', name: 'avanti, verso La Luna', box: [392, 220, 80, 22], at: [435, 236], exit: { scene: 'verso_luna', at: [150, 206], face: 'down' },
},
{
id: 'negozi', name: 'negozi', box: [20, 150, 200, 95], at: [150, 330], face: 'left',
actions: {
look: [{ say: 'I negozi sotto i palazzi, al riparo degli alberi. Con la festa domani, stasera chiudono tardi.' }],
},
},
{
id: 'alberi', name: 'alberi del viale', box: [230, 20, 160, 200], at: [330, 290], face: 'up',
actions: {
look: [{ say: 'Gli alberi del viale: d\'estate fanno un\'ombra che in piazza se la sognano.' }],
use: [{ say: 'Arrampicarmi? Ho ventisette anni, non sette.' }],
},
},
{
id: 'villette', name: 'case col balcone', box: [500, 0, 140, 230], at: [540, 330], face: 'right',
actions: {
look: [{ say: 'Case coi balconi pieni di fiori. Qui il palio si sente da lontano: il giusto.' }],
},
},
{
id: 'al_bivio', name: 'indietro, verso il bivio', box: [0, 334, 620, 26], at: [300, 354], exit: { scene: 'bivio_provinciale', at: [12, 248], face: 'right' },
},
],
},
// La Provinciale lungo la Carza, uscendo dal paese verso La Luna (foto di Andrea, 24 ottobre).
verso_luna: {
name: 'La Provinciale lungo la Sieve',
map: [70, 70],
name: 'La Provinciale lungo la Carza',
map: [66, 73],
background: 'assets/scenes/verso-luna.png',
walk: [[0, 360], [0, 200], [168, 190], [182, 196], [330, 360]],
depth: [[192, 0.45], [360, 1.7]],
entry: [150, 350],
entry: [150, 206],
hotspots: [
{
id: 'verso_la_luna', name: 'avanti, verso La Luna e Campomigliaio', box: [0, 186, 172, 26], at: [110, 200], face: 'up', default: 'walk',
id: 'verso_la_luna', name: 'avanti, verso La Luna e Campomigliaio', box: [0, 334, 330, 26], at: [150, 352], face: 'down', default: 'walk',
actions: {
walk: [{
if: 'rione:campomigliaio',
@@ -1063,9 +1099,9 @@ export const story = {
},
},
{
id: 'sieve', name: 'la Sieve', box: [340, 205, 300, 155], at: [250, 300], face: 'right',
id: 'carza', name: 'la Carza', box: [340, 205, 300, 155], at: [250, 300], face: 'right',
actions: {
look: [{ say: 'La Sieve. Più sassi che acqua: a farci il bagno ci si sbuccia le ginocchia.' }],
look: [{ say: 'La Carza, la stessa che passa sotto il ponticino per Cardetole. Più sassi che acqua.' }, { say: 'Da quando scavano le gallerie dell\'alta velocità, d\'estate sparisce. Torna viva d\'inverno.' }],
use: [{ say: 'Scendere nel greto ora? Il palio non sta mica sotto un sasso.' }],
},
},
@@ -1079,17 +1115,17 @@ export const story = {
{
id: 'casa_bianca', name: 'casa bianca', box: [84, 112, 80, 64], at: [120, 220], face: 'up',
actions: {
look: [{ say: 'Una casa bianca sotto il bosco, coi pini sopra. Bella vista sulla Sieve, e sui camion della Provinciale.' }],
look: [{ say: 'Una casa bianca sotto il bosco, coi pini sopra. Bella vista sulla Carza, e sui camion della Provinciale.' }],
},
},
{
id: 'muretto', name: 'muretto', box: [178, 196, 120, 150], at: [190, 300], face: 'right',
actions: {
look: [{ say: 'Il muretto lungo la Sieve, tutto licheni. È più vecchio di me, e sta in piedi meglio.' }],
look: [{ say: 'Il muretto lungo la Carza, tutto licheni. È più vecchio di me, e sta in piedi meglio.' }],
},
},
{
id: 'al_bivio', name: 'indietro, verso il bivio', box: [0, 334, 330, 26], at: [150, 354], exit: { scene: 'bivio_provinciale', at: [12, 248], face: 'right' },
id: 'al_viale', name: 'indietro, verso il paese', box: [0, 182, 240, 26], at: [150, 200], exit: { scene: 'provinciale_viale', at: [435, 238], face: 'down' },
},
],
},