将原 claude-dev-stack 目录拆分为独立的 Windows 和 WSL 部署栈,便于分别维护和使用。 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
115 lines
8.3 KiB
JavaScript
115 lines
8.3 KiB
JavaScript
import { z } from "zod";
|
|
import { formatErrorForMcp } from "../utils/errors.js";
|
|
export function registerPhysicsTools(server, godot) {
|
|
server.tool("setup_collision", "Add a CollisionShape2D/3D child to a physics body or area node with a specified shape. Auto-detects 2D/3D from the parent node type.", {
|
|
node_path: z.string().describe("Path to the parent physics body or area node (e.g. CharacterBody2D, StaticBody3D, Area2D)"),
|
|
shape: z.string().describe("Shape type: 'rectangle'/'rect', 'circle', 'capsule', 'segment' (2D only), 'cylinder' (3D only), 'custom'/'convex'. For 3D: 'box'/'sphere' also work."),
|
|
width: z.number().optional().describe("Width for rectangle/box shape (default: 32 for 2D, 1 for 3D)"),
|
|
height: z.number().optional().describe("Height for rectangle/box/capsule/cylinder shape"),
|
|
depth: z.number().optional().describe("Depth for 3D box shape (default: 1)"),
|
|
radius: z.number().optional().describe("Radius for circle/sphere/capsule/cylinder shape"),
|
|
ax: z.number().optional().describe("Segment start X (2D segment only)"),
|
|
ay: z.number().optional().describe("Segment start Y (2D segment only)"),
|
|
bx: z.number().optional().describe("Segment end X (2D segment only)"),
|
|
by: z.number().optional().describe("Segment end Y (2D segment only)"),
|
|
points: z.array(z.array(z.number())).optional().describe("Convex polygon points as [[x,y],...] for 2D or [[x,y,z],...] for 3D"),
|
|
disabled: z.boolean().optional().describe("Create the collision shape disabled (default: false)"),
|
|
one_way_collision: z.boolean().optional().describe("Enable one-way collision (2D only, default: false)"),
|
|
dimension: z.string().optional().describe("Force '2d' or '3d' if auto-detection fails"),
|
|
}, async (params) => {
|
|
try {
|
|
const result = await godot.sendCommand("setup_collision", params);
|
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
}
|
|
catch (e) {
|
|
return { content: [{ type: "text", text: formatErrorForMcp(e) }], isError: true };
|
|
}
|
|
});
|
|
server.tool("set_physics_layers", "Set collision layer and/or mask on a physics body or area node. Supports bitmask integers or arrays of layer numbers.", {
|
|
node_path: z.string().describe("Path to the node with collision layers"),
|
|
collision_layer: z.union([z.number(), z.array(z.number())]).optional().describe("Collision layer: bitmask integer or array of layer numbers [1,3,5]"),
|
|
collision_mask: z.union([z.number(), z.array(z.number())]).optional().describe("Collision mask: bitmask integer or array of layer numbers [1,2,4]"),
|
|
}, async (params) => {
|
|
try {
|
|
const result = await godot.sendCommand("set_physics_layers", params);
|
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
}
|
|
catch (e) {
|
|
return { content: [{ type: "text", text: formatErrorForMcp(e) }], isError: true };
|
|
}
|
|
});
|
|
server.tool("get_physics_layers", "Get the current collision layer and mask for a node, including named layer info from ProjectSettings.", {
|
|
node_path: z.string().describe("Path to the node with collision layers"),
|
|
}, async (params) => {
|
|
try {
|
|
const result = await godot.sendCommand("get_physics_layers", params);
|
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
}
|
|
catch (e) {
|
|
return { content: [{ type: "text", text: formatErrorForMcp(e) }], isError: true };
|
|
}
|
|
});
|
|
server.tool("add_raycast", "Add a RayCast2D/3D child node for collision detection. Auto-detects 2D/3D from the parent node type.", {
|
|
node_path: z.string().describe("Path to the parent node"),
|
|
name: z.string().optional().describe("Name for the raycast node (default: 'RayCast')"),
|
|
target_x: z.number().optional().describe("Target position X (default: 0)"),
|
|
target_y: z.number().optional().describe("Target position Y (default: 50 for 2D, -1 for 3D)"),
|
|
target_z: z.number().optional().describe("Target position Z (3D only, default: 0)"),
|
|
collision_mask: z.number().optional().describe("Collision mask bitmask (default: 1)"),
|
|
enabled: z.boolean().optional().describe("Enable the raycast (default: true)"),
|
|
collide_with_areas: z.boolean().optional().describe("Collide with Area nodes (default: false)"),
|
|
collide_with_bodies: z.boolean().optional().describe("Collide with physics bodies (default: true)"),
|
|
hit_from_inside: z.boolean().optional().describe("Detect hits from inside shapes (default: false)"),
|
|
dimension: z.string().optional().describe("Force '2d' or '3d' if auto-detection fails"),
|
|
}, async (params) => {
|
|
try {
|
|
const result = await godot.sendCommand("add_raycast", params);
|
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
}
|
|
catch (e) {
|
|
return { content: [{ type: "text", text: formatErrorForMcp(e) }], isError: true };
|
|
}
|
|
});
|
|
server.tool("setup_physics_body", "Configure physics body properties. For CharacterBody2D/3D: floor settings, motion mode, etc. For RigidBody2D/3D: mass, gravity, damping, etc.", {
|
|
node_path: z.string().describe("Path to the physics body node"),
|
|
// CharacterBody properties
|
|
floor_stop_on_slope: z.boolean().optional().describe("CharacterBody: stop on slopes when not moving"),
|
|
floor_max_angle: z.number().optional().describe("CharacterBody: maximum floor angle in radians (default ~0.785 = 45 degrees)"),
|
|
floor_snap_length: z.number().optional().describe("CharacterBody: floor snap distance for sticking to the ground"),
|
|
wall_min_slide_angle: z.number().optional().describe("CharacterBody: minimum angle for wall sliding in radians"),
|
|
motion_mode: z.string().optional().describe("CharacterBody: 'grounded' or 'floating'"),
|
|
max_slides: z.number().optional().describe("CharacterBody: maximum slide iterations (default: 6)"),
|
|
slide_on_ceiling: z.boolean().optional().describe("CharacterBody: allow sliding on ceiling"),
|
|
// RigidBody properties
|
|
mass: z.number().optional().describe("RigidBody: mass in kg (default: 1)"),
|
|
gravity_scale: z.number().optional().describe("RigidBody: gravity multiplier (default: 1, 0 = no gravity)"),
|
|
linear_damp: z.number().optional().describe("RigidBody: linear velocity damping"),
|
|
angular_damp: z.number().optional().describe("RigidBody: angular velocity damping"),
|
|
freeze: z.boolean().optional().describe("RigidBody: freeze the body (stop physics simulation)"),
|
|
freeze_mode: z.string().optional().describe("RigidBody: 'static' or 'kinematic' freeze behavior"),
|
|
continuous_cd: z.union([z.string(), z.boolean()]).optional().describe("RigidBody: continuous collision detection. 2D: 'disabled'/'cast_ray'/'cast_shape'. 3D: true/false"),
|
|
contact_monitor: z.boolean().optional().describe("RigidBody: enable contact monitoring for body_entered/body_exited signals"),
|
|
max_contacts_reported: z.number().optional().describe("RigidBody: max contacts to report (requires contact_monitor)"),
|
|
}, async (params) => {
|
|
try {
|
|
const result = await godot.sendCommand("setup_physics_body", params);
|
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
}
|
|
catch (e) {
|
|
return { content: [{ type: "text", text: formatErrorForMcp(e) }], isError: true };
|
|
}
|
|
});
|
|
server.tool("get_collision_info", "Get detailed collision information for a node: all collision shapes, layers/masks, raycasts, and physics body settings. Scans children by default.", {
|
|
node_path: z.string().describe("Path to the node to inspect"),
|
|
include_children: z.boolean().optional().describe("Include children in the scan (default: true)"),
|
|
}, async (params) => {
|
|
try {
|
|
const result = await godot.sendCommand("get_collision_info", params);
|
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
}
|
|
catch (e) {
|
|
return { content: [{ type: "text", text: formatErrorForMcp(e) }], isError: true };
|
|
}
|
|
});
|
|
}
|
|
//# sourceMappingURL=physics-tools.js.map
|