TipsWorld.cs

Static helper class that exposes optional world-bound callbacks for tips/triggers. It holds delegate properties for HasLocalPlayer, LocalPlayerPosition, and AimRay so a game can wire local player and camera data into a tips library. Unset delegates leave related triggers inert.

using System;
using Sandbox;

namespace FieldGuide.Tips;

/// <summary>
/// Optional world seams a game sets once at bootstrap so world-anchored triggers can read the local
/// player. A library cannot reach into a game's player, so these stay null until the game wires them.
///
/// All are fail-inert: a trigger that needs an unset seam is simply INERT (it never fires and never
/// throws), so nothing crashes when a game skips them. Input, Signal and Timer triggers need no seam at
/// all. Set the seams you use, leave the rest null.
///
/// <code>
/// using Sandbox;
/// using FieldGuide.Tips;
///
/// TipsWorld.LocalPlayerPosition = () => MyLocalPlayer.WorldPosition;
/// TipsWorld.AimRay = () => new Ray( MyCamera.WorldPosition, MyCamera.WorldRotation.Forward );
/// </code>
/// </summary>
public static class TipsWorld
{
	/// <summary>Whether a local player exists to coach. Read by the self-driving coach when no context is
	/// pushed. Default: always true, so input-only tips show without any bootstrap wiring.</summary>
	public static Func<bool> HasLocalPlayer { get; set; } = static () => true;

	/// <summary>Local player world position, for <see cref="TipTriggerObject.Mode.PlayerEntered"/> triggers.
	/// Null leaves those triggers inert.</summary>
	public static Func<Vector3> LocalPlayerPosition { get; set; }

	/// <summary>Camera / aim ray, for <see cref="TipTriggerObject.Mode.LookedAt"/> triggers. Null leaves
	/// those triggers inert.</summary>
	public static Func<Ray> AimRay { get; set; }
}