first commit
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
using NitroxClient.GameLogic;
|
||||
using NitroxModel.Packets;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NitroxClient.MonoBehaviours.Vehicles;
|
||||
|
||||
public class CyclopsMovementReplicator : VehicleMovementReplicator
|
||||
{
|
||||
protected static readonly int CYCLOPS_YAW = Animator.StringToHash("cyclops_yaw");
|
||||
protected static readonly int CYCLOPS_PITCH = Animator.StringToHash("cyclops_pitch");
|
||||
|
||||
private SubControl subControl;
|
||||
|
||||
private RemotePlayer drivingPlayer;
|
||||
private bool throttleApplied;
|
||||
private float steeringWheelYaw;
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
subControl = GetComponent<SubControl>();
|
||||
}
|
||||
|
||||
public new void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (subControl.canAccel && throttleApplied)
|
||||
{
|
||||
// See SubControl.Update
|
||||
var topClamp = subControl.useThrottleIndex switch
|
||||
{
|
||||
1 => 0.66f,
|
||||
2 => 1f,
|
||||
_ => 0.33f,
|
||||
};
|
||||
subControl.engineRPMManager.AccelerateInput(topClamp);
|
||||
for (int i = 0; i < subControl.throttleHandlers.Length; i++)
|
||||
{
|
||||
subControl.throttleHandlers[i].OnSubAppliedThrottle();
|
||||
}
|
||||
}
|
||||
|
||||
if (Mathf.Abs(steeringWheelYaw) > 0.1f)
|
||||
{
|
||||
ShipSide shipSide = steeringWheelYaw > 0 ? ShipSide.Port : ShipSide.Starboard;
|
||||
for (int i = 0; i < subControl.turnHandlers.Length; i++)
|
||||
{
|
||||
subControl.turnHandlers[i].OnSubTurn(shipSide);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void ApplyNewMovementData(MovementData newMovementData)
|
||||
{
|
||||
if (newMovementData is not DrivenVehicleMovementData vehicleMovementData)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
steeringWheelYaw = vehicleMovementData.SteeringWheelYaw;
|
||||
float steeringWheelPitch = vehicleMovementData.SteeringWheelPitch;
|
||||
|
||||
// See SubControl.UpdateAnimation
|
||||
subControl.steeringWheelYaw = steeringWheelYaw;
|
||||
subControl.steeringWheelPitch = steeringWheelPitch;
|
||||
if (subControl.mainAnimator)
|
||||
{
|
||||
subControl.mainAnimator.SetFloat(VIEW_YAW, subControl.steeringWheelYaw);
|
||||
subControl.mainAnimator.SetFloat(VIEW_PITCH, subControl.steeringWheelPitch);
|
||||
|
||||
if (drivingPlayer != null)
|
||||
{
|
||||
drivingPlayer.AnimationController.SetFloat(CYCLOPS_YAW, subControl.steeringWheelYaw);
|
||||
drivingPlayer.AnimationController.SetFloat(CYCLOPS_PITCH, subControl.steeringWheelPitch);
|
||||
}
|
||||
}
|
||||
|
||||
throttleApplied = vehicleMovementData.ThrottleApplied;
|
||||
}
|
||||
|
||||
public override void Enter(RemotePlayer drivingPlayer)
|
||||
{
|
||||
this.drivingPlayer = drivingPlayer;
|
||||
}
|
||||
|
||||
public override void Exit()
|
||||
{
|
||||
drivingPlayer = null;
|
||||
throttleApplied = false;
|
||||
}
|
||||
}
|
@@ -0,0 +1,134 @@
|
||||
using FMOD.Studio;
|
||||
using NitroxClient.GameLogic;
|
||||
using NitroxClient.GameLogic.FMOD;
|
||||
using NitroxModel.GameLogic.FMOD;
|
||||
using NitroxModel.Packets;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NitroxClient.MonoBehaviours.Vehicles;
|
||||
|
||||
public class ExosuitMovementReplicator : VehicleMovementReplicator
|
||||
{
|
||||
private Exosuit exosuit;
|
||||
|
||||
public Vector3 velocity;
|
||||
private float jetLoopingSoundDistance;
|
||||
private float thrustPower;
|
||||
private bool jetsActive;
|
||||
private float timeJetsActiveChanged;
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
exosuit = GetComponent<Exosuit>();
|
||||
SetupSound();
|
||||
}
|
||||
|
||||
public new void Update()
|
||||
{
|
||||
Vector3 positionBefore = transform.position;
|
||||
base.Update();
|
||||
Vector3 positionAfter = transform.position;
|
||||
|
||||
velocity = (positionAfter - positionBefore) / Time.deltaTime;
|
||||
|
||||
|
||||
float volume = FMODSystem.CalculateVolume(transform.position, Player.main.transform.position, jetLoopingSoundDistance, 1f);
|
||||
EventInstance soundHandle = exosuit.loopingJetSound.playing ? exosuit.loopingJetSound.evt : exosuit.loopingJetSound.evtStop;
|
||||
if (soundHandle.hasHandle())
|
||||
{
|
||||
soundHandle.setVolume(volume);
|
||||
}
|
||||
|
||||
// See Exosuit.Update, thrust power simulation
|
||||
|
||||
if (jetsActive)
|
||||
{
|
||||
thrustPower = Mathf.Clamp01(thrustPower - Time.deltaTime * exosuit.thrustConsumption);
|
||||
exosuit.thrustIntensity += Time.deltaTime / exosuit.timeForFullVirbation;
|
||||
}
|
||||
else
|
||||
{
|
||||
float num = Time.deltaTime * exosuit.thrustConsumption * 0.7f;
|
||||
if (exosuit.onGround)
|
||||
{
|
||||
num = Time.deltaTime * exosuit.thrustConsumption * 4f;
|
||||
}
|
||||
thrustPower = Mathf.Clamp01(thrustPower + num);
|
||||
exosuit.thrustIntensity -= Time.deltaTime * 10f;
|
||||
}
|
||||
exosuit.thrustIntensity = Mathf.Clamp01(exosuit.thrustIntensity);
|
||||
|
||||
if (timeJetsActiveChanged + 0.3f <= Time.time)
|
||||
{
|
||||
if (jetsActive && thrustPower > 0f)
|
||||
{
|
||||
exosuit.loopingJetSound.Play();
|
||||
exosuit.fxcontrol.Play(0);
|
||||
exosuit.areFXPlaying = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
exosuit.loopingJetSound.Stop();
|
||||
exosuit.fxcontrol.Stop(0);
|
||||
exosuit.areFXPlaying = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void ApplyNewMovementData(MovementData newMovementData)
|
||||
{
|
||||
if (newMovementData is not DrivenVehicleMovementData vehicleMovementData)
|
||||
{
|
||||
return;
|
||||
}
|
||||
float steeringWheelYaw = vehicleMovementData.SteeringWheelYaw;
|
||||
float steeringWheelPitch = vehicleMovementData.SteeringWheelPitch;
|
||||
|
||||
// See Vehicle.Update (reverse operation for vehicle.steeringWheel... = ...)
|
||||
exosuit.steeringWheelYaw = steeringWheelPitch / 70f;
|
||||
exosuit.steeringWheelPitch = steeringWheelPitch / 45f;
|
||||
|
||||
if (exosuit.mainAnimator)
|
||||
{
|
||||
exosuit.mainAnimator.SetFloat(VIEW_YAW, steeringWheelYaw);
|
||||
exosuit.mainAnimator.SetFloat(VIEW_PITCH, steeringWheelPitch);
|
||||
}
|
||||
|
||||
// See Exosuit.jetsActive setter
|
||||
if (jetsActive != vehicleMovementData.ThrottleApplied)
|
||||
{
|
||||
jetsActive = vehicleMovementData.ThrottleApplied;
|
||||
timeJetsActiveChanged = Time.time;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupSound()
|
||||
{
|
||||
this.Resolve<FMODWhitelist>().TryGetSoundData(exosuit.loopingJetSound.asset.path, out SoundData jetSoundData);
|
||||
jetLoopingSoundDistance = jetSoundData.Radius;
|
||||
|
||||
if (FMODUWE.IsInvalidParameterId(exosuit.fmodIndexSpeed))
|
||||
{
|
||||
exosuit.fmodIndexSpeed = exosuit.ambienceSound.GetParameterIndex("speed");
|
||||
}
|
||||
if (FMODUWE.IsInvalidParameterId(exosuit.fmodIndexRotate))
|
||||
{
|
||||
exosuit.fmodIndexRotate = exosuit.ambienceSound.GetParameterIndex("rotate");
|
||||
}
|
||||
}
|
||||
|
||||
public override void Enter(RemotePlayer remotePlayer)
|
||||
{
|
||||
exosuit.SetIKEnabled(true);
|
||||
exosuit.thrustIntensity = 0;
|
||||
}
|
||||
|
||||
public override void Exit()
|
||||
{
|
||||
exosuit.SetIKEnabled(false);
|
||||
exosuit.loopingJetSound.Stop(STOP_MODE.ALLOWFADEOUT);
|
||||
exosuit.fxcontrol.Stop(0);
|
||||
|
||||
jetsActive = false;
|
||||
}
|
||||
}
|
@@ -0,0 +1,120 @@
|
||||
using FMOD.Studio;
|
||||
using NitroxClient.GameLogic;
|
||||
using NitroxModel.GameLogic.FMOD;
|
||||
using NitroxModel.Packets;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NitroxClient.MonoBehaviours.Vehicles;
|
||||
|
||||
public class SeamothMovementReplicator : VehicleMovementReplicator
|
||||
{
|
||||
private SeaMoth seaMoth;
|
||||
|
||||
private FMOD_CustomLoopingEmitter rpmSound;
|
||||
private FMOD_CustomEmitter revSound;
|
||||
private FMOD_CustomEmitter enterSeamoth;
|
||||
|
||||
private float radiusRpmSound;
|
||||
private float radiusRevSound;
|
||||
private float radiusEnterSound;
|
||||
|
||||
private bool throttleApplied;
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
seaMoth = GetComponent<SeaMoth>();
|
||||
SetupSound();
|
||||
}
|
||||
|
||||
public new void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (throttleApplied)
|
||||
{
|
||||
seaMoth.engineSound.AccelerateInput(1);
|
||||
}
|
||||
}
|
||||
|
||||
public override void ApplyNewMovementData(MovementData newMovementData)
|
||||
{
|
||||
if (newMovementData is not DrivenVehicleMovementData vehicleMovementData)
|
||||
{
|
||||
return;
|
||||
}
|
||||
float steeringWheelYaw = vehicleMovementData.SteeringWheelYaw;
|
||||
float steeringWheelPitch = vehicleMovementData.SteeringWheelPitch;
|
||||
|
||||
// See Vehicle.Update (reverse operation for vehicle.steeringWheel... = ...)
|
||||
seaMoth.steeringWheelYaw = steeringWheelYaw / 70f;
|
||||
seaMoth.steeringWheelPitch = steeringWheelPitch / 45f;
|
||||
|
||||
if (seaMoth.mainAnimator)
|
||||
{
|
||||
seaMoth.mainAnimator.SetFloat(VIEW_YAW, steeringWheelYaw);
|
||||
seaMoth.mainAnimator.SetFloat(VIEW_PITCH, steeringWheelPitch);
|
||||
}
|
||||
|
||||
// Adjusting volume for the engine Sound
|
||||
float distanceToPlayer = Vector3.Distance(Player.main.transform.position, transform.position);
|
||||
float volumeRpmSound = SoundHelper.CalculateVolume(distanceToPlayer, radiusRpmSound, 1f);
|
||||
float volumeRevSound = SoundHelper.CalculateVolume(distanceToPlayer, radiusRevSound, 1f);
|
||||
rpmSound.GetEventInstance().setVolume(volumeRpmSound);
|
||||
revSound.GetEventInstance().setVolume(volumeRevSound);
|
||||
|
||||
throttleApplied = vehicleMovementData.ThrottleApplied;
|
||||
}
|
||||
|
||||
private void SetupSound()
|
||||
{
|
||||
rpmSound = seaMoth.engineSound.engineRpmSFX;
|
||||
revSound = seaMoth.engineSound.engineRevUp;
|
||||
enterSeamoth = seaMoth.enterSeamoth;
|
||||
|
||||
rpmSound.followParent = true;
|
||||
revSound.followParent = true;
|
||||
|
||||
this.Resolve<FMODWhitelist>().IsWhitelisted(rpmSound.asset.path, out radiusRpmSound);
|
||||
this.Resolve<FMODWhitelist>().IsWhitelisted(revSound.asset.path, out radiusRevSound);
|
||||
this.Resolve<FMODWhitelist>().IsWhitelisted(seaMoth.enterSeamoth.asset.path, out radiusEnterSound);
|
||||
|
||||
rpmSound.GetEventInstance().setProperty(EVENT_PROPERTY.MINIMUM_DISTANCE, 1f);
|
||||
revSound.GetEventInstance().setProperty(EVENT_PROPERTY.MINIMUM_DISTANCE, 1f);
|
||||
enterSeamoth.GetEventInstance().setProperty(EVENT_PROPERTY.MINIMUM_DISTANCE, 1f);
|
||||
|
||||
rpmSound.GetEventInstance().setProperty(EVENT_PROPERTY.MAXIMUM_DISTANCE, radiusRpmSound);
|
||||
revSound.GetEventInstance().setProperty(EVENT_PROPERTY.MAXIMUM_DISTANCE, radiusRevSound);
|
||||
enterSeamoth.GetEventInstance().setProperty(EVENT_PROPERTY.MAXIMUM_DISTANCE, radiusEnterSound);
|
||||
|
||||
|
||||
if (FMODUWE.IsInvalidParameterId(seaMoth.fmodIndexSpeed))
|
||||
{
|
||||
seaMoth.fmodIndexSpeed = seaMoth.ambienceSound.GetParameterIndex("speed");
|
||||
}
|
||||
}
|
||||
|
||||
public override void Enter(RemotePlayer remotePlayer)
|
||||
{
|
||||
seaMoth.bubbles.Play();
|
||||
if (enterSeamoth)
|
||||
{
|
||||
// After first run, this sound will still be in "playing" mode so we need to release it by hand
|
||||
enterSeamoth.Stop();
|
||||
enterSeamoth.ReleaseEvent();
|
||||
enterSeamoth.CacheEventInstance();
|
||||
|
||||
float distanceToPlayer = Vector3.Distance(Player.main.transform.position, transform.position);
|
||||
float sound = SoundHelper.CalculateVolume(distanceToPlayer, radiusEnterSound, 1f);
|
||||
enterSeamoth.evt.setVolume(sound);
|
||||
|
||||
enterSeamoth.Play();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Exit()
|
||||
{
|
||||
seaMoth.bubbles.Stop();
|
||||
|
||||
throttleApplied = false;
|
||||
}
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
using NitroxClient.GameLogic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NitroxClient.MonoBehaviours.Vehicles;
|
||||
|
||||
public abstract class VehicleMovementReplicator : MovementReplicator
|
||||
{
|
||||
protected static readonly int VIEW_YAW = Animator.StringToHash("view_yaw");
|
||||
protected static readonly int VIEW_PITCH = Animator.StringToHash("view_pitch");
|
||||
|
||||
public abstract void Enter(RemotePlayer remotePlayer);
|
||||
public abstract void Exit();
|
||||
}
|
147
NitroxClient/MonoBehaviours/Vehicles/WatchedEntry.cs
Normal file
147
NitroxClient/MonoBehaviours/Vehicles/WatchedEntry.cs
Normal file
@@ -0,0 +1,147 @@
|
||||
using NitroxModel.DataStructures;
|
||||
using NitroxModel.Packets;
|
||||
using NitroxModel_Subnautica.DataStructures;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NitroxClient.MonoBehaviours.Vehicles;
|
||||
|
||||
public class WatchedEntry
|
||||
{
|
||||
/// <remarks>
|
||||
/// In unity position units. Refer to <see cref="ShouldBroadcastMovement"/> for use infos.
|
||||
/// </remarks>
|
||||
private const float MINIMAL_MOVEMENT_TRESHOLD = 0.05f;
|
||||
/// <remarks>
|
||||
/// In degrees (°). Refer to <see cref="ShouldBroadcastMovement"/> for use infos.
|
||||
/// </remarks>
|
||||
private const float MINIMAL_ROTATION_TRESHOLD = 0.05f;
|
||||
/// <remarks>
|
||||
/// In seconds. Refer to <see cref="ShouldBroadcastMovement"/> for use infos.
|
||||
/// </remarks>
|
||||
private const float MAX_TIME_WITHOUT_BROADCAST = 5f;
|
||||
/// <inheritdoc cref="MAX_TIME_WITHOUT_BROADCAST"/>
|
||||
private const float SAFETY_BROADCAST_WINDOW = 0.2f;
|
||||
|
||||
private readonly NitroxId Id;
|
||||
private readonly Transform transform;
|
||||
private readonly Vehicle vehicle;
|
||||
private readonly SubControl subControl;
|
||||
|
||||
private float latestBroadcastTime;
|
||||
private Vector3 latestLocalPositionSent;
|
||||
private Quaternion latestLocalRotationSent;
|
||||
|
||||
public WatchedEntry(NitroxId Id, Transform transform)
|
||||
{
|
||||
this.Id = Id;
|
||||
this.transform = transform;
|
||||
vehicle = transform.GetComponent<Vehicle>();
|
||||
subControl = transform.GetComponent<SubControl>();
|
||||
}
|
||||
|
||||
private bool IsDrivenVehicle()
|
||||
{
|
||||
return vehicle && Player.main.currentMountedVehicle == vehicle;
|
||||
}
|
||||
|
||||
private bool IsDrivenCyclops()
|
||||
{
|
||||
return subControl && Player.main.currentSub == subControl.sub && Player.main.mode == Player.Mode.Piloting;
|
||||
}
|
||||
|
||||
public MovementData GetMovementData(NitroxId id)
|
||||
{
|
||||
// Packets should be filled with more data if the vehicle is being driven by the local player
|
||||
if (IsDrivenVehicle())
|
||||
{
|
||||
// Those two values are set between -1 and 1 so we can easily scale them up while still in range for sbyte
|
||||
sbyte steeringWheelYaw = (sbyte)(Mathf.Clamp(vehicle.steeringWheelYaw, -1, 1) * 70f);
|
||||
sbyte steeringWheelPitch = (sbyte)(Mathf.Clamp(vehicle.steeringWheelPitch, -1, 1) * 45f);
|
||||
|
||||
bool throttleApplied = false;
|
||||
|
||||
Vector3 input = AvatarInputHandler.main.IsEnabled() ? GameInput.GetMoveDirection() : Vector3.zero;
|
||||
// See SeaMoth.UpdateSounds
|
||||
if (vehicle is SeaMoth)
|
||||
{
|
||||
throttleApplied = input.magnitude > 0f;
|
||||
}
|
||||
// See Exosuit.Update
|
||||
else if (vehicle is Exosuit)
|
||||
{
|
||||
throttleApplied = input.y > 0f;
|
||||
}
|
||||
|
||||
return new DrivenVehicleMovementData(id, transform.position.ToDto(), transform.rotation.ToDto(), steeringWheelYaw, steeringWheelPitch, throttleApplied);
|
||||
}
|
||||
|
||||
if (IsDrivenCyclops())
|
||||
{
|
||||
// Cyclop steering wheel's yaw and pitch are between -90 and 90 so they're already in range for sbyte
|
||||
sbyte steeringWheelYaw = (sbyte)Mathf.Clamp(subControl.steeringWheelYaw, -90, 90);
|
||||
sbyte steeringWheelPitch = (sbyte)Mathf.Clamp(subControl.steeringWheelPitch, -90, 90);
|
||||
|
||||
// See SubControl.Update
|
||||
bool throttleApplied = subControl.throttle.magnitude > 0.0001f;
|
||||
|
||||
return new DrivenVehicleMovementData(id, transform.position.ToDto(), transform.rotation.ToDto(), steeringWheelYaw, steeringWheelPitch, throttleApplied);
|
||||
}
|
||||
|
||||
// Normal case in which the vehicule isn't driven by the local player
|
||||
return new SimpleMovementData(id, transform.position.ToDto(), transform.rotation.ToDto());
|
||||
}
|
||||
|
||||
public void OnBroadcastPosition()
|
||||
{
|
||||
latestLocalPositionSent = transform.localPosition;
|
||||
latestLocalRotationSent = transform.localRotation;
|
||||
}
|
||||
|
||||
private bool HasVehicleMoved()
|
||||
{
|
||||
return Vector3.Distance(latestLocalPositionSent, transform.localPosition) > MINIMAL_MOVEMENT_TRESHOLD ||
|
||||
Quaternion.Angle(latestLocalRotationSent, transform.localRotation) > MINIMAL_ROTATION_TRESHOLD;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rate limiter which prevents all non-moving vehicles from sending too many packets following some rules:
|
||||
/// - the driven vehicle is not rate limited
|
||||
/// - position changes less than <see cref="MINIMAL_MOVEMENT_TRESHOLD"/> are ignored
|
||||
/// - rotation changes less than <see cref="MINIMAL_ROTATION_TRESHOLD"/> are ignored
|
||||
/// - every period of <see cref="MAX_TIME_WITHOUT_BROADCAST"/>, there's a <see cref="SAFETY_BROADCAST_WINDOW"/>
|
||||
/// during which movements packets are sent to avoid any packet drop's bad effect, regardless of <see cref="HasVehicleMoved"/>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <see cref="latestBroadcastTime"/> is not updated during the <see cref="SAFETY_BROADCAST_WINDOW"/> so we can recognize this window
|
||||
/// </remarks>
|
||||
public bool ShouldBroadcastMovement()
|
||||
{
|
||||
// Watched entry validity check (e.g. for vehicle death)
|
||||
if (!transform)
|
||||
{
|
||||
MovementBroadcaster.UnregisterWatched(Id);
|
||||
return false;
|
||||
}
|
||||
|
||||
float deltaTimeSinceBroadcast = DayNightCycle.main.timePassedAsFloat - latestBroadcastTime;
|
||||
|
||||
if (IsDrivenCyclops() || IsDrivenVehicle() || deltaTimeSinceBroadcast < 0 || HasVehicleMoved())
|
||||
{
|
||||
// As long as the vehicle has moved, we can reset the broadcast timer
|
||||
latestBroadcastTime = DayNightCycle.main.timePassedAsFloat;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (deltaTimeSinceBroadcast > MAX_TIME_WITHOUT_BROADCAST)
|
||||
{
|
||||
if (deltaTimeSinceBroadcast > MAX_TIME_WITHOUT_BROADCAST + SAFETY_BROADCAST_WINDOW)
|
||||
{
|
||||
// only reset the broadcast timer after the safety window has elapsed
|
||||
latestBroadcastTime = DayNightCycle.main.timePassedAsFloat;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user