feat: enhance card armor values and add retro VHS menu effects

- Updated `data/cards.json` to significantly increase armor values across all card tiers and types for better survivability balance.
- Refined skill descriptions in `data/skills.json` (Pierce, Berserk, Protect, Jam, Enfeeble) to clarify mechanics like "this turn" vs "permanent" effects and added flavor text.
- Added video background support to `MainMenuScene.js` using the new `tyrants-edge-menu.mp4` asset.
- Implemented retro VHS visual overlays on the menu: scanlines, stepped vignette, rolling tracking bar, and random screen flickers for atmospheric effect.
This commit is contained in:
Brian Fertig 2026-03-15 10:32:27 -06:00
parent e9b7a51b32
commit e012cda336
4 changed files with 202 additions and 144 deletions

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -15,8 +15,8 @@
},
{
"name": "pierce",
"description": "Before attacking: reduce the target's armor by value for this attack, then restore it after.",
"allDescription": "Before attacking: reduce ALL enemy cards' armor by value for this attack, then restore after.",
"description": "Before attacking: Find a weakpoint to reduce the target's armor by value for this attack.",
"allDescription": "Before attacking: reduce ALL enemy cards' armor by value for this attack.",
"trigger": "preAttack",
"category": "offense",
"supportsAll": true
@ -31,7 +31,7 @@
},
{
"name": "berserk",
"description": "On damage: permanently gain value attack power for this battle.",
"description": "On damage: RAGE! Permanently gain value attack power for this battle.",
"trigger": "on_attack",
"category": "offense"
},
@ -59,20 +59,20 @@
},
{
"name": "protect",
"description": "Before battle: grant +value armor to self and adjacent lane cards. Removed after battle.",
"description": "Before battle: grant +value armor to self and adjacent lane cards. Removed after each turn.",
"trigger": "preBattle",
"category": "defense"
},
{
"name": "jam",
"description": "Prevent the target card from attacking for value turns.",
"description": "Jam their signals! Reduce opponent skill abilities by value this turn, also has reduced effect on opponent neighbors.",
"trigger": "on_attack",
"category": "control"
},
{
"name": "enfeeble",
"description": "Reduce target's attack by value permanently.",
"allDescription": "Reduce ALL enemy cards' attack by value. Restored after battle.",
"description": "Reduce target's attack by value for this turn.",
"allDescription": "Reduce ALL enemy cards' attack by value. Restored after turn.",
"trigger": "on_attack",
"category": "control",
"supportsAll": true
@ -165,4 +165,4 @@
"trigger": "passive",
"category": "offense"
}
]
]

View File

@ -1,6 +1,10 @@
export class MainMenuScene extends Phaser.Scene {
constructor() { super('MainMenuScene'); }
preload() {
this.load.video('menu_bg', 'assets/video/tyrants-edge-menu.mp4');
}
create() {
const save = this.registry.get('save');
const { width, height } = this.scale;
@ -9,11 +13,65 @@ export class MainMenuScene extends Phaser.Scene {
const menuMusic = this.registry.get('music_main_menu');
if (menuMusic && !menuMusic.isPlaying) menuMusic.play();
// Background
this.add.rectangle(width / 2, height / 2, width, height, 0x0d1b2a);
// ── Video background ──────────────────────────────────────────────────────
const video = this.add.video(width / 2, height / 2, 'menu_bg');
video.setMute(true);
// setDisplaySize reads frame.realWidth which is null until the first frame
// is decoded — defer sizing to the 'created' event.
video.on('created', () => {
if (video.scene) video.setDisplaySize(width, height);
});
video.play(true); // true = loop
// Decorative elements
this.add.rectangle(width / 2, height / 2, width, 2, 0x1a3a5c).setAlpha(0.5);
// ── VHS / retro overlay effects ───────────────────────────────────────────
// 1. Dark tint so UI stays readable over whatever the video shows
this.add.rectangle(width / 2, height / 2, width, height, 0x000000, 0.42);
// 2. Scanlines — one dark horizontal pixel every 3 rows, baked into graphics
const scanlines = this.add.graphics();
for (let y = 0; y < height; y += 3) {
scanlines.fillStyle(0x000000, 0.22);
scanlines.fillRect(0, y, width, 1);
}
// 3. Vignette — stepped gradient darkening from each edge inward
const vig = this.add.graphics();
const steps = 12;
const vigSize = 180;
for (let i = 0; i < steps; i++) {
const a = (1 - i / steps) * 0.65;
const t = Math.round((vigSize / steps) * i);
const s = Math.ceil(vigSize / steps);
vig.fillStyle(0x000000, a);
vig.fillRect(t, 0, s, height); // left
vig.fillRect(width - t - s, 0, s, height); // right
vig.fillRect(0, t, width, s); // top
vig.fillRect(0, height - t - s, width, s); // bottom
}
// 4. Rolling VHS tracking bar — thin translucent band drifts down the screen
const scanBar = this.add.rectangle(width / 2, -30, width, 60, 0xffffff, 0.045);
this.tweens.add({
targets: scanBar,
y: height + 30,
duration: 7500,
repeat: -1,
ease: 'Linear'
});
// 5. Occasional screen flicker — random brief brightness pulse
const scheduleFlicker = () => {
this.time.delayedCall(Phaser.Math.Between(2500, 7000), () => {
if (!this.scene.isActive('MainMenuScene')) return;
const flash = this.add.rectangle(width / 2, height / 2, width, height, 0xffffff, 0.06);
this.time.delayedCall(60, () => {
if (flash.scene) flash.destroy();
scheduleFlicker();
});
});
};
scheduleFlicker();
// Title
this.add.text(width / 2, 120, "TYRANT'S EDGE", {