Sprint 8quater: si ricomincia dalla pausa, i soldi tornano leggibili, e il barbone perde il pixel magenta2026-08-10
- [feat] SU-349 — RICOMINCIA nel menu di pausa, e solo da soli. La voce esisteva solo a fine partita: ora si può buttare via una run storta senza aspettare il game over né passare dal menu principale. Sta fra OPZIONI ed ESCI, cioè in fondo e lontano da RIPRENDI, perché un tocco sbagliato costa la partita; riusa
_do_restart_or_rematch() (nessuna sequenza di reset duplicata) e la chiave HUD_MENU_RICOMINCIA che era già tradotta in tutte e 8 le lingue, quindi zero righe nuove nei CSV. In multiplayer la voce non compare, né all'host né all'ospite: la rivincita resta una cosa di fine partita. 📌 Il difetto che il diff ha fatto emergere, e che il gate del ticket da solo non copriva: _build_pause_items() veniva chiamata solo alla nascita dell'HUD e al cambio lingua, mai all'apertura della pausa — il menu era una *fotografia*. Con il solo gate scritto dentro la funzione, l'esito dipendeva dall'istante in cui nasce l'HUD: se nasceva un attimo prima che NetworkManager arrivasse a IN_GAME, RICOMINCIA sarebbe comparsa in multiplayer, cioè l'esatto contrario di quello che il ticket chiedeva; e se nasceva a sessione viva, non sarebbe più tornata per tutta la run neanche dopo la caduta della sessione. Ora le voci si ricostruiscono a ogni apertura (_toggle_pause, l'unica strada d'ingresso: il pulsante e l'ESC passano di lì). ⚠️ MP verificato in modo deterministico, non con una sessione vera (non collaudabile su questo Mac, conflitto EOS/ENet): forzando active/state di NetworkManager le tre liste escono RIPRENDI | OPZIONI | RICOMINCIA | ESCI da soli, RIPRENDI | OPZIONI | ESCI a sessione viva, e di nuovo con RICOMINCIA a sessione caduta. - [fix] SU-350 — il sacchetto dei soldi era l'ultimo raw da mezzo schermo rimasto nell'HUD.
moneybag.png era un raw 1254×1254 disegnato a 44 px col filtro NEAREST: a quel rapporto il filtro pesca un pixel ogni 28 e butta il resto, quindi il simbolo $ spariva e il sacchetto usciva come una poltiglia beige. È lo stesso difetto trovato in SU-341 su sleep.png e shower.png — la moneybag era la superstite: tutte le altre icone dell'HUD sono già pixel art nativa piccola (hud_hunger/hud_sleep/hud_hygiene 32×32, caticon 40×40, wine 12×20). Al suo posto coin.png (16×16), che era già in gioco come raccoglibile per strada, come effetto e in mano agli NPC: quello che raccogli è una monetina, quindi il contatore mostra una monetina. MONEY_ICON_SIZE passa da 44 a 48 = 16×3, scala intera, così i pixel restano quadrati e tutti uguali. Nessun asset nuovo, nessun ticket di generazione: moneybag.png resta in repo ma l'HUD non la carica più. Aggiornato anche shot_su314_money.gd, che cercava l'icona per il nome del file (e aveva pure i path dei nodi rimasti indietro da SU-339: senza quelli il provino falliva prima ancora di guardare l'icona). - [fix] SU-351 — via il pixel magenta fra le gambe del barbone. Era residuo del chroma key: i raw in
sprites_raw/ hanno il fondo magenta (#ED0BE4) e nel ritaglio la fessura fra le gambe non era stata pulita fino in fondo — stessa famiglia di SU-336 (l'alone magenta del gatto). In player_sheet.png i pixel magenta visibili erano nove, il peggiore #F00BDC in idle_se e il più esposto #981187 in idle/idle_s (il frame che si vede stando fermi, ed è quello fotografato da Ivan). Sostituiti con la media dei vicini puliti dentro lo stesso frame 32×32, alpha invariata: non inventati e non resi trasparenti, perché stanno dentro la sagoma e bucarli aprirebbe un foro. Verificato col confronto pixel-per-pixel contro git: esattamente 9 differenze, 0 alpha alterate, 0 magenta rimasto, e tre provini in-engine (SCEGLI IL PERSONAGGIO, in partita a zoom 7×, e le due pose ferme a 10×) con zero pixel magenta su 1280×800. 📌 Reperto che vale un ticket a parte, NON toccato qui: lo stesso residuo è su 97 dei 99 fogli NPC in assets/sprites/npc/bodies/, per 1.853 pixel isolati. Ma una passata cieca sarebbe un disastro: negli stessi file ci sono 12.196 pixel magenta in gruppo, che sono vestiti rosa disegnati apposta (runner, influencer, punk). Il discrimine che funziona è la solitudine del pixel — i nove del barbone erano tutti isolati, zero in gruppo. 📌 Trappola dei provini, pagata e ora scritta nello script: imporre spr.animation non basta, Player riscrive la posa a ogni frame e i due scatti «pose diverse» uscivano pixel-identici senza dare il minimo segno d'errore. Si spegne set_process/set_physics_process del giocatore prima di imporre la posa (shot_su351_barbone.gd).
RIENTRI ATTESI: SU-349, SU-350, SU-351 — le tre chiavi messe In revisione oggi. *(Giro precedente: delle tre chiavi attese — SU-341, SU-344, SU-348 — ne è rientrata zero: sono tutte e tre Fatto. È il primo giro a rientro pieno zero da quando misuriamo.)* 📌 Un giro anomalo, e va detto perché il confronto non regga più di quanto può: i tre ticket non sono nati dal triage ma sono stati scritti e lavorati nella stessa sessione, quindi la diagnosi era già in mano a chi scriveva il ticket — SU-350 conteneva la causa (il raw 1254×1254) e SU-351 le coordinate esatte dei nove pixel. È una condizione che non si ripete quando i ticket arrivano da Ivan giorni prima, e un eventuale zero rientri non va letto come merito del processo. 📌 Il valore vero del giro sta invece in una cosa che il ticket non chiedeva: la revisione del diff ha trovato che il gate multiplayer di SU-349, così com'era, dipendeva dall'istante di nascita dell'HUD e avrebbe potuto mostrare RICOMINCIA in multiplayer. Il ticket sarebbe passato verde lo stesso. ⚠️ Il buco strutturale resta aperto per l'ottavo giro: nessuna delle tre chiavi è stata vista su un device o un simulatore vero, e tutte e tre sono puramente visive o di tocco — una voce di menu che si preme col pollice, un'icona da leggere a colpo d'occhio, un pixel largo un pixel. ⚠️ Multiplayer non collaudato per l'ottavo giro (conflitto EOS/ENet noto dal 31/07): SU-349 ha un ramo MP nuovo, verificato solo forzando i campi di NetworkManager, mai con due peer veri. È il candidato numero uno a rientrare.
Sprint 8ter: le icone che Ivan ha scelto, l'insolato perde la bottiglia, e le note parlano otto lingue fino in fondo2026-08-10
- [feat] SU-341 — in gioco le tre icone dell'HUD, e due erano immagini da mezzo schermo. Ivan ha scelto fra le candidate di SU-340: hot dog per la fame, mezzaluna per il sonno, paperella per l'igiene. Entrano come file nuovi perché i vecchi servono ancora:
hotdog.png a MapOverlay per il camioncino, sleep.png e shower.png a Player.gd per gli effetti che volano via — il grep che il ticket chiedeva è servito esattamente a questo. 📌 La scoperta che vale la voce: sleep.png e shower.png erano raw da 1254×1254, cioè per disegnare un'icona da 22 px il gioco ne caricava una grande quanto mezzo schermo, e a quella riduzione i contorni sparivano — la doccia usciva come una macchia grigia irriconoscibile. Solo hotdog.png era già sano a 32×32. Le tre nuove sono pixel art disegnata a 32×32 nativi. Verificato con un A/B sullo stesso provino e non leggendo un report: i rettangoli del pannello sono identici prima e dopo (152×86, statsbox 136×70) e la differenza di pixel è confinata — 449 dentro il pannello, 5 fuori, che sono l'orologio avanzato di un frame. Il filtro nearest c'era già in _make_icon_rect, non è stato aggiunto qui. ⚠️ Un effetto collaterale da decidere, non un difetto: adesso HUD ed effetti non si somigliano più — recuperi energia e vola via una «z», ti lavi e vola via una doccia, ma nell'HUD ci sono mezzaluna e paperella. Il commento a Player.gd:708 diceva «resta sleep.png, identica all'HUD» e da oggi non è vero. Allinearli è di gusto, quindi la scelta resta a Ivan. ⚠️ Difetto visto e NON toccato perché ereditato da SU-339, già approvato: la barra dell'energia sborda oltre il bordo destro del pannello mentre fame e igiene finiscono a filo — ed è identico anche nel «prima», quindi non nasce qui. - [feat] SU-348 — il colpo di calore ha il suo sprite, e il pezzo difficile non era il disegno. Lo sprite approvato in SU-347 entra in gioco: lo stesso barbone che sbanda, ma senza bottiglia e senza bollicine. 📌 Il vero lavoro era un segnale che non c'era:
is_heatstroke era una variabile nuda, senza setter e senza signal, quindi nessuno avvisava il Player che doveva scambiare gli SpriteFrames — lo sprite più bello del mondo non sarebbe mai comparso. Ora ha la stessa forma di is_drunk, guard incluso, e il collaudo l'ha misurato: una sola emissione all'attivazione e zero riemissioni in 40 frame di esposizione continua al caldo, cioè nessuno swap a raffica mentre la calamità ricarica il timer. 📌 E serviva una riga che il brief non prevedeva: senza registrare la variante "heatstroke" in Skins.gd, il codice ripiegava sulla variante "drunk" e il ticket restava senza alcun effetto — la bottiglia sarebbe tornata. Precedenza dichiarata: vince la SBORNIA, sprite e tinta, perché è la stessa precedenza che la tinta della faccia applica già da SU-332; farle divergere darebbe faccia paonazza su sprite senza bottiglia, il peggior ibrido possibile. Misurato sullo sheet: 160×160, zero pixel magenta e zero trasparenti sporchi — quello della sbornia, già in gioco, ne ha 1 magenta e 644 sporchi, quindi il nuovo è più pulito dell'originale. Le 16 animazioni hanno nomi e regioni identiche, così lo swap a metà camminata non perde il frame. ⚠️ Un criterio del ticket NON è soddisfatto e non è stato nascosto: i puppet in multiplayer. Il colpo di calore viaggia dentro il flag drunk che c'era già — una sola booleana, scelta di SU-332 — quindi un altro giocatore insolato si vede con lo sprite della sbornia. Inventare un canale di rete nuovo era fuori perimetro. Nessuna prova MP eseguita. - [fix] SU-344 (secondo KO) — le note di rilascio tradotte fino in fondo, non solo le recenti. Il KO, parole di Ivan: «falle tutte multilingui quelle disponibili regresse». I sette file esteri si fermavano alla v0.26 e sotto usciva italiano in mezzo a una schermata inglese. Ora tutte e 8 le lingue hanno tutte e 9 le versioni, fino alla v0.21: 67 bullet nuovi per lingua, 469 in tutto. Verificato rifacendo i conti e non leggendo i report dei due agenti: 148 righe e 9 sezioni ovunque, conteggio bullet per sezione identico al master (15/14/7/12/19), zero bullet su 67 rimasti identici all'italiano in ogni lingua — cioè tradotti davvero e non incollati — zero emoji, zero «Street University», e diff +77/−0 su tutti e sette, col master italiano intatto. E guardato a schermo: provino nuovo che scorre davvero fino alla v0.21 in tutte e sette le lingue, perché allargare la finestra non basta — il pannello è una frazione del viewport e cresce insieme a lei, quindi si pilota lo ScrollContainer vero convertendo l'indice di riga in offset pixel. Il cinese rende tutti i glifi.
- [chore] Un provino diceva il falso da ieri, ed era il nostro.
shot_su344_note_rilascio.gd verificava che la v0.26 in tedesco ripiegasse in italiano — giusto quando è nato, assurdo adesso che il tedesco è tradotto fino alla v0.21. Peggio: cercava la stringa «Street University parla cinque lingue», corretta nello stesso giro perché il gioco si chiama STREET UNIVERSITY, quindi inseguiva un fantasma e stampava un esito che non voleva più dire niente. Riscritto per controllare l'opposto — che il tedesco resti tedesco fino in fondo — e ora stampa spie di italiano rimasto=0. 📌 Il ripiego non è stato tolto: resta la rete per una versione futura tradotta in ritardo, semplicemente nessuna versione oggi presente lo esercita.
RIENTRI ATTESI: SU-341, SU-344, SU-348 — le tre chiavi messe In revisione oggi. *(Giro precedente: delle dieci chiavi attese ne sono rientrate due, SU-340 e SU-344; le altre otto sono Fatto.)* 📌 Ma il numero da solo mente, e stavolta mente in meglio. Il changelog di ieri aveva scritto che il tasso di rientro mescola due cose diverse — fallimenti di qualità e lavoro che cresce — e che finché non le separiamo non dice niente. Separate: nessuno dei due rientri era un difetto. SU-340 diceva «fame hot dog ok / Sonno Luna ok / fai anche una icona per igiene» — le due consegnate erano approvate, il KO ne chiedeva una terza. SU-344 diceva «KO errore mio falle tutte multilingui quelle disponibili regresse», e «errore mio» sono parole sue. Quindi: zero fallimenti di qualità su dieci, contro i quattro su tredici del giro prima. È il primo giro in cui la separazione sta tutta da una parte sola. ⚠️ Il buco strutturale resta aperto per il settimo giro di fila: nessuna delle tre chiavi di oggi è stata vista su un device o un simulatore vero, e due su tre sono puramente visive — icone che devono leggersi a 22 px su un telefono al sole, e uno sprite che si distingue dall'altro per una bottiglia larga tre pixel. Il contatto a scala vera è una prova, ma non è un pollice su uno schermo da 6 pollici. ⚠️ E il multiplayer non è collaudato da sette giri: run_mp.sh non prova il percorso ENet su questo Mac (con eos_credentials.cfg presente NetworkManager sceglie il ramo EOS e il login anonimo fallisce), ed è un problema noto dal 2026-07-31 — ma da oggi SU-348 ha un limite MP dichiarato che vive esattamente lì.
Sprint 8bis: il colpo di calore diventa una sbronza, l'HUD si raddrizza, e l'export web c'era già2026-08-10
- [feat] SU-332 (KO) — il colpo di calore era mezzo effetto, ora è la sbronza intera. Il KO era netto: «lo farei esattamente come la sbronza — musica slow, effetti sullo schermo di distorsione, npc drunk». Prima l'insolazione faceva barcollare il barbone e si fermava lì, perché ogni effetto della sbornia era agganciato al suo
is_drunk in cinque file diversi. Ora c'è un predicato solo, GameState.is_woozy(), e da lì passano musica al rallentatore (pitch 0,60), distorsione a schermo, passanti con la testa d'animale e barcollio di camera: se domani nasce una terza afflizione barcollante si aggiunge lì dentro, e non può più capitare che un effetto segua la sbornia e un altro no — che è esattamente com'era nato questo KO. In più, come chiesto, durante l'insolazione vira all'arancione anche il mondo: nuovo HEAT_PALETTE_SHADER su un CanvasLayer 7 dedicato, sopra le finestre accese e sotto l'HUD, così le barre restano del loro colore e nessun file dell'HUD è stato toccato (era di un altro lotto). 📌 Scartato il CanvasModulate, e il motivo è la parte interessante: moltiplica, quindi avrebbe sommato il suo scurimento a quello notturno impastando la città. Lo shader invece conserva la luminanza (desaturazione 0,55 + tinta 1,32/0,84/0,52), e di notte il risultato è ambrato con le pozze di luce intatte invece che melma marrone. 📌 Una lettura dichiarata e ribaltabile: «esattamente come la sbronza» è stato applicato alla resa, non alle penalità — elemosina rifiutata, igiene che cala il triplo, bidoni inaccessibili e passanti in panico restano esclusiva della sbornia, perché prenderle avrebbe ribilanciato la calamità senza che nessuno l'avesse chiesto. ⚠️ Da guardare: la freccia verso il riparo resta verde acceso mentre tutto è arancione, perché vive nell'HUD ed è fuori dalla palette per costruzione. - [feat] SU-320 (KO, terzo giro) — la box cresce verso l'alto, e la riga contesa non si taglia. La soluzione l'ha data Ivan: «nella parte alta dello schermo non viene utilizzato lo spazio». A fine partita la fila delle vite si spegne, quindi non è più lei il tetto: a ENORME su telefono orizzontale la banda utile passa da 372 a 464 unità. La sovrapposizione fra lettere e «Inserisci il tuo nome:» passa da +2,8/+11,8 a −19,2/−23,2 su tutte e sette le combinazioni — il residuo dichiarato aperto due giri fa è chiuso, e senza togliere la riga, che era la strada alternativa. Stessa crescita sulla schermata finale con la classifica, più lo ScrollContainer chiesto come rete di sicurezza: la barra compare solo dove serve davvero (ENORME orizzontale, dove il testo sforava di 17 unità) e resta invisibile nelle altre 5 su 7. 📌 La causa vera, misurata e non indovinata: l'altezza del testo era calcolata sul font e valeva 145 unità dove il Label ne disegnava 171 —
line_spacing più le righe scritte col font-icona, che è più alto. Sul pannello finale lo scarto diventava 335 contro 507, ed è per questo che la classifica veniva tagliata. Non era un problema di scala: era un conto sbagliato che a taglia normale non si vedeva. Ora si misura un Label gemello invisibile e i due valori coincidono. - [change] SU-339 (KO) — il pannello si raddrizza e perde lo scotch. Era ruotato di −1,4° con due pezze di nastro adesivo sopra: ora è a 0,00° e le pezze sono zero. Controllati anche i vicini, come chiedeva il KO: la fila delle vite e la barra LIV non erano ruotate, quindi non sono state toccate. Il pannello si è anche stretto da 152×90 a 152×86, perché quelle quattro unità d'aria in cima esistevano solo per far passare il nastro. Consegnata anche la prova che Ivan aveva chiesto solo come screenshot — l'HUD senza la box senape — con il codice che resta *con* la box: è il provino a toglierla a runtime, così la scelta resta sua. ⚠️ Verdetto onesto sulla prova: le barre reggono ovunque perché hanno un fondo nero proprio, le icone no (luminanza 0,384 senza box contro 0,423 con box sul prato). Se vince «senza box» servono icone con contorno, e la decisione si incrocia con SU-340.
- [feat] SU-316 (KO, terzo giro) — lo stick si scolla dai tasti e diventa un anello. Il KO: «il controller (lo stick solo) lo vorrei molto trasparente e sullo stile di vampire survivor». Stick e tasti sfumavano insieme sullo stesso alpha; ora lo stick vive in un
_stick_root suo con STICK_ALPHA_FRACTION = 0,5, cioè parte sempre alla metà dei tasti — a riposo fa 0,20 × 0,5 = 0,10, quasi invisibile — pur restando governato dallo stesso slider, così Ivan aggiusta ancora tutto da un posto solo. Base e pallino sono ora due cerchi di linea sottile (draw_arc), contorno puro senza riempimento né rilievo; _ChunkyShape resta intatta e continua a disegnare i tasti, che non cambiano di una virgola. ⚠️ Limite dichiarato: lo screenshot allegato al ticket non è scaricabile da questa sessione, quindi «stile Vampire Survivors» è stato letto a parole. E il risultato non è VS puro: dentro l'anello restano le quattro frecce del D-PAD DIGITALE. 📌 Il primo contatto delle trasparenze è stato respinto dall'orchestratore: i tre riquadri erano indistinguibili e — al contrario di quanto diceva il report dell'agente — non avevano etichette. Faceva variare lo slider, che è la leva sbagliata: la sua forbice utile sullo stick va da 0,10 a 0,35, tutta dentro la zona quasi invisibile. Rifatto facendo variare la frazione (0,25/0,50/0,75, a riposo e toccato, su selciato scuro), che è l'unico numero che Ivan può correggere in una riga. - [feat] SU-338 — le note del busking sono pixel art, e due paure erano infondate.
note1.png da 1254×1254 e 203 KB a 12×16 e 629 byte, note2.png a 15×16. Player.gd non è stato toccato: git diff sul file è vuoto, cambiano solo i byte dei due PNG. 📌 Due cose che il ticket dava per scontate e che sono state verificate invece che assunte: il filtro NEAREST c'era già — in Godot 4 non è un campo del .import ma una proprietà del CanvasItem, e _make_busk_note() lo imposta a mano; e lo «scale tarato sui 1254 px» non esisteva, perché quella funzione fissa 14×14 con STRETCH_KEEP_ASPECT_CENTERED + EXPAND_IGNORE_SIZE, cioè indipendentemente dalla sorgente. Box misurato 14×14 identico prima e dopo: le note non escono microscopiche. - [feat] SU-344 (KO) — tradotta anche la v0.26, tredici bullet per sette lingue. 📌 Ma la scoperta che vale la voce è un'altra:
audit_emoji_pittogrammi.py non guarda i .txt — scandisce .gd, .tscn e i sette CSV. Su un lavoro fatto interamente di file di testo il nostro controllo standard sarebbe uscito verde senza aver letto una riga. Servita una scansione Unicode a parte (esito: zero emoji). ⚠️ E il master italiano diceva ancora «Street University»: la v0.26 in italiano si apriva con «Street University parla cinque lingue», testo che i giocatori leggono. L'hanno beccata le traduzioni, che dicevano già tutte STREET UNIVERSITY mentre l'originale no. Corretta; era l'unica occorrenza rimasta in tutti i testi di gioco, verificato con un grep su note di rilascio e CSV. - [feat] SU-345 — il renderer Compatibility misurato, e l'export web che c'era già. Il ticket chiedeva se convenga passare a Compatibility «visto che dobbiamo esportare anche per web». 📌 La risposta riscrive la domanda: l'export web esiste già ed è collaudato — preset
Web in export_presets.cfg:828, tools/build_web.sh, e tools/web_variant.py che scrive renderer/rendering_method.web = "gl_compatibility" sulla sola copia di lavoro. Il «doppio binario» che il ticket proponeva di valutare è in produzione da SU-267 ed è una riga per chiave di piattaforma, non un fork. La build è stata rifatta e fatta girare in Chrome: WebGL 2.0 - Compatibility, partita avviata, città generata, zero errori in console. Cosa si rompe passando a Compatibility: niente — tredici shader tutti canvas_item, zero GPUParticles2D (sono tutte CPU), zero BackBufferCopy, zero SubViewport, zero Light2D e CanvasModulate, perché l'illuminazione notturna è fatta a mano con uniform array: *la* differenza numero uno fra i due renderer qui non esiste proprio. Confronto A/B a parità di seme e fotogramma su 8 scene: mediana della differenza 0-2 livelli su 255. 📌 Fatta anche la controprova contro il falso negativo, che era il rischio scritto nel ticket: se uno shader non compilasse, Godot ripiegherebbe sul default e quei ColorRect uscirebbero bianchi. Non lo sono in nessuno scatto. Il costo è circa il doppio di CPU di disegno (2,19 → 4,50 ms di giorno, 2,30 → 5,75 di notte col colpo di calore), misurato col cronometro del viewport perché gli fps su questo Mac mentono: cappati dal compositore a 119,6 in un giro e 347 nello stesso giro ripetuto. Raccomandazione: lasciare tutto com'è. ⚠️ Da mettere agli atti: in Chrome con la scheda in secondo piano requestAnimationFrame gira a zero e il gioco si congela — la generazione della città sembrava metterci due minuti, non era lenta, era ferma. - [test] SU-340 e SU-347 — due generazioni Codex in temp, in attesa del «Fatto» di Ivan. Cinque candidate per l'icona igiene, disegnate col riferimento di hot dog e luna che Ivan ha scelto e non con
shower.png, che non è pixel art. E la variante del barbone insolato (arch_player_vheatstroke.png): l'ubriaco senza bottiglia e senza bollicine, mano chiusa a pugno e mai un buco. 📌 Verificate dall'orchestratore le due cose che di solito vanno storte con Codex: le pose diagonali non sono state raddrizzate (SE e NE restano di tre quarti in tutti i fotogrammi — è l'errore classico e si vede solo a occhio), e la palette non è derivata (29 dei 30 colori più frequenti identici all'originale, con il 4,5% di pixel in meno, cioè esattamente quanto pesano bottiglia e bollicine). ⚠️ Codex ha ricodificato l'immagine invece di modificarla pixel per pixel: si vede dal fondo magenta che cambia sfumatura. Non è un problema perché la pipeline rileva il fondo da sola, ma vuol dire che la variante va guardata, non data per buona. - [chore] SU-346 — via il nodo
CityEvents da Main.tscn, coda di SU-333. Tolti ext_resource e blocco del nodo, cancellati lo stub da 34 righe e il suo .uid, riscritti due commenti in World.gd che rimandavano a un file ormai inesistente. L'RPC _cl_city_event resta come no-op e non è una dimenticanza: in Godot 4 togliere un metodo rpc fa scalare gli id di tutti quelli dopo. Verificato con una partita vera e non col solo compile-check: 18 dump in 90 s, orphans 0, nessun «Node not found». - [feat]
/avvisa-tester impara a parlare a un canale alla volta. Nuovo parametro opzionale Android, Apple o PC (anche più d'uno, anche le singole iphone/ipad/mac/windows), girato a genera_link_messaggi.py come --piattaforma. Il filtro lavora sulle colonne vere dell'Excel, cercate per nome e mai per lettera, e passa solo chi ha un sì esplicito: nelle colonne delle piattaforme la cella vuota vale «non ce l'ha». Serve perché i canali non vanno mai allo stesso passo — Play completed e TestFlight ancora in revisione, o dmg e zip appena rifatti con gli store fermi. 📌 Il Mac sta sotto PC e non sotto Apple, perché il criterio è il canale da cui arriva la build (cartella desktop) e non la marca dell'hardware. Chi resta fuori dal filtro si ritrova i link ripuliti come gli esclusi, così non resta in colonna un piano B cliccabile che manderebbe l'annuncio proprio a chi si voleva lasciare per il giro dopo. Provato su una copia dell'Excel: senza filtro 65 tester, android 34, apple 23, pc 16. - [chore] Committati sei
.uid di provini rimasti indietro dallo sprint precedente (probe_su334_pausa, shot_su332_calamita, shot_su337_camioncino, shot_su339_hud, shot_su343_bagno, shot_su344_note_rilascio): i .gd erano tracciati, i sidecar no. Se n'è accorto un agente guardando git status, non un controllo nostro.
RIENTRI ATTESI: SU-316, SU-320, SU-332, SU-338, SU-339, SU-340, SU-344, SU-345, SU-346, SU-347 — le dieci chiavi messe In revisione oggi. *(Giro precedente: delle tredici chiavi attese ne sono rientrate sei — SU-316, SU-320, SU-332, SU-339, SU-340, SU-344 — cioè 6 su 13, contro il 2 su 9 del giro prima. SU-333 è tornato in «Da fare» ma non era una bocciatura: chiedeva di aprire il ticket di coda e mettere quello a Fatto. Le altre sei — SU-334, SU-335, SU-336, SU-337, SU-342, SU-343 — sono Fatto.)* 📌 La previsione scritta l'ultima volta va letta, perché è andata a metà. Erano stati indicati cinque sospetti in ordine: SU-320, SU-343, SU-316, SU-340, SU-339. Quattro su cinque sono rientrati davvero (SU-320, SU-316, SU-340, SU-339). Ma il secondo della lista, SU-343, è stato approvato pur avendo un difetto dichiarato aperto (FUMAROLA), mentre sono rientrati due ticket non previsti, SU-332 e SU-344. 📌 E la differenza fra i due gruppi è la cosa da imparare. I quattro previsti sono rientrati perché qualcosa non andava. I due imprevisti no: il KO di SU-332 chiedeva di andare oltre («fallo esattamente come la sbronza», più uno sprite nuovo) e quello di SU-344 pure («traduci anche la 0.26»). Non erano difetti, era il lavoro che cresceva. Il tasso di rientro mescola quindi due cose diverse, e finché non le separiamo un 6 su 13 può voler dire «abbiamo lavorato male» oppure «Ivan ha visto la cosa fatta e ha voluto di più» — qui erano quattro della prima specie e due della seconda. Dichiarare un difetto aperto non salva dal rientro (SU-320) ma nemmeno lo provoca (SU-343): quello che conta è se il difetto residuo dà fastidio a giocare. ⚠️ Il buco strutturale non è stato chiuso: nessuna delle dieci chiavi è stata vista su un device o un simulatore vero, e sei di loro riguardano il touch. ⚠️ Il multiplayer non è collaudato da sei giri, e SU-332 tocca ancora la resa condivisa fra i peer.
Sprint 8: il meteo si sente addosso, l'HUD dimagrisce dell'84%, e i controlli diventano tuoi2026-08-10
- [feat] SU-332 — le calamità meteo diventano tre e fanno male davvero. Prima il meteo era due cose separate e nessuna si sentiva: uno stato ambientale casuale che si ritirava ai dadi ogni 3 ore di gioco muovendo moltiplicatori invisibili, e le ondate caldo/freddo, che avevano la forma giusta (preavviso, freccia al riparo, finestra di pericolo) ma si limitavano a far calare le stat. Ora le calamità sono PIOGGIA, GRANDINE, ONDATA DI CALDO e ognuna lascia un segno: un secondo scoperto sotto la pioggia ti lascia fradicio per 60 s a velocità ×0,55 (
WET_DURATION/WET_SPEED_MULT), la grandine toglie una vita del gatto ogni 2,0 s (HAIL_LIFE_INTERVAL: la finestra dura 5 s, quindi al massimo due, con due secondi per correre al riparo), il caldo dà un colpo di calore da 18 s che riusa auto-walk, drift, wobble e stelline della sbornia — distinto per colore e senza cambio sprite, che resta esclusiva della sbornia vera. 📌 Il meteo casuale è sparito: via TRANSITION_TABLE e il reroll a ore di gioco. Lo stato ambientale resta ma fisso, con apply_district_weather() pronto per quando i quartieri vorranno imporne uno. La vecchia "cold" non viene cancellata ma tradotta da normalize_wave_kind() in grandine, così DebugPanel e i filtri meteo dei quartieri continuano a funzionare senza toccarli. In multiplayer l'insolazione viaggia dentro il flag drunk già presente in _net_state: zero byte in più sul pacchetto. ⚠️ Non misurato: il rallentamento da fradicio va *camminato*, i provini fotografano lo stato non la sensazione; e il multiplayer con due peer veri non è stato provato. - [change] SU-333 — via i sette eventi cittadini casuali, e uno di loro non ha mai fatto niente. Ogni 1-3 minuti il gioco pescava un evento e lo annunciava in alto: furgone della mensa, pioggia, festa di strada, giro di polizia, monete nei bidoni, colpo di freddo, turisti.
CityEvents.gd passa da 191 a 34 righe, e con lui spariscono lo spawn fisico del furgone e i messaggi di inizio/fine. 📌 La scoperta che vale la voce: get_generosity_modifier() — quello che dava +50% alla festa e +40% ai turisti — non aveva NESSUN chiamante. La resa dell'elemosina è 1.0 prima e 1.0 dopo: quei moltiplicatori non hanno mai influito su una singola monetina. L'RPC _cl_city_event resta come no-op e non viene cancellata, perché in Godot 4 togliere un metodo rpc fa scalare gli id di tutti quelli dopo e un client con build diversa non combacerebbe più. ⚠️ Resta aperto: il nodo CityEvents è ancora dichiarato in Main.tscn, che in questo sprint era di un altro lotto — serve un passaggio sulla scena. - [change] SU-339 — HUD minimale: il pannello perde l'84% dell'area. Da 224×326 a 152×90. Via le scritte FAME/ENERGIA/IGIENE e l'intera riga RICERCATO: resta
[icona | barra]. Un colore fisso per statistica (PB_COL_HUNGER verde, PB_COL_ENERGY blu, PB_COL_HYGIENE ciano): la barra si svuota e non cambia più tinta, così il colore identifica *cosa* stai guardando e non *quanto* stai messo male — il pericolo resta segnalato dal solo lampeggio sotto STAT_DANGER. Nell'angolo in alto a destra meteo e RECORD lasciano il posto a ora, giorno e countdown della retata; il punteggio resta. I soldi escono dal pannello e vanno nel corridoio fra pannello e barra XP/vita, con un ripiego che li fa scendere sotto quando il canvas è 4:3. wanted_level continua a essere calcolato: sparisce dalla vista, non dal gioco. Tolte 9 chiavi orfane da ui_hud.csv. - [fix] SU-334 — il pulsante pausa spariva dopo RICOMINCIA, e la causa non era quella scritta nel ticket. L'indizio nel ticket puntava allo snapshot attorno alle Opzioni: escluso, e poi rimosso perché inutile. La causa vera: l'HUD leggeva
VirtualControls.visible, una condizione composta il cui ultimo termine vive in una cache aggiornata da un poll a 20 Hz. RICOMINCIA ricarica la scena nello stesso frame in cui il pannello di fine partita è ancora a schermo; il nuovo HUD interroga la cache prima del poll, riceve «controlli nascosti», e siccome quella lettura era una sola (call_deferred, mai ripetuta) il pulsante restava spento per tutta la partita nuova, mentre joystick e tasti tornavano su 0-50 ms dopo. L'intermittenza è esattamente questo: dipende da dove cade il poll rispetto al reload, ed è il motivo per cui chiudere e riaprire l'app la scatenava. Il rimedio non è una pezza: si chiedono le due condizioni strutturali (si gioca a dita? c'è un pad?), che danno la stessa risposta a qualunque istante le si interroghi. Sonda deterministica in probe_su334_pausa.gd, 9 controlli su 9. ⚠️ Un difetto introdotto dal rimedio e chiuso in chiusura: slegando il pulsante da vc.visible aveva smesso di sparire anche al game over — il pannello sta nel gruppo touch_modal, quindi prima lo nascondeva di riflesso — e sarebbe rimasto a galleggiare sopra la schermata di fine partita, visibile ma inerte. Aggiunto un controllo su game_over_panel.visible letto direttamente e non dalla cache, così la proprietà che rende buono il rimedio resta intatta. - [feat] SU-316 (KO) — sezione CONTROLLI, trasparenza regolabile e joystick che nasce sotto il dito. Il KO di Ivan riscriveva il ticket da capo: lo 0,15 cablato del giro precedente doveva diventare uno slider, più una spunta per togliere del tutto il joystick, il tutto dentro una sezione CONTROLLI. Il fade non si butta: diventa configurabile (default a 0,20 per cadere su una fermata dello slider da 10%). 📌 La sezione non è una pagina nuova di
OptionsPanel ma un filtro sulla tabella VOCI: si entra e si esce dentro il componente, quindi compare sia nel menu principale sia in pausa per costruzione, senza toccare MainMenu.gd né HUD.gd che erano di altri lotti. 📌 Il pad fluttuante non tiene nessuna lista di rettangoli dell'HUD: la pressione si raccoglie in _unhandled_input, cioè dopo _input e dopo la GUI — quello che arriva lì è per definizione «area libera», e la definizione si aggiorna da sola se l'HUD cambia. Trascinamento e rilascio restano in _input, così il dito catturato non si può rubare a metà corsa. Il centro non è clampato, perché il KO dice «il centro è il primo punto che si tocca»: scarto misurato < 1 px, e il primo tocco vale già come comando (28,5 px di movimento senza staccare). ⚠️ Il prezzo dichiarato: se un domani si mette sopra il gioco un Control a tutto schermo che ferma il mouse, il pad smette di nascere lì. - [fix] SU-337 — il barbone passava davanti ai camioncini, ed era una riga. Il mondo non usa lo Y-sort di Godot: ordina a mano con
z_index, e la REGOLA 1 scritta in cima a WorldGenerator.gd dice che lo z va messo sulla y del punto in cui l'oggetto tocca il suolo. I camioncini usavano rect.get_center().y - 28.0, cioè il tetto dello sprite: finivano ~56 px più a nord di dove appoggiano, e chi ci stava dietro prendeva uno z più alto. Ora è + 28.0, la stessa quota che _ombra_camioncino usava già per l'ombra — van e ombra sono finalmente d'accordo. Provini coi numeri a schermo: van z=500 / barbone z=544 (sopra, sbagliato) → van z=556 / barbone z=544 (sparisce dietro), e il caso «davanti» resta 364 contro 376. - [fix] SU-343 — il bagno chimico copriva altra roba: da 44 sovrapposizioni a 14 città pulite su 16. Il piazzamento verificava solo il punto d'appoggio, con una clearance circolare di ~40 px contro un ingombro visivo alto 47. Ora c'è
BAGNO_ALTEZZA_VISIVA misurata sui PNG e _bagno_ingombro_libero(), che confronta il rettangolo vero della fila con rect, occupied_blocks, bin_avoid_rects e i prop vicini; _punto_bagno ripesca fino a 20 volte e l'ultima spiaggia prova quattro angoli veri su tutti i piazzali liberi invece di un offset alla cieca sul più grande. Censimento su 8 quartieri × 2 semi. ⚠️ NON risolto e dichiarato sul ticket: FUMAROLA resta sovrapposta su entrambi i semi — quartiere povero di piazze che genera sei cabine, dove una fontana da 56 px in un isolato da 96 non lascia spazio a una fila da 59. La strada proposta: quando la fila intera non ci sta, piazzarne una più corta invece di rinunciare o sovrapporre. - [fix] SU-320 (KO) + SU-342 — la schermata del nome regge l'ingrandimento del testo. Il giro precedente aveva ingrandito tasti e box provando solo il canvas storico (768 unità a DIMENSIONE TESTO normale), senza mai aprire l'opzione su grande o enorme — che restringe il canvas vero (768 → 512 a 1,5×, via
content_scale_factor). Il box.offset_bottom = 350.0 era un numero assoluto: appena il canvas si stringeva, CONFERMA finiva fuori. Ora il layout parte dal fondo utile e sale, mai il contrario, e per guadagnare spazio stringe prima il font del motivo e mai i tasti. 14 combinazioni provate (4 taglie testo + 3 orientamenti/dispositivi, schermata nome e classifica), tutte con margine positivo. La classifica non è stata toccata: è già proporzionale al viewport, e HighScore.MAX_ENTRIES è 5 — non può diventare lunga da dover scorrere. 📌 Un difetto in più trovato in chiusura, presente a TUTTE le taglie e non solo alle grandi: le Label delle lettere stanno a y = -36 rispetto all'origine del NameEntry, quindi sporgono sopra il punto in cui lo si posiziona, e risalivano a coprire «Inserisci il tuo nome:» — ereditato da quando il font degli slot è passato da 42 a 56. Ora la sporgenza la dichiara NameEntry.letters_top_overhang(), e l'aria fra scritta e lettere è diventata la prima cosa che si sacrifica quando lo spazio manca, prima del font e molto prima di accettare una sovrapposizione. ⚠️ Residuo: a GRANDE ed ENORME su telefono orizzontale il contenuto non ci sta e le lettere coprono ancora quella riga; a PICCOLO e MEDIO è pulito, e prima non lo era a nessuna taglia. - [feat] SU-344 — le note di rilascio escono nella lingua scelta, in tutte e otto. Erano un unico file italiano, e da quando l'avviso «nuova versione installata» ci manda la gente a leggerle (SU-331) quella schermata la apre molta più gente. Otto file separati (
release_notes_<lingua>.txt), l'italiano resta il master ed è anche il ripiego: se una lingua non ha la sezione di una versione si mostra quella italiana sezione per sezione, non un buco. Tradotte a mano le ultime tre versioni; dalla v0.26 in giù si ripiega, per scelta dichiarata. prepare_release.sh ora avvisa anche quando una singola lingua resta indietro. ⚠️ Senza il pezzo aggiunto in chiusura, il ticket sarebbe stato un no-op nelle build vere: i sei include_filter di export_presets.cfg e l'array RICHIESTI di export_all.sh nominavano solo release_notes_it.txt, quindi dmg, zip e apk avrebbero imbarcato la sola versione italiana — e il ripiego in gioco l'avrebbe nascosto benissimo, fino al primo tester straniero. Le otto lingue sono elencate una per una e non con un jolly, perché pre-volo e verifica del .pck confrontano stringa per stringa. - [fix] SU-336 — l'alone magenta del gatto era impasto col fondo, e i coriandoli si sono salvati per un pelo. L'alone nasce dal fondo magenta dei raw non tolto prima della riduzione: la media d'area lo impasta nel contorno (stesso difetto di SU-255 e SU-305) e si riconosce perché ogni pixel ha un colore diverso — 95 pixel, 95 colori. Ricolorata la sola frangia col vicino opaco sano, alpha mai toccato: 73 px su
catfight.png, 6 su catsleep.png, 4 su catfly.png; sagome e bbox invariati al pixel. 📌 Il primo giro li aveva ricolorati tutti, spegnendo i 21 pixel rosa isolati che sono i coriandoli disegnati della nuvola di rissa; ripristinato da git e rifatto con la regola giusta — si ricolora solo ciò che tocca un pixel sano, ciò che galleggia isolato nel trasparente è arte. Stesso motivo per cui caticon.png non è stato toccato: i suoi 8 pixel sono due blocchi 2×2 di colore piatto, cioè le zampette. Script in repo perché il giro si ripeta identico. ⚠️ Scoperta fuori scope: la stessa frangia sta su tutti i fogli personaggio (player_sheet 151 px, player_cat_sheet 145, polizia 142, civili 123, ubriaco 120, gattara 97). È un artefatto sistemico della pipeline di import, non un difetto del gatto, e non è stato toccato: pulire player_cat_sheet senza player_sheet farebbe cambiare il contorno del barbone nell'istante in cui raccoglie un gatto. - [test] SU-335 e SU-340 — generazioni Codex consegnate in temp, in attesa del «Fatto» di Ivan, che è l'approvazione. Due note musicali disegnate native a 12×16 e 15×16 al posto dei due raw da 1254×1254 che il motore rimpiccioliva (è quello il motivo per cui stonavano); e quattro icone candidate per fame e sonno. 📌 Una misura che cambia la domanda:
shower.png, che il ticket indicava come riferimento di stile, non è pixel art — è un raw 1254×1254 sfumato, cioè ha lo stesso identico difetto delle note musicali. Le candidate sono quindi uscite morbide e coerenti con lei, ma non coi pixel piatti dell'hot dog attuale: la scelta davanti a Ivan non è quale disegno, è quale stile, e se vince l'hot dog allora è la doccia a dover essere rifatta. - [docs] Nota di processo sull'orchestrazione:
ui_menu.csv era stato assegnato per errore a due lotti contemporaneamente. Nessun danno — il secondo non ci ha scritto e le 10 chiavi del primo sono state verificate presenti a fine giro — ma la mappa di proprietà dei file va controllata anche sui CSV, non solo sugli .gd.
RIENTRI ATTESI: SU-316, SU-320, SU-332, SU-333, SU-334, SU-335, SU-336, SU-337, SU-339, SU-340, SU-342, SU-343, SU-344 — le tredici chiavi messe In revisione in questo giro. Al prossimo sprint, prima dei lotti: quante sono tornate in «Da fare» e con quale KO. *(Giro precedente: delle nove chiavi attese — SU-314, SU-315, SU-316, SU-317, SU-318, SU-319, SU-320, SU-322, SU-323 — ne sono rientrate due, SU-316 e SU-320: tasso di rientro 2 su 9. SU-322 è ancora In revisione, le altre sei sono Fatto. La previsione scritta allora indicava SU-315 e SU-316 come primi sospetti: SU-316 è rientrato davvero, SU-315 no.)* ⚠️ Come andrà letto questo giro, dichiarato prima di conoscere il risultato: sono tredici chiavi e nessuna è stata vista su un device o un simulatore vero — solo provini in-engine sul Mac, dove la safe-area è sempre zero. È la stessa lacuna strutturale del giro scorso, e non è stata chiusa. ⚠️ I candidati al rientro, in ordine di sospetto. Primo: SU-320/SU-342, dove un difetto di resa è dichiarato aperto (le lettere sopra «Inserisci il tuo nome:» a GRANDE ed ENORME) — se Ivan lo giudica inaccettabile torna indietro, ed è il rientro più prevedibile che abbiamo mai scritto. Secondo: SU-343, dove FUMAROLA è dichiarata non risolta. Terzo: SU-316, la feature più grossa del giro e quella che dipende di più dal comportamento vero del touch di iOS, che qui è simulato da Input.parse_input_event. Quarto: SU-340, dove la risposta giusta potrebbe essere «rigenerate tutte con un altro riferimento». Quinto: SU-339, dove blu ed energia e ciano e igiene sono colori vicini e ora il colore è metà dell'identificazione. ⚠️ Il buco di sempre: il multiplayer non è stato collaudato, e SU-332 tocca il relay delle fasi meteo mentre SU-333 smonta quello degli eventi città. È il candidato che rientra da cinque giri di fila.