diff --git a/assets/characters/andrea-birra.json b/assets/characters/andrea-birra.json new file mode 100644 index 0000000..862e376 --- /dev/null +++ b/assets/characters/andrea-birra.json @@ -0,0 +1,70 @@ +{ + "image": "andrea-birra.png", + "height": 120, + "frames": { + "walk_1": { + "x": 0, + "y": 0, + "w": 68, + "h": 114, + "ax": 40.2, + "ay": 114 + }, + "walk_2": { + "x": 70, + "y": 0, + "w": 41, + "h": 114, + "ax": 20.7, + "ay": 114 + }, + "walk_3": { + "x": 113, + "y": 0, + "w": 67, + "h": 114, + "ax": 38.4, + "ay": 114 + }, + "walk_4": { + "x": 182, + "y": 0, + "w": 46, + "h": 114, + "ax": 25.3, + "ay": 114 + }, + "cheer_1": { + "x": 230, + "y": 0, + "w": 51, + "h": 133, + "ax": 21.1, + "ay": 133 + }, + "cheer_2": { + "x": 283, + "y": 0, + "w": 55, + "h": 130, + "ax": 24.5, + "ay": 130 + }, + "cheer_3": { + "x": 340, + "y": 0, + "w": 62, + "h": 130, + "ax": 23.8, + "ay": 130 + }, + "talk": { + "x": 404, + "y": 0, + "w": 58, + "h": 120, + "ax": 21.7, + "ay": 120 + } + } +} diff --git a/assets/characters/andrea-birra.png b/assets/characters/andrea-birra.png new file mode 100644 index 0000000..2ef622b Binary files /dev/null and b/assets/characters/andrea-birra.png differ diff --git a/src/engine/render.js b/src/engine/render.js index 7f76ab8..6d310d3 100644 --- a/src/engine/render.js +++ b/src/engine/render.js @@ -179,7 +179,8 @@ export function render(ctx, game, { hover, spots, debug, time = performance.now( for (const shape of art) if (!shape.top) drawShape(ctx, shape); // Andrea e i personaggi della scena, dal più lontano al più vicino. const andrea = sprites.get('andrea'); - const drawAndreaNow = () => andrea ? drawSprite(ctx, andrea, game.pose(), s.x, s.y, game.scaleAt(s.y), s.facing) + const party = festa(game, time), drawAndreaNow = () => party ? drawSprite(ctx, party.sheet, party.pose, s.x, s.y, game.scaleAt(s.y), s.facing) + : andrea ? drawSprite(ctx, andrea, game.pose(), s.x, s.y, game.scaleAt(s.y), s.facing) : drawAndrea(ctx, s.x, s.y, game.scaleAt(s.y), s.facing, game.walking ? game.stride / 6 : null); // Durante il risveglio si guarda dagli occhi di Andrea: lui non si vede. const pov = game.closeup && game.story.closeups?.[game.closeup.id]?.type === 'risveglio'; @@ -765,6 +766,16 @@ export const EFFECTS = { ctx.restore(); }, }; +// All'Ingorgo Andrea ha la sua birra (Andrea, 31 ottobre): camminando la tiene davanti al petto, parlando pure; fermo +// alza il braccio col bicchiere e lo muove a tempo con la folla. Prendere e voltarsi restano quelli di sempre. +const CHEER = ['cheer_1', 'cheer_2', 'cheer_1', 'cheer_3']; +export function festa(game, time) { + const sheet = sprites.get('andrea_birra'), band = (game.scene.effects ?? []).find(e => e.type === 'crowd' && onNow(e, game)); + if (!sheet || !band) return null; + const pose = game.pose(), beat = Math.floor(time / 60000 * (band.bpm ?? 120)) % CHEER.length; + const mine = pose === 'stand' ? CHEER[beat] : pose; + return sheet.atlas.frames[mine] ? { sheet, pose: mine } : null; +} // Andrea dentro la folla dell'Ingorgo (Andrea, 31 ottobre: «sembra di volare fra le persone»): sopra di lui, dalla // vita in giù, si ridisegna la gente dello sfondo che gli sta davanti. A strisce come l'effetto `crowd`, con lo stesso // sobbalzo a tempo (così le teste davanti saltano con le altre) e il bordo in alto a teste, mai dritto. diff --git a/src/story/story.js b/src/story/story.js index 2c78db9..9c0265e 100644 --- a/src/story/story.js +++ b/src/story/story.js @@ -531,6 +531,7 @@ export const story = { // Andrea col pallone, per «Fai due tiri» (foglio di Codex dal suo sprite): non parla, lo disegna basket-render. andrea_basket: { name: 'Andrea', sprite: 'assets/characters/andrea-basket.json' }, andrea_pesca: { name: 'Andrea', sprite: 'assets/characters/andrea-pesca.json' }, + andrea_birra: { name: 'Andrea', sprite: 'assets/characters/andrea-birra.json' }, // all'Ingorgo, con la birra // Quattro ragazzi inventati (sprite di Codex, idea di Andrea del 27 ottobre): nessuna persona reale. pescatori: { name: 'I ragazzi delle canne', color: '#b8f08a', sprite: 'assets/characters/pescatori.json' }, // La gente della Smarronata del 1993 (Andrea, 31 ottobre): tutti inventati, vestiti come nei fotogrammi del video. diff --git a/tests/_tmp_folla.browser.mjs b/tests/_tmp_folla.browser.mjs deleted file mode 100644 index 4265a79..0000000 --- a/tests/_tmp_folla.browser.mjs +++ /dev/null @@ -1,17 +0,0 @@ -import { chromium } from 'playwright'; -import { staticServer } from './helpers.mjs'; -const server = await staticServer(); -const browser = await chromium.launch({ args: ['--use-gl=angle', '--use-angle=swiftshader', '--enable-unsafe-swiftshader'] }); -const page = await browser.newPage({ viewport: { width: 1280, height: 860 } }); -await page.goto(`http://127.0.0.1:${server.port}/`); -await page.click('#splash'); await page.click('#new-game'); await page.click('#rione [data-rione="campomigliaio"]'); await page.click('#skip-intro'); -for (const [scene, at] of JSON.parse(process.env.SPOTS)) { - await page.evaluate(async ([scene, at]) => { - const { settle } = await import('/tests/helpers.mjs'); - settle(game); game.state.flags.ingorgo = true; - game.go(scene, at, 'right'); settle(game); - }, [scene, at]); - await page.waitForTimeout(1500); - await page.screenshot({ path: `${process.env.S}/folla-${scene}-${at[1]}.png`, clip: { x: 98, y: 60, width: 1084, height: 610 } }); -} -await browser.close(); server.kill(); diff --git a/tests/ingorgo.test.mjs b/tests/ingorgo.test.mjs index c12031d..cc6ecdf 100644 --- a/tests/ingorgo.test.mjs +++ b/tests/ingorgo.test.mjs @@ -168,6 +168,16 @@ test('le musiche e gli sfondi della festa esistono', () => { for (const f of files) assert.ok(existsSync(new URL('../' + f, import.meta.url)), `manca ${f}`); }); +test('all\'Ingorgo Andrea ha la birra: il foglio coi passi, il braccio alzato a tempo e la battuta', async () => { + const { readFileSync } = await import('node:fs'); + const atlas = JSON.parse(readFileSync(new URL('../assets/characters/andrea-birra.json', import.meta.url))); + for (const f of ['walk_1', 'walk_2', 'walk_3', 'walk_4', 'cheer_1', 'cheer_2', 'cheer_3', 'talk']) assert.ok(atlas.frames[f], f); + assert.equal(story.characters.andrea_birra.sprite, 'assets/characters/andrea-birra.json'); + const { festa } = await import('../src/engine/render.js'); + const g = new Game(story); g.go('piazza'); settle(g); + assert.equal(festa(g, 0), null, 'senza la festa (e senza il foglio caricato) Andrea è quello di sempre'); +}); + test('alla festa Andrea sta dentro la folla: dalla vita in giù la gente dello sfondo gli passa davanti', async () => { const { crowdFront } = await import('../src/engine/render.js'); const g = new Game(story); g.go('piazzetta', [330, 320]); settle(g);