Files
刘宇飞 d393302388 Complete Cocos Creator port with level bundles, themes, and tooling.
Adds level prefabs, theme assets, audio, extensions, and deployment scripts for the Unity WebGL migration.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-16 15:30:58 +08:00

165 lines
5.2 KiB
JavaScript

'use strict';
const fs = require('fs');
const path = require('path');
const prefabSync = require('./prefab-sync');
const bakeIgnore = require('./bake-ignore');
const IMPORT_DEBOUNCE_MS = 900;
/** levelId → debounce timer */
const importTimers = new Map();
let prefabWatcher = null;
let broadcastUnsubs = [];
function addBroadcastListener(message, handler) {
const fn = Editor.Message.__protected__?.addBroadcastListener || Editor.Message.addBroadcastListener;
if (typeof fn !== 'function') {
console.warn(`[level-map-editor] addBroadcastListener unavailable for ${message}`);
return null;
}
fn.call(Editor.Message, message, handler);
return () => {
const rm = Editor.Message.__protected__?.removeBroadcastListener || Editor.Message.removeBroadcastListener;
if (typeof rm === 'function') rm.call(Editor.Message, message, handler);
};
}
function scheduleImportFromPrefab(levelId, source) {
if (!levelId || bakeIgnore.shouldIgnoreImport(levelId)) return;
if (importTimers.has(levelId)) clearTimeout(importTimers.get(levelId));
importTimers.set(
levelId,
setTimeout(() => {
importTimers.delete(levelId);
runImportFromPrefab(levelId, source);
}, IMPORT_DEBOUNCE_MS),
);
}
function runImportFromPrefab(levelId, source) {
if (bakeIgnore.shouldIgnoreImport(levelId)) return null;
try {
const result = prefabSync.importLevelFromPrefab(levelId);
if (!result.ok) {
console.warn(`[level-map-editor] prefab import skipped Level${levelId}: ${result.reason}`);
return result;
}
console.log(
`[level-map-editor] prefab → JSON Level${levelId} (ground ${result.ground}, border ${result.border}, theme ${result.theme || '-'}) [${source}]`,
);
void prefabSync.refreshDatabaseAsset();
Editor.Message.broadcast('level-map-editor:prefab-synced', {
levelId,
source,
ground: result.ground,
border: result.border,
theme: result.theme,
});
return result;
} catch (e) {
console.error(`[level-map-editor] prefab import failed Level${levelId}`, e);
return { ok: false, reason: e.message, levelId };
}
}
async function handleAssetDbEvent(...args) {
let url = '';
for (const arg of args) {
if (typeof arg === 'string') {
if (arg.includes('level-prefabs/Level') && arg.endsWith('.prefab')) url = arg;
else if (arg.startsWith('db://') && arg.includes('Level') && arg.endsWith('.prefab')) url = arg;
} else if (arg && typeof arg === 'object') {
if (typeof arg.url === 'string') url = arg.url;
else if (typeof arg.path === 'string') url = arg.path;
}
}
if (!url && typeof args[0] === 'string' && !args[0].includes('/')) {
try {
url = await Editor.Message.request('asset-db', 'query-url', args[0]);
} catch {
/* ignore */
}
}
const levelId = prefabSync.extractLevelIdFromUrl(url);
if (levelId) scheduleImportFromPrefab(levelId, 'asset-db');
}
function watchPrefabDirectory() {
const dir = path.join(Editor.Project.path, prefabSync.PREFAB_DIR_REL);
if (!fs.existsSync(dir)) return;
try {
prefabWatcher = fs.watch(dir, (_event, filename) => {
const levelId = prefabSync.extractLevelIdFromFilename(filename);
if (levelId) scheduleImportFromPrefab(levelId, 'fs-watch');
});
} catch (e) {
console.warn('[level-map-editor] prefab fs.watch failed', e);
}
}
function registerAssetListeners() {
const events = [
'asset-db:asset-change',
'asset-db:asset-changed',
'asset-db:assets-changed',
'asset-db:asset-add',
'asset-db:assets-created',
];
for (const message of events) {
const unsub = addBroadcastListener(message, (...args) => {
void handleAssetDbEvent(...args);
});
if (unsub) broadcastUnsubs.push(unsub);
}
}
exports.methods = {
openPanel() {
const id = 'level-map-editor';
try {
Editor.Panel.open(id);
console.log(`[${id}] Panel.open('${id}')`);
} catch (e) {
console.error(`[${id}] open failed:`, e);
}
},
markBakePending(levelId, durationMs) {
bakeIgnore.markBakePending(levelId, durationMs);
},
importLevelFromPrefab(levelId) {
return runImportFromPrefab(Number(levelId), 'manual');
},
};
exports.load = function () {
prefabSync.clearSpriteUuidCache();
registerAssetListeners();
watchPrefabDirectory();
console.log('[level-map-editor] extension loaded (v1.7.1, prefab→JSON sync enabled)');
};
exports.unload = function () {
for (const unsub of broadcastUnsubs) {
try {
unsub();
} catch {
/* ignore */
}
}
broadcastUnsubs = [];
if (prefabWatcher) {
try {
prefabWatcher.close();
} catch {
/* ignore */
}
prefabWatcher = null;
}
for (const timer of importTimers.values()) clearTimeout(timer);
importTimers.clear();
console.log('[level-map-editor] extension unloaded');
};