Merge pull request #566

More Sound Sync
This commit is contained in:
Will Corby 2022-08-28 15:31:40 -07:00 committed by GitHub
commit 4a4daedc80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 783 additions and 21 deletions

View File

@ -93,6 +93,7 @@ public class AnimationSync : PlayerSyncObject
SetSuitState(QSBSceneManager.CurrentScene == OWScene.EyeOfTheUniverse); SetSuitState(QSBSceneManager.CurrentScene == OWScene.EyeOfTheUniverse);
InitAccelerationSync(); InitAccelerationSync();
ThrusterManager.CreateRemotePlayerVFX(Player); ThrusterManager.CreateRemotePlayerVFX(Player);
ThrusterManager.CreateRemotePlayerSFX(Player);
Delay.RunWhen(() => Player.CameraBody != null, Delay.RunWhen(() => Player.CameraBody != null,
() => body.GetComponent<PlayerHeadRotationSync>().Init(Player.CameraBody.transform)); () => body.GetComponent<PlayerHeadRotationSync>().Init(Player.CameraBody.transform));

View File

@ -37,6 +37,15 @@ public class PlayerSuitMessage : QSBMessage<bool>
var animator = player.AnimationSync; var animator = player.AnimationSync;
animator.SetSuitState(Data); animator.SetSuitState(Data);
if (player.SuitedUp)
{
player.AudioController.PlayWearSuit();
}
else
{
player.AudioController.PlayRemoveSuit();
}
} }
public override void OnReceiveLocal() public override void OnReceiveLocal()

View File

