feat: Add main menu assets and refactor puzzle thumbnail handling
- Add main menu UI image and background music (with updated track metadata) - Rename puzzle assets to include `.png` extensions for consistency - Introduce `thumbnails.json` manifest to decouple thumbnail paths from puzzle data - Update `NewPuzzleScene.js` to load thumbnails explicitly via manifest instead of inferring paths - Update `PuzzleScene.js` to ensure full puzzle images are loaded fresh on initial entry (not during network joins or restores)
|
|
@ -1,22 +1,22 @@
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"path": "assets/audio/music/track_01.mp3",
|
"path": "assets/audio/music/track_01.mp3",
|
||||||
"title": "Track 01",
|
"title": "Digital Memories",
|
||||||
"artist": "Unknown Artist"
|
"artist": "AI Com Truise"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/audio/music/track_02.mp3",
|
"path": "assets/audio/music/track_02.mp3",
|
||||||
"title": "Track 02",
|
"title": "Synthwave Highway",
|
||||||
"artist": "Unknown Artist"
|
"artist": "AI The Midnight"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/audio/music/track_03.mp3",
|
"path": "assets/audio/music/track_03.mp3",
|
||||||
"title": "Track 03",
|
"title": "Orbital Perspective",
|
||||||
"artist": "Unknown Artist"
|
"artist": "AI Tangerine Dream"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/audio/music/track_04.mp3",
|
"path": "assets/audio/music/track_04.mp3",
|
||||||
"title": "Track 04",
|
"title": "Just Puzzlin'",
|
||||||
"artist": "Unknown Artist"
|
"artist": "AI Earthless"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
Before Width: | Height: | Size: 3.8 MiB After Width: | Height: | Size: 3.8 MiB |
|
Before Width: | Height: | Size: 3.8 MiB After Width: | Height: | Size: 3.8 MiB |
|
Before Width: | Height: | Size: 325 KiB After Width: | Height: | Size: 325 KiB |
|
Before Width: | Height: | Size: 280 KiB After Width: | Height: | Size: 280 KiB |
|
Before Width: | Height: | Size: 386 KiB After Width: | Height: | Size: 386 KiB |
|
Before Width: | Height: | Size: 4.2 MiB After Width: | Height: | Size: 4.2 MiB |
|
After Width: | Height: | Size: 3.2 MiB |
|
|
@ -41,7 +41,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "puzzle_misty",
|
"key": "puzzle_misty",
|
||||||
"path": "assets/images/puzzles/misty_exploration",
|
"path": "assets/images/puzzles/misty_exploration.png",
|
||||||
"label": "Misty Exploration"
|
"label": "Misty Exploration"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -66,12 +66,12 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "puzzle_mandrill",
|
"key": "puzzle_mandrill",
|
||||||
"path": "assets/images/puzzles/thoughtful_mandrill",
|
"path": "assets/images/puzzles/thoughtful_mandrill.png",
|
||||||
"label": "Thoughtful Mandrill"
|
"label": "Thoughtful Mandrill"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "puzzle_ultimate_tree_fort",
|
"key": "puzzle_ultimate_tree_fort",
|
||||||
"path": "assets/images/puzzles/ultimate_tree_fort",
|
"path": "assets/images/puzzles/ultimate_tree_fort.png",
|
||||||
"label": "Ultimate Tree Fort"
|
"label": "Ultimate Tree Fort"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"key": "thumb_america",
|
||||||
|
"path": "assets/images/puzzles/thumb_america.png",
|
||||||
|
"puzzleKey": "puzzle_america"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "thumb_dwarven_mining_village",
|
||||||
|
"path": "assets/images/puzzles/thumb_dwarven_mining_village.png",
|
||||||
|
"puzzleKey": "puzzle_dwarven_mining_village"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "thumb_golden_in_the_mountains",
|
||||||
|
"path": "assets/images/puzzles/thumb_golden_in_the_mountains.png",
|
||||||
|
"puzzleKey": "puzzle_golden_in_the_mountains"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "thumb_good_vs_evil",
|
||||||
|
"path": "assets/images/puzzles/thumb_good_vs_evil.png",
|
||||||
|
"puzzleKey": "puzzle_good_vs_evil"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "thumb_growing_ea",
|
||||||
|
"path": "assets/images/puzzles/thumb_growing_ea.png",
|
||||||
|
"puzzleKey": "puzzle_growing_ea"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "thumb_guilin_landscape",
|
||||||
|
"path": "assets/images/puzzles/thumb_guilin_landscape.png",
|
||||||
|
"puzzleKey": "puzzle_guilin_landscape"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "thumb_home_sweet_home",
|
||||||
|
"path": "assets/images/puzzles/thumb_home_sweet_home.png",
|
||||||
|
"puzzleKey": "puzzle_home_sweet_home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "thumb_lone_ship",
|
||||||
|
"path": "assets/images/puzzles/thumb_lone_ship_in_the_seas.png",
|
||||||
|
"puzzleKey": "puzzle_lone_ship"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "thumb_misty",
|
||||||
|
"path": "assets/images/puzzles/thumb_misty_exploration.png",
|
||||||
|
"puzzleKey": "puzzle_misty"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "thumb_model_village",
|
||||||
|
"path": "assets/images/puzzles/thumb_model_village.png",
|
||||||
|
"puzzleKey": "puzzle_model_village"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "thumb_opulant_flowers",
|
||||||
|
"path": "assets/images/puzzles/thumb_opulant_flowers.png",
|
||||||
|
"puzzleKey": "puzzle_opulant_flowers"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "thumb_robot_in_a_flavela",
|
||||||
|
"path": "assets/images/puzzles/thumb_robot_in_a_flavela.png",
|
||||||
|
"puzzleKey": "puzzle_robot_in_a_flavela"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "thumb_disassembled_android",
|
||||||
|
"path": "assets/images/puzzles/thumb_the_disassembled_android.png",
|
||||||
|
"puzzleKey": "puzzle_disassembled_android"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "thumb_mandrill",
|
||||||
|
"path": "assets/images/puzzles/thumb_thoughtful_mandrill.png",
|
||||||
|
"puzzleKey": "puzzle_mandrill"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "thumb_ultimate_tree_fort",
|
||||||
|
"path": "assets/images/puzzles/thumb_ultimate_tree_fort.png",
|
||||||
|
"puzzleKey": "puzzle_ultimate_tree_fort"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
@ -18,8 +18,9 @@ class NewPuzzleScene extends Phaser.Scene {
|
||||||
}
|
}
|
||||||
|
|
||||||
preload() {
|
preload() {
|
||||||
// Load the puzzle list JSON; images are loaded dynamically in create()
|
// Load puzzle list and thumbnail manifest; full images are loaded by PuzzleScene
|
||||||
this.load.json('puzzle_list', 'assets/puzzles.json');
|
this.load.json('puzzle_list', 'assets/puzzles.json');
|
||||||
|
this.load.json('thumbnail_list', 'assets/thumbnails.json');
|
||||||
}
|
}
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
|
|
@ -59,12 +60,18 @@ class NewPuzzleScene extends Phaser.Scene {
|
||||||
sep.fillTriangle(960, 100, 966, 105, 960, 110);
|
sep.fillTriangle(960, 100, 966, 105, 960, 110);
|
||||||
sep.fillTriangle(960, 100, 954, 105, 960, 110);
|
sep.fillTriangle(960, 100, 954, 105, 960, 110);
|
||||||
|
|
||||||
// Read puzzle list from cache, then load any images not yet in the texture cache
|
// Read puzzle list and thumbnail manifest from cache
|
||||||
this._puzzleImages = this.cache.json.get('puzzle_list');
|
this._puzzleImages = this.cache.json.get('puzzle_list');
|
||||||
|
this._thumbnails = this.cache.json.get('thumbnail_list') || [];
|
||||||
|
|
||||||
const toLoad = this._puzzleImages.filter(img => !this.textures.exists(img.key));
|
// Build a lookup from puzzleKey -> thumbnail entry
|
||||||
|
this._thumbByPuzzleKey = {};
|
||||||
|
this._thumbnails.forEach(t => { this._thumbByPuzzleKey[t.puzzleKey] = t; });
|
||||||
|
|
||||||
|
// Only load thumbnail textures (full images are loaded by PuzzleScene)
|
||||||
|
const toLoad = this._thumbnails.filter(t => !this.textures.exists(t.key));
|
||||||
if (toLoad.length > 0) {
|
if (toLoad.length > 0) {
|
||||||
toLoad.forEach(img => this.load.image(img.key, img.path));
|
toLoad.forEach(t => this.load.image(t.key, t.path));
|
||||||
this.load.once('complete', () => this._buildDomUI());
|
this.load.once('complete', () => this._buildDomUI());
|
||||||
this.load.start();
|
this.load.start();
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -131,9 +138,8 @@ class NewPuzzleScene extends Phaser.Scene {
|
||||||
});
|
});
|
||||||
|
|
||||||
const thumb = document.createElement('img');
|
const thumb = document.createElement('img');
|
||||||
// Use thumbnail version: insert "thumb_" before the filename
|
const thumbEntry = this._thumbByPuzzleKey[img.key];
|
||||||
const lastSlash = img.path.lastIndexOf('/');
|
thumb.src = thumbEntry ? thumbEntry.path : img.path;
|
||||||
thumb.src = img.path.substring(0, lastSlash + 1) + 'thumb_' + img.path.substring(lastSlash + 1);
|
|
||||||
Object.assign(thumb.style, {
|
Object.assign(thumb.style, {
|
||||||
display: 'block',
|
display: 'block',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,13 @@ class PuzzleScene extends Phaser.Scene {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fresh start from NewPuzzleScene — load the full image if not cached
|
||||||
|
if (!this.cfg._networkJoin && !this.cfg._restore) {
|
||||||
|
if (!this.textures.exists(this.cfg.imageKey)) {
|
||||||
|
this.load.image(this.cfg.imageKey, this.cfg.imagePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
|
|
|
||||||