components/sight.cs

A Scene component that handles player "sight" and blinking UI. It finds UI components (eyelids and blink meter), manages a blink timer and animation, scans scene Renderers for objects tagged scp173 inside the camera frustum, plays a scare sound and marks those GameObjects as seen via broadcast RPCs, and throttles scare events.

Rough CodeNetworkingFile Access
using Sandbox;
using Sandbox.ui.eyelids;
using Sandbox.ui.blink_meter;
using System.Collections.Generic;
using System.Linq;

public class Sight : Component
{
    CameraComponent c;
    eyelids Eyelids;
    blink_meter Blink_Meter;
    GameObject UI;
    SoundEvent Scare;
    
    protected override void OnStart()
    {if (IsProxy) return; c = Scene.Camera;
    UI = Scene.Directory.FindbyName("UI");
    Eyelids = UI.GetComponent<eyelids>();
    Blink_Meter = UI.GetComponent<blink_meter>();
    Scare = ResourceLibrary.Get<SoundEvent>("prereqs/sound/effects/scare/scare.sound");}
    
    bool CanSee = true;
    TimeUntil TimeUntil_Blink = 4;
    bool CanScare = true;
    
    protected override void OnUpdate()
    { if (IsProxy) return;
        Blinking(); This();
    }
    
    async void Blinking()
    { Blink_Meter.w = (TimeUntil_Blink / 4 * 100).Clamp(0, 100);
        if (Input.Pressed("b") || TimeUntil_Blink)
        { if (!CanSee) return; CanSee = false;
            
            for (float i = Eyelids.o; i < 1; i += .3f)
            {
                Eyelids.o += .3f;
                Eyelids.o = Eyelids.o.Clamp(0, 1);
                await Task.Delay(1);
            } await Task.Delay(50); TimeUntil_Blink = 4;
            
            for (float i = Eyelids.o; i > 0; i -= .3f)
            {
                Eyelids.o -= .3f;
                Eyelids.o = Eyelids.o.Clamp(0, 1);
                await Task.Delay(1);
            }
        CanSee = true; }
    }
    
    void This()
    {
        var Renderers = new List<Renderer>(Scene.GetAll<Renderer>());
        for (int i = Renderers.Count - 1; i >= 0; i--)
        {if (!Renderers[i].Tags.Has("scp173"))
        {Renderers.RemoveAt(i); continue;}}
        var Frustum = c.GetFrustum();
        
        foreach (var r in Renderers)
        {
            if (Frustum.IsInside(r.GameObject.GetBounds(), false) && CanSee)
            {
                if (!r.GameObject.Tags.Has("seen") && CanScare)
                {GameObject.PlaySound(Scare); Throttle();}
                Tags_Add(r.GameObject, "seen"); continue;
            } Tags_Remove(r.GameObject, "seen");
        }
    }
    
    async void Throttle()
    {
        CanScare = false;
        await Task.Delay(10000);
        CanScare = true;
    }
    
    [Rpc.Broadcast]
    public void Tags_Add(GameObject go, string t)
    {go.Tags.Add(t);}
    
    [Rpc.Broadcast]
    public void Tags_Remove(GameObject go, string t)
    {go.Tags.Remove(t);}
}