diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index 4e7e04c67..535998dfc 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -13,5 +13,5 @@ jobs:
- uses: actions/upload-artifact@v2
with:
- name: QSB
+ name: Raicuparta.QuantumSpaceBuddies
path: .\QSB\Bin\Debug
\ No newline at end of file
diff --git a/EpicOnlineTransport/EpicOnlineTransport.csproj b/EpicOnlineTransport/EpicOnlineTransport.csproj
index 5655714a4..a1563cdf6 100644
--- a/EpicOnlineTransport/EpicOnlineTransport.csproj
+++ b/EpicOnlineTransport/EpicOnlineTransport.csproj
@@ -3,7 +3,7 @@
EpicTransport
+
-
diff --git a/EpicRerouter/EpicRerouter.csproj b/EpicRerouter/EpicRerouter.csproj
index dbebee21d..3cff5ff9a 100644
--- a/EpicRerouter/EpicRerouter.csproj
+++ b/EpicRerouter/EpicRerouter.csproj
@@ -3,7 +3,7 @@
Exe
-
-
+
+
diff --git a/MirrorWeaver/MirrorWeaver.csproj b/MirrorWeaver/MirrorWeaver.csproj
index 737bc8f83..61a059994 100644
--- a/MirrorWeaver/MirrorWeaver.csproj
+++ b/MirrorWeaver/MirrorWeaver.csproj
@@ -3,9 +3,8 @@
Exe
-
-
-
+
+
diff --git a/QSB/Animation/NPC/Patches/CharacterAnimationPatches.cs b/QSB/Animation/NPC/Patches/CharacterAnimationPatches.cs
index cc93e9c6a..3d5ddd531 100644
--- a/QSB/Animation/NPC/Patches/CharacterAnimationPatches.cs
+++ b/QSB/Animation/NPC/Patches/CharacterAnimationPatches.cs
@@ -36,7 +36,7 @@ public static bool AnimatorIKReplacement(
return true;
}
- var qsbObj = __instance.playerTrackingZone.GetWorldObject(); // OPTIMIZE : maybe cache this somewhere... or assess how slow this is
+ var qsbObj = __instance.playerTrackingZone.GetWorldObject();
PlayerInfo playerToUse = null;
if (__instance._inConversation)
diff --git a/QSB/Animation/Player/AnimationSync.cs b/QSB/Animation/Player/AnimationSync.cs
index e7e1d18a2..ec8370388 100644
--- a/QSB/Animation/Player/AnimationSync.cs
+++ b/QSB/Animation/Player/AnimationSync.cs
@@ -12,6 +12,7 @@
namespace QSB.Animation.Player;
+[UsedInUnityProject]
public class AnimationSync : PlayerSyncObject
{
private RuntimeAnimatorController _suitedAnimController;
@@ -92,6 +93,7 @@ public void InitRemote(Transform body)
SetSuitState(QSBSceneManager.CurrentScene == OWScene.EyeOfTheUniverse);
InitAccelerationSync();
ThrusterManager.CreateRemotePlayerVFX(Player);
+ ThrusterManager.CreateRemotePlayerSFX(Player);
Delay.RunWhen(() => Player.CameraBody != null,
() => body.GetComponent().Init(Player.CameraBody.transform));
diff --git a/QSB/Animation/Player/Messages/PlayerSuitMessage.cs b/QSB/Animation/Player/Messages/PlayerSuitMessage.cs
index 7c41161e8..856d2e671 100644
--- a/QSB/Animation/Player/Messages/PlayerSuitMessage.cs
+++ b/QSB/Animation/Player/Messages/PlayerSuitMessage.cs
@@ -37,6 +37,15 @@ public override void OnReceiveRemote()
var animator = player.AnimationSync;
animator.SetSuitState(Data);
+
+ if (player.SuitedUp)
+ {
+ player.AudioController.PlayWearSuit();
+ }
+ else
+ {
+ player.AudioController.PlayRemoveSuit();
+ }
}
public override void OnReceiveLocal()
diff --git a/QSB/Animation/Player/PlayerHeadRotationSync.cs b/QSB/Animation/Player/PlayerHeadRotationSync.cs
index cc41f8d56..d5f4867b1 100644
--- a/QSB/Animation/Player/PlayerHeadRotationSync.cs
+++ b/QSB/Animation/Player/PlayerHeadRotationSync.cs
@@ -4,6 +4,7 @@
namespace QSB.Animation.Player;
+[UsedInUnityProject]
public class PlayerHeadRotationSync : MonoBehaviour
{
private Animator _attachedAnimator;
diff --git a/QSB/Animation/Player/Thrusters/JetpackAccelerationSync.cs b/QSB/Animation/Player/Thrusters/JetpackAccelerationSync.cs
index 4ce656965..c8b6c55a2 100644
--- a/QSB/Animation/Player/Thrusters/JetpackAccelerationSync.cs
+++ b/QSB/Animation/Player/Thrusters/JetpackAccelerationSync.cs
@@ -1,8 +1,10 @@
using Mirror;
+using QSB.Utility;
using QSB.Utility.VariableSync;
namespace QSB.Animation.Player.Thrusters;
+[UsedInUnityProject]
public class JetpackAccelerationSync : NetworkBehaviour
{
public Vector3VariableSyncer AccelerationVariableSyncer;
diff --git a/QSB/Animation/Player/Thrusters/RemoteThrusterFlameController.cs b/QSB/Animation/Player/Thrusters/RemoteThrusterFlameController.cs
index 999e3914a..3f3e2280b 100644
--- a/QSB/Animation/Player/Thrusters/RemoteThrusterFlameController.cs
+++ b/QSB/Animation/Player/Thrusters/RemoteThrusterFlameController.cs
@@ -1,9 +1,11 @@
using QSB.Player;
+using QSB.Utility;
using QSB.WorldSync;
using UnityEngine;
namespace QSB.Animation.Player.Thrusters;
+[UsedInUnityProject]
internal class RemoteThrusterFlameController : MonoBehaviour
{
[SerializeField]
diff --git a/QSB/Animation/Player/Thrusters/RemoteThrusterParticlesBehaviour.cs b/QSB/Animation/Player/Thrusters/RemoteThrusterParticlesBehaviour.cs
index b28c9f66f..535ce1a4d 100644
--- a/QSB/Animation/Player/Thrusters/RemoteThrusterParticlesBehaviour.cs
+++ b/QSB/Animation/Player/Thrusters/RemoteThrusterParticlesBehaviour.cs
@@ -4,6 +4,7 @@
namespace QSB.Animation.Player.Thrusters;
+[UsedInUnityProject]
internal class RemoteThrusterParticlesBehaviour : MonoBehaviour
{
[SerializeField]
diff --git a/QSB/Animation/Player/Thrusters/RemoteThrusterWashController.cs b/QSB/Animation/Player/Thrusters/RemoteThrusterWashController.cs
index ffa1dd606..0b41d5196 100644
--- a/QSB/Animation/Player/Thrusters/RemoteThrusterWashController.cs
+++ b/QSB/Animation/Player/Thrusters/RemoteThrusterWashController.cs
@@ -4,6 +4,7 @@
namespace QSB.Animation.Player.Thrusters;
+[UsedInUnityProject]
internal class RemoteThrusterWashController : MonoBehaviour
{
[SerializeField]
diff --git a/QSB/Animation/Player/Thrusters/ThrusterManager.cs b/QSB/Animation/Player/Thrusters/ThrusterManager.cs
index e0bc0b3d5..227fa7666 100644
--- a/QSB/Animation/Player/Thrusters/ThrusterManager.cs
+++ b/QSB/Animation/Player/Thrusters/ThrusterManager.cs
@@ -1,4 +1,5 @@
-using QSB.Player;
+using QSB.Audio;
+using QSB.Player;
using UnityEngine;
namespace QSB.Animation.Player.Thrusters;
@@ -14,6 +15,11 @@ public static void CreateRemotePlayerVFX(PlayerInfo player)
InitParticleControllers(newVfx, player);
}
+ public static void CreateRemotePlayerSFX(PlayerInfo player)
+ {
+ player.Body.GetComponentInChildren(true)?.Init(player);
+ }
+
private static void InitFlameControllers(GameObject root, PlayerInfo player)
{
var existingControllers = root.GetComponentsInChildren(true);
diff --git a/QSB/AssetBundles/qsb_network b/QSB/AssetBundles/qsb_network
index 5af36f0d5..cbf886f50 100644
Binary files a/QSB/AssetBundles/qsb_network and b/QSB/AssetBundles/qsb_network differ
diff --git a/QSB/AssetBundles/qsb_network_big b/QSB/AssetBundles/qsb_network_big
index 4688aa523..22247a5b6 100644
Binary files a/QSB/AssetBundles/qsb_network_big and b/QSB/AssetBundles/qsb_network_big differ
diff --git a/QSB/Audio/Messages/PlayerAudioControllerOneShotMessage.cs b/QSB/Audio/Messages/PlayerAudioControllerOneShotMessage.cs
new file mode 100644
index 000000000..5c3b8c541
--- /dev/null
+++ b/QSB/Audio/Messages/PlayerAudioControllerOneShotMessage.cs
@@ -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);
+}
diff --git a/QSB/Audio/Messages/PlayerAudioControllerUpdateHazardDamageMessage.cs b/QSB/Audio/Messages/PlayerAudioControllerUpdateHazardDamageMessage.cs
new file mode 100644
index 000000000..b7ce4ca51
--- /dev/null
+++ b/QSB/Audio/Messages/PlayerAudioControllerUpdateHazardDamageMessage.cs
@@ -0,0 +1,12 @@
+using QSB.Messaging;
+using QSB.Player;
+
+namespace QSB.Audio.Messages;
+
+internal class PlayerAudioControllerUpdateHazardDamageMessage : QSBMessage<(uint userID, HazardVolume.HazardType latestHazardType)>
+{
+ public PlayerAudioControllerUpdateHazardDamageMessage((uint userID, HazardVolume.HazardType latestHazardType) data) : base(data) { }
+
+ public override void OnReceiveRemote() =>
+ QSBPlayerManager.GetPlayer(Data.userID)?.AudioController.SetHazardDamage(Data.latestHazardType);
+}
diff --git a/QSB/Audio/Messages/PlayerMovementAudioFootstepMessage.cs b/QSB/Audio/Messages/PlayerMovementAudioFootstepMessage.cs
new file mode 100644
index 000000000..f17503ec2
--- /dev/null
+++ b/QSB/Audio/Messages/PlayerMovementAudioFootstepMessage.cs
@@ -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);
+}
diff --git a/QSB/Audio/Messages/PlayerMovementAudioJumpMessage.cs b/QSB/Audio/Messages/PlayerMovementAudioJumpMessage.cs
new file mode 100644
index 000000000..adae81493
--- /dev/null
+++ b/QSB/Audio/Messages/PlayerMovementAudioJumpMessage.cs
@@ -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);
+}
diff --git a/QSB/Audio/Messages/ShipThrusterAudioOneShotMessage.cs b/QSB/Audio/Messages/ShipThrusterAudioOneShotMessage.cs
new file mode 100644
index 000000000..156e51b2f
--- /dev/null
+++ b/QSB/Audio/Messages/ShipThrusterAudioOneShotMessage.cs
@@ -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);
+ }
+
+}
diff --git a/QSB/Audio/Patches/PlayerAudioControllerPatches.cs b/QSB/Audio/Patches/PlayerAudioControllerPatches.cs
new file mode 100644
index 000000000..cf10f8234
--- /dev/null
+++ b/QSB/Audio/Patches/PlayerAudioControllerPatches.cs
@@ -0,0 +1,64 @@
+using HarmonyLib;
+using QSB.Audio.Messages;
+using QSB.Messaging;
+using QSB.Patches;
+using QSB.Player;
+using UnityEngine;
+
+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);
+
+ [HarmonyPostfix]
+ [HarmonyPatch(typeof(PlayerAudioController), nameof(PlayerAudioController.OnArtifactFocus))]
+ public static void PlayerAudioController_OnArtifactFocus() => PlayOneShot(AudioType.Artifact_Focus);
+
+ [HarmonyPostfix]
+ [HarmonyPatch(typeof(PlayerAudioController), nameof(PlayerAudioController.OnArtifactUnfocus))]
+ public static void PlayerAudioController_OnArtifactUnfocus() => PlayOneShot(AudioType.Artifact_Unfocus);
+
+ [HarmonyPostfix]
+ [HarmonyPatch(typeof(PlayerAudioController), nameof(PlayerAudioController.OnArtifactConceal))]
+ public static void PlayerAudioController_OnArtifactConceal() => PlayOneShot(AudioType.Artifact_Conceal);
+
+ [HarmonyPostfix]
+ [HarmonyPatch(typeof(PlayerAudioController), nameof(PlayerAudioController.OnArtifactUnconceal))]
+ public static void PlayerAudioController_OnArtifactUnconceal() => PlayOneShot(AudioType.Artifact_Unconceal);
+
+ [HarmonyPrefix]
+ [HarmonyPatch(typeof(PlayerAudioController), nameof(PlayerAudioController.UpdateHazardDamage))]
+ public static void PlayerAudioController_UpdateHazardDamage(PlayerAudioController __instance, float damage, HazardDetector hazardDetector)
+ {
+ var hazardType = damage > 0f ? hazardDetector.GetLatestHazardType() : HazardVolume.HazardType.NONE;
+ if (hazardType != __instance._hazardTypePlaying)
+ {
+ new PlayerAudioControllerUpdateHazardDamageMessage((QSBPlayerManager.LocalPlayerId, hazardDetector.GetLatestHazardType())).Send();
+ }
+ }
+}
\ No newline at end of file
diff --git a/QSB/Audio/Patches/PlayerImpactAudioPatches.cs b/QSB/Audio/Patches/PlayerImpactAudioPatches.cs
new file mode 100644
index 000000000..64cfea29d
--- /dev/null
+++ b/QSB/Audio/Patches/PlayerImpactAudioPatches.cs
@@ -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();
+ }
+
+ [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()?.Reset();
+
+ [HarmonyPostfix]
+ [HarmonyPatch(typeof(PlayerImpactAudio), nameof(PlayerImpactAudio.OnImpact))]
+ public static void PlayerImpactAudio_OnImpact_Postfix(PlayerImpactAudio __instance)
+ {
+ var tracker = __instance.gameObject.GetComponent();
+ if (tracker)
+ {
+ if (tracker.LastPlayed != AudioType.None)
+ {
+ new PlayerAudioControllerOneShotMessage(tracker.LastPlayed, QSBPlayerManager.LocalPlayerId, tracker.Pitch, tracker.Volume).Send();
+ }
+ }
+ }
+}
diff --git a/QSB/Audio/Patches/PlayerMovementAudioPatches.cs b/QSB/Audio/Patches/PlayerMovementAudioPatches.cs
new file mode 100644
index 000000000..ecdfa51bb
--- /dev/null
+++ b/QSB/Audio/Patches/PlayerMovementAudioPatches.cs
@@ -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();
+ }
+}
\ No newline at end of file
diff --git a/QSB/Audio/Patches/ThrusterAudioPatches.cs b/QSB/Audio/Patches/ThrusterAudioPatches.cs
new file mode 100644
index 000000000..be09b0d94
--- /dev/null
+++ b/QSB/Audio/Patches/ThrusterAudioPatches.cs
@@ -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();
+ }
+
+ [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()?.Reset();
+
+ [HarmonyPostfix]
+ [HarmonyPatch(typeof(ThrusterAudio), nameof(ThrusterAudio.OnFireRotationalThruster))]
+ public static void ThrusterAudio_OnFireRotationalThruster_Postfix(ThrusterAudio __instance)
+ {
+ if (__instance._rotationalSource.gameObject.TryGetComponent(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?
+ }
+ }
+ }
+}
diff --git a/QSB/Audio/QSBAudioSourceOneShotTracker.cs b/QSB/Audio/QSBAudioSourceOneShotTracker.cs
new file mode 100644
index 000000000..d3c8d6159
--- /dev/null
+++ b/QSB/Audio/QSBAudioSourceOneShotTracker.cs
@@ -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;
+
+///
+/// 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
+///
+[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();
+ }
+}
+
+[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();
+ 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();
+ if (tracker)
+ {
+ tracker.LastPlayed = type;
+ tracker.Volume = volume;
+ tracker.Index = index;
+ }
+ }
+}
diff --git a/QSB/Audio/QSBJetpackThrusterAudio.cs b/QSB/Audio/QSBJetpackThrusterAudio.cs
index e43f0b410..6a93b3110 100644
--- a/QSB/Audio/QSBJetpackThrusterAudio.cs
+++ b/QSB/Audio/QSBJetpackThrusterAudio.cs
@@ -1,8 +1,99 @@
-namespace QSB.Audio;
+using QSB.Player;
+using QSB.Utility;
+using UnityEngine;
+namespace QSB.Audio;
+
+[UsedInUnityProject]
internal class QSBJetpackThrusterAudio : QSBThrusterAudio
{
public OWAudioSource _underwaterSource;
public OWAudioSource _oxygenSource;
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);
+ }
}
\ No newline at end of file
diff --git a/QSB/Audio/QSBPlayerAudioController.cs b/QSB/Audio/QSBPlayerAudioController.cs
index c5ebe457a..0c50def97 100644
--- a/QSB/Audio/QSBPlayerAudioController.cs
+++ b/QSB/Audio/QSBPlayerAudioController.cs
@@ -1,11 +1,34 @@
-using UnityEngine;
+using QSB.PlayerBodySetup.Remote;
+using QSB.Utility;
+using UnityEngine;
namespace QSB.Audio;
+[UsedInUnityProject]
public class QSBPlayerAudioController : MonoBehaviour
{
public OWAudioSource _oneShotExternalSource;
public OWAudioSource _repairToolSource;
+ public OWAudioSource _damageAudioSource;
+
+ private AudioManager _audioManager;
+
+ public void Start()
+ {
+ _audioManager = Locator.GetAudioManager();
+
+ // TODO: This should be done in the Unity project
+ var damageAudio = new GameObject("DamageAudioSource");
+ damageAudio.SetActive(false);
+ damageAudio.transform.SetParent(transform, false);
+ damageAudio.transform.localPosition = Vector3.zero;
+ _damageAudioSource = damageAudio.AddComponent();
+ _damageAudioSource._audioSource = damageAudio.GetAddComponent();
+ _damageAudioSource.SetTrack(_repairToolSource.GetTrack());
+ _damageAudioSource.spatialBlend = 1f;
+ _damageAudioSource.gameObject.GetAddComponent();
+ damageAudio.SetActive(true);
+ }
public void PlayEquipTool()
=> _oneShotExternalSource?.PlayOneShot(AudioType.ToolTranslatorEquip);
@@ -18,4 +41,62 @@ public void PlayTurnOnFlashlight()
public void PlayTurnOffFlashlight()
=> _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);
+
+ private void StartHazardDamage(HazardVolume.HazardType latestHazardType)
+ {
+ var type = AudioType.EnterVolumeDamageHeat_LP;
+ if (latestHazardType == HazardVolume.HazardType.DARKMATTER)
+ {
+ type = AudioType.EnterVolumeDamageGhostfire_LP;
+ }
+ else if (latestHazardType == HazardVolume.HazardType.FIRE)
+ {
+ type = AudioType.EnterVolumeDamageFire_LP;
+ }
+
+ _damageAudioSource.clip = _audioManager.GetSingleAudioClip(type, true);
+ _damageAudioSource.Stop();
+ _damageAudioSource.FadeIn(0.2f, true, true, 1f);
+ }
+
+ private void EndHazardDamage()
+ {
+ if (_damageAudioSource.isPlaying)
+ {
+ _damageAudioSource.FadeOut(0.5f, OWAudioSource.FadeOutCompleteAction.STOP, 0f);
+ }
+ }
+
+ public void SetHazardDamage(HazardVolume.HazardType latestHazardType)
+ {
+ if (latestHazardType == HazardVolume.HazardType.NONE)
+ {
+ EndHazardDamage();
+ }
+ else
+ {
+ StartHazardDamage(latestHazardType);
+ }
+ }
}
\ No newline at end of file
diff --git a/QSB/ConversationSync/WorldObjects/QSBCharacterDialogueTree.cs b/QSB/ConversationSync/WorldObjects/QSBCharacterDialogueTree.cs
index 17e8d329b..45fac711c 100644
--- a/QSB/ConversationSync/WorldObjects/QSBCharacterDialogueTree.cs
+++ b/QSB/ConversationSync/WorldObjects/QSBCharacterDialogueTree.cs
@@ -4,6 +4,9 @@
namespace QSB.ConversationSync.WorldObjects;
+///
+/// BUG: do conversation leave on player leave so other people can actually talk lol
+///
public class QSBCharacterDialogueTree : WorldObject
{
public override void SendInitialState(uint to)
diff --git a/QSB/EchoesOfTheEye/DreamCandles/Messages/SetLitMessage.cs b/QSB/EchoesOfTheEye/DreamCandles/Messages/SetLitMessage.cs
index 175c51ed5..2910c4b47 100644
--- a/QSB/EchoesOfTheEye/DreamCandles/Messages/SetLitMessage.cs
+++ b/QSB/EchoesOfTheEye/DreamCandles/Messages/SetLitMessage.cs
@@ -1,6 +1,5 @@
using QSB.EchoesOfTheEye.DreamCandles.WorldObjects;
using QSB.Messaging;
-using QSB.Patches;
namespace QSB.EchoesOfTheEye.DreamCandles.Messages;
@@ -11,5 +10,5 @@ public SetLitMessage(bool lit, bool playAudio, bool instant) :
base((lit, playAudio, instant)) { }
public override void OnReceiveRemote() =>
- QSBPatch.RemoteCall(() => WorldObject.AttachedObject.SetLit(Data.Lit, Data.PlayAudio, Data.Instant));
+ WorldObject.AttachedObject.SetLit(Data.Lit, Data.PlayAudio, Data.Instant);
}
diff --git a/QSB/EchoesOfTheEye/DreamLantern/DreamLanternManager.cs b/QSB/EchoesOfTheEye/DreamLantern/DreamLanternManager.cs
index 700f0865c..bdb6d5b43 100644
--- a/QSB/EchoesOfTheEye/DreamLantern/DreamLanternManager.cs
+++ b/QSB/EchoesOfTheEye/DreamLantern/DreamLanternManager.cs
@@ -11,5 +11,5 @@ public class DreamLanternManager : WorldObjectManager
public override bool DlcOnly => true;
public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct) =>
- QSBWorldSync.Init();
+ QSBWorldSync.Init();
}
diff --git a/QSB/EchoesOfTheEye/DreamLantern/Messages/SetConcealedMessage.cs b/QSB/EchoesOfTheEye/DreamLantern/Messages/SetConcealedMessage.cs
index a06f1d595..802606a6c 100644
--- a/QSB/EchoesOfTheEye/DreamLantern/Messages/SetConcealedMessage.cs
+++ b/QSB/EchoesOfTheEye/DreamLantern/Messages/SetConcealedMessage.cs
@@ -1,13 +1,12 @@
using QSB.EchoesOfTheEye.DreamLantern.WorldObjects;
using QSB.Messaging;
-using QSB.Patches;
namespace QSB.EchoesOfTheEye.DreamLantern.Messages;
-internal class SetConcealedMessage : QSBWorldObjectMessage
+internal class SetConcealedMessage : QSBWorldObjectMessage
{
public SetConcealedMessage(bool concealed) : base(concealed) { }
- public override void OnReceiveRemote()
- => QSBPatch.RemoteCall(() => WorldObject.AttachedObject.SetConcealed(Data));
+ public override void OnReceiveRemote() =>
+ WorldObject.AttachedObject.SetConcealed(Data);
}
diff --git a/QSB/EchoesOfTheEye/DreamLantern/Messages/SetFocusMessage.cs b/QSB/EchoesOfTheEye/DreamLantern/Messages/SetFocusMessage.cs
index 1fa7c5e39..762820f70 100644
--- a/QSB/EchoesOfTheEye/DreamLantern/Messages/SetFocusMessage.cs
+++ b/QSB/EchoesOfTheEye/DreamLantern/Messages/SetFocusMessage.cs
@@ -1,13 +1,12 @@
using QSB.EchoesOfTheEye.DreamLantern.WorldObjects;
using QSB.Messaging;
-using QSB.Patches;
namespace QSB.EchoesOfTheEye.DreamLantern.Messages;
-internal class SetFocusMessage : QSBWorldObjectMessage
+internal class SetFocusMessage : QSBWorldObjectMessage
{
public SetFocusMessage(float focus) : base(focus) { }
public override void OnReceiveRemote()
- => QSBPatch.RemoteCall(() => WorldObject.AttachedObject.SetFocus(Data));
+ => WorldObject.AttachedObject.SetFocus(Data);
}
diff --git a/QSB/EchoesOfTheEye/DreamLantern/Messages/SetLitMessage.cs b/QSB/EchoesOfTheEye/DreamLantern/Messages/SetLitMessage.cs
index 48e4395a7..0cd54f897 100644
--- a/QSB/EchoesOfTheEye/DreamLantern/Messages/SetLitMessage.cs
+++ b/QSB/EchoesOfTheEye/DreamLantern/Messages/SetLitMessage.cs
@@ -1,13 +1,21 @@
using QSB.EchoesOfTheEye.DreamLantern.WorldObjects;
using QSB.Messaging;
-using QSB.Patches;
namespace QSB.EchoesOfTheEye.DreamLantern.Messages;
-internal class SetLitMessage : QSBWorldObjectMessage
+internal class SetLitMessage : QSBWorldObjectMessage
{
public SetLitMessage(bool lit) : base(lit) { }
public override void OnReceiveRemote()
- => QSBPatch.RemoteCall(() => WorldObject.AttachedObject.SetLit(Data));
+ {
+ WorldObject.AttachedObject.SetLit(Data);
+ WorldObject.DreamLanternItem?._oneShotSource?.PlayOneShot(Data ? AudioType.Artifact_Light : AudioType.Artifact_Extinguish);
+
+ // If a lantern is already lit you shouldn't be able to pick it up
+ if (Data)
+ {
+ WorldObject.DreamLanternItem?.EnableInteraction(false);
+ }
+ }
}
diff --git a/QSB/EchoesOfTheEye/DreamLantern/Messages/SetRangeMessage.cs b/QSB/EchoesOfTheEye/DreamLantern/Messages/SetRangeMessage.cs
index 093fde836..b5e7cdb94 100644
--- a/QSB/EchoesOfTheEye/DreamLantern/Messages/SetRangeMessage.cs
+++ b/QSB/EchoesOfTheEye/DreamLantern/Messages/SetRangeMessage.cs
@@ -1,13 +1,12 @@
using QSB.EchoesOfTheEye.DreamLantern.WorldObjects;
using QSB.Messaging;
-using QSB.Patches;
namespace QSB.EchoesOfTheEye.DreamLantern.Messages;
-internal class SetRangeMessage : QSBWorldObjectMessage
+internal class SetRangeMessage : QSBWorldObjectMessage
{
public SetRangeMessage(float minRange, float maxRange) : base((minRange, maxRange)) { }
public override void OnReceiveRemote()
- => QSBPatch.RemoteCall(() => WorldObject.AttachedObject.SetRange(Data.minRange, Data.maxRange));
+ => WorldObject.AttachedObject.SetRange(Data.minRange, Data.maxRange);
}
diff --git a/QSB/EchoesOfTheEye/DreamLantern/Patches/DreamLanternPatches.cs b/QSB/EchoesOfTheEye/DreamLantern/Patches/DreamLanternPatches.cs
index 2b83b6d6d..f2ed4886e 100644
--- a/QSB/EchoesOfTheEye/DreamLantern/Patches/DreamLanternPatches.cs
+++ b/QSB/EchoesOfTheEye/DreamLantern/Patches/DreamLanternPatches.cs
@@ -31,7 +31,13 @@ public static void SetLit(DreamLanternController __instance, bool lit)
return;
}
- __instance.GetWorldObject().SendMessage(new SetLitMessage(lit));
+ var qsbDreamLantern = __instance.GetWorldObject();
+ // ghost lanterns should only be controlled by the host
+ if (qsbDreamLantern.IsGhostLantern && !QSBCore.IsHost)
+ {
+ return;
+ }
+ qsbDreamLantern.SendMessage(new SetLitMessage(lit));
}
[HarmonyPrefix]
@@ -53,7 +59,13 @@ public static void SetConcealed(DreamLanternController __instance, bool conceale
return;
}
- __instance.GetWorldObject().SendMessage(new SetConcealedMessage(concealed));
+ var qsbDreamLantern = __instance.GetWorldObject();
+ // ghost lanterns should only be controlled by the host
+ if (qsbDreamLantern.IsGhostLantern && !QSBCore.IsHost)
+ {
+ return;
+ }
+ qsbDreamLantern.SendMessage(new SetConcealedMessage(concealed));
}
[HarmonyPrefix]
@@ -76,7 +88,13 @@ public static void SetFocus(DreamLanternController __instance, float focus)
return;
}
- __instance.GetWorldObject().SendMessage(new SetFocusMessage(focus));
+ var qsbDreamLantern = __instance.GetWorldObject();
+ // ghost lanterns should only be controlled by the host
+ if (qsbDreamLantern.IsGhostLantern && !QSBCore.IsHost)
+ {
+ return;
+ }
+ qsbDreamLantern.SendMessage(new SetFocusMessage(focus));
}
[HarmonyPrefix]
@@ -98,6 +116,12 @@ public static void SetRange(DreamLanternController __instance, float minRange, f
return;
}
- __instance.GetWorldObject().SendMessage(new SetRangeMessage(minRange, maxRange));
+ var qsbDreamLantern = __instance.GetWorldObject();
+ // ghost lanterns should only be controlled by the host
+ if (qsbDreamLantern.IsGhostLantern && !QSBCore.IsHost)
+ {
+ return;
+ }
+ qsbDreamLantern.SendMessage(new SetRangeMessage(minRange, maxRange));
}
}
diff --git a/QSB/EchoesOfTheEye/DreamLantern/WorldObjects/QSBDreamLantern.cs b/QSB/EchoesOfTheEye/DreamLantern/WorldObjects/QSBDreamLantern.cs
deleted file mode 100644
index 1f5fe10b2..000000000
--- a/QSB/EchoesOfTheEye/DreamLantern/WorldObjects/QSBDreamLantern.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using QSB.EchoesOfTheEye.DreamLantern.Messages;
-using QSB.Messaging;
-using QSB.WorldSync;
-
-namespace QSB.EchoesOfTheEye.DreamLantern.WorldObjects;
-
-///
-/// TODO: lanterns held by ghosts should only be controlled by the host (to prevent it from visually freaking out)
-///
-public class QSBDreamLantern : WorldObject
-{
- public override void SendInitialState(uint to)
- {
- this.SendMessage(new SetLitMessage(AttachedObject._lit) { To = to });
- this.SendMessage(new SetConcealedMessage(AttachedObject._concealed) { To = to });
- this.SendMessage(new SetFocusMessage(AttachedObject._focus) { To = to });
- this.SendMessage(new SetRangeMessage(AttachedObject._minRange, AttachedObject._maxRange) { To = to });
- }
-}
diff --git a/QSB/EchoesOfTheEye/DreamLantern/WorldObjects/QSBDreamLanternController.cs b/QSB/EchoesOfTheEye/DreamLantern/WorldObjects/QSBDreamLanternController.cs
new file mode 100644
index 000000000..ce484d02d
--- /dev/null
+++ b/QSB/EchoesOfTheEye/DreamLantern/WorldObjects/QSBDreamLanternController.cs
@@ -0,0 +1,31 @@
+using Cysharp.Threading.Tasks;
+using QSB.EchoesOfTheEye.DreamLantern.Messages;
+using QSB.Messaging;
+using QSB.WorldSync;
+using System.Threading;
+
+namespace QSB.EchoesOfTheEye.DreamLantern.WorldObjects;
+
+public class QSBDreamLanternController : WorldObject
+{
+ public DreamLanternItem DreamLanternItem { get; private set; }
+
+ public override async UniTask Init(CancellationToken ct)
+ {
+ // Ghosts don't have the item and instead the effects are controlled by GhostEffects
+ if (!IsGhostLantern)
+ {
+ DreamLanternItem = AttachedObject.GetComponent();
+ }
+ }
+
+ public override void SendInitialState(uint to)
+ {
+ this.SendMessage(new SetLitMessage(AttachedObject._lit) { To = to });
+ this.SendMessage(new SetConcealedMessage(AttachedObject._concealed) { To = to });
+ this.SendMessage(new SetFocusMessage(AttachedObject._focus) { To = to });
+ this.SendMessage(new SetRangeMessage(AttachedObject._minRange, AttachedObject._maxRange) { To = to });
+ }
+
+ public bool IsGhostLantern => AttachedObject.name == "GhostLantern"; // it's as shrimple as that
+}
diff --git a/QSB/EchoesOfTheEye/DreamObjectProjectors/Messages/ProjectorLitMessage.cs b/QSB/EchoesOfTheEye/DreamObjectProjectors/Messages/ProjectorLitMessage.cs
index bad80e3f4..3715abd11 100644
--- a/QSB/EchoesOfTheEye/DreamObjectProjectors/Messages/ProjectorLitMessage.cs
+++ b/QSB/EchoesOfTheEye/DreamObjectProjectors/Messages/ProjectorLitMessage.cs
@@ -1,6 +1,5 @@
using QSB.EchoesOfTheEye.DreamObjectProjectors.WorldObject;
using QSB.Messaging;
-using QSB.Patches;
namespace QSB.EchoesOfTheEye.DreamObjectProjectors.Messages;
@@ -8,6 +7,5 @@ internal class ProjectorLitMessage : QSBWorldObjectMessage QSBPatch.RemoteCall(() => WorldObject.AttachedObject.SetLit(Data));
+ public override void OnReceiveRemote() => WorldObject.AttachedObject.SetLit(Data);
}
diff --git a/QSB/EchoesOfTheEye/DreamRafts/Messages/RespawnRaftMessage.cs b/QSB/EchoesOfTheEye/DreamRafts/Messages/RespawnRaftMessage.cs
index 6dcf31055..ebf7a4c9e 100644
--- a/QSB/EchoesOfTheEye/DreamRafts/Messages/RespawnRaftMessage.cs
+++ b/QSB/EchoesOfTheEye/DreamRafts/Messages/RespawnRaftMessage.cs
@@ -1,6 +1,5 @@
using QSB.EchoesOfTheEye.DreamObjectProjectors.WorldObject;
using QSB.Messaging;
-using QSB.Patches;
namespace QSB.EchoesOfTheEye.DreamRafts.Messages;
@@ -9,6 +8,6 @@ public class RespawnRaftMessage : QSBWorldObjectMessage
public override void OnReceiveRemote()
{
var attachedObject = (DreamRaftProjector)WorldObject.AttachedObject;
- QSBPatch.RemoteCall(attachedObject.RespawnRaft);
+ attachedObject.RespawnRaft();
}
}
diff --git a/QSB/EchoesOfTheEye/DreamRafts/Patches/DreamRaftPatches.cs b/QSB/EchoesOfTheEye/DreamRafts/Patches/DreamRaftPatches.cs
index d1afc72eb..a0e9cd55a 100644
--- a/QSB/EchoesOfTheEye/DreamRafts/Patches/DreamRaftPatches.cs
+++ b/QSB/EchoesOfTheEye/DreamRafts/Patches/DreamRaftPatches.cs
@@ -60,19 +60,26 @@ private static void RespawnRaft_Postfix(DreamRaftProjector __instance)
/// this is to suspend the raft so it doesn't fall endlessly.
/// however, it's okay if it does that,
/// and we don't want it to extinguish with other players on it.
+ ///
+ /// BUG: this breaks when going thru the volume as a non auth player sometimes. oh well.
+ /// TODO: use in-raft-volume trigger volume instead. just copy from the ringworld rafts. use this for fake sectors as well
///
[HarmonyPrefix]
- [HarmonyPatch(typeof(DreamRaftProjector), nameof(DreamRaftProjector.ExtinguishImmediately))]
- private static bool ExtinguishImmediately(DreamRaftProjector __instance)
+ [HarmonyPatch(typeof(DreamWorldController), nameof(DreamWorldController.ExtinguishDreamRaft))]
+ private static bool ExtinguishDreamRaft(DreamWorldController __instance)
{
if (!QSBWorldSync.AllObjectsReady)
{
return true;
}
- // still release authority over the raft tho
- __instance._dreamRaftProjection.GetComponent().GetWorldObject()
- .NetworkBehaviour.netIdentity.UpdateAuthQueue(AuthQueueAction.Remove);
+ if (__instance._lastUsedRaftProjector)
+ {
+ // still release authority over the raft tho
+ __instance._lastUsedRaftProjector
+ ._dreamRaftProjection.GetComponent().GetWorldObject()
+ .NetworkBehaviour.netIdentity.UpdateAuthQueue(AuthQueueAction.Remove);
+ }
return false;
}
diff --git a/QSB/EchoesOfTheEye/EclipseCodeControllers/WorldObjects/QSBEclipseCodeController.cs b/QSB/EchoesOfTheEye/EclipseCodeControllers/WorldObjects/QSBEclipseCodeController.cs
index 0f8b1beb3..36ab0cf15 100644
--- a/QSB/EchoesOfTheEye/EclipseCodeControllers/WorldObjects/QSBEclipseCodeController.cs
+++ b/QSB/EchoesOfTheEye/EclipseCodeControllers/WorldObjects/QSBEclipseCodeController.cs
@@ -32,8 +32,17 @@ public override void OnRemoval()
}
}
- private void OnPlayerLeave(PlayerInfo obj) =>
- this.SendMessage(new UseControllerMessage(false));
+ private void OnPlayerLeave(PlayerInfo player)
+ {
+ if (!QSBCore.IsHost)
+ {
+ return;
+ }
+ if (PlayerInControl == player)
+ {
+ this.SendMessage(new UseControllerMessage(false));
+ }
+ }
public void SetUser(uint user)
{
diff --git a/QSB/EchoesOfTheEye/Ghosts/Actions/QSBIdentifyIntruderAction.cs b/QSB/EchoesOfTheEye/Ghosts/Actions/QSBIdentifyIntruderAction.cs
index e79417cc4..1934de9ec 100644
--- a/QSB/EchoesOfTheEye/Ghosts/Actions/QSBIdentifyIntruderAction.cs
+++ b/QSB/EchoesOfTheEye/Ghosts/Actions/QSBIdentifyIntruderAction.cs
@@ -56,7 +56,11 @@ public override float CalculateUtility()
return -100f;
}
- if (_running || (_data.interestedPlayer.sensor.isPlayerHeldLanternVisible && (_data.threatAwareness > GhostData.ThreatAwareness.EverythingIsNormal || _data.interestedPlayer.playerLocation.distance < 20f)) || _data.interestedPlayer.sensor.isIlluminatedByPlayer)
+ if (_running
+ || (_data.interestedPlayer.sensor.isPlayerHeldLanternVisible
+ && (_data.threatAwareness > GhostData.ThreatAwareness.EverythingIsNormal || _data.interestedPlayer.playerLocation.distance < 20f)
+ && _controller.AttachedObject.GetNodeMap().CheckLocalPointInBounds(_data.interestedPlayer.playerLocation.localPosition))
+ || _data.interestedPlayer.sensor.isIlluminatedByPlayer)
{
return 80f;
}
diff --git a/QSB/EchoesOfTheEye/Ghosts/Actions/QSBStalkAction.cs b/QSB/EchoesOfTheEye/Ghosts/Actions/QSBStalkAction.cs
index 2dd843468..9d9f1a450 100644
--- a/QSB/EchoesOfTheEye/Ghosts/Actions/QSBStalkAction.cs
+++ b/QSB/EchoesOfTheEye/Ghosts/Actions/QSBStalkAction.cs
@@ -37,8 +37,7 @@ protected override void OnEnterAction()
{
var flag = _data.interestedPlayer.player.AssignedSimulationLantern.AttachedObject.GetLanternController().IsConcealed();
_wasPlayerLanternConcealed = flag;
- _isFocusingLight = flag;
- _shouldFocusLightOnPlayer = flag;
+ _isFocusingLight = _shouldFocusLightOnPlayer = flag || !_data.interestedPlayer.sensor.isPlayerHoldingLantern;
_changeFocusTime = 0f;
_controller.ChangeLanternFocus(_isFocusingLight ? 1f : 0f, 2f);
_controller.SetLanternConcealed(!_isFocusingLight, true);
@@ -80,15 +79,16 @@ public override void FixedUpdate_Action()
&& _data.interestedPlayer.wasPlayerLocationKnown;
_wasPlayerLanternConcealed = isPlayerLanternConcealed;
- if (sawPlayerLanternConceal && !_shouldFocusLightOnPlayer)
+ var flag3 = (!_data.interestedPlayer.sensor.isPlayerHoldingLantern && _data.interestedPlayer.wasPlayerLocationKnown) || _data.interestedPlayer.sensor.isPlayerDroppedLanternVisible;
+ if ((sawPlayerLanternConceal || flag3) && !_shouldFocusLightOnPlayer)
{
_shouldFocusLightOnPlayer = true;
- _changeFocusTime = Time.time + 1f;
+ _changeFocusTime = Time.time + 0.5f;
}
else if (_data.interestedPlayer.sensor.isPlayerHeldLanternVisible && _shouldFocusLightOnPlayer)
{
_shouldFocusLightOnPlayer = false;
- _changeFocusTime = Time.time + 1f;
+ _changeFocusTime = Time.time + 0.5f;
}
if (_isFocusingLight != _shouldFocusLightOnPlayer && Time.time > _changeFocusTime)
diff --git a/QSB/EchoesOfTheEye/Ghosts/Messages/FaceLocalDirectionMessage.cs b/QSB/EchoesOfTheEye/Ghosts/Messages/FaceLocalDirectionMessage.cs
deleted file mode 100644
index 72eec1c1c..000000000
--- a/QSB/EchoesOfTheEye/Ghosts/Messages/FaceLocalDirectionMessage.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
-using QSB.Messaging;
-using QSB.Utility;
-using UnityEngine;
-
-namespace QSB.EchoesOfTheEye.Ghosts.Messages;
-
-internal class FaceLocalDirectionMessage : QSBWorldObjectMessage
-{
- public FaceLocalDirectionMessage(Vector3 localDirection, float degreesPerSecond, float turnAcceleration) : base((localDirection, degreesPerSecond, turnAcceleration)) { }
-
- public override void OnReceiveRemote()
- {
- if (QSBCore.IsHost)
- {
- DebugLog.ToConsole("Error - Received FaceLocalDirectionMessage on host. Something has gone horribly wrong!", OWML.Common.MessageType.Error);
- return;
- }
-
- WorldObject.FaceLocalDirection(Data.localDirection, Data.degreesPerSecond, Data.turnAcceleration, true);
- }
-}
diff --git a/QSB/EchoesOfTheEye/Ghosts/Patches/GhostPartyPathDirectorPatches.cs b/QSB/EchoesOfTheEye/Ghosts/Patches/GhostPartyPathDirectorPatches.cs
index 9db6902ad..68491445b 100644
--- a/QSB/EchoesOfTheEye/Ghosts/Patches/GhostPartyPathDirectorPatches.cs
+++ b/QSB/EchoesOfTheEye/Ghosts/Patches/GhostPartyPathDirectorPatches.cs
@@ -81,12 +81,15 @@ public static bool Update(GhostPartyPathDirector __instance)
ghostBrain.AttachedObject.transform.eulerAngles = Vector3.up * __instance._ghostSpawns[Random.Range(0, __instance._ghostSpawns.Length)].spawnTransform.eulerAngles.y;
ghostBrain.TabulaRasa();
partyPathAction.ResetPath();
- if (__instance._numEnabledGhostProxies < __instance._ghostFinalDestinations.Length && __instance._ghostFinalDestinations[__instance._numEnabledGhostProxies].proxyGhost != null)
+ if (!__instance._disableGhostProxies && __instance._numEnabledGhostProxies < __instance._ghostFinalDestinations.Length)
{
- __instance._ghostFinalDestinations[__instance._numEnabledGhostProxies].proxyGhost.gameObject.SetActive(true);
+ if (__instance._ghostFinalDestinations[__instance._numEnabledGhostProxies].proxyGhost != null)
+ {
+ __instance._ghostFinalDestinations[__instance._numEnabledGhostProxies].proxyGhost.Reveal();
+ }
+ __instance._numEnabledGhostProxies++;
}
- __instance._numEnabledGhostProxies++;
__instance._waitingGhosts.Add(ghostBrain.AttachedObject);
}
}
diff --git a/QSB/EchoesOfTheEye/Ghosts/WorldObjects/QSBGhostController.cs b/QSB/EchoesOfTheEye/Ghosts/WorldObjects/QSBGhostController.cs
index 228ad9837..311376f84 100644
--- a/QSB/EchoesOfTheEye/Ghosts/WorldObjects/QSBGhostController.cs
+++ b/QSB/EchoesOfTheEye/Ghosts/WorldObjects/QSBGhostController.cs
@@ -222,26 +222,6 @@ public void FaceNode(GhostNode node, TurnSpeed turnSpeed, float nodeDelay, bool
AttachedObject.FaceNode(node, turnSpeed, nodeDelay, autoFocusLantern);
}
- public void FaceLocalDirection(Vector3 localDirection, TurnSpeed turnSpeed)
- {
- FaceLocalDirection(localDirection, GhostConstants.GetTurnSpeed(turnSpeed), GhostConstants.GetTurnAcceleration(turnSpeed));
- }
-
- public void FaceLocalDirection(Vector3 localDirection, float degreesPerSecond, float turnAcceleration = 360f, bool remote = false)
- {
- if (!remote)
- {
- if (!QSBCore.IsHost)
- {
- return;
- }
-
- this.SendMessage(new FaceLocalDirectionMessage(localDirection, degreesPerSecond, turnAcceleration));
- }
-
- AttachedObject.FaceLocalDirection(localDirection, degreesPerSecond, turnAcceleration);
- }
-
public void StopMoving()
{
if (!QSBCore.IsHost)
diff --git a/QSB/EchoesOfTheEye/Ghosts/WorldObjects/QSBGhostEffects.cs b/QSB/EchoesOfTheEye/Ghosts/WorldObjects/QSBGhostEffects.cs
index bb31271fc..eeaf87693 100644
--- a/QSB/EchoesOfTheEye/Ghosts/WorldObjects/QSBGhostEffects.cs
+++ b/QSB/EchoesOfTheEye/Ghosts/WorldObjects/QSBGhostEffects.cs
@@ -79,20 +79,30 @@ public void Update_Effects()
var relativeVelocity = AttachedObject._controller.GetRelativeVelocity();
var num = (AttachedObject._movementStyle == GhostEffects.MovementStyle.Chase) ? 8f : 2f;
- var targetValue = new Vector2(relativeVelocity.x / num, relativeVelocity.z / num);
+ float num2 = new Vector2(relativeVelocity.y, relativeVelocity.z).magnitude * Mathf.Sign(relativeVelocity.z);
+ Vector2 targetValue = new Vector2(relativeVelocity.x / num, num2 / num);
AttachedObject._smoothedMoveSpeed = AttachedObject._moveSpeedSpring.Update(AttachedObject._smoothedMoveSpeed, targetValue, Time.deltaTime);
AttachedObject._animator.SetFloat(GhostEffects.AnimatorKeys.Float_MoveDirectionX, AttachedObject._smoothedMoveSpeed.x);
AttachedObject._animator.SetFloat(GhostEffects.AnimatorKeys.Float_MoveDirectionY, AttachedObject._smoothedMoveSpeed.y);
+ float num3 = Vector3.SignedAngle(new Vector3(relativeVelocity.x, 0f, relativeVelocity.z), relativeVelocity, Vector3.left);
+ float targetValue2 = Mathf.Clamp(num3 / 30f, -1f, 1f);
+ if (num3 > 15f && AttachedObject._controller.IsApproachingEndOfIncline())
+ {
+ targetValue2 = 0f;
+ }
+ AttachedObject._smoothedMoveSlope = AttachedObject._moveSlopeSpring.Update(AttachedObject._smoothedMoveSlope, targetValue2, Time.deltaTime);
+ AttachedObject._animator.SetFloat(GhostEffects.AnimatorKeys.Float_MoveSlope, AttachedObject._smoothedMoveSlope);
+
AttachedObject._smoothedTurnSpeed = AttachedObject._turnSpeedSpring.Update(AttachedObject._smoothedTurnSpeed, AttachedObject._controller.GetAngularVelocity() / 90f, Time.deltaTime);
AttachedObject._animator.SetFloat(GhostEffects.AnimatorKeys.Float_TurnSpeed, AttachedObject._smoothedTurnSpeed);
var target = _data.isIlluminated ? 1f : 0f;
- var num2 = _data.isIlluminated ? 8f : 0.8f;
- AttachedObject._eyeGlow = Mathf.MoveTowards(AttachedObject._eyeGlow, target, Time.deltaTime * num2);
+ var num4 = _data.isIlluminated ? 8f : 0.8f;
+ AttachedObject._eyeGlow = Mathf.MoveTowards(AttachedObject._eyeGlow, target, Time.deltaTime * num4);
var closestPlayer = QSBPlayerManager.GetClosestPlayerToWorldPoint(AttachedObject.transform.position, true);
- var num3 = (closestPlayer?.AssignedSimulationLantern?.AttachedObject?.GetLanternController()?.GetLight()?.GetFlickerScale() - 1f + 0.07f) / 0.14f ?? 0;
- num3 = Mathf.Lerp(0.7f, 1f, num3);
+ var num5 = (closestPlayer?.AssignedSimulationLantern?.AttachedObject?.GetLanternController()?.GetLight()?.GetFlickerScale() - 1f + 0.07f) / 0.14f ?? 0;
+ num5 = Mathf.Lerp(0.7f, 1f, num5);
AttachedObject.SetEyeGlow(AttachedObject._eyeGlow * num3);
if (AttachedObject._playingDeathSequence)
diff --git a/QSB/EchoesOfTheEye/Ghosts/WorldObjects/QSBGhostSensors.cs b/QSB/EchoesOfTheEye/Ghosts/WorldObjects/QSBGhostSensors.cs
index af8b8d108..e38b02830 100644
--- a/QSB/EchoesOfTheEye/Ghosts/WorldObjects/QSBGhostSensors.cs
+++ b/QSB/EchoesOfTheEye/Ghosts/WorldObjects/QSBGhostSensors.cs
@@ -46,8 +46,10 @@ public void Initialize(QSBGhostData data)
}
public bool CanGrabPlayer(GhostPlayer player)
- => !PlayerState.IsAttached()
+ => !PlayerState.IsAttached() // TODO : check for each player
&& player.playerLocation.distanceXZ < 2f + AttachedObject._grabDistanceBuff
+ && player.playerLocation.toPosition.y > -2f
+ && player.playerLocation.toPosition.y < 3f
&& player.playerLocation.degreesToPositionXZ < 20f + AttachedObject._grabAngleBuff
&& AttachedObject._animator.GetFloat("GrabWindow") > 0.5f;
diff --git a/QSB/EchoesOfTheEye/LightSensorSync/Messages/IlluminatingLanternsMessage.cs b/QSB/EchoesOfTheEye/LightSensorSync/Messages/IlluminatingLanternsMessage.cs
index f557cbf6a..a9b657ad5 100644
--- a/QSB/EchoesOfTheEye/LightSensorSync/Messages/IlluminatingLanternsMessage.cs
+++ b/QSB/EchoesOfTheEye/LightSensorSync/Messages/IlluminatingLanternsMessage.cs
@@ -10,12 +10,12 @@ namespace QSB.EchoesOfTheEye.LightSensorSync.Messages;
internal class IlluminatingLanternsMessage : QSBWorldObjectMessage
{
public IlluminatingLanternsMessage(IEnumerable lanterns) :
- base(lanterns.Select(x => x.GetWorldObject().ObjectId).ToArray()) { }
+ base(lanterns.Select(x => x.GetWorldObject().ObjectId).ToArray()) { }
public override void OnReceiveRemote()
{
WorldObject.AttachedObject._illuminatingDreamLanternList.Clear();
WorldObject.AttachedObject._illuminatingDreamLanternList.AddRange(
- Data.Select(x => x.GetWorldObject().AttachedObject));
+ Data.Select(x => x.GetWorldObject().AttachedObject));
}
}
diff --git a/QSB/EchoesOfTheEye/LightSensorSync/Messages/PlayerIlluminatingLanternsMessage.cs b/QSB/EchoesOfTheEye/LightSensorSync/Messages/PlayerIlluminatingLanternsMessage.cs
index 4cc78e694..40bde54f3 100644
--- a/QSB/EchoesOfTheEye/LightSensorSync/Messages/PlayerIlluminatingLanternsMessage.cs
+++ b/QSB/EchoesOfTheEye/LightSensorSync/Messages/PlayerIlluminatingLanternsMessage.cs
@@ -12,7 +12,7 @@ internal class PlayerIlluminatingLanternsMessage : QSBMessage<(uint playerId, in
public PlayerIlluminatingLanternsMessage(uint playerId, IEnumerable lanterns) :
base((
playerId,
- lanterns.Select(x => x.GetWorldObject().ObjectId).ToArray()
+ lanterns.Select(x => x.GetWorldObject().ObjectId).ToArray()
)) { }
public override void OnReceiveRemote()
@@ -21,6 +21,6 @@ public override void OnReceiveRemote()
lightSensor._illuminatingDreamLanternList.Clear();
lightSensor._illuminatingDreamLanternList.AddRange(
- Data.lanterns.Select(x => x.GetWorldObject().AttachedObject));
+ Data.lanterns.Select(x => x.GetWorldObject().AttachedObject));
}
}
diff --git a/QSB/EchoesOfTheEye/LightSensorSync/Patches/LightSensorPatches.cs b/QSB/EchoesOfTheEye/LightSensorSync/Patches/LightSensorPatches.cs
index 20ffaf748..dd0a42476 100644
--- a/QSB/EchoesOfTheEye/LightSensorSync/Patches/LightSensorPatches.cs
+++ b/QSB/EchoesOfTheEye/LightSensorSync/Patches/LightSensorPatches.cs
@@ -111,6 +111,11 @@ private static bool ManagedFixedUpdate(SingleLightSensor __instance)
_illuminatingDreamLanternList.AddRange(__instance._illuminatingDreamLanternList);
}
__instance.UpdateIllumination();
+ if (__instance._illuminatingDreamLanternList != null &&
+ !__instance._illuminatingDreamLanternList.SequenceEqual(_illuminatingDreamLanternList))
+ {
+ qsbLightSensor.SendMessage(new IlluminatingLanternsMessage(__instance._illuminatingDreamLanternList));
+ }
if (!illuminated && __instance._illuminated)
{
__instance.OnDetectLight.Invoke();
@@ -121,11 +126,6 @@ private static bool ManagedFixedUpdate(SingleLightSensor __instance)
__instance.OnDetectDarkness.Invoke();
qsbLightSensor.SendMessage(new SetIlluminatedMessage(false));
}
- if (__instance._illuminatingDreamLanternList != null &&
- !__instance._illuminatingDreamLanternList.SequenceEqual(_illuminatingDreamLanternList))
- {
- qsbLightSensor.SendMessage(new IlluminatingLanternsMessage(__instance._illuminatingDreamLanternList));
- }
}
var locallyIlluminated = qsbLightSensor._locallyIlluminated;
@@ -165,6 +165,11 @@ private static bool Player_ManagedFixedUpdate(SingleLightSensor __instance)
_illuminatingDreamLanternList.AddRange(__instance._illuminatingDreamLanternList);
}
__instance.UpdateIllumination();
+ if (__instance._illuminatingDreamLanternList != null &&
+ !__instance._illuminatingDreamLanternList.SequenceEqual(_illuminatingDreamLanternList))
+ {
+ new PlayerIlluminatingLanternsMessage(QSBPlayerManager.LocalPlayerId, __instance._illuminatingDreamLanternList).Send();
+ }
if (!illuminated && __instance._illuminated)
{
__instance.OnDetectLight.Invoke();
@@ -175,11 +180,6 @@ private static bool Player_ManagedFixedUpdate(SingleLightSensor __instance)
__instance.OnDetectDarkness.Invoke();
new PlayerSetIlluminatedMessage(QSBPlayerManager.LocalPlayerId, false).Send();
}
- if (__instance._illuminatingDreamLanternList != null &&
- !__instance._illuminatingDreamLanternList.SequenceEqual(_illuminatingDreamLanternList))
- {
- new PlayerIlluminatingLanternsMessage(QSBPlayerManager.LocalPlayerId, __instance._illuminatingDreamLanternList).Send();
- }
return false;
}
@@ -247,7 +247,7 @@ private static bool UpdateIllumination(SingleLightSensor __instance)
}
case LightSourceType.PROBE:
{
- if (lightSource is QSBProbe qsbProbe)
+ if (lightSource is QSBSurveyorProbe qsbProbe)
{
var probe = qsbProbe;
if (probe != null &&
@@ -350,7 +350,7 @@ private static void UpdateLocalIllumination(SingleLightSensor __instance)
}
case LightSourceType.PROBE:
{
- if (lightSource is not QSBProbe)
+ if (lightSource is not QSBSurveyorProbe)
{
var probe = Locator.GetProbe();
if (probe != null &&
diff --git a/QSB/EchoesOfTheEye/RaftSync/TransformSync/RaftTransformSync.cs b/QSB/EchoesOfTheEye/RaftSync/TransformSync/RaftTransformSync.cs
index dc5307d06..0e436c4ab 100644
--- a/QSB/EchoesOfTheEye/RaftSync/TransformSync/RaftTransformSync.cs
+++ b/QSB/EchoesOfTheEye/RaftSync/TransformSync/RaftTransformSync.cs
@@ -10,7 +10,8 @@ namespace QSB.EchoesOfTheEye.RaftSync.TransformSync;
public class RaftTransformSync : UnsectoredRigidbodySync, ILinkedNetworkBehaviour
{
- protected override bool UseInterpolation => false;
+ private bool IsRidingRaft => Locator.GetPlayerController() && Locator.GetPlayerController().GetGroundBody() == AttachedRigidbody;
+ protected override bool UseInterpolation => !IsRidingRaft;
private float _lastSetPositionTime;
private const float ForcePositionAfterTime = 1;
@@ -67,6 +68,10 @@ protected override void Uninit()
private void OnUnsuspend(OWRigidbody suspendedBody) => netIdentity.UpdateAuthQueue(AuthQueueAction.Add);
private void OnSuspend(OWRigidbody suspendedBody) => netIdentity.UpdateAuthQueue(AuthQueueAction.Remove);
+
+
+ public override void OnStartAuthority() => DebugLog.DebugWrite($"{this} + AUTH");
+ public override void OnStopAuthority() => DebugLog.DebugWrite($"{this} - AUTH");
///
/// replacement for base method
@@ -74,11 +79,10 @@ protected override void Uninit()
///
protected override void ApplyToAttached()
{
- var targetPos = ReferenceTransform.FromRelPos(transform.position);
- var targetRot = ReferenceTransform.FromRelRot(transform.rotation);
+ var targetPos = ReferenceTransform.FromRelPos(UseInterpolation ? SmoothPosition : transform.position);
+ var targetRot = ReferenceTransform.FromRelRot(UseInterpolation ? SmoothRotation : transform.rotation);
- var onRaft = Locator.GetPlayerController().GetGroundBody() == AttachedRigidbody;
- if (onRaft)
+ if (IsRidingRaft)
{
if (Time.unscaledTime >= _lastSetPositionTime + ForcePositionAfterTime)
{
diff --git a/QSB/EchoesOfTheEye/RaftSync/WorldObjects/QSBRaftDock.cs b/QSB/EchoesOfTheEye/RaftSync/WorldObjects/QSBRaftDock.cs
index ef1dc465b..7eccf6e36 100644
--- a/QSB/EchoesOfTheEye/RaftSync/WorldObjects/QSBRaftDock.cs
+++ b/QSB/EchoesOfTheEye/RaftSync/WorldObjects/QSBRaftDock.cs
@@ -1,5 +1,4 @@
using QSB.ItemSync.WorldObjects;
-using QSB.Patches;
using QSB.WorldSync;
namespace QSB.EchoesOfTheEye.RaftSync.WorldObjects;
@@ -8,6 +7,5 @@ public class QSBRaftDock : WorldObject, IQSBDropTarget
{
IItemDropTarget IQSBDropTarget.AttachedObject => AttachedObject;
- public void OnPressInteract() =>
- QSBPatch.RemoteCall(AttachedObject.OnPressInteract);
+ public void OnPressInteract() => AttachedObject.OnPressInteract();
}
diff --git a/QSB/EchoesOfTheEye/Sarcophagus/Messages/OpenMessage.cs b/QSB/EchoesOfTheEye/Sarcophagus/Messages/OpenMessage.cs
index 1a13c105c..7964383c9 100644
--- a/QSB/EchoesOfTheEye/Sarcophagus/Messages/OpenMessage.cs
+++ b/QSB/EchoesOfTheEye/Sarcophagus/Messages/OpenMessage.cs
@@ -1,11 +1,9 @@
using QSB.EchoesOfTheEye.Sarcophagus.WorldObjects;
using QSB.Messaging;
-using QSB.Patches;
namespace QSB.EchoesOfTheEye.Sarcophagus.Messages;
public class OpenMessage : QSBWorldObjectMessage
{
- public override void OnReceiveRemote() =>
- QSBPatch.RemoteCall(WorldObject.AttachedObject.OnPressInteract);
+ public override void OnReceiveRemote() => WorldObject.AttachedObject.OnPressInteract();
}
diff --git a/QSB/EchoesOfTheEye/SlideProjectors/Messages/NextSlideMessage.cs b/QSB/EchoesOfTheEye/SlideProjectors/Messages/NextSlideMessage.cs
index 211b214d4..4da96ec11 100644
--- a/QSB/EchoesOfTheEye/SlideProjectors/Messages/NextSlideMessage.cs
+++ b/QSB/EchoesOfTheEye/SlideProjectors/Messages/NextSlideMessage.cs
@@ -5,5 +5,5 @@ namespace QSB.EchoesOfTheEye.SlideProjectors.Messages;
internal class NextSlideMessage : QSBWorldObjectMessage
{
- public override void OnReceiveRemote() => WorldObject.NextSlide();
-}
\ No newline at end of file
+ public override void OnReceiveRemote() => WorldObject.AttachedObject.NextSlide();
+}
diff --git a/QSB/EchoesOfTheEye/SlideProjectors/Messages/PreviousSlideMessage.cs b/QSB/EchoesOfTheEye/SlideProjectors/Messages/PreviousSlideMessage.cs
index 92b3ffb62..e581363c5 100644
--- a/QSB/EchoesOfTheEye/SlideProjectors/Messages/PreviousSlideMessage.cs
+++ b/QSB/EchoesOfTheEye/SlideProjectors/Messages/PreviousSlideMessage.cs
@@ -5,5 +5,5 @@ namespace QSB.EchoesOfTheEye.SlideProjectors.Messages;
internal class PreviousSlideMessage : QSBWorldObjectMessage
{
- public override void OnReceiveRemote() => WorldObject.PreviousSlide();
-}
\ No newline at end of file
+ public override void OnReceiveRemote() => WorldObject.AttachedObject.PreviousSlide();
+}
diff --git a/QSB/EchoesOfTheEye/SlideProjectors/Patches/SlideProjectorPatches.cs b/QSB/EchoesOfTheEye/SlideProjectors/Patches/SlideProjectorPatches.cs
index a52c755b7..8c3e6def6 100644
--- a/QSB/EchoesOfTheEye/SlideProjectors/Patches/SlideProjectorPatches.cs
+++ b/QSB/EchoesOfTheEye/SlideProjectors/Patches/SlideProjectorPatches.cs
@@ -12,27 +12,55 @@ internal class SlideProjectorPatches : QSBPatch
{
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
- [HarmonyPostfix]
+ [HarmonyPrefix]
[HarmonyPatch(nameof(SlideProjector.OnPressInteract))]
- public static void OnPressInteract(SlideProjector __instance) =>
- __instance.GetWorldObject()
- .SendMessage(new UseSlideProjectorMessage(true));
+ public static void OnPressInteract(SlideProjector __instance)
+ {
+ if (!QSBWorldSync.AllObjectsReady)
+ {
+ return;
+ }
+ __instance.GetWorldObject().SendMessage(new UseSlideProjectorMessage(true));
+ }
- [HarmonyPostfix]
+ [HarmonyPrefix]
[HarmonyPatch(nameof(SlideProjector.CancelInteraction))]
- public static void CancelInteraction(SlideProjector __instance) =>
- __instance.GetWorldObject()
- .SendMessage(new UseSlideProjectorMessage(false));
+ public static void CancelInteraction(SlideProjector __instance)
+ {
+ if (!QSBWorldSync.AllObjectsReady)
+ {
+ return;
+ }
+ __instance.GetWorldObject().SendMessage(new UseSlideProjectorMessage(false));
+ }
- [HarmonyPostfix]
+ [HarmonyPrefix]
[HarmonyPatch(nameof(SlideProjector.NextSlide))]
- public static void NextSlide(SlideProjector __instance) =>
- __instance.GetWorldObject()
- .SendMessage(new NextSlideMessage());
+ public static void NextSlide(SlideProjector __instance)
+ {
+ if (Remote)
+ {
+ return;
+ }
+ if (!QSBWorldSync.AllObjectsReady)
+ {
+ return;
+ }
+ __instance.GetWorldObject().SendMessage(new NextSlideMessage());
+ }
- [HarmonyPostfix]
+ [HarmonyPrefix]
[HarmonyPatch(nameof(SlideProjector.PreviousSlide))]
- public static void PreviousSlide(SlideProjector __instance) =>
- __instance.GetWorldObject()
- .SendMessage(new PreviousSlideMessage());
+ public static void PreviousSlide(SlideProjector __instance)
+ {
+ if (Remote)
+ {
+ return;
+ }
+ if (!QSBWorldSync.AllObjectsReady)
+ {
+ return;
+ }
+ __instance.GetWorldObject().SendMessage(new PreviousSlideMessage());
+ }
}
diff --git a/QSB/EchoesOfTheEye/SlideProjectors/WorldObjects/QSBSlideProjector.cs b/QSB/EchoesOfTheEye/SlideProjectors/WorldObjects/QSBSlideProjector.cs
index 292e0bfe9..a0b34ae2e 100644
--- a/QSB/EchoesOfTheEye/SlideProjectors/WorldObjects/QSBSlideProjector.cs
+++ b/QSB/EchoesOfTheEye/SlideProjectors/WorldObjects/QSBSlideProjector.cs
@@ -2,7 +2,6 @@
using QSB.EchoesOfTheEye.SlideProjectors.Messages;
using QSB.Messaging;
using QSB.Player;
-using QSB.Utility;
using QSB.WorldSync;
using System.Threading;
@@ -18,8 +17,17 @@ public override async UniTask Init(CancellationToken ct) =>
public override void OnRemoval() =>
QSBPlayerManager.OnRemovePlayer -= OnPlayerLeave;
- private void OnPlayerLeave(PlayerInfo obj) =>
- this.SendMessage(new UseSlideProjectorMessage(false));
+ private void OnPlayerLeave(PlayerInfo player)
+ {
+ if (!QSBCore.IsHost)
+ {
+ return;
+ }
+ if (_user == player.PlayerId)
+ {
+ this.SendMessage(new UseSlideProjectorMessage(false));
+ }
+ }
public override void SendInitialState(uint to) =>
this.SendMessage(new UseSlideProjectorMessage(_user) { To = to });
@@ -31,61 +39,17 @@ public void SetUser(uint user)
{
AttachedObject._interactReceiver.SetInteractionEnabled(user == 0 || user == _user);
_user = user;
- }
- public void NextSlide()
- {
- var hasChangedSlide = false;
- if (AttachedObject._slideItem != null && AttachedObject._slideItem.slidesContainer.NextSlideAvailable())
+ if (user != 0)
{
- hasChangedSlide = AttachedObject._slideItem.slidesContainer.IncreaseSlideIndex();
- if (hasChangedSlide)
+ if (AttachedObject._slideItem != null && AttachedObject.IsProjectorFullyLit())
{
- if (AttachedObject._oneShotSource != null)
- {
- AttachedObject._oneShotSource.PlayOneShot(AudioType.Projector_Next);
- }
-
- if (AttachedObject.IsProjectorFullyLit())
- {
- AttachedObject._slideItem.slidesContainer.SetCurrentRead();
- AttachedObject._slideItem.slidesContainer.TryPlayMusicForCurrentSlideTransition(true);
- }
+ AttachedObject._slideItem.slidesContainer.TryPlayMusicForCurrentSlideInclusive();
}
}
-
- if (AttachedObject._gearInterface != null)
- {
- var audioVolume = hasChangedSlide ? 0f : 0.5f;
- AttachedObject._gearInterface.AddRotation(45f, audioVolume);
- }
- }
-
- public void PreviousSlide()
- {
- var hasChangedSlide = false;
- if (AttachedObject._slideItem != null && AttachedObject._slideItem.slidesContainer.PrevSlideAvailable())
- {
- hasChangedSlide = AttachedObject._slideItem.slidesContainer.DecreaseSlideIndex();
- if (hasChangedSlide)
- {
- if (AttachedObject._oneShotSource != null)
- {
- AttachedObject._oneShotSource.PlayOneShot(AudioType.Projector_Prev);
- }
-
- if (AttachedObject.IsProjectorFullyLit())
- {
- AttachedObject._slideItem.slidesContainer.SetCurrentRead();
- AttachedObject._slideItem.slidesContainer.TryPlayMusicForCurrentSlideTransition(false);
- }
- }
- }
-
- if (AttachedObject._gearInterface != null)
+ else
{
- var audioVolume = hasChangedSlide ? 0f : 0.5f;
- AttachedObject._gearInterface.AddRotation(-45f, audioVolume);
+ Locator.GetSlideReelMusicManager().OnExitSlideProjector();
}
}
}
diff --git a/QSB/EchoesOfTheEye/WineCellar/Messages/WineCellarSwitchMessage.cs b/QSB/EchoesOfTheEye/WineCellar/Messages/WineCellarSwitchMessage.cs
new file mode 100644
index 000000000..88c82005d
--- /dev/null
+++ b/QSB/EchoesOfTheEye/WineCellar/Messages/WineCellarSwitchMessage.cs
@@ -0,0 +1,9 @@
+using QSB.EchoesOfTheEye.WineCellar.WorldObjects;
+using QSB.Messaging;
+
+namespace QSB.EchoesOfTheEye.WineCellar.Messages;
+
+internal class WineCellarSwitchMessage : QSBWorldObjectMessage
+{
+ public override void OnReceiveRemote() => WorldObject.AttachedObject.OnPressInteract();
+}
diff --git a/QSB/EchoesOfTheEye/WineCellar/Patches/WineCellarPatches.cs b/QSB/EchoesOfTheEye/WineCellar/Patches/WineCellarPatches.cs
new file mode 100644
index 000000000..40a4100c0
--- /dev/null
+++ b/QSB/EchoesOfTheEye/WineCellar/Patches/WineCellarPatches.cs
@@ -0,0 +1,26 @@
+using HarmonyLib;
+using QSB.EchoesOfTheEye.WineCellar.Messages;
+using QSB.EchoesOfTheEye.WineCellar.WorldObjects;
+using QSB.Messaging;
+using QSB.Patches;
+using QSB.WorldSync;
+
+namespace QSB.EchoesOfTheEye.WineCellar.Patches;
+
+internal class WineCellarPatches : QSBPatch
+{
+ public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
+
+ [HarmonyPostfix]
+ [HarmonyPatch(typeof(WineCellarSwitch), nameof(WineCellarSwitch.OnPressInteract))]
+ public static void OnPressInteract(WineCellarSwitch __instance)
+ {
+ if (Remote)
+ {
+ return;
+ }
+
+ var worldObject = __instance.GetWorldObject();
+ worldObject.SendMessage(new WineCellarSwitchMessage());
+ }
+}
diff --git a/QSB/EchoesOfTheEye/WineCellar/WineCellarManager.cs b/QSB/EchoesOfTheEye/WineCellar/WineCellarManager.cs
new file mode 100644
index 000000000..e9b523578
--- /dev/null
+++ b/QSB/EchoesOfTheEye/WineCellar/WineCellarManager.cs
@@ -0,0 +1,15 @@
+using Cysharp.Threading.Tasks;
+using QSB.EchoesOfTheEye.WineCellar.WorldObjects;
+using QSB.WorldSync;
+using System.Threading;
+
+namespace QSB.EchoesOfTheEye.WineCellar;
+
+internal class WineCellarManager : WorldObjectManager
+{
+ public override WorldObjectScene WorldObjectScene => WorldObjectScene.SolarSystem;
+ public override bool DlcOnly => true;
+
+ public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct)
+ => QSBWorldSync.Init();
+}
diff --git a/QSB/EchoesOfTheEye/WineCellar/WorldObjects/QSBWineCellarSwitch.cs b/QSB/EchoesOfTheEye/WineCellar/WorldObjects/QSBWineCellarSwitch.cs
new file mode 100644
index 000000000..58219a004
--- /dev/null
+++ b/QSB/EchoesOfTheEye/WineCellar/WorldObjects/QSBWineCellarSwitch.cs
@@ -0,0 +1,16 @@
+using QSB.EchoesOfTheEye.WineCellar.Messages;
+using QSB.Messaging;
+using QSB.WorldSync;
+
+namespace QSB.EchoesOfTheEye.WineCellar.WorldObjects;
+
+internal class QSBWineCellarSwitch : WorldObject
+{
+ public override void SendInitialState(uint to)
+ {
+ if (AttachedObject.enabled)
+ {
+ this.SendMessage(new WineCellarSwitchMessage { To = to });
+ }
+ }
+}
diff --git a/QSB/ElevatorSync/WorldObjects/QSBElevator.cs b/QSB/ElevatorSync/WorldObjects/QSBElevator.cs
index 65b891e5f..d1413d923 100644
--- a/QSB/ElevatorSync/WorldObjects/QSBElevator.cs
+++ b/QSB/ElevatorSync/WorldObjects/QSBElevator.cs
@@ -43,7 +43,7 @@ public void RemoteCall(bool isGoingUp)
}
}
- QSBPatch.RemoteCall(AttachedObject.StartLift);
+ AttachedObject.StartLift();
}
private void SetDirection(bool isGoingUp)
diff --git a/QSB/GetAttachedOWRigidbodyPatch.cs b/QSB/GetAttachedOWRigidbodyPatch.cs
new file mode 100644
index 000000000..7ee82ab1e
--- /dev/null
+++ b/QSB/GetAttachedOWRigidbodyPatch.cs
@@ -0,0 +1,43 @@
+using HarmonyLib;
+using QSB.Patches;
+using UnityEngine;
+
+namespace QSB;
+
+///
+/// TODO: TEST THIS. see if things horribly break. this could be huge.
+///
+[HarmonyPatch(typeof(OWExtensions))]
+public class GetAttachedOWRigidbodyPatch : QSBPatch
+{
+ public override QSBPatchTypes Type => QSBPatchTypes.OnModStart;
+
+ [HarmonyPrefix]
+ [HarmonyPatch(nameof(OWExtensions.GetAttachedOWRigidbody), typeof(GameObject), typeof(bool))]
+ private static bool GetAttachedOWRigidbody(GameObject obj, bool ignoreThisTransform, out OWRigidbody __result)
+ {
+ OWRigidbody owrigidbody = null;
+ var transform = obj.transform;
+ if (ignoreThisTransform)
+ {
+ transform = obj.transform.parent;
+ }
+ while (owrigidbody == null)
+ {
+ owrigidbody = transform.GetComponent();
+ /*
+ if (owrigidbody != null && !owrigidbody.gameObject.activeInHierarchy)
+ {
+ owrigidbody = null;
+ }
+ */
+ if ((transform == obj.transform.root && owrigidbody == null) || owrigidbody != null)
+ {
+ break;
+ }
+ transform = transform.parent;
+ }
+ __result = owrigidbody;
+ return false;
+ }
+}
diff --git a/QSB/ItemSync/Messages/SocketItemMessage.cs b/QSB/ItemSync/Messages/SocketItemMessage.cs
index 3b824ee9b..57a9243ba 100644
--- a/QSB/ItemSync/Messages/SocketItemMessage.cs
+++ b/QSB/ItemSync/Messages/SocketItemMessage.cs
@@ -7,16 +7,13 @@
namespace QSB.ItemSync.Messages;
-internal class SocketItemMessage : QSBMessage<(SocketMessageType Type, int SocketId, int ItemId)>
+internal class SocketItemMessage : QSBWorldObjectMessage
{
- public SocketItemMessage(SocketMessageType type, OWItemSocket socket, OWItem item) : base((
+ public SocketItemMessage(SocketMessageType type, OWItemSocket socket) : base((
type,
- socket ? socket.GetWorldObject().ObjectId : -1,
- item ? item.GetWorldObject().ObjectId : -1
+ socket ? socket.GetWorldObject().ObjectId : -1
)) { }
- public override bool ShouldReceive => QSBWorldSync.AllObjectsReady;
-
public override void OnReceiveRemote()
{
switch (Data.Type)
@@ -24,12 +21,11 @@ public override void OnReceiveRemote()
case SocketMessageType.Socket:
{
var qsbItemSocket = Data.SocketId.GetWorldObject();
- var qsbItem = Data.ItemId.GetWorldObject();
- qsbItemSocket.PlaceIntoSocket(qsbItem);
- qsbItem.ItemState.HasBeenInteractedWith = true;
- qsbItem.ItemState.State = ItemStateType.Socketed;
- qsbItem.ItemState.Socket = qsbItemSocket.AttachedObject;
+ qsbItemSocket.PlaceIntoSocket(WorldObject);
+ WorldObject.ItemState.HasBeenInteractedWith = true;
+ WorldObject.ItemState.State = ItemStateType.Socketed;
+ WorldObject.ItemState.Socket = qsbItemSocket.AttachedObject;
var player = QSBPlayerManager.GetPlayer(From);
player.HeldItem = null;
@@ -39,7 +35,6 @@ public override void OnReceiveRemote()
case SocketMessageType.StartUnsocket:
{
var qsbItemSocket = Data.SocketId.GetWorldObject();
- var qsbItem = Data.ItemId.GetWorldObject();
if (!qsbItemSocket.IsSocketOccupied())
{
@@ -47,19 +42,17 @@ public override void OnReceiveRemote()
return;
}
- qsbItem.StoreLocation();
+ WorldObject.StoreLocation();
var player = QSBPlayerManager.GetPlayer(From);
- player.HeldItem = qsbItem;
+ player.HeldItem = WorldObject;
qsbItemSocket.RemoveFromSocket();
return;
}
case SocketMessageType.CompleteUnsocket:
{
- var qsbItem = Data.ItemId.GetWorldObject();
-
- qsbItem.OnCompleteUnsocket();
+ WorldObject.OnCompleteUnsocket();
return;
}
}
diff --git a/QSB/ItemSync/Patches/ItemToolPatches.cs b/QSB/ItemSync/Patches/ItemToolPatches.cs
index 56acc639d..e8730cd08 100644
--- a/QSB/ItemSync/Patches/ItemToolPatches.cs
+++ b/QSB/ItemSync/Patches/ItemToolPatches.cs
@@ -35,7 +35,7 @@ public static void SocketItem(ItemTool __instance, OWItemSocket socket)
var qsbItem = item.GetWorldObject();
qsbItem.ItemState.State = ItemStateType.Socketed;
qsbItem.ItemState.Socket = socket;
- new SocketItemMessage(SocketMessageType.Socket, socket, item).Send();
+ qsbItem.SendMessage(new SocketItemMessage(SocketMessageType.Socket, socket));
}
[HarmonyPrefix]
@@ -46,7 +46,7 @@ public static void StartUnsocketItem(OWItemSocket socket)
var qsbItem = item.GetWorldObject();
qsbItem.ItemState.HasBeenInteractedWith = true;
QSBPlayerManager.LocalPlayer.HeldItem = qsbItem;
- new SocketItemMessage(SocketMessageType.StartUnsocket, socket, item).Send();
+ qsbItem.SendMessage(new SocketItemMessage(SocketMessageType.StartUnsocket, socket));
}
[HarmonyPrefix]
@@ -54,7 +54,8 @@ public static void StartUnsocketItem(OWItemSocket socket)
public static void CompleteUnsocketItem(ItemTool __instance)
{
var item = __instance._heldItem;
- new SocketItemMessage(SocketMessageType.CompleteUnsocket, null, item).Send();
+ var qsbItem = item.GetWorldObject();
+ qsbItem.SendMessage(new SocketItemMessage(SocketMessageType.CompleteUnsocket, null));
}
[HarmonyPrefix]
diff --git a/QSB/ItemSync/WorldObjects/Items/QSBDreamLanternItem.cs b/QSB/ItemSync/WorldObjects/Items/QSBDreamLanternItem.cs
index 5e7a263cf..f3a33e816 100644
--- a/QSB/ItemSync/WorldObjects/Items/QSBDreamLanternItem.cs
+++ b/QSB/ItemSync/WorldObjects/Items/QSBDreamLanternItem.cs
@@ -1,3 +1,67 @@
-namespace QSB.ItemSync.WorldObjects.Items;
+using Cysharp.Threading.Tasks;
+using System.Linq;
+using System.Threading;
+using UnityEngine;
-public class QSBDreamLanternItem : QSBItem { }
+namespace QSB.ItemSync.WorldObjects.Items;
+
+public class QSBDreamLanternItem : QSBItem
+{
+ private Material[] _materials;
+
+ public override async UniTask Init(CancellationToken ct)
+ {
+ await base.Init(ct);
+
+ // Some lanterns (ie, nonfunctioning) don't have a view model group
+ if (AttachedObject._lanternType != DreamLanternType.Nonfunctioning)
+ {
+ _materials = AttachedObject._lanternController._viewModelGroup?.GetComponentsInChildren(true)?.SelectMany(x => x.materials)?.ToArray();
+ }
+ }
+
+ public override void PickUpItem(Transform holdTransform)
+ {
+ base.PickUpItem(holdTransform);
+
+ // Fixes #502: Artifact is visible through the walls
+ if (AttachedObject._lanternType != DreamLanternType.Nonfunctioning)
+ {
+ foreach (var m in _materials)
+ {
+ if (m.renderQueue >= 2000)
+ {
+ m.renderQueue -= 2000;
+ }
+ }
+
+ // The view model looks much smaller than the dropped item
+ AttachedObject.gameObject.transform.localScale = Vector3.one * 2f;
+ }
+
+ AttachedObject.EnableInteraction(true);
+ }
+
+ public override void DropItem(Vector3 worldPosition, Vector3 worldNormal, Transform parent, Sector sector, IItemDropTarget customDropTarget)
+ {
+ base.DropItem(worldPosition, worldNormal, parent, sector, customDropTarget);
+
+ if (AttachedObject._lanternType != DreamLanternType.Nonfunctioning)
+ {
+ foreach (var m in _materials)
+ {
+ if (m.renderQueue < 2000)
+ {
+ m.renderQueue += 2000;
+ }
+ }
+
+ AttachedObject.gameObject.transform.localScale = Vector3.one;
+ }
+
+ // If in the DreamWorld, don't let other people pick up your lantern
+ // Since this method is only called on the remote, this only makes other players unable to pick it up
+ // If the lantern is lit, the user is in the DreamWorld
+ AttachedObject.EnableInteraction(!AttachedObject.GetLanternController().IsLit());
+ }
+}
diff --git a/QSB/ItemSync/WorldObjects/Items/QSBItem.cs b/QSB/ItemSync/WorldObjects/Items/QSBItem.cs
index 45503a031..bfb676953 100644
--- a/QSB/ItemSync/WorldObjects/Items/QSBItem.cs
+++ b/QSB/ItemSync/WorldObjects/Items/QSBItem.cs
@@ -2,7 +2,6 @@
using QSB.ItemSync.Messages;
using QSB.ItemSync.WorldObjects.Sockets;
using QSB.Messaging;
-using QSB.Patches;
using QSB.Player;
using QSB.SectorSync.WorldObjects;
using QSB.WorldSync;
@@ -71,7 +70,7 @@ private void OnPlayerLeave(PlayerInfo player)
if (_lastSocket != null)
{
- QSBPatch.RemoteCall(() => _lastSocket.PlaceIntoSocket(this));
+ _lastSocket.PlaceIntoSocket(this);
}
else
{
@@ -95,10 +94,10 @@ public override void SendInitialState(uint to)
switch (ItemState.State)
{
case ItemStateType.Held:
- ((IQSBItem)this).SendMessage(new MoveToCarryMessage(ItemState.HoldingPlayer.PlayerId));
+ ((IQSBItem)this).SendMessage(new MoveToCarryMessage(ItemState.HoldingPlayer.PlayerId) { To = to });
break;
case ItemStateType.Socketed:
- new SocketItemMessage(SocketMessageType.Socket, ItemState.Socket, AttachedObject).Send();
+ ((IQSBItem)this).SendMessage(new SocketItemMessage(SocketMessageType.Socket, ItemState.Socket) { To = to });
break;
case ItemStateType.OnGround:
((IQSBItem)this).SendMessage(
@@ -108,18 +107,18 @@ public override void SendInitialState(uint to)
ItemState.Parent,
ItemState.Sector,
ItemState.CustomDropTarget,
- ItemState.Rigidbody));
+ ItemState.Rigidbody) { To = to });
break;
}
}
public ItemType GetItemType() => AttachedObject.GetItemType();
- public void PickUpItem(Transform holdTransform) =>
- QSBPatch.RemoteCall(() => AttachedObject.PickUpItem(holdTransform));
+ public virtual void PickUpItem(Transform holdTransform) =>
+ AttachedObject.PickUpItem(holdTransform);
- public void DropItem(Vector3 worldPosition, Vector3 worldNormal, Transform parent, Sector sector, IItemDropTarget customDropTarget) =>
- QSBPatch.RemoteCall(() => AttachedObject.DropItem(worldPosition, worldNormal, parent, sector, customDropTarget));
+ public virtual void DropItem(Vector3 worldPosition, Vector3 worldNormal, Transform parent, Sector sector, IItemDropTarget customDropTarget) =>
+ AttachedObject.DropItem(worldPosition, worldNormal, parent, sector, customDropTarget);
public void OnCompleteUnsocket() => AttachedObject.OnCompleteUnsocket();
}
diff --git a/QSB/ItemSync/WorldObjects/Sockets/QSBItemSocket.cs b/QSB/ItemSync/WorldObjects/Sockets/QSBItemSocket.cs
index 5aff8bc81..a475e522b 100644
--- a/QSB/ItemSync/WorldObjects/Sockets/QSBItemSocket.cs
+++ b/QSB/ItemSync/WorldObjects/Sockets/QSBItemSocket.cs
@@ -1,21 +1,25 @@
using QSB.ItemSync.WorldObjects.Items;
-using QSB.Patches;
using QSB.WorldSync;
namespace QSB.ItemSync.WorldObjects.Sockets;
internal class QSBItemSocket : WorldObject
{
- public override void SendInitialState(uint to)
- {
- // todo SendInitialState
- }
-
public bool IsSocketOccupied() => AttachedObject.IsSocketOccupied();
public void PlaceIntoSocket(IQSBItem item)
- => QSBPatch.RemoteCall(() => AttachedObject.PlaceIntoSocket((OWItem)item.AttachedObject));
+ {
+ AttachedObject.PlaceIntoSocket((OWItem)item.AttachedObject);
+
+ // Don't let other users unsocket a DreamLantern in the dreamworld that doesn't belong to them
+ // DreamLanternSockets only exist in the DreamWorld
+ AttachedObject.EnableInteraction(AttachedObject is not DreamLanternSocket);
+ }
public void RemoveFromSocket()
- => QSBPatch.RemoteCall(AttachedObject.RemoveFromSocket);
-}
\ No newline at end of file
+ {
+ AttachedObject.RemoveFromSocket();
+
+ AttachedObject.EnableInteraction(true);
+ }
+}
diff --git a/QSB/Menus/MenuManager.cs b/QSB/Menus/MenuManager.cs
index d98290ca6..007e59108 100644
--- a/QSB/Menus/MenuManager.cs
+++ b/QSB/Menus/MenuManager.cs
@@ -42,7 +42,7 @@ internal class MenuManager : MonoBehaviour, IAddComponentOnStart
private const int _titleButtonIndex = 2;
private float _connectPopupOpenTime;
- private const string UpdateChangelog = "QSB Version 0.21.1\r\nFixed gamepass not working, and fixed a small bug with light sensors.";
+ private const string UpdateChangelog = "QSB Version 0.22.0\r\nFixed lots of bugs, and added lots of SFX and VFX stuff.";
private Action PopupClose;
@@ -361,7 +361,16 @@ private void CreateCommonPopups()
ConnectPopup.EnableMenu(false);
Connect();
};
- ConnectPopup.OnActivateMenu += () => _connectPopupOpenTime = Time.time;
+
+ ConnectPopup.OnActivateMenu += () =>
+ {
+ _connectPopupOpenTime = Time.time;
+ if (QSBCore.Helper.Interaction.ModExists("Raicuparta.NomaiVR"))
+ {
+ // ClearInputTextField is called AFTER OnActivateMenu
+ Delay.RunNextFrame(() => ConnectPopup._inputField.SetTextWithoutNotify(GUIUtility.systemCopyBuffer));
+ }
+ };
OneButtonInfoPopup = QSBCore.MenuApi.MakeInfoPopup("", "");
OneButtonInfoPopup.OnPopupConfirm += () => OnCloseInfoPopup(true);
@@ -480,6 +489,11 @@ private void InitPauseMenus()
langController.AddTextElement(DisconnectPopup._labelText, false);
langController.AddTextElement(DisconnectPopup._confirmButton._buttonText, false);
langController.AddTextElement(DisconnectPopup._cancelButton._buttonText, false);
+ langController.AddTextElement(OneButtonInfoPopup._labelText, false);
+ langController.AddTextElement(OneButtonInfoPopup._confirmButton._buttonText, false);
+ langController.AddTextElement(TwoButtonInfoPopup._labelText, false);
+ langController.AddTextElement(TwoButtonInfoPopup._confirmButton._buttonText, false);
+ langController.AddTextElement(TwoButtonInfoPopup._cancelButton._buttonText, false);
}
private void MakeTitleMenus()
@@ -498,25 +512,6 @@ private void MakeTitleMenus()
Delay.RunWhen(PlayerData.IsLoaded, () => SetButtonActive(ResumeGameButton, PlayerData.LoadLoopCount() > 1));
SetButtonActive(NewGameButton, true);
- if (QSBCore.DebugSettings.SkipTitleScreen)
- {
- Application.runInBackground = true;
- var titleScreenManager = FindObjectOfType();
- var titleScreenAnimation = titleScreenManager._cameraController;
- const float small = 1 / 1000f;
- titleScreenAnimation._gamepadSplash = false;
- titleScreenAnimation._introPan = false;
- titleScreenAnimation._fadeDuration = small;
- titleScreenAnimation.Start();
- var titleAnimationController = titleScreenManager._gfxController;
- titleAnimationController._logoFadeDelay = small;
- titleAnimationController._logoFadeDuration = small;
- titleAnimationController._echoesFadeDelay = small;
- titleAnimationController._optionsFadeDelay = small;
- titleAnimationController._optionsFadeDuration = small;
- titleAnimationController._optionsFadeSpacing = small;
- }
-
var mainMenuFontController = GameObject.Find("MainMenu").GetComponent();
mainMenuFontController.AddTextElement(HostButton.transform.GetChild(0).GetChild(1).GetComponent());
mainMenuFontController.AddTextElement(ConnectButton.transform.GetChild(0).GetChild(1).GetComponent());
@@ -566,8 +561,8 @@ private void Disconnect()
private void PreHost()
{
- bool doesSingleplayerSaveExist = false;
- bool doesMultiplayerSaveExist = false;
+ var doesSingleplayerSaveExist = false;
+ var doesMultiplayerSaveExist = false;
if (!QSBCore.IsStandalone)
{
var manager = QSBMSStoreProfileManager.SharedInstance;
@@ -681,7 +676,7 @@ private void Connect()
PlayerData.Init(manager.currentProfileMultiplayerGameSave, manager.currentProfileGameSettings, manager.currentProfileGraphicsSettings, manager.currentProfileInputJSON);
}
- var address = ConnectPopup.GetInputText();
+ var address = ConnectPopup.GetInputText().Trim();
if (address == string.Empty)
{
address = QSBCore.DefaultServerIP;
@@ -695,7 +690,7 @@ private void Connect()
Locator.GetMenuInputModule().DisableInputs();
QSBNetworkManager.singleton.networkAddress = address;
- // hack to get disconnect call if start client fails immediately
+ // hack to get disconnect call if start client fails immediately (happens on kcp transport when failing to resolve host name)
typeof(NetworkClient).GetProperty(nameof(NetworkClient.connection))!.SetValue(null, new NetworkConnectionToServer());
QSBNetworkManager.singleton.StartClient();
}
diff --git a/QSB/Messaging/OWEvents.cs b/QSB/Messaging/OWEvents.cs
index 5820a4f5d..865b0b120 100644
--- a/QSB/Messaging/OWEvents.cs
+++ b/QSB/Messaging/OWEvents.cs
@@ -33,4 +33,8 @@ public static class OWEvents
public const string ExitDreamWorld = nameof(ExitDreamWorld);
public const string EnterRemoteFlightConsole = nameof(EnterRemoteFlightConsole);
public const string ExitRemoteFlightConsole = nameof(ExitRemoteFlightConsole);
+ public const string ProbeSnapshotRemoved = "Probe Snapshot Removed"; // pain
+ public const string StartShipIgnition = nameof(StartShipIgnition);
+ public const string CompleteShipIgnition = nameof(CompleteShipIgnition);
+ public const string CancelShipIgnition = nameof(CancelShipIgnition);
}
\ No newline at end of file
diff --git a/QSB/Messaging/QSBMessageManager.cs b/QSB/Messaging/QSBMessageManager.cs
index b67976c10..2a96c406d 100644
--- a/QSB/Messaging/QSBMessageManager.cs
+++ b/QSB/Messaging/QSBMessageManager.cs
@@ -2,6 +2,7 @@
using OWML.Common;
using QSB.ClientServerStateSync;
using QSB.ClientServerStateSync.Messages;
+using QSB.Patches;
using QSB.Player;
using QSB.Player.Messages;
using QSB.Player.TransformSync;
@@ -86,7 +87,9 @@ private static void OnClientReceive(QSBMessage msg)
if (msg.From != QSBPlayerManager.LocalPlayerId)
{
+ QSBPatch.Remote = true;
msg.OnReceiveRemote();
+ QSBPatch.Remote = false;
}
else
{
diff --git a/QSB/ModelShip/Messages/CrashModelShipMessage.cs b/QSB/ModelShip/Messages/CrashModelShipMessage.cs
new file mode 100644
index 000000000..4eda49410
--- /dev/null
+++ b/QSB/ModelShip/Messages/CrashModelShipMessage.cs
@@ -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();
+ crashBehaviour._crashEffect.Play();
+ crashBehaviour.gameObject.GetComponent().PlayOneShot(AudioType.TH_ModelShipCrash);
+ }
+}
diff --git a/QSB/ModelShip/Messages/RespawnModelShipMessage.cs b/QSB/ModelShip/Messages/RespawnModelShipMessage.cs
index 241e40a71..d3d34fbff 100644
--- a/QSB/ModelShip/Messages/RespawnModelShipMessage.cs
+++ b/QSB/ModelShip/Messages/RespawnModelShipMessage.cs
@@ -1,5 +1,4 @@
using QSB.Messaging;
-using QSB.Patches;
using QSB.WorldSync;
namespace QSB.ModelShip.Messages;
@@ -8,6 +7,13 @@ internal class RespawnModelShipMessage : QSBMessage
{
public RespawnModelShipMessage(bool playEffects) : base(playEffects) { }
- public override void OnReceiveRemote() =>
- QSBPatch.RemoteCall(() => QSBWorldSync.GetUnityObject().RespawnModelShip(Data));
+ public override void OnReceiveRemote()
+ {
+ var flightConsole = QSBWorldSync.GetUnityObject();
+ flightConsole.RespawnModelShip(Data);
+ if (Data)
+ {
+ flightConsole._modelShipBody.GetComponent().PlayOneShot(AudioType.TH_RetrieveModelShip);
+ }
+ }
}
diff --git a/QSB/ModelShip/Messages/UseFlightConsoleMessage.cs b/QSB/ModelShip/Messages/UseFlightConsoleMessage.cs
index 703d0c65a..464e7f3b6 100644
--- a/QSB/ModelShip/Messages/UseFlightConsoleMessage.cs
+++ b/QSB/ModelShip/Messages/UseFlightConsoleMessage.cs
@@ -26,20 +26,14 @@ private static void Handler(bool active)
private UseFlightConsoleMessage(bool active) : base(active) { }
- public override void OnReceiveLocal()
- {
- if (QSBCore.IsHost)
- {
- ModelShipTransformSync.LocalInstance.netIdentity.SetAuthority(Data
- ? From
- : QSBPlayerManager.LocalPlayerId);
- }
- }
+ public override void OnReceiveLocal() => SetCurrentFlyer(From, Data);
public override void OnReceiveRemote()
{
var console = QSBWorldSync.GetUnityObject();
+ SetCurrentFlyer(From, Data);
+
if (Data)
{
console._modelShipBody.Unsuspend();
@@ -62,12 +56,24 @@ public override void OnReceiveRemote()
QSBWorldSync.GetUnityObject()._detector.SetActive(Data);
QSBWorldSync.GetUnityObjects().ForEach(x => x._owCollider.SetActivation(Data));
+ }
+
+ private void SetCurrentFlyer(uint flyer, bool isFlying)
+ {
+ ModelShipManager.Instance.CurrentFlyer = isFlying
+ ? flyer
+ : uint.MaxValue;
if (QSBCore.IsHost)
{
- ModelShipTransformSync.LocalInstance.netIdentity.SetAuthority(Data
- ? From
- : QSBPlayerManager.LocalPlayerId);
+ ModelShipTransformSync.LocalInstance.netIdentity.SetAuthority(isFlying
+ ? flyer
+ : 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();
+ console.RespawnModelShip(false);
}
}
diff --git a/QSB/ModelShip/ModelShipManager.cs b/QSB/ModelShip/ModelShipManager.cs
index 6b60fc9c9..23417fb3d 100644
--- a/QSB/ModelShip/ModelShipManager.cs
+++ b/QSB/ModelShip/ModelShipManager.cs
@@ -13,12 +13,37 @@ internal class ModelShipManager : WorldObjectManager
public override WorldObjectScene WorldObjectScene => WorldObjectScene.SolarSystem;
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)
{
if (QSBCore.IsHost)
{
Instantiate(QSBNetworkManager.singleton.ModelShipPrefab).SpawnWithServerAuthority();
}
+
+ // Is 0 by default -> 2D (bad)
+ QSBWorldSync.GetUnityObject()._consoleAudio.spatialBlend = 1;
}
public override void UnbuildWorldObjects()
diff --git a/QSB/ModelShip/ModelShipThrusterManager.cs b/QSB/ModelShip/ModelShipThrusterManager.cs
new file mode 100644
index 000000000..7541cc91b
--- /dev/null
+++ b/QSB/ModelShip/ModelShipThrusterManager.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using UnityEngine;
+
+namespace QSB.ModelShip;
+
+public static class ModelShipThrusterManager
+{
+ public static readonly List ThrusterFlameControllers = new();
+ public static ThrusterWashController ThrusterWashController { get; private set; }
+
+ public static void CreateModelShipVFX(GameObject modelShip)
+ {
+ ThrusterFlameControllers.Clear();
+ foreach (var item in modelShip.GetComponentsInChildren())
+ {
+ ThrusterFlameControllers.Add(item);
+ }
+
+ ThrusterWashController = modelShip.GetComponentInChildren();
+ }
+}
diff --git a/QSB/ModelShip/ModelShipThrusterVariableSyncer.cs b/QSB/ModelShip/ModelShipThrusterVariableSyncer.cs
new file mode 100644
index 000000000..30c827842
--- /dev/null
+++ b/QSB/ModelShip/ModelShipThrusterVariableSyncer.cs
@@ -0,0 +1,69 @@
+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 void Init(GameObject modelShip)
+ {
+ ThrusterModel = modelShip.GetComponent();
+ _thrusterAudio = modelShip.GetComponentInChildren();
+
+ ModelShipThrusterManager.CreateModelShipVFX(modelShip);
+ }
+
+ public void Update()
+ {
+ if (QSBPlayerManager.LocalPlayer.FlyingModelShip)
+ {
+ GetFromShip();
+ return;
+ }
+
+ if (AccelerationSyncer.public_HasChanged())
+ {
+ if (AccelerationSyncer.Value == Vector3.zero)
+ {
+ foreach (var item in ModelShipThrusterManager.ThrusterFlameControllers)
+ {
+ item.OnStopTranslationalThrust();
+ }
+
+ _thrusterAudio.OnStopTranslationalThrust();
+
+ ModelShipThrusterManager.ThrusterWashController.OnStopTranslationalThrust();
+ }
+ else
+ {
+ foreach (var item in ModelShipThrusterManager.ThrusterFlameControllers)
+ {
+ item.OnStartTranslationalThrust();
+ }
+
+ _thrusterAudio.OnStartTranslationalThrust();
+
+ ModelShipThrusterManager.ThrusterWashController.OnStartTranslationalThrust();
+ }
+ }
+ }
+
+ private void GetFromShip()
+ {
+ if (ThrusterModel)
+ {
+ AccelerationSyncer.Value = ThrusterModel.GetLocalAcceleration();
+ }
+ }
+}
diff --git a/QSB/ModelShip/Patches/ModelShipPatches.cs b/QSB/ModelShip/Patches/ModelShipPatches.cs
index eebe63701..b434641a9 100644
--- a/QSB/ModelShip/Patches/ModelShipPatches.cs
+++ b/QSB/ModelShip/Patches/ModelShipPatches.cs
@@ -2,6 +2,7 @@
using QSB.Messaging;
using QSB.ModelShip.Messages;
using QSB.Patches;
+using UnityEngine;
namespace QSB.ModelShip.Patches;
@@ -20,4 +21,14 @@ private static void RemoteFlightConsole_RespawnModelShip(bool playEffects)
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();
+ }
+ }
}
diff --git a/QSB/ModelShip/Patches/ModelShipThrusterAudioPatches.cs b/QSB/ModelShip/Patches/ModelShipThrusterAudioPatches.cs
new file mode 100644
index 000000000..8d4a76b6e
--- /dev/null
+++ b/QSB/ModelShip/Patches/ModelShipThrusterAudioPatches.cs
@@ -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;
+ }
+ }
+}
diff --git a/QSB/ModelShip/Patches/ModelShipThrusterPatches.cs b/QSB/ModelShip/Patches/ModelShipThrusterPatches.cs
new file mode 100644
index 000000000..8697105c3
--- /dev/null
+++ b/QSB/ModelShip/Patches/ModelShipThrusterPatches.cs
@@ -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 (ModelShipThrusterManager.ThrusterFlameControllers == null) return true;
+
+ if (ModelShipThrusterManager.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;
+ }
+}
diff --git a/QSB/ModelShip/TransformSync/ModelShipTransformSync.cs b/QSB/ModelShip/TransformSync/ModelShipTransformSync.cs
index 23c72c266..29b08c093 100644
--- a/QSB/ModelShip/TransformSync/ModelShipTransformSync.cs
+++ b/QSB/ModelShip/TransformSync/ModelShipTransformSync.cs
@@ -1,4 +1,5 @@
-using QSB.Syncs.Sectored.Rigidbodies;
+using QSB.ShipSync;
+using QSB.Syncs.Sectored.Rigidbodies;
using QSB.Utility;
using QSB.WorldSync;
@@ -8,6 +9,8 @@ internal class ModelShipTransformSync : SectoredRigidbodySync
{
public static ModelShipTransformSync LocalInstance { get; private set; }
+ public ModelShipThrusterVariableSyncer ThrusterVariableSyncer { get; private set; }
+
public override void OnStartClient()
{
base.OnStartClient();
@@ -31,6 +34,14 @@ protected override OWRigidbody InitAttachedRigidbody()
return modelShip;
}
+ protected override void Init()
+ {
+ base.Init();
+
+ ThrusterVariableSyncer = this.GetRequiredComponent();
+ ThrusterVariableSyncer.Init(AttachedRigidbody.gameObject);
+ }
+
///
/// replacement for base method
/// using SetPos/Rot instead of Move
diff --git a/QSB/OrbSync/TransformSync/NomaiOrbTransformSync.cs b/QSB/OrbSync/TransformSync/NomaiOrbTransformSync.cs
index 7fc2bb43e..e3b092fd8 100644
--- a/QSB/OrbSync/TransformSync/NomaiOrbTransformSync.cs
+++ b/QSB/OrbSync/TransformSync/NomaiOrbTransformSync.cs
@@ -59,17 +59,9 @@ protected override void Uninit()
{
base.Uninit();
- // this is null sometimes on here, but not on other similar transforms syncs (like anglers)
- // idk why, but whatever
- if (AttachedTransform)
- {
- var body = AttachedTransform.GetAttachedOWRigidbody();
- if (body)
- {
- body.OnUnsuspendOWRigidbody -= OnUnsuspend;
- body.OnSuspendOWRigidbody -= OnSuspend;
- }
- }
+ var body = AttachedTransform.GetAttachedOWRigidbody();
+ body.OnUnsuspendOWRigidbody -= OnUnsuspend;
+ body.OnSuspendOWRigidbody -= OnSuspend;
}
private void OnUnsuspend(OWRigidbody suspendedBody) => netIdentity.UpdateAuthQueue(AuthQueueAction.Add);
diff --git a/QSB/Patches/QSBPatch.cs b/QSB/Patches/QSBPatch.cs
index 42853c67c..618ba1ebe 100644
--- a/QSB/Patches/QSBPatch.cs
+++ b/QSB/Patches/QSBPatch.cs
@@ -1,6 +1,4 @@
using HarmonyLib;
-using QSB.Utility;
-using System;
namespace QSB.Patches;
@@ -12,30 +10,8 @@ public abstract class QSBPatch
public void DoPatches(Harmony instance) => instance.PatchAll(GetType());
- #region remote calls
-
- protected static bool Remote { get; private set; }
- protected static object RemoteData { get; private set; }
-
- public static void RemoteCall(Action call, object data = null)
- {
- Remote = true;
- RemoteData = data;
- nameof(QSBPatch).Try("doing remote call", call);
- Remote = false;
- RemoteData = null;
- }
-
- public static T RemoteCall(Func call, object data = null)
- {
- Remote = true;
- RemoteData = data;
- var t = default(T);
- nameof(QSBPatch).Try("doing remote call", () => t = call());
- Remote = false;
- RemoteData = null;
- return t;
- }
-
- #endregion
+ ///
+ /// this is true when a message is received remotely (OnReceiveRemote) or a player leaves (OnRemovePlayer)
+ ///
+ public static bool Remote;
}
diff --git a/QSB/Player/Messages/PlayerJoinMessage.cs b/QSB/Player/Messages/PlayerJoinMessage.cs
index 5a49644be..e5281c2b6 100644
--- a/QSB/Player/Messages/PlayerJoinMessage.cs
+++ b/QSB/Player/Messages/PlayerJoinMessage.cs
@@ -71,6 +71,13 @@ public override void OnReceiveRemote()
{
if (QSBCore.IsHost)
{
+ if (QSBCore.DebugSettings.KickEveryone)
+ {
+ DebugLog.ToConsole($"Kicking {PlayerName} because of DebugSettings.KickEveryone", MessageType.Error);
+ new PlayerKickMessage(From, "This server has DebugSettings.KickEveryone enabled.").Send();
+ return;
+ }
+
if (QSBVersion != QSBCore.QSBVersion)
{
DebugLog.ToConsole($"Error - Client {PlayerName} connecting with wrong QSB version. (Client:{QSBVersion}, Server:{QSBCore.QSBVersion})", MessageType.Error);
@@ -92,7 +99,7 @@ public override void OnReceiveRemote()
return;
}
- if (QSBPlayerManager.PlayerList.Any(x => x.EyeState >= EyeState.Observatory))
+ if (QSBPlayerManager.PlayerList.Any(x => x.EyeState > EyeState.Observatory))
{
DebugLog.ToConsole($"Error - Client {PlayerName} connecting too late into eye scene.", MessageType.Error);
new PlayerKickMessage(From, QSBLocalization.Current.GameProgressLimit).Send();
diff --git a/QSB/Player/Messages/PlayerKickMessage.cs b/QSB/Player/Messages/PlayerKickMessage.cs
index 26cad6577..3374e7624 100644
--- a/QSB/Player/Messages/PlayerKickMessage.cs
+++ b/QSB/Player/Messages/PlayerKickMessage.cs
@@ -28,19 +28,6 @@ public override void Deserialize(NetworkReader reader)
PlayerId = reader.Read();
}
- public override void OnReceiveLocal()
- {
- if (!QSBCore.IsHost)
- {
- return;
- }
-
- Delay.RunFramesLater(10, KickPlayer);
- }
-
- private void KickPlayer()
- => PlayerId.GetNetworkConnection().Disconnect();
-
public override void OnReceiveRemote()
{
if (PlayerId != QSBPlayerManager.LocalPlayerId)
@@ -57,5 +44,7 @@ public override void OnReceiveRemote()
DebugLog.ToAll(string.Format(QSBLocalization.Current.KickedFromServer, Data));
MenuManager.Instance.OnKicked(Data);
+
+ NetworkClient.Disconnect();
}
}
\ No newline at end of file
diff --git a/QSB/Player/Patches/VolumePatches.cs b/QSB/Player/Patches/VolumePatches.cs
index 45f23b93f..841316035 100644
--- a/QSB/Player/Patches/VolumePatches.cs
+++ b/QSB/Player/Patches/VolumePatches.cs
@@ -1,5 +1,6 @@
using HarmonyLib;
using QSB.Patches;
+using QSB.Utility;
using UnityEngine;
namespace QSB.Player.Patches;
@@ -43,8 +44,15 @@ public static void OnEffectVolumeEnter(RingRiverFluidVolume __instance, GameObje
[HarmonyPrefix]
[HarmonyPatch(typeof(ElectricityVolume), nameof(ElectricityVolume.OnEffectVolumeEnter))]
- public static bool OnEffectVolumeEnter(ElectricityVolume __instance, GameObject hitObj) =>
+ [HarmonyPatch(typeof(DreamWarpVolume), nameof(DreamWarpVolume.OnEnterTriggerVolume))]
+ [HarmonyPatch(typeof(NomaiWarpPlatform), nameof(NomaiWarpPlatform.OnEntry))]
+ public static bool PreventRemotePlayerEnter(object __instance, GameObject hitObj)
+ {
+ DebugLog.DebugWrite($"{__instance} funny prevent enter {hitObj}");
// this is a dogshit fix to a bug where this would ApplyShock to remote players,
// which would actually apply the shock affects to the entire planet / sector
- hitObj.name != "REMOTE_PlayerDetector";
+ //
+ // TODO: also do this with remote probes
+ return hitObj.name is not ("REMOTE_PlayerDetector" or "REMOTE_CameraDetector");
+ }
}
diff --git a/QSB/Player/PlayerHUDMarker.cs b/QSB/Player/PlayerHUDMarker.cs
index 58c23b292..82131999e 100644
--- a/QSB/Player/PlayerHUDMarker.cs
+++ b/QSB/Player/PlayerHUDMarker.cs
@@ -3,6 +3,7 @@
namespace QSB.Player;
+[UsedInUnityProject]
public class PlayerHUDMarker : HUDDistanceMarker
{
private PlayerInfo _player;
@@ -33,7 +34,7 @@ private bool ShouldBeVisible()
return false;
}
- return _player.IsReady && !_player.IsDead && !_player.InDreamWorld && _player.Visible;
+ return _player.IsReady && !_player.IsDead && (!_player.InDreamWorld || QSBPlayerManager.LocalPlayer.InDreamWorld) && _player.Visible;
}
private void Update()
diff --git a/QSB/Player/PlayerInfo.cs b/QSB/Player/PlayerInfo.cs
index f84c14cef..06715acd8 100644
--- a/QSB/Player/PlayerInfo.cs
+++ b/QSB/Player/PlayerInfo.cs
@@ -3,6 +3,7 @@
using QSB.Audio;
using QSB.ClientServerStateSync;
using QSB.Messaging;
+using QSB.ModelShip;
using QSB.Player.Messages;
using QSB.Player.TransformSync;
using QSB.QuantumSync.WorldObjects;
@@ -31,9 +32,10 @@ public partial class PlayerInfo
public bool IsInEyeShuttle { get; set; }
public IQSBQuantumObject EntangledObject { get; set; }
public QSBPlayerAudioController AudioController { get; set; }
- public bool IsLocalPlayer => TransformSync.isLocalPlayer;
+ 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 bool FlyingShip => ShipManager.Instance.CurrentFlyer == PlayerId;
+ public bool FlyingModelShip => ModelShipManager.Instance.CurrentFlyer == PlayerId;
public PlayerInfo(PlayerTransformSync transformSync)
{
@@ -155,4 +157,4 @@ public void SetVisible(bool visible, float seconds = 0)
}
public override string ToString() => $"{PlayerId}:{GetType().Name} ({Name})";
-}
\ No newline at end of file
+}
diff --git a/QSB/Player/PlayerInfoParts/LocalTools.cs b/QSB/Player/PlayerInfoParts/LocalTools.cs
index 7af6d84e3..bf25ff0dd 100644
--- a/QSB/Player/PlayerInfoParts/LocalTools.cs
+++ b/QSB/Player/PlayerInfoParts/LocalTools.cs
@@ -15,7 +15,7 @@ public PlayerProbeLauncher LocalProbeLauncher
return null;
}
- return CameraBody?.transform.Find("ProbeLauncher").GetComponent();
+ return (PlayerProbeLauncher)Locator.GetToolModeSwapper().GetProbeLauncher();
}
}
@@ -43,7 +43,7 @@ public Signalscope LocalSignalscope
return null;
}
- return CameraBody?.transform.Find("Signalscope").GetComponent();
+ return Locator.GetToolModeSwapper().GetSignalScope();
}
}
@@ -57,7 +57,7 @@ public NomaiTranslator LocalTranslator
return null;
}
- return CameraBody?.transform.Find("NomaiTranslatorProp").GetComponent();
+ return Locator.GetToolModeSwapper().GetTranslator();
}
}
}
diff --git a/QSB/Player/PlayerInfoParts/Tools.cs b/QSB/Player/PlayerInfoParts/Tools.cs
index 65788e416..04141b5cd 100644
--- a/QSB/Player/PlayerInfoParts/Tools.cs
+++ b/QSB/Player/PlayerInfoParts/Tools.cs
@@ -15,7 +15,7 @@ namespace QSB.Player;
public partial class PlayerInfo
{
public GameObject ProbeBody { get; set; }
- public QSBProbe Probe { get; set; }
+ public QSBSurveyorProbe Probe { get; set; }
public QSBFlashlight FlashLight => CameraBody == null ? null : CameraBody.GetComponentInChildren();
public QSBTool Signalscope => GetToolByType(ToolType.Signalscope);
public QSBTool Translator => GetToolByType(ToolType.Translator);
diff --git a/QSB/Player/PlayerMapMarker.cs b/QSB/Player/PlayerMapMarker.cs
index 2a5f48f83..4d35a7340 100644
--- a/QSB/Player/PlayerMapMarker.cs
+++ b/QSB/Player/PlayerMapMarker.cs
@@ -1,7 +1,9 @@
-using UnityEngine;
+using QSB.Utility;
+using UnityEngine;
namespace QSB.Player;
+[UsedInUnityProject]
public class PlayerMapMarker : MonoBehaviour
{
private PlayerInfo _player;
@@ -58,7 +60,7 @@ private bool ShouldBeVisible()
var playerScreenPos = Locator.GetActiveCamera().WorldToScreenPoint(transform.position);
var isInfrontOfCamera = playerScreenPos.z > 0f;
- return _player.IsReady && !_player.IsDead && !_player.InDreamWorld && _player.Visible && isInfrontOfCamera;
+ return _player.IsReady && !_player.IsDead && (!_player.InDreamWorld || QSBPlayerManager.LocalPlayer.InDreamWorld) && _player.Visible && isInfrontOfCamera;
}
public void LateUpdate()
diff --git a/QSB/Player/QSBPlayerManager.cs b/QSB/Player/QSBPlayerManager.cs
index b6d32ade4..a7216c8e1 100644
--- a/QSB/Player/QSBPlayerManager.cs
+++ b/QSB/Player/QSBPlayerManager.cs
@@ -5,7 +5,6 @@
using QSB.Tools.ProbeTool;
using QSB.Utility;
using System;
-using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
@@ -22,7 +21,7 @@ public static PlayerInfo LocalPlayer
if (localInstance == null)
{
DebugLog.ToConsole("Error - Trying to get LocalPlayer when the local PlayerTransformSync instance is null." +
- $"{Environment.NewLine} Stacktrace : {Environment.StackTrace} ", MessageType.Error);
+ $"{Environment.NewLine} Stacktrace : {Environment.StackTrace} ", MessageType.Error);
return null;
}
@@ -66,8 +65,8 @@ public static List GetPlayersWithCameras(bool includeLocalCamera = t
{
var cameraList = PlayerList.Where(x => x.Camera != null && x.PlayerId != LocalPlayerId).ToList();
if (includeLocalCamera
- && LocalPlayer != default
- && LocalPlayer.Camera != null)
+ && LocalPlayer != default
+ && LocalPlayer.Camera != null)
{
cameraList.Add(LocalPlayer);
}
@@ -75,11 +74,11 @@ public static List GetPlayersWithCameras(bool includeLocalCamera = t
{
if (LocalPlayer == default)
{
- DebugLog.ToConsole($"Error - LocalPlayer is null.", MessageType.Error);
+ DebugLog.ToConsole("Error - LocalPlayer is null.", MessageType.Error);
return cameraList;
}
- DebugLog.ToConsole($"Error - LocalPlayer.Camera is null.", MessageType.Error);
+ DebugLog.ToConsole("Error - LocalPlayer.Camera is null.", MessageType.Error);
LocalPlayer.Camera = Locator.GetPlayerCamera();
}
@@ -89,7 +88,7 @@ public static List GetPlayersWithCameras(bool includeLocalCamera = t
public static (Flashlight LocalFlashlight, IEnumerable RemoteFlashlights) GetPlayerFlashlights()
=> (Locator.GetFlashlight(), PlayerList.Where(x => x.FlashLight != null).Select(x => x.FlashLight));
- public static (SurveyorProbe LocalProbe, IEnumerable RemoteProbes) GetPlayerProbes()
+ public static (SurveyorProbe LocalProbe, IEnumerable RemoteProbes) GetPlayerProbes()
=> (Locator.GetProbe(), PlayerList.Where(x => x.Probe != null).Select(x => x.Probe));
public static IEnumerable GetThrusterLightTrackers()
@@ -109,7 +108,7 @@ public static PlayerInfo GetClosestPlayerToWorldPoint(List playerLis
{
if (playerList == null)
{
- DebugLog.ToConsole($"Error - Cannot get closest player from null player list.", MessageType.Error);
+ DebugLog.ToConsole("Error - Cannot get closest player from null player list.", MessageType.Error);
return null;
}
@@ -117,7 +116,7 @@ public static PlayerInfo GetClosestPlayerToWorldPoint(List playerLis
if (playerList.Count == 0)
{
- DebugLog.ToConsole($"Error - Cannot get closest player from empty (ready) player list.", MessageType.Error);
+ DebugLog.ToConsole("Error - Cannot get closest player from empty (ready) player list.", MessageType.Error);
return null;
}
@@ -126,67 +125,4 @@ public static PlayerInfo GetClosestPlayerToWorldPoint(List playerLis
public static IEnumerable<(PlayerInfo Player, IQSBItem HeldItem)> GetPlayerCarryItems()
=> PlayerList.Select(x => (x, x.HeldItem));
-
- private static Dictionary _connectionIdToPlayer = new();
-
- public static IEnumerator ValidatePlayers()
- {
- while (true)
- {
- if (QSBCore.IsInMultiplayer && QSBCore.IsHost)
- {
- _connectionIdToPlayer.Clear();
-
- var playersToRemove = new List();
-
- foreach (var player in PlayerList)
- {
- var transformSync = player.TransformSync;
-
- if (transformSync == null)
- {
- DebugLog.ToConsole($"Error - {player.PlayerId}'s TransformSync is null.", MessageType.Error);
- playersToRemove.Add(player);
- continue;
- }
-
- var networkIdentity = transformSync.netIdentity;
-
- if (networkIdentity == null)
- {
- DebugLog.ToConsole($"Error - {player.PlayerId}'s TransformSync's NetworkIdentity is null.", MessageType.Error);
- playersToRemove.Add(player);
- continue;
- }
-
- var connectionToClient = networkIdentity.connectionToClient;
-
- if (_connectionIdToPlayer.ContainsKey(connectionToClient.connectionId))
- {
- // oh god oh fuck
- DebugLog.ToConsole($"Error - {player.PlayerId}'s connectionToClient.connectionId is already being used?!?", MessageType.Error);
- playersToRemove.Add(player);
- continue;
- }
-
- _connectionIdToPlayer.Add(connectionToClient.connectionId, player);
- }
-
- if (playersToRemove.Count != 0)
- {
- DebugLog.DebugWrite($"Removing {playersToRemove.Count} invalid players.", MessageType.Success);
-
- foreach (var player in playersToRemove)
- {
- OnRemovePlayer?.Invoke(player);
- player.HudMarker?.Remove();
- PlayerList.Remove(player);
- DebugLog.DebugWrite($"Remove Invalid Player : {player}", MessageType.Info);
- }
- }
- }
-
- yield return new WaitForSecondsRealtime(5);
- }
- }
-}
\ No newline at end of file
+}
diff --git a/QSB/Player/RemotePlayerFluidDetector.cs b/QSB/Player/RemotePlayerFluidDetector.cs
index 50def10e5..a732999cb 100644
--- a/QSB/Player/RemotePlayerFluidDetector.cs
+++ b/QSB/Player/RemotePlayerFluidDetector.cs
@@ -7,6 +7,7 @@
namespace QSB.Player;
+[UsedInUnityProject]
public class RemotePlayerFluidDetector : PriorityDetector
{
private SplashEffect[] _splashEffects;
diff --git a/QSB/Player/RemotePlayerVelocity.cs b/QSB/Player/RemotePlayerVelocity.cs
index 5b5c87f00..80d6c251e 100644
--- a/QSB/Player/RemotePlayerVelocity.cs
+++ b/QSB/Player/RemotePlayerVelocity.cs
@@ -1,7 +1,9 @@
-using UnityEngine;
+using QSB.Utility;
+using UnityEngine;
namespace QSB.Player;
+[UsedInUnityProject]
public class RemotePlayerVelocity : MonoBehaviour
{
private Vector3 _prevRelPosition;
diff --git a/QSB/Player/TransformSync/PlayerTransformSync.cs b/QSB/Player/TransformSync/PlayerTransformSync.cs
index a0e0dcd6a..ee2bc73d3 100644
--- a/QSB/Player/TransformSync/PlayerTransformSync.cs
+++ b/QSB/Player/TransformSync/PlayerTransformSync.cs
@@ -1,5 +1,6 @@
using OWML.Common;
using QSB.Messaging;
+using QSB.Patches;
using QSB.Player.Messages;
using QSB.PlayerBodySetup.Local;
using QSB.PlayerBodySetup.Remote;
@@ -12,6 +13,7 @@
namespace QSB.Player.TransformSync;
+[UsedInUnityProject]
public class PlayerTransformSync : SectoredTransformSync
{
protected override bool IsPlayerObject => true;
@@ -29,25 +31,8 @@ public class PlayerTransformSync : SectoredTransformSync
private Transform _visibleStickTip;
private Transform _networkStickTip => _networkStickPivot.GetChild(0);
- private bool _hasRanOnStartClient;
-
public override void OnStartClient()
{
- if (_hasRanOnStartClient)
- {
- DebugLog.ToConsole($"ERROR - OnStartClient is being called AGAIN for {Player.PlayerId}'s PlayerTransformSync!", MessageType.Error);
- return;
- }
-
- _hasRanOnStartClient = true;
- if (QSBPlayerManager.PlayerList.Any(x => x.TransformSync == this))
- {
- // this really shouldnt happen...
- DebugLog.ToConsole($"Error - A PlayerInfo already exists with TransformSync {name}", MessageType.Error);
- Destroy(gameObject); // probably bad
- return;
- }
-
var player = new PlayerInfo(this);
QSBPlayerManager.PlayerList.SafeAdd(player);
base.OnStartClient();
@@ -57,26 +42,16 @@ public override void OnStartClient()
JoinLeaveSingularity.Create(Player, true);
}
- public override void OnStartLocalPlayer()
- {
- if (LocalInstance != null)
- {
- DebugLog.ToConsole($"ERROR - LocalInstance is already non-null in OnStartLocalPlayer!", MessageType.Error);
- Destroy(gameObject); // probably bad
- return;
- }
-
- LocalInstance = this;
- }
-
- public override void OnStopLocalPlayer() => LocalInstance = null;
+ public override void OnStartLocalPlayer() => LocalInstance = this;
public override void OnStopClient()
{
JoinLeaveSingularity.Create(Player, false);
// TODO : Maybe move this to a leave event...? Would ensure everything could finish up before removing the player
+ QSBPatch.Remote = true;
QSBPlayerManager.OnRemovePlayer?.Invoke(Player);
+ QSBPatch.Remote = false;
base.OnStopClient();
Player.HudMarker?.Remove();
QSBPlayerManager.PlayerList.Remove(Player);
diff --git a/QSB/PlayerBodySetup/Remote/DreamWorldSpawnAnimator.cs b/QSB/PlayerBodySetup/Remote/DreamWorldSpawnAnimator.cs
index 9f9d52d90..b43db3e71 100644
--- a/QSB/PlayerBodySetup/Remote/DreamWorldSpawnAnimator.cs
+++ b/QSB/PlayerBodySetup/Remote/DreamWorldSpawnAnimator.cs
@@ -1,7 +1,9 @@
-using UnityEngine;
+using QSB.Utility;
+using UnityEngine;
namespace QSB.PlayerBodySetup.Remote;
+[UsedInUnityProject]
public class DreamWorldSpawnAnimator : MonoBehaviour
{
[SerializeField]
diff --git a/QSB/PlayerBodySetup/Remote/QSBDitheringAnimator.cs b/QSB/PlayerBodySetup/Remote/QSBDitheringAnimator.cs
index d982c35ab..6889e7fad 100644
--- a/QSB/PlayerBodySetup/Remote/QSBDitheringAnimator.cs
+++ b/QSB/PlayerBodySetup/Remote/QSBDitheringAnimator.cs
@@ -1,9 +1,11 @@
-using System.Linq;
+using QSB.Utility;
+using System.Linq;
using UnityEngine;
using UnityEngine.Rendering;
namespace QSB.PlayerBodySetup.Remote;
+[UsedInUnityProject]
public class QSBDitheringAnimator : MonoBehaviour
{
public bool FullyVisible => !enabled && OWMath.ApproxEquals(_visibleFraction, 1);
diff --git a/QSB/PoolSync/CustomNomaiRemoteCameraPlatform.cs b/QSB/PoolSync/CustomNomaiRemoteCameraPlatform.cs
index 14c93d43f..a08833778 100644
--- a/QSB/PoolSync/CustomNomaiRemoteCameraPlatform.cs
+++ b/QSB/PoolSync/CustomNomaiRemoteCameraPlatform.cs
@@ -138,6 +138,23 @@ private void OnDestroy()
QSBPlayerManager.OnRemovePlayer -= OnRemovePlayer;
}
+ private void LateUpdate()
+ {
+ // can't put this stuff in Update/UpdateHologramTransforms as
+ // manual bone rotations need to happen after the animator has changed them
+ if ((_platformActive && _anyoneStillOnPlatform) || _cameraState == CameraState.Disconnecting_FadeIn)
+ {
+ foreach (var item in _playerToHologram)
+ {
+ var hologram = item.Value.transform;
+ var anim = hologram.GetChild(0).gameObject.GetComponent();
+ var cameraRotation = item.Key.CameraBody.transform.localRotation.eulerAngles;
+ var rotation = Quaternion.Euler(-cameraRotation.y, -cameraRotation.z, cameraRotation.x); // wtf why
+ anim.GetBoneTransform(HumanBodyBones.Head).localRotation = rotation;
+ }
+ }
+ }
+
private void Update()
{
if (_platformActive)
@@ -719,10 +736,6 @@ public void OnRemotePlayerEnter(uint playerId)
hologramCopy.GetChild(0).Find("player_mesh_noSuit:Traveller_HEA_Player/player_mesh_noSuit:Player_Head").gameObject.layer = 0;
hologramCopy.GetChild(0).Find("Traveller_Mesh_v01:Traveller_Geo/Traveller_Mesh_v01:PlayerSuit_Helmet").gameObject.layer = 0;
- // BUG : Look at this again... probably need to sync head rotation to something else
- //var ikSync = hologramCopy.GetChild(0).gameObject.AddComponent();
- //ikSync.Init(player.CameraBody.transform);
-
if (player.AnimationSync.VisibleAnimator == null)
{
DebugLog.ToConsole($"Warning - {playerId}'s VisibleAnimator is null!", MessageType.Error);
diff --git a/QSB/QSB.csproj b/QSB/QSB.csproj
index 597afd068..f3bdfee4f 100644
--- a/QSB/QSB.csproj
+++ b/QSB/QSB.csproj
@@ -3,7 +3,7 @@
Quantum Space Buddies
Quantum Space Buddies
Multiplayer mod for Outer Wilds
- $(OwmlDir)\Mods\QSB
+ $(OwmlDir)\Mods\Raicuparta.QuantumSpaceBuddies
CS1998;CS0649
@@ -50,7 +50,6 @@
<_Files Include="$(OutputPath)\*.pdb" />
<_Files Include="$(GameDllsDir)\EOS-SDK.dll" />
- <_Files Include="$(GameDllsDir)\UniSense.dll" />
<_Files Include="$(GameDllsDir)\Autofac.dll" />
<_Files Include="$(GameDllsDir)\Newtonsoft.Json.dll" />
<_Files Include="$(GameDllsDir)\0Harmony.dll" />
@@ -77,9 +76,9 @@
PreserveNewest
-
- PreserveNewest
-
+
+ PreserveNewest
+
PreserveNewest
@@ -90,13 +89,12 @@
-
+
+
-
-
diff --git a/QSB/QSBCore.cs b/QSB/QSBCore.cs
index b8b9429f7..b7107571e 100644
--- a/QSB/QSBCore.cs
+++ b/QSB/QSBCore.cs
@@ -5,7 +5,6 @@
using QSB.Localization;
using QSB.Menus;
using QSB.Patches;
-using QSB.Player;
using QSB.QuantumSync;
using QSB.SaveSync;
using QSB.Utility;
@@ -68,18 +67,13 @@ public class QSBCore : ModBehaviour
public static readonly string[] IncompatibleMods =
{
- // cheats mods
- "Glitch.AltDebugMenu",
- "PacificEngine.CheatsMod",
// incompatible mods
"Raicuparta.NomaiVR",
"xen.NewHorizons",
"Vesper.AutoResume",
"Vesper.OuterWildsMMO",
"_nebula.StopTime",
- "Leadpogrommer.PeacefulGhosts",
"PacificEngine.OW_Randomizer",
- "xen.DayDream"
};
private static void DetermineGameVendor()
@@ -153,7 +147,6 @@ public void Start()
if (DebugSettings.AutoStart)
{
DebugSettings.UseKcpTransport = true;
- DebugSettings.SkipTitleScreen = true;
DebugSettings.DebugMode = true;
}
@@ -188,8 +181,6 @@ public void Start()
QSBWorldSync.Managers = components.OfType().ToArray();
QSBPatchManager.OnPatchType += OnPatchType;
QSBPatchManager.OnUnpatchType += OnUnpatchType;
-
- StartCoroutine(QSBPlayerManager.ValidatePlayers());
}
private static void OnPatchType(QSBPatchTypes type)
@@ -253,10 +244,9 @@ public override void Configure(IModConfig config)
IncompatibleModsAllowed = config.GetSettingsValue("incompatibleModsAllowed");
}
-#if DEBUG
private void Update()
{
- if (Keyboard.current[Key.Q].isPressed && Keyboard.current[Key.D].wasPressedThisFrame)
+ if (Keyboard.current[Key.Q].isPressed && Keyboard.current[Key.NumpadEnter].wasPressedThisFrame)
{
DebugSettings.DebugMode = !DebugSettings.DebugMode;
@@ -268,7 +258,6 @@ private void Update()
DebugLog.ToConsole($"DEBUG MODE = {DebugSettings.DebugMode}");
}
}
-#endif
}
/*
diff --git a/QSB/QSBNetworkManager.cs b/QSB/QSBNetworkManager.cs
index 78fb5359d..28a05a52a 100644
--- a/QSB/QSBNetworkManager.cs
+++ b/QSB/QSBNetworkManager.cs
@@ -13,6 +13,7 @@
using QSB.EchoesOfTheEye.RaftSync.TransformSync;
using QSB.JellyfishSync.TransformSync;
using QSB.Messaging;
+using QSB.ModelShip;
using QSB.ModelShip.TransformSync;
using QSB.OrbSync.Messages;
using QSB.OrbSync.TransformSync;
@@ -26,6 +27,7 @@
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 +58,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;
@@ -74,7 +77,9 @@ public override void Awake()
if (QSBCore.DebugSettings.UseKcpTransport)
{
- transport = gameObject.AddComponent();
+ var kcpTransport = gameObject.AddComponent();
+ kcpTransport.Timeout = int.MaxValue; // effectively disables kcp ping and timeout (good for testing)
+ transport = kcpTransport;
}
else
{
@@ -147,8 +152,14 @@ public override void Awake()
spawnPrefabs.Add(ShipLegPrefab);
ModelShipPrefab = MakeNewNetworkObject(14, "NetworkModelShip", typeof(ModelShipTransformSync));
+ var modelShipVector3Syncer = ModelShipPrefab.AddComponent();
+ var modelShipThrusterVariableSyncer = ModelShipPrefab.AddComponent();
+ modelShipThrusterVariableSyncer.AccelerationSyncer = modelShipVector3Syncer;
spawnPrefabs.Add(ModelShipPrefab);
+ StationaryProbeLauncherPrefab = MakeNewNetworkObject(15, "NetworkStationaryProbeLauncher", typeof(StationaryProbeLauncherVariableSyncer));
+ spawnPrefabs.Add(StationaryProbeLauncherPrefab);
+
ConfigureNetworkManager();
}
@@ -347,8 +358,18 @@ public override void OnServerDisconnect(NetworkConnectionToClient conn) // Calle
identity.SetAuthority(QSBPlayerManager.LocalPlayerId);
}
}
+ // revert authority from model ship
+ if (ModelShipTransformSync.LocalInstance != null)
+ {
+ var identity = ModelShipTransformSync.LocalInstance.netIdentity;
+ if (identity != null && identity.connectionToClient == conn)
+ {
+ identity.SetAuthority(QSBPlayerManager.LocalPlayerId);
+ }
+ }
// stop dragging for the orbs this player was dragging
+ // i THINK this is here because orb authority is in network behavior, which may not work properly in OnPlayerLeave
foreach (var qsbOrb in QSBWorldSync.GetWorldObjects())
{
if (qsbOrb.NetworkBehaviour == null)
diff --git a/QSB/QuantumSync/Patches/QuantumPatches.cs b/QSB/QuantumSync/Patches/QuantumPatches.cs
index 0da7ade9f..a37e418ba 100644
--- a/QSB/QuantumSync/Patches/QuantumPatches.cs
+++ b/QSB/QuantumSync/Patches/QuantumPatches.cs
@@ -478,4 +478,17 @@ public static bool QuantumSkeletonTower_ChangeQuantumState(QuantumSkeletonTower
return false;
}
+ [HarmonyPrefix]
+ [HarmonyPatch(typeof(QuantumObject), nameof(QuantumObject.OnProbeSnapshot))]
+ public static bool OnProbeSnapshot()
+ {
+ return false;
+ }
+
+ [HarmonyPrefix]
+ [HarmonyPatch(typeof(QuantumObject), nameof(QuantumObject.OnProbeSnapshotRemoved))]
+ public static bool OnProbeSnapshotRemoved()
+ {
+ return false;
+ }
}
\ No newline at end of file
diff --git a/QSB/QuantumSync/QuantumManager.cs b/QSB/QuantumSync/QuantumManager.cs
index 1d932e6eb..ae1429d2e 100644
--- a/QSB/QuantumSync/QuantumManager.cs
+++ b/QSB/QuantumSync/QuantumManager.cs
@@ -126,6 +126,28 @@ public static IEnumerable GetEntangledPlayers(QuantumObject obj)
return QSBPlayerManager.PlayerList.Where(x => x.EntangledObject == worldObj);
}
+ public static void OnTakeProbeSnapshot(PlayerInfo player, ProbeCamera.ID cameraId)
+ {
+ foreach (var quantumObject in QSBWorldSync.GetWorldObjects())
+ {
+ if (quantumObject.ControllingPlayer == QSBPlayerManager.LocalPlayerId)
+ {
+ quantumObject.OnTakeProbeSnapshot(player, cameraId);
+ }
+ }
+ }
+
+ public static void OnRemoveProbeSnapshot(PlayerInfo player)
+ {
+ foreach (var quantumObject in QSBWorldSync.GetWorldObjects())
+ {
+ if (quantumObject.ControllingPlayer == QSBPlayerManager.LocalPlayerId)
+ {
+ quantumObject.OnRemoveProbeSnapshot(player);
+ }
+ }
+ }
+
#region debug shapes
private static GameObject _debugSphere, _debugCube, _debugCapsule;
diff --git a/QSB/QuantumSync/WorldObjects/IQSBQuantumObject.cs b/QSB/QuantumSync/WorldObjects/IQSBQuantumObject.cs
index 55d43df07..ebf3a9899 100644
--- a/QSB/QuantumSync/WorldObjects/IQSBQuantumObject.cs
+++ b/QSB/QuantumSync/WorldObjects/IQSBQuantumObject.cs
@@ -1,4 +1,5 @@
-using QSB.WorldSync;
+using QSB.Player;
+using QSB.WorldSync;
using System.Collections.Generic;
namespace QSB.QuantumSync.WorldObjects;
@@ -12,4 +13,6 @@ public interface IQSBQuantumObject : IWorldObject
void SetIsQuantum(bool isQuantum);
VisibilityObject GetVisibilityObject();
+ void OnTakeProbeSnapshot(PlayerInfo player, ProbeCamera.ID cameraId);
+ void OnRemoveProbeSnapshot(PlayerInfo player);
}
\ No newline at end of file
diff --git a/QSB/QuantumSync/WorldObjects/QSBQuantumObject.cs b/QSB/QuantumSync/WorldObjects/QSBQuantumObject.cs
index 9f9011a22..a9c1e1084 100644
--- a/QSB/QuantumSync/WorldObjects/QSBQuantumObject.cs
+++ b/QSB/QuantumSync/WorldObjects/QSBQuantumObject.cs
@@ -3,6 +3,7 @@
using QSB.Messaging;
using QSB.Player;
using QSB.QuantumSync.Messages;
+using QSB.Tools.ProbeTool;
using QSB.Utility;
using QSB.WorldSync;
using System.Collections.Generic;
@@ -28,6 +29,8 @@ internal abstract class QSBQuantumObject : WorldObject, IQSBQuantumObject
public uint ControllingPlayer { get; set; }
public bool IsEnabled { get; private set; }
+ private List _visibleToProbes = new();
+
public override void OnRemoval()
{
if (HostControls)
@@ -174,6 +177,99 @@ private void OnDisable(Shape s) =>
((IQSBQuantumObject)this).SendMessage(new QuantumAuthorityMessage(0u));
});
+ public void OnTakeProbeSnapshot(PlayerInfo player, ProbeCamera.ID cameraId)
+ {
+ if (player.IsLocalPlayer)
+ {
+ var probe = Locator.GetProbe();
+ ProbeCamera probeCamera = default;
+ switch (cameraId)
+ {
+ case ProbeCamera.ID.Forward:
+ probeCamera = probe.GetForwardCamera();
+ break;
+ case ProbeCamera.ID.Reverse:
+ probeCamera = probe.GetReverseCamera();
+ break;
+ case ProbeCamera.ID.Rotating:
+ probeCamera = probe.GetRotatingCamera();
+ break;
+ case ProbeCamera.ID.PreLaunch:
+ probeCamera = player.LocalProbeLauncher._preLaunchCamera;
+ break;
+ }
+
+ var distance = Vector3.Distance(AttachedObject.transform.position, probeCamera.transform.position);
+ if (distance < AttachedObject._maxSnapshotLockRange
+ && AttachedObject.IsIlluminated()
+ && !probeCamera.HasInterference()
+ && AttachedObject.CheckVisibilityFromProbe(probeCamera.GetOWCamera()))
+ {
+ if (!_visibleToProbes.Contains(player))
+ {
+ _visibleToProbes.Add(player);
+ }
+
+ AttachedObject._visibleInProbeSnapshot = _visibleToProbes.Any(x => x != null);
+ return;
+ }
+ }
+ else
+ {
+ var probe = player.Probe;
+ QSBProbeCamera probeCamera = default;
+ switch (cameraId)
+ {
+ case ProbeCamera.ID.Forward:
+ probeCamera = probe.GetForwardCamera();
+ break;
+ case ProbeCamera.ID.Reverse:
+ probeCamera = probe.GetReverseCamera();
+ break;
+ case ProbeCamera.ID.Rotating:
+ probeCamera = probe.GetRotatingCamera();
+ break;
+ case ProbeCamera.ID.PreLaunch:
+ //TODO : uhhhh yeah do this lol
+ probeCamera = null;
+ break;
+ }
+
+ var distance = Vector3.Distance(AttachedObject.transform.position, probeCamera.transform.position);
+ if (distance < AttachedObject._maxSnapshotLockRange
+ && AttachedObject.IsIlluminated()
+ && !probeCamera.HasInterference()
+ && AttachedObject.CheckVisibilityFromProbe(probeCamera.GetOWCamera()))
+ {
+ if (!_visibleToProbes.Contains(player))
+ {
+ _visibleToProbes.Add(player);
+ }
+
+ _visibleToProbes.Add(player);
+ AttachedObject._visibleInProbeSnapshot = _visibleToProbes.Any(x => x != null);
+ return;
+ }
+ }
+
+ if (_visibleToProbes.Contains(player))
+ {
+ _visibleToProbes.Remove(player);
+ }
+
+ AttachedObject._visibleInProbeSnapshot = _visibleToProbes.Any(x => x != null);
+ }
+
+ public void OnRemoveProbeSnapshot(PlayerInfo player)
+ {
+ if (_visibleToProbes.Contains(player))
+ {
+ _visibleToProbes.Remove(player);
+ }
+
+ AttachedObject._visibleInProbeSnapshot = _visibleToProbes.Any(x => x != null);
+ }
+
public override void DisplayLines()
{
if (AttachedObject == null)
diff --git a/QSB/RoastingSync/QSBMarshmallow.cs b/QSB/RoastingSync/QSBMarshmallow.cs
index 747770c8b..b6f01a44f 100644
--- a/QSB/RoastingSync/QSBMarshmallow.cs
+++ b/QSB/RoastingSync/QSBMarshmallow.cs
@@ -1,9 +1,11 @@
using QSB.Player;
+using QSB.Utility;
using System.Linq;
using UnityEngine;
namespace QSB.RoastingSync;
+[UsedInUnityProject]
public class QSBMarshmallow : MonoBehaviour
{
public const float RAW_TOASTED_FRACTION = 0.2f;
@@ -51,6 +53,8 @@ public void SpawnMallow()
_mallowRenderer.enabled = true;
_mallowState = Marshmallow.MallowState.Default;
enabled = true;
+
+ _attachedPlayer.AudioController.PlayOneShot(AudioType.ToolMarshmallowReplace);
}
public void Disable()
@@ -66,6 +70,8 @@ public void Extinguish()
{
_fireRenderer.enabled = false;
_mallowState = Marshmallow.MallowState.Default;
+
+ _attachedPlayer.AudioController.PlayOneShot(AudioType.ToolMarshmallowBlowOut);
}
}
@@ -140,6 +146,8 @@ public void Burn()
_toastedFraction = 1f;
_initBurnTime = Time.time;
_mallowState = Marshmallow.MallowState.Burning;
+
+ _attachedPlayer.AudioController.PlayOneShot(AudioType.ToolMarshmallowIgnite);
}
}
diff --git a/QSB/SaveSync/QSBStandaloneProfileManager.cs b/QSB/SaveSync/QSBStandaloneProfileManager.cs
index 06ef63f6f..b78c5e9f6 100644
--- a/QSB/SaveSync/QSBStandaloneProfileManager.cs
+++ b/QSB/SaveSync/QSBStandaloneProfileManager.cs
@@ -278,7 +278,7 @@ private void LoadSaveFilesFromProfiles()
Debug.LogError("Could not find graphics settings for " + profile.profileName);
}
- if (inputJSON == "")
+ if (string.IsNullOrEmpty(inputJSON))
{
profile.brokenRebindingData = File.Exists(inputsPath);
inputJSON = ((InputManager)OWInput.SharedInputManager).commandManager.DefaultInputActions.ToJson();
diff --git a/QSB/ShipSync/Messages/FlyShipMessage.cs b/QSB/ShipSync/Messages/FlyShipMessage.cs
index 144efd708..e1083c139 100644
--- a/QSB/ShipSync/Messages/FlyShipMessage.cs
+++ b/QSB/ShipSync/Messages/FlyShipMessage.cs
@@ -39,10 +39,12 @@ public override void OnReceiveRemote()
var shipCockpitController = ShipManager.Instance.CockpitController;
if (Data)
{
+ QSBPlayerManager.GetPlayer(From)?.AudioController?.PlayOneShot(AudioType.ShipCockpitBuckleUp);
shipCockpitController._interactVolume.DisableInteraction();
}
else
{
+ QSBPlayerManager.GetPlayer(From)?.AudioController?.PlayOneShot(AudioType.ShipCockpitUnbuckle);
shipCockpitController._interactVolume.EnableInteraction();
}
}
diff --git a/QSB/ShipSync/Messages/LegDetachMessage.cs b/QSB/ShipSync/Messages/LegDetachMessage.cs
index cc3a9608b..f0d87dd04 100644
--- a/QSB/ShipSync/Messages/LegDetachMessage.cs
+++ b/QSB/ShipSync/Messages/LegDetachMessage.cs
@@ -1,5 +1,4 @@
using QSB.Messaging;
-using QSB.Patches;
using QSB.ShipSync.WorldObjects;
namespace QSB.ShipSync.Messages;
@@ -7,5 +6,5 @@ namespace QSB.ShipSync.Messages;
internal class LegDetachMessage : QSBWorldObjectMessage
{
public override void OnReceiveRemote() =>
- QSBPatch.RemoteCall(WorldObject.AttachedObject.Detach);
+ WorldObject.AttachedObject.Detach();
}
diff --git a/QSB/ShipSync/Messages/ModuleDetachMessage.cs b/QSB/ShipSync/Messages/ModuleDetachMessage.cs
index e47c242c4..b7ab2e811 100644
--- a/QSB/ShipSync/Messages/ModuleDetachMessage.cs
+++ b/QSB/ShipSync/Messages/ModuleDetachMessage.cs
@@ -1,5 +1,4 @@
using QSB.Messaging;
-using QSB.Patches;
using QSB.ShipSync.WorldObjects;
namespace QSB.ShipSync.Messages;
@@ -7,5 +6,5 @@ namespace QSB.ShipSync.Messages;
internal class ModuleDetachMessage : QSBWorldObjectMessage
{
public override void OnReceiveRemote() =>
- QSBPatch.RemoteCall(WorldObject.AttachedObject.Detach);
+ WorldObject.AttachedObject.Detach();
}
diff --git a/QSB/ShipSync/Messages/ShipIgnitionMessage.cs b/QSB/ShipSync/Messages/ShipIgnitionMessage.cs
new file mode 100644
index 000000000..ff7bdd181
--- /dev/null
+++ b/QSB/ShipSync/Messages/ShipIgnitionMessage.cs
@@ -0,0 +1,49 @@
+using QSB.Messaging;
+using QSB.Player;
+using QSB.Player.TransformSync;
+using static QSB.ShipSync.Messages.ShipIgnitionMessage;
+
+namespace QSB.ShipSync.Messages;
+
+internal class ShipIgnitionMessage : QSBMessage
+{
+ 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 (PlayerTransformSync.LocalInstance && 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;
+ }
+ }
+}
diff --git a/QSB/ShipSync/Messages/ShipLightMessage.cs b/QSB/ShipSync/Messages/ShipLightMessage.cs
index 4e52bafb0..e81fdec04 100644
--- a/QSB/ShipSync/Messages/ShipLightMessage.cs
+++ b/QSB/ShipSync/Messages/ShipLightMessage.cs
@@ -1,5 +1,4 @@
using QSB.Messaging;
-using QSB.Patches;
using QSB.ShipSync.WorldObjects;
namespace QSB.ShipSync.Messages;
@@ -9,5 +8,5 @@ internal class ShipLightMessage : QSBWorldObjectMessage
public ShipLightMessage(bool on) : base(on) { }
public override void OnReceiveRemote() =>
- QSBPatch.RemoteCall(() => WorldObject.AttachedObject.SetOn(Data));
+ WorldObject.AttachedObject.SetOn(Data);
}
diff --git a/QSB/ShipSync/Patches/ShipAudioPatches.cs b/QSB/ShipSync/Patches/ShipAudioPatches.cs
new file mode 100644
index 000000000..1e76d57ab
--- /dev/null
+++ b/QSB/ShipSync/Patches/ShipAudioPatches.cs
@@ -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;
+ }
+ }
+}
diff --git a/QSB/ShipSync/Patches/ShipFlameWashPatches.cs b/QSB/ShipSync/Patches/ShipFlameWashPatches.cs
index ddf2eac80..525c2c380 100644
--- a/QSB/ShipSync/Patches/ShipFlameWashPatches.cs
+++ b/QSB/ShipSync/Patches/ShipFlameWashPatches.cs
@@ -1,4 +1,6 @@
using HarmonyLib;
+using QSB.ModelShip;
+using QSB.ModelShip.TransformSync;
using QSB.Patches;
using QSB.Player;
using QSB.ShipSync.TransformSync;
@@ -41,16 +43,34 @@ public static bool GetThrustFraction(ThrusterFlameController __instance, ref flo
[HarmonyPatch(typeof(ThrusterWashController), nameof(ThrusterWashController.Update))]
public static bool Update(ThrusterWashController __instance)
{
- if (ShipThrusterManager.ShipWashController != __instance)
+ var isShip = ShipThrusterManager.ShipWashController == __instance;
+ var isModelShip = ModelShipThrusterManager.ThrusterWashController == __instance;
+
+ if (!isShip && !isModelShip)
{
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 aboveGround = false;
- var localAcceleration = QSBPlayerManager.LocalPlayer.FlyingShip
- ? __instance._thrusterModel.GetLocalAcceleration()
- : ShipTransformSync.LocalInstance.ThrusterVariableSyncer.AccelerationSyncer.Value;
var emissionScale = __instance._emissionThrusterScale.Evaluate(localAcceleration.y);
if (emissionScale > 0f)
{
diff --git a/QSB/ShipSync/ShipCustomAttach.cs b/QSB/ShipSync/ShipCustomAttach.cs
index e8106d096..2d6a0afb8 100644
--- a/QSB/ShipSync/ShipCustomAttach.cs
+++ b/QSB/ShipSync/ShipCustomAttach.cs
@@ -19,9 +19,14 @@ public class ShipCustomAttach : MonoBehaviour
private PlayerAttachPoint _playerAttachPoint;
+ ///
+ /// uses a static field instead of a persistent condition cuz those are synced
+ ///
+ private static bool _tutorialPrompt = true;
+
private void Awake()
{
- Locator.GetPromptManager().AddScreenPrompt(_attachPrompt, PromptPosition.UpperRight);
+ Locator.GetPromptManager().AddScreenPrompt(_attachPrompt, _tutorialPrompt ? PromptPosition.Center : PromptPosition.UpperRight);
Locator.GetPromptManager().AddScreenPrompt(_detachPrompt, PromptPosition.UpperRight);
_playerAttachPoint = gameObject.AddComponent();
@@ -34,8 +39,8 @@ private void OnDestroy()
{
if (Locator.GetPromptManager())
{
- Locator.GetPromptManager().RemoveScreenPrompt(_attachPrompt, PromptPosition.UpperRight);
- Locator.GetPromptManager().RemoveScreenPrompt(_detachPrompt, PromptPosition.UpperRight);
+ Locator.GetPromptManager().RemoveScreenPrompt(_attachPrompt);
+ Locator.GetPromptManager().RemoveScreenPrompt(_detachPrompt);
}
}
@@ -43,6 +48,11 @@ private void Update()
{
_attachPrompt.SetVisibility(false);
_detachPrompt.SetVisibility(false);
+ // dont show prompt if paused or something
+ if (!OWInput.IsInputMode(InputMode.Character))
+ {
+ return;
+ }
var attachedToUs = _playerAttachPoint.enabled;
_detachPrompt.SetVisibility(attachedToUs);
@@ -78,6 +88,13 @@ private void Update()
transform.position = Locator.GetPlayerTransform().position;
_playerAttachPoint.AttachPlayer();
ShipManager.Instance.CockpitController._shipAudioController.PlayBuckle();
+
+ if (_tutorialPrompt)
+ {
+ _tutorialPrompt = false;
+ Locator.GetPromptManager().RemoveScreenPrompt(_attachPrompt);
+ Locator.GetPromptManager().AddScreenPrompt(_attachPrompt, PromptPosition.UpperRight);
+ }
}
}
}
diff --git a/QSB/ShipSync/ShipManager.cs b/QSB/ShipSync/ShipManager.cs
index c85a27516..405059c4c 100644
--- a/QSB/ShipSync/ShipManager.cs
+++ b/QSB/ShipSync/ShipManager.cs
@@ -22,6 +22,7 @@ internal class ShipManager : WorldObjectManager
public static ShipManager Instance;
+ public ShipThrusterAudio ShipThrusterAudio;
public InteractZone HatchInteractZone;
public HatchController HatchController;
public ShipTractorBeamSwitch ShipTractorBeam;
@@ -92,6 +93,7 @@ public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken
return;
}
+ ShipThrusterAudio = QSBWorldSync.GetUnityObject();
HatchInteractZone = HatchController.GetComponent();
ShipTractorBeam = QSBWorldSync.GetUnityObject();
CockpitController = QSBWorldSync.GetUnityObject();
@@ -136,6 +138,10 @@ public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken
QSBWorldSync.Init();
QSBWorldSync.Init();
+
+ // Make sure all relevant audio sources are 3D
+ QSBWorldSync.GetUnityObject()._ignitionSource.spatialBlend = 1f;
+ QSBWorldSync.GetUnityObject()._rotationalSource.spatialBlend = 1f;
}
public override void UnbuildWorldObjects()
diff --git a/QSB/ShipSync/ShipThrusterVariableSyncer.cs b/QSB/ShipSync/ShipThrusterVariableSyncer.cs
index 79053946a..c8e316730 100644
--- a/QSB/ShipSync/ShipThrusterVariableSyncer.cs
+++ b/QSB/ShipSync/ShipThrusterVariableSyncer.cs
@@ -1,6 +1,7 @@
using Mirror;
using QSB.Player;
using QSB.Utility.VariableSync;
+using QSB.WorldSync;
using UnityEngine;
namespace QSB.ShipSync;
@@ -10,10 +11,12 @@ public class ShipThrusterVariableSyncer : NetworkBehaviour
public Vector3VariableSyncer AccelerationSyncer;
private ShipThrusterModel _thrusterModel;
+ private ShipThrusterAudio _thrusterAudio;
public void Init()
{
_thrusterModel = Locator.GetShipBody().GetComponent();
+ _thrusterAudio = Locator.GetShipBody().GetComponentInChildren();
}
public void Update()
@@ -33,6 +36,8 @@ public void Update()
item.OnStopTranslationalThrust();
}
+ _thrusterAudio.OnStopTranslationalThrust();
+
ShipThrusterManager.ShipWashController.OnStopTranslationalThrust();
}
else
@@ -42,6 +47,8 @@ public void Update()
item.OnStartTranslationalThrust();
}
+ _thrusterAudio.OnStartTranslationalThrust();
+
ShipThrusterManager.ShipWashController.OnStartTranslationalThrust();
}
}
diff --git a/QSB/ShipSync/TransformSync/ShipLegTransformSync.cs b/QSB/ShipSync/TransformSync/ShipLegTransformSync.cs
index 3efd0912d..39e4db8fe 100644
--- a/QSB/ShipSync/TransformSync/ShipLegTransformSync.cs
+++ b/QSB/ShipSync/TransformSync/ShipLegTransformSync.cs
@@ -15,7 +15,11 @@ protected override bool CheckValid()
=> AttachedTransform
&& base.CheckValid();
- protected override bool CheckReady() => base.CheckReady() && _qsbModule.AttachedObject.isDetached;
+ protected override bool CheckReady()
+ => base.CheckReady()
+ && _qsbModule != null // not sure how either of these can be null, but i guess better safe than sorry
+ && _qsbModule.AttachedObject != null
+ && _qsbModule.AttachedObject.isDetached;
protected override bool UseInterpolation => true;
diff --git a/QSB/ShipSync/TransformSync/ShipModuleTransformSync.cs b/QSB/ShipSync/TransformSync/ShipModuleTransformSync.cs
index 1f655cd71..72cb2d2df 100644
--- a/QSB/ShipSync/TransformSync/ShipModuleTransformSync.cs
+++ b/QSB/ShipSync/TransformSync/ShipModuleTransformSync.cs
@@ -15,7 +15,11 @@ protected override bool CheckValid()
=> AttachedTransform
&& base.CheckValid();
- protected override bool CheckReady() => base.CheckReady() && _qsbModule.AttachedObject.isDetached;
+ protected override bool CheckReady()
+ => base.CheckReady()
+ && _qsbModule != null // not sure how either of these can be null, but i guess better safe than sorry
+ && _qsbModule.AttachedObject != null
+ && _qsbModule.AttachedObject.isDetached;
protected override bool UseInterpolation => true;
diff --git a/QSB/ShipSync/TransformSync/ShipTransformSync.cs b/QSB/ShipSync/TransformSync/ShipTransformSync.cs
index 29d6597ba..772bc9952 100644
--- a/QSB/ShipSync/TransformSync/ShipTransformSync.cs
+++ b/QSB/ShipSync/TransformSync/ShipTransformSync.cs
@@ -56,10 +56,10 @@ protected override void ApplyToAttached()
return;
}
- var targetPos = ReferenceTransform.FromRelPos(transform.position);
- var targetRot = ReferenceTransform.FromRelRot(transform.rotation);
+ var targetPos = ReferenceTransform.FromRelPos(UseInterpolation ? SmoothPosition : transform.position);
+ var targetRot = ReferenceTransform.FromRelRot(UseInterpolation ? SmoothRotation : transform.rotation);
- if (PlayerState.IsInsideShip())
+ if (IsInsideShip)
{
if (Time.unscaledTime >= _lastSetPositionTime + ForcePositionAfterTime)
{
@@ -98,5 +98,6 @@ private static void SetVelocity(OWRigidbody rigidbody, Vector3 newVelocity)
rigidbody._currentVelocity = newVelocity;
}
- protected override bool UseInterpolation => false;
+ private static bool IsInsideShip => PlayerState.IsInsideShip();
+ protected override bool UseInterpolation => !IsInsideShip;
}
diff --git a/QSB/Syncs/QSBNetworkTransformChild.cs b/QSB/Syncs/QSBNetworkTransformChild.cs
index b96fc084b..b3336e011 100644
--- a/QSB/Syncs/QSBNetworkTransformChild.cs
+++ b/QSB/Syncs/QSBNetworkTransformChild.cs
@@ -4,6 +4,7 @@
namespace QSB.Syncs;
+[UsedInUnityProject]
public class QSBNetworkTransformChild : QSBNetworkBehaviour
{
public Transform Target;
diff --git a/QSB/TimeSync/Messages/SetSecondsRemainingMessage.cs b/QSB/TimeSync/Messages/SetSecondsRemainingMessage.cs
index c8fbe2740..fd99e1608 100644
--- a/QSB/TimeSync/Messages/SetSecondsRemainingMessage.cs
+++ b/QSB/TimeSync/Messages/SetSecondsRemainingMessage.cs
@@ -1,5 +1,4 @@
using QSB.Messaging;
-using QSB.Patches;
namespace QSB.TimeSync.Messages;
@@ -9,5 +8,5 @@ namespace QSB.TimeSync.Messages;
public class SetSecondsRemainingMessage : QSBMessage
{
public SetSecondsRemainingMessage(float secondsRemaining) : base(secondsRemaining) => To = 0;
- public override void OnReceiveRemote() => QSBPatch.RemoteCall(() => TimeLoop.SetSecondsRemaining(Data));
+ public override void OnReceiveRemote() => TimeLoop.SetSecondsRemaining(Data);
}
diff --git a/QSB/TimeSync/Patches/TimePatches.cs b/QSB/TimeSync/Patches/TimePatches.cs
index 2c21229a3..24eab5fa0 100644
--- a/QSB/TimeSync/Patches/TimePatches.cs
+++ b/QSB/TimeSync/Patches/TimePatches.cs
@@ -37,10 +37,10 @@ is OWTime.PauseType.Initializing
or OWTime.PauseType.Streaming
or OWTime.PauseType.Loading;
- [HarmonyPrefix]
+ [HarmonyPostfix]
[HarmonyPatch(typeof(SubmitActionSkipToNextLoop), nameof(SubmitActionSkipToNextLoop.AdvanceToNewTimeLoop))]
- public static bool StopMeditation()
- => false;
+ public static void PreventMeditationSoftlock()
+ => OWInput.ChangeInputMode(InputMode.Character);
}
internal class ClientTimePatches : QSBPatch
diff --git a/QSB/TimeSync/StopMeditation.cs b/QSB/TimeSync/StopMeditation.cs
index 4a7dddc18..4187670db 100644
--- a/QSB/TimeSync/StopMeditation.cs
+++ b/QSB/TimeSync/StopMeditation.cs
@@ -1,23 +1,8 @@
-using UnityEngine;
+using QSB.Utility;
+using UnityEngine;
namespace QSB.TimeSync;
-public class StopMeditation : MonoBehaviour
-{
- public void Init()
- {
- var menuManager = Locator.GetSceneMenuManager();
-
- if (menuManager == null)
- {
- return;
- }
-
- if (menuManager._pauseMenu == null || menuManager.pauseMenu._skipToNextLoopButton == null)
- {
- return;
- }
-
- menuManager.pauseMenu._skipToNextLoopButton.SetActive(false);
- }
-}
\ No newline at end of file
+// TODO remove from unity project eventually
+[UsedInUnityProject]
+public class StopMeditation : MonoBehaviour { }
diff --git a/QSB/TimeSync/TimeSyncUI.cs b/QSB/TimeSync/TimeSyncUI.cs
index 46706b1e6..d57833e49 100644
--- a/QSB/TimeSync/TimeSyncUI.cs
+++ b/QSB/TimeSync/TimeSyncUI.cs
@@ -36,6 +36,9 @@ private void OnUniverseSceneLoad(OWScene oldScene, OWScene newScene)
_canvas = obj._canvas;
_text = obj._text;
_canvas.enabled = false;
+
+ var langController = QSBWorldSync.GetUnityObject().transform.GetChild(0).GetComponent();
+ langController.AddTextElement(_text);
}
public void OnDestroy()
diff --git a/QSB/TimeSync/WakeUpSync.cs b/QSB/TimeSync/WakeUpSync.cs
index 9b8ef5894..8366a9958 100644
--- a/QSB/TimeSync/WakeUpSync.cs
+++ b/QSB/TimeSync/WakeUpSync.cs
@@ -5,7 +5,6 @@
using QSB.DeathSync;
using QSB.Inputs;
using QSB.Messaging;
-using QSB.Patches;
using QSB.Player;
using QSB.Player.Messages;
using QSB.TimeSync.Messages;
@@ -16,6 +15,10 @@
namespace QSB.TimeSync;
+///
+/// BUG: this runs on remote players = BAD! can we move this off of network player?
+///
+[UsedInUnityProject]
public class WakeUpSync : NetworkBehaviour
{
public static WakeUpSync LocalInstance { get; private set; }
@@ -117,7 +120,6 @@ private void Init()
{
new RequestStateResyncMessage().Send();
CurrentState = State.Loaded;
- gameObject.GetRequiredComponent().Init();
if (isServer)
{
SendServerTime();
@@ -146,7 +148,11 @@ public void OnClientReceiveMessage(float time, int count, float secondsRemaining
{
_serverTime = time;
_serverLoopCount = count;
- QSBPatch.RemoteCall(() => TimeLoop.SetSecondsRemaining(secondsRemaining));
+ // prevents accidental supernova at start of loop
+ if (_serverLoopCount == PlayerData.LoadLoopCount())
+ {
+ TimeLoop.SetSecondsRemaining(secondsRemaining);
+ }
}
private void WakeUpOrSleep()
diff --git a/QSB/Tools/FlashlightTool/QSBFlashlight.cs b/QSB/Tools/FlashlightTool/QSBFlashlight.cs
index 6f1b60dc1..440c24f67 100644
--- a/QSB/Tools/FlashlightTool/QSBFlashlight.cs
+++ b/QSB/Tools/FlashlightTool/QSBFlashlight.cs
@@ -4,6 +4,7 @@
namespace QSB.Tools.FlashlightTool;
+[UsedInUnityProject]
public class QSBFlashlight : MonoBehaviour, ILightSource
{
[SerializeField]
diff --git a/QSB/Tools/ProbeLauncherTool/Messages/ChangeModeMessage.cs b/QSB/Tools/ProbeLauncherTool/Messages/ChangeModeMessage.cs
new file mode 100644
index 000000000..169560f22
--- /dev/null
+++ b/QSB/Tools/ProbeLauncherTool/Messages/ChangeModeMessage.cs
@@ -0,0 +1,11 @@
+using QSB.Messaging;
+using QSB.Tools.ProbeLauncherTool.WorldObjects;
+
+namespace QSB.Tools.ProbeLauncherTool.Messages;
+
+internal class ChangeModeMessage : QSBWorldObjectMessage
+{
+ public ChangeModeMessage() : base() { }
+
+ public override void OnReceiveRemote() => WorldObject.ChangeMode();
+}
diff --git a/QSB/Tools/ProbeLauncherTool/Messages/LaunchProbeMessage.cs b/QSB/Tools/ProbeLauncherTool/Messages/LaunchProbeMessage.cs
index c0988b792..535c1a2f0 100644
--- a/QSB/Tools/ProbeLauncherTool/Messages/LaunchProbeMessage.cs
+++ b/QSB/Tools/ProbeLauncherTool/Messages/LaunchProbeMessage.cs
@@ -3,9 +3,9 @@
namespace QSB.Tools.ProbeLauncherTool.Messages;
-internal class LaunchProbeMessage : QSBWorldObjectMessage
+internal class LaunchProbeMessage : QSBWorldObjectMessage
{
- 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);
}
\ No newline at end of file
diff --git a/QSB/Tools/ProbeLauncherTool/Messages/PlayerLauncherChangeModeMessage.cs b/QSB/Tools/ProbeLauncherTool/Messages/PlayerLauncherChangeModeMessage.cs
new file mode 100644
index 000000000..14fd7da71
--- /dev/null
+++ b/QSB/Tools/ProbeLauncherTool/Messages/PlayerLauncherChangeModeMessage.cs
@@ -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();
+ }
+}
diff --git a/QSB/Tools/ProbeLauncherTool/Messages/PlayerLauncherTakeSnapshotMessage.cs b/QSB/Tools/ProbeLauncherTool/Messages/PlayerLauncherTakeSnapshotMessage.cs
new file mode 100644
index 000000000..93b090d0c
--- /dev/null
+++ b/QSB/Tools/ProbeLauncherTool/Messages/PlayerLauncherTakeSnapshotMessage.cs
@@ -0,0 +1,22 @@
+using QSB.Messaging;
+using QSB.Player;
+using QSB.QuantumSync;
+using QSB.WorldSync;
+
+namespace QSB.Tools.ProbeLauncherTool.Messages;
+
+internal class PlayerLauncherTakeSnapshotMessage : QSBMessage
+{
+ public PlayerLauncherTakeSnapshotMessage(ProbeCamera.ID cameraId) : base(cameraId) { }
+
+ public override bool ShouldReceive => QSBWorldSync.AllObjectsReady;
+
+ public override void OnReceiveLocal() => QuantumManager.OnTakeProbeSnapshot(QSBPlayerManager.LocalPlayer, Data);
+
+ public override void OnReceiveRemote()
+ {
+ var player = QSBPlayerManager.GetPlayer(From);
+ player.ProbeLauncherTool.TakeSnapshot();
+ QuantumManager.OnTakeProbeSnapshot(player, Data);
+ }
+}
diff --git a/QSB/Tools/ProbeLauncherTool/Messages/RemoveSnapshotMessage.cs b/QSB/Tools/ProbeLauncherTool/Messages/RemoveSnapshotMessage.cs
new file mode 100644
index 000000000..3d75101bd
--- /dev/null
+++ b/QSB/Tools/ProbeLauncherTool/Messages/RemoveSnapshotMessage.cs
@@ -0,0 +1,26 @@
+using QSB.Messaging;
+using QSB.Player;
+using QSB.Player.TransformSync;
+using QSB.QuantumSync;
+
+namespace QSB.Tools.ProbeLauncherTool.Messages;
+
+internal class RemoveSnapshotMessage : QSBMessage
+{
+ static RemoveSnapshotMessage()
+ => GlobalMessenger.AddListener(OWEvents.ProbeSnapshotRemoved, Handle);
+
+ private static void Handle()
+ {
+ if (PlayerTransformSync.LocalInstance)
+ {
+ new RemoveSnapshotMessage().Send();
+ }
+ }
+
+ public override void OnReceiveLocal()
+ => QuantumManager.OnRemoveProbeSnapshot(QSBPlayerManager.LocalPlayer);
+
+ public override void OnReceiveRemote()
+ => QuantumManager.OnRemoveProbeSnapshot(QSBPlayerManager.GetPlayer(From));
+}
diff --git a/QSB/Tools/ProbeLauncherTool/Messages/StationaryProbeLauncherMessage.cs b/QSB/Tools/ProbeLauncherTool/Messages/StationaryProbeLauncherMessage.cs
new file mode 100644
index 000000000..dcb5fe07a
--- /dev/null
+++ b/QSB/Tools/ProbeLauncherTool/Messages/StationaryProbeLauncherMessage.cs
@@ -0,0 +1,11 @@
+using QSB.Messaging;
+using QSB.Tools.ProbeLauncherTool.WorldObjects;
+
+namespace QSB.Tools.ProbeLauncherTool.Messages;
+
+public class StationaryProbeLauncherMessage : QSBWorldObjectMessage
+{
+ public StationaryProbeLauncherMessage(bool inUse, uint userID) : base((inUse, userID)) { }
+
+ public override void OnReceiveRemote() => WorldObject.OnRemoteUseStateChanged(Data.Item1, Data.Item2);
+}
diff --git a/QSB/Tools/ProbeLauncherTool/Messages/TakeSnapshotMessage.cs b/QSB/Tools/ProbeLauncherTool/Messages/TakeSnapshotMessage.cs
new file mode 100644
index 000000000..f0d1e77cf
--- /dev/null
+++ b/QSB/Tools/ProbeLauncherTool/Messages/TakeSnapshotMessage.cs
@@ -0,0 +1,17 @@
+using QSB.Messaging;
+using QSB.Player;
+using QSB.QuantumSync;
+using QSB.Tools.ProbeLauncherTool.WorldObjects;
+
+namespace QSB.Tools.ProbeLauncherTool.Messages;
+
+internal class TakeSnapshotMessage : QSBWorldObjectMessage
+{
+ public TakeSnapshotMessage(ProbeCamera.ID cameraId) : base(cameraId) { }
+
+ public override void OnReceiveLocal()
+ => QuantumManager.OnTakeProbeSnapshot(QSBPlayerManager.LocalPlayer, Data);
+
+ public override void OnReceiveRemote()
+ => WorldObject.TakeSnapshot(QSBPlayerManager.GetPlayer(From), Data);
+}
diff --git a/QSB/Tools/ProbeLauncherTool/Patches/LauncherPatches.cs b/QSB/Tools/ProbeLauncherTool/Patches/LauncherPatches.cs
index 66e165375..7b04554e0 100644
--- a/QSB/Tools/ProbeLauncherTool/Patches/LauncherPatches.cs
+++ b/QSB/Tools/ProbeLauncherTool/Patches/LauncherPatches.cs
@@ -89,4 +89,57 @@ public static bool ProbeLauncherEffects_PlayLaunchClip(ProbeLauncherEffects __in
__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()
+ ?.GetWorldObject()
+ ?.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()
+ ?.GetWorldObject()
+ ?.SendMessage(new TakeSnapshotMessage());
+ }
+ else
+ {
+ new PlayerLauncherTakeSnapshotMessage().Send();
+ }
+ }*/
+
+ [HarmonyPostfix]
+ [HarmonyPatch(typeof(ProbeLauncher), nameof(ProbeLauncher.TakeSnapshotWithCamera))]
+ public static void TakeSnapshotWithCamera(ProbeLauncher __instance, ProbeCamera camera)
+ {
+ DebugLog.DebugWrite($"TakeSnapshotWithCamera - cameraid:{camera.GetID()}");
+ if (__instance != QSBPlayerManager.LocalPlayer.LocalProbeLauncher)
+ {
+ DebugLog.DebugWrite($"- not local launcher");
+ __instance
+ ?.GetWorldObject()
+ ?.SendMessage(new TakeSnapshotMessage(camera.GetID()));
+ }
+ else
+ {
+ DebugLog.DebugWrite($"- local launcher");
+ new PlayerLauncherTakeSnapshotMessage(camera.GetID()).Send();
+ }
+ }
}
\ No newline at end of file
diff --git a/QSB/Tools/ProbeLauncherTool/Patches/StationaryProbeLauncherPatches.cs b/QSB/Tools/ProbeLauncherTool/Patches/StationaryProbeLauncherPatches.cs
new file mode 100644
index 000000000..cb07642a6
--- /dev/null
+++ b/QSB/Tools/ProbeLauncherTool/Patches/StationaryProbeLauncherPatches.cs
@@ -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().OnLocalUseStateChanged(false);
+}
diff --git a/QSB/Tools/ProbeLauncherTool/ProbeLauncherManager.cs b/QSB/Tools/ProbeLauncherTool/ProbeLauncherManager.cs
index cff58709d..761232559 100644
--- a/QSB/Tools/ProbeLauncherTool/ProbeLauncherManager.cs
+++ b/QSB/Tools/ProbeLauncherTool/ProbeLauncherManager.cs
@@ -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(typeof(PlayerProbeLauncher));
+ QSBWorldSync.Init(typeof(PlayerProbeLauncher), typeof(StationaryProbeLauncher));
+ // Using ProbeLaunchers here so we can do inheritance, put only applying it to found StationaryProbeLauncher
+ QSBWorldSync.Init(QSBWorldSync.GetUnityObjects().SortDeterministic());
if (scene == OWScene.SolarSystem)
{
QSBWorldSync.Init(new[]
diff --git a/QSB/Tools/ProbeLauncherTool/QSBProbeLauncherTool.cs b/QSB/Tools/ProbeLauncherTool/QSBProbeLauncherTool.cs
index 244b6b0d0..afafbd11f 100644
--- a/QSB/Tools/ProbeLauncherTool/QSBProbeLauncherTool.cs
+++ b/QSB/Tools/ProbeLauncherTool/QSBProbeLauncherTool.cs
@@ -1,19 +1,26 @@
-using UnityEngine;
+using QSB.Utility;
+using UnityEngine;
namespace QSB.Tools.ProbeLauncherTool;
+[UsedInUnityProject]
public class QSBProbeLauncherTool : QSBTool
{
public GameObject PreLaunchProbeProxy;
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 +34,28 @@ public void LaunchProbe()
{
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();
+ }
}
\ No newline at end of file
diff --git a/QSB/Tools/ProbeLauncherTool/VariableSync/StationaryProbeLauncherVariableSyncer.cs b/QSB/Tools/ProbeLauncherTool/VariableSync/StationaryProbeLauncherVariableSyncer.cs
new file mode 100644
index 000000000..b857afec4
--- /dev/null
+++ b/QSB/Tools/ProbeLauncherTool/VariableSync/StationaryProbeLauncherVariableSyncer.cs
@@ -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();
+ launcher._degreesY = reader.Read();
+ launcher._audioSource.SetLocalVolume(reader.Read());
+
+ // 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;
+}
diff --git a/QSB/Tools/ProbeLauncherTool/WorldObjects/QSBProbeLauncher.cs b/QSB/Tools/ProbeLauncherTool/WorldObjects/QSBProbeLauncher.cs
index 80742acad..3c714a24b 100644
--- a/QSB/Tools/ProbeLauncherTool/WorldObjects/QSBProbeLauncher.cs
+++ b/QSB/Tools/ProbeLauncherTool/WorldObjects/QSBProbeLauncher.cs
@@ -1,13 +1,20 @@
using Cysharp.Threading.Tasks;
using QSB.Messaging;
+using QSB.Player;
+using QSB.QuantumSync;
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
{
+ private uint _probeOwnerID = uint.MaxValue;
+ protected QSBSurveyorProbe LaunchedProbe { get; private set; }
+
public override async UniTask Init(CancellationToken ct) =>
AttachedObject.OnLaunchProbe += OnLaunchProbe;
@@ -16,21 +23,27 @@ public override void OnRemoval() =>
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 +57,13 @@ public void RetrieveProbe(bool playEffects)
}
}
- 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 +78,20 @@ public void LaunchProbe(bool playEffects)
AttachedObject._effects.PlayLaunchParticles(false);
}
}
+
+ public void ChangeMode()
+ {
+ AttachedObject._effects.PlayChangeModeClip();
+ }
+
+ public void TakeSnapshot(PlayerInfo player, ProbeCamera.ID cameraId)
+ {
+ // 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();
+
+ QuantumManager.OnTakeProbeSnapshot(player, cameraId);
+ }
}
\ No newline at end of file
diff --git a/QSB/Tools/ProbeLauncherTool/WorldObjects/QSBStationaryProbeLauncher.cs b/QSB/Tools/ProbeLauncherTool/WorldObjects/QSBStationaryProbeLauncher.cs
new file mode 100644
index 000000000..e170e4a04
--- /dev/null
+++ b/QSB/Tools/ProbeLauncherTool/WorldObjects/QSBStationaryProbeLauncher.cs
@@ -0,0 +1,116 @@
+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;
+
+// TODO John - i think this can be simplified, i might be able to remove the variable syncer
+// TODO: on player leave? idk if this will be needed if i ever simplify this
+public class QSBStationaryProbeLauncher : QSBProbeLauncher, ILinkedWorldObject
+{
+ 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();
+ }
+ }
+}
diff --git a/QSB/Tools/ProbeTool/Messages/RotateProbeMessage.cs b/QSB/Tools/ProbeTool/Messages/RotateProbeMessage.cs
new file mode 100644
index 000000000..50c7bc42e
--- /dev/null
+++ b/QSB/Tools/ProbeTool/Messages/RotateProbeMessage.cs
@@ -0,0 +1,32 @@
+using QSB.Messaging;
+using QSB.Player;
+using QSB.WorldSync;
+using UnityEngine;
+
+namespace QSB.Tools.ProbeTool.Messages;
+
+internal class RotateProbeMessage : QSBMessage<(RotationType rotationType, Vector2 cameraRotation)>
+{
+ public RotateProbeMessage(RotationType rotationType, Vector2 cameraRotation) : base((rotationType, cameraRotation)) { }
+
+ public override bool ShouldReceive => QSBWorldSync.AllObjectsReady;
+
+ public override void OnReceiveRemote()
+ {
+ var playerProbe = QSBPlayerManager.GetPlayer(From).Probe;
+ var rotatingCamera = playerProbe.GetRotatingCamera();
+
+ if (Data.rotationType == RotationType.Horizontal)
+ {
+ rotatingCamera.RotateHorizontal(Data.cameraRotation.x);
+ }
+ else if (Data.rotationType == RotationType.Vertical)
+ {
+ rotatingCamera.RotateVertical(Data.cameraRotation.y);
+ }
+ else
+ {
+ rotatingCamera.ResetRotation();
+ }
+ }
+}
diff --git a/QSB/Tools/ProbeTool/Patches/ProbeToolPatches.cs b/QSB/Tools/ProbeTool/Patches/ProbeToolPatches.cs
index db5c5fba0..fcfc9d821 100644
--- a/QSB/Tools/ProbeTool/Patches/ProbeToolPatches.cs
+++ b/QSB/Tools/ProbeTool/Patches/ProbeToolPatches.cs
@@ -1,5 +1,9 @@
using HarmonyLib;
+using QSB.Messaging;
using QSB.Patches;
+using QSB.Tools.ProbeTool.Messages;
+using QSB.Utility;
+using UnityEngine;
namespace QSB.Tools.ProbeTool.Patches;
@@ -37,4 +41,53 @@ public static bool FixedUpdateOverride(NomaiWarpStreaming __instance)
return false;
}
+
+ [HarmonyPrefix]
+ [HarmonyPatch(typeof(ProbeCamera), nameof(ProbeCamera.RotateHorizontal))]
+ public static bool RotateHorizontal(ProbeCamera __instance, float degrees)
+ {
+ if (__instance._id != ProbeCamera.ID.Rotating)
+ {
+ Debug.LogWarning("Tried to rotate a non-rotating ProbeCamera!", __instance);
+ return false;
+ }
+
+ __instance._cameraRotation.x += degrees;
+ __instance.transform.parent.localRotation = __instance._origParentLocalRotation * Quaternion.AngleAxis(__instance._cameraRotation.x, Vector3.up);
+ __instance.RaiseEvent(nameof(__instance.OnRotateCamera), __instance._cameraRotation);
+ new RotateProbeMessage(RotationType.Horizontal, __instance._cameraRotation).Send();
+ return false;
+ }
+
+ [HarmonyPrefix]
+ [HarmonyPatch(typeof(ProbeCamera), nameof(ProbeCamera.RotateVertical))]
+ public static bool RotateVertical(ProbeCamera __instance, float degrees)
+ {
+ if (__instance._id != ProbeCamera.ID.Rotating)
+ {
+ Debug.LogWarning("Tried to rotate a non-rotating ProbeCamera!", __instance);
+ return false;
+ }
+
+ __instance._cameraRotation.y = Mathf.Clamp(__instance._cameraRotation.y + degrees, -90f, 0f);
+ __instance.transform.localRotation = __instance._origLocalRotation * Quaternion.AngleAxis(__instance._cameraRotation.y, Vector3.right);
+ __instance.RaiseEvent(nameof(__instance.OnRotateCamera), __instance._cameraRotation);
+ new RotateProbeMessage(RotationType.Vertical, __instance._cameraRotation).Send();
+ return false;
+ }
+
+ [HarmonyPrefix]
+ [HarmonyPatch(typeof(ProbeCamera), nameof(ProbeCamera.ResetRotation))]
+ public static bool ResetRotation(ProbeCamera __instance)
+ {
+ if (__instance._id == ProbeCamera.ID.Rotating)
+ {
+ __instance._cameraRotation = Vector2.zero;
+ __instance.transform.localRotation = __instance._origLocalRotation;
+ __instance.transform.parent.localRotation = __instance._origParentLocalRotation;
+ new RotateProbeMessage(RotationType.Reset, __instance._cameraRotation).Send();
+ }
+
+ return false;
+ }
}
\ No newline at end of file
diff --git a/QSB/Tools/ProbeTool/ProbeCreator.cs b/QSB/Tools/ProbeTool/ProbeCreator.cs
index 4a82e84bd..8742ac34f 100644
--- a/QSB/Tools/ProbeTool/ProbeCreator.cs
+++ b/QSB/Tools/ProbeTool/ProbeCreator.cs
@@ -26,7 +26,7 @@ public static Transform CreateProbe(PlayerInfo player)
{
var REMOTE_Probe_Body = Object.Instantiate(GetPrefab());
- var qsbProbe = REMOTE_Probe_Body.GetComponent();
+ var qsbProbe = REMOTE_Probe_Body.GetComponent();
player.Probe = qsbProbe;
qsbProbe.SetOwner(player);
diff --git a/QSB/Tools/ProbeTool/QSBProbeAnimatorController.cs b/QSB/Tools/ProbeTool/QSBProbeAnimatorController.cs
new file mode 100644
index 000000000..7a4915af8
--- /dev/null
+++ b/QSB/Tools/ProbeTool/QSBProbeAnimatorController.cs
@@ -0,0 +1,92 @@
+using UnityEngine;
+
+namespace QSB.Tools.ProbeTool;
+
+[RequireComponent(typeof(Animator))]
+internal class QSBProbeAnimatorController : MonoBehaviour
+{
+ private Animator _animator;
+ private QSBSurveyorProbe _probe;
+
+ [SerializeField]
+ private Transform _centerBone;
+
+ private Quaternion _startCenterBoneRotation = Quaternion.identity;
+ private Quaternion _targetCenterBoneRotation = Quaternion.identity;
+ private Quaternion _currentCenterBoneRotation = Quaternion.identity;
+
+ private float _rotationT;
+
+ private void Awake()
+ {
+ this._animator = base.GetComponent();
+ this._probe = transform.parent.parent.parent.GetRequiredComponent();
+ this._probe.OnLaunchProbe += this.OnProbeFire;
+ this._probe.OnAnchorProbe += this.OnProbeAnchor;
+ this._probe.OnRetrieveProbe += this.OnProbeReset;
+ }
+
+ private void Start()
+ {
+ this._probe.GetRotatingCamera().OnRotateCamera += this.OnRotateCamera;
+ base.enabled = false;
+ }
+
+ private void OnDestroy()
+ {
+ this._probe.OnLaunchProbe -= this.OnProbeFire;
+ this._probe.OnAnchorProbe -= this.OnProbeAnchor;
+ this._probe.OnRetrieveProbe -= this.OnProbeReset;
+ }
+
+ public Quaternion GetCenterBoneLocalRotation()
+ {
+ return this._centerBone.localRotation;
+ }
+
+ private void OnProbeFire()
+ {
+ this._animator.SetTrigger("Fire");
+ }
+
+ private void OnProbeAnchor()
+ {
+ this._animator.SetTrigger("Impact");
+ }
+
+ private void OnProbeReset()
+ {
+ this._animator.SetTrigger("Reset");
+ this._centerBone.localRotation = Quaternion.identity;
+ this._startCenterBoneRotation = Quaternion.identity;
+ this._targetCenterBoneRotation = Quaternion.identity;
+ this._currentCenterBoneRotation = Quaternion.identity;
+ this._rotationT = 0f;
+ base.enabled = false;
+ }
+
+ private void OnRotateCamera(Vector2 rotation)
+ {
+ this._startCenterBoneRotation = this._currentCenterBoneRotation;
+ this._targetCenterBoneRotation = Quaternion.AngleAxis(rotation.x, Vector3.up);
+ this._rotationT = 0f;
+ base.enabled = true;
+ }
+
+ private void LateUpdate()
+ {
+ this._rotationT += 10f * Time.deltaTime;
+ if (this._rotationT >= 1f)
+ {
+ this._rotationT = 1f;
+ this._currentCenterBoneRotation = this._targetCenterBoneRotation;
+ base.enabled = false;
+ }
+ else
+ {
+ float t = Mathf.Sqrt(Mathf.SmoothStep(0f, 1f, this._rotationT));
+ this._currentCenterBoneRotation = Quaternion.Lerp(this._startCenterBoneRotation, this._targetCenterBoneRotation, t);
+ }
+ this._centerBone.localRotation = this._currentCenterBoneRotation;
+ }
+}
diff --git a/QSB/Tools/ProbeTool/QSBProbeCamera.cs b/QSB/Tools/ProbeTool/QSBProbeCamera.cs
new file mode 100644
index 000000000..8efaa1661
--- /dev/null
+++ b/QSB/Tools/ProbeTool/QSBProbeCamera.cs
@@ -0,0 +1,94 @@
+using UnityEngine;
+
+namespace QSB.Tools.ProbeTool;
+
+[RequireComponent(typeof(OWCamera))]
+public class QSBProbeCamera : MonoBehaviour
+{
+ [SerializeField]
+ private ProbeCamera.ID _id;
+
+ private OWCamera _camera;
+ private RenderTexture _snapshotTexture;
+ private NoiseImageEffect _noiseEffect;
+ private static OWCamera _lastSnapshotCamera;
+ private Quaternion _origLocalRotation;
+ private Quaternion _origParentLocalRotation;
+ private Vector2 _cameraRotation = Vector2.zero;
+ private QuantumMoon _quantumMoon;
+ private SandLevelController _sandLevelController;
+
+ public event ProbeCamera.RotateCameraEvent OnRotateCamera;
+
+ private void Awake()
+ {
+ _camera = this.GetRequiredComponent();
+ _camera.enabled = false;
+ _noiseEffect = GetComponent();
+ //this._snapshotTexture = ProbeCamera.GetSharedSnapshotTexture();
+ }
+
+ private void OnDestroy()
+ => _snapshotTexture = null;
+
+ private void Start()
+ {
+ var astroObject = Locator.GetAstroObject(AstroObject.Name.QuantumMoon);
+ if (astroObject != null)
+ {
+ _quantumMoon = astroObject.GetComponent();
+ }
+ }
+
+ public static OWCamera GetLastSnapshotCamera() =>
+ _lastSnapshotCamera;
+
+ public OWCamera GetOWCamera()
+ => _camera;
+
+ public ProbeCamera.ID GetID()
+ => _id;
+
+ public void SetSandLevelController(SandLevelController sandLevelController)
+ => _sandLevelController = sandLevelController;
+
+ public bool HasInterference() =>
+ _id != ProbeCamera.ID.PreLaunch
+ //&& ((this._quantumMoon != null && this._quantumMoon.IsPlayerInside() != this._quantumMoon.IsProbeInside())
+ || (_sandLevelController != null && _sandLevelController.IsPointBuried(transform.position));
+ //|| (Locator.GetCloakFieldController() != null && Locator.GetCloakFieldController().isPlayerInsideCloak != Locator.GetCloakFieldController().isProbeInsideCloak));
+
+ public RenderTexture TakeSnapshot()
+ {
+ _lastSnapshotCamera = _camera;
+ if (_noiseEffect != null)
+ {
+ _noiseEffect.enabled = HasInterference();
+ }
+
+ _camera.targetTexture = _snapshotTexture;
+ _camera.Render();
+ return _snapshotTexture;
+ }
+
+ public void RotateHorizontal(float cameraRotationX)
+ {
+ _cameraRotation.x = cameraRotationX;
+ transform.parent.localRotation = _origParentLocalRotation * Quaternion.AngleAxis(_cameraRotation.x, Vector3.up);
+ OnRotateCamera?.Invoke(_cameraRotation);
+ }
+
+ public void RotateVertical(float cameraRotationY)
+ {
+ _cameraRotation.y = cameraRotationY;
+ transform.localRotation = _origLocalRotation * Quaternion.AngleAxis(_cameraRotation.y, Vector3.right);
+ OnRotateCamera?.Invoke(_cameraRotation);
+ }
+
+ public void ResetRotation()
+ {
+ _cameraRotation = Vector2.zero;
+ transform.localRotation = _origLocalRotation;
+ transform.parent.localRotation = _origParentLocalRotation;
+ }
+}
diff --git a/QSB/Tools/ProbeTool/QSBProbeEffects.cs b/QSB/Tools/ProbeTool/QSBProbeEffects.cs
index bc0f5bad7..e517f75ff 100644
--- a/QSB/Tools/ProbeTool/QSBProbeEffects.cs
+++ b/QSB/Tools/ProbeTool/QSBProbeEffects.cs
@@ -5,17 +5,18 @@
namespace QSB.Tools.ProbeTool;
+[UsedInUnityProject]
internal class QSBProbeEffects : MonoBehaviour
{
public OWAudioSource _flightLoopAudio;
public OWAudioSource _anchorAudio;
public ParticleSystem _anchorParticles;
- private QSBProbe _probe;
+ private QSBSurveyorProbe _probe;
private void Awake()
{
- _probe = QSBWorldSync.GetUnityObjects().First(x => gameObject.transform.IsChildOf(x.transform));
+ _probe = QSBWorldSync.GetUnityObjects().First(x => gameObject.transform.IsChildOf(x.transform));
if (_probe == null)
{
DebugLog.ToConsole($"Error - Couldn't find QSBProbe!", OWML.Common.MessageType.Error);
@@ -25,6 +26,7 @@ private void Awake()
_probe.OnAnchorProbe += OnAnchor;
_probe.OnUnanchorProbe += OnUnanchor;
_probe.OnStartRetrieveProbe += OnStartRetrieve;
+ _probe.OnTakeSnapshot += OnTakeSnapshot;
}
private void OnDestroy()
@@ -33,6 +35,7 @@ private void OnDestroy()
_probe.OnAnchorProbe -= OnAnchor;
_probe.OnUnanchorProbe -= OnUnanchor;
_probe.OnStartRetrieveProbe -= OnStartRetrieve;
+ _probe.OnTakeSnapshot -= OnTakeSnapshot;
}
private void OnLaunch() => _flightLoopAudio.FadeIn(0.1f, true, true);
@@ -57,5 +60,11 @@ private void OnUnanchor()
=> _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);
}
\ No newline at end of file
diff --git a/QSB/Tools/ProbeTool/QSBProbeLantern.cs b/QSB/Tools/ProbeTool/QSBProbeLantern.cs
index 74365302f..728033aaa 100644
--- a/QSB/Tools/ProbeTool/QSBProbeLantern.cs
+++ b/QSB/Tools/ProbeTool/QSBProbeLantern.cs
@@ -5,6 +5,7 @@
namespace QSB.Tools.ProbeTool;
+[UsedInUnityProject]
internal class QSBProbeLantern : MonoBehaviour
{
public float _fadeInDuration;
@@ -13,7 +14,7 @@ internal class QSBProbeLantern : MonoBehaviour
public OWEmissiveRenderer _emissiveRenderer;
public float _originalRange;
- private QSBProbe _probe;
+ private QSBSurveyorProbe _probe;
private OWLight2 _light;
private float _fadeFraction;
private float _targetFade;
@@ -23,7 +24,7 @@ internal class QSBProbeLantern : MonoBehaviour
private void Awake()
{
- _probe = QSBWorldSync.GetUnityObjects().First(x => gameObject.transform.IsChildOf(x.transform));
+ _probe = QSBWorldSync.GetUnityObjects().First(x => gameObject.transform.IsChildOf(x.transform));
if (_probe == null)
{
DebugLog.ToConsole($"Error - Couldn't find QSBProbe!", OWML.Common.MessageType.Error);
diff --git a/QSB/Tools/ProbeTool/QSBProbeSpotlight.cs b/QSB/Tools/ProbeTool/QSBProbeSpotlight.cs
index 2e235cd20..00c600168 100644
--- a/QSB/Tools/ProbeTool/QSBProbeSpotlight.cs
+++ b/QSB/Tools/ProbeTool/QSBProbeSpotlight.cs
@@ -5,19 +5,20 @@
namespace QSB.Tools.ProbeTool;
+[UsedInUnityProject]
internal class QSBProbeSpotlight : MonoBehaviour
{
public ProbeCamera.ID _id;
public float _fadeInLength = 1f;
public float _intensity;
- private QSBProbe _probe;
+ private QSBSurveyorProbe _probe;
private OWLight2 _light;
private float _timer;
private void Awake()
{
- _probe = QSBWorldSync.GetUnityObjects().First(x => gameObject.transform.IsChildOf(x.transform));
+ _probe = QSBWorldSync.GetUnityObjects().First(x => gameObject.transform.IsChildOf(x.transform));
if (_probe == null)
{
DebugLog.ToConsole($"Error - Couldn't find QSBProbe!", OWML.Common.MessageType.Error);
diff --git a/QSB/Tools/ProbeTool/QSBProbe.cs b/QSB/Tools/ProbeTool/QSBSurveyorProbe.cs
similarity index 81%
rename from QSB/Tools/ProbeTool/QSBProbe.cs
rename to QSB/Tools/ProbeTool/QSBSurveyorProbe.cs
index 45881b8ca..1869b7fa2 100644
--- a/QSB/Tools/ProbeTool/QSBProbe.cs
+++ b/QSB/Tools/ProbeTool/QSBSurveyorProbe.cs
@@ -4,7 +4,8 @@
namespace QSB.Tools.ProbeTool;
-public class QSBProbe : MonoBehaviour, ILightSource
+[UsedInUnityProject]
+public class QSBSurveyorProbe : MonoBehaviour, ILightSource
{
public delegate void SurveyorProbeEvent();
public delegate void RetrieveEvent(float retrieveLength);
@@ -17,11 +18,15 @@ 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;
private RulesetDetector _rulesetDetector;
private SingularityWarpEffect _warpEffect;
+ private QSBProbeCamera _forwardCam;
+ private QSBProbeCamera _reverseCam;
+ private QSBProbeCamera _rotatingCam;
private bool _isRetrieving;
private PlayerInfo _owner;
private bool _anchored;
@@ -30,6 +35,21 @@ public class QSBProbe : MonoBehaviour, ILightSource
public RulesetDetector GetRulesetDetector()
=> _rulesetDetector;
+ public QSBProbeCamera GetForwardCamera()
+ {
+ return _forwardCam;
+ }
+
+ public QSBProbeCamera GetReverseCamera()
+ {
+ return _reverseCam;
+ }
+
+ public QSBProbeCamera GetRotatingCamera()
+ {
+ return _rotatingCam;
+ }
+
private void Awake()
{
_detectorObj = GetComponentInChildren().gameObject;
@@ -37,6 +57,23 @@ private void Awake()
_warpEffect = GetComponentInChildren();
_warpEffect.OnWarpComplete += OnWarpComplete;
_isRetrieving = false;
+
+ var probeCameras = GetComponentsInChildren();
+ for (var i = 0; i < probeCameras.Length; i++)
+ {
+ if (probeCameras[i].GetID() == ProbeCamera.ID.Forward)
+ {
+ _forwardCam = probeCameras[i];
+ }
+ else if (probeCameras[i].GetID() == ProbeCamera.ID.Reverse)
+ {
+ _reverseCam = probeCameras[i];
+ }
+ else if (probeCameras[i].GetID() == ProbeCamera.ID.Rotating)
+ {
+ _rotatingCam = probeCameras[i];
+ }
+ }
}
private void Start()
@@ -127,6 +164,9 @@ public void HandleEvent(ProbeEvent probeEvent)
case ProbeEvent.Unanchor:
_anchored = false;
_owner.ProbeActive = true; // just in case it was missed
+ _forwardCam.SetSandLevelController(null);
+ _reverseCam.SetSandLevelController(null);
+ _rotatingCam.SetSandLevelController(null);
OnUnanchorProbe();
break;
case ProbeEvent.Retrieve:
@@ -165,6 +205,9 @@ private void Deactivate()
_lightSourceVol.SetVolumeActivation(false);
gameObject.SetActive(false);
_isRetrieving = false;
+ _forwardCam.SetSandLevelController(null);
+ _reverseCam.SetSandLevelController(null);
+ _rotatingCam.SetSandLevelController(null);
}
public void OnStartRetrieve(float duration)
@@ -204,4 +247,6 @@ public bool CheckIlluminationAtPoint(Vector3 point, float buffer = 0f, float max
public LightSourceType GetLightSourceType() => LightSourceType.PROBE;
public OWLight2[] GetLights() => _illuminationCheckLights;
public Vector3 GetLightSourcePosition() => _lightSourceVol.transform.position;
+
+ public void TakeSnapshot() => OnTakeSnapshot?.Invoke();
}
\ No newline at end of file
diff --git a/QSB/Tools/ProbeTool/RotationType.cs b/QSB/Tools/ProbeTool/RotationType.cs
new file mode 100644
index 000000000..30883d710
--- /dev/null
+++ b/QSB/Tools/ProbeTool/RotationType.cs
@@ -0,0 +1,8 @@
+namespace QSB.Tools.ProbeTool;
+
+public enum RotationType
+{
+ Horizontal,
+ Vertical,
+ Reset
+}
diff --git a/QSB/Tools/QSBTool.cs b/QSB/Tools/QSBTool.cs
index d36336736..0885143e0 100644
--- a/QSB/Tools/QSBTool.cs
+++ b/QSB/Tools/QSBTool.cs
@@ -5,6 +5,7 @@
namespace QSB.Tools;
+[UsedInUnityProject]
public class QSBTool : PlayerTool
{
public PlayerInfo Player { get; set; }
diff --git a/QSB/Tools/TranslatorTool/QSBNomaiTranslator.cs b/QSB/Tools/TranslatorTool/QSBNomaiTranslator.cs
index 46209451c..aaf41bf77 100644
--- a/QSB/Tools/TranslatorTool/QSBNomaiTranslator.cs
+++ b/QSB/Tools/TranslatorTool/QSBNomaiTranslator.cs
@@ -1,7 +1,9 @@
-using UnityEngine;
+using QSB.Utility;
+using UnityEngine;
namespace QSB.Tools.TranslatorTool;
+[UsedInUnityProject]
public class QSBNomaiTranslator : QSBTool
{
public static float distToClosestTextCenter = 1f;
diff --git a/QSB/Tools/TranslatorTool/QSBNomaiTranslatorProp.cs b/QSB/Tools/TranslatorTool/QSBNomaiTranslatorProp.cs
index 8a266dd3d..c825e5b8c 100644
--- a/QSB/Tools/TranslatorTool/QSBNomaiTranslatorProp.cs
+++ b/QSB/Tools/TranslatorTool/QSBNomaiTranslatorProp.cs
@@ -1,10 +1,12 @@
-using System.Collections.Generic;
+using QSB.Utility;
+using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
namespace QSB.Tools.TranslatorTool;
+[UsedInUnityProject]
internal class QSBNomaiTranslatorProp : MonoBehaviour
{
private static MaterialPropertyBlock s_matPropBlock;
diff --git a/QSB/Tools/TranslatorTool/QSBTranslatorScanBeam.cs b/QSB/Tools/TranslatorTool/QSBTranslatorScanBeam.cs
index a94e6a27e..adc484cac 100644
--- a/QSB/Tools/TranslatorTool/QSBTranslatorScanBeam.cs
+++ b/QSB/Tools/TranslatorTool/QSBTranslatorScanBeam.cs
@@ -1,8 +1,10 @@
-using QSB.WorldSync;
+using QSB.Utility;
+using QSB.WorldSync;
using UnityEngine;
namespace QSB.Tools.TranslatorTool;
+[UsedInUnityProject]
internal class QSBTranslatorScanBeam : MonoBehaviour
{
public Renderer _projectorRenderer;
diff --git a/QSB/Translations/de.json b/QSB/Translations/de.json
new file mode 100644
index 000000000..60fd0e3f2
--- /dev/null
+++ b/QSB/Translations/de.json
@@ -0,0 +1,126 @@
+{
+ "Language": "GERMAN",
+ "MainMenuHost": "MEHRSPIELER HOSTEN",
+ "MainMenuConnect": "VERBINDEN ZUM MEHRSPIELER",
+ "PauseMenuDisconnect": "VERBINDUNG TRENNEN",
+ "PauseMenuStopHosting": "HOSTEN BEENDEN",
+ "PublicIPAddress": "Öffentliche IP Addresse\n\n(DEIN MEHRSPIELER SPIELSTAND WIRD ÜBERSCHRIEBEN)",
+ "ProductUserID": "Produktnutzer ID\n\n(DEIN MEHRSPIELER SPIELSTAND WIRD ÜBERSCHRIEBEN)",
+ "Connect": "VERBINDEN",
+ "Cancel": "ABBRECHEN",
+ "HostExistingOrNewOrCopy": "Willst du eine bestehende Mehrspieler Expedition, eine neue Expedition oder eine Kopie einer bestehenden Einzelspieler Expedition hosten?",
+ "HostNewOrCopy": "Willst du eine neue Expedition oder eine Kopie einer bestehenden Einzelspieler Expedition hosten?",
+ "HostExistingOrNew": "Willst du eine bestehende Mehrspieler Expedition oder eine neue Expedition hosten?",
+ "ExistingSave": "BESTEHENDER SPIELSTAND",
+ "NewSave": "NEUER SPIELSTAND",
+ "CopySave": "SPIELSTAND KOPIEREN",
+ "DisconnectAreYouSure": "Bist du sicher, dass du die Verbindung trennen willst?\nDies wird dich zurück in das Hauptmenü bringen.",
+ "Yes": "JA",
+ "No": "NEIN",
+ "StopHostingAreYouSure": "Bist du sicher, dass du aufhören willst zu hosten?\nDies wird die Verbindung alle anderen Spieler trennen und sie zurück in das Hauptmenü bringen.",
+ "CopyProductUserIDToClipboard": "Server wird gehostet.\nAndere Spieler werden sich mit Hilfe deiner Produktnutzer ID verbinden. Sie lautet :\n{0}\nWillst du das in die Zwischenablage kopieren?",
+ "Connecting": "VERBINDEN...",
+ "OK": "BESTÄTIGEN",
+ "ServerRefusedConnection": "Der Server hat die Verbindung abgelehnt.\n{0}",
+ "ClientDisconnectWithError": "Der Client wurde mit einem Fehler getrennt!\n{0}",
+ "QSBVersionMismatch": "QSB-Version stimmt nicht überein. (Client:{0}, Server:{1})",
+ "OWVersionMismatch": "Outer Wilds-Version stimmt nicht überein. (Client:{0}, Server:{1})",
+ "DLCMismatch": "DLC Installationsstatus stimmt nicht überein. (Client:{0}, Server:{1})",
+ "GameProgressLimit": "Spiel ist zu weit fortgeschritten.",
+ "AddonMismatch": "Addons stimmen nicht überein. (Client:{0} addons, Server:{1} addons)",
+ "IncompatibleMod": "Es wird eine inkompatible/unerlaubte Modifikation genutzt. Die erste gefundene Modifikation war {0}",
+ "PlayerJoinedTheGame": "{0} trat bei!",
+ "PlayerWasKicked": "{0} wurde gekickt.",
+ "KickedFromServer": "Vom Server gekickt. Grund : {0}",
+ "RespawnPlayer": "Spieler respawnen",
+ "TimeSyncTooFarBehind": "{0}\nVorspulen zur Anpassung an die Serverzeit...",
+ "TimeSyncWaitingForStartOfServer": "Es wird auf den Start des Servers gewartet...",
+ "TimeSyncTooFarAhead": "{0}\nPausieren zur Anpassung an die Serverzeit...",
+ "TimeSyncWaitForAllToReady": "Es wird auf den Beginn der Schleife gewartet...",
+ "TimeSyncWaitForAllToDie": "Es wird auf das Ende der Schleife gewartet...",
+ "GalaxyMapEveryoneNotPresent": "Es ist noch nicht soweit. Alle sollten hier sein um das zu erleben.",
+ "YouAreDead": "Du bist tot.",
+ "WaitingForRespawn": "Es wird auf jemanden gewartet, der dich respawnt...",
+ "WaitingForAllToDie": "Es wird darauf gewartet, dass {0} Spieler sterben...",
+ "AttachToShip": "Am Schiff befestigen",
+ "DetachFromShip": "Vom Schiff lösen",
+ "DeathMessages": {
+ "Default": [
+ "{0} starb",
+ "{0} wurde getötet"
+ ],
+ "Impact": [
+ "{0} vergaß Bremsraketen zu benutzen",
+ "{0} ist zu hart auf den Boden geknallt",
+ "{0} schlug zu hart auf den Boden auf",
+ "{0} wurde geplättet",
+ "{0} starb durch einen Aufprall",
+ "{0} ist zu hart auf den Boden aufgeschlagen"
+ ],
+ "Asphyxiation": [
+ "{0} vergaß zu atmen",
+ "{0} erstickte",
+ "{0} starb an Erstickung",
+ "{0} vergaß wie man atmet",
+ "{0} vergaß den Sauerstoff zu prüfen",
+ "{0} ging die Luft aus",
+ "{0} ging der Sauerstoff aus",
+ "{0} brauchte sowieso keine Luft"
+ ],
+ "Energy": [
+ "{0} wurde gekocht"
+ ],
+ "Supernova": [
+ "{0} ging die Zeit aus",
+ "{0} burnt up",
+ "{0} wurde gekocht",
+ "{0} wurde verdampft",
+ "{0} verlor die Zeit aus den Augen"
+ ],
+ "Digestion": [
+ "{0} wurde gegessen",
+ "{0} fand einen Fisch",
+ "{0} traf eine Böse Kreatur",
+ "{0} ärgerte den falschen Fisch",
+ "{0} wurde verdaut",
+ "{0} ist an Verdauung gestorben"
+ ],
+ "Crushed": [
+ "{0} wurde zerquetscht",
+ "{0} wurde gequetscht",
+ "{0} wurde begraben",
+ "{0} ist nicht rechtzeitig entkommen",
+ "{0} ging im Sand schwimmen",
+ "{0} unterschätzte Sand",
+ "{0} wurde unter Sand gefangen"
+ ],
+ "Lava": [
+ "{0} starb in Lava",
+ "{0} wurde geschmolzen",
+ "{0} versuchte in Lava zu schwimmen",
+ "{0} fiel in Lava",
+ "{0} ist an Lava gestorben",
+ "{0} schwamm in Lava",
+ "{0} wurde in Lava verbrannt"
+ ],
+ "BlackHole": [
+ "{0} jagte die Erinnerungen"
+ ],
+ "DreamExplosion": [
+ "{0} explodierte",
+ "{0} war ein Betatester",
+ "{0} ging in die Luft",
+ "{0} wurde frittiert",
+ "{0} starb wegen einer Explosion",
+ "{0} benutzte das falsche Artefakt"
+ ],
+ "CrushedByElevator": [
+ "{0} wurde zerquetscht",
+ "{0} wurde gequetscht",
+ "{0} wurde von einem Aufzug zerquetscht",
+ "{0} stand unter einem Aufzug",
+ "{0} wurde ein Flach-Kamina",
+ "{0} wurde von einem Aufzug gequetscht"
+ ]
+ }
+}
diff --git a/QSB/Translations/fr.json b/QSB/Translations/fr.json
index f1fda671f..efa92c3a6 100644
--- a/QSB/Translations/fr.json
+++ b/QSB/Translations/fr.json
@@ -3,7 +3,7 @@
"MainMenuHost": "OUVRIR AU MULTIJOUEUR",
"MainMenuConnect": "SE CONNECTER AU MULTIJOUEUR",
"PauseMenuDisconnect": "DÉCONNECTER",
- "PauseMenuStopHosting": "ARRÊTEZ L'HÉBERGEMENT",
+ "PauseMenuStopHosting": "ARRÊTER L'HÉBERGEMENT",
"PublicIPAddress": "Adresse IP publique\n\n(CELA EFFACERA VOTRE PROGRESSION MULTIJOUEUR)",
"ProductUserID": "ID utilisateur\n\n(CELA EFFACERA VOTRE PROGRESSION MULTIJOUEUR)",
"Connect": "SE CONNECTER",
@@ -28,7 +28,7 @@
"DLCMismatch": "Les états d'installation du DLC ne correspondent pas. (Client:{0}, Serveur:{1})",
"GameProgressLimit": "Le jeu a trop progressé.",
"AddonMismatch": "Non-concordance des addons. (Client:{0} addons, Serveur:{1} addons)",
- "IncompatibleMod": "Utiliser un mod incompatible/non autorisé. Le premier mod trouvé était {0}",
+ "IncompatibleMod": "Tu utilises un mod incompatible/non autorisé. Le premier mod trouvé était {0}",
"PlayerJoinedTheGame": "{0} a rejoint!",
"PlayerWasKicked": "{0} a été expulsé.",
"KickedFromServer": "Tu as été expulsé du serveur. Raison : {0}",
@@ -39,6 +39,11 @@
"TimeSyncWaitForAllToReady": "Attendez le début de la boucle...",
"TimeSyncWaitForAllToDie": "Attendez la fin de la boucle...",
"GalaxyMapEveryoneNotPresent": "Ce n’est pas encore le moment. Tout le monde doit être ici pour continuer.",
+ "YouAreDead": "Tu es mort.",
+ "WaitingForRespawn": "Attendez que quelqu'un vouz ressuscité...",
+ "WaitingForAllToDie": "Attendez que {0} joueur(s) meurt/meurent...",
+ "AttachToShip": "S'attacher à la fusée",
+ "DetachFromShip": "Se détacher de la fusée",
"DeathMessages": {
"Default": [
"{0} est mort",
diff --git a/QSB/Utility/CustomRelativisticParticleSystem.cs b/QSB/Utility/CustomRelativisticParticleSystem.cs
index c93ee048a..65520af13 100644
--- a/QSB/Utility/CustomRelativisticParticleSystem.cs
+++ b/QSB/Utility/CustomRelativisticParticleSystem.cs
@@ -4,6 +4,7 @@
namespace QSB.Utility;
+[UsedInUnityProject]
internal class CustomRelativisticParticleSystem : MonoBehaviour
{
private ParticleSystem _particleSystem;
diff --git a/QSB/Utility/DebugActions.cs b/QSB/Utility/DebugActions.cs
index 66d1476a4..46f85d5a7 100644
--- a/QSB/Utility/DebugActions.cs
+++ b/QSB/Utility/DebugActions.cs
@@ -2,6 +2,7 @@
using QSB.ItemSync.WorldObjects.Items;
using QSB.Messaging;
using QSB.Player;
+using QSB.QuantumSync.WorldObjects;
using QSB.RespawnSync;
using QSB.ShipSync;
using QSB.Utility.Messages;
@@ -15,7 +16,7 @@ namespace QSB.Utility;
public class DebugActions : MonoBehaviour, IAddComponentOnStart
{
- public static Type WorldObjectSelection;
+ public static Type WorldObjectSelection = typeof(QSBSocketedQuantumObject);
private static void GoToVessel()
{
@@ -166,7 +167,8 @@ public void Update()
{
var dreamLanternItem = QSBWorldSync.GetWorldObjects().First(x =>
x.AttachedObject._lanternType == DreamLanternType.Functioning &&
- QSBPlayerManager.PlayerList.All(y => y.HeldItem != x)
+ QSBPlayerManager.PlayerList.All(y => y.HeldItem != x) &&
+ !x.AttachedObject.GetLanternController().IsLit()
).AttachedObject;
Locator.GetToolModeSwapper().GetItemCarryTool().PickUpItemInstantly(dreamLanternItem);
}
@@ -191,9 +193,7 @@ public void Update()
sarcoController.secondSealProjector.SetLit(false);
sarcoController.thirdSealProjector.SetLit(false);
- sarcoController._attemptOpenAfterDelay = true;
- sarcoController._openAttemptTime = Time.time + 0.5f;
- sarcoController.enabled = true;
+ sarcoController.OnPressInteract();
}
if (Keyboard.current[Key.Numpad4].wasPressedThisFrame)
@@ -225,7 +225,7 @@ public void Update()
var player = new PlayerInfo(QSBPlayerManager.LocalPlayer.TransformSync);
QSBPlayerManager.PlayerList.SafeAdd(player);
QSBPlayerManager.OnAddPlayer?.Invoke(player);
- DebugLog.DebugWrite($"Create Player : {player}", MessageType.Info);
+ DebugLog.DebugWrite($"CREATING FAKE PLAYER : {player}", MessageType.Info);
JoinLeaveSingularity.Create(player, true);
}
diff --git a/QSB/Utility/DebugGUI.cs b/QSB/Utility/DebugGUI.cs
index 5d237c002..253968d11 100644
--- a/QSB/Utility/DebugGUI.cs
+++ b/QSB/Utility/DebugGUI.cs
@@ -238,7 +238,7 @@ private static void DrawGui()
#endregion
- if (QSBWorldSync.AllObjectsReady)
+ if (QSBWorldSync.AllObjectsReady && QSBCore.DLCInstalled)
{
var ghost = QSBWorldSync.GetWorldObjects().First(x => x.AttachedObject._name == "Kamaji");
WriteLine(4, ghost.AttachedObject._name);
diff --git a/QSB/Utility/DebugLog.cs b/QSB/Utility/DebugLog.cs
index 5f01ec8e5..efbbfb5af 100644
--- a/QSB/Utility/DebugLog.cs
+++ b/QSB/Utility/DebugLog.cs
@@ -14,6 +14,13 @@ public static class DebugLog
public static void ToConsole(string message, MessageType type = MessageType.Message)
{
+ if (QSBCore.Helper == null)
+ {
+ // yes i know this is only meant for OWML, but it's useful as a backup
+ ModConsole.OwmlConsole.WriteLine(message, type, GetCallingType());
+ return;
+ }
+
if (QSBCore.DebugSettings.InstanceIdInLogs)
{
message = $"[{ProcessInstanceId}] " + message;
diff --git a/QSB/Utility/DebugSettings.cs b/QSB/Utility/DebugSettings.cs
index e7216fa63..8933a6217 100644
--- a/QSB/Utility/DebugSettings.cs
+++ b/QSB/Utility/DebugSettings.cs
@@ -23,8 +23,8 @@ public class DebugSettings
[JsonProperty("autoStart")]
public bool AutoStart;
- [JsonProperty("skipTitleScreen")]
- public bool SkipTitleScreen;
+ [JsonProperty("kickEveryone")]
+ public bool KickEveryone;
[JsonProperty("debugMode")]
public bool DebugMode;
diff --git a/QSB/Utility/Messages/DebugTriggerSupernovaMessage.cs b/QSB/Utility/Messages/DebugTriggerSupernovaMessage.cs
index 1dbba5479..614c0d10e 100644
--- a/QSB/Utility/Messages/DebugTriggerSupernovaMessage.cs
+++ b/QSB/Utility/Messages/DebugTriggerSupernovaMessage.cs
@@ -1,5 +1,4 @@
using QSB.Messaging;
-using QSB.Patches;
namespace QSB.Utility.Messages;
@@ -12,6 +11,6 @@ public override void OnReceiveRemote()
PlayerData.SaveLoopCount(2);
TimeLoop.SetTimeLoopEnabled(true);
TimeLoop._isTimeFlowing = true;
- QSBPatch.RemoteCall(() => TimeLoop.SetSecondsRemaining(0));
+ TimeLoop.SetSecondsRemaining(0);
}
}
diff --git a/QSB/Utility/UsedInUnityProjectAttribute.cs b/QSB/Utility/UsedInUnityProjectAttribute.cs
new file mode 100644
index 000000000..c6f21a2f3
--- /dev/null
+++ b/QSB/Utility/UsedInUnityProjectAttribute.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace QSB.Utility;
+
+///
+/// denotes that the given type is used in the unity project
+/// and therefore caution should be used when moving/renaming/deleting
+///
+public class UsedInUnityProjectAttribute : Attribute { }
diff --git a/QSB/Utility/VariableSync/BoolVariableSyncer.cs b/QSB/Utility/VariableSync/BoolVariableSyncer.cs
index 73338da55..e021aab52 100644
--- a/QSB/Utility/VariableSync/BoolVariableSyncer.cs
+++ b/QSB/Utility/VariableSync/BoolVariableSyncer.cs
@@ -1,5 +1,4 @@
namespace QSB.Utility.VariableSync;
-public class BoolVariableSyncer : BaseVariableSyncer
-{
-}
\ No newline at end of file
+[UsedInUnityProject]
+public class BoolVariableSyncer : BaseVariableSyncer { }
diff --git a/QSB/Utility/VariableSync/Vector3VariableSyncer.cs b/QSB/Utility/VariableSync/Vector3VariableSyncer.cs
index 884714996..1ca8a5a40 100644
--- a/QSB/Utility/VariableSync/Vector3VariableSyncer.cs
+++ b/QSB/Utility/VariableSync/Vector3VariableSyncer.cs
@@ -2,6 +2,7 @@
namespace QSB.Utility.VariableSync;
+[UsedInUnityProject]
public class Vector3VariableSyncer : BaseVariableSyncer
{
///
diff --git a/QSB/debugsettings.template.json b/QSB/debugsettings.template.json
index 4afa74251..83abb2f6e 100644
--- a/QSB/debugsettings.template.json
+++ b/QSB/debugsettings.template.json
@@ -5,7 +5,7 @@
"hookDebugLogs": false,
"avoidTimeSync": false,
"autoStart": false,
- "skipTitleScreen": false,
+ "kickEveryone": false,
"debugMode": false,
"drawGui": false,
"drawLines": false,
diff --git a/QSB/manifest.json b/QSB/manifest.json
index 053552817..e0551f00f 100644
--- a/QSB/manifest.json
+++ b/QSB/manifest.json
@@ -7,8 +7,8 @@
"body": "- Disable *all* other mods. (Can heavily affect performance)\n- Make sure you are not running any other network-intensive applications."
},
"uniqueName": "Raicuparta.QuantumSpaceBuddies",
- "version": "0.21.1",
- "owmlVersion": "2.5.2",
+ "version": "0.22.0",
+ "owmlVersion": "2.7.0",
"dependencies": [ "_nebula.MenuFramework", "JohnCorby.VanillaFix" ],
"pathsToPreserve": [ "debugsettings.json", "storage.json" ]
}
diff --git a/README.md b/README.md
index 05d88efe1..0c11f2cc6 100644
--- a/README.md
+++ b/README.md
@@ -40,8 +40,7 @@ Spoilers within!
#### Hosting a server
-- Enter a game. This can be a new expedition or an existing save file.
-- On the pause screen, click the option `OPEN TO MULTIPLAYER`.
+- On the title screen, click the option `OPEN TO MULTIPLAYER`.
- Share your Product User ID with the people who want to connect.
- Enjoy!
@@ -139,7 +138,6 @@ The template for this file is this :
"hookDebugLogs": false,
"avoidTimeSync": false,
"autoStart": false,
- "skipTitleScreen": false,
"debugMode": false,
"drawGui": false,
"drawLines": false,
@@ -156,7 +154,6 @@ The template for this file is this :
- hookDebugLogs - Print Unity logs and warnings.
- avoidTimeSync - Disables the syncing of time.
- autoStart - Host/connect automatically for faster testing.
-- skipTitleScreen - Auto-skips the splash screen.
- debugMode - Enables debug mode. If this is set to `false`, none of the following settings do anything.
- drawGui - Draws a GUI at the top of the screen that gives information on many things.
- drawLines - Draws gizmo-esque lines around things. Indicates reference sectors/transforms, triggers, etc. LAGGY.
@@ -185,6 +182,7 @@ The template for this file is this :
- [Tlya](https://github.com/Tllya) - Russian translation.
- [Xen](https://github.com/xen-42) - French translation.
- [ShoosGun](https://github.com/ShoosGun) - Portuguese translation.
+- [DertolleDude](https://github.com/DertolleDude) - German translation.
### Special Thanks
- Thanks to Logan Ver Hoef for help with the game code, and for helping make the damn game in the first place.
diff --git a/TRANSLATING.md b/TRANSLATING.md
index bd1f8c92a..ce4437c20 100644
--- a/TRANSLATING.md
+++ b/TRANSLATING.md
@@ -9,10 +9,10 @@ QSB can only be translated to the languages Outer Wilds supports - so if you don
- French
- Russian
- Portuguese (Brazil)
+- German
### Un-translated languages :
- Spanish (Latin American)
-- German
- Italian
- Polish
- Japanese