Weapons/ToolGun/ToolMode.cs
using Sandbox.Rendering;
public abstract partial class ToolMode : Component, IToolInfo
{
public Toolgun Toolgun => GetComponent<Toolgun>();
public Player Player => GetComponentInParent<Player>();
/// <summary>
/// The mode should set this true or false in OnControl to indicate if the current state is valid for performing actions.
/// </summary>
public bool IsValidState { get; protected set; } = true;
/// <summary>
/// When true, the toolgun will absorb mouse input so the camera doesn't move.
/// The mode can then read <see cref="Input.AnalogLook"/> to use the mouse for rotation etc.
/// </summary>
public virtual bool AbsorbMouseInput => false;
/// <summary>
/// Display name for the tool, defaults to the TypeDescription title.
/// </summary>
public virtual string Name => TypeDescription?.Title ?? GetType().Name;
/// <summary>
/// Description of what this tool does.
/// </summary>
public virtual string Description => string.Empty;
/// <summary>
/// Label for the primary action (attack1), or null if none.
/// </summary>
public virtual string PrimaryAction => null;
/// <summary>
/// Label for the secondary action (attack2), or null if none.
/// </summary>
public virtual string SecondaryAction => null;
/// <summary>
/// Label for the reload action, or null if none.
/// </summary>
public virtual string ReloadAction => null;
/// <summary>
/// Tags that TraceSelect will ignore. Override per-tool to filter out specific objects.
/// Defaults to "player" so tools cannot target players.
/// </summary>
public virtual IEnumerable<string> TraceIgnoreTags => ["player"];
/// <summary>
/// When true, TraceSelect will also hit hitboxes.
/// </summary>
public virtual bool TraceHitboxes => false;
public TypeDescription TypeDescription { get; protected set; }
protected override void OnStart()
{
TypeDescription = TypeLibrary.GetType( GetType() );
}
protected override void OnEnabled()
{
if ( Network.IsOwner )
{
this.LoadCookies();
}
}
protected override void OnDisabled()
{
DisableSnapGrid();
if ( Network.IsOwner )
{
this.SaveCookies();
}
}
public virtual void DrawScreen( Rect rect, HudPainter paint )
{
var t = $"{TypeDescription.Icon} {TypeDescription.Title}";
var text = new TextRendering.Scope( t, Color.White, 64 );
text.LineHeight = 0.75f;
text.FontName = "Poppins";
text.TextColor = Color.Orange;
text.FontWeight = 700;
var measured = text.Measure();
float textW = measured.x;
float textH = measured.y;
if ( textW <= rect.Width )
{
paint.DrawText( text, rect, TextFlag.Center );
return;
}
// Marquee: scroll text right-to-left, looping seamlessly.
// The render target viewport naturally clips anything outside [0, rect.Width].
const float scrollSpeed = 80f;
const float gap = 60f;
float cycle = textW + gap;
float offset = (Time.Now * scrollSpeed) % cycle;
float y = rect.Top + (rect.Height - textH) * 0.5f;
float x = rect.Width - offset;
paint.DrawText( text, new Rect( x, y, textW, textH ), TextFlag.SingleLine | TextFlag.Left );
paint.DrawText( text, new Rect( x - cycle, y, textW, textH ), TextFlag.SingleLine | TextFlag.Left );
}
public virtual void DrawHud( HudPainter painter, Vector2 crosshair )
{
if ( IsValidState )
{
painter.SetBlendMode( BlendMode.Normal );
painter.DrawCircle( crosshair, 5, Color.Black );
painter.DrawCircle( crosshair, 3, Color.White );
}
else
{
Color redColor = "#e53";
painter.SetBlendMode( BlendMode.Normal );
painter.DrawCircle( crosshair, 5, redColor.Darken( 0.3f ) );
painter.DrawCircle( crosshair, 3, redColor );
}
}
/// <summary>
/// Called on the host after placing an entity or constraint. Fires an RPC to the owning
/// client so it can walk the contraption graph and record achievement stats locally.
/// </summary>
[Rpc.Owner]
protected void CheckContraptionStats( GameObject anchor )
{
var builder = new LinkedGameObjectBuilder();
builder.AddConnected( anchor );
var wheels = builder.Objects.Sum( o => o.GetComponentsInChildren<WheelEntity>().Count() );
var thrusters = builder.Objects.Sum( o => o.GetComponentsInChildren<ThrusterEntity>().Count() );
var hoverballs = builder.Objects.Sum( o => o.GetComponentsInChildren<HoverballEntity>().Count() );
var constraints = builder.Objects.Sum( o => o.GetComponentsInChildren<ConstraintCleanup>().Count() );
var chairs = builder.Objects.Sum( o => o.GetComponentsInChildren<BaseChair>().Count() );
Sandbox.Services.Stats.Increment( "tool.constraint.create", 1 );
Sandbox.Services.Stats.SetValue( "tool.contraption.wheel", wheels );
Sandbox.Services.Stats.SetValue( "tool.contraption.thruster", thrusters );
Sandbox.Services.Stats.SetValue( "tool.contraption.hoverball", hoverballs );
Sandbox.Services.Stats.SetValue( "tool.contraption.constraint", constraints );
Sandbox.Services.Stats.SetValue( "tool.contraption.chair", chairs );
}
}