@ -1,4 +1,5 @@
using QSB.Player; using QSB.Audio;
using QSB.Player;
using UnityEngine; using UnityEngine;
namespace QSB.Animation.Player.Thrusters; namespace QSB.Animation.Player.Thrusters;
@ -14,6 +15,11 @@ internal static class ThrusterManager
InitParticleControllers(newVfx, player); InitParticleControllers(newVfx, player);
} }
public static void CreateRemotePlayerSFX(PlayerInfo player)
{
player.Body.GetComponentInChildren<QSBJetpackThrusterAudio>(true)?.Init(player);
}
private static void InitFlameControllers(GameObject root, PlayerInfo player) private static void InitFlameControllers(GameObject root, PlayerInfo player)
{ {
var existingControllers = root.GetComponentsInChildren<RemoteThrusterFlameController>(true); var existingControllers = root.GetComponentsInChildren<RemoteThrusterFlameController>(true);

View File

@ -0,0 +1,16 @@
using QSB.Messaging;
using QSB.Player;
using QSB.WorldSync;
namespace QSB.Audio.Messages;
public class PlayerAudioControllerOneShotMessage : QSBMessage<(AudioType audioType, uint userID, float pitch, float volume)>
{
public PlayerAudioControllerOneShotMessage(AudioType audioType, uint userID, float pitch = 1f, float volume = 1f) : base((audioType, userID, pitch, volume)) { }
public override bool ShouldReceive => QSBWorldSync.AllObjectsReady;
public override void OnReceiveRemote() =>
QSBPlayerManager.GetPlayer(Data.userID)?.AudioController?.PlayOneShot(Data.audioType, Data.pitch, Data.volume);
}

View File

@ -0,0 +1,16 @@
using QSB.Messaging;
using QSB.Player;
using QSB.WorldSync;
namespace QSB.Audio.Messages;
public class PlayerMovementAudioFootstepMessage : QSBMessage<(AudioType audioType, float pitch, uint userID)>
{
public PlayerMovementAudioFootstepMessage(AudioType audioType, float pitch, uint userID) : base((audioType, pitch, userID)) { }
public override bool ShouldReceive => QSBWorldSync.AllObjectsReady;
public override void OnReceiveRemote() =>
QSBPlayerManager.GetPlayer(Data.userID)?.AudioController?.PlayFootstep(Data.audioType, Data.pitch);
}

View File

@ -0,0 +1,16 @@
using QSB.Messaging;
using QSB.Player;
using QSB.WorldSync;
namespace QSB.Audio.Messages;
public class PlayerMovementAudioJumpMessage : QSBMessage<(float pitch, uint userID)>
{
public PlayerMovementAudioJumpMessage(float pitch, uint userID) : base((pitch, userID)) { }
public override bool ShouldReceive => QSBWorldSync.AllObjectsReady;
public override void OnReceiveRemote() =>
QSBPlayerManager.GetPlayer(Data.userID)?.AudioController?.OnJump(Data.pitch);
}

View File

@ -0,0 +1,21 @@
using QSB.Messaging;
using QSB.ShipSync;
using QSB.WorldSync;
namespace QSB.Audio.Messages;
public class ShipThrusterAudioOneShotMessage : QSBMessage<(AudioType audioType, float pitch, float volume)>
{
public ShipThrusterAudioOneShotMessage(AudioType audioType, float pitch = 1f, float volume = 1f) : base((audioType, pitch, volume)) { }
public override bool ShouldReceive => QSBWorldSync.AllObjectsReady;
public override void OnReceiveRemote()
{
var source = ShipManager.Instance.ShipThrusterAudio._rotationalSource;
source.pitch = Data.pitch;
source.PlayOneShot(Data.audioType, Data.volume);
}
}

View File

@ -0,0 +1,36 @@
using HarmonyLib;
using QSB.Audio.Messages;
using QSB.Messaging;
using QSB.Patches;
using QSB.Player;
namespace QSB.Audio.Patches;
[HarmonyPatch]
internal class PlayerAudioControllerPatches : QSBPatch
{
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
private static void PlayOneShot(AudioType audioType) =>
new PlayerAudioControllerOneShotMessage(audioType, QSBPlayerManager.LocalPlayerId).Send();
[HarmonyPostfix]
[HarmonyPatch(typeof(PlayerAudioController), nameof(PlayerAudioController.PlayMarshmallowEat))]
public static void PlayerAudioController_PlayMarshmallowEat() => PlayOneShot(AudioType.ToolMarshmallowEat);
[HarmonyPostfix]
[HarmonyPatch(typeof(PlayerAudioController), nameof(PlayerAudioController.PlayMarshmallowEatBurnt))]
public static void PlayerAudioController_PlayMarshmallowEatBurnt() => PlayOneShot(AudioType.ToolMarshmallowEatBurnt);
[HarmonyPostfix]
[HarmonyPatch(typeof(PlayerAudioController), nameof(PlayerAudioController.PlayPatchPuncture))]
public static void PlayerAudioController_PlayPatchPuncture() => PlayOneShot(AudioType.PlayerSuitPatchPuncture);
[HarmonyPostfix]
[HarmonyPatch(typeof(PlayerAudioController), nameof(PlayerAudioController.PlayMedkit))]
public static void PlayerAudioController_PlayMedkit() => PlayOneShot(AudioType.ShipCabinUseMedkit);
[HarmonyPostfix]
[HarmonyPatch(typeof(PlayerAudioController), nameof(PlayerAudioController.PlayRefuel))]
public static void PlayerAudioController_PlayRefuel() => PlayOneShot(AudioType.ShipCabinUseRefueller);
}

View File

@ -0,0 +1,40 @@
using HarmonyLib;
using QSB.Audio.Messages;
using QSB.Messaging;
using QSB.Patches;
using QSB.Player;
namespace QSB.Audio.Patches;
internal class PlayerImpactAudioPatches : QSBPatch
{
// Since we patch Start we do it when the mod starts, else it won't run
public override QSBPatchTypes Type => QSBPatchTypes.OnModStart;
[HarmonyPostfix]
[HarmonyPatch(typeof(PlayerImpactAudio), nameof(PlayerImpactAudio.Start))]
public static void PlayerImpactAudio_Start(PlayerImpactAudio __instance)
{
__instance.gameObject.AddComponent<QSBAudioSourceOneShotTracker>();
}
[HarmonyPrefix]
[HarmonyPatch(typeof(PlayerImpactAudio), nameof(PlayerImpactAudio.OnImpact))]
public static void PlayerImpactAudio_OnImpact_Prefix(PlayerImpactAudio __instance) =>
// First we reset in case no audio is actually played
__instance.gameObject.GetComponent<QSBAudioSourceOneShotTracker>()?.Reset();
[HarmonyPostfix]
[HarmonyPatch(typeof(PlayerImpactAudio), nameof(PlayerImpactAudio.OnImpact))]
public static void PlayerImpactAudio_OnImpact_Postfix(PlayerImpactAudio __instance)
{
var tracker = __instance.gameObject.GetComponent<QSBAudioSourceOneShotTracker>();
if (tracker)
{
if (tracker.LastPlayed != AudioType.None)
{
new PlayerAudioControllerOneShotMessage(tracker.LastPlayed, QSBPlayerManager.LocalPlayerId, tracker.Pitch, tracker.Volume).Send();
}
}
}
}

View File

@ -0,0 +1,32 @@
using HarmonyLib;
using QSB.Audio.Messages;
using QSB.Messaging;
using QSB.Patches;
using QSB.Player;
namespace QSB.Audio.Patches;
internal class PlayerMovementAudioPatches : QSBPatch
{
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
[HarmonyPostfix]
[HarmonyPatch(typeof(PlayerMovementAudio), nameof(PlayerMovementAudio.PlayFootstep))]
public static void PlayerMovementAudio_PlayFootstep(PlayerMovementAudio __instance)
{
var underwater = !PlayerState.IsCameraUnderwater() && __instance._fluidDetector.InFluidType(FluidVolume.Type.WATER);
var audioType = underwater ? AudioType.MovementShallowWaterFootstep : PlayerMovementAudio.GetFootstepAudioType(__instance._playerController.GetGroundSurface());
if (audioType != AudioType.None)
{
new PlayerMovementAudioFootstepMessage(audioType, __instance._footstepAudio.pitch, QSBPlayerManager.LocalPlayerId).Send();
}
}
[HarmonyPostfix]
[HarmonyPatch(typeof(PlayerMovementAudio), nameof(PlayerMovementAudio.OnJump))]
public static void PlayerMovementAudio_OnJump(PlayerMovementAudio __instance)
{
new PlayerMovementAudioJumpMessage(__instance._jumpAudio.pitch, QSBPlayerManager.LocalPlayerId).Send();
}
}

View File

@ -0,0 +1,42 @@
using HarmonyLib;
using QSB.Audio.Messages;
using QSB.Messaging;
using QSB.Patches;
namespace QSB.Audio.Patches;
internal class ThrusterAudioPatches : QSBPatch
{
// Since we patch Start we do it when the mod starts, else it won't run
public override QSBPatchTypes Type => QSBPatchTypes.OnModStart;
[HarmonyPostfix]
[HarmonyPatch(typeof(ThrusterAudio), nameof(ThrusterAudio.Start))]
public static void ThrusterAudio_Start(ThrusterAudio __instance)
{
__instance._rotationalSource.gameObject.AddComponent<QSBAudioSourceOneShotTracker>();
}
[HarmonyPrefix]
[HarmonyPatch(typeof(ThrusterAudio), nameof(ThrusterAudio.OnFireRotationalThruster))]
public static void ThrusterAudio_OnFireRotationalThruster_Prefix(ThrusterAudio __instance) =>
// First we reset in case no audio is actually played
__instance._rotationalSource.gameObject.GetComponent<QSBAudioSourceOneShotTracker>()?.Reset();
[HarmonyPostfix]
[HarmonyPatch(typeof(ThrusterAudio), nameof(ThrusterAudio.OnFireRotationalThruster))]
public static void ThrusterAudio_OnFireRotationalThruster_Postfix(ThrusterAudio __instance)
{
if (__instance._rotationalSource.gameObject.TryGetComponent<QSBAudioSourceOneShotTracker>(out var tracker))
{
if (tracker.LastPlayed != AudioType.None)
{
if (__instance is ShipThrusterAudio)
{
new ShipThrusterAudioOneShotMessage(tracker.LastPlayed, tracker.Pitch, tracker.Volume).Send();
}
// TODO: Apply to player jetpack thruster?
}
}
}
}

View File

@ -0,0 +1,63 @@
using HarmonyLib;
using QSB.Patches;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace QSB.Audio;
/// <summary>
/// tracks what audioType was last played on when PlayOneShot is called on an OWAudioSource
/// makes it easier to send a message afterwards syncing what was just played
/// </summary>
[RequireComponent(typeof(OWAudioSource))]
public class QSBAudioSourceOneShotTracker : MonoBehaviour
{
public AudioType LastPlayed { get; internal set; }
public float Pitch { get => _source.pitch; }
public float Volume { get; internal set; }
public int Index { get; internal set; }
public void Reset() => LastPlayed = AudioType.None;
private OWAudioSource _source;
public void Awake()
{
_source = GetComponent<OWAudioSource>();
}
}
[HarmonyPatch(typeof(OWAudioSource))]
internal class OneShotTrackerPatches : QSBPatch
{
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
[HarmonyPostfix]
[HarmonyPatch(nameof(OWAudioSource.PlayOneShot), new[] { typeof(AudioType), typeof(float) })]
private static void TrackOneShot_AudioType(OWAudioSource __instance, AudioType type, float volume)
{
var tracker = __instance.gameObject.GetComponent<QSBAudioSourceOneShotTracker>();
if (tracker)
{
tracker.LastPlayed = type;
tracker.Volume = volume;
tracker.Index = -1;
}
}
[HarmonyPostfix]
[HarmonyPatch(nameof(OWAudioSource.PlayOneShot), new[] { typeof(AudioType), typeof(int), typeof(float) })]
private static void TrackOneShot_AudioType(OWAudioSource __instance, AudioType type, int index, float volume)
{
var tracker = __instance.gameObject.GetComponent<QSBAudioSourceOneShotTracker>();
if (tracker)
{
tracker.LastPlayed = type;
tracker.Volume = volume;
tracker.Index = index;
}
}
}

View File

@ -1,4 +1,6 @@
using QSB.Utility; using QSB.Player;
using QSB.Utility;
using UnityEngine;
namespace QSB.Audio; namespace QSB.Audio;
@ -8,4 +10,90 @@ internal class QSBJetpackThrusterAudio : QSBThrusterAudio
public OWAudioSource _underwaterSource; public OWAudioSource _underwaterSource;
public OWAudioSource _oxygenSource; public OWAudioSource _oxygenSource;
public OWAudioSource _boostSource; public OWAudioSource _boostSource;
private PlayerInfo _attachedPlayer;
private bool _wasBoosting;
// Taken from Player_Body settings
private const float maxTranslationalThrust = 6f;
private bool _underwater;
private RemotePlayerFluidDetector _fluidDetector;
public void Init(PlayerInfo player)
{
_attachedPlayer = player;
enabled = true;
_fluidDetector = player.FluidDetector;
_fluidDetector.OnEnterFluidType += OnEnterExitFluidType;
_fluidDetector.OnExitFluidType += OnEnterExitFluidType;
}
private void OnDestroy()
{
if (_fluidDetector != null)
{
_fluidDetector.OnEnterFluidType -= OnEnterExitFluidType;
_fluidDetector.OnExitFluidType -= OnEnterExitFluidType;
}
}
private void OnEnterExitFluidType(FluidVolume.Type type)
{
_underwater = _fluidDetector.InFluidType(FluidVolume.Type.WATER);
}
private void Update()
{
if(_attachedPlayer == null)
{
enabled = false;
return;
}
var acc = _attachedPlayer.JetpackAcceleration.AccelerationVariableSyncer.Value;
var thrustFraction = acc.magnitude / maxTranslationalThrust;
// TODO: Sync
var usingBooster = false;
var usingOxygen = false;
float targetVolume = usingBooster ? 0f : thrustFraction;
float targetPan = -acc.x / maxTranslationalThrust * 0.4f;
UpdateTranslationalSource(_translationalSource, targetVolume, targetPan, !_underwater && !usingOxygen);
UpdateTranslationalSource(_underwaterSource, targetVolume, targetPan, _underwater);
UpdateTranslationalSource(_oxygenSource, targetVolume, targetPan, !_underwater && usingOxygen);
if (!_wasBoosting && usingBooster)
{
_boostSource.FadeIn(0.3f, false, false, 1f);
}
else if (_wasBoosting && !usingBooster)
{
_boostSource.FadeOut(0.3f, OWAudioSource.FadeOutCompleteAction.STOP, 0f);
}
_wasBoosting = usingBooster;
}
private void UpdateTranslationalSource(OWAudioSource source, float targetVolume, float targetPan, bool active)
{
if (!active)
{
targetVolume = 0f;
targetPan = 0f;
}
if (!source.isPlaying && targetVolume > 0f)
{
source.SetLocalVolume(0f);
source.Play();
}
else if (source.isPlaying && source.volume <= 0f)
{
source.Stop();
}
source.SetLocalVolume(Mathf.MoveTowards(source.GetLocalVolume(), targetVolume, 5f * Time.deltaTime));
source.panStereo = Mathf.MoveTowards(source.panStereo, targetPan, 5f * Time.deltaTime);
}
} }

