diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cb726df..a31a6f53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +# 1.2.2 + +**Game Version**: +- Added compatibility with V50 Patch 1 + +**Bug Fixes**: +- Fixed visual glitch where VR players would not appear to be sinking in mud +- Fixed visual glitch where VR players who died in water got the underwater filter applied sporadically + +**Mod Compatibility**: +- Fixed lighting culling issues when CullFactory is installed + # 1.2.1 ### V50 IS HERE diff --git a/LCVR.csproj b/LCVR.csproj index d1626343..44d48558 100644 --- a/LCVR.csproj +++ b/LCVR.csproj @@ -4,7 +4,7 @@ netstandard2.1 LCVR Collecting Scrap in VR - 1.2.1 + 1.2.2 true 12.0 LethalCompanyVR @@ -28,6 +28,7 @@ + diff --git a/Source/Compat.cs b/Source/Compat.cs index 73f866af..f73dbcc8 100644 --- a/Source/Compat.cs +++ b/Source/Compat.cs @@ -1,20 +1,22 @@ using System.Collections.Generic; using System.Linq; +using BepInEx; namespace LCVR; public class Compat { - private static readonly CompatibleMod[] ModCompatibilityList = + private readonly CompatibleMod[] ModCompatibilityList = [ - new("MoreCompany", "me.swipez.melonloader.morecompany"), - new("Mimics", "x753.Mimics"), - new("Diversity", "Chaos.Diversity"), + new CompatibleMod("MoreCompany", "me.swipez.melonloader.morecompany"), + new CompatibleMod("Mimics", "x753.Mimics"), + new CompatibleMod("Diversity", "Chaos.Diversity"), + new CompatibleMod("CullFactory", "com.fumiko.CullFactory") ]; - private static readonly List DetectedMods = []; + private readonly List DetectedMods = []; - public Compat(BepInEx.PluginInfo[] plugins) + public Compat(IEnumerable plugins) { foreach (var plugin in plugins) { diff --git a/Source/Compatibility/CullFactory/Patches.cs b/Source/Compatibility/CullFactory/Patches.cs new file mode 100644 index 00000000..80e41ff3 --- /dev/null +++ b/Source/Compatibility/CullFactory/Patches.cs @@ -0,0 +1,29 @@ +using CullFactory.Data; +using HarmonyLib; +using LCVR.Patches; +using LCVR.Player; +using UnityEngine; + +namespace LCVR.Compatibility.CullFactory; + +[LCVRPatch(dependency: "CullFactory")] +[HarmonyPatch] +internal static class Patches +{ + /// + /// Fix for CullFactory to include the VR helmet lights in the array + /// + [HarmonyPatch(typeof(DynamicObjects), nameof(DynamicObjects.CollectAllPlayerLights))] + [HarmonyPostfix] + private static void OnCollectAllPlayerLights() + { + if (!VRSession.Instance) + return; + + var clientId = VRSession.Instance.LocalPlayer.PlayerController.playerClientId; + var lights = DynamicObjects.allPlayerLights[clientId]; + var cameraLights = VRSession.Instance.MainCamera.GetComponentsInChildren(); + + DynamicObjects.allPlayerLights[clientId] = [..lights, ..cameraLights]; + } +} diff --git a/Source/Items/VRFlashlight.cs b/Source/Items/VRFlashlight.cs index 34018b0c..09ed8327 100644 --- a/Source/Items/VRFlashlight.cs +++ b/Source/Items/VRFlashlight.cs @@ -19,10 +19,10 @@ protected override void OnUpdate() if (IsLocal) return; - var isHoldingActiveFlashlight = (player.currentlyHeldObjectServer?.itemProperties.itemId == 1 || player.currentlyHeldObjectServer?.itemProperties.itemId == 6) - && player.currentlyHeldObjectServer.isBeingUsed; - // currentlyHeldObjectServer is guaranteed to not be null at this point - + var isHoldingActiveFlashlight = + player.currentlyHeldObjectServer is { isBeingUsed: true } and ({ itemProperties.itemId: 1 } or + { itemProperties.itemId: 6 }); + if (!item.isPocketed) { // Update flashlight offsets diff --git a/Source/Networking/VRNetPlayer.cs b/Source/Networking/VRNetPlayer.cs index e658b11f..91ed4a12 100644 --- a/Source/Networking/VRNetPlayer.cs +++ b/Source/Networking/VRNetPlayer.cs @@ -16,14 +16,14 @@ public class VRNetPlayer : MonoBehaviour { private ChainIKConstraintData originalLeftArmConstraintData; private ChainIKConstraintData originalRightArmConstraintData; - + private GameObject playerGhost; private Transform usernameBillboard; private CanvasGroup usernameAlpha; private TextMeshProUGUI usernameText; private bool spectatorWasParentedToShip; - + private Transform xrOrigin; private Transform leftController; private Transform rightController; @@ -46,7 +46,7 @@ public class VRNetPlayer : MonoBehaviour public PlayerControllerB PlayerController { get; private set; } public Bones Bones { get; private set; } - + public Transform LeftItemHolder { get; private set; } public Transform RightItemHolder { get; private set; } @@ -111,20 +111,20 @@ private void Awake() usernameAlpha = playerGhost.GetComponentInChildren(); playerGhost.GetComponentInChildren().player = this; - + // Disable rendering ghost until player dies foreach (var renderer in playerGhost.GetComponentsInChildren()) { renderer.enabled = false; } - + // Set username text if (PlayerController.playerSteamId is 76561198438308784 or 76561199575858981) { usernameText.color = new Color(0, 1, 1, 1); usernameText.fontStyle = FontStyles.Bold; } - + usernameText.text = $"{PlayerController.playerUsername}"; } @@ -195,7 +195,8 @@ private void Update() transform.position.z + (modelOffset.z * 1.5f) - (cameraPosAccounted.z * 1.5f) ); - Bones.Model.localPosition = transform.InverseTransformPoint(transform.position + modelOffset); + Bones.Model.localPosition = transform.InverseTransformPoint(transform.position + modelOffset) + + Vector3.down * (2.5f * PlayerController.sinkingValue); } else { @@ -236,7 +237,7 @@ private void LateUpdate() { rightFingerCurler?.Update(); } - + // Rotate spectator username billboard if (StartOfRound.Instance.localPlayerController.localVisorTargetPoint is not null) { @@ -265,7 +266,7 @@ public void HideSpectatorGhost() { renderer.enabled = false; } - + usernameAlpha.alpha = 0f; } @@ -279,7 +280,7 @@ public void ShowSpectatorNameBillboard() usernameAlpha.alpha = 1f; } - + internal void UpdateTargetTransforms(DNet.Rig rig) { leftController.localPosition = rig.leftHandPosition; @@ -321,7 +322,7 @@ internal void UpdateSpectatorTransforms(DNet.SpectatorRig rig) } spectatorWasParentedToShip = rig.parentedToShip; - + head.localPosition = rig.headPosition; head.eulerAngles = rig.headRotation; @@ -343,7 +344,7 @@ internal void UpdateSpectatorTransforms(DNet.SpectatorRig rig) void OnDestroy() { Destroy(playerGhost); - + Bones.ResetToPrefabPositions(); Destroy(Bones.LeftArmRig.GetComponent()); @@ -357,4 +358,4 @@ void OnDestroy() GetComponentInChildren().Build(); } -} +} \ No newline at end of file diff --git a/Source/Patches/Spectating/EnvironmentPatches.cs b/Source/Patches/Spectating/EnvironmentPatches.cs index 3369820c..2d6274b8 100644 --- a/Source/Patches/Spectating/EnvironmentPatches.cs +++ b/Source/Patches/Spectating/EnvironmentPatches.cs @@ -113,4 +113,43 @@ private static bool PreventTeleportDeadPlayer(ShipTeleporter __instance, ref IEn __result = Utils.NopRoutine(); return false; } + + /// + /// Prevent the spectator camera (which is not used) from triggering the underwater filter + /// + [HarmonyPatch(typeof(HUDManager), nameof(HUDManager.UnderwaterScreenFilters))] + [HarmonyTranspiler] + private static IEnumerable SpectatorCamDontTriggerWater(IEnumerable instructions) + { + return new CodeMatcher(instructions) + .MatchForward(false, [new CodeMatch(OpCodes.Ldc_I4_1)]) + .SetOpcodeAndAdvance(OpCodes.Ldc_I4_0) + .InstructionEnumeration(); + } + + /// + /// Allow dead players to still experience the underwater filter + /// + [HarmonyPatch(typeof(PlayerControllerB), nameof(PlayerControllerB.SetFaceUnderwaterFilters))] + [HarmonyTranspiler] + private static IEnumerable EnableDeadPlayerUnderwater(IEnumerable instructions) + { + return new CodeMatcher(instructions) + .Advance(1) + .RemoveInstructions(4) + .InstructionEnumeration(); + } + + /// + /// Prevent dead players from dying again if they are underwater as a spectator + /// + [HarmonyPatch(typeof(PlayerControllerB), nameof(PlayerControllerB.SetFaceUnderwaterFilters))] + [HarmonyPostfix] + private static void UnderwaterPreventDeath(PlayerControllerB __instance) + { + if (!__instance.isPlayerDead) + return; + + StartOfRound.Instance.drowningTimer = 1; + } } diff --git a/Source/Patches/Spectating/Patches.cs b/Source/Patches/Spectating/Patches.cs index 3156c007..704c6037 100644 --- a/Source/Patches/Spectating/Patches.cs +++ b/Source/Patches/Spectating/Patches.cs @@ -232,7 +232,7 @@ private static void OnPlayerUpdate(PlayerControllerB __instance) __instance.takingFallDamage = false; } - [HarmonyPatch(typeof(PlayerControllerB), "ActivateItem_performed")] + [HarmonyPatch(typeof(PlayerControllerB), nameof(PlayerControllerB.ActivateItem_performed))] [HarmonyPostfix] private static void SpectateNextPlayer(PlayerControllerB __instance) { @@ -287,7 +287,7 @@ private static void HideSpectatingText() /// /// Toggle death screen UI by pressing the secondary use button /// - [HarmonyPatch(typeof(PlayerControllerB), "ItemSecondaryUse_performed")] + [HarmonyPatch(typeof(PlayerControllerB), nameof(PlayerControllerB.ItemSecondaryUse_performed))] [HarmonyPostfix] private static void OnToggleDeathScreen(PlayerControllerB __instance) { @@ -300,7 +300,7 @@ private static void OnToggleDeathScreen(PlayerControllerB __instance) /// /// Toggle spectator light by pressing the tertiary use button /// - [HarmonyPatch(typeof(PlayerControllerB), "Discard_performed")] + [HarmonyPatch(typeof(PlayerControllerB), nameof(PlayerControllerB.Discard_performed))] [HarmonyPostfix] private static void OnToggleSpectatorLight(PlayerControllerB __instance) { diff --git a/Source/Plugin.cs b/Source/Plugin.cs index 1d947062..f9c1a5af 100644 --- a/Source/Plugin.cs +++ b/Source/Plugin.cs @@ -28,15 +28,17 @@ namespace LCVR; #region Compatibility Dependencies [BepInDependency("me.swipez.melonloader.morecompany", DependencyFlags.SoftDependency)] [BepInDependency("x753.Mimics", DependencyFlags.SoftDependency)] +[BepInDependency("com.fumiko.CullFactory", DependencyFlags.SoftDependency)] #endregion public class Plugin : BaseUnityPlugin { public const string PLUGIN_GUID = "io.daxcess.lcvr"; public const string PLUGIN_NAME = "LCVR"; - public const string PLUGIN_VERSION = "1.2.1"; + public const string PLUGIN_VERSION = "1.2.2"; private readonly string[] GAME_ASSEMBLY_HASHES = [ - "7CFABBA203022CC46EF309B0E651276CB59217AF6D38C34E2085E67957DBBCBD" // V50 + "7CFABBA203022CC46EF309B0E651276CB59217AF6D38C34E2085E67957DBBCBD", // V50 + "4C265CECBC1A075E52D9E1FA458C67AA25C087362B472DF66DF370B9A0676A67", // V50 Patch 1 ]; public new static Config Config { get; private set; } diff --git a/Source/UI/VRHUD.cs b/Source/UI/VRHUD.cs index 753d4085..819b4696 100644 --- a/Source/UI/VRHUD.cs +++ b/Source/UI/VRHUD.cs @@ -350,6 +350,9 @@ private void Awake() // Set up a global light for spectators to be able to toggle spectatorLight = Instantiate(AssetManager.spectatorLight, transform); spectatorLight.SetActive(false); + + // Prevents CullFactory from culling the light + spectatorLight.hideFlags |= HideFlags.DontSave; } private void LateUpdate() @@ -385,14 +388,14 @@ public void HideHUD(bool hide) inventory.SetActive(!hide); } - public void ToggleDeathScreen(bool? enabled = null) + public void ToggleDeathScreen(bool? visible = null) { if (!deathScreen) return; - if (enabled != null) + if (visible != null) { - deathScreen.transform.localScale = Vector3.one * (enabled == true ? 1.1f : 0f); + deathScreen.transform.localScale = Vector3.one * (visible == true ? 1.1f : 0f); return; } @@ -402,9 +405,12 @@ public void ToggleDeathScreen(bool? enabled = null) deathScreen.transform.localScale = Vector3.one * 1.1f; } - public void ToggleSpectatorLight(bool? enabled = null) + public void ToggleSpectatorLight(bool? active = null) { - spectatorLight?.SetActive(enabled ?? !spectatorLight.activeSelf); + if (spectatorLight is not { } light) + return; + + light.SetActive(active ?? !light.activeSelf); } ///