Skip to content

Commit

Permalink
New features and bugfixes for 1.2.4
Browse files Browse the repository at this point in the history
- Fixed issue where a specific setup could break spectating
- Fixed door interactions on Artiface
- Added hangar lever interactions
- Added knife stabbing motion controls
  • Loading branch information
DaXcess committed May 14, 2024
1 parent 1722f98 commit 67cef4d
Show file tree
Hide file tree
Showing 12 changed files with 293 additions and 18 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
# 1.2.4

**Bug Fixes**:
- Fixed some of the doors on Artiface not using the new VR interactions
- Leaving the game while spectating will no longer prevent spectating to work in the next game

**Additions**:
- Added VR motion controls to the knife (you can now stabby stab)
- Added VR interactions to the big doors on Artiface

# 1.2.3

**Bug Fixes**:
Expand Down
2 changes: 1 addition & 1 deletion LCVR.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<TargetFramework>netstandard2.1</TargetFramework>
<AssemblyName>LCVR</AssemblyName>
<Description>Collecting Scrap in VR</Description>
<Version>1.2.3</Version>
<Version>1.2.4</Version>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>12.0</LangVersion>
<Title>LethalCompanyVR</Title>
Expand Down
2 changes: 2 additions & 0 deletions Source/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ public class Config(ConfigFile file)
public ConfigEntry<bool> DisableBreakerBoxInteraction { get; } = file.Bind("Interaction", "DisableBreakerBoxInteraction", false, "Disabled needing to physically open the breaker box and flip the switches with your finger.");
public ConfigEntry<bool> DisableDoorInteraction { get; } = file.Bind("Interaction", "DisableDoorInteraction", false, "Disable needing to physically open and close doors by interacting with the door handles. Will also disable the need to use keys and lockpickers physically on the door handle.");

public ConfigEntry<bool> DisableHangarLeverInteraction { get; } = file.Bind("Interaction", "DisableHangarLeverInteraction", false, "Disable needing to physically pull the lever for the big doors on Artiface");

public ConfigEntry<bool> DisableMuffleInteraction { get; } = file.Bind("Interaction", "DisableMuffleInteraction", false, "Disables the self-muffling feature, which makes it so that holding your hand in front of your mouth will no longer make you inaudible to enemies.");
public ConfigEntry<bool> DisableFaceInteractions { get; } = file.Bind("Interaction", "DisableFaceInteractions", false, "Disables the functionality to hold certain items up to your face to use them.");

Expand Down
86 changes: 86 additions & 0 deletions Source/Items/VRKnife.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using System.Linq;
using LCVR.Assets;
using LCVR.Player;
using UnityEngine;

namespace LCVR.Items;

public class VRKnife : VRItem<KnifeItem>
{
private GameObject interactionTarget;
private GameObject knifeCollider;

private Vector3 previous;
private float attackTimer;

public float Speed { get; private set; }
private Vector3 Position => VRSession.Instance.LocalPlayer.transform.InverseTransformPoint(transform.position);

private new void Awake()
{
base.Awake();

if (!IsLocal)
return;

interactionTarget = Instantiate(AssetManager.interactable, VRSession.Instance.MainCamera.transform);
interactionTarget.transform.localPosition = new Vector3(0, 0, 0.5f);
interactionTarget.transform.localScale = Vector3.one * 0.3f;
interactionTarget.AddComponent<KnifeInteractor>();
interactionTarget.AddComponent<Rigidbody>().isKinematic = true;

knifeCollider = Instantiate(AssetManager.interactable, transform);
knifeCollider.transform.localPosition = new Vector3(0, 0, 7.25f);
knifeCollider.transform.localScale = new Vector3(1.2f, 3, 12.9f);

previous = Position;
}

protected override void OnUpdate()
{
if (!IsLocal)
return;

Speed = (Position - previous).magnitude / Time.deltaTime;
previous = Position;
}

private void OnDestroy()
{
Destroy(interactionTarget);
Destroy(knifeCollider);
}

internal void Attack()
{
if (Time.realtimeSinceStartup < attackTimer)
return;

attackTimer = Time.realtimeSinceStartup + 0.15f;
item.ItemActivate(true);
}

internal static RaycastHit[] GetKnifeHits(KnifeItem knife)
{
var tf = knife.transform;

var forwardHits = UnityEngine.Physics.SphereCastAll(tf.position, 0.3f, tf.forward, 0.75f, knife.knifeMask,
QueryTriggerInteraction.Collide);
var upHits = UnityEngine.Physics.SphereCastAll(tf.position, 0.3f, -tf.up, 0.75f, knife.knifeMask,
QueryTriggerInteraction.Collide);

RaycastHit[] allHits = [..forwardHits, ..upHits];

return allHits.GroupBy(x => x.collider).Select(x => x.First()).ToArray();
}
}