View File

@ -20,4 +20,25 @@ public class QSBPlayerAudioController : MonoBehaviour
public void PlayTurnOffFlashlight() public void PlayTurnOffFlashlight()
=> _oneShotExternalSource?.PlayOneShot(AudioType.ToolFlashlightOff); => _oneShotExternalSource?.PlayOneShot(AudioType.ToolFlashlightOff);
public void PlayWearSuit()
=> PlayOneShot(AudioType.PlayerSuitWearSuit);
public void PlayRemoveSuit()
=> PlayOneShot(AudioType.PlayerSuitRemoveSuit);
public void PlayOneShot(AudioType audioType, float pitch = 1f, float volume = 1f)
{
if (_oneShotExternalSource)
{
_oneShotExternalSource.pitch = pitch;
_oneShotExternalSource.PlayOneShot(audioType, volume);
}
}
public void PlayFootstep(AudioType audioType, float pitch) =>
PlayOneShot(audioType, pitch, 0.7f);
public void OnJump(float pitch) =>
PlayOneShot(AudioType.MovementJump, pitch, 0.7f);
} }

View File

@ -33,4 +33,7 @@ public static class OWEvents
public const string ExitDreamWorld = nameof(ExitDreamWorld); public const string ExitDreamWorld = nameof(ExitDreamWorld);
public const string EnterRemoteFlightConsole = nameof(EnterRemoteFlightConsole); public const string EnterRemoteFlightConsole = nameof(EnterRemoteFlightConsole);
public const string ExitRemoteFlightConsole = nameof(ExitRemoteFlightConsole); public const string ExitRemoteFlightConsole = nameof(ExitRemoteFlightConsole);
public const string StartShipIgnition = nameof(StartShipIgnition);
public const string CompleteShipIgnition = nameof(CompleteShipIgnition);
public const string CancelShipIgnition = nameof(CancelShipIgnition);
} }

