609 lines
20 KiB
JavaScript
609 lines
20 KiB
JavaScript
/* global Phaser, generateRoomCode */
|
|
|
|
const DIFFICULTIES = [
|
|
{ pieces: 20, label: '20 Pieces' },
|
|
{ pieces: 40, label: '40 Pieces' },
|
|
{ pieces: 60, label: '60 Pieces' },
|
|
{ pieces: 100, label: '100 Pieces' },
|
|
{ pieces: 140, label: '140 Pieces' },
|
|
{ pieces: 180, label: '180 Pieces' },
|
|
{ pieces: 250, label: '250 Pieces' }
|
|
];
|
|
|
|
const BACKGROUNDS = [
|
|
{ key: 'bg_dark_wood', path: 'assets/images/ui/dark_wood.jpg', label: 'Dark Wood' },
|
|
{ key: 'bg_green_felt', path: 'assets/images/ui/green_felt.jpg', label: 'Green Felt' },
|
|
];
|
|
|
|
class NewPuzzleScene extends Phaser.Scene {
|
|
constructor() {
|
|
super({ key: 'NewPuzzleScene' });
|
|
}
|
|
|
|
preload() {
|
|
// Load puzzle list and thumbnail manifest; full images are loaded by PuzzleScene
|
|
this.load.json('puzzle_list', 'assets/puzzles.json');
|
|
this.load.json('thumbnail_list', 'assets/thumbnails.json');
|
|
|
|
// Loading progress text
|
|
this._loadingText = this.add.text(960, 540, 'Loading 0%...', {
|
|
fontFamily: 'Arial, sans-serif',
|
|
fontSize: '36px',
|
|
color: '#ffffff',
|
|
stroke: '#000000',
|
|
strokeThickness: 4,
|
|
}).setOrigin(0.5);
|
|
|
|
this._onLoadProgress = (value) => {
|
|
if (this._loadingText) {
|
|
this._loadingText.setText(`Loading ${Math.round(value * 100)}%...`);
|
|
}
|
|
};
|
|
this._onLoadComplete = () => {
|
|
if (this._loadingText) {
|
|
this._loadingText.destroy();
|
|
this._loadingText = null;
|
|
}
|
|
this.load.off('progress', this._onLoadProgress);
|
|
this.load.off('complete', this._onLoadComplete);
|
|
};
|
|
this.load.on('progress', this._onLoadProgress);
|
|
this.load.on('complete', this._onLoadComplete);
|
|
}
|
|
|
|
create() {
|
|
this.selectedImageIdx = null;
|
|
this.selectedPieces = 60;
|
|
this.selectedBg = BACKGROUNDS[0]; // default to dark wood
|
|
|
|
const W = this.sys.game.config.width;
|
|
const H = this.sys.game.config.height;
|
|
|
|
// Background
|
|
if (!this.textures.exists('main_menu_bg')) {
|
|
this.load.image('main_menu_bg', 'assets/images/ui/main_menu.png');
|
|
this.load.once('complete', () => this.add.image(960, 540, 'main_menu_bg').setDisplaySize(1920, 1080).setDepth(-1));
|
|
this.load.start();
|
|
} else {
|
|
this.add.image(960, 540, 'main_menu_bg').setDisplaySize(1920, 1080);
|
|
}
|
|
|
|
// Title — centered in the top bar area
|
|
this.add.text(960, 45, 'Choose Your Puzzle', {
|
|
fontFamily: 'Georgia, serif',
|
|
fontSize: '58px',
|
|
color: '#ffffff',
|
|
stroke: '#1565c0',
|
|
strokeThickness: 6,
|
|
shadow: { offsetX: 0, offsetY: 0, color: '#1e88e5', blur: 28, fill: true, stroke: true }
|
|
}).setOrigin(0.5, 0.5);
|
|
|
|
// Decorative separator below the title
|
|
const sep = this.add.graphics();
|
|
|
|
// Faint full-width rule
|
|
sep.lineStyle(1, 0x64b5f6, 0.4);
|
|
sep.lineBetween(77, 88, 1843, 88);
|
|
|
|
// Bright centre segment
|
|
sep.lineStyle(2, 0x1e88e5, 1);
|
|
sep.lineBetween(653, 88, 1267, 88);
|
|
|
|
// Diamond accent at the midpoint
|
|
sep.fillStyle(0xffb74d, 1);
|
|
sep.fillTriangle(960, 83, 966, 88, 960, 93);
|
|
sep.fillTriangle(960, 83, 954, 88, 960, 93);
|
|
|
|
// Read puzzle list and thumbnail manifest from cache
|
|
this._puzzleImages = this.cache.json.get('puzzle_list');
|
|
this._thumbnails = this.cache.json.get('thumbnail_list') || [];
|
|
|
|
// 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) {
|
|
toLoad.forEach(t => this.load.image(t.key, t.path));
|
|
|
|
// Show loading text for thumbnail loading
|
|
this._loadingText = this.add.text(960, 540, 'Loading 0%...', {
|
|
fontFamily: 'Arial, sans-serif',
|
|
fontSize: '36px',
|
|
color: '#ffffff',
|
|
stroke: '#000000',
|
|
strokeThickness: 4,
|
|
}).setOrigin(0.5).setDepth(9999);
|
|
|
|
this.load.on('progress', (value) => {
|
|
if (this._loadingText) {
|
|
this._loadingText.setText(`Loading ${Math.round(value * 100)}%...`);
|
|
}
|
|
});
|
|
this.load.once('complete', () => {
|
|
if (this._loadingText) {
|
|
this._loadingText.destroy();
|
|
this._loadingText = null;
|
|
}
|
|
this._buildDomUI();
|
|
});
|
|
this.load.start();
|
|
} else {
|
|
this._buildDomUI();
|
|
}
|
|
}
|
|
|
|
// ─── DOM UI ──────────────────────────────────────────────────────────
|
|
|
|
_syncOverlayToCanvas() {
|
|
if (!this._uiLayer) return;
|
|
const rect = this.sys.game.canvas.getBoundingClientRect();
|
|
Object.assign(this._uiLayer.style, {
|
|
left: rect.left + 'px',
|
|
top: rect.top + 'px',
|
|
transform: `scale(${rect.width / 1920}, ${rect.height / 1080})`,
|
|
});
|
|
}
|
|
|
|
_buildDomUI() {
|
|
this._uiLayer = document.createElement('div');
|
|
Object.assign(this._uiLayer.style, {
|
|
position: 'fixed',
|
|
width: '1920px',
|
|
height: '1080px',
|
|
transformOrigin: 'top left',
|
|
pointerEvents: 'none',
|
|
zIndex: '10',
|
|
});
|
|
document.body.appendChild(this._uiLayer);
|
|
this._syncOverlayToCanvas();
|
|
|
|
// Keep overlay aligned when the window resizes
|
|
this._onResizeScale = () => this._syncOverlayToCanvas();
|
|
this.scale.on('resize', this._onResizeScale);
|
|
window.addEventListener('resize', this._onResizeScale);
|
|
|
|
this._injectStyles();
|
|
this._buildPuzzleGrid(this._uiLayer);
|
|
this._buildControlsBar(this._uiLayer);
|
|
|
|
this._refreshStartButton();
|
|
this.events.once('shutdown', () => {
|
|
if (this._onResizeScale) {
|
|
this.scale.off('resize', this._onResizeScale);
|
|
window.removeEventListener('resize', this._onResizeScale);
|
|
}
|
|
this._destroyDomUI();
|
|
});
|
|
}
|
|
|
|
_injectStyles() {
|
|
this._styleEl = document.createElement('style');
|
|
this._styleEl.textContent = `
|
|
@keyframes iPuzzleSpinBorder {
|
|
from { transform: translate(-50%, -50%) rotate(0deg); }
|
|
to { transform: translate(-50%, -50%) rotate(360deg); }
|
|
}
|
|
@keyframes iPuzzleShimmer {
|
|
0% { transform: translateX(-100%); }
|
|
25% { transform: translateX(200%); }
|
|
100% { transform: translateX(200%); }
|
|
}
|
|
`;
|
|
document.head.appendChild(this._styleEl);
|
|
}
|
|
|
|
_buildPuzzleGrid(uiLayer) {
|
|
// Scrollable area: sits below the Phaser title (top 10%) and above the controls bar (bottom 28%)
|
|
const scrollArea = document.createElement('div');
|
|
Object.assign(scrollArea.style, {
|
|
position: 'absolute',
|
|
top: '10%',
|
|
left: '5%',
|
|
width: '90%',
|
|
height: '62%',
|
|
overflowY: 'auto',
|
|
overflowX: 'hidden',
|
|
pointerEvents: 'auto',
|
|
});
|
|
|
|
const grid = document.createElement('div');
|
|
Object.assign(grid.style, {
|
|
display: 'grid',
|
|
gridTemplateColumns: 'repeat(3, 1fr)',
|
|
gap: '1.5vmin',
|
|
padding: '1vmin',
|
|
});
|
|
|
|
this._cardEls = this._puzzleImages.map((img, i) => {
|
|
const card = document.createElement('div');
|
|
Object.assign(card.style, {
|
|
cursor: 'pointer',
|
|
borderRadius: '8px',
|
|
overflow: 'hidden',
|
|
transition: 'transform 0.2s ease',
|
|
background: 'rgba(10, 10, 30, 0.8)',
|
|
position: 'relative',
|
|
});
|
|
|
|
// Spinning gradient element for selection emitter (hidden by default)
|
|
const spinner = document.createElement('div');
|
|
Object.assign(spinner.style, {
|
|
position: 'absolute',
|
|
top: '50%',
|
|
left: '50%',
|
|
width: '200%',
|
|
height: '200%',
|
|
background: 'conic-gradient(from 0deg, transparent 0%, #f57c00 5%, #ffb74d 10%, #f57c00 15%, transparent 20%, transparent 50%, #f57c00 55%, #ffb74d 60%, #f57c00 65%, transparent 70%)',
|
|
animation: 'iPuzzleSpinBorder 2.5s linear infinite',
|
|
display: 'none',
|
|
zIndex: '0',
|
|
});
|
|
card._spinner = spinner;
|
|
|
|
// Content wrapper sits above the spinner
|
|
const contentWrap = document.createElement('div');
|
|
Object.assign(contentWrap.style, {
|
|
position: 'relative',
|
|
zIndex: '1',
|
|
background: 'rgba(10, 10, 30, 0.8)',
|
|
borderRadius: '5px',
|
|
overflow: 'hidden',
|
|
margin: '3px',
|
|
transition: 'box-shadow 0.15s',
|
|
});
|
|
card._contentWrap = contentWrap;
|
|
|
|
const thumb = document.createElement('img');
|
|
const thumbEntry = this._thumbByPuzzleKey[img.key];
|
|
thumb.src = thumbEntry ? thumbEntry.path : img.path;
|
|
Object.assign(thumb.style, {
|
|
display: 'block',
|
|
width: '100%',
|
|
aspectRatio: '16 / 9',
|
|
objectFit: 'cover',
|
|
});
|
|
|
|
const label = document.createElement('div');
|
|
Object.assign(label.style, {
|
|
padding: '0.5vmin 0.8vmin',
|
|
color: '#e0e0e0',
|
|
fontSize: '1.5vmin',
|
|
fontFamily: 'Arial, sans-serif',
|
|
textAlign: 'center',
|
|
});
|
|
label.textContent = img.label;
|
|
|
|
// Shimmer overlay (hidden by default)
|
|
const shimmer = document.createElement('div');
|
|
Object.assign(shimmer.style, {
|
|
position: 'absolute',
|
|
top: '0',
|
|
left: '0',
|
|
width: '100%',
|
|
height: '100%',
|
|
background: 'linear-gradient(110deg, transparent 20%, rgba(255,255,255,0.12) 35%, rgba(255,255,255,0.25) 50%, rgba(255,255,255,0.12) 65%, transparent 80%)',
|
|
animation: 'iPuzzleShimmer 4s ease-in-out infinite',
|
|
pointerEvents: 'none',
|
|
zIndex: '2',
|
|
display: 'none',
|
|
});
|
|
card._shimmer = shimmer;
|
|
|
|
contentWrap.appendChild(thumb);
|
|
contentWrap.appendChild(label);
|
|
contentWrap.appendChild(shimmer);
|
|
card.appendChild(spinner);
|
|
card.appendChild(contentWrap);
|
|
|
|
card.addEventListener('mouseenter', () => {
|
|
card.style.transform = 'scale(1.05)';
|
|
if (this.selectedImageIdx !== i) {
|
|
contentWrap.style.boxShadow = '0 0 0 2px #64b5f6';
|
|
}
|
|
});
|
|
card.addEventListener('mouseleave', () => {
|
|
card.style.transform = 'scale(1)';
|
|
if (this.selectedImageIdx !== i) {
|
|
contentWrap.style.boxShadow = '';
|
|
}
|
|
});
|
|
card.addEventListener('click', () => this._selectImage(i));
|
|
|
|
grid.appendChild(card);
|
|
return card;
|
|
});
|
|
|
|
scrollArea.appendChild(grid);
|
|
uiLayer.appendChild(scrollArea);
|
|
}
|
|
|
|
_buildControlsBar(uiLayer) {
|
|
const bar = document.createElement('div');
|
|
Object.assign(bar.style, {
|
|
position: 'absolute',
|
|
bottom: '0',
|
|
left: '0',
|
|
width: '100%',
|
|
height: '28%',
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
gap: '1.5vmin',
|
|
borderTop: '1px solid rgba(139, 109, 78, 0.4)',
|
|
background: 'rgba(42, 28, 16, 0.88)',
|
|
pointerEvents: 'auto',
|
|
});
|
|
|
|
// "Player Name" row
|
|
const nameRow = document.createElement('div');
|
|
Object.assign(nameRow.style, {
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
gap: '1.5vmin',
|
|
});
|
|
|
|
const nameLabel = document.createElement('div');
|
|
Object.assign(nameLabel.style, {
|
|
color: '#e0e0e0',
|
|
fontSize: '1.8vmin',
|
|
fontFamily: 'Arial, sans-serif',
|
|
});
|
|
nameLabel.textContent = 'Player Name';
|
|
|
|
this._nameInput = document.createElement('input');
|
|
Object.assign(this._nameInput.style, {
|
|
background: 'rgba(10, 10, 30, 0.9)',
|
|
color: '#ffffff',
|
|
border: '2px solid #1e88e5',
|
|
borderRadius: '4px',
|
|
padding: '0.5vmin 1.2vmin',
|
|
fontSize: '1.8vmin',
|
|
fontFamily: 'Arial, sans-serif',
|
|
outline: 'none',
|
|
width: '18vmin',
|
|
});
|
|
this._nameInput.maxLength = 16;
|
|
this._nameInput.placeholder = 'Your name';
|
|
this._nameInput.autocomplete = 'off';
|
|
this._nameInput.value = localStorage.getItem('ipuzzle_playerName') || '';
|
|
this._nameInput.addEventListener('input', () => {
|
|
localStorage.setItem('ipuzzle_playerName', this._nameInput.value.trim());
|
|
this._refreshStartButton();
|
|
});
|
|
|
|
const nameHint = document.createElement('div');
|
|
Object.assign(nameHint.style, {
|
|
color: '#90a4ae',
|
|
fontSize: '1.3vmin',
|
|
fontFamily: 'Arial, sans-serif',
|
|
});
|
|
nameHint.textContent = 'Shown to other players in multiplayer';
|
|
|
|
nameRow.appendChild(nameLabel);
|
|
nameRow.appendChild(this._nameInput);
|
|
nameRow.appendChild(nameHint);
|
|
|
|
// "Difficulty" label
|
|
const diffLabel = document.createElement('div');
|
|
Object.assign(diffLabel.style, {
|
|
color: '#e0e0e0',
|
|
fontSize: '1.8vmin',
|
|
fontFamily: 'Arial, sans-serif',
|
|
});
|
|
diffLabel.textContent = 'Difficulty';
|
|
|
|
// Difficulty buttons row
|
|
const diffRow = document.createElement('div');
|
|
Object.assign(diffRow.style, {
|
|
display: 'flex',
|
|
gap: '2vmin',
|
|
});
|
|
|
|
this._diffBtnEls = DIFFICULTIES.map((diff, i) => {
|
|
const btn = document.createElement('button');
|
|
btn.textContent = diff.label;
|
|
const isDefault = diff.pieces === this.selectedPieces;
|
|
Object.assign(btn.style, {
|
|
padding: '0.7vmin 2.5vmin',
|
|
background: isDefault ? '#1565c0' : 'rgba(10, 10, 30, 0.8)',
|
|
color: '#e0e0e0',
|
|
border: '1px solid ' + (isDefault ? '#64b5f6' : 'rgba(100, 181, 246, 0.3)'),
|
|
borderRadius: '4px',
|
|
fontSize: '1.6vmin',
|
|
fontFamily: 'Arial, sans-serif',
|
|
cursor: 'pointer',
|
|
transition: 'background 0.1s, border-color 0.1s',
|
|
});
|
|
btn.addEventListener('mouseenter', () => {
|
|
if (this.selectedPieces !== diff.pieces) btn.style.background = '#1565c0';
|
|
});
|
|
btn.addEventListener('mouseleave', () => {
|
|
if (this.selectedPieces !== diff.pieces) btn.style.background = 'rgba(10, 10, 30, 0.8)';
|
|
});
|
|
btn.addEventListener('click', () => this._selectDifficulty(i, diff.pieces));
|
|
diffRow.appendChild(btn);
|
|
return { el: btn, pieces: diff.pieces };
|
|
});
|
|
|
|
// "Background" label
|
|
const bgLabel = document.createElement('div');
|
|
Object.assign(bgLabel.style, {
|
|
color: '#e0e0e0',
|
|
fontSize: '1.8vmin',
|
|
fontFamily: 'Arial, sans-serif',
|
|
});
|
|
bgLabel.textContent = 'Background';
|
|
|
|
// Background buttons row
|
|
const bgRow = document.createElement('div');
|
|
Object.assign(bgRow.style, {
|
|
display: 'flex',
|
|
gap: '2vmin',
|
|
});
|
|
|
|
this._bgBtnEls = BACKGROUNDS.map((bg, i) => {
|
|
const btn = document.createElement('button');
|
|
btn.textContent = bg.label;
|
|
const isDefault = bg.key === this.selectedBg.key;
|
|
Object.assign(btn.style, {
|
|
padding: '0.7vmin 2.5vmin',
|
|
background: isDefault ? '#1565c0' : 'rgba(10, 10, 30, 0.8)',
|
|
color: '#e0e0e0',
|
|
border: '1px solid ' + (isDefault ? '#64b5f6' : 'rgba(100, 181, 246, 0.3)'),
|
|
borderRadius: '4px',
|
|
fontSize: '1.6vmin',
|
|
fontFamily: 'Arial, sans-serif',
|
|
cursor: 'pointer',
|
|
transition: 'background 0.1s, border-color 0.1s',
|
|
});
|
|
btn.addEventListener('mouseenter', () => {
|
|
if (this.selectedBg.key !== bg.key) btn.style.background = '#1565c0';
|
|
});
|
|
btn.addEventListener('mouseleave', () => {
|
|
if (this.selectedBg.key !== bg.key) btn.style.background = 'rgba(10, 10, 30, 0.8)';
|
|
});
|
|
btn.addEventListener('click', () => this._selectBackground(i));
|
|
bgRow.appendChild(btn);
|
|
return { el: btn, bg };
|
|
});
|
|
|
|
// Action buttons row (Back left, Start right)
|
|
const actionRow = document.createElement('div');
|
|
Object.assign(actionRow.style, {
|
|
display: 'flex',
|
|
justifyContent: 'center',
|
|
gap: '4vmin',
|
|
width: '55%',
|
|
});
|
|
|
|
const backBtn = this._makeDomBtn('← Back', 'rgba(10, 10, 30, 0.8)', '#c62828', '#ef5350');
|
|
backBtn.style.color = '#e0e0e0';
|
|
backBtn.style.flex = '1';
|
|
backBtn.addEventListener('click', () => this.scene.start('MainMenuScene'));
|
|
|
|
this._startBtnEl = this._makeDomBtn('Start Puzzle →', 'rgba(10, 10, 30, 0.8)', 'rgba(10, 10, 30, 0.8)', 'rgba(100, 100, 100, 0.3)');
|
|
Object.assign(this._startBtnEl.style, {
|
|
flex: '1',
|
|
color: '#666666',
|
|
cursor: 'not-allowed',
|
|
});
|
|
this._startBtnEl.disabled = true;
|
|
this._startBtnEl._bgNormal = 'rgba(10, 10, 30, 0.8)';
|
|
this._startBtnEl._bgHover = 'rgba(10, 10, 30, 0.8)';
|
|
this._startBtnEl.addEventListener('click', () => this._startPuzzle());
|
|
|
|
actionRow.appendChild(backBtn);
|
|
actionRow.appendChild(this._startBtnEl);
|
|
|
|
bar.appendChild(nameRow);
|
|
bar.appendChild(diffLabel);
|
|
bar.appendChild(diffRow);
|
|
bar.appendChild(bgLabel);
|
|
bar.appendChild(bgRow);
|
|
bar.appendChild(actionRow);
|
|
uiLayer.appendChild(bar);
|
|
}
|
|
|
|
// ─── Selection ───────────────────────────────────────────────────────
|
|
|
|
_selectImage(idx) {
|
|
this.selectedImageIdx = idx;
|
|
this._cardEls.forEach((card, i) => {
|
|
const sel = i === idx;
|
|
card._spinner.style.display = sel ? 'block' : 'none';
|
|
card._shimmer.style.display = sel ? 'block' : 'none';
|
|
card._contentWrap.style.boxShadow = sel ? '0 0 8px rgba(245, 124, 0, 0.4)' : '';
|
|
});
|
|
this._refreshStartButton();
|
|
}
|
|
|
|
_selectDifficulty(idx, pieces) {
|
|
this.selectedPieces = pieces;
|
|
this._diffBtnEls.forEach(({ el, pieces: p }) => {
|
|
const sel = p === pieces;
|
|
el.style.background = sel ? '#1565c0' : 'rgba(10, 10, 30, 0.8)';
|
|
el.style.borderColor = sel ? '#64b5f6' : 'rgba(100, 181, 246, 0.3)';
|
|
});
|
|
this._refreshStartButton();
|
|
}
|
|
|
|
_selectBackground(idx) {
|
|
this.selectedBg = BACKGROUNDS[idx];
|
|
this._bgBtnEls.forEach(({ el, bg }) => {
|
|
const sel = bg.key === this.selectedBg.key;
|
|
el.style.background = sel ? '#1565c0' : 'rgba(10, 10, 30, 0.8)';
|
|
el.style.borderColor = sel ? '#64b5f6' : 'rgba(100, 181, 246, 0.3)';
|
|
});
|
|
}
|
|
|
|
_refreshStartButton() {
|
|
if (!this._startBtnEl) return;
|
|
const hasName = this._nameInput && this._nameInput.value.trim().length > 0;
|
|
const ready = this.selectedImageIdx !== null && this.selectedPieces !== null && hasName;
|
|
this._startBtnEl.disabled = !ready;
|
|
this._startBtnEl._bgNormal = ready ? '#2e7d32' : 'rgba(10, 10, 30, 0.8)';
|
|
this._startBtnEl._bgHover = ready ? '#43a047' : 'rgba(10, 10, 30, 0.8)';
|
|
Object.assign(this._startBtnEl.style, {
|
|
background: this._startBtnEl._bgNormal,
|
|
borderColor: ready ? '#66bb6a' : 'rgba(100, 100, 100, 0.3)',
|
|
color: ready ? '#ffffff' : '#666666',
|
|
cursor: ready ? 'pointer' : 'not-allowed',
|
|
});
|
|
}
|
|
|
|
// ─── Helpers ─────────────────────────────────────────────────────────
|
|
|
|
_makeDomBtn(label, bgNormal, bgHover, borderColor) {
|
|
const btn = document.createElement('button');
|
|
btn.textContent = label;
|
|
btn._bgNormal = bgNormal;
|
|
btn._bgHover = bgHover;
|
|
Object.assign(btn.style, {
|
|
padding: '0.8vmin 0',
|
|
background: bgNormal,
|
|
color: '#e0e0e0',
|
|
border: `1px solid ${borderColor}`,
|
|
borderRadius: '4px',
|
|
fontSize: '1.7vmin',
|
|
fontFamily: 'Arial, sans-serif',
|
|
cursor: 'pointer',
|
|
whiteSpace: 'nowrap',
|
|
textAlign: 'center',
|
|
});
|
|
btn.addEventListener('mouseenter', () => { if (!btn.disabled) btn.style.background = btn._bgHover; });
|
|
btn.addEventListener('mouseleave', () => { if (!btn.disabled) btn.style.background = btn._bgNormal; });
|
|
return btn;
|
|
}
|
|
|
|
_destroyDomUI() {
|
|
if (this._uiLayer && this._uiLayer.parentNode) {
|
|
this._uiLayer.parentNode.removeChild(this._uiLayer);
|
|
}
|
|
this._uiLayer = null;
|
|
if (this._styleEl && this._styleEl.parentNode) {
|
|
this._styleEl.parentNode.removeChild(this._styleEl);
|
|
}
|
|
this._styleEl = null;
|
|
}
|
|
|
|
// ─── Start puzzle ────────────────────────────────────────────────────
|
|
|
|
_startPuzzle() {
|
|
const name = this._nameInput ? this._nameInput.value.trim() : '';
|
|
if (this.selectedImageIdx === null || this.selectedPieces === null || !name) return;
|
|
const img = this._puzzleImages[this.selectedImageIdx];
|
|
this.scene.start('PuzzleScene', {
|
|
imageKey: img.key,
|
|
imagePath: img.path,
|
|
pieceCount: this.selectedPieces,
|
|
roomCode: generateRoomCode(),
|
|
bgKey: this.selectedBg.key,
|
|
bgPath: this.selectedBg.path,
|
|
playerName: name,
|
|
});
|
|
}
|
|
}
|