public class KnifeInteractor : MonoBehaviour
{
private void OnTriggerEnter(Collider other)
{
var knife = other.GetComponentInParent<VRKnife>();
if (knife?.Speed > 6)
knife.Attack();
}
}
8 changes: 4 additions & 4 deletions Source/Items/VRShovelItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ internal class VRShovelItem : VRItem<Shovel>
private readonly Queue<Vector3> positions = new();
private Vector3 lastPosition = Vector3.zero;

private bool isHitting = false;
private bool hasSwung = false;
private float lastActionTime = 0;
private float timeNotReeledUp = 0;
private bool isHitting;
private bool hasSwung;
private float lastActionTime;
private float timeNotReeledUp;

private new void Awake()
{
Expand Down
36 changes: 36 additions & 0 deletions Source/Patches/Items/KnifeItemPatches.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Collections.Generic;
using System.Reflection.Emit;
using HarmonyLib;
using LCVR.Items;
using UnityEngine;
using static HarmonyLib.AccessTools;

namespace LCVR.Patches.Items;

[LCVRPatch]
[HarmonyPatch]
internal static class KnifeItemPatches
{
[HarmonyPatch(typeof(KnifeItem), nameof(KnifeItem.HitKnife))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> HitKnifeVRPatch(IEnumerable<CodeInstruction> instructions)
{
return new CodeMatcher(instructions)
.MatchForward(false,
[
new CodeMatch(OpCodes.Call,
Method(typeof(UnityEngine.Physics), nameof(UnityEngine.Physics.SphereCastAll),
[
typeof(Vector3), typeof(float), typeof(Vector3), typeof(float), typeof(int),
typeof(QueryTriggerInteraction)
]))
])
.Advance(-23)
.RemoveInstructions(24)
.InsertAndAdvance(
new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Call, Method(typeof(VRKnife), nameof(VRKnife.GetKnifeHits)))
)
.InstructionEnumeration();
}
}
12 changes: 10 additions & 2 deletions Source/Patches/Spectating/Patches.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ internal static class SpectatorPlayerPatches
private static int lastSpectatedIndex = -1;

private static bool allowSpectatorActions;

/// <summary>
/// Initialize values when joining a new game, since this class is static and values persist across games
/// </summary>
[HarmonyPatch(typeof(StartOfRound), nameof(StartOfRound.Start))]
[HarmonyPatch]
private static void OnGameJoined()
{
isSpectating = false;
}

/// <summary>
/// Store some fields that need to be restored after death
Expand All @@ -48,8 +58,6 @@ private static void BeforePlayerDeath(PlayerControllerB __instance)
[HarmonyPostfix]
private static void OnPlayerDeath(PlayerControllerB __instance)
{
Logger.LogDebug($"{__instance.IsOwner}, {isSpectating}, {__instance.AllowPlayerDeath()}");

if (!__instance.IsOwner || isSpectating || !__instance.AllowPlayerDeath())
return;

Expand Down
17 changes: 7 additions & 10 deletions Source/Physics/Interactions/Door.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using HarmonyLib;
using LCVR.Assets;
using LCVR.Patches;
Expand All @@ -15,8 +16,9 @@ public class Door : MonoBehaviour, VRInteractable
{
internal static readonly Dictionary<string, (Vector3, Vector3, Vector3)> registeredDoorHandles = new()
{
["SteelDoorMapModel"] = (new(-0.29f, 0.2336f, -0.025f), Vector3.zero, new(1, 0.15f, 0.025f)),
["FancyDoorMapModel"] = (new(-0.29f, 0.2836f, -0.055f), Vector3.zero, new(1, 0.09f, 0.155f))
["TestRoom"] = (new Vector3(-0.29f, 0.2336f, -0.025f), Vector3.zero, new Vector3(1, 0.15f, 0.025f)),
["SteelDoorMapModel"] = (new Vector3(-0.29f, 0.2336f, -0.025f), Vector3.zero, new Vector3(1, 0.15f, 0.025f)),
["FancyDoorMapModel"] = (new Vector3(-0.29f, 0.2836f, -0.055f), Vector3.zero, new Vector3(1, 0.09f, 0.155f))
};

internal DoorLock door;
Expand Down Expand Up @@ -182,18 +184,13 @@ private static void InitializeDoorInteractor(DoorLock __instance)

if (!Door.registeredDoorHandles.TryGetValue(__instance.NetworkObject.name, out var offsets))
{
var idx = __instance.NetworkObject.name.LastIndexOf("(Clone)", StringComparison.Ordinal);

if (idx < 0)
return;

var name = __instance.NetworkObject.name.Remove(idx, 7);

var rx = new Regex("\\(.+\\)");
var name = rx.Replace(__instance.NetworkObject.name, "").TrimEnd();

if (!Door.registeredDoorHandles.TryGetValue(name, out offsets))
return;
}


var (position, rotation, scale) = offsets;

// Make sure default ray based interaction no longer works for this door
Expand Down
131 changes: 131 additions & 0 deletions Source/Physics/Interactions/HangarLever.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
using HarmonyLib;
using LCVR.Assets;
using LCVR.Patches;
using LCVR.Player;
using UnityEngine;
using Object = UnityEngine.Object;

namespace LCVR.Physics.Interactions;

public class HangarLever : MonoBehaviour, VRInteractable
{
private InteractTrigger trigger;
private AnimatedObjectTrigger animTrigger;

private bool interacting;
private Transform lookAtTransform;

private float lockedRotation;
private float lockedRotationTime;

public InteractableFlags Flags => InteractableFlags.BothHands;

private void Awake()
{
trigger = GetComponentInParent<InteractTrigger>();
animTrigger = GetComponentInParent<AnimatedObjectTrigger>();

transform.parent.GetComponent<BoxCollider>().enabled = false;
}

public void OnColliderEnter(VRInteractor interactor)
{
}

public void OnColliderExit(VRInteractor interactor)
{
}

public bool OnButtonPress(VRInteractor interactor)
{
if (!trigger.interactable)
return false;

interacting = true;
lookAtTransform = interactor.transform;

interactor.FingerCurler.ForceFist(true);

return true;
}

public void OnButtonRelease(VRInteractor interactor)
{
interactor.FingerCurler.ForceFist(false);

if (!interacting)
return;

interacting = false;

var rot = GetLookRotation();
switch (animTrigger.boolValue)
{
case true when rot is > 160 and < 200:
trigger.Interact(VRSession.Instance.LocalPlayer.transform);
lockedRotation = 180;
lockedRotationTime = Time.realtimeSinceStartup + 0.5f;

break;
case false when rot < 20:
trigger.Interact(VRSession.Instance.LocalPlayer.transform);
lockedRotation = 0;
lockedRotationTime = Time.realtimeSinceStartup + 0.5f;

break;
}
}

private void LateUpdate()
{
if (lockedRotationTime > Time.realtimeSinceStartup)
{
transform.parent.localEulerAngles = new Vector3(0, lockedRotation, 0);
return;
}

if (!interacting)
return;

transform.parent.localEulerAngles = new Vector3(0, GetLookRotation(), 0);
}

private float GetLookRotation()
{
// I know, I'm supposed to calculate this myself but idk how and internet is not helping
var tf = transform.parent;

var rotation = tf.rotation;
tf.LookAt(lookAtTransform.position);

var lookRotation = tf.localEulerAngles.y;
tf.rotation = rotation;

// Prevent specific issue with the lever jumping down
if (lookRotation > 270)
return 0;

return Mathf.Clamp(lookRotation, 0, 180);
}
}

[LCVRPatch]
[HarmonyPatch]
internal static class LeverSwitchPatches
{
[HarmonyPatch(typeof(InteractTrigger), nameof(InteractTrigger.Start))]
[HarmonyPostfix]
private static void OnInteractTriggerStart(InteractTrigger __instance)
{
if (__instance.gameObject.name is not "LeverSwitchHandle")
return;

__instance.gameObject.name = "LeverSwitchInteractable";

var interactableObject = Object.Instantiate(AssetManager.interactable, __instance.transform);

interactableObject.transform.localPosition = new Vector3(0.0044f, -0.0513f, 0.2529f);
interactableObject.transform.localScale = new Vector3(0.0553f, 0.1696f, 0.0342f);
interactableObject.AddComponent<HangarLever>();
}
}
1 change: 1 addition & 0 deletions Source/Player/Items.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ internal static class Items
{ "Pro-flashlight", typeof(VRFlashlight) },
{ "Flashlight", typeof(VRFlashlight) },
{ "Laser pointer", typeof(VRFlashlight) },
{ "Kitchen knife", typeof(VRKnife) }
};

public static void UpdateVRControlsItemsOffsets()
Expand Down
4 changes: 4 additions & 0 deletions Source/Player/VRSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,10 @@ private void InitializeVRSession()
VRController.DisableInteractTrigger("DoorInteractable");
VRController.DisableInteractTrigger("LockPickerInteractable");
}

// Hangar Lever
if (!Plugin.Config.DisableHangarLeverInteraction.Value)
VRController.DisableInteractTrigger("LeverSwitchInteractable");
#endregion

#if DEBUG
Expand Down

0 comments on commit 67cef4d

Please sign in to comment.