Editor toolset bridge for generating gameplay scaffolding C# components. It defines many McpTool methods that call McpGate.Run with named arguments to emit various ready-made gameplay scripts (interaction prompt, stations, carry system, pickups, wallets, directors, save systems, player/NPC controllers, etc.). Each method is an async Task<object> wrapper that forwards parameters to the MCP bridge.
// AUTO-GENERATED by scripts/emit-mcp-wrappers.mjs — DO NOT EDIT.
// Regenerate: node scripts/extract-manifest.mjs && node scripts/emit-mcp-wrappers.mjs
// Source of truth: sbox-mcp-server/src/tools/ (zod schemas) → scripts/tools-manifest.json
using System.Text.Json.Nodes;
using System.Threading.Tasks;
using Editor.Mcp;
/// <summary>
/// Generate complete, compile-verified gameplay C# components: player/NPC controllers, game
/// managers, health, pickups, inventory, save systems, economy, loot tables, round/phase machines,
/// interaction systems, placement mode, and more. Each tool writes a .cs file into the project;
/// follow with trigger_hotload + compile_status.
/// </summary>
[McpToolset( "bridge_scaffold_gameplay", "Generate complete, compile-verified gameplay C# components: player/NPC controllers, game managers, health, pickups, inventory, save systems, economy, loot tables, round/phase machines, interaction systems, placement mode, and more. Each tool writes a .cs file into the project; follow with trigger_hotload + compile_status." )]
public static class BridgeScaffoldGameplayTools
{
/// <summary>
/// Generate an eye-traced interaction-prompt HUD — a PanelComponent (.razor + .razor.scss pair,
/// like create_leaderboard_panel) that every frame traces a ray from the scene camera
/// (Scene.Trace.Ray, out to [Property] float Range) and, when the crosshair is on a component
/// implementing Component.IPressable, shows a centered "Press E"-style pill. The prompt text comes
/// from the target's IPressable.GetTooltip() when it overrides it (most don't), else a [Property]
/// DefaultPrompt built from the action. This is the visible half of the interaction loop: it PAIRS
/// with create_interactable / add_interaction_station (which implement IPressable) — this tool
/// tells the player they CAN press, those tools handle the press. Host it under a ScreenPanel
/// (add_screen_panel), then add the component to that panel object. The generated Razor is
/// razor_lint-safe by construction: PanelComponent + BuildHash override folding the visible state,
/// no switch-expressions and no non-ASCII in @code, and a class root selector in the SCSS.
/// LOCAL/visual-only (no [Sync]).
/// </summary>
/// <param name="name">Class/file name for the generated .razor. Defaults to 'InteractionPrompt'.</param>
/// <param name="directory">Subdirectory for the generated .razor + .razor.scss. Defaults to 'Code/UI'.</param>
/// <param name="action">Verb woven into the default prompt text ('Press E to <action>'). Defaults to 'use'.</param>
/// <param name="range">Eye-trace reach in world units — how close the crosshair must be to a pressable to show the prompt. Defaults to 120.</param>
[McpTool( "add_interaction_prompt" )]
public static Task<object> AddInteractionPrompt( string name = null, string directory = null, string action = null, double? range = null )
=> McpGate.Run( "add_interaction_prompt", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "action", action ), ( "range", range ) ) );
/// <summary>
/// Generate a Component.IPressable 'station' prop (crafting bench / shop till / arcade cabinet)
/// that ONE user occupies at a time. Occupancy is host-authoritative: the occupant is a
/// [Sync(SyncFlags.FromHost)] Guid (GameObject/Connection aren't [Sync]-able) and Press() routes
/// the claim to the host via an [Rpc.Host] Occupy(). Includes a reservation grace window (the
/// station stays reserved for its last user for graceSeconds after they leave, so a brief walk-away
/// can't jump the queue), an optional unlock-level gate (users below requiredLevel can't use it —
/// wire the static ResolveUserLevel hook to your progression system to activate it), and an
/// overlay-open hook (a static OnStationOpened(GameObject) event to open your UI, plus an opt-in
/// [Rpc.Broadcast] mirror). Single-player safe. Optionally attached to an existing GameObject by
/// GUID (only after a trigger_hotload). Give the prop a Collider so the player's use key can
/// raycast it. Mined from interaction-station patterns across shipped s&box games.
/// </summary>
/// <param name="name">Class name for the generated component. Defaults to 'InteractionStation'.</param>
/// <param name="directory">Subdirectory for the .cs file (path override). Defaults to 'Code'.</param>
/// <param name="graceSeconds">Seconds the station stays reserved for its last user after they leave, before anyone else can claim it. 0 = no grace window. Defaults to 5.</param>
/// <param name="requiredLevel">Unlock-level gate: users below this level can't use the station. 0 = no gate. The gate only bites once you wire the static ResolveUserLevel hook to your progression system. Defaults to 0.</param>
/// <param name="targetId">GUID of an existing GameObject to attach the station component to (only attaches if the type is already loaded — generate, trigger_hotload, then it places; otherwise add it after the hotload).</param>
[McpTool( "add_interaction_station" )]
public static Task<object> AddInteractionStation( string name = null, string directory = null, double? graceSeconds = null, int? requiredLevel = null, string targetId = null )
=> McpGate.Run( "add_interaction_station", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "graceSeconds", graceSeconds ), ( "requiredLevel", requiredLevel ), ( "targetId", targetId ) ) );
/// <summary>
/// Generate a first-person pickup / carry / throw component (sealed Component) for physics props.
/// Attach it to the PLAYER (the object that owns the camera). It eye-traces from Scene.Camera for a
/// Rigidbody-bearing GameObject tagged [Property] CarryTag (default 'carryable') within [Property]
/// Range; grabbing routes a host-authoritative [Rpc.Host] request that re-validates the target and
/// caller, hands the object's network ownership to the carrier
/// (GameObject.Network.AssignOwnership), and disables the rigidbody's MotionEnabled while held. The
/// held object follows a hold point ([Property] Vector3 HoldOffset in front of the camera) each
/// FixedUpdate; dropping restores physics, throwing applies an impulse ([Property] float
/// ThrowForce). The held-object id is [Sync(SyncFlags.FromHost)] so proxies see the carrying state,
/// and static OnPickedUp / OnDropped events fire uniformly for SFX/VFX. PAIRS with physics props —
/// give each carryable a Rigidbody + Collider and the CarryTag (set_tags); network-spawn them for
/// multiplayer so ownership + transform replicate. Single-player safe (IsProxy is false and RPCs
/// run locally with no session). Inputs: GrabAction (default 'use') grabs/drops, ThrowAction
/// (default 'attack1') throws. Optionally attach to an existing player GameObject by GUID after a
/// hotload.
/// </summary>
/// <param name="name">Class name for the generated component. Defaults to 'CarrySystem'.</param>
/// <param name="directory">Subdirectory for the generated .cs file. Defaults to 'Code'.</param>
/// <param name="range">Eye-trace reach for grabbing a carryable, in world units. Defaults to 130.</param>
/// <param name="throwForce">Impulse magnitude applied on throw (scales with the prop's mass — tune per game). Defaults to 20000.</param>
/// <param name="carryTag">Only objects with this tag (and a Rigidbody) can be picked up; lower-cased/underscored to match s&box tag convention. Defaults to 'carryable'.</param>
/// <param name="targetId">GUID of the PLAYER GameObject (the one with the camera) to attach to (only attaches if the type is already loaded — hotload first).</param>
[McpTool( "create_carry_system" )]
public static Task<object> CreateCarrySystem( string name = null, string directory = null, double? range = null, double? throwForce = null, string carryTag = null, string targetId = null )
=> McpGate.Run( "create_carry_system", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "range", range ), ( "throwForce", throwForce ), ( "carryTag", carryTag ), ( "targetId", targetId ) ) );
/// <summary>
/// Generate a networked coin / currency pickup component (sealed, Component.ITriggerListener).
/// Host-spawned; when a GameObject carrying PlayerTag ('player') enters its trigger the HOST
/// validates and grants Value (default 1) into a wallet on the player, then destroys the pickup
/// network-wide (the host Destroy() replicates — there is no NetworkDestroy on this SDK). Optional
/// magnet: while MagnetRadius (default 0 = off) is > 0 the coin accelerates toward the nearest
/// player each FixedUpdate (host-side, capped by MaxMagnetSpeed). IsProxy guards keep the grant +
/// despawn host-only in multiplayer (NetworkSpawn the coin on the host); single-player works with
/// no networking. The deposit is reflection-free and dependency-free: a static Grant seam is wired
/// ONCE to the direct typed call — player.Components.Get<EconomyWallet>()?.AddMoney(amount) —
/// so the component compiles with NO hard reference to a specific wallet class (rename the wallet
/// type if yours differs; mirrors create_pickup's self-contained convention). WalletComponentName
/// (default 'EconomyWallet') is used to locate the wallet and name the fix if Grant is left unwired
/// (never silent). Pairs with create_economy_wallet (AddMoney/TrySpend/CanAfford) and
/// create_floating_combat_text (spawn a '+N' popup from OnCollected).
/// </summary>
/// <param name="name">Class name for the generated component. Defaults to 'CurrencyPickup'.</param>
/// <param name="directory">Subdirectory for the generated .cs file. Defaults to 'Code'.</param>
/// <param name="value">How much currency the pickup grants into the wallet. Defaults to 1.</param>
/// <param name="magnetRadius">Magnet range in world units — within it the coin flies to the nearest player each FixedUpdate. 0 = magnet off. Defaults to 0.</param>
/// <param name="walletComponentName">Type name of the wallet component to deposit into (used to locate it and to name the fix if the Grant seam is left unwired). Defaults to 'EconomyWallet'.</param>
/// <param name="targetId">GUID of a coin GameObject to attach to — give it a trigger Collider (SphereCollider, IsTrigger=true). Only attaches if the type is already loaded — hotload first.</param>
[McpTool( "create_currency_pickup" )]
public static Task<object> CreateCurrencyPickup( string name = null, string directory = null, int? value = null, double? magnetRadius = null, string walletComponentName = null, string targetId = null )
=> McpGate.Run( "create_currency_pickup", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "value", value ), ( "magnetRadius", magnetRadius ), ( "walletComponentName", walletComponentName ), ( "targetId", targetId ) ) );
/// <summary>
/// Generate a host-authoritative time-of-day clock: [Sync(SyncFlags.FromHost)] TimeOfDay (0–24) +
/// Day advancing by Time.Delta, IsDay/IsNight from sunrise/sunset hours, and static OnNewDay /
/// OnDayNightChanged events to drive lighting, NPC schedules, or spawns. Single-player safe. Pairs
/// with create_round_phase_machine. Optionally attached to a GameObject by GUID (after a hotload).
/// </summary>
/// <param name="name">Class name. Defaults to 'DayNightClock'.</param>
/// <param name="directory">Subdirectory for the .cs file. Defaults to 'Code'.</param>
/// <param name="dayLengthSeconds">Real seconds per in-game day. Defaults to 600 (10 min).</param>
/// <param name="startHour">Hour the clock starts at (0–24). Defaults to 8.</param>
/// <param name="sunriseHour">Hour day begins. Defaults to 6.</param>
/// <param name="sunsetHour">Hour night begins. Defaults to 20.</param>
/// <param name="targetId">GUID of an existing GameObject to attach to (hotload first).</param>
[McpTool( "create_day_night_clock" )]
public static Task<object> CreateDayNightClock( string name = null, string directory = null, double? dayLengthSeconds = null, double? startHour = null, double? sunriseHour = null, double? sunsetHour = null, string targetId = null )
=> McpGate.Run( "create_day_night_clock", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "dayLengthSeconds", dayLengthSeconds ), ( "startHour", startHour ), ( "sunriseHour", sunriseHour ), ( "sunsetHour", sunsetHour ), ( "targetId", targetId ) ) );
/// <summary>
/// Generate a host-authoritative currency Wallet component: a [Sync(SyncFlags.FromHost)] Money
/// balance (only the host can write it — plain [Sync] money is the classic economy exploit) with
/// AddMoney / TrySpend / SetMoney / CanAfford and an OnMoneyChanged event. Single-player safe.
/// Optionally attached to an existing GameObject by GUID (after a hotload). Pairs with a save
/// system for persistence. Mined from the most-requested currency pattern across 51 games.
/// </summary>
/// <param name="name">Class name. Defaults to 'Wallet'.</param>
/// <param name="directory">Subdirectory for the .cs file. Defaults to 'Code'.</param>
/// <param name="startingMoney">Initial balance the host seeds on start. Defaults to 0.</param>
/// <param name="targetId">GUID of an existing GameObject to attach the Wallet to (only attaches if the type is already loaded — hotload first).</param>
[McpTool( "create_economy_wallet" )]
public static Task<object> CreateEconomyWallet( string name = null, string directory = null, int? startingMoney = null, string targetId = null )
=> McpGate.Run( "create_economy_wallet", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "startingMoney", startingMoney ), ( "targetId", targetId ) ) );
/// <summary>
/// Generate a generalized L4D-style AI/pacing director component (host-authoritative). On a
/// configurable interval the host rolls a weighted pick over a [Property] List<GameObject>
/// EventPrefabs (with a parallel List<float> Weights), skips any event already active
/// (dedupe) and anything past a MaxActive concurrency cap, clones the chosen prefab, NetworkSpawns
/// it, and attaches a generated {name}TimedEvent companion so each spawned event self-destructs
/// after EventLifetime seconds. Great for ambient events, waves, and world events. Single-player
/// safe (IsProxy guard; NetworkSpawn falls back to a local clone). Fill EventPrefabs/Weights in the
/// inspector or via the bridge after a hotload; edit the RollInterval() stub to make pacing
/// adaptive (player-count/inactivity/time-pressure factors) per the ai-director cookbook.
/// Optionally attached to an existing GameObject by GUID (after a hotload). NOTE: emits ONE .cs
/// file containing two classes ({name} + {name}TimedEvent); the type only resolves after
/// trigger_hotload.
/// </summary>
/// <param name="name">Class name for the director (a {name}TimedEvent companion is generated alongside it). Defaults to 'EventDirector'.</param>
/// <param name="path">Subdirectory for the generated .cs file. Defaults to 'Code'.</param>
/// <param name="intervalSeconds">Base seconds between director rolls. Defaults to 30 (clamped to >= 0.1).</param>
/// <param name="maxActive">Maximum number of concurrently-live events. Defaults to 3 (clamped to >= 1).</param>
/// <param name="eventLifetime">Seconds before each spawned event self-destructs. Defaults to 60 (clamped to >= 0.1).</param>
/// <param name="targetId">GUID of an existing GameObject to attach the director to (only attaches if the type is already loaded — hotload first).</param>
[McpTool( "create_event_director" )]
public static Task<object> CreateEventDirector( string name = null, string path = null, double? intervalSeconds = null, int? maxActive = null, double? eventLifetime = null, string targetId = null )
=> McpGate.Run( "create_event_director", McpGate.Args( ( "name", name ), ( "path", path ), ( "intervalSeconds", intervalSeconds ), ( "maxActive", maxActive ), ( "eventLifetime", eventLifetime ), ( "targetId", targetId ) ) );
/// <summary>
/// Generate a host-authoritative gacha / loot-box roller component. Two-level pick: parallel
/// [Property] lists RarityNames + RarityWeights select a RARITY by cumulative weight (the
/// create_weighted_loot_table shape), then a flat 'Rarity:Item' [Property] list (e.g.
/// 'Legendary:Dragon Fang') picks an ITEM uniformly within that rarity — simple and
/// inspector-editable. A pity counter (PityAfter, default 50) guarantees the rarest tier (the LAST
/// entry in RarityNames) after N rolls without it and resets on a hit. Duplicate detection against
/// an owned-items set fires a host-side OnDuplicate hook (marked TODO: convert dupes to
/// shards/currency). Roll() routes to the host via an [Rpc.Host] RequestRoll (Rpc.Caller
/// re-validated — NetFlags is not security) and the result fans out via [Rpc.Broadcast] so every
/// machine fires the static OnRolled(rarity, item, isDuplicate) event; single-player safe (RPCs run
/// locally). Use create_weighted_loot_table instead for a simpler single-tier weighted pick with no
/// pity/dupe/networking. Pairs with create_economy_wallet (spend currency to roll) and
/// create_inventory (store the pulls).
/// </summary>
/// <param name="name">Class name for the generated component. Defaults to 'GachaDropTable'.</param>
/// <param name="directory">Subdirectory for the generated .cs file. Defaults to 'Code'.</param>
/// <param name="pityAfter">Rolls without a rarest-tier hit before the next roll is guaranteed rarest. 0 disables pity. Defaults to 50.</param>
/// <param name="targetId">GUID of a per-player/manager GameObject to attach to (only attaches if the type is already loaded — hotload first).</param>
[McpTool( "create_gacha_drop_table" )]
public static Task<object> CreateGachaDropTable( string name = null, string directory = null, int? pityAfter = null, string targetId = null )
=> McpGate.Run( "create_gacha_drop_table", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "pityAfter", pityAfter ), ( "targetId", targetId ) ) );
/// <summary>
/// Generate a minimal game-manager Component: a static Instance singleton, [Property] MaxPlayers /
/// GameState, and a Component.INetworkListener OnActive hook that logs player connects. Writes
/// <name>.cs and returns { created, path, className }. NOTE: the
/// includeScore/includeTimer/includeSpawning params are not currently applied — the same minimal
/// manager is always generated (for richer game-loop scaffolds see create_round_phase_machine /
/// create_objective_system / create_economy_wallet). Follow with trigger_hotload, then
/// get_compile_errors, then place via add_component_to_new_object.
/// </summary>
/// <param name="name">Class name. Defaults to 'GameManager'.</param>
/// <param name="directory">Subdirectory under code/ for the file.</param>
/// <param name="includeScore">Include score tracking (currently not applied by the handler).</param>
/// <param name="includeTimer">Include round timer with countdown (currently not applied by the handler).</param>
/// <param name="includeSpawning">Include player spawning from prefab at spawn point (currently not applied by the handler).</param>
[McpTool( "create_game_manager" )]
public static Task<object> CreateGameManager( string name = null, string directory = null, bool? includeScore = null, bool? includeTimer = null, bool? includeSpawning = null )
=> McpGate.Run( "create_game_manager", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "includeScore", includeScore ), ( "includeTimer", includeTimer ), ( "includeSpawning", includeSpawning ) ) );
/// <summary>
/// Generate a Health component: MaxHealth, [Sync] CurrentHealth, TakeDamage/Heal, an OnDeath event,
/// optional regen and respawn. Host-authoritative damage when networked, single-player safe.
/// Optionally attached to an existing GameObject by GUID.
/// </summary>
/// <param name="name">Class name. Defaults to 'Health'.</param>
/// <param name="directory">Subdirectory for the .cs file. Defaults to 'Code'.</param>
/// <param name="maxHealth">Starting/maximum health. Defaults to 100.</param>
/// <param name="regen">Include passive health regeneration after a delay. Defaults to false.</param>
/// <param name="respawn">On death, respawn at a RespawnPoint (wire it with set_component_reference) instead of disabling. Defaults to false.</param>
/// <param name="targetId">GUID of an existing GameObject to attach the Health component to (only attaches if the type is already loaded — hotload first).</param>
[McpTool( "create_health_system" )]
public static Task<object> CreateHealthSystem( string name = null, string directory = null, double? maxHealth = null, bool? regen = null, bool? respawn = null, string targetId = null )
=> McpGate.Run( "create_health_system", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "maxHealth", maxHealth ), ( "regen", regen ), ( "respawn", respawn ), ( "targetId", targetId ) ) );
/// <summary>
/// Generate a hold-to-confirm action component (sealed Component). While a named input action is
/// held (Input.Down), a public Progress value fills 0→1 over [Property] float HoldSeconds;
/// releasing early snaps back to 0, or drains down if [Property] bool DecayOnRelease. Reaching 1
/// fires the static OnConfirmed(GameObject) event, then a short CooldownSeconds blocks
/// re-triggering. The classic 'hold E to disarm / open / revive' interaction. No UI is generated —
/// read the public Progress (0..1) from your own HUD to draw a radial or bar; a #region Feedback
/// hook marks where to tie in a sound/effect. LOCAL/owner-only: input is IsProxy-guarded so it
/// never fires on proxies and is single-player safe. For a host-authoritative outcome, call an
/// [Rpc.Host] from inside the OnConfirmed subscriber. Attach to the player (or any owned object
/// that reads input); optionally attach to an existing GameObject by GUID after a hotload.
/// </summary>
/// <param name="name">Class name for the generated component. Defaults to 'HoldToConfirm'.</param>
/// <param name="directory">Subdirectory for the generated .cs file. Defaults to 'Code'.</param>
/// <param name="action">Input action name that must be held (must exist in the project's Input settings — see ensure_input_action). Defaults to 'use'.</param>
/// <param name="holdSeconds">Seconds of continuous hold required to confirm. Defaults to 1.5.</param>
/// <param name="decayOnRelease">Baked default for DecayOnRelease: if true, releasing early drains Progress back down instead of snapping to 0 (editable per-instance). Defaults to false.</param>
/// <param name="targetId">GUID of a GameObject to attach the component to (only attaches if the type is already loaded — hotload first).</param>
[McpTool( "create_hold_to_confirm" )]
public static Task<object> CreateHoldToConfirm( string name = null, string directory = null, string action = null, double? holdSeconds = null, bool? decayOnRelease = null, string targetId = null )
=> McpGate.Run( "create_hold_to_confirm", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "action", action ), ( "holdSeconds", holdSeconds ), ( "decayOnRelease", decayOnRelease ), ( "targetId", targetId ) ) );
/// <summary>
/// Generate a host-authoritative passive income component: every tickSeconds the host grants
/// incomePerTick × Multiplier, auto-wiring the first sibling component with an AddMoney(int) method
/// (a create_economy_wallet scaffold plugs in with zero code) or an overridable Grant() seam;
/// TotalEarned is [Sync(FromHost)] and static OnIncomeTick fires per grant. The idle-game kit:
/// wallet (create_economy_wallet) + this + create_offline_progress. Writes a .cs file and returns {
/// created, path, className, nextSteps } — follow with trigger_hotload + compile_status.
/// </summary>
/// <param name="name">Class/file name (default 'IdleIncome' -> Code/IdleIncome.cs). Errors if the file exists.</param>
/// <param name="directory">Directory for the .cs file. Default 'Code'.</param>
/// <param name="incomePerTick">Amount granted per tick. Default 1.</param>
/// <param name="tickSeconds">Seconds between grants. Default 1.</param>
[McpTool( "create_idle_income" )]
public static Task<object> CreateIdleIncome( string name = null, string directory = null, double? incomePerTick = null, double? tickSeconds = null )
=> McpGate.Run( "create_idle_income", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "incomePerTick", incomePerTick ), ( "tickSeconds", tickSeconds ) ) );
/// <summary>
/// Generate a Component.IPressable interactable: the built-in PlayerController 'use' key drives
/// Press()/Hover()/Blur() with no custom player code. Includes a static OnPressed event, an
/// optional cooldown (TimeUntil), and a private OnPress() extensionpoint for effects. For
/// host-authoritative side-effects call an [Rpc.Host] from OnPress(). The Prompt property is left
/// to your game's HUD. Optionally attached to an existing GameObject by GUID (after a hotload).
/// </summary>
/// <param name="name">Class name. Defaults to 'Interactable'.</param>
/// <param name="directory">Subdirectory for the .cs file. Defaults to 'Code'.</param>
/// <param name="prompt">Prompt string shown by the game's HUD when hovering. Defaults to 'Press'.</param>
/// <param name="cooldownSeconds">Seconds before the interactable can be pressed again. 0 = no cooldown. Defaults to 0.</param>
/// <param name="targetId">GUID of an existing GameObject to attach the component to (only attaches if the type is already loaded — hotload first).</param>
[McpTool( "create_interactable" )]
public static Task<object> CreateInteractable( string name = null, string directory = null, string prompt = null, double? cooldownSeconds = null, string targetId = null )
=> McpGate.Run( "create_interactable", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "prompt", prompt ), ( "cooldownSeconds", cooldownSeconds ), ( "targetId", targetId ) ) );
/// <summary>
/// Generate a slot-based inventory component using parallel List<string> ItemIds /
/// List<int> Counts (serialization-safe, inspector-editable). Includes TryAdd (stack-first,
/// partial-add rejected), TryRemove, CountOf, Move (swap or merge same-id slots), and Clear. Static
/// OnChanged event fires after every successful mutation. Host-authoritative usage note: mutate on
/// the host in multiplayer, replicate via your own [Sync]/RPC. Pairs with create_pickup.
/// </summary>
/// <param name="name">Class name. Defaults to 'Inventory'.</param>
/// <param name="directory">Subdirectory for the .cs file. Defaults to 'Code'.</param>
/// <param name="capacity">Total slot count. Defaults to 24.</param>
/// <param name="maxStack">Maximum items per slot (stack cap). Defaults to 99.</param>
/// <param name="targetId">GUID of an existing GameObject to attach to (hotload first).</param>
[McpTool( "create_inventory" )]
public static Task<object> CreateInventory( string name = null, string directory = null, int? capacity = null, int? maxStack = null, string targetId = null )
=> McpGate.Run( "create_inventory", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "capacity", capacity ), ( "maxStack", maxStack ), ( "targetId", targetId ) ) );
/// <summary>
/// Generate a Razor PanelComponent that fetches and displays a Sandbox.Services leaderboard derived
/// from a stat name. Produces TWO files: {name}.razor and {name}.razor.scss. The panel
/// auto-refreshes every 30 s, shows rank/displayName/value rows, handles loading state, and
/// includes a BuildHash() override (razor-lint clean). Must be hosted under a ScreenPanel or
/// WorldPanel. Stats must be configured for the project ident on sbox.game. Uses
/// Leaderboards.Get(statName) + board.Refresh() -- the exact API from ServicesQueryHandler. Returns
/// { created, razorPath, scssPath, className, note }. Follow with trigger_hotload, then
/// get_compile_errors, then host it via add_screen_panel (panelComponent=className).
/// </summary>
/// <param name="name">Class name for the panel component. Defaults to 'LeaderboardPanel'.</param>
/// <param name="directory">Subdirectory for the generated files. Defaults to 'Code/UI'.</param>
/// <param name="statName">Sandbox.Services stat name the leaderboard is derived from. Defaults to 'score'.</param>
/// <param name="title">Display title shown at the top of the panel. Defaults to 'Leaderboard'.</param>
/// <param name="maxRows">Maximum leaderboard rows to fetch and display. Defaults to 10.</param>
[McpTool( "create_leaderboard_panel" )]
public static Task<object> CreateLeaderboardPanel( string name = null, string directory = null, string statName = null, string title = null, int? maxRows = null )
=> McpGate.Run( "create_leaderboard_panel", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "statName", statName ), ( "title", title ), ( "maxRows", maxRows ) ) );
/// <summary>
/// Generate an NPC controller script with NavMeshAgent pathfinding. Supports patrol, chase, and
/// patrol-chase behaviors.
/// </summary>
/// <param name="name">Class name. Defaults to 'NpcController'.</param>
/// <param name="directory">Subdirectory under code/ for the file.</param>
/// <param name="behavior">AI behavior: 'patrol' (follow waypoints), 'chase' (follow player), 'patrol_chase' (patrol until player nearby). Defaults to 'patrol'. One of: patrol | chase | patrol_chase.</param>
/// <param name="moveSpeed">Movement speed. Defaults to 150.</param>
/// <param name="chaseRange">Detection range for chase behavior. Defaults to 500.</param>
[McpTool( "create_npc_controller" )]
public static Task<object> CreateNpcController( string name = null, string directory = null, string behavior = null, double? moveSpeed = null, double? chaseRange = null )
=> McpGate.Run( "create_npc_controller", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "behavior", behavior ), ( "moveSpeed", moveSpeed ), ( "chaseRange", chaseRange ) ) );
/// <summary>
/// Generate an ObjectiveManager component — the win/lose brain of a game. Tracks an objective
/// (collect_all / reach_goal / survive_time / eliminate_all), fires a win, and handles a lose
/// condition (fall below kill-Z / timer / out of lives). Self-contained C#; other systems call
/// ObjectiveManager.Instance. Optionally placed as a scene singleton. Returns { created, path,
/// className, gameObject, note } — gameObject is the placed singleton, or null with a note when the
/// fresh type isn't in the TypeLibrary yet. Follow with trigger_hotload, then get_compile_errors;
/// if placement was skipped, place with add_component_to_new_object after the hotload.
/// </summary>
/// <param name="name">Class name. Defaults to 'ObjectiveManager'.</param>
/// <param name="directory">Subdirectory for the .cs file. Defaults to 'Code'.</param>
/// <param name="objective">Win condition. Defaults to 'reach_goal'. One of: collect_all | reach_goal | survive_time | eliminate_all.</param>
/// <param name="targetCount">How many to collect/eliminate (for collect_all / eliminate_all). Defaults to 3.</param>
/// <param name="timeLimit">Seconds — survive this long to win (survive_time) or before losing (loseOn=timer). Defaults to 60.</param>
/// <param name="loseOn">Lose condition. 'fall' = player drops below killZ. Defaults to 'fall'. One of: fall | timer | lives | none.</param>
/// <param name="killZ">World Z below which the player is considered fallen out of the world. Defaults to -1000.</param>
/// <param name="lives">Lives before game over (loseOn=lives). Defaults to 1.</param>
/// <param name="placeInScene">Place the manager as a scene singleton. Defaults to true. (Only attaches if the type is already loaded — generate, hotload, then it places; otherwise add it after hotload.).</param>
[McpTool( "create_objective_system" )]
public static Task<object> CreateObjectiveSystem( string name = null, string directory = null, string objective = null, int? targetCount = null, double? timeLimit = null, string loseOn = null, double? killZ = null, int? lives = null, bool? placeInScene = null )
=> McpGate.Run( "create_objective_system", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "objective", objective ), ( "targetCount", targetCount ), ( "timeLimit", timeLimit ), ( "loseOn", loseOn ), ( "killZ", killZ ), ( "lives", lives ), ( "placeInScene", placeInScene ) ) );
/// <summary>
/// Generate an offline / idle-progress component (sealed, owner/host-only) — the idle-game staple.
/// Persists LastSeenUtc (DateTime) to FileSystem.Data JSON on a dirty-flag autosave heartbeat
/// (AutosaveSeconds) and on OnDisabled, copying create_save_system's persistence patterns. On
/// enable it computes elapsed = now − LastSeenUtc, guards a clock rollback (negative → 0), clamps
/// to MaxOfflineHours (default 8), then replays that time through a SimulateOffline(double seconds)
/// TODO hook in fixed TickSeconds chunks (default 1) so idle accumulation is deterministic
/// (frame-rate independent), and fires the static OnOfflineProgressApplied(seconds) event (drive a
/// 'welcome back, you earned X' screen). IsProxy-guarded so a client can't author their own offline
/// earnings. Fill in the SimulateOffline hook with your idle math (e.g.
/// wallet.AddMoney(rate*seconds)). Pairs with create_economy_wallet / create_save_system /
/// create_stat_modifier_system.
/// </summary>
/// <param name="name">Class name for the generated component. Defaults to 'OfflineProgress'.</param>
/// <param name="directory">Subdirectory for the generated .cs file. Defaults to 'Code'.</param>
/// <param name="maxOfflineHours">Offline time is clamped to this many hours (stops a week-away paying out a week). Defaults to 8.</param>
/// <param name="tickSeconds">SimulateOffline chunk size in seconds — smaller = finer-grained deterministic replay (floored at 0.1). Defaults to 1.</param>
/// <param name="targetId">GUID of an idle/save-manager GameObject to attach to (only attaches if the type is already loaded — hotload first).</param>
[McpTool( "create_offline_progress" )]
public static Task<object> CreateOfflineProgress( string name = null, string directory = null, double? maxOfflineHours = null, double? tickSeconds = null, string targetId = null )
=> McpGate.Run( "create_offline_progress", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "maxOfflineHours", maxOfflineHours ), ( "tickSeconds", tickSeconds ), ( "targetId", targetId ) ) );
/// <summary>
/// Generate a trigger-based collectible component. On enter by a tagged object it raises
/// OnCollected (wire it to your objective/score system) and despawns. Optionally builds a visible
/// pickup GameObject with a trigger SphereCollider (+ a model) in one call. Returns { created,
/// path, className, gameObject, note } — gameObject is the placed pickup (null unless
/// placeInScene=true); a note flags when the component couldn't attach because the fresh type needs
/// a hotload. Follow with trigger_hotload, then get_compile_errors.
/// </summary>
/// <param name="name">Class name. Defaults to 'Pickup'.</param>
/// <param name="directory">Subdirectory for the .cs file. Defaults to 'Code'.</param>
/// <param name="action">Effect flavour (all self-contained; the heal/item branches show the typed call to a companion system in comments). Defaults to 'score'. One of: score | heal | item | custom.</param>
/// <param name="amount">Magnitude of the effect (score points, heal amount). Defaults to 1.</param>
/// <param name="filterTag">Only collect for objects with this tag. Defaults to 'player'.</param>
/// <param name="placeInScene">Also build a pickup GameObject (trigger SphereCollider + optional model). Defaults to false.</param>
/// <param name="position">World position when placeInScene is true. As "x,y,z" (or JSON {x,y,z}).</param>
/// <param name="radius">Trigger sphere radius when placed. Defaults to 24.</param>
/// <param name="model">Optional model path for a visible pickup (e.g. 'models/dev/box.vmdl'). Cloud assets must be installed first.</param>
[McpTool( "create_pickup" )]
public static Task<object> CreatePickup( string name = null, string directory = null, string action = null, double? amount = null, string filterTag = null, bool? placeInScene = null, string position = null, double? radius = null, string model = null )
=> McpGate.Run( "create_pickup", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "action", action ), ( "amount", amount ), ( "filterTag", filterTag ), ( "placeInScene", placeInScene ), ( "position", position ), ( "radius", radius ), ( "model", model ) ) );
/// <summary>
/// Generate a ghost-preview + commit placement component (single class). StartPlacing() clones
/// GhostPrefab as a NetworkMode.Never preview with colliders disabled and ModelRenderers tinted
/// semi-transparent. Each frame while placing: ray from Scene.Camera.GetMouseRay(),
/// IgnoreGameObjectHierarchy(ghost), snap hit position to GridSize (0 = freeform), move ghost. On
/// Input.Pressed('attack1') TryPlace() re-validates distance and commits a real clone.
/// StopPlacing() destroys the ghost. Static OnPlaced(GameObject, Vector3) event. Includes a
/// multiplayer RPC note. API grounded in building-placement cookbook (enifun.shop_manager pattern).
/// </summary>
/// <param name="name">Class name. Defaults to 'PlacementMode'.</param>
/// <param name="directory">Subdirectory for the .cs file. Defaults to 'Code'.</param>
/// <param name="gridSize">Snap grid size in world units (0 = freeform placement). Defaults to 0.</param>
/// <param name="targetId">GUID of an existing GameObject to attach to (hotload first).</param>
[McpTool( "create_placement_mode" )]
public static Task<object> CreatePlacementMode( string name = null, string directory = null, double? gridSize = null, string targetId = null )
=> McpGate.Run( "create_placement_mode", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "gridSize", gridSize ), ( "targetId", targetId ) ) );
/// <summary>
/// Generate a player controller script with WASD movement, mouse look, jumping, and sprint.
/// Supports first-person, third-person, and top-down movement modes. Optionally places a player rig
/// (GameObject + CharacterController + Camera) in the scene — note the generated component is
/// attached AFTER a trigger_hotload (it isn't in the TypeLibrary until a recompile).
/// </summary>
/// <param name="name">Class name. Defaults to 'PlayerController'.</param>
/// <param name="directory">Subdirectory under code/ for the file.</param>
/// <param name="type">Movement mode: 'first_person' (mouse-look body+camera, WASD relative to facing), 'third_person' (mouse yaw, WASD relative to facing, boom camera), or 'top_down' (screen-relative WASD, fixed overhead camera, no jump). Defaults to 'first_person'. One of: first_person | third_person | top_down.</param>
/// <param name="moveSpeed">Movement speed in units/sec. Defaults to 300.</param>
/// <param name="jumpForce">Jump force (ignored for top_down). Defaults to 350.</param>
/// <param name="sprintMultiplier">Sprint speed multiplier (held 'run' action). Defaults to 1.5.</param>
/// <param name="placeInScene">If true, build a player rig in the scene: a GameObject (tagged 'player') with a CharacterController and (unless createCamera=false) a Camera. The generated controller component is NOT attached in this call — trigger_hotload then add_component_with_properties on the returned GameObject. Defaults to false (file-only).</param>
/// <param name="createCamera">When placeInScene is true, also create a Camera (FP/TP: child at eye/boom offset; top_down: fixed overhead). Defaults to true.</param>
/// <param name="spawnPosition">When placeInScene is true, the world position to spawn the player rig at — object {x,y,z} or comma string "x,y,z". Defaults to the origin. As "x,y,z" (or JSON {x,y,z}).</param>
[McpTool( "create_player_controller" )]
public static Task<object> CreatePlayerController( string name = null, string directory = null, string type = null, double? moveSpeed = null, double? jumpForce = null, double? sprintMultiplier = null, bool? placeInScene = null, bool? createCamera = null, string spawnPosition = null )
=> McpGate.Run( "create_player_controller", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "type", type ), ( "moveSpeed", moveSpeed ), ( "jumpForce", jumpForce ), ( "sprintMultiplier", sprintMultiplier ), ( "placeInScene", placeInScene ), ( "createCamera", createCamera ), ( "spawnPosition", spawnPosition ) ) );
/// <summary>
/// Generate a host-authoritative round/phase machine: a [Sync(SyncFlags.FromHost)] CurrentPhase
/// cycled through your named phases on a per-phase timer (host-only), with a static OnPhaseChanged
/// event that fires on every machine. Great for round/match flow, match phases, or a day/night
/// cycle. Single-player safe. Optionally attached to an existing GameObject by GUID (after a
/// hotload). Mined from the round-flow pattern across the 51 games.
/// </summary>
/// <param name="name">Class name. Defaults to 'GameDirector'.</param>
/// <param name="directory">Subdirectory for the .cs file. Defaults to 'Code'.</param>
/// <param name="phases">Ordered phase names (become an enum), e.g. ["Lobby","Day","Night","Payout"]. Defaults to ["Lobby","Active","Ended"].</param>
/// <param name="duration">Default seconds per phase (each phase also gets its own tunable [Property]). Defaults to 60.</param>
/// <param name="loop">Loop back to the first phase after the last (true) or hold on the last phase (false). Defaults to true.</param>
/// <param name="targetId">GUID of an existing GameObject to attach to (only if the type is already loaded — hotload first).</param>
[McpTool( "create_round_phase_machine" )]
public static Task<object> CreateRoundPhaseMachine( string name = null, string directory = null, string[] phases = null, double? duration = null, bool? loop = null, string targetId = null )
=> McpGate.Run( "create_round_phase_machine", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "phases", phases ), ( "duration", duration ), ( "loop", loop ), ( "targetId", targetId ) ) );
/// <summary>
/// Generate a host-authoritative MULTI-STATE round machine (the complex variant of
/// create_round_phase_machine). Produces one .cs file: a RoundManager singleton component + an
/// abstract RoundState base (Begin/Tick/OnTimeUp/Finish lifecycle with a per-state
/// [Sync(SyncFlags.FromHost)] TimeUntil timer) + one sealed stub class per named state. The manager
/// auto-attaches the state components on start (you only place the manager), ticks ONLY the active
/// state on the host, Advance()s on timeout with index-wrap, SKIPS any state whose CanEnter()
/// returns false, and announces every transition via a static OnStateChanged event plus an
/// [Rpc.Broadcast] mirror so the host fires immediately and proxies converge without waiting a
/// snapshot (the [Sync] index reconciles late joiners). Single-player safe. USE THIS (not
/// create_round_phase_machine) when each phase needs its OWN behaviour — entry side-effects,
/// per-frame Tick logic, a skip condition, or copy-data-out-on-exit; use the phase machine for 3–5
/// light phases that differ only in duration. Optionally attached to an existing GameObject by GUID
/// (after a hotload).
/// </summary>
/// <param name="name">Manager class name. Defaults to 'RoundManager'. The abstract base is derived from it (RoundManager → RoundState).</param>
/// <param name="directory">Subdirectory for the .cs file (path override). Defaults to 'Code'.</param>
/// <param name="states">Ordered state names — each becomes a sealed {Name}State stub class. Defaults to ["Waiting","Active","PostRound"].</param>
/// <param name="duration">Default seconds each state lasts (each state also gets its own tunable [Property] Duration). 0 = no auto-advance for a state. Defaults to 30.</param>
/// <param name="durations">Optional per-state duration override: an array aligned to `states` ([10,120,8]) OR an object keyed by state name ({"Waiting":10,"Active":120}). Any state not covered falls back to `duration`. JSON value.</param>
/// <param name="loop">Loop back to the first state after the last (true) or hold on the last state (false). Defaults to true.</param>
/// <param name="targetId">GUID of an existing GameObject to attach the manager to (only if the type is already loaded — trigger_hotload first).</param>
[McpTool( "create_round_state_machine" )]
public static Task<object> CreateRoundStateMachine( string name = null, string directory = null, string[] states = null, double? duration = null, JsonNode durations = null, bool? loop = null, string targetId = null )
=> McpGate.Run( "create_round_state_machine", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "states", states ), ( "duration", duration ), ( "durations", durations ), ( "loop", loop ), ( "targetId", targetId ) ) );
/// <summary>
/// Generate a multi-slot save MANAGER component (the slot-picker sibling of create_save_system).
/// Use this when the game needs SEVERAL named save slots the player chooses between (New Game /
/// Load Game menu, per-character or per-run saves) — not one silent autosave. Use
/// create_save_system instead when a single implicit save file is enough. Emits one sealed
/// Component that lists / creates / loads / saves / deletes N slots: a lightweight manifest file
/// (saveslots.json) holds per-slot metadata for the picker (Used flag + Name + SavedAtUnix
/// timestamp + PlaytimeSeconds) so listing never loads a heavy payload, and each slot's game state
/// lives in its own saveslot_<i>.json. Versioned SlotData POCO with clamp-on-load Sanitize()
/// and delete-on-version-mismatch; runs only on the owning machine (IsProxy guard). Static
/// OnSlotLoaded / OnSlotSaved / OnSlotDeleted hooks for HUD. Storage stays within the verified
/// FileSystem.Data.ReadJsonOrDefault / WriteJson / DeleteFile surface (index-file pattern, no
/// directory enumeration). Set sceneReconciliation:true to also reconcile scene objects by
/// GameObject.Id on load — records the save marks destroyed are destroyed, survivors repositioned,
/// missing skipped (good for a placeable-world tycoon). Optionally attached to an existing
/// GameObject by GUID (after a hotload).
/// </summary>
/// <param name="name">Class name for the generated component. Defaults to 'SaveSlotManager'.</param>
/// <param name="directory">Subdirectory for the generated .cs file. Defaults to 'Code'.</param>
/// <param name="maxSlots">How many save slots the manager manages (manifest is normalized to exactly this many, indexed 0..N-1). Clamped to 1..100. Defaults to 3.</param>
/// <param name="sceneReconciliation">If true, saved records carry each object's GameObject.Id GUID and load reconciles the live scene against them (destroy the save's destroyed records via Scene.Directory.FindByGuid, reposition survivors, skip missing) — call RecordObject(go) to track a placeable. If false (default), the slot save is a plain payload with no scene reconciliation. Defaults to false.</param>
/// <param name="targetId">GUID of an existing GameObject to attach the manager to (only attaches if the type is already loaded — hotload first).</param>
[McpTool( "create_save_slots" )]
public static Task<object> CreateSaveSlots( string name = null, string directory = null, int? maxSlots = null, bool? sceneReconciliation = null, string targetId = null )
=> McpGate.Run( "create_save_slots", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "maxSlots", maxSlots ), ( "sceneReconciliation", sceneReconciliation ), ( "targetId", targetId ) ) );
/// <summary>
/// Generate a versioned save-system component: a SaveData POCO with Version bump on schema change,
/// dirty-flag autosave on a TimeUntil timer, clamp-on-load Sanitize() for corrupt/hand-edited
/// saves, and delete-on-version-mismatch to start fresh instead of crashing. Runs only on the
/// owning machine (IsProxy guard). Fires static OnLoaded/OnSaved hooks for HUD and analytics.
/// FileSystem.Data.ReadJsonOrDefault/WriteJson verified live on the current SDK. Optionally
/// attached to an existing GameObject by GUID (after a hotload).
/// </summary>
/// <param name="name">Class name. Defaults to 'SaveSystem'.</param>
/// <param name="directory">Subdirectory for the .cs file. Defaults to 'Code'.</param>
/// <param name="fileName">Save file name under FileSystem.Data (e.g. 'save.json'). Defaults to 'save.json'.</param>
/// <param name="version">Schema version embedded in SaveData. Old saves with a different version start fresh. Defaults to 1.</param>
/// <param name="autosaveSeconds">Seconds between autosave ticks (0 disables autosave). Defaults to 10.</param>
/// <param name="targetId">GUID of an existing GameObject to attach to (only if the type is already loaded -- hotload first).</param>
[McpTool( "create_save_system" )]
public static Task<object> CreateSaveSystem( string name = null, string directory = null, string fileName = null, int? version = null, double? autosaveSeconds = null, string targetId = null )
=> McpGate.Run( "create_save_system", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "fileName", fileName ), ( "version", version ), ( "autosaveSeconds", autosaveSeconds ), ( "targetId", targetId ) ) );
/// <summary>
/// Generate an enum-keyed stat modifier system with three modifier layers: SET
/// (highest-priority-wins hard override), ADD (summed bonuses), MULT (multiplied factors applied
/// last). Modifier storage uses parallel private Lists of primitive types (serialization-safe).
/// RemoveModifiersFrom(source) cleans up all mods from a buff/debuff source by reference. Static
/// OnStatChanged(stat, value) event fires after every add/remove. Mined from RPG/buff/debuff
/// patterns across shipped s&box games. Returns { created, path, className, stats, placedOn,
/// note } — stats echoes the sanitized stat names ({name}Stat enum values); placedOn is the target
/// GameObject when attached (needs the type hotloaded). Follow with trigger_hotload, then
/// get_compile_errors.
/// </summary>
/// <param name="name">Class name prefix -- generates {name}Stat enum + {name} Component. Defaults to 'StatSystem'.</param>
/// <param name="directory">Subdirectory for the .cs file. Defaults to 'Code'.</param>
/// <param name="stats">Stat names as a JSON array or comma-separated string. Defaults to 'Health,Speed,Damage'. JSON value.</param>
/// <param name="targetId">GUID of an existing GameObject to attach to (hotload first).</param>
[McpTool( "create_stat_modifier_system" )]
public static Task<object> CreateStatModifierSystem( string name = null, string directory = null, JsonNode stats = null, string targetId = null )
=> McpGate.Run( "create_stat_modifier_system", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "stats", stats ), ( "targetId", targetId ) ) );
/// <summary>
/// Generate a host-authoritative balanced team assigner component (smallest-bucket draft):
/// AssignSmallest(steamId) drops a joining player into the emptiest team, announces via
/// [Rpc.Broadcast] so every client's roster agrees, and fires static OnTeamAssigned(steamId, index,
/// name); plus Rebalance(), GetTeam, GetMembers. Writes a .cs file and returns { created, path,
/// className, teams, nextSteps } — follow with trigger_hotload + compile_status, attach to your
/// game manager, call AssignSmallest from your join hook (e.g. INetworkListener.OnActive).
/// </summary>
/// <param name="name">Class/file name (default 'TeamAssigner' -> Code/TeamAssigner.cs). Errors if the file exists.</param>
/// <param name="directory">Directory for the .cs file. Default 'Code'.</param>
/// <param name="teams">Team names in index order. Default ["Red", "Blue"].</param>
[McpTool( "create_team_assigner" )]
public static Task<object> CreateTeamAssigner( string name = null, string directory = null, string[] teams = null )
=> McpGate.Run( "create_team_assigner", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "teams", teams ) ) );
/// <summary>
/// Generate a trigger-zone Component (Component.ITriggerListener): auto-adds a trigger BoxCollider
/// on start, filters entrants by a TriggerTag [Property] (default 'player'), and logs enter/exit
/// via private OnPlayerEnter/OnPlayerExit extension points you fill in. Writes <name>.cs and
/// returns { created, path, className }. NOTE: the action/filterTag params are not currently
/// applied at generation time — the zone always logs; implement teleport/damage/spawn in the
/// generated methods (edit_script). Follow with trigger_hotload, then get_compile_errors.
/// </summary>
/// <param name="name">Class name. Defaults to 'TriggerZone'.</param>
/// <param name="directory">Subdirectory under code/ for the file.</param>
/// <param name="action">What happens on trigger (currently not applied by the handler — the generated zone always logs; implement the effect in OnPlayerEnter yourself). One of: log | teleport | damage | spawn.</param>
/// <param name="filterTag">Only trigger for objects with this tag (currently not applied at generation — the generated TriggerTag [Property] defaults to 'player'; change it per-instance with set_property).</param>
[McpTool( "create_trigger_zone" )]
public static Task<object> CreateTriggerZone( string name = null, string directory = null, string action = null, string filterTag = null )
=> McpGate.Run( "create_trigger_zone", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "action", action ), ( "filterTag", filterTag ) ) );
/// <summary>
/// Generate a cumulative-weight random loot picker: parallel Name/Weight lists
/// (inspector-editable), a Roll() method that returns a winning entry name and fires a static
/// OnLoot event, and optional pity (guarantee the last/rarest entry after PityAfter consecutive
/// non-rare rolls). Roll() is host-authoritative -- only call it on the host and replicate the
/// result (clients rolling their own loot is equivalent to clients writing their own money
/// balance). Optionally attached to an existing GameObject by GUID (after a hotload).
/// </summary>
/// <param name="name">Class name. Defaults to 'LootTable'.</param>
/// <param name="directory">Subdirectory for the .cs file. Defaults to 'Code'.</param>
/// <param name="entries">Loot table entries. Defaults to common:70 / uncommon:25 / rare:5. JSON value.</param>
/// <param name="pity">If true, guarantee the last (rarest) entry after PityAfter consecutive non-rare rolls. Defaults to false.</param>
/// <param name="targetId">GUID of an existing GameObject to attach to (only if the type is already loaded -- hotload first).</param>
[McpTool( "create_weighted_loot_table" )]
public static Task<object> CreateWeightedLootTable( string name = null, string directory = null, JsonNode entries = null, bool? pity = null, string targetId = null )
=> McpGate.Run( "create_weighted_loot_table", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "entries", entries ), ( "pity", pity ), ( "targetId", targetId ) ) );
}