View File

@ -0,0 +1,16 @@
using QSB.Messaging;
using QSB.WorldSync;
namespace QSB.ModelShip.Messages;
internal class CrashModelShipMessage : QSBMessage
{
public CrashModelShipMessage() { }
public override void OnReceiveRemote()
{
var crashBehaviour = QSBWorldSync.GetUnityObject<ModelShipCrashBehavior>();
crashBehaviour._crashEffect.Play();
crashBehaviour.gameObject.GetComponent<OWAudioSource>().PlayOneShot(AudioType.TH_ModelShipCrash);
}
}

View File

@ -8,6 +8,10 @@ internal class RespawnModelShipMessage : QSBMessage<bool>
{ {
public RespawnModelShipMessage(bool playEffects) : base(playEffects) { } public RespawnModelShipMessage(bool playEffects) : base(playEffects) { }
public override void OnReceiveRemote() => public override void OnReceiveRemote()
QSBPatch.RemoteCall(() => QSBWorldSync.GetUnityObject<RemoteFlightConsole>().RespawnModelShip(Data)); {
var flightConsole = QSBWorldSync.GetUnityObject<RemoteFlightConsole>();
QSBPatch.RemoteCall(() => flightConsole.RespawnModelShip(Data));
if (Data) flightConsole._modelShipBody.GetComponent<OWAudioSource>().PlayOneShot(AudioType.TH_RetrieveModelShip);
}
} }

View File

@ -1,6 +1,7 @@
using QSB.AuthoritySync; using QSB.AuthoritySync;
using QSB.Messaging; using QSB.Messaging;
using QSB.ModelShip.TransformSync; using QSB.ModelShip.TransformSync;
using QSB.Patches;
using QSB.Player; using QSB.Player;
using QSB.Player.TransformSync; using QSB.Player.TransformSync;
using QSB.Utility; using QSB.Utility;
@ -26,20 +27,14 @@ internal class UseFlightConsoleMessage : QSBMessage<bool>
private UseFlightConsoleMessage(bool active) : base(active) { } private UseFlightConsoleMessage(bool active) : base(active) { }
public override void OnReceiveLocal() public override void OnReceiveLocal() => SetCurrentFlyer(From, Data);
{
if (QSBCore.IsHost)
{
ModelShipTransformSync.LocalInstance.netIdentity.SetAuthority(Data
? From
: QSBPlayerManager.LocalPlayerId);
}
}
public override void OnReceiveRemote() public override void OnReceiveRemote()
{ {
var console = QSBWorldSync.GetUnityObject<RemoteFlightConsole>(); var console = QSBWorldSync.GetUnityObject<RemoteFlightConsole>();
SetCurrentFlyer(From, Data);
if (Data) if (Data)
{ {
console._modelShipBody.Unsuspend(); console._modelShipBody.Unsuspend();
@ -62,12 +57,24 @@ internal class UseFlightConsoleMessage : QSBMessage<bool>
QSBWorldSync.GetUnityObject<ModelShipController>()._detector.SetActive(Data); QSBWorldSync.GetUnityObject<ModelShipController>()._detector.SetActive(Data);
QSBWorldSync.GetUnityObjects<ModelShipLandingSpot>().ForEach(x => x._owCollider.SetActivation(Data)); QSBWorldSync.GetUnityObjects<ModelShipLandingSpot>().ForEach(x => x._owCollider.SetActivation(Data));
}
private void SetCurrentFlyer(uint flyer, bool isFlying)
{
ModelShipManager.Instance.CurrentFlyer = isFlying
? flyer
: uint.MaxValue;
if (QSBCore.IsHost) if (QSBCore.IsHost)
{ {
ModelShipTransformSync.LocalInstance.netIdentity.SetAuthority(Data ModelShipTransformSync.LocalInstance.netIdentity.SetAuthority(isFlying
? From ? flyer
: QSBPlayerManager.LocalPlayerId); : QSBPlayerManager.LocalPlayerId); // Host gets authority when its not in use
} }
// Client messes up its position when they start flying it
// We can just recall it immediately so its in the right place.
var console = QSBWorldSync.GetUnityObject<RemoteFlightConsole>();
QSBPatch.RemoteCall(() => console.RespawnModelShip(false));
} }
} }

View File

