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);
}
///