Bug fixes (verified against Cocos Creator 3.8.8): - diagnostics: shell:true for .cmd/.bat on Windows (T125/T126) - prefabs: duplicatePrefab target resolves under assets/ (T420) - cocos-project: add preview.open candidate for 3.8.8 (T444) - assets-advanced: detect directory assets in inspectAssetDependencies (T110) - scene: improve component-not-found errors with compilation hint (T429) Documentation improvements: - tool-registry: add enum and injected vars to execute_javascript, path format examples, asset ref limitation note - resources: add cocos://mcp/execute-context resource with variables, patterns, pitfalls New tools (core 37->38, full 101->110): - scene-management: create_scene, query_scene_state (core), copy_paste_node, rename_node, reparent_node - scripts: create_script with component/plain templates - prefabs: create_prefab - assets-advanced: batch_asset_ops, find_unused_assets
149 lines
4.4 KiB
JavaScript
149 lines
4.4 KiB
JavaScript
'use strict';
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const { resolveProjectPath } = require('../path-safety');
|
|
const { refreshAssets } = require('./files');
|
|
|
|
const PRIMITIVE_TYPES = new Set(['Number', 'String', 'Boolean']);
|
|
|
|
function generateComponentTemplate(className, properties = []) {
|
|
const ccTypes = new Set(['Component']);
|
|
const propLines = [];
|
|
|
|
for (const prop of properties) {
|
|
const propName = String(prop.name || 'property').trim();
|
|
const propType = String(prop.type || 'Number').trim();
|
|
const defaultValue = prop.default !== undefined ? prop.default : getDefaultForType(propType);
|
|
|
|
if (!PRIMITIVE_TYPES.has(propType)) {
|
|
ccTypes.add(propType);
|
|
}
|
|
|
|
if (PRIMITIVE_TYPES.has(propType)) {
|
|
propLines.push(` @property`);
|
|
propLines.push(` ${propName}: ${typeToTs(propType)} = ${defaultValue};`);
|
|
} else {
|
|
propLines.push(` @property(${propType})`);
|
|
propLines.push(` ${propName}: ${propType} | null = null;`);
|
|
}
|
|
}
|
|
|
|
const importTypes = Array.from(ccTypes).join(', ');
|
|
const propsBlock = propLines.length > 0
|
|
? propLines.join('\n')
|
|
: ' // Add properties here';
|
|
|
|
return `import { _decorator, ${importTypes} } from 'cc';
|
|
const { ccclass, property } = _decorator;
|
|
|
|
@ccclass('${className}')
|
|
export class ${className} extends Component {
|
|
${propsBlock}
|
|
|
|
start() {
|
|
}
|
|
|
|
update(deltaTime: number) {
|
|
}
|
|
}
|
|
`;
|
|
}
|
|
|
|
function generatePlainTemplate(className) {
|
|
return `export class ${className} {
|
|
}
|
|
`;
|
|
}
|
|
|
|
function getDefaultForType(type) {
|
|
switch (type) {
|
|
case 'Number': return '0';
|
|
case 'String': return "''";
|
|
case 'Boolean': return 'false';
|
|
default: return 'null';
|
|
}
|
|
}
|
|
|
|
function typeToTs(type) {
|
|
switch (type) {
|
|
case 'Number': return 'number';
|
|
case 'String': return 'string';
|
|
case 'Boolean': return 'boolean';
|
|
default: return type;
|
|
}
|
|
}
|
|
|
|
function createScriptTools({ createSchema, getRuntimeContext }) {
|
|
return [
|
|
{
|
|
name: 'create_script',
|
|
profile: 'full',
|
|
description: '[core] Create a new TypeScript component script with a standard Cocos template.',
|
|
inputSchema: createSchema(
|
|
{
|
|
scriptName: { type: 'string', description: 'Class name in PascalCase, e.g. PlayerController.' },
|
|
savePath: { type: 'string', description: 'Asset-db URL or project path, e.g. db://assets/scripts/PlayerController.ts.' },
|
|
template: {
|
|
type: 'string',
|
|
enum: ['component', 'plain'],
|
|
description: 'Script template. Default: component.',
|
|
},
|
|
properties: {
|
|
type: 'array',
|
|
items: { type: 'object' },
|
|
description: 'Optional @property declarations. Each item: { name, type, default }. Type can be Number, String, Boolean, or a CC class name like Label, Sprite, Node.',
|
|
},
|
|
},
|
|
['scriptName', 'savePath']
|
|
),
|
|
handler: async (args) => {
|
|
const scriptName = String(args.scriptName || '').trim();
|
|
let savePath = String(args.savePath || '').trim();
|
|
if (!scriptName) {
|
|
throw new Error('scriptName is required.');
|
|
}
|
|
if (!savePath) {
|
|
throw new Error('savePath is required.');
|
|
}
|
|
if (!savePath.endsWith('.ts')) {
|
|
throw new Error('savePath must end with .ts');
|
|
}
|
|
if (savePath.startsWith('db://')) {
|
|
savePath = savePath.slice('db://'.length);
|
|
}
|
|
|
|
const templateType = args.template === 'plain' ? 'plain' : 'component';
|
|
const properties = Array.isArray(args.properties) ? args.properties : [];
|
|
|
|
const content = templateType === 'plain'
|
|
? generatePlainTemplate(scriptName)
|
|
: generateComponentTemplate(scriptName, properties);
|
|
|
|
const { projectPath } = getRuntimeContext();
|
|
const fullPath = resolveProjectPath(projectPath, savePath);
|
|
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
|
fs.writeFileSync(fullPath, content, 'utf8');
|
|
|
|
const refreshMessage = await refreshAssets(projectPath, fullPath);
|
|
|
|
return {
|
|
created: true,
|
|
scriptName,
|
|
savePath,
|
|
template: templateType,
|
|
className: scriptName,
|
|
contentLength: content.length,
|
|
refreshMessage,
|
|
};
|
|
},
|
|
},
|
|
];
|
|
}
|
|
|
|
module.exports = {
|
|
createScriptTools,
|
|
generateComponentTemplate,
|
|
generatePlainTemplate,
|
|
};
|