feat(ui): add custom fonts and apply Audiowide/RaiderCrusader across all scenes

- Add `Audiowide-Regular.ttf` and `raidercrusader.ttf` to assets/fonts.
- Update Phaser version to v3.90.0 in index.html.
- Load custom fonts in BootScene.preload().
- Apply `fontFamily: 'Audiowide'` to standard UI text (stats, labels, menus).
- Apply `fontFamily: 'RaiderCrusader'` to prominent titles and large display text (main menu, victory/defeat, pack labels).
- Consistently update text styles in CardObject, CardTooltip, HealthBar, and all scene files.
This commit is contained in:
Brian Fertig 2026-03-15 13:57:59 -06:00
parent 4c0e7eb77c
commit c4e967c72c
16 changed files with 199 additions and 167 deletions

Binary file not shown.

Binary file not shown.

View File

@ -1,17 +1,35 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tyrant's Edge</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: #1a1a2e; display: flex; justify-content: center; align-items: center; height: 100vh; overflow: hidden; }
canvas { display: block; }
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #1a1a2e;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
overflow: hidden;
}
canvas {
display: block;
}
</style>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/phaser@3.60.0/dist/phaser.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/phaser@v3.90.0/dist/phaser.min.js"></script>
<script type="module" src="src/game.js"></script>
</body>
</html>
</html>

View File

