91b878f347
- Added 6 Quaternius weapon models (Pistol, Revolver, Rifle, Shotgun, P90, SniperRifle) - Created weapon resource files with balanced stats - Wired Pistol + Rifle into player.tscn replacing Kenney blasters - Fixed WASD input (matched input map action names) - Fixed Escape key toggles mouse capture - Added Audio autoload singleton - Fixed broken sound asset paths across all scripts - Fixed all scene ext_resource text paths (nested under assets/kenney-fps/) - Fixed all .import source_file paths - Deleted stale .import files (will be regenerated by editor on open)
342 lines
17 KiB
JavaScript
342 lines
17 KiB
JavaScript
import { join } from 'path';
|
|
import { existsSync } from 'fs';
|
|
import { normalizeParameters, validateSubPath, validateNodePath, createErrorResponse, validateProjectArgs, validateSceneArgs, } from '../utils/godot-runner.js';
|
|
import { executeSceneOp } from '../utils/handler-helpers.js';
|
|
export const sceneToolDefinitions = [
|
|
{
|
|
name: 'create_scene',
|
|
description: 'Create a new Godot scene file with a single root node. Writes a fresh .tscn at scenePath. Use when starting a new scene from scratch; for adding nodes to an existing scene, use add_node. rootNodeType defaults to Node2D — pass "Node3D" for 3D scenes or "Control" for UI. Saves automatically. Overwrites silently if the file already exists. Returns: success and the scenePath that was written.',
|
|
annotations: { idempotentHint: true },
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
projectPath: { type: 'string', description: 'Path to the Godot project directory' },
|
|
scenePath: {
|
|
type: 'string',
|
|
description: 'Scene file path relative to the project (e.g. "scenes/main.tscn")',
|
|
},
|
|
rootNodeType: { type: 'string', description: 'Root node type (default: Node2D)' },
|
|
},
|
|
required: ['projectPath', 'scenePath'],
|
|
},
|
|
outputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
success: { type: 'boolean' },
|
|
scenePath: { type: 'string' },
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: 'add_node',
|
|
description: 'Add a node to a Godot scene. Saves automatically. Common spatial properties (position, position3d, rotation, scale, visible, modulate) can be set as top-level params; for any other property, pass it under properties. Vector2/Vector3/Color values auto-convert from {x,y}/{x,y,z}/{r,g,b,a}. parentNodePath defaults to the scene root. Returns a plain-text confirmation message naming the new node and type. Errors if nodeType is not a registered Godot class or parentNodePath does not exist.',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
projectPath: { type: 'string', description: 'Path to the Godot project directory' },
|
|
scenePath: { type: 'string', description: 'Scene file path relative to the project' },
|
|
nodeType: {
|
|
type: 'string',
|
|
description: 'Godot node class to instantiate (e.g. "Sprite2D", "CollisionShape2D", "Label")',
|
|
},
|
|
nodeName: {
|
|
type: 'string',
|
|
description: 'Name for the new node as it appears in the scene tree',
|
|
},
|
|
parentNodePath: {
|
|
type: 'string',
|
|
description: 'Parent node path from scene root (e.g. "root/Player"). Defaults to the root node.',
|
|
},
|
|
position: {
|
|
type: 'object',
|
|
description: 'Vector2 position (e.g. {"x": 100, "y": 200})',
|
|
properties: { x: { type: 'number' }, y: { type: 'number' } },
|
|
},
|
|
position3d: {
|
|
type: 'object',
|
|
description: 'Vector3 position for 3D nodes (e.g. {"x": 0, "y": 1, "z": 0})',
|
|
properties: { x: { type: 'number' }, y: { type: 'number' }, z: { type: 'number' } },
|
|
},
|
|
rotation: { type: 'number', description: 'Rotation in radians' },
|
|
scale: {
|
|
type: 'object',
|
|
description: 'Vector2 scale (e.g. {"x": 2, "y": 2})',
|
|
properties: { x: { type: 'number' }, y: { type: 'number' } },
|
|
},
|
|
visible: { type: 'boolean', description: 'Whether the node is visible' },
|
|
modulate: {
|
|
type: 'object',
|
|
description: 'Color modulation (e.g. {"r": 1, "g": 0, "b": 0, "a": 1})',
|
|
properties: {
|
|
r: { type: 'number' },
|
|
g: { type: 'number' },
|
|
b: { type: 'number' },
|
|
a: { type: 'number' },
|
|
},
|
|
},
|
|
properties: {
|
|
type: 'object',
|
|
description: 'Additional property values as a JSON object. Top-level params (position, rotation, etc.) take precedence over keys in this dict.',
|
|
},
|
|
},
|
|
required: ['projectPath', 'scenePath', 'nodeType', 'nodeName'],
|
|
},
|
|
},
|
|
{
|
|
name: 'load_sprite',
|
|
description: 'Set the texture on an existing Sprite2D, Sprite3D, or TextureRect node. Use this when the node already exists; for new nodes, pass texture via add_node properties. Saves automatically. texturePath must be a real file under projectPath. Returns a plain-text confirmation message naming the loaded texture. Errors if the node is not one of those three classes, or the texture file does not exist.',
|
|
annotations: { idempotentHint: true },
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
projectPath: { type: 'string', description: 'Path to the Godot project directory' },
|
|
scenePath: { type: 'string', description: 'Scene file path relative to the project' },
|
|
nodePath: {
|
|
type: 'string',
|
|
description: 'Path to the target node from scene root (e.g. "root/Player/Sprite2D")',
|
|
},
|
|
texturePath: {
|
|
type: 'string',
|
|
description: 'Path to the texture file relative to the project (e.g. "assets/player.png")',
|
|
},
|
|
},
|
|
required: ['projectPath', 'scenePath', 'nodePath', 'texturePath'],
|
|
},
|
|
},
|
|
{
|
|
name: 'save_scene',
|
|
description: 'Re-pack and save a scene, optionally to a different path (save-as). Most mutations (add_node, set_node_properties, delete_nodes, etc.) auto-save — only use this for save-as via newPath, or to re-canonicalize a hand-edited .tscn. Overwrites silently. Returns a plain-text confirmation naming the save path. Errors if the scene file does not exist.',
|
|
annotations: { idempotentHint: true },
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
projectPath: { type: 'string', description: 'Path to the Godot project directory' },
|
|
scenePath: { type: 'string', description: 'Scene file path relative to the project' },
|
|
newPath: {
|
|
type: 'string',
|
|
description: 'Save to a different path (relative to project) instead of overwriting the original',
|
|
},
|
|
},
|
|
required: ['projectPath', 'scenePath'],
|
|
},
|
|
},
|
|
{
|
|
name: 'export_mesh_library',
|
|
description: 'Export a scene of MeshInstance3D nodes as a MeshLibrary .res file for use in GridMap. Use this when authoring tile palettes for grid-based 3D levels; ignore for 2D or general scene work. The source scene must contain MeshInstance3D children. Pass meshItemNames to export a subset, or omit to export all. Saves the .res to outputPath, overwriting silently. Returns a plain-text confirmation with the exported item count. Errors if the scene contains no valid meshes.',
|
|
annotations: { destructiveHint: true },
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
projectPath: { type: 'string', description: 'Path to the Godot project directory' },
|
|
scenePath: { type: 'string', description: 'Scene file path relative to the project' },
|
|
outputPath: {
|
|
type: 'string',
|
|
description: 'Output path for the MeshLibrary .res file (relative to project)',
|
|
},
|
|
meshItemNames: {
|
|
type: 'array',
|
|
items: { type: 'string' },
|
|
description: 'Names of specific mesh items to export. Omit to export all.',
|
|
},
|
|
},
|
|
required: ['projectPath', 'scenePath', 'outputPath'],
|
|
},
|
|
},
|
|
{
|
|
name: 'batch_scene_operations',
|
|
description: 'Use this instead of chaining add_node / load_sprite / save_scene calls when you have multiple mutations on the same or related scenes — runs in one Godot process (~3s startup avoided per call) and shares an in-memory scene cache, saving once at the end. Each item picks its sub-operation (add_node, load_sprite, save) and supplies its own params; abortOnError stops on first failure (default false continues). Returns: results[] in input order, each tagged with operation and scenePath plus success or error.',
|
|
annotations: { destructiveHint: true },
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
projectPath: { type: 'string', description: 'Path to the Godot project directory' },
|
|
operations: {
|
|
type: 'array',
|
|
description: 'Ordered list of scene operations. Each item has its own operation and scenePath.',
|
|
items: {
|
|
type: 'object',
|
|
properties: {
|
|
operation: {
|
|
type: 'string',
|
|
enum: ['add_node', 'load_sprite', 'save'],
|
|
description: 'The sub-operation to perform',
|
|
},
|
|
scenePath: { type: 'string', description: 'Scene file path for this operation' },
|
|
nodeType: { type: 'string', description: '[add_node] Node class to instantiate' },
|
|
nodeName: { type: 'string', description: '[add_node] Name for the new node' },
|
|
parentNodePath: {
|
|
type: 'string',
|
|
description: '[add_node] Parent node path (defaults to root)',
|
|
},
|
|
properties: { type: 'object', description: '[add_node] Initial property values' },
|
|
nodePath: { type: 'string', description: '[load_sprite] Target node path' },
|
|
texturePath: {
|
|
type: 'string',
|
|
description: '[load_sprite] Texture file path relative to project',
|
|
},
|
|
newPath: {
|
|
type: 'string',
|
|
description: '[save] Save to a different path instead of overwriting',
|
|
},
|
|
},
|
|
required: ['operation'],
|
|
},
|
|
},
|
|
abortOnError: {
|
|
type: 'boolean',
|
|
description: 'Stop processing on first error (default: false)',
|
|
},
|
|
},
|
|
required: ['projectPath', 'operations'],
|
|
},
|
|
outputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
results: {
|
|
type: 'array',
|
|
items: {
|
|
type: 'object',
|
|
properties: {
|
|
operation: { type: 'string' },
|
|
scenePath: { type: 'string' },
|
|
success: { type: 'boolean' },
|
|
error: { type: 'string' },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
];
|
|
// --- Handlers ---
|
|
export async function handleCreateScene(runner, args) {
|
|
args = normalizeParameters(args);
|
|
const v = validateSceneArgs(args, { sceneRequired: false });
|
|
if ('isError' in v)
|
|
return v;
|
|
const params = {
|
|
scenePath: args.scenePath,
|
|
rootNodeType: args.rootNodeType || 'Node2D',
|
|
};
|
|
return executeSceneOp(runner, 'create_scene', params, v.projectPath, 'Failed to create scene', ['Check if the root node type is valid'], undefined, { parseStdoutAsJson: true });
|
|
}
|
|
export async function handleAddNode(runner, args) {
|
|
args = normalizeParameters(args);
|
|
const v = validateSceneArgs(args);
|
|
if ('isError' in v)
|
|
return v;
|
|
if (!args.nodeType || !args.nodeName) {
|
|
return createErrorResponse('nodeType and nodeName are required', [
|
|
'Provide both nodeType and nodeName',
|
|
]);
|
|
}
|
|
// Merge promoted top-level params into properties dict
|
|
const promotedKeys = [
|
|
'position',
|
|
'position3d',
|
|
'rotation',
|
|
'scale',
|
|
'visible',
|
|
'modulate',
|
|
];
|
|
const mergedProps = args.properties || {};
|
|
for (const key of promotedKeys) {
|
|
if (args[key] !== undefined) {
|
|
mergedProps[key] = args[key];
|
|
}
|
|
}
|
|
const params = {
|
|
scenePath: args.scenePath,
|
|
nodeType: args.nodeType,
|
|
nodeName: args.nodeName,
|
|
};
|
|
if (args.parentNodePath)
|
|
params.parentNodePath = args.parentNodePath;
|
|
if (Object.keys(mergedProps).length > 0)
|
|
params.properties = mergedProps;
|
|
return executeSceneOp(runner, 'add_node', params, v.projectPath, 'Failed to add node', [
|
|
'Check if the node type is valid',
|
|
'Ensure the parent node path exists',
|
|
]);
|
|
}
|
|
export async function handleLoadSprite(runner, args) {
|
|
args = normalizeParameters(args);
|
|
const v = validateSceneArgs(args);
|
|
if ('isError' in v)
|
|
return v;
|
|
if (!args.nodePath || !validateNodePath(args.nodePath)) {
|
|
return createErrorResponse('Valid nodePath is required', ['Provide the target node path']);
|
|
}
|
|
if (!args.texturePath || !validateSubPath(v.projectPath, args.texturePath)) {
|
|
return createErrorResponse('Valid texturePath is required', [
|
|
'Provide a relative texture path that stays inside the project directory',
|
|
]);
|
|
}
|
|
const textureFullPath = join(v.projectPath, args.texturePath);
|
|
if (!existsSync(textureFullPath)) {
|
|
return createErrorResponse(`Texture file does not exist: ${args.texturePath}`, [
|
|
'Ensure the texture path is correct',
|
|
]);
|
|
}
|
|
const params = {
|
|
scenePath: args.scenePath,
|
|
nodePath: args.nodePath,
|
|
texturePath: args.texturePath,
|
|
};
|
|
return executeSceneOp(runner, 'load_sprite', params, v.projectPath, 'Failed to load sprite', [
|
|
'Check if the node is a Sprite2D, Sprite3D, or TextureRect',
|
|
]);
|
|
}
|
|
export async function handleSaveScene(runner, args) {
|
|
args = normalizeParameters(args);
|
|
const v = validateSceneArgs(args);
|
|
if ('isError' in v)
|
|
return v;
|
|
if (args.newPath && !validateSubPath(v.projectPath, args.newPath)) {
|
|
return createErrorResponse('Invalid newPath', [
|
|
'Provide a valid relative path without ".." that stays inside the project directory',
|
|
]);
|
|
}
|
|
const params = { scenePath: args.scenePath };
|
|
if (args.newPath)
|
|
params.newPath = args.newPath;
|
|
return executeSceneOp(runner, 'save_scene', params, v.projectPath, 'Failed to save scene', [
|
|
'Check if the scene file is valid',
|
|
]);
|
|
}
|
|
export async function handleExportMeshLibrary(runner, args) {
|
|
args = normalizeParameters(args);
|
|
const v = validateSceneArgs(args);
|
|
if ('isError' in v)
|
|
return v;
|
|
if (!args.outputPath || !validateSubPath(v.projectPath, args.outputPath)) {
|
|
return createErrorResponse('Valid outputPath is required', [
|
|
'Provide an output path for the .res file that stays inside the project directory',
|
|
]);
|
|
}
|
|
const params = {
|
|
scenePath: args.scenePath,
|
|
outputPath: args.outputPath,
|
|
};
|
|
if (args.meshItemNames && Array.isArray(args.meshItemNames)) {
|
|
params.meshItemNames = args.meshItemNames;
|
|
}
|
|
return executeSceneOp(runner, 'export_mesh_library', params, v.projectPath, 'Failed to export mesh library', ['Check if the scene contains valid 3D meshes']);
|
|
}
|
|
export async function handleBatchSceneOperations(runner, args) {
|
|
args = normalizeParameters(args);
|
|
const v = validateProjectArgs(args);
|
|
if ('isError' in v)
|
|
return v;
|
|
if (!args.operations || !Array.isArray(args.operations)) {
|
|
return createErrorResponse('operations array is required', [
|
|
'Provide an operations array with at least one item',
|
|
]);
|
|
}
|
|
const params = {
|
|
operations: args.operations,
|
|
abortOnError: args.abortOnError ?? false,
|
|
};
|
|
return executeSceneOp(runner, 'batch_scene_operations', params, v.projectPath, 'Batch scene operations failed', ['Check that all scene paths exist', 'Ensure node types are valid'], undefined, { parseStdoutAsJson: true });
|
|
}
|
|
//# sourceMappingURL=scene-tools.js.map
|