@ -13,12 +13,37 @@ internal class ModelShipManager : WorldObjectManager
public override WorldObjectScene WorldObjectScene => WorldObjectScene.SolarSystem; public override WorldObjectScene WorldObjectScene => WorldObjectScene.SolarSystem;
public override bool DlcOnly => false; public override bool DlcOnly => false;
public static ModelShipManager Instance;
public uint CurrentFlyer
{
get => _currentFlyer;
set
{
if (_currentFlyer != uint.MaxValue && value != uint.MaxValue)
{
DebugLog.ToConsole($"Warning - Trying to set current model ship flyer while someone is still flying? Current:{_currentFlyer}, New:{value}", MessageType.Warning);
}
_currentFlyer = value;
}
}
private uint _currentFlyer = uint.MaxValue;
public void Start()
{
Instance = this;
}
public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct) public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct)
{ {
if (QSBCore.IsHost) if (QSBCore.IsHost)
{ {
Instantiate(QSBNetworkManager.singleton.ModelShipPrefab).SpawnWithServerAuthority(); Instantiate(QSBNetworkManager.singleton.ModelShipPrefab).SpawnWithServerAuthority();
} }
// Is 0 by default -> 2D (bad)
QSBWorldSync.GetUnityObject<RemoteFlightConsole>()._consoleAudio.spatialBlend = 1;
} }
public override void UnbuildWorldObjects() public override void UnbuildWorldObjects()

View File

@ -0,0 +1,76 @@
using Mirror;
using QSB.Player;
using QSB.Utility;
using QSB.Utility.VariableSync;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace QSB.ModelShip;
public class ModelShipThrusterVariableSyncer : MonoBehaviour
{
public Vector3VariableSyncer AccelerationSyncer;
public ThrusterModel ThrusterModel { get; private set; }
private ThrusterAudio _thrusterAudio;
public List<ThrusterFlameController> ThrusterFlameControllers = new();
public ThrusterWashController ThrusterWashController { get; private set; }
public void Init(GameObject modelShip)
{
ThrusterModel = modelShip.GetComponent<ThrusterModel>();
_thrusterAudio = modelShip.GetComponentInChildren<ThrusterAudio>();
ThrusterFlameControllers.Clear();
foreach (var item in modelShip.GetComponentsInChildren<ThrusterFlameController>())
{
ThrusterFlameControllers.Add(item);
}
ThrusterWashController = modelShip.GetComponentInChildren<ThrusterWashController>();
}
public void Update()
{
if (QSBPlayerManager.LocalPlayer.FlyingModelShip)
{
GetFromShip();
return;
}
if (AccelerationSyncer.public_HasChanged())
{
if (AccelerationSyncer.Value == Vector3.zero)
{
foreach (var item in ThrusterFlameControllers)
{
item.OnStopTranslationalThrust();
}
_thrusterAudio.OnStopTranslationalThrust();
ThrusterWashController.OnStopTranslationalThrust();
}
else
{
foreach (var item in ThrusterFlameControllers)
{
item.OnStartTranslationalThrust();
}
_thrusterAudio.OnStartTranslationalThrust();
ThrusterWashController.OnStartTranslationalThrust();
}
}
}
private void GetFromShip()
{
if (ThrusterModel)
{
AccelerationSyncer.Value = ThrusterModel.GetLocalAcceleration();
}
}
}

View File

@ -2,6 +2,7 @@
using QSB.Messaging; using QSB.Messaging;
using QSB.ModelShip.Messages; using QSB.ModelShip.Messages;
using QSB.Patches; using QSB.Patches;
using UnityEngine;
namespace QSB.ModelShip.Patches; namespace QSB.ModelShip.Patches;
@ -20,4 +21,14 @@ public class ModelShipPatches : QSBPatch
new RespawnModelShipMessage(playEffects).Send(); new RespawnModelShipMessage(playEffects).Send();
} }
[HarmonyPrefix]
[HarmonyPatch(typeof(ModelShipCrashBehavior), nameof(ModelShipCrashBehavior.OnImpact))]
private static void ModelShipCrashBehavior_OnImpact(ModelShipCrashBehavior __instance, ImpactData impactData)
{
if (impactData.speed > 10f && Time.time > __instance._lastCrashTime + 1f)
{
new CrashModelShipMessage().Send();
}
}
} }

View File

@ -0,0 +1,31 @@
using HarmonyLib;
using QSB.ModelShip.TransformSync;
using QSB.Patches;
using QSB.Player;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace QSB.ModelShip.Patches;
internal class ModelShipThrusterAudioPatches : QSBPatch
{
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
[HarmonyPrefix]
[HarmonyPatch(typeof(ThrusterModel), nameof(ThrusterModel.GetThrustFraction))]
public static bool ThrusterModel_GetThrustFraction(ThrusterModel __instance, ref float __result)
{
if (__instance == ModelShipTransformSync.LocalInstance.ThrusterVariableSyncer.ThrusterModel && !QSBPlayerManager.LocalPlayer.FlyingModelShip)
{
__result = ModelShipTransformSync.LocalInstance.ThrusterVariableSyncer.AccelerationSyncer.Value.magnitude / __instance._maxTranslationalThrust;
return false;
}
else
{
return true;
}
}
}

View File

@ -0,0 +1,32 @@
using HarmonyLib;
using QSB.ModelShip.TransformSync;
using QSB.Patches;
using QSB.Player;
using System.Linq;
using UnityEngine;
namespace QSB.ModelShip.Patches;
internal class ModelShipThrusterPatches : QSBPatch
{
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
[HarmonyPrefix]
[HarmonyPatch(typeof(ThrusterFlameController), nameof(ThrusterFlameController.GetThrustFraction))]
public static bool GetThrustFraction(ThrusterFlameController __instance, ref float __result)
{
var modelShipThrusters = ModelShipTransformSync.LocalInstance?.ThrusterVariableSyncer;
if (modelShipThrusters == null) return true;
if (modelShipThrusters.ThrusterFlameControllers.Contains(__instance) && !QSBPlayerManager.LocalPlayer.FlyingModelShip)
{
if(__instance._thrusterModel.IsThrusterBankEnabled(OWUtilities.GetShipThrusterBank(__instance._thruster)))
{
__result = Vector3.Dot(modelShipThrusters.AccelerationSyncer.Value, __instance._thrusterFilter);
return false;
}
}
return true;
}
}