@ -60,7 +60,7 @@ export class CardObject extends Phaser.GameObjects.Container {
this.atkText = this.scene.add.text(
-w / 2 + Math.round(5 * scale), topBannerCY,
`${this.cardData.currentAttack}`,
{ fontSize: fs(9), color: '#ff8877', fontStyle: 'bold' }
{ fontSize: fs(9), color: '#ff8877', fontStyle: 'bold', fontFamily: 'Audiowide' }
).setOrigin(0, 0.5);
this.add(this.atkText);
@ -68,7 +68,7 @@ export class CardObject extends Phaser.GameObjects.Container {
this.atkLabel = this.scene.add.text(
this.atkText.x + this.atkText.width + Math.round(2 * scale), topBannerCY,
'ATK',
{ fontSize: fs(5.5), color: '#886666' }
{ fontSize: fs(5.5), color: '#886666', fontFamily: 'Audiowide' }
).setOrigin(0, 0.5);
this.add(this.atkLabel);
@ -76,7 +76,7 @@ export class CardObject extends Phaser.GameObjects.Container {
this.armText = this.scene.add.text(
w / 2 - Math.round(5 * scale), topBannerCY,
`${this.cardData.currentArmor}`,
{ fontSize: fs(9), color: '#88aaff', fontStyle: 'bold' }
{ fontSize: fs(9), color: '#88aaff', fontStyle: 'bold', fontFamily: 'Audiowide' }
).setOrigin(1, 0.5);
this.add(this.armText);
@ -84,7 +84,7 @@ export class CardObject extends Phaser.GameObjects.Container {
this.armLabel = this.scene.add.text(
this.armText.x - this.armText.width - Math.round(2 * scale), topBannerCY,
'ARM',
{ fontSize: fs(5.5), color: '#667799' }
{ fontSize: fs(5.5), color: '#667799', fontFamily: 'Audiowide' }
).setOrigin(1, 0.5);
this.add(this.armLabel);
@ -119,7 +119,7 @@ export class CardObject extends Phaser.GameObjects.Container {
this.nameText = this.scene.add.text(0, nameY, this.cardData.name, {
fontSize: fs(8), color: '#ffffff',
wordWrap: { width: w - Math.round(8 * scale) }, align: 'center'
wordWrap: { width: w - Math.round(8 * scale) }, align: 'center', fontFamily: 'Audiowide'
}).setOrigin(0.5, 0.5);
this.add(this.nameText);
@ -133,7 +133,7 @@ export class CardObject extends Phaser.GameObjects.Container {
const skillY = contentTop + contentH * 0.72;
this.skillText = this.scene.add.text(0, skillY, skillStr, {
fontSize: fs(7), color: '#ffcc44',
wordWrap: { width: w - Math.round(8 * scale) }, align: 'center'
wordWrap: { width: w - Math.round(8 * scale) }, align: 'center', fontFamily: 'Audiowide'
}).setOrigin(0.5, 0.5);
this.add(this.skillText);
}
@ -146,7 +146,7 @@ export class CardObject extends Phaser.GameObjects.Container {
this.hpText = this.scene.add.text(
-w / 2 + Math.round(5 * scale), bottomBannerCY,
`${Math.max(0, this.cardData.currentHP)}`,
{ fontSize: fs(9), color: '#44ee88', fontStyle: 'bold' }
{ fontSize: fs(9), color: '#44ee88', fontStyle: 'bold', fontFamily: 'Audiowide' }
).setOrigin(0, 0.5);
this.add(this.hpText);
@ -154,7 +154,7 @@ export class CardObject extends Phaser.GameObjects.Container {
this.hpLabel = this.scene.add.text(
this.hpText.x + this.hpText.width + Math.round(2 * scale), bottomBannerCY,
'HP',
{ fontSize: fs(5.5), color: '#337755' }
{ fontSize: fs(5.5), color: '#337755', fontFamily: 'Audiowide' }
).setOrigin(0, 0.5);
this.add(this.hpLabel);
@ -162,7 +162,7 @@ export class CardObject extends Phaser.GameObjects.Container {
this.dlyText = this.scene.add.text(
w / 2 - Math.round(5 * scale), bottomBannerCY,
`${this.cardData.currentDelay}`,
{ fontSize: fs(9), color: '#aaaaaa', fontStyle: 'bold' }
{ fontSize: fs(9), color: '#aaaaaa', fontStyle: 'bold', fontFamily: 'Audiowide' }
).setOrigin(1, 0.5);
this.add(this.dlyText);
@ -170,7 +170,7 @@ export class CardObject extends Phaser.GameObjects.Container {
const dlyLabel = this.scene.add.text(
w / 2 - Math.round(19 * scale), bottomBannerCY,
'DLY',
{ fontSize: fs(5.5), color: '#777777' }
{ fontSize: fs(5.5), color: '#777777', fontFamily: 'Audiowide' }
).setOrigin(1, 0.5);
this.add(dlyLabel);
@ -183,7 +183,8 @@ export class CardObject extends Phaser.GameObjects.Container {
{
fontSize: fs(6), color: '#ffffff', fontStyle: 'bold',
backgroundColor: '#000000aa',
padding: { x: Math.round(2 * scale), y: Math.round(1 * scale) }
padding: { x: Math.round(2 * scale), y: Math.round(1 * scale) },
fontFamily: 'Audiowide'
}
).setOrigin(0, 0);
this.add(lvBadge);
@ -251,7 +252,8 @@ export class CardObject extends Phaser.GameObjects.Container {
color: '#4488ff',
fontStyle: 'bold',
stroke: '#000000',
strokeThickness: Math.max(1, Math.round(2 * s))
strokeThickness: Math.max(1, Math.round(2 * s)),
fontFamily: 'Audiowide'
}
).setOrigin(1, 0.5).setDepth(50);
this.add(lossText);
@ -307,7 +309,8 @@ export class CardObject extends Phaser.GameObjects.Container {
color: '#88aaff',
fontStyle: 'bold',
stroke: '#000000',
strokeThickness: Math.max(1, Math.round(2 * s))
strokeThickness: Math.max(1, Math.round(2 * s)),
fontFamily: 'Audiowide'
}
).setOrigin(1, 0.5).setDepth(50);
this.add(bonusText);
@ -365,7 +368,8 @@ export class CardObject extends Phaser.GameObjects.Container {
color: '#ffaa00',
fontStyle: 'bold',
stroke: '#000000',
strokeThickness: Math.max(1, Math.round(2 * s))
strokeThickness: Math.max(1, Math.round(2 * s)),
fontFamily: 'Audiowide'
}
).setOrigin(0, 0.5).setDepth(50);
this.add(bonusText);
@ -430,7 +434,8 @@ export class CardObject extends Phaser.GameObjects.Container {
color: '#ff4444',
fontStyle: 'bold',
stroke: '#000000',
strokeThickness: Math.max(1, Math.round(2 * s))
strokeThickness: Math.max(1, Math.round(2 * s)),
fontFamily: 'Audiowide'
}
).setOrigin(0, 0.5).setDepth(50);
this.add(lossText);

View File

@ -43,7 +43,7 @@ export class CardTooltip extends Phaser.GameObjects.Container {
// Card name
lines.push({
text: this.cardData.name,
style: { fontSize: '28px', color: '#ffffff', fontStyle: 'bold', wordWrap: { width: W - PAD * 2 }, align: 'center' },
style: { fontSize: '28px', color: '#ffffff', fontStyle: 'bold', wordWrap: { width: W - PAD * 2 }, align: 'center', fontFamily: 'Audiowide' },
marginTop: 0, align: 'center'
});
@ -51,7 +51,7 @@ export class CardTooltip extends Phaser.GameObjects.Container {
const meta = `${this._cap(this.cardData.type)} · ${this._cap(this.cardData.faction)} · ${this._cap(this.cardData.rarity)}`;
lines.push({
text: meta,
style: { fontSize: '16px', color: '#999999', wordWrap: { width: W - PAD * 2 }, align: 'center' },
style: { fontSize: '16px', color: '#999999', wordWrap: { width: W - PAD * 2 }, align: 'center', fontFamily: 'Audiowide' },
marginTop: 6, align: 'center'
});
@ -62,7 +62,7 @@ export class CardTooltip extends Phaser.GameObjects.Container {
const stats = `ATK ${this.cardData.attack} HP ${this.cardData.health} ARM ${this.cardData.armor} DLY ${this.cardData.delay}`;
lines.push({
text: stats,
style: { fontSize: '20px', color: '#cccccc', fontStyle: 'bold', wordWrap: { width: W - PAD * 2 }, align: 'center' },
style: { fontSize: '20px', color: '#cccccc', fontStyle: 'bold', wordWrap: { width: W - PAD * 2 }, align: 'center', fontFamily: 'Audiowide' },
marginTop: 12, align: 'center'
});
@ -80,9 +80,9 @@ export class CardTooltip extends Phaser.GameObjects.Container {
const valStr = skill.value != null ? ` ${skill.value}` : '';
lines.push({
text: `${skill.name}${allMod}${valStr}`,
style: { fontSize: '21px', color: catColor, fontStyle: 'bold', wordWrap: { width: W - PAD * 2 } },
style: { fontSize: '21px', color: catColor, fontStyle: 'bold', wordWrap: { width: W - PAD * 2 }, fontFamily: 'Audiowide' },
marginTop: 14, align: 'left',
suffix: { text: ` ${triggerLabel}`, style: { fontSize: '15px', color: '#888888' } }
suffix: { text: ` ${triggerLabel}`, style: { fontSize: '15px', color: '#888888', fontFamily: 'Audiowide' } }
});
// Skill description — use allDescription when "all" modifier is present
@ -94,7 +94,7 @@ export class CardTooltip extends Phaser.GameObjects.Container {
: rawDesc;
lines.push({
text: desc,
style: { fontSize: '17px', color: '#bbbbbb', wordWrap: { width: W - PAD * 2 } },
style: { fontSize: '17px', color: '#bbbbbb', wordWrap: { width: W - PAD * 2 }, fontFamily: 'Audiowide' },
marginTop: 4, align: 'left'
});
}
@ -106,7 +106,7 @@ export class CardTooltip extends Phaser.GameObjects.Container {
lines.push({ divider: true, marginTop: 14 });
lines.push({
text: `"${this.cardData.flavorText}"`,
style: { fontSize: '16px', color: '#777777', fontStyle: 'italic', wordWrap: { width: W - PAD * 2 }, align: 'center' },
style: { fontSize: '16px', color: '#777777', fontStyle: 'italic', wordWrap: { width: W - PAD * 2 }, align: 'center', fontFamily: 'Audiowide' },
marginTop: 10, align: 'center'
});
}

View File

@ -9,7 +9,7 @@ export class HealthBar {
this.bg = scene.add.rectangle(x, y, width, height, 0x333333).setOrigin(0, 0.5);
this.bar = scene.add.rectangle(x, y, width, height, 0x22cc44).setOrigin(0, 0.5);
this.text = scene.add.text(x + width / 2, y, `${maxHP}/${maxHP}`, {
fontSize: '10px', color: '#ffffff'
fontSize: '10px', color: '#ffffff', fontFamily: 'Audiowide'
}).setOrigin(0.5, 0.5);
}

View File

@ -129,8 +129,8 @@ export class BattleScene extends Phaser.Scene {
this.add.rectangle(280, height / 2, 1, height, 0x222244);
// Side labels
this.add.text(14, 95, 'OPPONENT', { fontSize: '17px', color: '#ff8888' });
this.add.text(14, 520, 'PLAYER', { fontSize: '17px', color: '#88aaff' });
this.add.text(14, 95, 'OPPONENT', { fontSize: '17px', color: '#ff8888', fontFamily: 'Audiowide' });
this.add.text(14, 520, 'PLAYER', { fontSize: '17px', color: '#88aaff', fontFamily: 'Audiowide' });
this.battlefield = new BattleField(this, {
playerY: 715, opponentY: 305,
@ -161,7 +161,7 @@ export class BattleScene extends Phaser.Scene {
if (!this.playerDeckData) {
this.add.text(width / 2, height / 2, 'No deck configured!\nGo to Deck Builder first.', {
fontSize: '20px', color: '#ff4444', align: 'center'
fontSize: '20px', color: '#ff4444', align: 'center', fontFamily: 'Audiowide'
}).setOrigin(0.5);
this._makeBackButton();
return;
@ -175,23 +175,23 @@ export class BattleScene extends Phaser.Scene {
// ── Top bar ──────────────────────────────────────────────────────────────
this.turnText = this.add.text(960, 30, 'Turn 0', {
fontSize: '20px', color: '#ffffff'
fontSize: '20px', color: '#ffffff', fontFamily: 'Audiowide'
}).setOrigin(0.5);
const battleLabel = this.missionData ? this.missionData.name : 'Skirmish Battle';
this.add.text(960, 58, battleLabel, {
fontSize: '14px', color: '#888888'
fontSize: '14px', color: '#888888', fontFamily: 'Audiowide'
}).setOrigin(0.5);
// ── Combat log — bottom strip ────────────────────────────────────────────
this.statusText = this.add.text(width / 2, 1058, 'Press SPACE or click Next Turn to start', {
fontSize: '17px', color: '#aaaaaa'
fontSize: '17px', color: '#aaaaaa', fontFamily: 'Audiowide'
}).setOrigin(0.5);
this.logLines = [];
for (let i = 0; i < 6; i++) {
this.logLines.push(this.add.text(14, 915 + i * 20, '', {
fontSize: '15px', color: '#777777'
fontSize: '15px', color: '#777777', fontFamily: 'Audiowide'
}));
}
@ -207,7 +207,7 @@ export class BattleScene extends Phaser.Scene {
.setInteractive({ useHandCursor: true })
.setStrokeStyle(1, 0x44aa44);
this.autoBtnText = this.add.text(1680, 35, 'Auto: OFF', {
fontSize: '18px', color: '#ffffff'
fontSize: '18px', color: '#ffffff', fontFamily: 'Audiowide'
}).setOrigin(0.5);
autoBtn.on('pointerdown', () => this._toggleAuto());
@ -215,7 +215,7 @@ export class BattleScene extends Phaser.Scene {
.setInteractive({ useHandCursor: true })
.setStrokeStyle(1, 0x4488ff);
this.add.text(1840, 35, 'Next Turn', {
fontSize: '18px', color: '#ffffff'
fontSize: '18px', color: '#ffffff', fontFamily: 'Audiowide'
}).setOrigin(0.5);
nextBtn.on('pointerdown', () => this._beginTurn());
@ -240,7 +240,7 @@ export class BattleScene extends Phaser.Scene {
// Label above the card (separate text so it never scales/shakes with the card)
this.add.text(s.cx, s.cy - h / 2 - 14, s.label,
{ fontSize: '15px', color: s.labelColor }).setOrigin(0.5);
{ fontSize: '15px', color: s.labelColor, fontFamily: 'Audiowide' }).setOrigin(0.5);
// Use CardObject for visual consistency with lane cards
const cardObj = new CardObject(this, s.cx, s.cy, s.data, { width: w, height: h });
@ -249,8 +249,8 @@ export class BattleScene extends Phaser.Scene {
this.commanderObjects.set(s.data.instanceId, cardObj);
}
this.oDeckText = this.add.text(14, 115, '', { fontSize: '15px', color: '#aaaaaa' });
this.pDeckText = this.add.text(14, 540, '', { fontSize: '15px', color: '#aaaaaa' });
this.oDeckText = this.add.text(14, 115, '', { fontSize: '15px', color: '#aaaaaa', fontFamily: 'Audiowide' });
this.pDeckText = this.add.text(14, 540, '', { fontSize: '15px', color: '#aaaaaa', fontFamily: 'Audiowide' });
}
_beginTurn() {
@ -1481,7 +1481,7 @@ export class BattleScene extends Phaser.Scene {
const dmgText = this.add.text(
dmgAnchorX, dmgAnchorY - 100,
`-${event.damage}`,
{ fontSize: '56px', color: '#ff3333', stroke: '#000000', strokeThickness: 5 }
{ fontSize: '56px', color: '#ff3333', stroke: '#000000', strokeThickness: 5, fontFamily: 'RaiderCrusader' }
).setOrigin(0.5).setDepth(30);
this.tweens.add({
@ -2463,7 +2463,8 @@ export class BattleScene extends Phaser.Scene {
color: '#ff8877',
fontStyle: 'bold',
stroke: '#000000',
strokeThickness: Math.max(1, Math.round(2 * s))
strokeThickness: Math.max(1, Math.round(2 * s)),
fontFamily: 'Audiowide'
}
).setOrigin(1, 0.5).setDepth(50);
@ -2664,7 +2665,8 @@ export class BattleScene extends Phaser.Scene {
fontStyle: 'bold',
stroke: '#000000',
strokeThickness: 3,
align: 'center'
align: 'center',
fontFamily: 'Audiowide'
}).setOrigin(0.5, 0.5).setDepth(55);
this.tweens.add({
@ -2750,7 +2752,7 @@ export class BattleScene extends Phaser.Scene {
this.pickerObjects.push(panel);
const title = this.add.text(width / 2, panelY - panelH / 2 + 26, 'Choose a card to deploy', {
fontSize: '22px', color: '#d4af37'
fontSize: '22px', color: '#d4af37', fontFamily: 'Audiowide'
}).setOrigin(0.5).setDepth(11);
this.pickerObjects.push(title);
@ -2792,7 +2794,7 @@ export class BattleScene extends Phaser.Scene {
.setInteractive({ useHandCursor: true })
.setDepth(11);
const passTxt = this.add.text(width / 2, panelY + panelH / 2 - 28, 'Pass (deploy nothing)', {
fontSize: '17px', color: '#aaaaaa'
fontSize: '17px', color: '#aaaaaa', fontFamily: 'Audiowide'
}).setOrigin(0.5).setDepth(12);
passBtn.on('pointerdown', () => {
this._destroyCardPicker();
@ -2908,11 +2910,11 @@ export class BattleScene extends Phaser.Scene {
this.add.text(width / 2, height / 2 - 120, won ? 'VICTORY!' : 'DEFEAT', {
fontSize: '72px', color: won ? '#44ff44' : '#ff4444',
stroke: '#000000', strokeThickness: 6
stroke: '#000000', strokeThickness: 6, fontFamily: 'RaiderCrusader'
}).setOrigin(0.5);
this.add.text(width / 2, height / 2 - 40, `Battle lasted ${this.engine.turn} turns`, {
fontSize: '24px', color: '#aaaaaa'
fontSize: '24px', color: '#aaaaaa', fontFamily: 'Audiowide'
}).setOrigin(0.5);
if (won && this.missionData) {
@ -2938,7 +2940,7 @@ export class BattleScene extends Phaser.Scene {
this.registry.set('save', save);
this.add.text(width / 2, height / 2 + 20, `+${rewards.gold} Gold`, {
fontSize: '36px', color: '#ffd700'
fontSize: '36px', color: '#ffd700', fontFamily: 'RaiderCrusader'
}).setOrigin(0.5);
if (rewards.cards && rewards.cards.length > 0) {
@ -2948,7 +2950,7 @@ export class BattleScene extends Phaser.Scene {
return c ? c.name : id;
}).join(', ');
this.add.text(width / 2, height / 2 + 70, `New card(s): ${cardNames}`, {
fontSize: '22px', color: '#aaffaa'
fontSize: '22px', color: '#aaffaa', fontFamily: 'Audiowide'
}).setOrigin(0.5);
}
@ -2957,12 +2959,12 @@ export class BattleScene extends Phaser.Scene {
const cmdCard = cardManager.getCard(unlockedCommander);
const cmdName = cmdCard ? cmdCard.name : unlockedCommander;
this.add.text(width / 2, height / 2 + 110, `Commander unlocked: ${cmdName}!`, {
fontSize: '22px', color: '#ffdd44'
fontSize: '22px', color: '#ffdd44', fontFamily: 'Audiowide'
}).setOrigin(0.5);
}
} else if (!won) {
this.add.text(width / 2, height / 2 + 20, 'Better luck next time!', {
fontSize: '24px', color: '#aaaaaa'
fontSize: '24px', color: '#aaaaaa', fontFamily: 'Audiowide'
}).setOrigin(0.5);
}
@ -2970,7 +2972,7 @@ export class BattleScene extends Phaser.Scene {
.setInteractive({ useHandCursor: true })
.setStrokeStyle(2, 0x4488ff);
this.add.text(width / 2, height / 2 + 160, 'Continue', {
fontSize: '28px', color: '#ffffff'
fontSize: '28px', color: '#ffffff', fontFamily: 'Audiowide'
}).setOrigin(0.5);
backBtn.on('pointerdown', () => {
if (this.missionData) {
@ -3011,7 +3013,7 @@ export class BattleScene extends Phaser.Scene {
const bg = this.add.rectangle(80, 35, 180, 44, 0x333333)
.setInteractive({ useHandCursor: true })
.setStrokeStyle(1, 0x888888);
this.add.text(80, 35, '← Back', { fontSize: '18px', color: '#ffffff' }).setOrigin(0.5);
this.add.text(80, 35, '← Back', { fontSize: '18px', color: '#ffffff', fontFamily: 'Audiowide' }).setOrigin(0.5);
bg.on('pointerdown', () => {
if (this.autoTimer) this.autoTimer.remove();
this.scene.start('MainMenuScene');

View File

@ -7,6 +7,10 @@ export class BootScene extends Phaser.Scene {
constructor() { super('BootScene'); }
preload() {
// Load custom fonts
this.load.font('RaiderCrusader', 'assets/fonts/raidercrusader.ttf');
this.load.font('Audiowide', 'assets/fonts/Audiowide-Regular.ttf');
// Load all JSON data files
this.load.image('vs', 'assets/images/ui/vs.png');
this.load.image('attacksFirst', 'assets/images/ui/attacksFirst.png');
@ -51,7 +55,7 @@ export class BootScene extends Phaser.Scene {
// Loading text
const loadText = this.add.text(this.scale.width / 2, this.scale.height / 2, 'Loading...', {
fontSize: '28px', color: '#ffffff'
fontSize: '28px', color: '#ffffff', fontFamily: 'Audiowide'
}).setOrigin(0.5);
this.load.on('progress', (value) => {

View File

@ -18,12 +18,12 @@ export class CampaignScene extends Phaser.Scene {
this.add.rectangle(width / 2, height / 2, width, height, 0x0d1b2a);
this.add.text(width / 2, 45, campaign ? campaign.name : 'Campaign', {
fontSize: '42px', color: '#d4af37'
fontSize: '42px', color: '#d4af37', fontFamily: 'RaiderCrusader'
}).setOrigin(0.5);
if (campaign) {
this.add.text(width / 2, 90, `Level ${campaign.level}${campaign.faction.toUpperCase()}`, {
fontSize: '18px', color: '#888888'
fontSize: '18px', color: '#888888', fontFamily: 'Audiowide'
}).setOrigin(0.5);
}
@ -56,32 +56,32 @@ export class CampaignScene extends Phaser.Scene {
this.add.text(x, y - 70, mission.name, {
fontSize: '20px', color: isUnlocked ? '#ffffff' : '#666666',
wordWrap: { width: 270 }, align: 'center'
wordWrap: { width: 270 }, align: 'center', fontFamily: 'Audiowide'
}).setOrigin(0.5);
this.add.text(x, y - 35, `Mission ${i + 1}`, {
fontSize: '16px', color: '#888888'
fontSize: '16px', color: '#888888', fontFamily: 'Audiowide'
}).setOrigin(0.5);
this.add.text(x, y, isDone ? 'Complete' : isUnlocked ? 'Click to battle' : 'Locked', {
fontSize: '16px', color: isDone ? '#44ff44' : isUnlocked ? '#aaffaa' : '#666666'
fontSize: '16px', color: isDone ? '#44ff44' : isUnlocked ? '#aaffaa' : '#666666', fontFamily: 'Audiowide'
}).setOrigin(0.5);
// Rewards
this.add.text(x, y + 30, `+${mission.rewards.gold} gold`, {
fontSize: '14px', color: '#ffd700'
fontSize: '14px', color: '#ffd700', fontFamily: 'Audiowide'
}).setOrigin(0.5);
if (mission.rewards.cards && mission.rewards.cards.length > 0) {
this.add.text(x, y + 55, `Card reward available`, {
fontSize: '14px', color: '#aaffaa'
fontSize: '14px', color: '#aaffaa', fontFamily: 'Audiowide'
}).setOrigin(0.5);
}
});
// Mission lore panel
this.loreText = this.add.text(width / 2, height - 70, 'Hover over a mission to learn more.', {
fontSize: '18px', color: '#888888', align: 'center', wordWrap: { width: width - 100 }
fontSize: '18px', color: '#888888', align: 'center', wordWrap: { width: width - 100 }, fontFamily: 'Audiowide'
}).setOrigin(0.5);
this._makeBackButton();
@ -91,7 +91,7 @@ export class CampaignScene extends Phaser.Scene {
const save = this.registry.get('save');
if (!save.decks || save.decks.length === 0) {
this.add.text(960, 940, 'No deck! Create a deck in Deck Builder first.', {
fontSize: '22px', color: '#ff4444'
fontSize: '22px', color: '#ff4444', fontFamily: 'Audiowide'
}).setOrigin(0.5);
return;
}
@ -133,7 +133,7 @@ export class CampaignScene extends Phaser.Scene {
const bg = this.add.rectangle(80, 45, 160, 50, 0x333333)
.setInteractive({ useHandCursor: true })
.setStrokeStyle(1, 0x888888);
this.add.text(80, 45, 'Back', { fontSize: '18px', color: '#ffffff' }).setOrigin(0.5);
this.add.text(80, 45, 'Back', { fontSize: '18px', color: '#ffffff', fontFamily: 'Audiowide' }).setOrigin(0.5);
bg.on('pointerdown', () => {
this.sound.play('sfx_menu_select', { volume: 0.7 });
this.scene.start('CampaignSelectScene');

View File

@ -13,11 +13,11 @@ export class CampaignSelectScene extends Phaser.Scene {
this.add.rectangle(width / 2, height / 2, width, height, 0x0d1b2a);
this.add.text(width / 2, 45, 'Select Campaign', {
fontSize: '42px', color: '#d4af37'
fontSize: '42px', color: '#d4af37', fontFamily: 'RaiderCrusader'
}).setOrigin(0.5);
this.add.text(width / 2, 95, `Player Level: ${save.level || 1}`, {
fontSize: '20px', color: '#88aaff'
fontSize: '20px', color: '#88aaff', fontFamily: 'Audiowide'
}).setOrigin(0.5);
const FACTION_COLORS = {
@ -50,25 +50,25 @@ export class CampaignSelectScene extends Phaser.Scene {
const nameColor = isUnlocked ? '#ffffff' : '#666666';
this.add.text(x, y - 150, campaign.name, {
fontSize: '22px', color: nameColor, fontStyle: 'bold',
wordWrap: { width: 270 }, align: 'center'
wordWrap: { width: 270 }, align: 'center', fontFamily: 'Audiowide'
}).setOrigin(0.5);
// Faction label
const fColor = FACTION_COLORS[campaign.faction] || '#888888';
this.add.text(x, y - 115, campaign.faction.toUpperCase(), {
fontSize: '14px', color: fColor
fontSize: '14px', color: fColor, fontFamily: 'Audiowide'
}).setOrigin(0.5);
// Level requirement
this.add.text(x, y - 85, `Level ${campaign.level}`, {
fontSize: '16px', color: '#aaaaaa'
fontSize: '16px', color: '#aaaaaa', fontFamily: 'Audiowide'
}).setOrigin(0.5);
// Progress or lock status
if (isUnlocked) {
if (campaign.missions.length === 0) {
this.add.text(x, y, 'Coming Soon', {
fontSize: '18px', color: '#888844'
fontSize: '18px', color: '#888844', fontFamily: 'Audiowide'
}).setOrigin(0.5);
} else {
const progressStr = isComplete
@ -76,7 +76,7 @@ export class CampaignSelectScene extends Phaser.Scene {
: `${completedMissions.length} / ${campaign.missions.length} missions`;
const progressColor = isComplete ? '#44ff44' : '#aaffaa';
this.add.text(x, y - 20, progressStr, {
fontSize: '16px', color: progressColor
fontSize: '16px', color: progressColor, fontFamily: 'Audiowide'
}).setOrigin(0.5);
// Click to play
@ -89,23 +89,23 @@ export class CampaignSelectScene extends Phaser.Scene {
});
this.add.text(x, y + 30, 'Click to play', {
fontSize: '14px', color: '#88aaff'
fontSize: '14px', color: '#88aaff', fontFamily: 'Audiowide'
}).setOrigin(0.5);
}
// Starter reward info
if (campaign.starterReward?.cards?.length > 0 && !isComplete) {
this.add.text(x, y + 80, 'Completion Reward:', {
fontSize: '13px', color: '#ffd700'
fontSize: '13px', color: '#ffd700', fontFamily: 'Audiowide'
}).setOrigin(0.5);
this.add.text(x, y + 100, `${campaign.starterReward.cards.length} cards`, {
fontSize: '13px', color: '#ffeeaa'
fontSize: '13px', color: '#ffeeaa', fontFamily: 'Audiowide'
}).setOrigin(0.5);
}
} else {
// Locked
this.add.text(x, y - 20, 'LOCKED', {
fontSize: '24px', color: '#555555', fontStyle: 'bold'
fontSize: '24px', color: '#555555', fontStyle: 'bold', fontFamily: 'Audiowide'
}).setOrigin(0.5);
// Unlock requirement
@ -114,7 +114,7 @@ export class CampaignSelectScene extends Phaser.Scene {
: `Requires Level ${campaign.requiredLevel}`;
this.add.text(x, y + 20, reqText, {
fontSize: '13px', color: '#666666',
wordWrap: { width: 260 }, align: 'center'
wordWrap: { width: 260 }, align: 'center', fontFamily: 'Audiowide'
}).setOrigin(0.5);
}
});
@ -126,7 +126,7 @@ export class CampaignSelectScene extends Phaser.Scene {
const bg = this.add.rectangle(80, 45, 160, 50, 0x333333)
.setInteractive({ useHandCursor: true })
.setStrokeStyle(1, 0x888888);
this.add.text(80, 45, 'Back', { fontSize: '18px', color: '#ffffff' }).setOrigin(0.5);
this.add.text(80, 45, 'Back', { fontSize: '18px', color: '#ffffff', fontFamily: 'Audiowide' }).setOrigin(0.5);
bg.on('pointerdown', () => {
this.sound.play('sfx_menu_select', { volume: 0.7 });
this.scene.start('MainMenuScene');

View File

@ -110,14 +110,14 @@ export class CollectionScene extends Phaser.Scene {
// Title
this.add.text(30, 22, 'COLLECTION', {
fontSize: '32px', color: '#d4af37', fontStyle: 'bold'
fontSize: '32px', color: '#d4af37', fontStyle: 'bold', fontFamily: 'Audiowide'
}).setOrigin(0, 0);
// Owned count
const totalCards = this.cardManager.getAllCards().length;
const ownedCards = Object.keys(this.save.collection).length;
this.ownedText = this.add.text(320, 30, `Owned: ${ownedCards}/${totalCards}`, {
fontSize: '18px', color: '#888888'
fontSize: '18px', color: '#888888', fontFamily: 'Audiowide'
}).setOrigin(0, 0);
// Faction filters
@ -157,7 +157,7 @@ export class CollectionScene extends Phaser.Scene {
const padX = 14;
const h = 24;
const displayLabel = label.charAt(0).toUpperCase() + label.slice(1);
const tmpText = this.add.text(0, 0, displayLabel, { fontSize: '14px' });
const tmpText = this.add.text(0, 0, displayLabel, { fontSize: '14px', fontFamily: 'Audiowide' });
const textW = tmpText.width;
tmpText.destroy();
const w = textW + padX * 2;
@ -166,7 +166,7 @@ export class CollectionScene extends Phaser.Scene {
.setInteractive({ useHandCursor: true })
.setStrokeStyle(1, 0x666688);
const text = this.add.text(x + w / 2, y + h / 2, displayLabel, {
fontSize: '14px', color: '#ffffff'
fontSize: '14px', color: '#ffffff', fontFamily: 'Audiowide'
}).setOrigin(0.5);
bg.on('pointerdown', onClick);
@ -276,7 +276,7 @@ export class CollectionScene extends Phaser.Scene {
const emptyText = this.add.text(
GRID_PAD_X + GRID_W / 2, GRID_PAD_Y + GRID_H / 2,
'No cards match the current filter.',
{ fontSize: '22px', color: '#666666' }
{ fontSize: '22px', color: '#666666', fontFamily: 'Audiowide' }
).setOrigin(0.5);
this.gridContainer.add(emptyText);
this._gridCards.push(emptyText);
@ -404,11 +404,11 @@ export class CollectionScene extends Phaser.Scene {
.setStrokeStyle(1, 0x222244);
this._showcasePlaceholder = this.add.text(cx, cy, 'SELECT A CARD', {
fontSize: '24px', color: '#333355', fontStyle: 'bold'
fontSize: '24px', color: '#333355', fontStyle: 'bold', fontFamily: 'Audiowide'
}).setOrigin(0.5);
this._showcaseNotCollected = this.add.text(cx, cy + 190, 'NOT COLLECTED', {
fontSize: '16px', color: '#aa3333', fontStyle: 'bold'
fontSize: '16px', color: '#aa3333', fontStyle: 'bold', fontFamily: 'Audiowide'
}).setOrigin(0.5).setVisible(false);
}
@ -742,7 +742,7 @@ export class CollectionScene extends Phaser.Scene {
const bg = this.add.rectangle(80, 1040, 140, 40, 0x333333)
.setInteractive({ useHandCursor: true })
.setStrokeStyle(1, 0x888888);
this.add.text(80, 1040, 'Back', { fontSize: '18px', color: '#ffffff' }).setOrigin(0.5);
this.add.text(80, 1040, 'Back', { fontSize: '18px', color: '#ffffff', fontFamily: 'Audiowide' }).setOrigin(0.5);
bg.on('pointerdown', () => this.scene.start('MainMenuScene'));
bg.on('pointerover', () => bg.setStrokeStyle(2, 0xaaaaaa));
bg.on('pointerout', () => bg.setStrokeStyle(1, 0x888888));

View File

@ -36,7 +36,7 @@ export class DeckBuilderScene extends Phaser.Scene {
scanlines.fillRect(0, y, width, 1);
}
this.add.text(width / 2, 36, 'Deck Builder', { fontSize: '38px', color: '#d4af37' }).setOrigin(0.5);
this.add.text(width / 2, 36, 'Deck Builder', { fontSize: '38px', color: '#d4af37', fontFamily: 'RaiderCrusader' }).setOrigin(0.5);
const save = this.registry.get('save');
this.cardManager = this.registry.get('cardManager');
@ -118,7 +118,7 @@ export class DeckBuilderScene extends Phaser.Scene {
// ── Commander section ──
if (commanders.length > 0) {
const cmdLabel = this.add.text(this.GRID_LEFT, cursorY, 'Commanders', {
fontSize: '18px', color: '#d4af37'
fontSize: '18px', color: '#d4af37', fontFamily: 'Audiowide'
});
this.scrollContainer.add(cmdLabel);
@ -139,7 +139,7 @@ export class DeckBuilderScene extends Phaser.Scene {
// ── Assault section ──
if (assaults.length > 0) {
const assLabel = this.add.text(this.GRID_LEFT, cursorY, 'Assault Cards', {
fontSize: '18px', color: '#d4af37'
fontSize: '18px', color: '#d4af37', fontFamily: 'Audiowide'
});
this.scrollContainer.add(assLabel);
@ -160,7 +160,7 @@ export class DeckBuilderScene extends Phaser.Scene {
// Show empty message if all filtered out
if (commanders.length === 0 && assaults.length === 0) {
const emptyTxt = this.add.text(500, 400, 'No cards match the current filters.', {
fontSize: '20px', color: '#666666'
fontSize: '20px', color: '#666666', fontFamily: 'Audiowide'
}).setOrigin(0.5);
this.scrollContainer.add(emptyTxt);
}
@ -194,7 +194,7 @@ export class DeckBuilderScene extends Phaser.Scene {
// Owned/used count
const countY = cy + this.CARD_H / 2 + 4;
const countText = this.add.text(cx, countY, `Owned: ${owned} | In Deck: ${used}`, {
fontSize: '11px', color: canAdd ? '#88cc88' : '#555555'
fontSize: '11px', color: canAdd ? '#88cc88' : '#555555', fontFamily: 'Audiowide'
}).setOrigin(0.5, 0);
this.scrollContainer.add(countText);
@ -204,7 +204,7 @@ export class DeckBuilderScene extends Phaser.Scene {
const btnBg = this.add.rectangle(cx, btnY, this.CARD_W - 10, this.BTN_H, canAdd ? 0x225522 : 0x222222)
.setStrokeStyle(1, canAdd ? 0x44aa44 : 0x333333);
const btnTxt = this.add.text(cx, btnY, btnLabel, {
fontSize: '12px', color: canAdd ? '#44ff44' : '#444444'
fontSize: '12px', color: canAdd ? '#44ff44' : '#444444', fontFamily: 'Audiowide'
}).setOrigin(0.5);
this.scrollContainer.add(btnBg);
this.scrollContainer.add(btnTxt);
@ -255,7 +255,7 @@ export class DeckBuilderScene extends Phaser.Scene {
this.deckPanelGroup.push(panelBg);
const title = this.add.text(panelCX, 90, 'Current Deck', {
fontSize: '24px', color: '#d4af37'
fontSize: '24px', color: '#d4af37', fontFamily: 'Audiowide'
}).setOrigin(0.5);
this.deckPanelGroup.push(title);
@ -264,7 +264,7 @@ export class DeckBuilderScene extends Phaser.Scene {
const cmdBg = this.add.rectangle(panelCX, 140, PANEL_W - 40, 46, cmdCard ? 0x1a3a5c : 0x1a1a2e)
.setStrokeStyle(2, 0xffd700);
const cmdText = this.add.text(panelCX, 140, cmdCard ? `Commander: ${cmdCard.name}` : 'No Commander Selected', {
fontSize: '17px', color: cmdCard ? '#ffd700' : '#555555'
fontSize: '17px', color: cmdCard ? '#ffd700' : '#555555', fontFamily: 'Audiowide'
}).setOrigin(0.5);
this.deckPanelGroup.push(cmdBg, cmdText);
@ -272,7 +272,7 @@ export class DeckBuilderScene extends Phaser.Scene {
const rmBtn = this.add.rectangle(panelCX + PANEL_W / 2 - 40, 140, 34, 28, 0x662222)
.setInteractive({ useHandCursor: true }).setStrokeStyle(1, 0xff4444);
const rmTxt = this.add.text(panelCX + PANEL_W / 2 - 40, 140, 'X', {
fontSize: '14px', color: '#ff4444'
fontSize: '14px', color: '#ff4444', fontFamily: 'Audiowide'
}).setOrigin(0.5);
rmBtn.on('pointerdown', () => {
const oldCmd = this.workingDeck.commander;
@ -301,20 +301,20 @@ export class DeckBuilderScene extends Phaser.Scene {
const bg = this.add.rectangle(panelCX, y, PANEL_W - 40, ENTRY_H - 4, 0x1a3a5c)
.setStrokeStyle(1, rarityColors[card.rarity] || 0x336699);
const txt = this.add.text(PANEL_X + 30, y, `${card.name}`, {
fontSize: '15px', color: '#ffffff'
fontSize: '15px', color: '#ffffff', fontFamily: 'Audiowide'
}).setOrigin(0, 0.5);
const countTxt = this.add.text(PANEL_X + 300, y, `x${count}`, {
fontSize: '15px', color: '#aaaaaa'
fontSize: '15px', color: '#aaaaaa', fontFamily: 'Audiowide'
}).setOrigin(0, 0.5);
const lv1 = card.levels[0];
const statsTxt = this.add.text(PANEL_X + 360, y, `ATK:${lv1.attack} HP:${lv1.health}`, {
fontSize: '13px', color: '#8888cc'
fontSize: '13px', color: '#8888cc', fontFamily: 'Audiowide'
}).setOrigin(0, 0.5);
const rmBtn = this.add.rectangle(panelCX + PANEL_W / 2 - 40, y, 34, 28, 0x662222)
.setInteractive({ useHandCursor: true }).setStrokeStyle(1, 0xff4444);
const rmTxt = this.add.text(panelCX + PANEL_W / 2 - 40, y, '-', {
fontSize: '18px', color: '#ff4444'
fontSize: '18px', color: '#ff4444', fontFamily: 'Audiowide'
}).setOrigin(0.5);
rmBtn.on('pointerdown', () => {
const idx = this.workingDeck.cards.lastIndexOf(cardId);
@ -337,7 +337,7 @@ export class DeckBuilderScene extends Phaser.Scene {
const slotBg = this.add.rectangle(panelCX, y, PANEL_W - 40, ENTRY_H - 4, 0x0d0d1a)
.setStrokeStyle(1, 0x222244);
const slotTxt = this.add.text(panelCX, y, '— empty slot —', {
fontSize: '13px', color: '#333355'
fontSize: '13px', color: '#333355', fontFamily: 'Audiowide'
}).setOrigin(0.5);
this.deckPanelGroup.push(slotBg, slotTxt);
y += ENTRY_H;
@ -346,14 +346,14 @@ export class DeckBuilderScene extends Phaser.Scene {
// Card count
const countColor = filled > 10 ? '#ff4444' : filled >= 3 ? '#aaaaff' : '#888888';
const totalTxt = this.add.text(panelCX, y + 16, `Cards: ${filled} / 10`, {
fontSize: '18px', color: countColor
fontSize: '18px', color: countColor, fontFamily: 'Audiowide'
}).setOrigin(0.5);
this.deckPanelGroup.push(totalTxt);
// Validation
const validation = DeckManager.validate(this.workingDeck, this.cardManager);
const validTxt = this.add.text(panelCX, y + 44, validation.valid ? 'Valid Deck' : validation.errors[0], {
fontSize: '15px', color: validation.valid ? '#44ff44' : '#ff6644'
fontSize: '15px', color: validation.valid ? '#44ff44' : '#ff6644', fontFamily: 'Audiowide'
}).setOrigin(0.5);
this.deckPanelGroup.push(validTxt);
@ -365,7 +365,7 @@ export class DeckBuilderScene extends Phaser.Scene {
const saveBg = this.add.rectangle(panelCX - 110, saveBtnY, 190, 46, 0x224422)
.setInteractive({ useHandCursor: true }).setStrokeStyle(2, 0x44aa44);
const saveTxt = this.add.text(panelCX - 110, saveBtnY, 'Save Deck', {
fontSize: '18px', color: '#ffffff'
fontSize: '18px', color: '#ffffff', fontFamily: 'Audiowide'
}).setOrigin(0.5);
saveBg.on('pointerdown', () => {
const save = this.registry.get('save');
@ -379,7 +379,7 @@ export class DeckBuilderScene extends Phaser.Scene {
const clearBg = this.add.rectangle(panelCX + 110, saveBtnY, 190, 46, 0x442222)
.setInteractive({ useHandCursor: true }).setStrokeStyle(2, 0xaa4444);
const clearTxt = this.add.text(panelCX + 110, saveBtnY, 'Clear Deck', {
fontSize: '18px', color: '#ffffff'
fontSize: '18px', color: '#ffffff', fontFamily: 'Audiowide'
}).setOrigin(0.5);
clearBg.on('pointerdown', () => {
this.workingDeck.commander = null;
@ -419,7 +419,7 @@ export class DeckBuilderScene extends Phaser.Scene {
// ── Faction header ──
const fHeader = this.add.text(PANEL_X + 20, y, 'Faction', {
fontSize: '14px', color: '#d4af37', fontStyle: 'bold'
fontSize: '14px', color: '#d4af37', fontStyle: 'bold', fontFamily: 'Audiowide'
});
this.filterGroup.push(fHeader);
y += ROW_H;
@ -433,7 +433,7 @@ export class DeckBuilderScene extends Phaser.Scene {
// ── Rarity header ──
const rHeader = this.add.text(PANEL_X + 20, y, 'Rarity', {
fontSize: '14px', color: '#d4af37', fontStyle: 'bold'
fontSize: '14px', color: '#d4af37', fontStyle: 'bold', fontFamily: 'Audiowide'
});
this.filterGroup.push(rHeader);
y += ROW_H;
@ -458,14 +458,14 @@ export class DeckBuilderScene extends Phaser.Scene {
let checkmark = null;
if (active) {
checkmark = this.add.text(cx, y, '\u2713', {
fontSize: '12px', color: '#ffffff', fontStyle: 'bold'
fontSize: '12px', color: '#ffffff', fontStyle: 'bold', fontFamily: 'Audiowide'
}).setOrigin(0.5, 0.4);
this.filterGroup.push(checkmark);
}
// Label text
const txt = this.add.text(cx + checkSize / 2 + 8, y + 1, label, {
fontSize: '13px', color: active ? colorStr : '#555555'
fontSize: '13px', color: active ? colorStr : '#555555', fontFamily: 'Audiowide'
}).setOrigin(0, 0.5);
// Hit area covers the full row for easy clicking
@ -492,7 +492,7 @@ export class DeckBuilderScene extends Phaser.Scene {
const backBtn = this.add.rectangle(80, this.scale.height - 40, 140, 40, 0x333333)
.setInteractive({ useHandCursor: true }).setStrokeStyle(1, 0x888888);
this.add.text(80, this.scale.height - 40, 'Back', {
fontSize: '18px', color: '#ffffff'
fontSize: '18px', color: '#ffffff', fontFamily: 'Audiowide'
}).setOrigin(0.5);
backBtn.on('pointerdown', () => this.scene.start('MainMenuScene'));
}
@ -500,7 +500,7 @@ export class DeckBuilderScene extends Phaser.Scene {
_showMsg(msg, color) {
const { width } = this.scale;
const t = this.add.text(width / 2, this.scale.height - 100, msg, {
fontSize: '18px', color
fontSize: '18px', color, fontFamily: 'Audiowide'
}).setOrigin(0.5);
this.time.delayedCall(2000, () => { if (t.scene) t.destroy(); });
}

View File

@ -104,10 +104,10 @@ export class FusionScene extends Phaser.Scene {
_buildHeader() {
this.add.text(360, 28, 'FUSION LAB', {
fontSize: '36px', color: '#d4af37', fontStyle: 'bold'
fontSize: '36px', color: '#d4af37', fontStyle: 'bold', fontFamily: 'RaiderCrusader'
}).setOrigin(0.5);
this.add.text(360, 58, 'Combine 3 copies of a card to forge a stronger one', {
fontSize: '16px', color: '#888888'
fontSize: '16px', color: '#888888', fontFamily: 'Audiowide'
}).setOrigin(0.5);
}
@ -115,7 +115,7 @@ export class FusionScene extends Phaser.Scene {
_buildStacksArea() {
this.add.text(STACKS_LEFT + 10, STACKS_TOP - 22, 'AVAILABLE CARDS', {
fontSize: '14px', color: '#aaaaaa', fontStyle: 'bold'
fontSize: '14px', color: '#aaaaaa', fontStyle: 'bold', fontFamily: 'Audiowide'
});
this.stacksContainer = this.add.container(0, 0);
@ -148,7 +148,7 @@ export class FusionScene extends Phaser.Scene {
this._previewTitle = this.add.text(PREVIEW_LEFT + PREVIEW_W / 2, PREVIEW_TOP + 15,
'POSSIBLE RESULTS', {
fontSize: '18px', color: '#d4af37', fontStyle: 'bold'
fontSize: '18px', color: '#d4af37', fontStyle: 'bold', fontFamily: 'Audiowide'
}).setOrigin(0.5).setVisible(false);
this._previewContainer = this.add.container(0, 0);
@ -161,12 +161,12 @@ export class FusionScene extends Phaser.Scene {
.setStrokeStyle(1, 0x334466);
this.add.text(DECK_LEFT + DECK_W / 2, DECK_TOP + 20, 'YOUR DECK', {
fontSize: '20px', color: '#d4af37', fontStyle: 'bold'
fontSize: '20px', color: '#d4af37', fontStyle: 'bold', fontFamily: 'Audiowide'
}).setOrigin(0.5);
this.add.text(DECK_LEFT + DECK_W / 2, DECK_TOP + 50,
'Cards in your deck cannot be fused.\nRemove them in Deck Builder first.', {
fontSize: '13px', color: '#666666', align: 'center', lineSpacing: 4
fontSize: '13px', color: '#666666', align: 'center', lineSpacing: 4, fontFamily: 'Audiowide'
}).setOrigin(0.5, 0);
this._deckContainer = this.add.container(0, 0);
@ -184,7 +184,7 @@ export class FusionScene extends Phaser.Scene {
const save = this.save;
if (!save.decks || save.decks.length === 0) {
const t = this.add.text(DECK_LEFT + DECK_W / 2, DECK_TOP + 160, 'No deck created yet.', {
fontSize: '16px', color: '#555555'
fontSize: '16px', color: '#555555', fontFamily: 'Audiowide'
}).setOrigin(0.5);
this._deckContainer.add(t);
return;
@ -202,7 +202,7 @@ export class FusionScene extends Phaser.Scene {
const entries = Object.entries(counts);
if (entries.length === 0) {
const t = this.add.text(DECK_LEFT + DECK_W / 2, DECK_TOP + 160,
'Deck is empty.', { fontSize: '16px', color: '#555555' }).setOrigin(0.5);
'Deck is empty.', { fontSize: '16px', color: '#555555', fontFamily: 'Audiowide' }).setOrigin(0.5);
this._deckContainer.add(t);
return;
}
@ -231,7 +231,7 @@ export class FusionScene extends Phaser.Scene {
if (count > 1) {
const badge = this.add.text(x + thumbW / 2 - 2, y - thumbH / 2 + 2, `×${count}`, {
fontSize: '14px', color: '#ffffff', fontStyle: 'bold',
backgroundColor: '#000000aa', padding: { x: 3, y: 2 }
backgroundColor: '#000000aa', padding: { x: 3, y: 2 }, fontFamily: 'Audiowide'
}).setOrigin(1, 0);
this._deckContainer.add(badge);
}
@ -242,14 +242,14 @@ export class FusionScene extends Phaser.Scene {
_buildBottomBar() {
// Recipe reference
this.add.text(40, 850, 'Fusion Recipes:', { fontSize: '16px', color: '#d4af37' });
this.add.text(40, 850, 'Fusion Recipes:', { fontSize: '16px', color: '#d4af37', fontFamily: 'Audiowide' });
['3× Common → Rare', '3× Rare → Epic', '3× Epic → Legendary'].forEach((r, i) => {
this.add.text(40, 875 + i * 22, r, { fontSize: '14px', color: '#888888' });
this.add.text(40, 875 + i * 22, r, { fontSize: '14px', color: '#888888', fontFamily: 'Audiowide' });
});
// Info text (visible when a stack is selected)
this._fuseInfoText = this.add.text(1060, 855, '', {
fontSize: '20px', color: '#ffffff'
fontSize: '20px', color: '#ffffff', fontFamily: 'Audiowide'
}).setOrigin(0.5).setVisible(false);
// Fuse button
@ -258,7 +258,7 @@ export class FusionScene extends Phaser.Scene {
.setInteractive({ useHandCursor: true })
.setVisible(false);
this._fuseBtnText = this.add.text(1060, 920, 'FUSE', {
fontSize: '26px', color: '#ffd700', fontStyle: 'bold'
fontSize: '26px', color: '#ffd700', fontStyle: 'bold', fontFamily: 'Audiowide'
}).setOrigin(0.5).setVisible(false);
this._fuseBtnBg.on('pointerover', () => {
@ -333,7 +333,7 @@ export class FusionScene extends Phaser.Scene {
if (stacks.length === 0) {
const t = this.add.text(STACKS_LEFT + STACKS_W / 2, STACKS_TOP + 200,
'No cards available for fusion.\nCollect more copies or remove\ncards from your deck.', {
fontSize: '20px', color: '#666666', align: 'center', lineSpacing: 6
fontSize: '20px', color: '#666666', align: 'center', lineSpacing: 6, fontFamily: 'Audiowide'
}).setOrigin(0.5);
this.stacksContainer.add(t);
return;
@ -399,7 +399,8 @@ export class FusionScene extends Phaser.Scene {
const badgeText = this.add.text(badgeX, badgeY, badgeLabel, {
fontSize: stack.fusible ? '16px' : '13px',
color: stack.fusible ? '#44ff44' : '#ff6666',
fontStyle: 'bold'
fontStyle: 'bold',
fontFamily: 'Audiowide'
}).setOrigin(0.5);
container.add(badgeText);
@ -494,7 +495,7 @@ export class FusionScene extends Phaser.Scene {
if (candidates.length === 0) {
const t = this.add.text(PREVIEW_LEFT + PREVIEW_W / 2, PREVIEW_TOP + 100,
'No cards of this rarity in this faction.', {
fontSize: '16px', color: '#ff8800'
fontSize: '16px', color: '#ff8800', fontFamily: 'Audiowide'
}).setOrigin(0.5);
this._previewContainer.add(t);
this._previewCards.push(t);
@ -703,7 +704,7 @@ export class FusionScene extends Phaser.Scene {
// Card name text
const nameText = this.add.text(width / 2, 630, resultCardDef.name, {
fontSize: '28px', color: '#ffffff', fontStyle: 'bold',
stroke: '#000000', strokeThickness: 3
stroke: '#000000', strokeThickness: 3, fontFamily: 'Audiowide'
}).setOrigin(0.5).setDepth(201).setAlpha(0);
this.tweens.add({ targets: nameText, alpha: 1, duration: 300, delay: 800 });
@ -711,7 +712,7 @@ export class FusionScene extends Phaser.Scene {
const rarityHex = '#' + glowColor.toString(16).padStart(6, '0');
const rarityText = this.add.text(width / 2, 665,
resultCardDef.rarity.toUpperCase(), {
fontSize: '18px', color: rarityHex, fontStyle: 'bold'
fontSize: '18px', color: rarityHex, fontStyle: 'bold', fontFamily: 'Audiowide'
}).setOrigin(0.5).setDepth(201).setAlpha(0);
this.tweens.add({ targets: rarityText, alpha: 1, duration: 300, delay: 900 });
@ -721,7 +722,7 @@ export class FusionScene extends Phaser.Scene {
.setInteractive({ useHandCursor: true })
.setDepth(201).setAlpha(0);
const collectText = this.add.text(width / 2, 730, 'COLLECT', {
fontSize: '24px', color: '#44ff44', fontStyle: 'bold'
fontSize: '24px', color: '#44ff44', fontStyle: 'bold', fontFamily: 'Audiowide'
}).setOrigin(0.5).setDepth(201).setAlpha(0);
this.tweens.add({
@ -816,7 +817,7 @@ export class FusionScene extends Phaser.Scene {
this._backBtn = this.add.rectangle(80, 1040, 160, 45, 0x333333)
.setInteractive({ useHandCursor: true })
.setStrokeStyle(1, 0x888888);
this.add.text(80, 1040, 'Back', { fontSize: '18px', color: '#ffffff' }).setOrigin(0.5);
this.add.text(80, 1040, 'Back', { fontSize: '18px', color: '#ffffff', fontFamily: 'Audiowide' }).setOrigin(0.5);
this._backBtn.on('pointerover', () => {
this.sound.play('sfx_menu_hover', { volume: 0.5 });
});
@ -832,7 +833,7 @@ export class FusionScene extends Phaser.Scene {
_showMsg(msg, color) {
if (this._msgText) this._msgText.destroy();
this._msgText = this.add.text(this.scale.width / 2, 990, msg, {
fontSize: '22px', color
fontSize: '22px', color, fontFamily: 'Audiowide'
}).setOrigin(0.5).setDepth(10);
this.time.delayedCall(2500, () => {
if (this._msgText) { this._msgText.destroy(); this._msgText = null; }

View File

@ -80,25 +80,26 @@ export class MainMenuScene extends Phaser.Scene {
fontSize: '72px',
color: '#d4af37',
stroke: '#000000',
strokeThickness: 6
strokeThickness: 6,
fontFamily: 'RaiderCrusader'
}).setOrigin(0.5);
this.add.text(width / 2, 190, 'A Collectible Card Game', {
fontSize: '24px', color: '#888888'
fontSize: '24px', color: '#888888', fontFamily: 'Audiowide'
}).setOrigin(0.5);
// Level & Gold display
this.add.text(width - 30, 30, `Level: ${save.level || 1}`, {
fontSize: '24px', color: '#88aaff'
fontSize: '24px', color: '#88aaff', fontFamily: 'Audiowide'
}).setOrigin(1, 0);
this.goldText = this.add.text(width - 30, 60, `Gold: ${save.gold}`, {
fontSize: '24px', color: '#ffd700'
fontSize: '24px', color: '#ffd700', fontFamily: 'Audiowide'
}).setOrigin(1, 0);
// Version
this.add.text(20, height - 20, 'v1.0', {
fontSize: '14px', color: '#444444'
fontSize: '14px', color: '#444444', fontFamily: 'Audiowide'
}).setOrigin(0, 1);
const buttons = [
@ -133,7 +134,7 @@ export class MainMenuScene extends Phaser.Scene {
.setStrokeStyle(2, 0x4488ff);
const text = this.add.text(x, y, label, {
fontSize: '28px', color: '#ffffff'
fontSize: '28px', color: '#ffffff', fontFamily: 'Audiowide'
}).setOrigin(0.5);
bg.on('pointerover', () => { this.sound.play('sfx_menu_hover', { volume: 0.5 }); bg.setFillStyle(0x2a5a8c); });
@ -182,7 +183,7 @@ export class MainMenuScene extends Phaser.Scene {
.setInteractive({ useHandCursor: true })
.setStrokeStyle(1, 0x336633);
this.add.text(x, 25, '💾 Save Game to Your Drive', {
fontSize: '14px', color: '#669966'
fontSize: '14px', color: '#669966', fontFamily: 'Audiowide'
}).setOrigin(0.5);
saveBg.on('pointerover', () => saveBg.setStrokeStyle(1, 0x55aa55));
@ -210,7 +211,7 @@ export class MainMenuScene extends Phaser.Scene {
.setInteractive({ useHandCursor: true })
.setStrokeStyle(1, 0x334466);
this.add.text(x, 65, '📂 Restore Game from File', {
fontSize: '14px', color: '#6688aa'
fontSize: '14px', color: '#6688aa', fontFamily: 'Audiowide'
}).setOrigin(0.5);
restoreBg.on('pointerover', () => restoreBg.setStrokeStyle(1, 0x5577bb));
@ -251,7 +252,7 @@ export class MainMenuScene extends Phaser.Scene {
_showStatusMsg(msg, color) {
if (this._statusMsg) this._statusMsg.destroy();
this._statusMsg = this.add.text(145, 95, msg, {
fontSize: '13px', color
fontSize: '13px', color, fontFamily: 'Audiowide'
}).setOrigin(0.5, 0);
this.time.delayedCall(3000, () => {
if (this._statusMsg) { this._statusMsg.destroy(); this._statusMsg = null; }
@ -266,7 +267,7 @@ export class MainMenuScene extends Phaser.Scene {
.setStrokeStyle(2, 0x448844);
const text = this.add.text(x, y, getLabel(), {
fontSize: '20px', color: '#88cc88'
fontSize: '20px', color: '#88cc88', fontFamily: 'Audiowide'
}).setOrigin(0.5);
bg.on('pointerover', () => { this.sound.play('sfx_menu_hover', { volume: 0.5 }); bg.setFillStyle(0x2a3f2a); });

View File

@ -12,7 +12,7 @@ export class SkirmishSetupScene extends Phaser.Scene {
this.add.rectangle(width / 2, height / 2, width, height, 0x0d1b2a);
this.add.text(width / 2, 60, 'Skirmish Setup', {
fontSize: '42px', color: '#d4af37'
fontSize: '42px', color: '#d4af37', fontFamily: 'RaiderCrusader'
}).setOrigin(0.5);
this.selectedLevel = save.level || 1;
@ -20,11 +20,11 @@ export class SkirmishSetupScene extends Phaser.Scene {
// ── Level selector ────────────────────────────────────────────────
this.add.text(width / 2, 180, 'Card Level', {
fontSize: '24px', color: '#ffffff'
fontSize: '24px', color: '#ffffff', fontFamily: 'Audiowide'
}).setOrigin(0.5);
this.add.text(width / 2, 215, 'Both player and enemy cards will use this level', {
fontSize: '14px', color: '#888888'
fontSize: '14px', color: '#888888', fontFamily: 'Audiowide'
}).setOrigin(0.5);
this.levelButtons = [];
@ -39,7 +39,7 @@ export class SkirmishSetupScene extends Phaser.Scene {
.setStrokeStyle(2, isSelected ? 0x88ccff : (available ? 0x4488ff : 0x555555));
const text = this.add.text(bx, by, `Lv ${lv}`, {
fontSize: '20px', color: available ? '#ffffff' : '#555555'
fontSize: '20px', color: available ? '#ffffff' : '#555555', fontFamily: 'Audiowide'
}).setOrigin(0.5);
if (available) {
@ -56,11 +56,11 @@ export class SkirmishSetupScene extends Phaser.Scene {
// ── Difficulty selector ───────────────────────────────────────────
this.add.text(width / 2, 380, 'Difficulty', {
fontSize: '24px', color: '#ffffff'
fontSize: '24px', color: '#ffffff', fontFamily: 'Audiowide'
}).setOrigin(0.5);
this.add.text(width / 2, 415, 'Label only — no stat changes yet', {
fontSize: '14px', color: '#888888'
fontSize: '14px', color: '#888888', fontFamily: 'Audiowide'
}).setOrigin(0.5);
const difficulties = ['Easy', 'Medium', 'Hard', 'Legendary'];
@ -82,7 +82,7 @@ export class SkirmishSetupScene extends Phaser.Scene {
.setStrokeStyle(2, isSelected ? diffBorderColors[diff] : 0x444444);
const text = this.add.text(bx, by, diff, {
fontSize: '20px', color: isSelected ? '#ffffff' : '#aaaaaa'
fontSize: '20px', color: isSelected ? '#ffffff' : '#aaaaaa', fontFamily: 'Audiowide'
}).setOrigin(0.5);
bg.on('pointerdown', () => {
@ -99,7 +99,7 @@ export class SkirmishSetupScene extends Phaser.Scene {
.setInteractive({ useHandCursor: true })
.setStrokeStyle(2, 0x4488ff);
this.add.text(width / 2, 620, 'Start Battle', {
fontSize: '28px', color: '#ffffff', fontStyle: 'bold'
fontSize: '28px', color: '#ffffff', fontStyle: 'bold', fontFamily: 'Audiowide'
}).setOrigin(0.5);
startBg.on('pointerover', () => startBg.setFillStyle(0x3366aa));
@ -150,7 +150,7 @@ export class SkirmishSetupScene extends Phaser.Scene {
_showError(msg) {
const { width } = this.scale;
this.add.text(width / 2, 720, msg, {
fontSize: '20px', color: '#ff4444'
fontSize: '20px', color: '#ff4444', fontFamily: 'Audiowide'
}).setOrigin(0.5);
}
@ -158,7 +158,7 @@ export class SkirmishSetupScene extends Phaser.Scene {
const bg = this.add.rectangle(80, 45, 160, 50, 0x333333)
.setInteractive({ useHandCursor: true })
.setStrokeStyle(1, 0x888888);
this.add.text(80, 45, 'Back', { fontSize: '18px', color: '#ffffff' }).setOrigin(0.5);
this.add.text(80, 45, 'Back', { fontSize: '18px', color: '#ffffff', fontFamily: 'Audiowide' }).setOrigin(0.5);
bg.on('pointerdown', () => {
this.sound.play('sfx_menu_select', { volume: 0.7 });
this.scene.start('MainMenuScene');

View File

@ -35,13 +35,13 @@ export class StoreScene extends Phaser.Scene {
scanlines.fillStyle(0x000000, 0.22);
scanlines.fillRect(0, y, width, 1);
}
this.add.text(width / 2, 45, 'Card Store', { fontSize: '42px', color: '#d4af37' }).setOrigin(0.5);
this.add.text(width / 2, 45, 'Card Store', { fontSize: '42px', color: '#d4af37', fontFamily: 'RaiderCrusader' }).setOrigin(0.5);
this.add.text(width / 2, 90, 'Buy packs to expand your collection', {
fontSize: '20px', color: '#888888'
fontSize: '20px', color: '#888888', fontFamily: 'Audiowide'
}).setOrigin(0.5);
this.goldText = this.add.text(width - 20, 20, `Gold: ${save.gold}`, {
fontSize: '24px', color: '#ffd700'
fontSize: '24px', color: '#ffd700', fontFamily: 'Audiowide'
}).setOrigin(1, 0);
const packs = packManager.getAllPacks();
@ -61,24 +61,24 @@ export class StoreScene extends Phaser.Scene {
// Pack art (procedural)
this.add.rectangle(x, y - 60, 180, 150, 0x0d2240).setStrokeStyle(1, 0x4488ff);
this.add.text(x, y - 60, 'PACK', { fontSize: '38px', color: '#4488ff' }).setOrigin(0.5);
this.add.text(x, y - 60, 'PACK', { fontSize: '38px', color: '#4488ff', fontFamily: 'RaiderCrusader' }).setOrigin(0.5);
this.add.text(x, y + 45, pack.name, { fontSize: '24px', color: '#ffffff' }).setOrigin(0.5);
this.add.text(x, y + 75, `${pack.cardCount} cards per pack`, { fontSize: '18px', color: '#aaaaaa' }).setOrigin(0.5);
this.add.text(x, y + 100, `Cost: ${pack.cost} Gold`, { fontSize: '20px', color: '#ffd700' }).setOrigin(0.5);
this.add.text(x, y + 45, pack.name, { fontSize: '24px', color: '#ffffff', fontFamily: 'Audiowide' }).setOrigin(0.5);
this.add.text(x, y + 75, `${pack.cardCount} cards per pack`, { fontSize: '18px', color: '#aaaaaa', fontFamily: 'Audiowide' }).setOrigin(0.5);
this.add.text(x, y + 100, `Cost: ${pack.cost} Gold`, { fontSize: '20px', color: '#ffd700', fontFamily: 'Audiowide' }).setOrigin(0.5);
// Drop rates display
const rateLines = Object.entries(pack.dropRates).map(([r, v]) =>
`${r.charAt(0).toUpperCase() + r.slice(1)}: ${Math.floor(v * 100)}%`
).join(' ');
this.add.text(x, y + 128, rateLines, {
fontSize: '14px', color: '#888888', wordWrap: { width: 360 }, align: 'center'
fontSize: '14px', color: '#888888', wordWrap: { width: 360 }, align: 'center', fontFamily: 'Audiowide'
}).setOrigin(0.5);
const buyBtn = this.add.rectangle(x, y + 170, 240, 55, 0x224422)
.setInteractive({ useHandCursor: true })
.setStrokeStyle(2, 0x44aa44);
const buyTxt = this.add.text(x, y + 170, 'Buy Pack', { fontSize: '22px', color: '#ffffff' }).setOrigin(0.5);
const buyTxt = this.add.text(x, y + 170, 'Buy Pack', { fontSize: '22px', color: '#ffffff', fontFamily: 'Audiowide' }).setOrigin(0.5);
buyBtn.on('pointerover', () => buyBtn.setFillStyle(0x336633));
buyBtn.on('pointerout', () => buyBtn.setFillStyle(0x224422));
@ -116,7 +116,7 @@ export class StoreScene extends Phaser.Scene {
// Label
const label = this.add.text(width / 2, 700, 'You received:', {
fontSize: '22px', color: '#ffffff'
fontSize: '22px', color: '#ffffff', fontFamily: 'Audiowide'
}).setOrigin(0.5).setAlpha(0);
this.revealArea.push(label);
this.tweens.add({ targets: label, alpha: 1, duration: 300 });
@ -129,21 +129,22 @@ export class StoreScene extends Phaser.Scene {
.setStrokeStyle(3, rarityColors[card.rarity] || 0x888888)
.setAlpha(0);
const nameT = this.add.text(x, y - 70, card.name, {
fontSize: '20px', color: '#ffffff', wordWrap: { width: 260 }, align: 'center'
fontSize: '20px', color: '#ffffff', wordWrap: { width: 260 }, align: 'center', fontFamily: 'Audiowide'
}).setOrigin(0.5).setAlpha(0);
const rarityT = this.add.text(x, y - 30, card.rarity.toUpperCase(), {
fontSize: '16px',
color: '#' + (rarityColors[card.rarity] || 0x888888).toString(16).padStart(6, '0')
color: '#' + (rarityColors[card.rarity] || 0x888888).toString(16).padStart(6, '0'),
fontFamily: 'Audiowide'
}).setOrigin(0.5).setAlpha(0);
const lv1 = card.levels[0];
const statsT = this.add.text(x, y + 4, `ATK:${lv1.attack} HP:${lv1.health} ARM:${lv1.armor}`, {
fontSize: '16px', color: '#aaaaff'
fontSize: '16px', color: '#aaaaff', fontFamily: 'Audiowide'
}).setOrigin(0.5).setAlpha(0);
const skillT = this.add.text(x, y + 34, lv1.skills.map(s => s.name).join(', '), {
fontSize: '14px', color: '#ffcc44'
fontSize: '14px', color: '#ffcc44', fontFamily: 'Audiowide'
}).setOrigin(0.5).setAlpha(0);
const newT = this.add.text(x, y + 65, (save.collection[card.id] || 0) === 1 ? 'NEW!' : `x${save.collection[card.id]}`, {
fontSize: '16px', color: (save.collection[card.id] || 0) === 1 ? '#44ff44' : '#aaaaaa'
fontSize: '16px', color: (save.collection[card.id] || 0) === 1 ? '#44ff44' : '#aaaaaa', fontFamily: 'Audiowide'
}).setOrigin(0.5).setAlpha(0);
this.revealArea.push(bg, nameT, rarityT, statsT, skillT, newT);
@ -158,7 +159,7 @@ export class StoreScene extends Phaser.Scene {
_showMsg(msg, color) {
const { width } = this.scale;
const t = this.add.text(width / 2, 660, msg, { fontSize: '24px', color }).setOrigin(0.5);
const t = this.add.text(width / 2, 660, msg, { fontSize: '24px', color, fontFamily: 'Audiowide' }).setOrigin(0.5);
this.time.delayedCall(2000, () => t.destroy());
}
@ -166,7 +167,7 @@ export class StoreScene extends Phaser.Scene {
const bg = this.add.rectangle(80, 1040, 160, 45, 0x333333)
.setInteractive({ useHandCursor: true })
.setStrokeStyle(1, 0x888888);
this.add.text(80, 1040, 'Back', { fontSize: '18px', color: '#ffffff' }).setOrigin(0.5);
this.add.text(80, 1040, 'Back', { fontSize: '18px', color: '#ffffff', fontFamily: 'Audiowide' }).setOrigin(0.5);
bg.on('pointerdown', () => this.scene.start('MainMenuScene'));
}
}