feat: enhance Shift backgrounds and add commander death SFX for TA
- Add background image support for Shift game menu screens - Remove redundant title/subtitle text in Shift difficulty select - Add nuclear explosion SFX for Total Annihilation commander deaths - Extend _throttledSfx to support optional volume parameter
This commit is contained in:
parent
6e9cc84f6a
commit
9f54556ba9
Binary file not shown.
|
After Width: | Height: | Size: 1.9 MiB |
|
|
@ -156,7 +156,10 @@ export const MANIFEST = {
|
|||
image('spireclimb-act3', 'assets/images/spireclimb-act3.png'),
|
||||
(scene) => sheetsFrom(scene, 'spireclimb-artwork', ['cardSheet', 'creatureSheet', 'playerSheets']),
|
||||
],
|
||||
shift: (scene) => imagesFrom(scene, 'shift-artwork'),
|
||||
shift: [
|
||||
image('shift-background', 'assets/images/background-shift.png'),
|
||||
(scene) => imagesFrom(scene, 'shift-artwork'),
|
||||
],
|
||||
slots: (scene) => imagesFrom(scene, 'slots-artwork'),
|
||||
dungeonboss: (scene) => sheetsFrom(scene, 'dungeonboss-artwork',
|
||||
['roomSheet', 'heroSheet', 'spellSheet', 'bossSheet', 'deckBackSheet', 'iconSheet']),
|
||||
|
|
@ -278,7 +281,7 @@ export const MANIFEST = {
|
|||
{ type: 'audio', key: 'sfx-scifi-launch', path: 'assets/fx/scifi-launch.mp3' },
|
||||
// TA-specific sfx: machine guns per weapon.sound, rocket variants (random pick between the
|
||||
// two on fire, see TotalAnnihilationGame._onSimEvent), and unit/vehicle death cues.
|
||||
...['50cal', 'machinegun', 'rocket-1', 'rocket-2', 'unit-loss', 'vehicle-loss']
|
||||
...['50cal', 'machinegun', 'rocket-1', 'rocket-2', 'nuclear', 'unit-loss', 'vehicle-loss']
|
||||
.map((n) => ({ type: 'audio', key: `sfx-ta-${n}`, path: `assets/fx/ta-${n}.mp3` })),
|
||||
(scene) => musicFrom(scene, 'hacker-music'),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -83,24 +83,26 @@ export default class ShiftGame extends Phaser.Scene {
|
|||
this.bestText = null;
|
||||
}
|
||||
|
||||
_drawMenuBackground() {
|
||||
if (this.textures.exists('shift-background')) {
|
||||
this.add.image(GAME_WIDTH / 2, GAME_HEIGHT / 2, 'shift-background')
|
||||
.setDisplaySize(GAME_WIDTH, GAME_HEIGHT).setDepth(D.bg);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Difficulty select ─────────────────────────────────────────────────────────
|
||||
|
||||
showDifficultySelect() {
|
||||
this.view = 'difficulty';
|
||||
this.overlayUp = false;
|
||||
this.clearLayer();
|
||||
this._drawMenuBackground();
|
||||
const cx = GAME_WIDTH / 2;
|
||||
|
||||
const title = this.add.text(cx, 160, 'SHIFT', {
|
||||
fontFamily: 'Righteous', fontSize: '96px', color: COLORS.goldHex,
|
||||
}).setOrigin(0.5);
|
||||
const sub = this.add.text(cx, 268, 'Slide tiles into the empty space to restore the image.', {
|
||||
fontFamily: '"Julius Sans One"', fontSize: '30px', color: COLORS.mutedHex,
|
||||
}).setOrigin(0.5);
|
||||
const pick = this.add.text(cx, 356, 'Choose a difficulty', {
|
||||
const pick = this.add.text(cx, 390, 'Choose a difficulty', {
|
||||
fontFamily: 'Righteous', fontSize: '34px', color: COLORS.textHex,
|
||||
}).setOrigin(0.5);
|
||||
this.layer.add([title, sub, pick]);
|
||||
this.layer.add(pick);
|
||||
|
||||
const TILE_W = 430;
|
||||
const TILE_H = 400;
|
||||
|
|
@ -218,6 +220,7 @@ export default class ShiftGame extends Phaser.Scene {
|
|||
this.view = 'artwork';
|
||||
this.overlayUp = false;
|
||||
this.clearLayer();
|
||||
this._drawMenuBackground();
|
||||
const cx = GAME_WIDTH / 2;
|
||||
|
||||
const title = this.add.text(cx, 120, 'Choose Your Image', {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import * as Phaser from 'phaser';
|
|||
import { GAME_WIDTH, GAME_HEIGHT } from '../../config.js';
|
||||
import { MusicPlayer } from '../../ui/MusicPlayer.js';
|
||||
import { getGameSoundtrack } from '../../services/soundtrack.js';
|
||||
import { playSound } from '../../ui/Sounds.js';
|
||||
import { playSound, playSoundEx } from '../../ui/Sounds.js';
|
||||
import { api } from '../../services/api.js';
|
||||
import { compileRules } from './TARules.js';
|
||||
import { generateMap, decodeMap } from './TAMapGen.js';
|
||||
|
|
@ -263,8 +263,13 @@ export default class TotalAnnihilationGame extends Phaser.Scene {
|
|||
if (ev.t === 'weaponFired' && ev.sound) this._throttledSfx(this._pickSound(ev.sound), 90);
|
||||
if (ev.t === 'impact' && ev.sound) this._throttledSfx(this._pickSound(ev.sound), 90);
|
||||
if (ev.t === 'unitDestroyed' && !ev.isBuilding) {
|
||||
const moveClass = this.rules.defById[ev.defId]?.moveClass;
|
||||
if (moveClass) this._throttledSfx(moveClass === 'foot' ? 'sfx-ta-unit-loss' : 'sfx-ta-vehicle-loss', 120);
|
||||
const def = this.rules.defById[ev.defId];
|
||||
if (def?.isCommander) {
|
||||
// Play the nuclear explosion at full volume for maximum impact
|
||||
this._throttledSfx('sfx-ta-nuclear', 300, 1);
|
||||
} else if (def?.moveClass) {
|
||||
this._throttledSfx(def.moveClass === 'foot' ? 'sfx-ta-unit-loss' : 'sfx-ta-vehicle-loss', 120);
|
||||
}
|
||||
}
|
||||
if (ev.t === 'buildingComplete' && ev.army === this.playerArmy) {
|
||||
// Terrain under a finished structure changes, so its chunk has to be restamped.
|
||||
|
|
@ -290,12 +295,19 @@ export default class TotalAnnihilationGame extends Phaser.Scene {
|
|||
}
|
||||
|
||||
/** Gate repeated plays of the same sound key so a volley of units firing at once doesn't
|
||||
* stack a dozen overlapping instances of the same clip. */
|
||||
_throttledSfx(key, gapMs) {
|
||||
* stack a dozen overlapping instances of the same clip.
|
||||
* @param {string} key - Sound key
|
||||
* @param {number} gapMs - Minimum ms between plays
|
||||
* @param {number} [volume] - Optional volume 0-1 (defaults to normal via playSound) */
|
||||
_throttledSfx(key, gapMs, volume) {
|
||||
const now = this.time.now;
|
||||
if ((this.lastSfxAt[key] ?? -1e9) + gapMs > now) return;
|
||||
this.lastSfxAt[key] = now;
|
||||
playSound(this, key);
|
||||
if (volume !== undefined) {
|
||||
playSoundEx(this, key, { volume });
|
||||
} else {
|
||||
playSound(this, key);
|
||||
}
|
||||
}
|
||||
|
||||
_finish(over) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue