terminalCode Example
Sandbox: ToolMode system — creating custom Tool Gun modes
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
- All ToolMode subclasses are auto-discovered via Game.TypeLibrary.GetTypes<ToolMode>() and added as components to the Toolgun on the host
- Only one mode is enabled at a time — switching disables the old and enables the new
- [Sync] properties on ToolMode are synced across the network
- FireToolAction / FirePostToolAction integrate with IToolActionEvents for limits and undo
- TraceSelect() returns a SelectionPoint — the surface point the player is aiming at
Was this helpful?