diff --git a/assets/images/advancewars/advancewars-ui.png b/assets/images/advancewars/advancewars-ui.png new file mode 100644 index 0000000..71954ff Binary files /dev/null and b/assets/images/advancewars/advancewars-ui.png differ diff --git a/assets/images/advancewars/advancewars-ui.psd b/assets/images/advancewars/advancewars-ui.psd new file mode 100644 index 0000000..8a858b4 Binary files /dev/null and b/assets/images/advancewars/advancewars-ui.psd differ diff --git a/data/advancewars-artwork.json b/data/advancewars-artwork.json index c54fe60..178755f 100644 --- a/data/advancewars-artwork.json +++ b/data/advancewars-artwork.json @@ -20,7 +20,7 @@ }, "uiSheet": { "key": "advancewars-ui", - "path": null, + "path": "assets/images/advancewars/advancewars-ui.png", "frameWidth": 48, "frameHeight": 48 }, diff --git a/src/games/advancewars/AdvanceWarsBattleAnim.js b/src/games/advancewars/AdvanceWarsBattleAnim.js index 62f129c..a5bcf75 100644 --- a/src/games/advancewars/AdvanceWarsBattleAnim.js +++ b/src/games/advancewars/AdvanceWarsBattleAnim.js @@ -10,6 +10,7 @@ import { playSound } from '../../ui/Sounds.js'; const W = 760; const H = 380; +const HP_TEXT_PADDING = 20; // --------------------------------------------------------------------------- // Battle backgrounds: one backdrop image per terrain/building a fighter can @@ -232,7 +233,14 @@ export function playBattleAnim(scene, rules, state, texKey, battles, onDone) { const bar = scene.add.rectangle(cx + side * W / 4 - 108, cy - H / 2 + 58, 216, 12, 0x7dff9a, 1).setOrigin(0, 0.5).setDepth(52); objs.push(barBg, bar); - return { img, bar, spec }; + // remaining-HP readout in the outer top corner of this fighter's half — + // same font/size as the capture cut-in's points counter. 1-10 scale + // (AW's classic HP display), zero being the floor. + const hpText = scene.add.text(cx + side * W / 2 - side * HP_TEXT_PADDING, cy - H / 2 + HP_TEXT_PADDING, '', + { fontFamily: FONT, fontSize: '64px', color: '#ffe14d', stroke: '#000000', strokeThickness: 6 }) + .setOrigin(side > 0 ? 1 : 0, 0).setDepth(51); + objs.push(hpText); + return { img, bar, spec, hpText }; }; // starting HP = hp before the volleys (event carries post-hit hp; rebuild) @@ -249,8 +257,18 @@ export function playBattleAnim(scene, rules, state, texKey, battles, onDone) { f.bar.width = Math.max(0, 216 * (hp / 100)); f.bar.setFillStyle(hp > 50 ? 0x7dff9a : hp > 25 ? 0xffe14d : 0xff5a5a, 1); }; + const hpDisplay = (hp) => Math.max(0, Math.ceil(hp / 10)); + const animateHp = (f, fromHp, toHp) => { + const proxy = { hp: fromHp }; + scene.tweens.add({ + targets: proxy, hp: toHp, duration: 500, ease: 'Sine.easeOut', + onUpdate: () => f.hpText.setText(String(hpDisplay(proxy.hp))), + }); + }; setBar(left, leftHp); setBar(right, rightHp); + left.hpText.setText(String(hpDisplay(leftHp))); + right.hpText.setText(String(hpDisplay(rightHp))); const flip = scene.time.addEvent({ delay: 220, loop: true, callback: () => { @@ -290,8 +308,17 @@ export function playBattleAnim(scene, rules, state, texKey, battles, onDone) { objs.push(flash); scene.tweens.add({ targets: flash, alpha: 0, duration: 450, onComplete: () => flash.destroy() }); scene.cameras.main.shake(120, 0.004); - if (fromLeft) { rightHp = b.defender.hp; setBar(right, rightHp); } - else { leftHp = b.defender.hp; setBar(left, leftHp); } + if (fromLeft) { + const prevHp = rightHp; + rightHp = b.defender.hp; + setBar(right, rightHp); + animateHp(right, prevHp, rightHp); + } else { + const prevHp = leftHp; + leftHp = b.defender.hp; + setBar(left, leftHp); + animateHp(left, prevHp, leftHp); + } if (b.killed) { scene.tweens.add({ targets: victim.img, alpha: 0, y: victim.img.y + 16, duration: 550 }); } diff --git a/src/games/advancewars/AdvanceWarsMapView.js b/src/games/advancewars/AdvanceWarsMapView.js index 974e695..83b1025 100644 --- a/src/games/advancewars/AdvanceWarsMapView.js +++ b/src/games/advancewars/AdvanceWarsMapView.js @@ -101,6 +101,25 @@ function mkCanvasSheet(scene, key, spec, count) { const ground = (c, color) => { c.fillStyle = color; c.fillRect(0, 0, CELL, CELL); }; +// Tile cursor is always the built-in procedural corner-bracket art — kept +// independent of the UI sheet even once it's painted, per explicit request. +function drawCursor(c) { + c.strokeStyle = '#ffffff'; c.lineWidth = 4; + for (const [sx, sy, dx, dy] of [[3, 3, 1, 1], [45, 3, -1, 1], [3, 45, 1, -1], [45, 45, -1, -1]]) { + c.beginPath(); c.moveTo(sx + dx * 12, sy); c.lineTo(sx, sy); c.lineTo(sx, sy + dy * 12); c.stroke(); + } +} + +const CURSOR_TEX_KEY = 'advancewars-cursor-proc'; + +function ensureCursorTexture(scene) { + if (scene.textures.exists(CURSOR_TEX_KEY)) return CURSOR_TEX_KEY; + const tex = scene.textures.createCanvas(CURSOR_TEX_KEY, CELL, CELL); + drawCursor(tex.getContext()); + tex.refresh(); + return CURSOR_TEX_KEY; +} + const PROC_PAINTERS = { terrain(scene, key, spec) { const { at, finish } = mkCanvasSheet(scene, key, spec, spec.frames); @@ -180,12 +199,7 @@ const PROC_PAINTERS = { ui(scene, key, spec) { const { at, finish } = mkCanvasSheet(scene, key, spec, spec.frames); - at(UI_FRAMES.cursor, (c) => { - c.strokeStyle = '#ffffff'; c.lineWidth = 4; - for (const [sx, sy, dx, dy] of [[3, 3, 1, 1], [45, 3, -1, 1], [3, 45, 1, -1], [45, 45, -1, -1]]) { - c.beginPath(); c.moveTo(sx + dx * 12, sy); c.lineTo(sx, sy); c.lineTo(sx, sy + dy * 12); c.stroke(); - } - }); + at(UI_FRAMES.cursor, drawCursor); const boom = (r) => (c) => { c.fillStyle = '#ffb02f'; for (let i = 0; i < 8; i++) { @@ -244,7 +258,7 @@ export class AdvanceWarsMapView { this.atkG = scene.add.graphics().setDepth(this.depths.atkOv); this.fogG = scene.add.graphics().setDepth(this.depths.fog); this.pathG = scene.add.graphics().setDepth(this.depths.path); - this.cursor = scene.add.image(0, 0, this.tex.ui, UI_FRAMES.cursor) + this.cursor = scene.add.image(0, 0, ensureCursorTexture(scene)) .setDisplaySize(this.tile, this.tile) .setDepth(this.depths.cursor).setVisible(false); this.syncUnits();