Adds level prefabs, theme assets, audio, extensions, and deployment scripts for the Unity WebGL migration. Co-authored-by: Cursor <cursoragent@cursor.com>
97 lines
3.3 KiB
JavaScript
97 lines
3.3 KiB
JavaScript
'use strict';
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const { pathToFileURL } = require('url');
|
|
|
|
/** 需要贴图预览的表单字段 id */
|
|
const HUD_ICON_KEYS = [
|
|
'navigation', 'revert', 'speed1', 'speed2', 'speed4',
|
|
'zoomIn', 'zoomOut', 'audioOn', 'audioOff',
|
|
];
|
|
|
|
const PREVIEW_INPUT_IDS = [
|
|
'tc-background',
|
|
...['playerFront', 'playerBack', 'vehicleNorth', 'vehicleEast', 'vehicleSouth', 'vehicleWest', 'prop', 'propGround'].map((k) => `tc-ent-${k}`),
|
|
...['Baseblock', 'JumpBlock', 'WallBlock', 'borderDecor'].map((k) => `tc-tile-${k}`),
|
|
...HUD_ICON_KEYS.map((k) => `tc-hud-${k}`),
|
|
];
|
|
|
|
function textureFsPath(projectRoot, texRel) {
|
|
const normalized = String(texRel || '').trim();
|
|
if (!normalized) return null;
|
|
let base = normalized.replace(/\\/g, '/');
|
|
if (base.startsWith('assets/resources/')) base = base.slice('assets/resources/'.length);
|
|
if (base.endsWith('.png')) base = base.slice(0, -4);
|
|
if (!base.startsWith('textures/')) base = `textures/${base}`;
|
|
return path.join(projectRoot, 'assets/resources', `${base}.png`);
|
|
}
|
|
|
|
function updatePreviewBox(previewEl, texRel, projectRoot) {
|
|
if (!previewEl) return;
|
|
let img = previewEl.querySelector('img');
|
|
let empty = previewEl.querySelector('.tc-preview-empty');
|
|
const fsPath = textureFsPath(projectRoot, texRel);
|
|
if (!fsPath || !fs.existsSync(fsPath)) {
|
|
previewEl.classList.add('empty');
|
|
if (img) {
|
|
img.style.display = 'none';
|
|
img.removeAttribute('src');
|
|
}
|
|
if (empty) empty.style.display = 'flex';
|
|
previewEl.title = texRel ? `未找到: ${fsPath || texRel}` : '';
|
|
return;
|
|
}
|
|
previewEl.classList.remove('empty');
|
|
if (!img) {
|
|
img = document.createElement('img');
|
|
img.alt = '';
|
|
previewEl.appendChild(img);
|
|
}
|
|
if (!empty) {
|
|
empty = document.createElement('span');
|
|
empty.className = 'tc-preview-empty';
|
|
empty.textContent = '无图';
|
|
previewEl.appendChild(empty);
|
|
}
|
|
empty.style.display = 'none';
|
|
img.style.display = 'block';
|
|
img.src = `${pathToFileURL(fsPath).href}?v=${fs.statSync(fsPath).mtimeMs}`;
|
|
previewEl.title = fsPath;
|
|
}
|
|
|
|
function refreshAllPreviews(formRoot, projectRoot) {
|
|
if (!formRoot) return;
|
|
for (const id of PREVIEW_INPUT_IDS) {
|
|
const input = formRoot.querySelector(`#${id}`);
|
|
const preview = formRoot.querySelector(`#${id}-preview`);
|
|
if (preview && input) {
|
|
updatePreviewBox(preview, input.value, projectRoot);
|
|
}
|
|
}
|
|
}
|
|
|
|
function bindPreviewInputs(formRoot, projectRoot, onRefresh) {
|
|
if (!formRoot) return;
|
|
for (const id of PREVIEW_INPUT_IDS) {
|
|
const input = formRoot.querySelector(`#${id}`);
|
|
if (!input || input._tcPreviewBound) continue;
|
|
input._tcPreviewBound = true;
|
|
const handler = () => {
|
|
const preview = formRoot.querySelector(`#${id}-preview`);
|
|
updatePreviewBox(preview, input.value, projectRoot);
|
|
if (onRefresh) onRefresh();
|
|
};
|
|
input.addEventListener('change', handler);
|
|
input.addEventListener('blur', handler);
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
PREVIEW_INPUT_IDS,
|
|
textureFsPath,
|
|
updatePreviewBox,
|
|
refreshAllPreviews,
|
|
bindPreviewInputs,
|
|
};
|