Merge pull request #563 from misternebula/stationary-probe-launcher

Stationary probe launcher + more launcher sfx sync
This commit is contained in:
Nick 2022-08-27 14:46:25 -04:00 committed by GitHub
commit 7328dbede8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 369 additions and 13 deletions

View File

@ -26,6 +26,7 @@ using QSB.ShipSync;
using QSB.ShipSync.TransformSync;
using QSB.Syncs.Occasional;
using QSB.TimeSync;
using QSB.Tools.ProbeLauncherTool.VariableSync;
using QSB.Tools.ProbeTool.TransformSync;
using QSB.Utility;
using QSB.Utility.VariableSync;
@ -56,6 +57,7 @@ public class QSBNetworkManager : NetworkManager, IAddComponentOnStart
public GameObject ShipModulePrefab { get; private set; }
public GameObject ShipLegPrefab { get; private set; }
public GameObject ModelShipPrefab { get; private set; }
public GameObject StationaryProbeLauncherPrefab { get; private set; }
private string PlayerName { get; set; }
private GameObject _probePrefab;
@ -149,6 +151,9 @@ public class QSBNetworkManager : NetworkManager, IAddComponentOnStart
ModelShipPrefab = MakeNewNetworkObject(14, "NetworkModelShip", typeof(ModelShipTransformSync));
spawnPrefabs.Add(ModelShipPrefab);
StationaryProbeLauncherPrefab = MakeNewNetworkObject(15, "NetworkStationaryProbeLauncher", typeof(StationaryProbeLauncherVariableSyncer));
spawnPrefabs.Add(StationaryProbeLauncherPrefab);
ConfigureNetworkManager();
}

View File

@ -0,0 +1,11 @@
using QSB.Messaging;
using QSB.Tools.ProbeLauncherTool.WorldObjects;
namespace QSB.Tools.ProbeLauncherTool.Messages;
internal class ChangeModeMessage : QSBWorldObjectMessage<QSBProbeLauncher>
{
public ChangeModeMessage() : base() { }
public override void OnReceiveRemote() => WorldObject.ChangeMode();
}

View File

@ -3,9 +3,9 @@ using QSB.Tools.ProbeLauncherTool.WorldObjects;
namespace QSB.Tools.ProbeLauncherTool.Messages;
internal class LaunchProbeMessage : QSBWorldObjectMessage<QSBProbeLauncher, bool>
internal class LaunchProbeMessage : QSBWorldObjectMessage<QSBProbeLauncher, (bool playEffects, uint probeOwnerID)>
{
public LaunchProbeMessage(bool playEffects) : base(playEffects) { }
public LaunchProbeMessage(bool playEffects, uint probeOwnerID) : base((playEffects, probeOwnerID)) { }
public override void OnReceiveRemote() => WorldObject.LaunchProbe(Data);
public override void OnReceiveRemote() => WorldObject.LaunchProbe(Data.playEffects, Data.probeOwnerID);
}

View File

@ -0,0 +1,16 @@
using QSB.Messaging;
using QSB.Player;
using QSB.WorldSync;
namespace QSB.Tools.ProbeLauncherTool.Messages;
internal class PlayerLauncherChangeModeMessage : QSBMessage
{
public override bool ShouldReceive => QSBWorldSync.AllObjectsReady;
public override void OnReceiveRemote()
{
var player = QSBPlayerManager.GetPlayer(From);
player.ProbeLauncherTool.ChangeMode();
}
}

View File

@ -0,0 +1,16 @@
using QSB.Messaging;
using QSB.Player;
using QSB.WorldSync;
namespace QSB.Tools.ProbeLauncherTool.Messages;
internal class PlayerLauncherTakeSnapshotMessage : QSBMessage
{
public override bool ShouldReceive => QSBWorldSync.AllObjectsReady;
public override void OnReceiveRemote()
{
var player = QSBPlayerManager.GetPlayer(From);
player.ProbeLauncherTool.TakeSnapshot();
}
}

View File

