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>
This commit is contained in:
164
extensions/level-map-editor/dist/main.js
vendored
Normal file
164
extensions/level-map-editor/dist/main.js
vendored
Normal file
@@ -0,0 +1,164 @@
|
||||
'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');
|
||||
};
|
||||
Reference in New Issue
Block a user