View File

@ -1,4 +1,5 @@
using QSB.Syncs.Sectored.Rigidbodies; using QSB.ShipSync;
using QSB.Syncs.Sectored.Rigidbodies;
using QSB.Utility; using QSB.Utility;
using QSB.WorldSync; using QSB.WorldSync;
@ -8,6 +9,8 @@ internal class ModelShipTransformSync : SectoredRigidbodySync
{ {
public static ModelShipTransformSync LocalInstance { get; private set; } public static ModelShipTransformSync LocalInstance { get; private set; }
public ModelShipThrusterVariableSyncer ThrusterVariableSyncer { get; private set; }
public override void OnStartClient() public override void OnStartClient()
{ {
base.OnStartClient(); base.OnStartClient();
@ -31,6 +34,14 @@ internal class ModelShipTransformSync : SectoredRigidbodySync
return modelShip; return modelShip;
} }
protected override void Init()
{
base.Init();
ThrusterVariableSyncer = this.GetRequiredComponent<ModelShipThrusterVariableSyncer>();
ThrusterVariableSyncer.Init(AttachedRigidbody.gameObject);
}
/// <summary> /// <summary>
/// replacement for base method /// replacement for base method
/// using SetPos/Rot instead of Move /// using SetPos/Rot instead of Move

View File

@ -3,6 +3,7 @@ using QSB.Animation.Player;
using QSB.Audio; using QSB.Audio;
using QSB.ClientServerStateSync; using QSB.ClientServerStateSync;
using QSB.Messaging; using QSB.Messaging;
using QSB.ModelShip;
using QSB.Player.Messages; using QSB.Player.Messages;
using QSB.Player.TransformSync; using QSB.Player.TransformSync;
using QSB.QuantumSync.WorldObjects; using QSB.QuantumSync.WorldObjects;
@ -34,6 +35,7 @@ public partial class PlayerInfo
public bool IsLocalPlayer => TransformSync.isLocalPlayer; // if TransformSync is ever null, i give permission for nebula to make fun of me about it for the rest of time - johncorby public bool IsLocalPlayer => TransformSync.isLocalPlayer; // if TransformSync is ever null, i give permission for nebula to make fun of me about it for the rest of time - johncorby
public ThrusterLightTracker ThrusterLightTracker; public ThrusterLightTracker ThrusterLightTracker;
public bool FlyingShip => ShipManager.Instance.CurrentFlyer == PlayerId; public bool FlyingShip => ShipManager.Instance.CurrentFlyer == PlayerId;
public bool FlyingModelShip => ModelShipManager.Instance.CurrentFlyer == PlayerId;
public PlayerInfo(PlayerTransformSync transformSync) public PlayerInfo(PlayerTransformSync transformSync)
{ {

View File

@ -13,6 +13,7 @@ using QSB.EchoesOfTheEye.EclipseElevators.VariableSync;
using QSB.EchoesOfTheEye.RaftSync.TransformSync; using QSB.EchoesOfTheEye.RaftSync.TransformSync;
using QSB.JellyfishSync.TransformSync; using QSB.JellyfishSync.TransformSync;
using QSB.Messaging; using QSB.Messaging;
using QSB.ModelShip;
using QSB.ModelShip.TransformSync; using QSB.ModelShip.TransformSync;
using QSB.OrbSync.Messages; using QSB.OrbSync.Messages;
using QSB.OrbSync.TransformSync; using QSB.OrbSync.TransformSync;
@ -149,6 +150,9 @@ public class QSBNetworkManager : NetworkManager, IAddComponentOnStart
spawnPrefabs.Add(ShipLegPrefab); spawnPrefabs.Add(ShipLegPrefab);
ModelShipPrefab = MakeNewNetworkObject(14, "NetworkModelShip", typeof(ModelShipTransformSync)); ModelShipPrefab = MakeNewNetworkObject(14, "NetworkModelShip", typeof(ModelShipTransformSync));
var modelShipVector3Syncer = ModelShipPrefab.AddComponent<Vector3VariableSyncer>();
var modelShipThrusterVariableSyncer = ModelShipPrefab.AddComponent<ModelShipThrusterVariableSyncer>();
modelShipThrusterVariableSyncer.AccelerationSyncer = modelShipVector3Syncer;
spawnPrefabs.Add(ModelShipPrefab); spawnPrefabs.Add(ModelShipPrefab);
StationaryProbeLauncherPrefab = MakeNewNetworkObject(15, "NetworkStationaryProbeLauncher", typeof(StationaryProbeLauncherVariableSyncer)); StationaryProbeLauncherPrefab = MakeNewNetworkObject(15, "NetworkStationaryProbeLauncher", typeof(StationaryProbeLauncherVariableSyncer));

View File

@ -53,6 +53,8 @@ public class QSBMarshmallow : MonoBehaviour
_mallowRenderer.enabled = true; _mallowRenderer.enabled = true;
_mallowState = Marshmallow.MallowState.Default; _mallowState = Marshmallow.MallowState.Default;
enabled = true; enabled = true;
_attachedPlayer.AudioController.PlayOneShot(AudioType.ToolMarshmallowReplace);
} }
public void Disable() public void Disable()
@ -68,6 +70,8 @@ public class QSBMarshmallow : MonoBehaviour
{ {
_fireRenderer.enabled = false; _fireRenderer.enabled = false;
_mallowState = Marshmallow.MallowState.Default; _mallowState = Marshmallow.MallowState.Default;
_attachedPlayer.AudioController.PlayOneShot(AudioType.ToolMarshmallowBlowOut);
} }
} }
@ -142,6 +146,8 @@ public class QSBMarshmallow : MonoBehaviour
_toastedFraction = 1f; _toastedFraction = 1f;
_initBurnTime = Time.time; _initBurnTime = Time.time;
_mallowState = Marshmallow.MallowState.Burning; _mallowState = Marshmallow.MallowState.Burning;
_attachedPlayer.AudioController.PlayOneShot(AudioType.ToolMarshmallowIgnite);
} }
} }

