Aetheria/Assets/Scripts/ServerShared/Behaviors/Sensor.cs
2026-06-21 09:37:54 +01:00

167 lines
5.8 KiB
C#

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
using System;
using System.Collections.Generic;
using static CultMath.math;
public class Sensor : Behavior, IEventBehavior
{
private readonly PerformanceStat _sensitivity;
private readonly BezierCurve _sensitivityCurve;
private readonly PerformanceStat _pingBoost;
private readonly PerformanceStat _pingEnergy;
private readonly PerformanceStat _pingVisibility;
private readonly PerformanceStat _pingRange;
private readonly PerformanceStat _pingCooldownDuration;
private readonly float _pingDuration;
private readonly float _pingRadiusExponent;
private float _pingCooldown;
private float _pingLerp;
private bool _pinging;
private float _pingRadius;
private HashSet<Entity> _pingedEntities = new HashSet<Entity>();
public float Cooldown
{
get => saturate(_pingCooldown);
}
public float PingRadius
{
get => _pingRadius;
}
public float PingLerp => _pingLerp;
public bool Pinging => _pinging;
public int PingedEntityCount => _pingedEntities.Count;
public float PingBrightness => pow(1 - _pingLerp, _pingRadiusExponent);
public event Action OnPingStart;
public event Action OnPingEnd;
public void ResetEvents()
{
OnPingStart = null;
OnPingEnd = null;
}
public void Ping()
{
if(_pingCooldown < 0 && Entity.TryConsumeEnergy(Evaluate(_pingEnergy)))
{
Entity.VisibilitySources[this] = Evaluate(_pingVisibility);
_pinging = true;
_pingCooldown = 1;
_pingLerp = 0;
_pingRadius = 0;
_pingedEntities.Clear();
OnPingStart?.Invoke();
}
}
public Sensor(RuntimeBehaviorDefinition definition, EquippedItem item) : base(definition, item)
{
_sensitivity = definition.PerformanceStat(3, new PerformanceStat());
_sensitivityCurve = definition.BezierCurve(4, null);
_pingBoost = definition.PerformanceStat(5, null);
_pingEnergy = definition.PerformanceStat(6, null);
_pingVisibility = definition.PerformanceStat(7, null);
_pingRange = definition.PerformanceStat(8, null);
_pingCooldownDuration = definition.PerformanceStat(9, null);
_pingDuration = definition.Float(10, 2);
_pingRadiusExponent = definition.Float(11, .5f);
RegisterPerformanceStats();
}
public Sensor(RuntimeBehaviorDefinition definition, ConsumableItemEffect item) : base(definition, item)
{
_sensitivity = definition.PerformanceStat(3, new PerformanceStat());
_sensitivityCurve = definition.BezierCurve(4, null);
_pingBoost = definition.PerformanceStat(5, null);
_pingEnergy = definition.PerformanceStat(6, null);
_pingVisibility = definition.PerformanceStat(7, null);
_pingRange = definition.PerformanceStat(8, null);
_pingCooldownDuration = definition.PerformanceStat(9, null);
_pingDuration = definition.Float(10, 2);
_pingRadiusExponent = definition.Float(11, .5f);
RegisterPerformanceStats();
}
private void RegisterPerformanceStats()
{
RegisterPerformanceStat("Sensitivity", _sensitivity);
RegisterPerformanceStat("PingBoost", _pingBoost);
RegisterPerformanceStat("PingEnergy", _pingEnergy);
RegisterPerformanceStat("PingVisibility", _pingVisibility);
RegisterPerformanceStat("PingRange", _pingRange);
RegisterPerformanceStat("PingCooldown", _pingCooldownDuration);
}
public override bool Execute(float dt)
{
if (_pinging)
{
_pingLerp += dt / _pingDuration;
_pingRadius = lerp(0, Evaluate(_pingRange), pow(_pingLerp, _pingRadiusExponent));
if (_pingLerp > 1)
{
_pinging = false;
OnPingEnd?.Invoke();
}
}
_pingCooldown -= dt / Evaluate(_pingCooldownDuration);
// TODO: Handle Active Detection / Visibility From Reflected Radiance
var forward = Direction.xz;
var entityPosition = Entity.CultPositionXZ;
foreach (var entity in Entity.Zone.Entities)
{
if (entity == Entity) continue;
var diff = entity.CultPositionXZ - entityPosition;
var angle = AetheriaMath.AngleRadians(forward, diff);
var dist = length(diff);
float previous, next;
Entity.EntityInfoGathered.TryGetValue(entity, out previous);
if (!_pingedEntities.Contains(entity) && dist < _pingRadius)
{
_pingedEntities.Add(entity);
next = saturate(
previous +
entity.Visibility *
Evaluate(_sensitivity) *
Evaluate(_pingBoost) *
dist);
}
else
{
next = saturate(
previous +
entity.Visibility *
Evaluate(_sensitivity) *
_sensitivityCurve.Evaluate(angle / PI) *
dt / dist);
}
next *= 1 - ItemManager.GameplaySettings.TargetInfoDecay * dt;
//Context.Log($"{entity.Name} visibility {(int)(previous * 100)}% -> {(int)(next * 100)}%");
Entity.EntityInfoGathered[entity] = next;
}
return true;
}
public void RestoreRuntimeState(
bool pinging,
float pingCooldown,
float pingLerp,
float pingRadius)
{
_pinging = pinging;
_pingCooldown = pingCooldown;
_pingLerp = pingLerp;
_pingRadius = pingRadius;
_pingedEntities.Clear();
}
}