terminalCode Example

Sandbox: ToolMode system — creating custom Tool Gun modes

calendar_today May 4, 2026 schedule ~1 min read person patrickjr verified 50

ToolMode System — Creating Custom Tool Gun Modes

ToolMode is the base class for all Tool Gun tools in Sandbox. Tools are components on the Toolgun GameObject and are enabled/disabled to switch between them.

Base class structure

CSHARP
public abstract partial class ToolMode : Component, IToolInfo
{
    public Toolgun Toolgun => GetComponent<Toolgun>();
    public Player Player => GetComponentInParent<Player>();

    // Set true/false in OnControl to indicate if the current state is valid
    public bool IsValidState { get; protected set; } = true;

    // When true, mouse input is absorbed (camera doesn't move)
    // Useful for rotation-based tools
    public virtual bool AbsorbMouseInput => false;

    // Display name — defaults to TypeDescription title
    public virtual string Name => Game.Language.GetPhrase( TypeDescription?.Title ?? GetType().Name );

    // Labels shown in the tool info HUD
    public virtual string PrimaryAction => null;
    public virtual string SecondaryAction => null;
    public virtual string ReloadAction => null;

    // Tags that TraceSelect will ignore — defaults to "player"
    public virtual IEnumerable<string> TraceIgnoreTags => ["player"];

    // Override to use snap grid
    public virtual bool UseSnapGrid => false;
}

Registering actions

CSHARP
protected override void OnStart()
{
    base.OnStart();

    // Register named actions for the tool info HUD
    RegisterAction( ToolInput.Primary, () => "#tool.hint.mytool.place", OnPlace );
    RegisterAction( ToolInput.Reload, () => "#tool.hint.mytool.remove", OnRemove );
}

private void OnPlace()
{
    var select = TraceSelect();
    if ( !select.IsValid() ) return;

    // ... create your entity/constraint ...

    // Track created objects for undo and limits
    Track( go );

    // Fire pre/post events (checks limits, records undo)
    // This is done automatically when using RegisterAction
}

Switching tool modes

CSHARP
// From the Toolgun — called via RPC from client
[Rpc.Host]
public void SetToolMode( string name )
{
    var targetMode = Game.TypeLibrary.GetType<ToolMode>( name );
    var newMode = GetComponents<ToolMode>( true )
        .Where( x => x.GetType() == targetMode.TargetType )
        .FirstOrDefault();

    var currentMode = GetCurrentMode();
    currentMode?.Enabled = false;
    newMode.Enabled = true;

    Network.Refresh( GameObject );
}

// From PlayerInventory — gives toolgun if needed, then switches mode
public void SetToolMode( string toolModeName )
{
    if ( !HasWeapon<Toolgun>() )
        Pickup( "weapons/toolgun/toolgun.prefab", false );

    var toolgun = GetWeapon<Toolgun>();
    SwitchWeapon( toolgun );
    toolgun.SetToolMode( toolModeName );
}

Tool attributes

CSHARP
[Icon( "🔧" )]
[Title( "#tool.name.mytool" )]
[ClassName( "mytool" )]
[Group( "#tool.group.building" )]
public class MyTool : ToolMode
{
    [Property, Sync]
    public float MyProperty { get; set; } = 1.0f;
    // ...
}

Key points

Was this helpful?