View File

@ -39,10 +39,12 @@ internal class FlyShipMessage : QSBMessage<bool>
var shipCockpitController = ShipManager.Instance.CockpitController; var shipCockpitController = ShipManager.Instance.CockpitController;
if (Data) if (Data)
{ {
QSBPlayerManager.GetPlayer(From)?.AudioController?.PlayOneShot(AudioType.ShipCockpitBuckleUp);
shipCockpitController._interactVolume.DisableInteraction(); shipCockpitController._interactVolume.DisableInteraction();
} }
else else
{ {
QSBPlayerManager.GetPlayer(From)?.AudioController?.PlayOneShot(AudioType.ShipCockpitUnbuckle);
shipCockpitController._interactVolume.EnableInteraction(); shipCockpitController._interactVolume.EnableInteraction();
} }
} }

View File

@ -0,0 +1,48 @@
using QSB.Messaging;
using QSB.Player;
using static QSB.ShipSync.Messages.ShipIgnitionMessage;
namespace QSB.ShipSync.Messages;
internal class ShipIgnitionMessage : QSBMessage<ShipIgnitionType>
{
public enum ShipIgnitionType
{
START_IGNITION,
COMPLETE_IGNITION,
CANCEL_IGNITION
}
static ShipIgnitionMessage()
{
GlobalMessenger.AddListener(OWEvents.StartShipIgnition, () => Handler(ShipIgnitionType.START_IGNITION));
GlobalMessenger.AddListener(OWEvents.CompleteShipIgnition, () => Handler(ShipIgnitionType.COMPLETE_IGNITION));
GlobalMessenger.AddListener(OWEvents.CancelShipIgnition, () => Handler(ShipIgnitionType.CANCEL_IGNITION));
}
public ShipIgnitionMessage(ShipIgnitionType data) : base(data) { }
private static void Handler(ShipIgnitionType type)
{
if (QSBPlayerManager.LocalPlayer.FlyingShip)
{
new ShipIgnitionMessage(type).Send();
}
}
public override void OnReceiveRemote()
{
switch (Data)
{
case ShipIgnitionType.START_IGNITION:
GlobalMessenger.FireEvent(OWEvents.StartShipIgnition);
break;
case ShipIgnitionType.COMPLETE_IGNITION:
GlobalMessenger.FireEvent(OWEvents.CompleteShipIgnition);
break;
case ShipIgnitionType.CANCEL_IGNITION:
GlobalMessenger.FireEvent(OWEvents.CancelShipIgnition);
break;
}
}
}

View File

@ -0,0 +1,44 @@
using HarmonyLib;
using QSB.Patches;
using QSB.Player;
using QSB.ShipSync.TransformSync;
using UnityEngine;
namespace QSB.ShipSync.Patches;
internal class ShipAudioPatches : QSBPatch
{
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
[HarmonyPrefix]
[HarmonyPatch(typeof(ShipThrusterAudio), nameof(ShipThrusterAudio.Update))]
public static bool ShipThrusterAudio_Update(ShipThrusterAudio __instance)
{
if (!QSBPlayerManager.LocalPlayer.FlyingShip)
{
// Just copy pasted the original method with this one line changed
Vector3 localAcceleration = ShipTransformSync.LocalInstance.ThrusterVariableSyncer.AccelerationSyncer.Value;
localAcceleration.y *= 0.5f;
localAcceleration.z *= 0.5f;
Vector3 vector = __instance._thrusterModel.IsThrusterBankEnabled(ThrusterBank.Left) ? localAcceleration : Vector3.zero;
vector.x = Mathf.Max(0f, vector.x);
Vector3 vector2 = __instance._thrusterModel.IsThrusterBankEnabled(ThrusterBank.Right) ? localAcceleration : Vector3.zero;
vector2.x = Mathf.Min(0f, vector2.x);
float maxTranslationalThrust = __instance._thrusterModel.GetMaxTranslationalThrust();
__instance.UpdateTranslationalSourceVolume(__instance._leftTranslationalSource, __instance._thrustToVolumeCurve.Evaluate(vector.magnitude / maxTranslationalThrust), !__instance._underwater);
__instance.UpdateTranslationalSourceVolume(__instance._rightTranslationalSource, __instance._thrustToVolumeCurve.Evaluate(vector2.magnitude / maxTranslationalThrust), !__instance._underwater);
__instance.UpdateTranslationalSourceVolume(__instance._leftUnderwaterSource, __instance._thrustToVolumeCurve.Evaluate(vector.magnitude / maxTranslationalThrust), __instance._underwater);
__instance.UpdateTranslationalSourceVolume(__instance._rightUnderwaterSource, __instance._thrustToVolumeCurve.Evaluate(vector2.magnitude / maxTranslationalThrust), __instance._underwater);
if (!__instance._thrustersFiring && !__instance._leftTranslationalSource.isPlaying && !__instance._rightTranslationalSource.isPlaying && !__instance._leftUnderwaterSource.isPlaying && !__instance._rightUnderwaterSource.isPlaying)
{
__instance.enabled = false;
}
return false;
}
else
{
return true;
}
}
}

View File

