Compare commits
2 Commits
c0ad354458
...
8e59c3b1bf
| Author | SHA1 | Date |
|---|---|---|
|
|
8e59c3b1bf | |
|
|
f96f2d491c |
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
Binary file not shown.
|
|
@ -20,7 +20,7 @@
|
|||
},
|
||||
"uiSheet": {
|
||||
"key": "advancewars-ui",
|
||||
"path": null,
|
||||
"path": "assets/images/advancewars/advancewars-ui.png",
|
||||
"frameWidth": 48,
|
||||
"frameHeight": 48
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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 });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,85 @@
|
|||
// Advance Wars "Day N" banner — shown once per full day cycle, right as
|
||||
// control returns to the player after the last enemy army's turn. Each
|
||||
// letter flies in on its own (alternating in from above/below with a
|
||||
// dramatic overshoot), the full word holds for 1.5s, then every letter
|
||||
// flies back out the way it came. Tap to skip.
|
||||
|
||||
import { GAME_WIDTH, GAME_HEIGHT } from '../../config.js';
|
||||
import { FONT } from './AdvanceWarsUI.js';
|
||||
|
||||
const SIZE = 150;
|
||||
const HOLD_MS = 1500;
|
||||
const STAGGER_MS = 70;
|
||||
const FLY_DIST = 420;
|
||||
const IN_DURATION = 420;
|
||||
const OUT_DURATION = 380;
|
||||
|
||||
export function playDayBanner(scene, day, onDone) {
|
||||
const text = `DAY ${day}`;
|
||||
const cy = GAME_HEIGHT / 2;
|
||||
const objs = [];
|
||||
let finished = false;
|
||||
|
||||
const dim = scene.add.rectangle(GAME_WIDTH / 2, cy, GAME_WIDTH, 260, 0x000000, 0.55)
|
||||
.setDepth(60).setInteractive();
|
||||
objs.push(dim);
|
||||
|
||||
const style = {
|
||||
fontFamily: FONT, fontSize: `${SIZE}px`, color: '#ffffff',
|
||||
stroke: '#000000', strokeThickness: 10,
|
||||
};
|
||||
const spaceWidth = SIZE * 0.45;
|
||||
const gap = 6;
|
||||
|
||||
// lay every non-space letter out at its final centered position first
|
||||
// (measuring natural glyph width as each is created), then animate each
|
||||
// one in from off-screen to that spot.
|
||||
const letters = [];
|
||||
let cursor = 0;
|
||||
for (const ch of text) {
|
||||
if (ch === ' ') { cursor += spaceWidth + gap; continue; }
|
||||
const t = scene.add.text(0, cy, ch, style).setOrigin(0, 0.5).setDepth(61).setAlpha(0);
|
||||
letters.push({ t, x: cursor });
|
||||
cursor += t.width + gap;
|
||||
objs.push(t);
|
||||
}
|
||||
const totalWidth = cursor - gap;
|
||||
const startX = GAME_WIDTH / 2 - totalWidth / 2;
|
||||
for (const l of letters) l.x += startX;
|
||||
|
||||
const cleanup = () => {
|
||||
if (finished) return;
|
||||
finished = true;
|
||||
for (const o of objs) o.destroy();
|
||||
onDone?.();
|
||||
};
|
||||
dim.on('pointerdown', cleanup);
|
||||
|
||||
letters.forEach(({ t, x }, i) => {
|
||||
const fromAbove = i % 2 === 0;
|
||||
t.setPosition(x, cy + (fromAbove ? -FLY_DIST : FLY_DIST));
|
||||
t.setScale(2.2);
|
||||
t.setRotation(fromAbove ? -0.6 : 0.6);
|
||||
scene.tweens.add({
|
||||
targets: t, x, y: cy, scale: 1, rotation: 0, alpha: 1,
|
||||
delay: i * STAGGER_MS, duration: IN_DURATION, ease: 'Back.easeOut',
|
||||
});
|
||||
});
|
||||
|
||||
const inTotal = (letters.length - 1) * STAGGER_MS + IN_DURATION;
|
||||
scene.time.delayedCall(inTotal + HOLD_MS, () => {
|
||||
if (finished) return;
|
||||
letters.forEach(({ t }, i) => {
|
||||
const toAbove = i % 2 !== 0;
|
||||
scene.tweens.add({
|
||||
targets: t, y: cy + (toAbove ? -FLY_DIST : FLY_DIST), scale: 2.2,
|
||||
rotation: toAbove ? 0.6 : -0.6, alpha: 0,
|
||||
delay: i * STAGGER_MS, duration: OUT_DURATION, ease: 'Back.easeIn',
|
||||
});
|
||||
});
|
||||
const outTotal = (letters.length - 1) * STAGGER_MS + OUT_DURATION;
|
||||
scene.time.delayedCall(outTotal + 80, cleanup);
|
||||
});
|
||||
|
||||
return { skip: cleanup };
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@ import { AdvanceWarsHUD, ActionMenu, DamagePreview, ProductionMenu, TileInfo, mk
|
|||
import * as Screens from './AdvanceWarsScreens.js';
|
||||
import { playBattleAnim } from './AdvanceWarsBattleAnim.js';
|
||||
import { playCaptureAnim } from './AdvanceWarsCaptureAnim.js';
|
||||
import { playDayBanner } from './AdvanceWarsDayBanner.js';
|
||||
|
||||
const SAVE_KEY = 'advancewars-save';
|
||||
const ANIM_KEY = 'advancewars-battle-anims';
|
||||
|
|
@ -507,6 +508,10 @@ export default class AdvanceWarsGame extends Phaser.Scene {
|
|||
await this.replayEvents(log, { enemy: true });
|
||||
}
|
||||
if (this.run !== run) return;
|
||||
if (!run.state.result) {
|
||||
await new Promise((resolve) => playDayBanner(this, run.state.day, resolve));
|
||||
}
|
||||
if (this.run !== run) return;
|
||||
run.busy = false;
|
||||
this.refreshAll();
|
||||
if (run.state.result) { this.onMissionOver(); return; }
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Reference in New Issue