/* 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 Unity.Mathematics; using static Unity.Mathematics.math; [Inspectable] public class SensorData : BehaviorData { [Inspectable, LegacyPayloadKey(3)] public PerformanceStat Sensitivity = new PerformanceStat(); [InspectableAnimationCurve, LegacyPayloadKey(4)] public BezierCurve SensitivityCurve; [Inspectable, LegacyPayloadKey(5)] public PerformanceStat PingBoost; [Inspectable, LegacyPayloadKey(6)] public PerformanceStat PingEnergy; [Inspectable, LegacyPayloadKey(7)] public PerformanceStat PingVisibility; [Inspectable, LegacyPayloadKey(8)] public PerformanceStat PingRange; [Inspectable, LegacyPayloadKey(9)] public PerformanceStat PingCooldown; [Inspectable, LegacyPayloadKey(10)] public float PingDuration = 2; [Inspectable, LegacyPayloadKey(11)] public float PingRadiusExponent = .5f; public override Behavior CreateInstance(EquippedItem item) { return new Sensor(this, item); } public override Behavior CreateInstance(ConsumableItemEffect item) { return new Sensor(this, item); } } public class Sensor : Behavior, IEventBehavior { private SensorData _data; private float _pingCooldown; private float _pingLerp; private bool _pinging; private float _pingRadius; private HashSet _pingedEntities = new HashSet(); public float Cooldown { get => saturate(_pingCooldown); } public float PingRadius { get => _pingRadius; } public float PingBrightness => pow(1 - _pingLerp, _data.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(_data.PingEnergy))) { Entity.VisibilitySources[this] = Evaluate(_data.PingVisibility); _pinging = true; _pingCooldown = 1; _pingLerp = 0; _pingRadius = 0; _pingedEntities.Clear(); OnPingStart?.Invoke(); } } public Sensor(SensorData data, EquippedItem item) : base(data, item) { _data = data; } public Sensor(SensorData data, ConsumableItemEffect item) : base(data, item) { _data = data; } public override bool Execute(float dt) { if (_pinging) { _pingLerp += dt / _data.PingDuration; _pingRadius = lerp(0, Evaluate(_data.PingRange), pow(_pingLerp, _data.PingRadiusExponent)); if (_pingLerp > 1) { _pinging = false; OnPingEnd?.Invoke(); } } _pingCooldown -= dt / Evaluate(_data.PingCooldown); // TODO: Handle Active Detection / Visibility From Reflected Radiance var forward = Direction.xz; foreach (var entity in Entity.Zone.Entities) { if (entity == Entity) continue; var diff = entity.Position.xz - Entity.Position.xz; var angle = acos(dot(forward, normalize(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(_data.Sensitivity) * Evaluate(_data.PingBoost) * dist); } else { next = saturate( previous + entity.Visibility * Evaluate(_data.Sensitivity) * _data.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; } }