@ -1,4 +1,6 @@
using HarmonyLib; using HarmonyLib;
using QSB.ModelShip;
using QSB.ModelShip.TransformSync;
using QSB.Patches; using QSB.Patches;
using QSB.Player; using QSB.Player;
using QSB.ShipSync.TransformSync; using QSB.ShipSync.TransformSync;
@ -41,16 +43,34 @@ internal class ShipFlameWashPatches : QSBPatch
[HarmonyPatch(typeof(ThrusterWashController), nameof(ThrusterWashController.Update))] [HarmonyPatch(typeof(ThrusterWashController), nameof(ThrusterWashController.Update))]
public static bool Update(ThrusterWashController __instance) public static bool Update(ThrusterWashController __instance)
{ {
if (ShipThrusterManager.ShipWashController != __instance) var isShip = ShipThrusterManager.ShipWashController == __instance;
var isModelShip = ModelShipTransformSync.LocalInstance.ThrusterVariableSyncer.ThrusterWashController == __instance;
if (!isShip && !isModelShip)
{ {
return true; return true;
} }
bool isLocal;
Vector3 remoteAcceleration;
if (isShip)
{
isLocal = QSBPlayerManager.LocalPlayer.FlyingShip;
remoteAcceleration = ShipTransformSync.LocalInstance.ThrusterVariableSyncer.AccelerationSyncer.Value;
}
else
{
isLocal = QSBPlayerManager.LocalPlayer.FlyingModelShip;
remoteAcceleration = ModelShipTransformSync.LocalInstance.ThrusterVariableSyncer.AccelerationSyncer.Value;
}
var localAcceleration = isLocal
? __instance._thrusterModel.GetLocalAcceleration()
: remoteAcceleration;
// The rest of this is just copy pasted from the original method
var hitInfo = default(RaycastHit); var hitInfo = default(RaycastHit);
var aboveGround = false; var aboveGround = false;
var localAcceleration = QSBPlayerManager.LocalPlayer.FlyingShip
? __instance._thrusterModel.GetLocalAcceleration()
: ShipTransformSync.LocalInstance.ThrusterVariableSyncer.AccelerationSyncer.Value;
var emissionScale = __instance._emissionThrusterScale.Evaluate(localAcceleration.y); var emissionScale = __instance._emissionThrusterScale.Evaluate(localAcceleration.y);
if (emissionScale > 0f) if (emissionScale > 0f)
{ {

View File

@ -22,6 +22,7 @@ internal class ShipManager : WorldObjectManager
public static ShipManager Instance; public static ShipManager Instance;
public ShipThrusterAudio ShipThrusterAudio;
public InteractZone HatchInteractZone; public InteractZone HatchInteractZone;
public HatchController HatchController; public HatchController HatchController;
public ShipTractorBeamSwitch ShipTractorBeam; public ShipTractorBeamSwitch ShipTractorBeam;
@ -92,6 +93,7 @@ internal class ShipManager : WorldObjectManager
return; return;
} }
ShipThrusterAudio = QSBWorldSync.GetUnityObject<ShipThrusterAudio>();
HatchInteractZone = HatchController.GetComponent<InteractZone>(); HatchInteractZone = HatchController.GetComponent<InteractZone>();
ShipTractorBeam = QSBWorldSync.GetUnityObject<ShipTractorBeamSwitch>(); ShipTractorBeam = QSBWorldSync.GetUnityObject<ShipTractorBeamSwitch>();
CockpitController = QSBWorldSync.GetUnityObject<ShipCockpitController>(); CockpitController = QSBWorldSync.GetUnityObject<ShipCockpitController>();
@ -136,6 +138,10 @@ internal class ShipManager : WorldObjectManager
QSBWorldSync.Init<QSBShipDetachableModule, ShipDetachableModule>(); QSBWorldSync.Init<QSBShipDetachableModule, ShipDetachableModule>();
QSBWorldSync.Init<QSBShipDetachableLeg, ShipDetachableLeg>(); QSBWorldSync.Init<QSBShipDetachableLeg, ShipDetachableLeg>();
// Make sure all relevant audio sources are 3D
QSBWorldSync.GetUnityObject<ShipThrusterAudio>()._ignitionSource.spatialBlend = 1f;
QSBWorldSync.GetUnityObject<ShipThrusterAudio>()._rotationalSource.spatialBlend = 1f;
} }
public override void UnbuildWorldObjects() public override void UnbuildWorldObjects()

View File

@ -1,6 +1,7 @@
using Mirror; using Mirror;
using QSB.Player; using QSB.Player;
using QSB.Utility.VariableSync; using QSB.Utility.VariableSync;
using QSB.WorldSync;
using UnityEngine; using UnityEngine;
namespace QSB.ShipSync; namespace QSB.ShipSync;
@ -10,10 +11,12 @@ public class ShipThrusterVariableSyncer : NetworkBehaviour
public Vector3VariableSyncer AccelerationSyncer; public Vector3VariableSyncer AccelerationSyncer;
private ShipThrusterModel _thrusterModel; private ShipThrusterModel _thrusterModel;
private ShipThrusterAudio _thrusterAudio;
public void Init() public void Init()
{ {
_thrusterModel = Locator.GetShipBody().GetComponent<ShipThrusterModel>(); _thrusterModel = Locator.GetShipBody().GetComponent<ShipThrusterModel>();
_thrusterAudio = Locator.GetShipBody().GetComponentInChildren<ShipThrusterAudio>();
} }
public void Update() public void Update()
@ -33,6 +36,8 @@ public class ShipThrusterVariableSyncer : NetworkBehaviour
item.OnStopTranslationalThrust(); item.OnStopTranslationalThrust();
} }
_thrusterAudio.OnStopTranslationalThrust();
ShipThrusterManager.ShipWashController.OnStopTranslationalThrust(); ShipThrusterManager.ShipWashController.OnStopTranslationalThrust();
} }
else else
@ -42,6 +47,8 @@ public class ShipThrusterVariableSyncer : NetworkBehaviour
item.OnStartTranslationalThrust(); item.OnStartTranslationalThrust();
} }
_thrusterAudio.OnStartTranslationalThrust();
ShipThrusterManager.ShipWashController.OnStartTranslationalThrust(); ShipThrusterManager.ShipWashController.OnStartTranslationalThrust();
} }
} }