Speed Limiter

This commit is contained in:
Noah Pilarski 2025-04-10 14:30:56 -04:00
parent 264bcb2446
commit 5374448514
5 changed files with 228 additions and 0 deletions

View File

@ -0,0 +1,20 @@
using NewHorizons.Components.Volumes;
using NewHorizons.External.Modules.Volumes.VolumeInfos;
using UnityEngine;
namespace NewHorizons.Builder.Volumes
{
public static class SpeedLimiterVolumeBuilder
{
public static SpeedLimiterVolume Make(GameObject planetGO, Sector sector, SpeedLimiterVolumeInfo info)
{
var volume = VolumeBuilder.Make<SpeedLimiterVolume>(planetGO, sector, info);
volume.maxSpeed = info.maxSpeed;
volume.stoppingDistance = info.stoppingDistance;
volume.maxEntryAngle = info.maxEntryAngle;
return volume;
}
}
}

View File

@ -191,6 +191,13 @@ namespace NewHorizons.Builder.Volumes
SpeedTrapVolumeBuilder.Make(go, sector, speedTrapVolume);
}
}
if (config.Volumes.speedLimiterVolumes != null)
{
foreach (var speedLimiterVolume in config.Volumes.speedLimiterVolumes)
{
SpeedLimiterVolumeBuilder.Make(go, sector, speedLimiterVolume);
}
}
if (config.Volumes.lightSourceVolumes != null)
{
foreach (var lightSourceVolume in config.Volumes.lightSourceVolumes)

View File

@ -0,0 +1,166 @@
using System.Collections.Generic;
using System;
using UnityEngine;
namespace NewHorizons.Components.Volumes
{
public class SpeedLimiterVolume : BaseVolume
{
public float maxSpeed = 10f;
public float stoppingDistance = 100f;
public float maxEntryAngle = 60f;
private OWRigidbody _parentBody;
private List<TrackedBody> _trackedBodies = new List<TrackedBody>();
private bool _playerJustExitedDream;
public override void Awake()
{
_parentBody = GetComponentInParent<OWRigidbody>();
base.Awake();
GlobalMessenger.AddListener("ExitDreamWorld", OnExitDreamWorld);
}
public void Start()
{
enabled = false;
}
public override void OnDestroy()
{
base.OnDestroy();
GlobalMessenger.RemoveListener("ExitDreamWorld", OnExitDreamWorld);
}
public void FixedUpdate()
{
foreach (var trackedBody in _trackedBodies)
{
bool slowed = false;
Vector3 velocity = trackedBody.body.GetVelocity() - _parentBody.GetVelocity();
float magnitude = velocity.magnitude;
if (magnitude <= maxSpeed)
{
slowed = true;
}
else
{
bool needsSlowing = true;
float velocityReduction = trackedBody.deceleration * Time.deltaTime;
float requiredReduction = maxSpeed - magnitude;
if (requiredReduction > velocityReduction)
{
velocityReduction = requiredReduction;
slowed = true;
}
if (trackedBody.name == Detector.Name.Ship)
{
Autopilot component = Locator.GetShipTransform().GetComponent<Autopilot>();
if (component != null && component.IsFlyingToDestination())
{
needsSlowing = false;
}
}
if (needsSlowing)
{
Vector3 velocityChange = velocityReduction * velocity.normalized;
trackedBody.body.AddVelocityChange(velocityChange);
if (trackedBody.name == Detector.Name.Ship && PlayerState.IsInsideShip())
{
Locator.GetPlayerBody().AddVelocityChange(velocityChange);
}
}
}
if (slowed)
{
if (trackedBody.name == Detector.Name.Ship)
GlobalMessenger.FireEvent("ShipExitSpeedLimiter");
_trackedBodies.Remove(trackedBody);
if (_trackedBodies.Count == 0)
enabled = false;
}
}
}
private void OnExitDreamWorld()
{
_playerJustExitedDream = true;
}
public override void OnTriggerVolumeEntry(GameObject hitObj)
{
DynamicForceDetector component = hitObj.GetComponent<DynamicForceDetector>();
if (component == null || !component.CompareNameMask(Detector.Name.Player | Detector.Name.Probe | Detector.Name.Ship)) return;
if (component.GetName() == Detector.Name.Player && (PlayerState.IsInsideShip() || _playerJustExitedDream))
{
_playerJustExitedDream = false;
}
else
{
OWRigidbody attachedOWRigidbody = component.GetAttachedOWRigidbody();
Vector3 from = transform.position - attachedOWRigidbody.GetPosition();
Vector3 to = attachedOWRigidbody.GetVelocity() - _parentBody.GetVelocity();
float magnitude = to.magnitude;
if (magnitude > maxSpeed && Vector3.Angle(from, to) < maxEntryAngle)
{
float deceleration = (maxSpeed * maxSpeed - magnitude * magnitude) / (2f * stoppingDistance);
TrackedBody trackedBody = new TrackedBody(attachedOWRigidbody, component.GetName(), deceleration);
_trackedBodies.Add(trackedBody);
if (component.GetName() == Detector.Name.Ship)
GlobalMessenger.FireEvent("ShipEnterSpeedLimiter");
enabled = true;
}
}
}
public override void OnTriggerVolumeExit(GameObject hitObj)
{
DynamicForceDetector component = hitObj.GetComponent<DynamicForceDetector>();
if (component == null) return;
OWRigidbody body = component.GetAttachedOWRigidbody();
TrackedBody trackedBody = _trackedBodies.Find((TrackedBody i) => i.body == body);
if (trackedBody != null)
{
if (trackedBody.name == Detector.Name.Ship)
GlobalMessenger.FireEvent("ShipExitSpeedLimiter");
_trackedBodies.Remove(trackedBody);
if (_trackedBodies.Count == 0)
enabled = false;
}
}
[Serializable]
protected class TrackedBody
{
public OWRigidbody body;
public Detector.Name name;
public float deceleration;
public bool decelerated;
public TrackedBody(OWRigidbody body, Detector.Name name, float deceleration)
{
this.body = body;
this.name = name;
this.deceleration = deceleration;
}
public override bool Equals(object obj)
{
if (obj is TrackedBody trackedBody)
{
return trackedBody.body == body && trackedBody.name == name;
}
return base.Equals(obj);
}
public override int GetHashCode() => body.GetHashCode();
}
}
}

View File

@ -0,0 +1,28 @@
using Newtonsoft.Json;
using System.ComponentModel;
using UnityEngine;
namespace NewHorizons.External.Modules.Volumes.VolumeInfos
{
[JsonObject]
public class SpeedLimiterVolumeInfo : VolumeInfo
{
/// <summary>
/// The speed the volume will slow you down to when you enter it.
/// </summary>
[DefaultValue(10f)]
public float maxSpeed = 10f;
/// <summary>
///
/// </summary>
[DefaultValue(100f)]
public float stoppingDistance = 100f;
/// <summary>
///
/// </summary>
[DefaultValue(60f)]
public float maxEntryAngle = 60f;
}
}

View File

@ -101,6 +101,13 @@ namespace NewHorizons.External.Modules.Volumes
/// </summary>
public SpeedTrapVolumeInfo[] speedTrapVolumes;
/// <summary>
/// Add speed limiter volumes to this planet.
/// Slows down the player, ship, and probe when they enter this volume.
/// Used on the Stranger in DLC.
/// </summary>
public SpeedLimiterVolumeInfo[] speedLimiterVolumes;
/// <summary>
/// Add visor effect volumes to this planet.
/// </summary>