feat: enhance Jewel Quest and Shift game UI/UX
- Add custom background image for Jewel Quest menu screens - Remove redundant title/subtitle text from Jewel Quest class and level select screens - Reorganize Jewel Quest UI layout with improved positioning and semi-transparent text backgrounds - Update Jewel Quest level select grid and status text positioning for better visual hierarchy - Add background backdrop to Shift game "Choose Your Image" title for improved readability
|
After Width: | Height: | Size: 2.5 MiB |
|
Before Width: | Height: | Size: 2.6 MiB After Width: | Height: | Size: 2.6 MiB |
|
Before Width: | Height: | Size: 3.1 MiB After Width: | Height: | Size: 3.6 MiB |
|
Before Width: | Height: | Size: 3.4 MiB After Width: | Height: | Size: 3.2 MiB |
|
Before Width: | Height: | Size: 2.6 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 3.8 MiB After Width: | Height: | Size: 3.2 MiB |
|
Before Width: | Height: | Size: 3.2 MiB After Width: | Height: | Size: 3.1 MiB |
|
|
@ -85,7 +85,10 @@ export const MANIFEST = {
|
||||||
...Array.from({ length: 6 }, (_, i) =>
|
...Array.from({ length: 6 }, (_, i) =>
|
||||||
image(`bloxorz-bg-${i + 1}`, `assets/images/bloxorz/background-${i + 1}.png`)),
|
image(`bloxorz-bg-${i + 1}`, `assets/images/bloxorz/background-${i + 1}.png`)),
|
||||||
],
|
],
|
||||||
jewelquest: [image('bg-jewelquest-battle', 'assets/images/background-jewelquest.png')],
|
jewelquest: [
|
||||||
|
image('bg-jewelquest-battle', 'assets/images/background-jewelquest.png'),
|
||||||
|
image('bg-jewelquest-menu', 'assets/images/background-jewelquest-menu.png'),
|
||||||
|
],
|
||||||
bejeweled: [image('bg-bejeweled', 'assets/images/background-bejeweledblitz.png')],
|
bejeweled: [image('bg-bejeweled', 'assets/images/background-bejeweledblitz.png')],
|
||||||
dominion: [
|
dominion: [
|
||||||
// Dominion card art. One 270×390 cell per card (art fills the top ~60%;
|
// Dominion card art. One 270×390 cell per card (art fills the top ~60%;
|
||||||
|
|
|
||||||
|
|
@ -88,8 +88,12 @@ export default class JewelQuestGame extends Phaser.Scene {
|
||||||
} catch (_) { /* private mode */ }
|
} catch (_) { /* private mode */ }
|
||||||
|
|
||||||
this.makeTextures();
|
this.makeTextures();
|
||||||
// Solid felt background for non-battle screens (class select, level select, intro)
|
// Background for non-battle screens (class select, level select, intro)
|
||||||
|
if (this.textures.exists('bg-jewelquest-menu')) {
|
||||||
|
this.add.image(GAME_WIDTH / 2, GAME_HEIGHT / 2, 'bg-jewelquest-menu').setDisplaySize(GAME_WIDTH, GAME_HEIGHT).setDepth(D.felt);
|
||||||
|
} else {
|
||||||
this.add.rectangle(GAME_WIDTH / 2, GAME_HEIGHT / 2, GAME_WIDTH, GAME_HEIGHT, FELT).setDepth(D.felt);
|
this.add.rectangle(GAME_WIDTH / 2, GAME_HEIGHT / 2, GAME_WIDTH, GAME_HEIGHT, FELT).setDepth(D.felt);
|
||||||
|
}
|
||||||
|
|
||||||
this.layer = this.add.container(0, 0);
|
this.layer = this.add.container(0, 0);
|
||||||
if (this.playerClass) this.showLevelSelect();
|
if (this.playerClass) this.showLevelSelect();
|
||||||
|
|
@ -361,14 +365,6 @@ export default class JewelQuestGame extends Phaser.Scene {
|
||||||
this.clearLayer();
|
this.clearLayer();
|
||||||
const cx = GAME_WIDTH / 2;
|
const cx = GAME_WIDTH / 2;
|
||||||
|
|
||||||
const title = this.add.text(cx, 80, 'JEWEL QUEST', {
|
|
||||||
fontFamily: 'Righteous', fontSize: '64px', color: COLORS.goldHex,
|
|
||||||
}).setOrigin(0.5);
|
|
||||||
const sub = this.add.text(cx, 138, 'Choose your champion. Match gems for mana, match skulls for damage, and spend mana on spells.', {
|
|
||||||
fontFamily: '"Julius Sans One"', fontSize: '22px', color: COLORS.mutedHex,
|
|
||||||
}).setOrigin(0.5);
|
|
||||||
this.layer.add([title, sub]);
|
|
||||||
|
|
||||||
const loadout = this.loadout();
|
const loadout = this.loadout();
|
||||||
const ids = Object.keys(CLASSES);
|
const ids = Object.keys(CLASSES);
|
||||||
const CARD_W = 430;
|
const CARD_W = 430;
|
||||||
|
|
@ -376,7 +372,7 @@ export default class JewelQuestGame extends Phaser.Scene {
|
||||||
const GAP = 26;
|
const GAP = 26;
|
||||||
const totalW = ids.length * CARD_W + (ids.length - 1) * GAP;
|
const totalW = ids.length * CARD_W + (ids.length - 1) * GAP;
|
||||||
const left = cx - totalW / 2 + CARD_W / 2;
|
const left = cx - totalW / 2 + CARD_W / 2;
|
||||||
const cy = 540;
|
const cy = 640;
|
||||||
|
|
||||||
ids.forEach((id, i) => {
|
ids.forEach((id, i) => {
|
||||||
const cls = CLASSES[id];
|
const cls = CLASSES[id];
|
||||||
|
|
@ -459,14 +455,6 @@ export default class JewelQuestGame extends Phaser.Scene {
|
||||||
this.clearLayer();
|
this.clearLayer();
|
||||||
const cx = GAME_WIDTH / 2;
|
const cx = GAME_WIDTH / 2;
|
||||||
|
|
||||||
const title = this.add.text(cx, 78, 'JEWEL QUEST', {
|
|
||||||
fontFamily: 'Righteous', fontSize: '60px', color: COLORS.goldHex,
|
|
||||||
}).setOrigin(0.5);
|
|
||||||
const sub = this.add.text(cx, 130, 'Match 3+ gems to bank mana, match skulls to deal damage, and cast spells to break your rival.', {
|
|
||||||
fontFamily: '"Julius Sans One"', fontSize: '22px', color: COLORS.mutedHex,
|
|
||||||
}).setOrigin(0.5);
|
|
||||||
this.layer.add([title, sub]);
|
|
||||||
|
|
||||||
if (!this.bank.length) {
|
if (!this.bank.length) {
|
||||||
const msg = this.add.text(cx, 520, 'No levels found in /data/jewelquest.json', {
|
const msg = this.add.text(cx, 520, 'No levels found in /data/jewelquest.json', {
|
||||||
fontFamily: '"Julius Sans One"', fontSize: '26px', color: COLORS.dangerHex, align: 'center',
|
fontFamily: '"Julius Sans One"', fontSize: '26px', color: COLORS.dangerHex, align: 'center',
|
||||||
|
|
@ -486,20 +474,27 @@ export default class JewelQuestGame extends Phaser.Scene {
|
||||||
`Spells ${loadout.spellCount}/5`,
|
`Spells ${loadout.spellCount}/5`,
|
||||||
nextMs ? `Next bonus after Level ${nextMs.afterLevel}` : 'All bonuses earned',
|
nextMs ? `Next bonus after Level ${nextMs.afterLevel}` : 'All bonuses earned',
|
||||||
].join(' • ');
|
].join(' • ');
|
||||||
const prog = this.add.text(cx, 176, `Defeated ${this.levelsCompleted} / ${this.bank.length}`, {
|
const prog = this.add.text(cx, 376, `Defeated ${this.levelsCompleted} / ${this.bank.length}`, {
|
||||||
fontFamily: 'Righteous', fontSize: '24px', color: COLORS.textHex,
|
fontFamily: 'Righteous', fontSize: '24px', color: COLORS.textHex,
|
||||||
}).setOrigin(0.5);
|
}).setOrigin(0.5);
|
||||||
const loadoutText = this.add.text(cx, 216, strip, {
|
const loadoutText = this.add.text(cx, 416, strip, {
|
||||||
fontFamily: '"Julius Sans One"', fontSize: '20px', color: COLORS.goldHex,
|
fontFamily: '"Julius Sans One"', fontSize: '20px', color: COLORS.goldHex,
|
||||||
}).setOrigin(0.5);
|
}).setOrigin(0.5);
|
||||||
this.layer.add([prog, loadoutText]);
|
|
||||||
|
// Semi-transparent background behind the status text
|
||||||
|
const bgW = Math.max(prog.width, loadoutText.width) + 60;
|
||||||
|
const bgH = 120;
|
||||||
|
const bg = this.add.graphics().setDepth(D.ui - 1);
|
||||||
|
bg.fillStyle(0x000000, 0.65);
|
||||||
|
bg.fillRoundedRect(cx - bgW / 2, 316, bgW, bgH, 12);
|
||||||
|
this.layer.add([bg, prog, loadoutText]);
|
||||||
|
|
||||||
const COLS = 10;
|
const COLS = 10;
|
||||||
const TILE = 128;
|
const TILE = 128;
|
||||||
const GAP = 16;
|
const GAP = 16;
|
||||||
const gridW = COLS * TILE + (COLS - 1) * GAP;
|
const gridW = COLS * TILE + (COLS - 1) * GAP;
|
||||||
const left = cx - gridW / 2 + TILE / 2;
|
const left = cx - gridW / 2 + TILE / 2;
|
||||||
const top = 330;
|
const top = 530;
|
||||||
|
|
||||||
this.bank.forEach((lv, i) => {
|
this.bank.forEach((lv, i) => {
|
||||||
const col = i % COLS;
|
const col = i % COLS;
|
||||||
|
|
@ -619,7 +614,14 @@ export default class JewelQuestGame extends Phaser.Scene {
|
||||||
const statText = this.add.text(cx, 800, `Skill ${stars} HP ❤ ${lv.hp} Class ${enemyCls}`, {
|
const statText = this.add.text(cx, 800, `Skill ${stars} HP ❤ ${lv.hp} Class ${enemyCls}`, {
|
||||||
fontFamily: '"Julius Sans One"', fontSize: '28px', color: COLORS.textHex,
|
fontFamily: '"Julius Sans One"', fontSize: '28px', color: COLORS.textHex,
|
||||||
}).setOrigin(0.5);
|
}).setOrigin(0.5);
|
||||||
this.layer.add([name, bio, tagline, statText]);
|
|
||||||
|
// Semi-transparent background behind the opponent info text
|
||||||
|
const bgW = Math.max(name.width, bio.width, tagline.width, statText.width) + 60;
|
||||||
|
const bgH = 240;
|
||||||
|
const bg = this.add.graphics().setDepth(D.ui - 1);
|
||||||
|
bg.fillStyle(0x000000, 0.65);
|
||||||
|
bg.fillRoundedRect(cx - bgW / 2, 600, bgW, bgH, 12);
|
||||||
|
this.layer.add([bg, name, bio, tagline, statText]);
|
||||||
|
|
||||||
const fight = new Button(this, cx - 130, GAME_HEIGHT - 110, 'FIGHT!', () => this.startBattle(level),
|
const fight = new Button(this, cx - 130, GAME_HEIGHT - 110, 'FIGHT!', () => this.startBattle(level),
|
||||||
{ width: 240, height: 66, fontSize: 30 });
|
{ width: 240, height: 66, fontSize: 30 });
|
||||||
|
|
|
||||||
|
|
@ -223,10 +223,15 @@ export default class ShiftGame extends Phaser.Scene {
|
||||||
this._drawMenuBackground();
|
this._drawMenuBackground();
|
||||||
const cx = GAME_WIDTH / 2;
|
const cx = GAME_WIDTH / 2;
|
||||||
|
|
||||||
|
const titleBg = this.add.graphics().setDepth(D.overlay - 1);
|
||||||
|
titleBg.fillStyle(0x000000, 0.55);
|
||||||
|
titleBg.fillRoundedRect(cx - 310, 75, 620, 90, 12);
|
||||||
|
titleBg.lineStyle(2, 0xffffff, 0.3);
|
||||||
|
titleBg.strokeRoundedRect(cx - 310, 75, 620, 90, 12);
|
||||||
const title = this.add.text(cx, 120, 'Choose Your Image', {
|
const title = this.add.text(cx, 120, 'Choose Your Image', {
|
||||||
fontFamily: 'Righteous', fontSize: '64px', color: COLORS.goldHex,
|
fontFamily: 'Righteous', fontSize: '64px', color: COLORS.goldHex,
|
||||||
}).setOrigin(0.5);
|
}).setOrigin(0.5);
|
||||||
this.layer.add(title);
|
this.layer.add([titleBg, title]);
|
||||||
|
|
||||||
const CARD_W = 310;
|
const CARD_W = 310;
|
||||||
const CARD_H = 360;
|
const CARD_H = 360;
|
||||||
|
|
|
||||||