@ -0,0 +1,11 @@
using QSB.Messaging;
using QSB.Tools.ProbeLauncherTool.WorldObjects;
namespace QSB.Tools.ProbeLauncherTool.Messages;
public class StationaryProbeLauncherMessage : QSBWorldObjectMessage<QSBStationaryProbeLauncher, (bool, uint)>
{
public StationaryProbeLauncherMessage(bool inUse, uint userID) : base((inUse, userID)) { }
public override void OnReceiveRemote() => WorldObject.OnRemoteUseStateChanged(Data.Item1, Data.Item2);
}

View File

@ -0,0 +1,16 @@
using QSB.Messaging;
using QSB.Tools.ProbeLauncherTool.WorldObjects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace QSB.Tools.ProbeLauncherTool.Messages;
internal class TakeSnapshotMessage : QSBWorldObjectMessage<QSBProbeLauncher>
{
public TakeSnapshotMessage() : base() { }
public override void OnReceiveRemote() => WorldObject.TakeSnapshot();
}

View File

@ -89,4 +89,38 @@ internal class LauncherPatches : QSBPatch
__instance._owAudioSource.GetAudioSource().spatialBlend = 1f;
return true;
}
[HarmonyPostfix]
[HarmonyPatch(typeof(ProbeLauncherEffects), nameof(ProbeLauncherEffects.PlayChangeModeClip))]
public static void ProbeLauncherEffects_PlayChangeModeClip(ProbeLauncherEffects __instance)
{
if (__instance != QSBPlayerManager.LocalPlayer.LocalProbeLauncher._effects)
{
__instance.gameObject
.GetComponent<ProbeLauncher>()
?.GetWorldObject<QSBProbeLauncher>()
?.SendMessage(new ChangeModeMessage());
}
else
{
new PlayerLauncherChangeModeMessage().Send();
}
}
[HarmonyPostfix]
[HarmonyPatch(typeof(ProbeLauncherEffects), nameof(ProbeLauncherEffects.PlaySnapshotClip))]
public static void ProbeLauncherEffects_PlaySnapshotClip(ProbeLauncherEffects __instance)
{
if (__instance != QSBPlayerManager.LocalPlayer.LocalProbeLauncher._effects)
{
__instance.gameObject
.GetComponent<ProbeLauncher>()
?.GetWorldObject<QSBProbeLauncher>()
?.SendMessage(new TakeSnapshotMessage());
}
else
{
new PlayerLauncherTakeSnapshotMessage().Send();
}
}
}

View File

@ -0,0 +1,17 @@
using HarmonyLib;
using QSB.Patches;
using QSB.Tools.ProbeLauncherTool.WorldObjects;
using QSB.WorldSync;
namespace QSB.Tools.ProbeLauncherTool.Patches;
[HarmonyPatch]
public class StationaryProbeLauncherPatches : QSBPatch
{
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
[HarmonyPostfix]
[HarmonyPatch(typeof(StationaryProbeLauncher), nameof(StationaryProbeLauncher.FinishExitSequence))]
public static void StationaryProbeLauncher_FinishExitSequence(StationaryProbeLauncher __instance) =>
__instance.GetWorldObject<QSBStationaryProbeLauncher>().OnLocalUseStateChanged(false);
}

View File

