Skip to content

Sensor

Abstract-ish base for the small sensor family (ProximitySensor, MotionSensor, OccupancySensor, and others). Wires sensors into the motherboard / circuitboard automation system through Activate, IsTriggered, and a held UniTask that flips circuitboards on and off.

Class shape

public class Sensor : SmallDevice, ISmartRotatable
{
    [Header("ISmartRotation")]
    public SmartRotate.ConnectionType ConnectionType = SmartRotate.ConnectionType.Exhaustive;
    public int[] OpenEndsPermutation = new int[6] { 0, 1, 2, 3, 4, 5 };

    public List<Motherboard> LinkedMotherboards = new List<Motherboard>();

    private UniTask _notTriggeredTask;
    private CancellationTokenSource _taskCancel;

    public virtual bool IsTriggered => false;
    ...
}

Inherits from SmallDevice (a Device variant). Implements ISmartRotatable so it participates in the smart-rotate cable connector logic via ConnectionType.Exhaustive and a six-element permutation of open ends.

Trigger lifecycle

IsTriggered is the per-subclass condition (override returns true while a target is detected). Activate is the inherited Device interactable value; subclass OnThreadUpdate keeps the two in sync via OnServer.Interact(InteractActivate, ...). The base class itself runs no detection.

WaitUntilNotTriggered() is an async UniTask:

public async UniTask WaitUntilNotTriggered()
{
    if (GameManager.GameState == GameState.None) return;
    if (GameManager.RunSimulation)
        OnServer.Interact(this, InteractableType.Activate, 1);
    SetMotherboards(isTriggered: true);
    while (IsTriggered)
        await UniTask.Delay(100, ignoreTimeScale: false, PlayerLoopTiming.Update, _taskCancel.Token);
    if (GameManager.GameState != GameState.None && !_taskCancel.IsCancellationRequested)
    {
        if (GameManager.RunSimulation)
            OnServer.Interact(this, InteractableType.Activate, 0);
        SetMotherboards(isTriggered: false);
    }
}

ActivateSensor() starts that task if Activate == 0 and no task is pending. The task polls IsTriggered every 100 ms; when it goes false, the sensor clears Activate and flips every linked motherboard back off. ResetSensor() cancels the task at destruction or on game-state change.

Polling cadence is 100 ms (UniTask.Delay(100)) on the Update loop, distinct from OnThreadUpdate() which subclasses use for their own per-tick detection scan.

Motherboard linkage

public virtual void SetMotherboards(bool isTriggered)
{
    foreach (Motherboard linkedMotherboard in LinkedMotherboards)
    {
        if (linkedMotherboard is Circuitboard circuitboard)
        {
            circuitboard.RemoteToggle(isTriggered);
        }
    }
}

public override void OnLinkWithBoard(Motherboard motherboard)
{
    base.OnLinkWithBoard(motherboard);
    LinkedMotherboards.Add(motherboard);
}

public override void OnUnlinkWithBoard(Motherboard motherboard)
{
    base.OnUnlinkWithBoard(motherboard);
    LinkedMotherboards.Remove(motherboard);
}

SetMotherboards ignores non-Circuitboard motherboards. ProximitySensor and MotionSensor both override this to additionally require circuitboard.ParentComputer.AsDevice().Powered before issuing RemoteToggle.

ISmartRotatable

public SmartRotate.ConnectionType GetConnectionType() => ConnectionType;
public void SetOpenEndsPermutation(int[] permutation) => OpenEndsPermutation = (int[])permutation.Clone();
public void SetConnectionType(SmartRotate.ConnectionType connectionType) => ConnectionType = connectionType;
public int[] GetOpenEndsPermutation() => (int[])OpenEndsPermutation.Clone();

Default ConnectionType = SmartRotate.ConnectionType.Exhaustive, default permutation {0, 1, 2, 3, 4, 5}.

Verification history

  • 2026-05-18: Page created from Assembly-CSharp.decompiled.cs line 397847. Initial class shape, trigger lifecycle, motherboard linkage, ISmartRotatable surface captured. No prior page existed.

Open questions

None.