diff --git a/Directory.Build.props b/Directory.Build.props index 4cf7c933..468bd66a 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -16,7 +16,6 @@ - C:\Program Files\Epic Games\OuterWilds $(AppData)\OuterWildsModManager\OWML $(SolutionDir)\qsb-unityproject\Assets diff --git a/QSB/Animation/Player/AnimationSync.cs b/QSB/Animation/Player/AnimationSync.cs index ec837038..d79b8773 100644 --- a/QSB/Animation/Player/AnimationSync.cs +++ b/QSB/Animation/Player/AnimationSync.cs @@ -39,6 +39,7 @@ public class AnimationSync : PlayerSyncObject /// /// This wipes the NetworkAnimator's fields, so it assumes the parameters have changed. /// Basically just forces it to set all its dirty flags. + /// BUG: this doesnt work for other players because its only called by the host. /// private void SendInitialState(uint to) => NetworkAnimator.Invoke("Awake"); diff --git a/QSB/ConversationSync/CameraFacingBillboard.cs b/QSB/ConversationSync/CameraFacingBillboard.cs index 109392ec..21499bb4 100644 --- a/QSB/ConversationSync/CameraFacingBillboard.cs +++ b/QSB/ConversationSync/CameraFacingBillboard.cs @@ -1,7 +1,9 @@ -using UnityEngine; +using QSB.Utility; +using UnityEngine; namespace QSB.ConversationSync; +[UsedInUnityProject] public class CameraFacingBillboard : MonoBehaviour { private OWCamera _activeCam; diff --git a/QSB/DeathSync/Patches/DeathPatches.cs b/QSB/DeathSync/Patches/DeathPatches.cs index 478e25d8..59fa30a3 100644 --- a/QSB/DeathSync/Patches/DeathPatches.cs +++ b/QSB/DeathSync/Patches/DeathPatches.cs @@ -3,7 +3,6 @@ using QSB.DeathSync.Messages; using QSB.Messaging; using QSB.Patches; using QSB.Player; -using QSB.Utility; using System.Linq; using UnityEngine; @@ -14,96 +13,47 @@ public class DeathPatches : QSBPatch { public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect; + /// + /// don't take damage from impact in ship + /// [HarmonyPrefix] [HarmonyPatch(typeof(PlayerResources), nameof(PlayerResources.OnImpact))] - public static bool PlayerResources_OnImpact(PlayerResources __instance, ImpactData impact) => - // don't take damage from impact in ship - !PlayerState.IsInsideShip(); + public static bool PlayerResources_OnImpact(PlayerResources __instance, ImpactData impact) + { + if (QSBCore.ShipDamage) + { + return true; + } + + return !PlayerState.IsInsideShip(); + } /// /// don't insta-die from impact in ship /// [HarmonyPrefix] - [HarmonyPatch(typeof(HighSpeedImpactSensor), nameof(HighSpeedImpactSensor.FixedUpdate))] - public static bool HighSpeedImpactSensor_FixedUpdate(HighSpeedImpactSensor __instance) + [HarmonyPatch(typeof(HighSpeedImpactSensor), nameof(HighSpeedImpactSensor.HandlePlayerInsideShip))] + public static bool HighSpeedImpactSensor_HandlePlayerInsideShip(HighSpeedImpactSensor __instance) { - if (__instance._isPlayer && (PlayerState.IsAttached() || PlayerState.IsInsideShuttle() || PlayerState.UsingNomaiRemoteCamera())) + if (QSBCore.ShipDamage) { - return false; + return true; } - if (__instance._dieNextUpdate && !__instance._dead) + var shipCenter = Locator.GetShipTransform().position + Locator.GetShipTransform().up * 2f; + var distanceFromShip = Vector3.Distance(__instance._body.GetPosition(), shipCenter); + if (distanceFromShip > 8f) { - __instance._dead = true; - __instance._dieNextUpdate = false; - if (__instance.gameObject.CompareTag("Player")) - { - Locator.GetDeathManager().SetImpactDeathSpeed(__instance._impactSpeed); - Locator.GetDeathManager().KillPlayer(DeathType.Impact); - } - else if (__instance.gameObject.CompareTag("Ship")) - { - __instance.GetComponent().Explode(); - } + __instance._body.SetPosition(shipCenter); } - if (__instance._isPlayer && PlayerState.IsInsideShip()) + if (!__instance._dead) { - var shipCenter = Locator.GetShipTransform().position + Locator.GetShipTransform().up * 2f; - var distanceFromShip = Vector3.Distance(__instance._body.GetPosition(), shipCenter); - if (distanceFromShip > 8f) + var a = __instance._body.GetVelocity() - Locator.GetShipBody().GetPointVelocity(__instance._body.GetPosition()); + if (a.sqrMagnitude > __instance._sqrCheckSpeedThreshold) { - __instance._body.SetPosition(shipCenter); - } - - if (!__instance._dead) - { - var a = __instance._body.GetVelocity() - Locator.GetShipBody().GetPointVelocity(__instance._body.GetPosition()); - if (a.sqrMagnitude > __instance._sqrCheckSpeedThreshold) - { - __instance._impactSpeed = a.magnitude; - __instance._body.AddVelocityChange(-a); - } - } - - return false; - } - - var passiveReferenceFrame = __instance._sectorDetector.GetPassiveReferenceFrame(); - if (!__instance._dead && passiveReferenceFrame != null) - { - var relativeVelocity = __instance._body.GetVelocity() - passiveReferenceFrame.GetOWRigidBody().GetPointVelocity(__instance._body.GetPosition()); - if (relativeVelocity.sqrMagnitude > __instance._sqrCheckSpeedThreshold) - { - var hitCount = Physics.RaycastNonAlloc(__instance.transform.TransformPoint(__instance._localOffset), relativeVelocity, __instance._raycastHits, relativeVelocity.magnitude * Time.deltaTime + __instance._radius, OWLayerMask.physicalMask, QueryTriggerInteraction.Ignore); - for (var i = 0; i < hitCount; i++) - { - if (__instance._raycastHits[i].rigidbody.mass > 10f && !__instance._raycastHits[i].rigidbody.Equals(__instance._body.GetRigidbody())) - { - var owRigidbody = __instance._raycastHits[i].rigidbody.GetComponent(); - if (owRigidbody == null) - { - DebugLog.ToConsole("Rigidbody does not have attached OWRigidbody!!!", OWML.Common.MessageType.Error); - Debug.Break(); - } - else - { - relativeVelocity = __instance._body.GetVelocity() - owRigidbody.GetPointVelocity(__instance._body.GetPosition()); - var a2 = Vector3.Project(relativeVelocity, __instance._raycastHits[i].normal); - if (a2.sqrMagnitude > __instance._sqrCheckSpeedThreshold) - { - __instance._body.AddVelocityChange(-a2); - __instance._impactSpeed = a2.magnitude; - if (!PlayerState.IsInsideTheEye()) - { - __instance._dieNextUpdate = true; - } - - break; - } - } - } - } + __instance._impactSpeed = a.magnitude; + __instance._body.AddVelocityChange(-a); } } diff --git a/QSB/EchoesOfTheEye/AlarmTotemSync/AlarmTotemManager.cs b/QSB/EchoesOfTheEye/AlarmTotemSync/AlarmTotemManager.cs index b50b1e90..992f79ee 100644 --- a/QSB/EchoesOfTheEye/AlarmTotemSync/AlarmTotemManager.cs +++ b/QSB/EchoesOfTheEye/AlarmTotemSync/AlarmTotemManager.cs @@ -1,8 +1,9 @@ using Cysharp.Threading.Tasks; using QSB.EchoesOfTheEye.AlarmTotemSync.WorldObjects; +using QSB.Utility; using QSB.WorldSync; +using System.Linq; using System.Threading; -using UnityEngine; namespace QSB.EchoesOfTheEye.AlarmTotemSync; @@ -11,18 +12,12 @@ public class AlarmTotemManager : WorldObjectManager public override WorldObjectScene WorldObjectScene => WorldObjectScene.SolarSystem; public override bool DlcOnly => true; - private QSBAlarmSequenceController _qsbAlarmSequenceController; + public static AlarmBell[] AlarmBells; public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct) { - // QSBWorldSync.Init(); + QSBWorldSync.Init(); QSBWorldSync.Init(); - - _qsbAlarmSequenceController = new GameObject(nameof(QSBAlarmSequenceController)) - .AddComponent(); - DontDestroyOnLoad(_qsbAlarmSequenceController.gameObject); + AlarmBells = QSBWorldSync.GetUnityObjects().Where(x => x._lightController).SortDeterministic().ToArray(); } - - public override void UnbuildWorldObjects() => - Destroy(_qsbAlarmSequenceController.gameObject); } diff --git a/QSB/EchoesOfTheEye/AlarmTotemSync/Messages/SetVisibleMessage.cs b/QSB/EchoesOfTheEye/AlarmTotemSync/Messages/SetVisibleMessage.cs new file mode 100644 index 00000000..fd8ad1a4 --- /dev/null +++ b/QSB/EchoesOfTheEye/AlarmTotemSync/Messages/SetVisibleMessage.cs @@ -0,0 +1,37 @@ +using QSB.EchoesOfTheEye.AlarmTotemSync.WorldObjects; +using QSB.Messaging; + +namespace QSB.EchoesOfTheEye.AlarmTotemSync.Messages; + +public class SetVisibleMessage : QSBWorldObjectMessage +{ + public SetVisibleMessage(bool visible) : base(visible) { } + + public override void OnReceiveRemote() + { + if (WorldObject.AttachedObject._isPlayerVisible == Data) + { + return; + } + + WorldObject.AttachedObject._isPlayerVisible = Data; + if (Data) + { + Locator.GetAlarmSequenceController().IncreaseAlarmCounter(); + WorldObject.AttachedObject._secondsConcealed = 0f; + WorldObject.AttachedObject._simTotemMaterials[0] = WorldObject.AttachedObject._simAlarmMaterial; + WorldObject.AttachedObject._simTotemRenderer.sharedMaterials = WorldObject.AttachedObject._simTotemMaterials; + WorldObject.AttachedObject._simVisionConeRenderer.SetColor(WorldObject.AttachedObject._simAlarmColor); + GlobalMessenger.FireEvent("AlarmTotemTriggered"); + } + else + { + Locator.GetAlarmSequenceController().DecreaseAlarmCounter(); + WorldObject.AttachedObject._secondsConcealed = 0f; + WorldObject.AttachedObject._simTotemMaterials[0] = WorldObject.AttachedObject._origSimEyeMaterial; + WorldObject.AttachedObject._simTotemRenderer.sharedMaterials = WorldObject.AttachedObject._simTotemMaterials; + WorldObject.AttachedObject._simVisionConeRenderer.SetColor(WorldObject.AttachedObject._simVisionConeRenderer.GetOriginalColor()); + WorldObject.AttachedObject._pulseLightController.FadeTo(0f, 0.5f); + } + } +} diff --git a/QSB/EchoesOfTheEye/AlarmTotemSync/Messages/TotemEnabledMessage.cs b/QSB/EchoesOfTheEye/AlarmTotemSync/Messages/TotemEnabledMessage.cs deleted file mode 100644 index 446c3f0e..00000000 --- a/QSB/EchoesOfTheEye/AlarmTotemSync/Messages/TotemEnabledMessage.cs +++ /dev/null @@ -1,12 +0,0 @@ -using QSB.EchoesOfTheEye.AlarmTotemSync.WorldObjects; -using QSB.Messaging; - -namespace QSB.EchoesOfTheEye.AlarmTotemSync.Messages; - -public class TotemEnabledMessage : QSBWorldObjectMessage -{ - public TotemEnabledMessage(bool enabled) : base(enabled) { } - - public override void OnReceiveRemote() => - WorldObject.SetEnabled(Data); -} diff --git a/QSB/EchoesOfTheEye/AlarmTotemSync/Messages/TotemVisibleForMessage.cs b/QSB/EchoesOfTheEye/AlarmTotemSync/Messages/TotemVisibleForMessage.cs deleted file mode 100644 index e8624afb..00000000 --- a/QSB/EchoesOfTheEye/AlarmTotemSync/Messages/TotemVisibleForMessage.cs +++ /dev/null @@ -1,16 +0,0 @@ -using QSB.EchoesOfTheEye.AlarmTotemSync.WorldObjects; -using QSB.Messaging; -using System.Collections.Generic; - -namespace QSB.EchoesOfTheEye.AlarmTotemSync.Messages; - -public class TotemVisibleForMessage : QSBWorldObjectMessage> -{ - public TotemVisibleForMessage(List visibleFor) : base(visibleFor) { } - - public override void OnReceiveRemote() - { - WorldObject.VisibleFor.Clear(); - WorldObject.VisibleFor.AddRange(Data); - } -} diff --git a/QSB/EchoesOfTheEye/AlarmTotemSync/Messages/TotemVisibleMessage.cs b/QSB/EchoesOfTheEye/AlarmTotemSync/Messages/TotemVisibleMessage.cs deleted file mode 100644 index 947302e9..00000000 --- a/QSB/EchoesOfTheEye/AlarmTotemSync/Messages/TotemVisibleMessage.cs +++ /dev/null @@ -1,11 +0,0 @@ -using QSB.EchoesOfTheEye.AlarmTotemSync.WorldObjects; -using QSB.Messaging; - -namespace QSB.EchoesOfTheEye.AlarmTotemSync.Messages; - -public class TotemVisibleMessage : QSBWorldObjectMessage -{ - public TotemVisibleMessage(bool visible) : base(visible) { } - public override void OnReceiveLocal() => OnReceiveRemote(); - public override void OnReceiveRemote() => WorldObject.SetVisible(From, Data); -} diff --git a/QSB/EchoesOfTheEye/AlarmTotemSync/Patches/AlarmSequenceControllerPatches.cs b/QSB/EchoesOfTheEye/AlarmTotemSync/Patches/AlarmSequenceControllerPatches.cs new file mode 100644 index 00000000..d51aa386 --- /dev/null +++ b/QSB/EchoesOfTheEye/AlarmTotemSync/Patches/AlarmSequenceControllerPatches.cs @@ -0,0 +1,93 @@ +using HarmonyLib; +using QSB.Patches; +using UnityEngine; + +namespace QSB.EchoesOfTheEye.AlarmTotemSync.Patches; + +[HarmonyPatch(typeof(AlarmSequenceController))] +public class AlarmSequenceControllerPatches : QSBPatch +{ + public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect; + + [HarmonyPrefix] + [HarmonyPatch(nameof(AlarmSequenceController.IncreaseAlarmCounter))] + private static bool IncreaseAlarmCounter(AlarmSequenceController __instance) + { + __instance._alarmCounter++; + if (__instance._alarmCounter == 1) + { + __instance.PlayChimes(); + } + __instance.enabled = true; + return false; + } + + [HarmonyPrefix] + [HarmonyPatch(nameof(AlarmSequenceController.DecreaseAlarmCounter))] + private static bool DecreaseAlarmCounter(AlarmSequenceController __instance) + { + __instance._alarmCounter--; + if (__instance._alarmCounter < 0) + { + __instance._alarmCounter = 0; + Debug.LogError("Something went wrong, alarm counter should never drop below zero!"); + } + return false; + } + + [HarmonyPrefix] + [HarmonyPatch(nameof(AlarmSequenceController.StopChimes))] + private static bool StopChimes(AlarmSequenceController __instance) + { + __instance._playing = false; + __instance._stopRequested = false; + __instance._animationStarted = false; + foreach (var alarmBell in AlarmTotemManager.AlarmBells) + { + alarmBell.StopAnimation(); + } + return false; + } + + [HarmonyPrefix] + [HarmonyPatch(nameof(AlarmSequenceController.Update))] + private static bool Update(AlarmSequenceController __instance) + { + __instance.UpdateWakeFraction(); + if (__instance._playing) + { + __instance.UpdateChimes(); + } + __instance.UpdatePulse(); + if (!__instance._playing && __instance._alarmCounter == 0 && __instance._pulse <= 0.01f) + { + __instance._pulse = 0f; + __instance._targetPulse = 0f; + __instance.enabled = false; + } + return false; + } + + [HarmonyPrefix] + [HarmonyPatch(nameof(AlarmSequenceController.PlaySingleChime))] + private static bool PlaySingleChime(AlarmSequenceController __instance) + { + foreach (var alarmBell in AlarmTotemManager.AlarmBells) + { + alarmBell.PlaySingleChime(__instance._chimeIndex); + } + if (!__instance._animationStarted && !__instance._dreamWorldController.IsInDream()) + { + foreach (var alarmBell in AlarmTotemManager.AlarmBells) + { + alarmBell.PlayAnimation(); + } + __instance._animationStarted = true; + } + if (__instance._dreamWorldController.IsInDream() && !__instance._dreamWorldController.IsExitingDream()) + { + Locator.GetDreamWorldAudioController().PlaySingleAlarmChime(__instance._chimeIndex, __instance._volumeCurve.Evaluate(__instance._wakeFraction)); + } + return false; + } +} diff --git a/QSB/EchoesOfTheEye/AlarmTotemSync/Patches/AlarmTotemPatches.cs b/QSB/EchoesOfTheEye/AlarmTotemSync/Patches/AlarmTotemPatches.cs index 93169d62..8b84d710 100644 --- a/QSB/EchoesOfTheEye/AlarmTotemSync/Patches/AlarmTotemPatches.cs +++ b/QSB/EchoesOfTheEye/AlarmTotemSync/Patches/AlarmTotemPatches.cs @@ -1,8 +1,11 @@ using HarmonyLib; +using QSB.AuthoritySync; using QSB.EchoesOfTheEye.AlarmTotemSync.Messages; using QSB.EchoesOfTheEye.AlarmTotemSync.WorldObjects; using QSB.Messaging; using QSB.Patches; +using QSB.Player; +using QSB.Utility; using QSB.WorldSync; using UnityEngine; @@ -12,27 +15,58 @@ public class AlarmTotemPatches : QSBPatch { public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect; - /* [HarmonyPrefix] [HarmonyPatch(typeof(AlarmTotem), nameof(AlarmTotem.OnSectorOccupantAdded))] - private static void OnSectorOccupantAdded(AlarmTotem __instance, SectorDetector sectorDetector) + private static bool OnSectorOccupantAdded(AlarmTotem __instance, SectorDetector sectorDetector) { - if (sectorDetector.GetOccupantType() == DynamicOccupant.Player && QSBWorldSync.AllObjectsReady) + if (!QSBWorldSync.AllObjectsReady) { - __instance.GetWorldObject() - .SendMessage(new TotemEnabledMessage(true)); + return true; } + + if (sectorDetector.GetOccupantType() == DynamicOccupant.Player) + { + __instance.enabled = true; + var qsbAlarmTotem = __instance.GetWorldObject(); + qsbAlarmTotem.RequestOwnership(); + } + return false; } [HarmonyPrefix] [HarmonyPatch(typeof(AlarmTotem), nameof(AlarmTotem.OnSectorOccupantRemoved))] - private static void OnSectorOccupantRemoved(AlarmTotem __instance, SectorDetector sectorDetector) + private static bool OnSectorOccupantRemoved(AlarmTotem __instance, SectorDetector sectorDetector) { - if (sectorDetector.GetOccupantType() == DynamicOccupant.Player && QSBWorldSync.AllObjectsReady) + if (!QSBWorldSync.AllObjectsReady) { - __instance.GetWorldObject() - .SendMessage(new TotemEnabledMessage(false)); + return true; } + + if (sectorDetector.GetOccupantType() == DynamicOccupant.Player) + { + __instance.enabled = false; + var qsbAlarmTotem = __instance.GetWorldObject(); + qsbAlarmTotem.ReleaseOwnership(); + Delay.RunFramesLater(10, () => + { + // no one else took ownership, so we can safely turn stuff off + // ie turn off when no one else is there + if (qsbAlarmTotem.Owner == 0) + { + __instance._pulseLightController.SetIntensity(0f); + __instance._simTotemMaterials[0] = __instance._origSimEyeMaterial; + __instance._simTotemRenderer.sharedMaterials = __instance._simTotemMaterials; + __instance._simVisionConeRenderer.SetColor(__instance._simVisionConeRenderer.GetOriginalColor()); + if (__instance._isPlayerVisible) + { + __instance._isPlayerVisible = false; + __instance._secondsConcealed = 0f; + Locator.GetAlarmSequenceController().DecreaseAlarmCounter(); + } + } + }); + } + return false; } [HarmonyPrefix] @@ -43,45 +77,87 @@ public class AlarmTotemPatches : QSBPatch { return true; } - var qsbAlarmTotem = __instance.GetWorldObject(); - var isLocallyVisible = qsbAlarmTotem.IsLocallyVisible; - qsbAlarmTotem.IsLocallyVisible = __instance.CheckPlayerVisible(); - if (qsbAlarmTotem.IsLocallyVisible && !isLocallyVisible) + if (qsbAlarmTotem.Owner != QSBPlayerManager.LocalPlayerId) { - qsbAlarmTotem.SendMessage(new TotemVisibleMessage(true)); - } - else if (isLocallyVisible && !qsbAlarmTotem.IsLocallyVisible) - { - qsbAlarmTotem.SendMessage(new TotemVisibleMessage(false)); + return false; } var isPlayerVisible = __instance._isPlayerVisible; - __instance._isPlayerVisible = qsbAlarmTotem.VisibleFor.Count > 0; - if (__instance._isPlayerVisible && !isPlayerVisible) + __instance._isPlayerVisible = __instance.CheckPlayerVisible(); + if (!isPlayerVisible && __instance._isPlayerVisible) { Locator.GetAlarmSequenceController().IncreaseAlarmCounter(); + __instance._secondsConcealed = 0f; __instance._simTotemMaterials[0] = __instance._simAlarmMaterial; __instance._simTotemRenderer.sharedMaterials = __instance._simTotemMaterials; __instance._simVisionConeRenderer.SetColor(__instance._simAlarmColor); - if (__instance._isTutorialTotem) - { - GlobalMessenger.FireEvent("TutorialAlarmTotemTriggered"); - } + GlobalMessenger.FireEvent("AlarmTotemTriggered"); + qsbAlarmTotem.SendMessage(new SetVisibleMessage(true)); } else if (isPlayerVisible && !__instance._isPlayerVisible) { Locator.GetAlarmSequenceController().DecreaseAlarmCounter(); + __instance._secondsConcealed = 0f; __instance._simTotemMaterials[0] = __instance._origSimEyeMaterial; __instance._simTotemRenderer.sharedMaterials = __instance._simTotemMaterials; __instance._simVisionConeRenderer.SetColor(__instance._simVisionConeRenderer.GetOriginalColor()); __instance._pulseLightController.FadeTo(0f, 0.5f); + qsbAlarmTotem.SendMessage(new SetVisibleMessage(false)); } - return false; } - */ + + [HarmonyPrefix] + [HarmonyPatch(typeof(AlarmTotem), nameof(AlarmTotem.CheckPlayerVisible))] + private static bool CheckPlayerVisible(AlarmTotem __instance, out bool __result) + { + if (!__instance._isFaceOpen) + { + __result = false; + return false; + } + foreach (var player in QSBPlayerManager.PlayerList) + { + var position = player.Camera.transform.position; + if (__instance.CheckPointInVisionCone(position) && !__instance.CheckLineOccluded(__instance._sightOrigin.position, position)) + { + if (player.LightSensor.IsIlluminated()) + { + __result = true; + return false; + } + if (player.AssignedSimulationLantern == null) + { + continue; + } + var lanternController = player.AssignedSimulationLantern.AttachedObject.GetLanternController(); + if (lanternController.IsHeldByPlayer()) + { + if (lanternController.IsConcealed()) + { + if (!__instance._hasConcealedFromAlarm) + { + __instance._secondsConcealed += Time.deltaTime; + if (__instance._secondsConcealed > 1f) + { + __instance._hasConcealedFromAlarm = true; + GlobalMessenger.FireEvent("ConcealFromAlarmTotem"); + } + } + __result = false; + return false; + } + __result = true; + return false; + } + } + } + __result = false; + return false; + } + [HarmonyPrefix] [HarmonyPatch(typeof(AlarmBell), nameof(AlarmBell.OnEntry))] diff --git a/QSB/EchoesOfTheEye/AlarmTotemSync/QSBAlarmSequenceController.cs b/QSB/EchoesOfTheEye/AlarmTotemSync/QSBAlarmSequenceController.cs deleted file mode 100644 index d81c178d..00000000 --- a/QSB/EchoesOfTheEye/AlarmTotemSync/QSBAlarmSequenceController.cs +++ /dev/null @@ -1,245 +0,0 @@ -using UnityEngine; - -namespace QSB.EchoesOfTheEye.AlarmTotemSync; - -/// -/// copied and modified from base game -/// -public class QSBAlarmSequenceController : MonoBehaviour -{ - private const int CHIME_COUNT = 3; - - [SerializeField] - private float _wakeDuration = 5f; - - [SerializeField] - private float _recoveryDuration = 2f; - - [Header("Audio")] - [SerializeField] - private float _audioLingerTime = 3f; - - [SerializeField] - private AnimationCurve _volumeCurve; - - [Header("Pulse")] - [SerializeField] - private float _pulseAttackLength = 0.2f; - - [SerializeField] - private float _pulseHoldLength = 0.1f; - - [SerializeField] - private float _pulseCooldownLength = 1f; - - private DreamWorldController _dreamWorldController; - - private AlarmBell _activeBell; - - private int _alarmCounter; - - private float _wakeFraction; - - private float _targetPulse; - - private float _pulse; - - private bool _playing; - - private bool _animationStarted; - - private bool _stopRequested; - - private float _stopTime; - - private int _chimeIndex; - - private float _lastChimeTime; - - private float _chimeInterval; - - private void Awake() - { - // Locator.RegisterAlarmSequenceController(this); - GlobalMessenger.AddListener("ExitDreamWorld", OnExitDreamWorld); - } - - private void Start() - { - _dreamWorldController = Locator.GetDreamWorldController(); - enabled = false; - } - - private void OnDestroy() - { - GlobalMessenger.RemoveListener("ExitDreamWorld", OnExitDreamWorld); - } - - public void RegisterDreamEyeMaskController(DreamEyeMaskController dreamEyeMaskController) { } - - public float GetPulseIntensity() => _pulse; - - public bool IsAlarmWakingPlayer() => _alarmCounter > 0 && !PlayerState.IsResurrected(); - - public void IncreaseAlarmCounter() - { - if (!_dreamWorldController.IsInDream() || _dreamWorldController.IsExitingDream()) - { - return; - } - - _alarmCounter++; - if (_alarmCounter == 1) - { - PlayChimes(); - } - - enabled = true; - } - - public void DecreaseAlarmCounter() - { - if (!_dreamWorldController.IsInDream() || _dreamWorldController.IsExitingDream()) - { - return; - } - - _alarmCounter--; - if (_alarmCounter < 0) - { - _alarmCounter = 0; - Debug.LogError("Something went wrong, alarm counter should never drop below zero!"); - } - } - - private void PlayChimes() - { - if (Locator.GetDreamWorldController().GetDreamCampfire() != null) - { - _activeBell = Locator.GetDreamWorldController().GetDreamCampfire().GetAlarmBell(); - } - - _playing = true; - _chimeInterval = 1f; - _lastChimeTime = 0f; - _stopRequested = false; - } - - private void StopChimes() - { - _playing = false; - _stopRequested = false; - _animationStarted = false; - if (_activeBell != null) - { - _activeBell.StopAnimation(); - _activeBell = null; - } - } - - private void Update() - { - if (_dreamWorldController.IsInDream() && !_dreamWorldController.IsExitingDream()) - { - UpdateWakeFraction(); - } - - if (_playing) - { - UpdateChimes(); - } - - UpdatePulse(); - if (!_playing && _alarmCounter == 0 && _pulse <= 0.01f) - { - _pulse = 0f; - _targetPulse = 0f; - enabled = false; - } - } - - private void UpdateWakeFraction() - { - if (_alarmCounter > 0) - { - _wakeFraction = Mathf.MoveTowards(_wakeFraction, 1f, Time.deltaTime / _wakeDuration); - if (_wakeFraction >= 1f && !PlayerState.IsResurrected()) - { - _dreamWorldController.ExitDreamWorld(DreamWakeType.Alarm); - } - } - else if (_wakeFraction > 0f) - { - _wakeFraction = Mathf.MoveTowards(_wakeFraction, 0f, Time.deltaTime / _recoveryDuration); - if (_wakeFraction <= 0f) - { - StopChimes(); - } - } - } - - private void UpdateChimes() - { - if (Time.time > _lastChimeTime + _chimeInterval) - { - if (!PlayerState.IsResurrected()) - { - PlaySingleChime(); - } - - _targetPulse = 1f; - _lastChimeTime = Time.time; - _chimeInterval = Mathf.Max(_chimeInterval - 0.08f, 0.4f); - _chimeIndex++; - _chimeIndex = _chimeIndex >= CHIME_COUNT ? 0 : _chimeIndex; - } - - if (_stopRequested && Time.time > _stopTime) - { - StopChimes(); - } - } - - private void UpdatePulse() - { - if (Time.time > _lastChimeTime + _pulseAttackLength + _pulseHoldLength) - { - var num = _pulseCooldownLength * _chimeInterval; - _targetPulse = Mathf.MoveTowards(_targetPulse, 0f, 1f / num * Time.deltaTime); - _pulse = _targetPulse; - return; - } - - _pulse = Mathf.MoveTowards(_pulse, _targetPulse, 1f / _pulseAttackLength * Time.deltaTime); - } - - private void PlaySingleChime() - { - if (_activeBell != null) - { - _activeBell.PlaySingleChime(_chimeIndex); - if (!_animationStarted && !_dreamWorldController.IsInDream()) - { - _activeBell.PlayAnimation(); - _animationStarted = true; - } - } - - if (_dreamWorldController.IsInDream() && !_dreamWorldController.IsExitingDream()) - { - Locator.GetDreamWorldAudioController().PlaySingleAlarmChime(_chimeIndex, _volumeCurve.Evaluate(_wakeFraction)); - } - } - - private void OnExitDreamWorld() - { - if (_playing) - { - _stopRequested = true; - _stopTime = Time.time + _audioLingerTime; - } - - _alarmCounter = 0; - _wakeFraction = 0f; - } -} diff --git a/QSB/EchoesOfTheEye/AlarmTotemSync/WorldObjects/QSBAlarmTotem.cs b/QSB/EchoesOfTheEye/AlarmTotemSync/WorldObjects/QSBAlarmTotem.cs index 69ff7f9b..b8a3affd 100644 --- a/QSB/EchoesOfTheEye/AlarmTotemSync/WorldObjects/QSBAlarmTotem.cs +++ b/QSB/EchoesOfTheEye/AlarmTotemSync/WorldObjects/QSBAlarmTotem.cs @@ -1,83 +1,17 @@ -using Cysharp.Threading.Tasks; +using QSB.AuthoritySync; using QSB.EchoesOfTheEye.AlarmTotemSync.Messages; using QSB.Messaging; -using QSB.Player; -using QSB.WorldSync; -using System.Collections.Generic; -using System.Threading; namespace QSB.EchoesOfTheEye.AlarmTotemSync.WorldObjects; -/// -/// TODO: make this not NRE (by not doing enable sync) and then readd it back in -/// -public class QSBAlarmTotem : WorldObject +public class QSBAlarmTotem : AuthWorldObject { - public readonly List VisibleFor = new(); - public bool IsLocallyVisible; + public override bool CanOwn => AttachedObject.enabled; public override void SendInitialState(uint to) { - this.SendMessage(new TotemEnabledMessage(AttachedObject.enabled) { To = to }); - this.SendMessage(new TotemVisibleForMessage(VisibleFor) { To = to }); - } + base.SendInitialState(to); - public override async UniTask Init(CancellationToken ct) => - QSBPlayerManager.OnRemovePlayer += OnPlayerLeave; - - public override void OnRemoval() => - QSBPlayerManager.OnRemovePlayer -= OnPlayerLeave; - - private void OnPlayerLeave(PlayerInfo player) => - VisibleFor.QuickRemove(player.PlayerId); - - public void SetVisible(uint playerId, bool visible) - { - if (visible) - { - VisibleFor.SafeAdd(playerId); - } - else - { - VisibleFor.QuickRemove(playerId); - } - } - - public void SetEnabled(bool enabled) - { - if (AttachedObject.enabled == enabled) - { - return; - } - - if (!enabled && - AttachedObject._sector && - AttachedObject._sector.ContainsOccupant(DynamicOccupant.Player)) - { - // local player is in sector, do not disable - return; - } - - AttachedObject.enabled = enabled; - - if (!enabled) - { - AttachedObject._simTotemMaterials[0] = AttachedObject._origSimEyeMaterial; - AttachedObject._simTotemRenderer.sharedMaterials = AttachedObject._simTotemMaterials; - AttachedObject._simVisionConeRenderer.SetColor(AttachedObject._simVisionConeRenderer.GetOriginalColor()); - AttachedObject._pulseLightController.SetIntensity(0f); - /* - if (AttachedObject._isPlayerVisible) - { - AttachedObject._isPlayerVisible = false; - Locator.GetAlarmSequenceController().DecreaseAlarmCounter(); - } - */ - if (IsLocallyVisible) - { - IsLocallyVisible = false; - this.SendMessage(new TotemVisibleMessage(false)); - } - } + this.SendMessage(new SetVisibleMessage(AttachedObject._isPlayerVisible) { To = to }); } } diff --git a/QSB/EchoesOfTheEye/DreamLantern/DreamLanternManager.cs b/QSB/EchoesOfTheEye/DreamLantern/DreamLanternManager.cs index bdb6d5b4..21675177 100644 --- a/QSB/EchoesOfTheEye/DreamLantern/DreamLanternManager.cs +++ b/QSB/EchoesOfTheEye/DreamLantern/DreamLanternManager.cs @@ -10,6 +10,9 @@ public class DreamLanternManager : WorldObjectManager public override WorldObjectScene WorldObjectScene => WorldObjectScene.SolarSystem; public override bool DlcOnly => true; - public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct) => + public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct) + { QSBWorldSync.Init(); + QSBWorldSync.Init(); + } } diff --git a/QSB/ItemSync/WorldObjects/Items/QSBDreamLanternItem.cs b/QSB/EchoesOfTheEye/DreamLantern/WorldObjects/QSBDreamLanternItem.cs similarity index 95% rename from QSB/ItemSync/WorldObjects/Items/QSBDreamLanternItem.cs rename to QSB/EchoesOfTheEye/DreamLantern/WorldObjects/QSBDreamLanternItem.cs index f3a33e81..877fc157 100644 --- a/QSB/ItemSync/WorldObjects/Items/QSBDreamLanternItem.cs +++ b/QSB/EchoesOfTheEye/DreamLantern/WorldObjects/QSBDreamLanternItem.cs @@ -1,9 +1,10 @@ using Cysharp.Threading.Tasks; +using QSB.ItemSync.WorldObjects.Items; using System.Linq; using System.Threading; using UnityEngine; -namespace QSB.ItemSync.WorldObjects.Items; +namespace QSB.EchoesOfTheEye.DreamLantern.WorldObjects; public class QSBDreamLanternItem : QSBItem { diff --git a/QSB/EchoesOfTheEye/DreamWorld/Messages/DreamWorldFakePlayer.cs b/QSB/EchoesOfTheEye/DreamWorld/Messages/DreamWorldFakePlayer.cs new file mode 100644 index 00000000..10661e10 --- /dev/null +++ b/QSB/EchoesOfTheEye/DreamWorld/Messages/DreamWorldFakePlayer.cs @@ -0,0 +1,114 @@ +using HarmonyLib; +using QSB.Animation.Player; +using QSB.Messaging; +using QSB.Patches; +using QSB.Player; +using QSB.Utility; +using System.Collections.Generic; +using UnityEngine; + +namespace QSB.EchoesOfTheEye.DreamWorld.Messages; + +public class DreamWorldFakePlayer : MonoBehaviour +{ + private static readonly List _instances = new(); + + public static void Create(PlayerInfo player) + { + var go = new GameObject($"player {player} DreamWorldFakePlayer"); + go.SetActive(false); + go.AddComponent()._player = player; + go.SetActive(true); + } + + public static void Destroy(PlayerInfo player) + { + foreach (var dreamWorldFakePlayer in _instances) + { + if (dreamWorldFakePlayer._player == player) + { + Destroy(dreamWorldFakePlayer.gameObject); + } + } + } + + private PlayerInfo _player; + + private void Awake() + { + _instances.SafeAdd(this); + QSBPlayerManager.OnRemovePlayer += OnRemovePlayer; + + transform.parent = _player.TransformSync.ReferenceTransform; + transform.localPosition = _player.TransformSync.transform.position; + transform.localRotation = _player.TransformSync.transform.rotation; + + #region fake player + + var fakePlayer = _player.Body.transform.Find("REMOTE_Traveller_HEA_Player_v2").gameObject.InstantiateInactive(); + fakePlayer.transform.SetParent(transform, false); + + Destroy(fakePlayer.GetComponent()); + Destroy(fakePlayer.GetComponent()); + + var REMOTE_ItemCarryTool = fakePlayer.transform.Find( + // TODO : kill me for my sins + "Traveller_Rig_v01:Traveller_Trajectory_Jnt/" + + "Traveller_Rig_v01:Traveller_ROOT_Jnt/" + + "Traveller_Rig_v01:Traveller_Spine_01_Jnt/" + + "Traveller_Rig_v01:Traveller_Spine_02_Jnt/" + + "Traveller_Rig_v01:Traveller_Spine_Top_Jnt/" + + "Traveller_Rig_v01:Traveller_RT_Arm_Clavicle_Jnt/" + + "Traveller_Rig_v01:Traveller_RT_Arm_Shoulder_Jnt/" + + "Traveller_Rig_v01:Traveller_RT_Arm_Elbow_Jnt/" + + "Traveller_Rig_v01:Traveller_RT_Arm_Wrist_Jnt/" + + "REMOTE_ItemCarryTool" + ).gameObject; + Destroy(REMOTE_ItemCarryTool); + + fakePlayer.SetActive(true); + + #endregion + } + + private void OnDestroy() + { + _instances.QuickRemove(this); + QSBPlayerManager.OnRemovePlayer -= OnRemovePlayer; + } + + private void OnRemovePlayer(PlayerInfo player) + { + if (player != _player) + { + return; + } + Destroy(gameObject); + } +} + +public class DreamWorldFakePlayerPatch : QSBPatch +{ + public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect; + + /// + /// do this early to create the fake player BEFORE teleporting + /// + [HarmonyPatch(typeof(DreamWorldController), nameof(DreamWorldController.EnterDreamWorld))] + [HarmonyPrefix] + private static void EnterDreamWorld() + { + if (Locator.GetToolModeSwapper().GetItemCarryTool().GetHeldItemType() == ItemType.DreamLantern) + { + new DreamWorldFakePlayerMessage().Send(); + } + } +} + +public class DreamWorldFakePlayerMessage : QSBMessage +{ + public override void OnReceiveRemote() + { + DreamWorldFakePlayer.Create(QSBPlayerManager.GetPlayer(From)); + } +} diff --git a/QSB/EchoesOfTheEye/DreamWorld/Messages/EnterDreamWorldMessage.cs b/QSB/EchoesOfTheEye/DreamWorld/Messages/EnterDreamWorldMessage.cs index 5b9f9e05..7bd9b6ad 100644 --- a/QSB/EchoesOfTheEye/DreamWorld/Messages/EnterDreamWorldMessage.cs +++ b/QSB/EchoesOfTheEye/DreamWorld/Messages/EnterDreamWorldMessage.cs @@ -1,5 +1,5 @@ -using QSB.EchoesOfTheEye.Ghosts.WorldObjects; -using QSB.ItemSync.WorldObjects.Items; +using QSB.EchoesOfTheEye.DreamLantern.WorldObjects; +using QSB.EchoesOfTheEye.Ghosts.WorldObjects; using QSB.Messaging; using QSB.Player; using QSB.Player.TransformSync; diff --git a/QSB/EchoesOfTheEye/DreamWorld/Messages/ExitDreamWorldMessage.cs b/QSB/EchoesOfTheEye/DreamWorld/Messages/ExitDreamWorldMessage.cs index b1302cf8..373e25d3 100644 --- a/QSB/EchoesOfTheEye/DreamWorld/Messages/ExitDreamWorldMessage.cs +++ b/QSB/EchoesOfTheEye/DreamWorld/Messages/ExitDreamWorldMessage.cs @@ -50,5 +50,8 @@ internal class ExitDreamWorldMessage : QSBMessage ghost.GetEffects().OnSectorOccupantsUpdated(); } } + + Locator.GetAlarmSequenceController().OnExitDreamWorld(); + DreamWorldFakePlayer.Destroy(player); } } diff --git a/QSB/EchoesOfTheEye/RaftSync/Messages/RaftDockOnPressInteractMessage.cs b/QSB/EchoesOfTheEye/RaftSync/Messages/RaftDockOnPressInteractMessage.cs index 1e699b51..848e599f 100644 --- a/QSB/EchoesOfTheEye/RaftSync/Messages/RaftDockOnPressInteractMessage.cs +++ b/QSB/EchoesOfTheEye/RaftSync/Messages/RaftDockOnPressInteractMessage.cs @@ -5,5 +5,5 @@ namespace QSB.EchoesOfTheEye.RaftSync.Messages; public class RaftDockOnPressInteractMessage : QSBWorldObjectMessage { - public override void OnReceiveRemote() => WorldObject.OnPressInteract(); + public override void OnReceiveRemote() => WorldObject.AttachedObject.OnPressInteract(); } diff --git a/QSB/EchoesOfTheEye/RaftSync/WorldObjects/QSBRaftDock.cs b/QSB/EchoesOfTheEye/RaftSync/WorldObjects/QSBRaftDock.cs index 7eccf6e3..9b4761f3 100644 --- a/QSB/EchoesOfTheEye/RaftSync/WorldObjects/QSBRaftDock.cs +++ b/QSB/EchoesOfTheEye/RaftSync/WorldObjects/QSBRaftDock.cs @@ -6,6 +6,4 @@ namespace QSB.EchoesOfTheEye.RaftSync.WorldObjects; public class QSBRaftDock : WorldObject, IQSBDropTarget { IItemDropTarget IQSBDropTarget.AttachedObject => AttachedObject; - - public void OnPressInteract() => AttachedObject.OnPressInteract(); } diff --git a/QSB/EchoesOfTheEye/Sarcophagus/WorldObjects/QSBSarcophagus.cs b/QSB/EchoesOfTheEye/Sarcophagus/WorldObjects/QSBSarcophagus.cs index 21adaa72..abee01cf 100644 --- a/QSB/EchoesOfTheEye/Sarcophagus/WorldObjects/QSBSarcophagus.cs +++ b/QSB/EchoesOfTheEye/Sarcophagus/WorldObjects/QSBSarcophagus.cs @@ -8,7 +8,7 @@ public class QSBSarcophagus : WorldObject { public override void SendInitialState(uint to) { - if (AttachedObject._isOpen) + if (AttachedObject._isOpen || AttachedObject._isSlightlyOpen) { this.SendMessage(new OpenMessage()); } diff --git a/QSB/EchoesOfTheEye/VisionTorch/Messages/VisionTorchProjectMessage.cs b/QSB/EchoesOfTheEye/VisionTorch/Messages/VisionTorchProjectMessage.cs new file mode 100644 index 00000000..906810da --- /dev/null +++ b/QSB/EchoesOfTheEye/VisionTorch/Messages/VisionTorchProjectMessage.cs @@ -0,0 +1,17 @@ +using QSB.EchoesOfTheEye.VisionTorch.WorldObjects; +using QSB.Messaging; + +namespace QSB.EchoesOfTheEye.VisionTorch.Messages; + +public class VisionTorchProjectMessage : QSBWorldObjectMessage +{ + public VisionTorchProjectMessage(bool projecting) : base(projecting) { } + + public override void OnReceiveRemote() + { + WorldObject.AttachedObject._isProjecting = Data; + WorldObject.AttachedObject._mindProjectorTrigger.SetProjectorActive(Data); + // prevent the torch from actually doing things remotely + WorldObject.AttachedObject.mindProjectorTrigger._triggerVolume.SetTriggerActivation(false); + } +} diff --git a/QSB/EchoesOfTheEye/VisionTorch/Patches/VisionTorchPatches.cs b/QSB/EchoesOfTheEye/VisionTorch/Patches/VisionTorchPatches.cs new file mode 100644 index 00000000..d0badb34 --- /dev/null +++ b/QSB/EchoesOfTheEye/VisionTorch/Patches/VisionTorchPatches.cs @@ -0,0 +1,45 @@ +using HarmonyLib; +using QSB.EchoesOfTheEye.VisionTorch.Messages; +using QSB.EchoesOfTheEye.VisionTorch.WorldObjects; +using QSB.Messaging; +using QSB.Patches; +using QSB.WorldSync; + +namespace QSB.EchoesOfTheEye.VisionTorch.Patches; + +public class VisionTorchPatches : QSBPatch +{ + public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect; + + [HarmonyPrefix] + [HarmonyPatch(typeof(VisionTorchItem), nameof(VisionTorchItem.Update))] + private static bool Update(VisionTorchItem __instance) + { + if (!QSBWorldSync.AllObjectsReady) + { + return true; + } + + if (PlayerState.IsViewingProjector() && __instance._mindSlideProjector.mindSlideCollection.slideCollectionContainer.slideIndex == 1) + { + OWInput.ChangeInputMode(InputMode.None); + __instance._mindSlideProjector.OnProjectionComplete += __instance.OnProjectionComplete; + __instance.enabled = false; + return false; + } + __instance._wasProjecting = __instance._isProjecting; + __instance._isProjecting = OWInput.IsPressed(InputLibrary.toolActionPrimary, InputMode.Character); + if (__instance._isProjecting && !__instance._wasProjecting) + { + __instance._mindProjectorTrigger.SetProjectorActive(true); + __instance.GetWorldObject().SendMessage(new VisionTorchProjectMessage(true)); + } + else if (!__instance._isProjecting && __instance._wasProjecting) + { + __instance._mindProjectorTrigger.SetProjectorActive(false); + __instance.GetWorldObject().SendMessage(new VisionTorchProjectMessage(false)); + } + + return false; + } +} diff --git a/QSB/EchoesOfTheEye/VisionTorch/VisionTorchManager.cs b/QSB/EchoesOfTheEye/VisionTorch/VisionTorchManager.cs new file mode 100644 index 00000000..709126b6 --- /dev/null +++ b/QSB/EchoesOfTheEye/VisionTorch/VisionTorchManager.cs @@ -0,0 +1,15 @@ +using Cysharp.Threading.Tasks; +using QSB.EchoesOfTheEye.VisionTorch.WorldObjects; +using QSB.WorldSync; +using System.Threading; + +namespace QSB.EchoesOfTheEye.VisionTorch; + +public class VisionTorchManager : 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/VisionTorch/WorldObjects/QSBVisionTorchItem.cs b/QSB/EchoesOfTheEye/VisionTorch/WorldObjects/QSBVisionTorchItem.cs new file mode 100644 index 00000000..09b6961a --- /dev/null +++ b/QSB/EchoesOfTheEye/VisionTorch/WorldObjects/QSBVisionTorchItem.cs @@ -0,0 +1,13 @@ +using QSB.EchoesOfTheEye.VisionTorch.Messages; +using QSB.ItemSync.WorldObjects.Items; +using QSB.Messaging; + +namespace QSB.EchoesOfTheEye.VisionTorch.WorldObjects; + +public class QSBVisionTorchItem : QSBItem +{ + public override void SendInitialState(uint to) + { + this.SendMessage(new VisionTorchProjectMessage(AttachedObject._isProjecting) { To = to }); + } +} diff --git a/QSB/GetAttachedOWRigidbodyPatch.cs b/QSB/GetAttachedOWRigidbodyPatch.cs index 7ee82ab1..2d045faa 100644 --- a/QSB/GetAttachedOWRigidbodyPatch.cs +++ b/QSB/GetAttachedOWRigidbodyPatch.cs @@ -4,9 +4,6 @@ using UnityEngine; namespace QSB; -/// -/// TODO: TEST THIS. see if things horribly break. this could be huge. -/// [HarmonyPatch(typeof(OWExtensions))] public class GetAttachedOWRigidbodyPatch : QSBPatch { diff --git a/QSB/Inputs/Patches/InputPatches.cs b/QSB/Inputs/Patches/InputPatches.cs index c482ace7..b4ad08fa 100644 --- a/QSB/Inputs/Patches/InputPatches.cs +++ b/QSB/Inputs/Patches/InputPatches.cs @@ -9,7 +9,23 @@ internal class InputPatches : QSBPatch public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect; [HarmonyPrefix] - [HarmonyPatch(typeof(OWInput), nameof(OWInput.Update))] - public static bool OWInput_Update() - => QSBInputManager.Instance.InputsEnabled; + [HarmonyPatch(typeof(AbstractCommands), nameof(AbstractCommands.Update))] + public static bool AbstractCommands_Update(AbstractCommands __instance) + { + if (QSBInputManager.Instance.InputsEnabled) + { + return true; + } + + __instance.Consumed = false; + __instance.WasActiveLastFrame = __instance.IsActiveThisFrame; + __instance.IsActiveThisFrame = false; + + if (__instance.WasActiveLastFrame) + { + __instance.InputStartedTime = float.MaxValue; + } + + return false; + } } \ No newline at end of file diff --git a/QSB/ItemSync/ItemManager.cs b/QSB/ItemSync/ItemManager.cs index 7bf6413a..197a416d 100644 --- a/QSB/ItemSync/ItemManager.cs +++ b/QSB/ItemSync/ItemManager.cs @@ -5,7 +5,6 @@ using QSB.ItemSync.WorldObjects.Items; using QSB.ItemSync.WorldObjects.Sockets; using QSB.Utility; using QSB.WorldSync; -using System.Collections.Generic; using System.Linq; using System.Threading; using UnityEngine; @@ -21,14 +20,13 @@ internal class ItemManager : WorldObjectManager DebugLog.DebugWrite("Building OWItems...", MessageType.Info); // Items - QSBWorldSync.Init(); QSBWorldSync.Init(); QSBWorldSync.Init(); QSBWorldSync.Init(); QSBWorldSync.Init(); QSBWorldSync.Init(); - QSBWorldSync.Init(); QSBWorldSync.Init(); + // dream lantern and vision torch are set up in their own managers // Sockets QSBWorldSync.Init(); diff --git a/QSB/ItemSync/Patches/ItemRemotePatches.cs b/QSB/ItemSync/Patches/ItemRemotePatches.cs index ebcf4740..ff45b34c 100644 --- a/QSB/ItemSync/Patches/ItemRemotePatches.cs +++ b/QSB/ItemSync/Patches/ItemRemotePatches.cs @@ -149,6 +149,12 @@ internal class ItemRemotePatches : QSBPatch return true; } + if (__instance._isProjecting) + { + __instance._mindProjectorTrigger.SetProjectorActive(false); + __instance._isProjecting = false; + } + // if (Locator.GetDreamWorldController().IsInDream()) // { base_DropItem(__instance, position, normal, parent, sector, customDropTarget); @@ -213,6 +219,12 @@ internal class ItemRemotePatches : QSBPatch return true; } + if (__instance._isProjecting) + { + __instance._mindProjectorTrigger.SetProjectorActive(false); + __instance._isProjecting = false; + } + base_SocketItem(__instance, socketTransform, sector); if (__instance._visionBeam != null) { diff --git a/QSB/ItemSync/WorldObjects/Items/QSBVisionTorchItem.cs b/QSB/ItemSync/WorldObjects/Items/QSBVisionTorchItem.cs deleted file mode 100644 index 10ffd92a..00000000 --- a/QSB/ItemSync/WorldObjects/Items/QSBVisionTorchItem.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace QSB.ItemSync.WorldObjects.Items; - -/// -/// TODO: SYNC THIS SHIT LMAOOOOOO -/// -internal class QSBVisionTorchItem : QSBItem { } \ No newline at end of file diff --git a/QSB/Localization/QSBLocalization.cs b/QSB/Localization/QSBLocalization.cs index ea94b74c..a81af601 100644 --- a/QSB/Localization/QSBLocalization.cs +++ b/QSB/Localization/QSBLocalization.cs @@ -2,7 +2,6 @@ using QSB.Utility; using System; using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Linq; using System.Reflection; @@ -82,28 +81,4 @@ public static class QSBLocalization Current = newTranslation; LanguageChanged?.Invoke(); } - - public static CultureInfo CultureInfo - => Current.Language switch - { - /* - * Language tags from BCP-47 standard, implemented by windows - * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid/a9eac961-e77d-41a6-90a5-ce1a8b0cdb9c - * I have no fucking idea if this will work on linux. ¯\_(ツ)_/¯ - */ - - TextTranslation.Language.ENGLISH => new CultureInfo("en"), - TextTranslation.Language.SPANISH_LA => new CultureInfo("es-419"), - TextTranslation.Language.GERMAN => new CultureInfo("de"), - TextTranslation.Language.FRENCH => new CultureInfo("fr"), - TextTranslation.Language.ITALIAN => new CultureInfo("it"), - TextTranslation.Language.POLISH => new CultureInfo("pl"), - TextTranslation.Language.PORTUGUESE_BR => new CultureInfo("pt-BR"), - TextTranslation.Language.JAPANESE => new CultureInfo("ja"), - TextTranslation.Language.RUSSIAN => new CultureInfo("ru"), - TextTranslation.Language.CHINESE_SIMPLE => new CultureInfo("zh-Hans"), - TextTranslation.Language.KOREAN => new CultureInfo("ko"), - TextTranslation.Language.TURKISH => new CultureInfo("tr"), - _ => new CultureInfo("en") // what - }; } diff --git a/QSB/Menus/MenuManager.cs b/QSB/Menus/MenuManager.cs index 007e5910..22c78780 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.22.0\r\nFixed lots of bugs, and added lots of SFX and VFX stuff."; + private const string UpdateChangelog = "QSB Version 0.23.0\r\nA lot of small improvements and bug fixes."; private Action PopupClose; diff --git a/QSB/ModelShip/Messages/UseFlightConsoleMessage.cs b/QSB/ModelShip/Messages/UseFlightConsoleMessage.cs index 464e7f3b..f4abfe95 100644 --- a/QSB/ModelShip/Messages/UseFlightConsoleMessage.cs +++ b/QSB/ModelShip/Messages/UseFlightConsoleMessage.cs @@ -74,6 +74,9 @@ internal class UseFlightConsoleMessage : QSBMessage // 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); + if (console._modelShipBody) // for when model ship is destroyed + { + console.RespawnModelShip(false); + } } } diff --git a/QSB/ModelShip/ModelShipThrusterVariableSyncer.cs b/QSB/ModelShip/ModelShipThrusterVariableSyncer.cs index 30c82784..3e896587 100644 --- a/QSB/ModelShip/ModelShipThrusterVariableSyncer.cs +++ b/QSB/ModelShip/ModelShipThrusterVariableSyncer.cs @@ -1,9 +1,6 @@ -using Mirror; -using QSB.Player; -using QSB.Utility; +using QSB.Player; +using QSB.Player.TransformSync; using QSB.Utility.VariableSync; -using System.Collections.Generic; -using System.Linq; using UnityEngine; namespace QSB.ModelShip; @@ -26,7 +23,7 @@ public class ModelShipThrusterVariableSyncer : MonoBehaviour public void Update() { - if (QSBPlayerManager.LocalPlayer.FlyingModelShip) + if (PlayerTransformSync.LocalInstance && QSBPlayerManager.LocalPlayer.FlyingModelShip) { GetFromShip(); return; diff --git a/QSB/Player/PlayerHUDMarker.cs b/QSB/Player/PlayerHUDMarker.cs index 82131999..02e33c3c 100644 --- a/QSB/Player/PlayerHUDMarker.cs +++ b/QSB/Player/PlayerHUDMarker.cs @@ -1,4 +1,5 @@ -using QSB.Utility; +using QSB.ServerSettings; +using QSB.Utility; using UnityEngine; namespace QSB.Player; @@ -34,7 +35,16 @@ public class PlayerHUDMarker : HUDDistanceMarker return false; } - return _player.IsReady && !_player.IsDead && (!_player.InDreamWorld || QSBPlayerManager.LocalPlayer.InDreamWorld) && _player.Visible; + if (!ServerSettingsManager.ShowPlayerNames) + { + return false; + } + + return _player.IsReady && + !_player.IsDead && + _player.Visible && + _player.InDreamWorld == QSBPlayerManager.LocalPlayer.InDreamWorld && + _player.IsInMoon == QSBPlayerManager.LocalPlayer.IsInMoon; } private void Update() diff --git a/QSB/Player/PlayerInfo.cs b/QSB/Player/PlayerInfo.cs index 06715acd..c6a0fe42 100644 --- a/QSB/Player/PlayerInfo.cs +++ b/QSB/Player/PlayerInfo.cs @@ -22,6 +22,7 @@ public partial class PlayerInfo public uint PlayerId { get; } public string Name { get; set; } public PlayerHUDMarker HudMarker { get; set; } + public PlayerMapMarker MapMarker { get; set; } public PlayerTransformSync TransformSync { get; } public ClientState State { get; set; } public EyeState EyeState { get; set; } diff --git a/QSB/Player/PlayerInfoParts/DreamWorld.cs b/QSB/Player/PlayerInfoParts/DreamWorld.cs index 4060de67..600150d4 100644 --- a/QSB/Player/PlayerInfoParts/DreamWorld.cs +++ b/QSB/Player/PlayerInfoParts/DreamWorld.cs @@ -1,4 +1,6 @@ -using QSB.ItemSync.WorldObjects.Items; +using QSB.EchoesOfTheEye.DreamLantern; +using QSB.EchoesOfTheEye.DreamLantern.WorldObjects; +using QSB.ItemSync.WorldObjects.Items; namespace QSB.Player; diff --git a/QSB/Player/PlayerMapMarker.cs b/QSB/Player/PlayerMapMarker.cs index 4d35a734..417b7040 100644 --- a/QSB/Player/PlayerMapMarker.cs +++ b/QSB/Player/PlayerMapMarker.cs @@ -1,4 +1,5 @@ -using QSB.Utility; +using QSB.ServerSettings; +using QSB.Utility; using UnityEngine; namespace QSB.Player; @@ -31,6 +32,7 @@ public class PlayerMapMarker : MonoBehaviour public void Init(PlayerInfo player) { _player = player; + _player.MapMarker = this; _hasBeenSetUpForInit = true; } @@ -57,10 +59,20 @@ public class PlayerMapMarker : MonoBehaviour return false; } + if (!ServerSettingsManager.ShowPlayerNames) + { + return false; + } + var playerScreenPos = Locator.GetActiveCamera().WorldToScreenPoint(transform.position); var isInfrontOfCamera = playerScreenPos.z > 0f; - return _player.IsReady && !_player.IsDead && (!_player.InDreamWorld || QSBPlayerManager.LocalPlayer.InDreamWorld) && _player.Visible && isInfrontOfCamera; + return isInfrontOfCamera && + _player.IsReady && + !_player.IsDead && + _player.Visible && + _player.InDreamWorld == QSBPlayerManager.LocalPlayer.InDreamWorld && + _player.IsInMoon == QSBPlayerManager.LocalPlayer.IsInMoon; } public void LateUpdate() @@ -77,4 +89,9 @@ public class PlayerMapMarker : MonoBehaviour _canvasMarker.SetVisibility(shouldBeVisible); } } + + public void Remove() + { + // TODO + } } \ No newline at end of file diff --git a/QSB/Player/TransformSync/PlayerTransformSync.cs b/QSB/Player/TransformSync/PlayerTransformSync.cs index ee2bc73d..c030693c 100644 --- a/QSB/Player/TransformSync/PlayerTransformSync.cs +++ b/QSB/Player/TransformSync/PlayerTransformSync.cs @@ -54,6 +54,7 @@ public class PlayerTransformSync : SectoredTransformSync QSBPatch.Remote = false; base.OnStopClient(); Player.HudMarker?.Remove(); + Player.MapMarker?.Remove(); QSBPlayerManager.PlayerList.Remove(Player); DebugLog.DebugWrite($"Remove Player : {Player}", MessageType.Info); } diff --git a/QSB/PlayerBodySetup/Remote/ShaderReplacer.cs b/QSB/PlayerBodySetup/Remote/ShaderReplacer.cs index 29d20295..338b436b 100644 --- a/QSB/PlayerBodySetup/Remote/ShaderReplacer.cs +++ b/QSB/PlayerBodySetup/Remote/ShaderReplacer.cs @@ -27,7 +27,20 @@ public static class ShaderReplacer continue; } - material.shader = replacementShader; + // preserve override tag and render queue (for Standard shader) + // keywords and properties are already preserved + if (material.renderQueue != material.shader.renderQueue) + { + var renderType = material.GetTag("RenderType", false); + var renderQueue = material.renderQueue; + material.shader = replacementShader; + material.SetOverrideTag("RenderType", renderType); + material.renderQueue = renderQueue; + } + else + { + material.shader = replacementShader; + } } } } diff --git a/QSB/QSB.csproj b/QSB/QSB.csproj index f3bdfee4..177373de 100644 --- a/QSB/QSB.csproj +++ b/QSB/QSB.csproj @@ -7,17 +7,6 @@ CS1998;CS0649 - - - <_Files Remove="@(_Files)" /> - <_Files Include="$(OutputPath)\*.dll" /> - <_Files Include="$(OutputPath)\*.exe" /> - <_Files Include="$(OutputPath)\*.pdb" /> - - <_Files Include="$(OutputPath)\AssetBundles\*" /> - - - <_Files Remove="@(_Files)" /> @@ -31,33 +20,13 @@ - $(GameDir)\OuterWilds_Data\Managed $(UnityAssetsDir)\Dlls - - - <_Files Remove="@(_Files)" /> - <_Files Include="$(UnityDllsDir)\*.dll" /> - <_Files Include="$(UnityDllsDir)\*.exe" /> - <_Files Include="$(UnityDllsDir)\*.pdb" /> - - - + <_Files Remove="@(_Files)" /> <_Files Include="$(OutputPath)\*.dll" /> <_Files Include="$(OutputPath)\*.exe" /> - <_Files Include="$(OutputPath)\*.pdb" /> - - <_Files Include="$(GameDllsDir)\EOS-SDK.dll" /> - <_Files Include="$(GameDllsDir)\Autofac.dll" /> - <_Files Include="$(GameDllsDir)\Newtonsoft.Json.dll" /> - <_Files Include="$(GameDllsDir)\0Harmony.dll" /> - <_Files Include="$(GameDllsDir)\MonoMod*.dll" /> - <_Files Include="$(GameDllsDir)\Mono.Cecil.dll" /> - <_Files Include="$(GameDllsDir)\OWML*.dll" /> - <_Files Include="$(GameDllsDir)\NAudio-Unity.dll" /> - <_Files Include="$(GameDllsDir)\com.rlabrecque.steamworks.net.dll" /> @@ -90,7 +59,7 @@ - + diff --git a/QSB/QSBCore.cs b/QSB/QSBCore.cs index b7107571..9cf2191f 100644 --- a/QSB/QSBCore.cs +++ b/QSB/QSBCore.cs @@ -4,9 +4,11 @@ using OWML.Common; using OWML.ModHelper; using QSB.Localization; using QSB.Menus; +using QSB.Messaging; using QSB.Patches; using QSB.QuantumSync; using QSB.SaveSync; +using QSB.ServerSettings; using QSB.Utility; using QSB.WorldSync; using System; @@ -56,6 +58,8 @@ public class QSBCore : ModBehaviour Application.version.Split('.').Take(3).Join(delimiter: "."); public static bool DLCInstalled => EntitlementsManager.IsDlcOwned() == EntitlementsManager.AsyncOwnershipStatus.Owned; public static bool IncompatibleModsAllowed { get; private set; } + public static bool ShowPlayerNames { get; private set; } + public static bool ShipDamage { get; private set; } public static GameVendor GameVendor { get; private set; } = GameVendor.None; public static bool IsStandalone => GameVendor is GameVendor.Epic or GameVendor.Steam; public static IProfileManager ProfileManager => IsStandalone @@ -242,6 +246,14 @@ public class QSBCore : ModBehaviour { DefaultServerIP = config.GetSettingsValue("defaultServerIP"); IncompatibleModsAllowed = config.GetSettingsValue("incompatibleModsAllowed"); + ShowPlayerNames = config.GetSettingsValue("showPlayerNames"); + ShipDamage = config.GetSettingsValue("shipDamage"); + + if (IsHost) + { + ServerSettingsManager.ServerShowPlayerNames = ShowPlayerNames; + new ServerSettingsMessage().Send(); + } } private void Update() diff --git a/QSB/QSBNetworkManager.cs b/QSB/QSBNetworkManager.cs index 28a05a52..9d920dbf 100644 --- a/QSB/QSBNetworkManager.cs +++ b/QSB/QSBNetworkManager.cs @@ -315,7 +315,11 @@ public class QSBNetworkManager : NetworkManager, IAddComponentOnStart Destroy(GetComponent()); Destroy(GetComponent()); Destroy(GetComponent()); - QSBPlayerManager.PlayerList.ForEach(player => player.HudMarker?.Remove()); + QSBPlayerManager.PlayerList.ForEach(player => + { + player.HudMarker?.Remove(); + player.MapMarker?.Remove(); + }); QSBWorldSync.RemoveWorldObjects(); @@ -397,7 +401,11 @@ public class QSBNetworkManager : NetworkManager, IAddComponentOnStart DebugLog.DebugWrite("OnStopServer", MessageType.Info); Destroy(GetComponent()); DebugLog.ToConsole("Server stopped!", MessageType.Info); - QSBPlayerManager.PlayerList.ForEach(player => player.HudMarker?.Remove()); + QSBPlayerManager.PlayerList.ForEach(player => + { + player.HudMarker?.Remove(); + player.MapMarker?.Remove(); + }); base.OnStopServer(); } diff --git a/QSB/RespawnSync/RespawnHUDMarker.cs b/QSB/RespawnSync/RespawnHUDMarker.cs index f9653a5a..f135d114 100644 --- a/QSB/RespawnSync/RespawnHUDMarker.cs +++ b/QSB/RespawnSync/RespawnHUDMarker.cs @@ -12,7 +12,7 @@ public class RespawnHUDMarker : HUDDistanceMarker { _markerRadius = 0.2f; _markerTarget = transform; - _markerLabel = QSBLocalization.Current.RespawnPlayer.ToUpper(QSBLocalization.CultureInfo); + _markerLabel = QSBLocalization.Current.RespawnPlayer.ToUpper(); _isReady = true; base.InitCanvasMarker(); diff --git a/QSB/SaveSync/Messages/RequestGameStateMessage.cs b/QSB/SaveSync/Messages/RequestGameStateMessage.cs index 42a24547..da49e43c 100644 --- a/QSB/SaveSync/Messages/RequestGameStateMessage.cs +++ b/QSB/SaveSync/Messages/RequestGameStateMessage.cs @@ -1,6 +1,7 @@ using QSB.ConversationSync.Messages; using QSB.Messaging; using QSB.Player; +using QSB.ServerSettings; using QSB.Utility; namespace QSB.SaveSync.Messages; @@ -21,6 +22,7 @@ internal class RequestGameStateMessage : QSBMessage } new GameStateMessage(From).Send(); + new ServerSettingsMessage().Send(); var gameSave = PlayerData._currentGameSave; diff --git a/QSB/ServerSettings/ServerSettingsManager.cs b/QSB/ServerSettings/ServerSettingsManager.cs new file mode 100644 index 00000000..505f0ba2 --- /dev/null +++ b/QSB/ServerSettings/ServerSettingsManager.cs @@ -0,0 +1,10 @@ +using QSB.Utility; +using UnityEngine; + +namespace QSB.ServerSettings; + +internal class ServerSettingsManager : MonoBehaviour, IAddComponentOnStart +{ + public static bool ServerShowPlayerNames; + public static bool ShowPlayerNames => (ServerShowPlayerNames || QSBCore.IsHost) && QSBCore.ShowPlayerNames; +} diff --git a/QSB/ServerSettings/ServerSettingsMessage.cs b/QSB/ServerSettings/ServerSettingsMessage.cs new file mode 100644 index 00000000..1b88b00c --- /dev/null +++ b/QSB/ServerSettings/ServerSettingsMessage.cs @@ -0,0 +1,27 @@ +using Mirror; +using QSB.Messaging; + +namespace QSB.ServerSettings; + +internal class ServerSettingsMessage : QSBMessage +{ + private bool _showPlayerNames; + + public ServerSettingsMessage() + => _showPlayerNames = QSBCore.ShowPlayerNames; + + public override void Serialize(NetworkWriter writer) + { + base.Serialize(writer); + writer.Write(_showPlayerNames); + } + + public override void Deserialize(NetworkReader reader) + { + base.Deserialize(reader); + _showPlayerNames = reader.ReadBool(); + } + + public override void OnReceiveRemote() + => ServerSettingsManager.ServerShowPlayerNames = _showPlayerNames; +} diff --git a/QSB/ShipSync/ShipThrusterVariableSyncer.cs b/QSB/ShipSync/ShipThrusterVariableSyncer.cs index c8e31673..bb722be1 100644 --- a/QSB/ShipSync/ShipThrusterVariableSyncer.cs +++ b/QSB/ShipSync/ShipThrusterVariableSyncer.cs @@ -1,7 +1,7 @@ using Mirror; using QSB.Player; +using QSB.Player.TransformSync; using QSB.Utility.VariableSync; -using QSB.WorldSync; using UnityEngine; namespace QSB.ShipSync; @@ -21,7 +21,7 @@ public class ShipThrusterVariableSyncer : NetworkBehaviour public void Update() { - if (QSBPlayerManager.LocalPlayer.FlyingShip) + if (PlayerTransformSync.LocalInstance && QSBPlayerManager.LocalPlayer.FlyingShip) { GetFromShip(); return; diff --git a/QSB/TimeSync/Patches/TimePatches.cs b/QSB/TimeSync/Patches/TimePatches.cs index 24eab5fa..66d7899c 100644 --- a/QSB/TimeSync/Patches/TimePatches.cs +++ b/QSB/TimeSync/Patches/TimePatches.cs @@ -4,6 +4,7 @@ using QSB.Messaging; using QSB.Patches; using QSB.TimeSync.Messages; using QSB.Utility; +using UnityEngine; namespace QSB.TimeSync.Patches; @@ -25,8 +26,8 @@ internal class TimePatches : QSBPatch public static void PlayerCameraEffectController_WakeUp(PlayerCameraEffectController __instance) { // prevent funny thing when you pause while waking up - QSBInputManager.Instance.SetInputsEnabled(false); - Delay.RunWhen(() => !__instance._isOpeningEyes, () => QSBInputManager.Instance.SetInputsEnabled(true)); + Locator.GetPauseCommandListener().AddPauseCommandLock(); + Delay.RunWhen(() => !__instance._isOpeningEyes, () => Locator.GetPauseCommandListener().RemovePauseCommandLock()); } [HarmonyPrefix] diff --git a/QSB/TimeSync/WakeUpSync.cs b/QSB/TimeSync/WakeUpSync.cs index 8366a995..26479397 100644 --- a/QSB/TimeSync/WakeUpSync.cs +++ b/QSB/TimeSync/WakeUpSync.cs @@ -8,6 +8,7 @@ using QSB.Messaging; using QSB.Player; using QSB.Player.Messages; using QSB.TimeSync.Messages; +using QSB.TimeSync.Patches; using QSB.Utility; using QSB.WorldSync; using System; @@ -46,7 +47,6 @@ public class WakeUpSync : NetworkBehaviour { OWTime.SetTimeScale(1f); OWTime.SetMaxDeltaTime(0.06666667f); - OWTime.SetFixedTimestep(0.01666667f); Locator.GetActiveCamera().enabled = true; CurrentState = State.NotLoaded; CurrentReason = null; @@ -214,7 +214,6 @@ public class WakeUpSync : NetworkBehaviour CurrentState = State.FastForwarding; CurrentReason = reason; OWTime.SetMaxDeltaTime(0.033333335f); - OWTime.SetFixedTimestep(0.033333335f); TimeSyncUI.TargetTime = _serverTime; TimeSyncUI.Start(TimeSyncType.FastForwarding, reason); } @@ -245,7 +244,6 @@ public class WakeUpSync : NetworkBehaviour { OWTime.SetTimeScale(1f); OWTime.SetMaxDeltaTime(0.06666667f); - OWTime.SetFixedTimestep(0.01666667f); Locator.GetActiveCamera().enabled = true; CurrentState = State.Loaded; CurrentReason = null; diff --git a/QSB/Translations/zh_CN.json b/QSB/Translations/zh_CN.json new file mode 100644 index 00000000..d64142b0 --- /dev/null +++ b/QSB/Translations/zh_CN.json @@ -0,0 +1,126 @@ +{ + "Language": "CHINESE_SIMPLE", + "MainMenuHost": "开启多人游戏", + "MainMenuConnect": "连接至多人游戏", + "PauseMenuDisconnect": "断开连接", + "PauseMenuStopHosting": "关闭服务器", + "PublicIPAddress": "公共IP地址\n\n(您的多人存档数据将被覆盖)", + "ProductUserID": "用户ID\n\n(您的多人存档数据将被覆盖)", + "Connect": "连接", + "Cancel": "取消", + "HostExistingOrNewOrCopy": "您想使用一个现有的多人探险存档,或是开启一个新的存档,还是把现有的单人探险存档复制到多人探险存档?", + "HostNewOrCopy": "您想开启一个新的存档,还是把现有的单人探险存档复制到多人探险存档?", + "HostExistingOrNew": "您想使用一个现有的多人探险存档,还是开启一个新的存档?", + "ExistingSave": "现有存档", + "NewSave": "新建存档", + "CopySave": "复制存档", + "DisconnectAreYouSure": "您确定要断开连接吗?\n将会退出至主菜单。", + "Yes": "是", + "No": "否", + "StopHostingAreYouSure": "您确定要停止服务器吗?\n将会与所有人断开连接并且使他们退出至主菜单。", + "CopyProductUserIDToClipboard": "开启服务器\n其他玩家需要使用您的用户ID连接至服务器,用户ID是:\n{0}\n您想要复制到剪切板吗?", + "Connecting": "连接中……", + "OK": "好的", + "ServerRefusedConnection": "服务器拒绝了您的连接\n{0}", + "ClientDisconnectWithError": "客户端发生错误导致连接断开\n{0}", + "QSBVersionMismatch": "QSB版本号不匹配。(客户端:{0},服务端:{1})", + "OWVersionMismatch": "《星际拓荒》版本号不匹配。(客户端:{0},服务端:{1})", + "DLCMismatch": "DLC安装情况不匹配。(客户端:{0},服务端:{1})", + "GameProgressLimit": "游戏中时间太久了。", + "AddonMismatch": "插件不匹配(客户端:{0}插件,服务端:{1}插件)", + "IncompatibleMod": "使用了不兼容/不允许的模组,检测到的第一个模组是{0}", + "PlayerJoinedTheGame": "{0}加入了游戏!", + "PlayerWasKicked": "{0}被踢出了游戏。", + "KickedFromServer": "被踢出了游戏,理由是:{0}", + "RespawnPlayer": "复活玩家", + "TimeSyncTooFarBehind": "{0}\n快进以匹配服务器时间……", + "TimeSyncWaitingForStartOfServer": "等待服务器开始……", + "TimeSyncTooFarAhead": "{0}\n暂停等待以匹配服务器时间……", + "TimeSyncWaitForAllToReady": "等待开始轮回……", + "TimeSyncWaitForAllToDie": "等待轮回结束……", + "GalaxyMapEveryoneNotPresent": "现在还不是时候。需要所有人见证这个时刻。", + "YouAreDead": "你死了。", + "WaitingForRespawn": "等待某人将你复活……", + "WaitingForAllToDie": "等待{0}死亡……", + "AttachToShip": "固定在飞船上", + "DetachFromShip": "取消固定", + "DeathMessages": { + "Default": [ + "{0}死了", + "{0}被杀了" + ], + "Impact": [ + "{0}忘记使用推进器了", + "{0}拥抱了地面", + "{0}狠狠的撞上了地面", + "{0}爆炸了", + "{0}因为撞击而死", + "{0}撞击地面过猛" + ], + "Asphyxiation": [ + "{0}忘记了需要呼吸", + "{0}窒息了", + "{0}窒息而死", + "{0}忘记了怎么呼吸", + "{0}忘记检查氧气了", + "{0}把空气用完了", + "{0}把氧气用完了", + "{0}不再需要呼吸了" + ], + "Energy": [ + "{0}被烹饪了" + ], + "Supernova": [ + "{0}没有时间了", + "{0}被烧掉了", + "{0}被超新星烤熟了", + "{0}升华了", + "{0}遗忘了时间" + ], + "Digestion": [ + "{0}被吃掉了", + "{0}发现了一条鱼", + "{0}遇到了邪恶生物", + "{0}惹错了鱼", + "{0}被消化了", + "{0}因为消化系统而死" + ], + "Crushed": [ + "{0}被压死了", + "{0}被压扁了", + "{0}埋葬了自己", + "{0}没能及时逃离", + "{0}想在沙子中游泳", + "{0}小看了沙子的力量", + "{0}卡在沙子下面了" + ], + "Lava": [ + "{0}在岩浆中死去", + "{0}融化了", + "{0}尝试在岩浆中游泳", + "{0}掉进了岩浆", + "{0}因为岩浆而死", + "{0}在岩浆中游泳", + "{0}被岩浆烧毁" + ], + "BlackHole": [ + "{0}尝试追寻自己的记忆" + ], + "DreamExplosion": [ + "{0}爆炸了", + "{0}是第一个吃螃蟹的人", + "{0}被炸死了", + "{0}被炸了", + "{0}因为爆炸而死", + "{0}使用了错误的文物" + ], + "CrushedByElevator": [ + "{0}被粉碎了", + "{0}被挤扁了", + "{0}被电梯撞击", + "{0}站在了电梯下", + "{0}变成了一个平板人", + "{0}被电梯挤扁了" + ] + } +} \ No newline at end of file diff --git a/QSB/Utility/DebugActions.cs b/QSB/Utility/DebugActions.cs index 46f85d5a..5f75a53c 100644 --- a/QSB/Utility/DebugActions.cs +++ b/QSB/Utility/DebugActions.cs @@ -1,4 +1,6 @@ using OWML.Common; +using QSB.EchoesOfTheEye.DreamLantern; +using QSB.EchoesOfTheEye.DreamLantern.WorldObjects; using QSB.ItemSync.WorldObjects.Items; using QSB.Messaging; using QSB.Player; diff --git a/QSB/Utility/DebugLog.cs b/QSB/Utility/DebugLog.cs index efbbfb5a..90badb74 100644 --- a/QSB/Utility/DebugLog.cs +++ b/QSB/Utility/DebugLog.cs @@ -5,6 +5,8 @@ using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; +#pragma warning disable CS0618 + namespace QSB.Utility; public static class DebugLog diff --git a/QSB/Utility/DeterministicManager.cs b/QSB/Utility/DeterministicManager.cs index c1982a1d..d6a71aef 100644 --- a/QSB/Utility/DeterministicManager.cs +++ b/QSB/Utility/DeterministicManager.cs @@ -31,11 +31,11 @@ public static class DeterministicManager } }; - public static void OnWorldObjectsReady() + public static void OnWorldObjectsAdded() { if (QSBCore.DebugSettings.DumpWorldObjects) { - using (var file = File.CreateText(Path.Combine(QSBCore.Helper.Manifest.ModFolderPath, "world objects.csv"))) + using (var file = File.CreateText(Path.Combine(QSBCore.Helper.Manifest.ModFolderPath, $"[{DebugLog.ProcessInstanceId}] world objects.csv"))) { file.WriteLine("world object,deterministic path"); foreach (var worldObject in QSBWorldSync.GetWorldObjects()) @@ -51,7 +51,7 @@ public static class DeterministicManager } } - using (var file = File.CreateText(Path.Combine(QSBCore.Helper.Manifest.ModFolderPath, "cache.csv"))) + using (var file = File.CreateText(Path.Combine(QSBCore.Helper.Manifest.ModFolderPath, $"[{DebugLog.ProcessInstanceId}] cache.csv"))) { file.WriteLine("name,instance id,sibling index,parent,parent instance id"); foreach (var (transform, (siblingIndex, parent)) in _cache) @@ -267,7 +267,7 @@ public static class DeterministicManager } /// - /// only call this before world objects ready + /// only call this before world objects added /// public static string DeterministicPath(this Component component) { @@ -298,7 +298,7 @@ public static class DeterministicManager } /// - /// only call this before world objects ready + /// only call this before world objects added /// public static IEnumerable SortDeterministic(this IEnumerable components) where T : Component => components.OrderBy(DeterministicPath); diff --git a/QSB/Utility/Extensions.cs b/QSB/Utility/Extensions.cs index 17c5c865..eede0565 100644 --- a/QSB/Utility/Extensions.cs +++ b/QSB/Utility/Extensions.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Text; using UnityEngine; using Object = UnityEngine.Object; @@ -214,5 +215,23 @@ public static class Extensions } } + // https://stackoverflow.com/a/24031467 + public static string GetMD5Hash(this IEnumerable list) + { + using var md5 = System.Security.Cryptography.MD5.Create(); + + var longString = string.Concat(list); + var bytes = Encoding.ASCII.GetBytes(longString); + var hashBytes = md5.ComputeHash(bytes); + + var sb = new StringBuilder(); + for (var i = 0; i < hashBytes.Length; i++) + { + sb.Append(hashBytes[i].ToString("X2")); + } + + return sb.ToString(); + } + #endregion } \ No newline at end of file diff --git a/QSB/Utility/ZOverride.cs b/QSB/Utility/ZOverride.cs index 6966fcf2..98ae61e3 100644 --- a/QSB/Utility/ZOverride.cs +++ b/QSB/Utility/ZOverride.cs @@ -4,6 +4,7 @@ using UnityEngine.UI; namespace QSB.Utility; +[UsedInUnityProject] public class ZOverride : MonoBehaviour { private const string shaderTestMode = "unity_GUIZTestMode"; diff --git a/QSB/WorldSync/QSBWorldSync.cs b/QSB/WorldSync/QSBWorldSync.cs index 5d788f70..c57d6eeb 100644 --- a/QSB/WorldSync/QSBWorldSync.cs +++ b/QSB/WorldSync/QSBWorldSync.cs @@ -20,6 +20,7 @@ namespace QSB.WorldSync; public static class QSBWorldSync { public static WorldObjectManager[] Managers; + public static string WorldObjectsHash { get; private set; } /// /// Set when all WorldObjectManagers have called Init() on all their objects (AKA all the objects are created) @@ -82,8 +83,14 @@ public static class QSBWorldSync AllObjectsAdded = true; DebugLog.DebugWrite("World Objects added.", MessageType.Success); + DeterministicManager.OnWorldObjectsAdded(); + + WorldObjectsHash = WorldObjects.Select(x => x.GetType().Name).GetMD5Hash(); + DebugLog.DebugWrite($"WorldObject hash is {WorldObjectsHash}"); + if (!QSBCore.IsHost) { + new WorldObjectsHashMessage().Send(); new RequestLinksMessage().Send(); } @@ -96,8 +103,6 @@ public static class QSBWorldSync AllObjectsReady = true; DebugLog.DebugWrite("World Objects ready.", MessageType.Success); - DeterministicManager.OnWorldObjectsReady(); - if (!QSBCore.IsHost) { new RequestInitialStatesMessage().Send(); @@ -247,7 +252,7 @@ public static class QSBWorldSync if (WorldObjects[objectId] is not TWorldObject worldObject) { - DebugLog.ToConsole($"Error - {typeof(TWorldObject).Name} id {objectId} is actually {WorldObjects[objectId].GetType().Name}.", MessageType.Error); + DebugLog.ToConsole($"Error - WorldObject id {objectId} is {WorldObjects[objectId].GetType().Name}, expected {typeof(TWorldObject).Name}.", MessageType.Error); return default; } diff --git a/QSB/WorldSync/WorldObjectsHashMessage.cs b/QSB/WorldSync/WorldObjectsHashMessage.cs new file mode 100644 index 00000000..4d5e6bd2 --- /dev/null +++ b/QSB/WorldSync/WorldObjectsHashMessage.cs @@ -0,0 +1,30 @@ +using OWML.Common; +using QSB.Messaging; +using QSB.Player.Messages; +using QSB.Utility; + +namespace QSB.WorldSync; + +/// +/// sends QSBWorldSync.WorldObjectsHash to the server for sanity checking +/// +internal class WorldObjectsHashMessage : QSBMessage +{ + public WorldObjectsHashMessage() : base(QSBWorldSync.WorldObjectsHash) => To = 0; + + public override void OnReceiveRemote() + { + var serverHash = QSBWorldSync.WorldObjectsHash; + + if (serverHash != Data) + { + // oh fuck oh no oh god + DebugLog.ToConsole($"Kicking {From} because their WorldObjects hash is wrong. (server:{serverHash}, client:{Data})", MessageType.Error); + new PlayerKickMessage(From, $"WorldObject hash error. (Server:{serverHash}, Client:{Data})").Send(); + } + else + { + DebugLog.DebugWrite($"WorldObject hash from {From} verified!", MessageType.Success); + } + } +} diff --git a/QSB/default-config.json b/QSB/default-config.json index f78c880c..852343c8 100644 --- a/QSB/default-config.json +++ b/QSB/default-config.json @@ -1,7 +1,29 @@ { "enabled": true, "settings": { - "defaultServerIP": "localhost", - "incompatibleModsAllowed": false + "defaultServerIP": { + "title": "Last Entered IP/ID", + "type": "text", + "value": "localhost", + "tooltip": "Used if you leave the connect prompt blank." + }, + "incompatibleModsAllowed": { + "title": "Incompatible Mods Allowed", + "type": "toggle", + "value": false, + "tooltip": "Kicks players if they have certain mods." + }, + "showPlayerNames": { + "title": "Show Player Names", + "type": "toggle", + "value": true, + "tooltip": "Shows player names in the HUD and the map view." + }, + "shipDamage": { + "title": "Ship Damage", + "type": "toggle", + "value": true, + "tooltip": "Take impact damage when inside the ship." + } } } \ No newline at end of file diff --git a/QSB/manifest.json b/QSB/manifest.json index e0551f00..d78fe58b 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.22.0", - "owmlVersion": "2.7.0", + "version": "0.23.0", + "owmlVersion": "2.8.0", "dependencies": [ "_nebula.MenuFramework", "JohnCorby.VanillaFix" ], "pathsToPreserve": [ "debugsettings.json", "storage.json" ] } diff --git a/README.md b/README.md index 0c11f2cc..71fc9180 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,6 @@ See [TRANSLATING.md](TRANSLATING.md) - Clone QSB's source - Open the file `DevEnv.targets` in your favorite text editor - (optional if copying built dlls manually) Edit the entry `` to point to your OWML directory (it is installed inside the Mod Manager directory) -- (optional if no unity project) Edit the entry `` to point to the directory where Outer Wilds is installed - (optional if no unity project) Edit the entry `` to point to the Assets folder of the QSB unity project - Open the project solution file `QSB.sln` in Visual Studio 2022 @@ -171,7 +170,7 @@ The template for this file is this : ### Authors -- [\_nebula](https://github.com/misternebula) - Developer of v0.3 onwards +- [\_nebula](https://github.com/misternebula) - Developer of v0.3.0 onwards - [JohnCorby](https://github.com/JohnCorby) - Co-developer of v0.13.0 onwards. - [AmazingAlek](https://github.com/amazingalek) - Developer of v0.1.0 - v0.7.1. - [Raicuparta](https://github.com/Raicuparta) - Developer of v0.1.0 - v0.2.0. @@ -180,9 +179,10 @@ The template for this file is this : - [Chris Yeninas](https://github.com/PhantomGamers) - Help with project files and GitHub workflows. - [Tlya](https://github.com/Tllya) - Russian translation. -- [Xen](https://github.com/xen-42) - French translation. +- [Xen](https://github.com/xen-42) - French translation, and help with particle effects and sounds. - [ShoosGun](https://github.com/ShoosGun) - Portuguese translation. - [DertolleDude](https://github.com/DertolleDude) - German translation. +- [SakuradaYuki](https://github.com/SakuradaYuki) - Chinese 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 ce4437c2..9cdf25b1 100644 --- a/TRANSLATING.md +++ b/TRANSLATING.md @@ -10,13 +10,13 @@ QSB can only be translated to the languages Outer Wilds supports - so if you don - Russian - Portuguese (Brazil) - German +- Chinese (Simplified) ### Un-translated languages : - Spanish (Latin American) - Italian - Polish - Japanese -- Chinese (Simplified) - Korean - Turkish @@ -38,4 +38,4 @@ Once you are happy with it, go to [this page](https://github.com/misternebula/qu Scroll down to the "Propose new file" box. In the title box (default "Create new file") write something along the lines of "Add translation for (language)". -Press the big green "Propose new file", and we'll review it soon! \ No newline at end of file +Press the big green "Propose new file", and we'll review it soon!