@ -1,5 +1,6 @@
using Cysharp.Threading.Tasks;
using QSB.Tools.ProbeLauncherTool.WorldObjects;
using QSB.Utility;
using QSB.WorldSync;
using System.Linq;
using System.Threading;
@ -12,7 +13,9 @@ internal class ProbeLauncherManager : WorldObjectManager
public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct)
{
QSBWorldSync.Init<QSBProbeLauncher, ProbeLauncher>(typeof(PlayerProbeLauncher));
QSBWorldSync.Init<QSBProbeLauncher, ProbeLauncher>(typeof(PlayerProbeLauncher), typeof(StationaryProbeLauncher));
// Using ProbeLaunchers here so we can do inheritance, put only applying it to found StationaryProbeLauncher
QSBWorldSync.Init<QSBStationaryProbeLauncher, ProbeLauncher>(QSBWorldSync.GetUnityObjects<StationaryProbeLauncher>().SortDeterministic());
if (scene == OWScene.SolarSystem)
{
QSBWorldSync.Init<QSBProbeLauncher, ProbeLauncher>(new[]

View File

@ -8,12 +8,17 @@ public class QSBProbeLauncherTool : QSBTool
public ProbeLauncherEffects Effects;
public SingularityWarpEffect ProbeRetrievalEffect;
public void RetrieveProbe(bool playEffects)
private void VerifyAudioSource()
{
if (Effects._owAudioSource == null)
{
Effects._owAudioSource = Player.AudioController._repairToolSource;
}
}
public void RetrieveProbe(bool playEffects)
{
VerifyAudioSource();
PreLaunchProbeProxy.SetActive(true);
if (playEffects)
@ -27,13 +32,28 @@ public class QSBProbeLauncherTool : QSBTool
{
PreLaunchProbeProxy.SetActive(false);
if (Effects._owAudioSource == null)
{
Effects._owAudioSource = Player.AudioController._repairToolSource;
}
VerifyAudioSource();
// TODO : make this do underwater stuff correctly
Effects.PlayLaunchClip(false);
Effects.PlayLaunchParticles(false);
}
public void ChangeMode()
{
VerifyAudioSource();
Effects.PlayChangeModeClip();
}
public void TakeSnapshot()
{
VerifyAudioSource();
// Vanilla method uses the global player audio controller -> bad
Effects._owAudioSource.PlayOneShot(AudioType.ToolProbeTakePhoto, 1f);
// Also make the probe itself play the sound effect
if (Player.Probe.IsLaunched()) Player.Probe.TakeSnapshot();
}
}

View File

@ -0,0 +1,51 @@
using Mirror;
using QSB.Tools.ProbeLauncherTool.WorldObjects;
using QSB.Utility.LinkedWorldObject;
using QSB.Utility.VariableSync;
using QSB.WorldSync;
using UnityEngine;
namespace QSB.Tools.ProbeLauncherTool.VariableSync;
public class StationaryProbeLauncherVariableSyncer : BaseVariableSyncer<(float, float, float)>, ILinkedNetworkBehaviour
{
protected override bool HasChanged()
{
var launcher = (StationaryProbeLauncher)WorldObject.AttachedObject;
Value = (launcher._degreesX, launcher._degreesY, launcher._audioSource._localVolume);
return Value != PrevValue;
}
protected override void Serialize(NetworkWriter writer)
{
base.Serialize(writer);
var launcher = (StationaryProbeLauncher)WorldObject.AttachedObject;
writer.Write(launcher._degreesX);
writer.Write(launcher._degreesY);
writer.Write(launcher._audioSource.GetLocalVolume());
}
protected override void Deserialize(NetworkReader reader)
{
base.Deserialize(reader);
var launcher = (StationaryProbeLauncher)WorldObject.AttachedObject;
launcher._degreesX = reader.Read<float>();
launcher._degreesY = reader.Read<float>();
launcher._audioSource.SetLocalVolume(reader.Read<float>());
// Update rotation based on x and y degrees
launcher.transform.localRotation = Quaternion.AngleAxis(launcher._degreesX, launcher._localUpAxis) * launcher._initRotX;
launcher._verticalPivot.localRotation = Quaternion.AngleAxis(launcher._degreesY, -Vector3.right) * launcher._initRotY;
Value = (launcher._degreesX, launcher._degreesY, launcher._audioSource._localVolume);
}
protected QSBStationaryProbeLauncher WorldObject { get; private set; }
public void SetWorldObject(IWorldObject worldObject) => WorldObject = (QSBStationaryProbeLauncher)worldObject;
}

View File

@ -1,13 +1,19 @@
using Cysharp.Threading.Tasks;
using QSB.Messaging;
using QSB.Player;
using QSB.Tools.ProbeLauncherTool.Messages;
using QSB.Tools.ProbeTool;
using QSB.WorldSync;
using System.Threading;
using UnityEngine;
namespace QSB.Tools.ProbeLauncherTool.WorldObjects;
public class QSBProbeLauncher : WorldObject<ProbeLauncher>
{
private uint _probeOwnerID = uint.MaxValue;
protected QSBProbe LaunchedProbe { get; private set; }
public override async UniTask Init(CancellationToken ct) =>
AttachedObject.OnLaunchProbe += OnLaunchProbe;
@ -16,21 +22,27 @@ public class QSBProbeLauncher : WorldObject<ProbeLauncher>
public override void SendInitialState(uint to)
{
// Retrieval resets the probe owner ID
var probeOwnerID = _probeOwnerID;
if (AttachedObject._preLaunchProbeProxy.activeSelf)
{
this.SendMessage(new RetrieveProbeMessage(false));
}
else
{
this.SendMessage(new LaunchProbeMessage(false));
this.SendMessage(new LaunchProbeMessage(false, probeOwnerID));
}
}
private void OnLaunchProbe(SurveyorProbe probe) =>
this.SendMessage(new LaunchProbeMessage(true));
this.SendMessage(new LaunchProbeMessage(true, QSBPlayerManager.LocalPlayerId));
public void RetrieveProbe(bool playEffects)
{
_probeOwnerID = uint.MaxValue;
LaunchedProbe = null;
if (AttachedObject._preLaunchProbeProxy.activeSelf)
{
return;
@ -44,8 +56,13 @@ public class QSBProbeLauncher : WorldObject<ProbeLauncher>
}
}
public void LaunchProbe(bool playEffects)
public void LaunchProbe(bool playEffects, uint probeOwnerID)
{
_probeOwnerID = probeOwnerID;
LaunchedProbe = QSBPlayerManager.GetPlayer(_probeOwnerID)?.Probe;
if (LaunchedProbe == null) Debug.LogError($"Could not find probe owner with ID {_probeOwnerID}");
if (!AttachedObject._preLaunchProbeProxy.activeSelf)
{
return;
@ -60,4 +77,18 @@ public class QSBProbeLauncher : WorldObject<ProbeLauncher>
AttachedObject._effects.PlayLaunchParticles(false);
}
}
public void ChangeMode()
{
AttachedObject._effects.PlayChangeModeClip();
}
public void TakeSnapshot()
{
// Not using PlaySnapshotClip because that uses Locator.GetPlayerAudioController() instead of owAudioSource for some reason
AttachedObject._effects._owAudioSource.PlayOneShot(AudioType.ToolProbeTakePhoto, 1f);
// If their probe is launched also play a snapshot from it
if (LaunchedProbe && LaunchedProbe.IsLaunched()) LaunchedProbe.TakeSnapshot();
}
}

View File

@ -0,0 +1,114 @@
using Cysharp.Threading.Tasks;
using Mirror;
using QSB.AuthoritySync;
using QSB.Messaging;
using QSB.Player;
using QSB.Tools.ProbeLauncherTool.Messages;
using QSB.Tools.ProbeLauncherTool.VariableSync;
using QSB.Utility.LinkedWorldObject;
using System.Threading;
namespace QSB.Tools.ProbeLauncherTool.WorldObjects;
public class QSBStationaryProbeLauncher : QSBProbeLauncher, ILinkedWorldObject<StationaryProbeLauncherVariableSyncer>
{
private uint _currentUser = uint.MaxValue;
public StationaryProbeLauncherVariableSyncer NetworkBehaviour { get; private set; }
public void SetNetworkBehaviour(NetworkBehaviour networkBehaviour) => NetworkBehaviour = (StationaryProbeLauncherVariableSyncer)networkBehaviour;
private bool _isInUse;
private StationaryProbeLauncher _stationaryProbeLauncher;
public override async UniTask Init(CancellationToken ct)
{
// This is implemented by inheriting LinkedWorldObject normally,
// However I want to inherit from QSBProbeLauncher or else we'd have to redo the sync for the VFX/SFX
if (QSBCore.IsHost)
{
this.SpawnLinked(QSBNetworkManager.singleton.StationaryProbeLauncherPrefab, false);
}
else
{
await this.WaitForLink(ct);
}
await base.Init(ct);
_stationaryProbeLauncher = (StationaryProbeLauncher)AttachedObject;
_stationaryProbeLauncher._interactVolume.OnPressInteract += OnPressInteract;
// Fix spatial blend of sound effects
_stationaryProbeLauncher._effects._owAudioSource.spatialBlend = 1;
_stationaryProbeLauncher._audioSource.spatialBlend = 1;
UpdateUse();
}
public override void OnRemoval()
{
if (QSBCore.IsHost)
{
NetworkServer.Destroy(NetworkBehaviour.gameObject);
}
base.OnRemoval();
_stationaryProbeLauncher._interactVolume.OnPressInteract -= OnPressInteract;
}
private void OnPressInteract() => OnLocalUseStateChanged(true);
public override void SendInitialState(uint to)
{
base.SendInitialState(to);
this.SendMessage(new StationaryProbeLauncherMessage(_isInUse, _currentUser) { To = to });
}
public void OnRemoteUseStateChanged(bool isInUse, uint user)
{
_isInUse = isInUse;
_currentUser = isInUse ? user : uint.MaxValue;
// Whoever is using it needs authority to be able to rotate it
if (QSBCore.IsHost)
{
NetworkBehaviour.netIdentity.SetAuthority(_currentUser);
}
UpdateUse();
}
public void OnLocalUseStateChanged(bool isInUse)
{
_isInUse = isInUse;
_currentUser = isInUse ? QSBPlayerManager.LocalPlayerId : uint.MaxValue;
// Whoever is using it needs authority to be able to rotate it
if (QSBCore.IsHost)
{
NetworkBehaviour.netIdentity.SetAuthority(_currentUser);
}
this.SendMessage(new StationaryProbeLauncherMessage(isInUse, _currentUser));
}
private void UpdateUse()
{
// If somebody is using this we disable the interaction shape
_stationaryProbeLauncher._interactVolume.SetInteractionEnabled(!_isInUse);
if (_isInUse)
{
_stationaryProbeLauncher._audioSource.SetLocalVolume(0f);
_stationaryProbeLauncher._audioSource.Play();
}
else
{
_stationaryProbeLauncher._audioSource.Stop();
}
}
}

View File

@ -17,6 +17,7 @@ public class QSBProbe : MonoBehaviour, ILightSource
public event SurveyorProbeEvent OnUnanchorProbe;
public event SurveyorProbeEvent OnRetrieveProbe;
public event SurveyorProbeEvent OnProbeDestroyed;
public event SurveyorProbeEvent OnTakeSnapshot;
public event RetrieveEvent OnStartRetrieveProbe;
private GameObject _detectorObj;
@ -204,4 +205,6 @@ public class QSBProbe : MonoBehaviour, ILightSource
public LightSourceType GetLightSourceType() => LightSourceType.PROBE;
public OWLight2[] GetLights() => _illuminationCheckLights;
public Vector3 GetLightSourcePosition() => _lightSourceVol.transform.position;
public void TakeSnapshot() => OnTakeSnapshot?.Invoke();
}

View File

@ -25,6 +25,7 @@ internal class QSBProbeEffects : MonoBehaviour
_probe.OnAnchorProbe += OnAnchor;
_probe.OnUnanchorProbe += OnUnanchor;
_probe.OnStartRetrieveProbe += OnStartRetrieve;
_probe.OnTakeSnapshot += OnTakeSnapshot;
}
private void OnDestroy()
@ -33,6 +34,7 @@ internal class QSBProbeEffects : MonoBehaviour
_probe.OnAnchorProbe -= OnAnchor;
_probe.OnUnanchorProbe -= OnUnanchor;
_probe.OnStartRetrieveProbe -= OnStartRetrieve;
_probe.OnTakeSnapshot -= OnTakeSnapshot;
}
private void OnLaunch() => _flightLoopAudio.FadeIn(0.1f, true, true);
@ -57,5 +59,11 @@ internal class QSBProbeEffects : MonoBehaviour
=> _flightLoopAudio.FadeIn(0.5f);
private void OnStartRetrieve(float retrieveLength)
=> _flightLoopAudio.FadeOut(retrieveLength);
{
_flightLoopAudio.FadeOut(retrieveLength);
_anchorAudio.PlayOneShot(AudioType.ToolProbeRetrieve);
}
private void OnTakeSnapshot()
=> _anchorAudio.PlayOneShot(AudioType.ToolProbeTakePhoto);
}