7 Commits

Author SHA1 Message Date
msk 23eac84782 Update README.md 2022-02-23 20:15:16 -08:00
msk 274288e453 Update README.md 2022-02-16 01:37:10 -08:00
msk caa4dae8b3 Update README.md 2022-02-14 15:16:52 -08:00
msk f81069f658 quick fixup 2022-02-14 13:02:43 -08:00
msk a9be72693c hp text customization, shrink hp bg border, hide hp when aiming there 2022-02-14 00:07:51 -08:00
msk 416e49d6d7 README changes 2022-02-13 03:32:30 -08:00
msk 6249f07788 implement stats tilt
encompasses releases 1.1.2 and 1.1.3
2022-02-13 03:17:47 -08:00
17 changed files with 283 additions and 65 deletions
+2 -2
View File
@@ -13,14 +13,14 @@ MonoBehaviour:
m_EditorClassIdentifier: m_EditorClassIdentifier:
PackageName: TNH_Quality_of_Life_Improvements PackageName: TNH_Quality_of_Life_Improvements
Author: muskit Author: muskit
Version: 1.1.1 Version: 1.2.1
Icon: {fileID: 2800000, guid: 785b7946398f5314b95bf593d2d77d67, type: 3} Icon: {fileID: 2800000, guid: 785b7946398f5314b95bf593d2d77d67, type: 3}
ReadMe: {fileID: 102900000, guid: ab1d6dea017447a48ac348db588a6f35, type: 3} ReadMe: {fileID: 102900000, guid: ab1d6dea017447a48ac348db588a6f35, type: 3}
WebsiteURL: https://github.com/muskit/TNH-Quality-of-Life-Improvements WebsiteURL: https://github.com/muskit/TNH-Quality-of-Life-Improvements
Description: Quality of life improvements to the Take and Hold experience. Description: Quality of life improvements to the Take and Hold experience.
AdditionalDependencies: AdditionalDependencies:
- BepInEx-BepInExPack_H3VR-5.4.1700 - BepInEx-BepInExPack_H3VR-5.4.1700
StripNamespaces: 1 StripNamespaces: 0
AdditionalNamespaces: AdditionalNamespaces:
- TNHQoLImprovements - TNHQoLImprovements
BuildItems: BuildItems:
+33 -19
View File
@@ -43,11 +43,13 @@ public class MeatKitPlugin : BaseUnityPlugin
// BepInEx configuration // BepInEx configuration
//--- Health Counter ---// //--- Health Counter ---//
public static ConfigEntry<bool> cfgSolidifyHPText; public static ConfigEntry<bool> cfgHPHiddenWhenAiming;
public static ConfigEntry<bool> cfgShowHPBackground; public static ConfigEntry<bool> cfgShowHPBackground;
public static ConfigEntry<float> cfgHPBackgroundOpacity; public static ConfigEntry<float> cfgHPBackgroundOpacity;
public static ConfigEntry<HPTextType> cfgHPTextType;
//--- Take and Hold Info ---// //--- Take and Hold Info ---//
public static ConfigEntry<bool> cfgShowLPC; public static ConfigEntry<bool> cfgShowLPC;
public static ConfigEntry<bool> cfgInfoFollowCamera;
public static ConfigEntry<bool> cfgShowTokens; public static ConfigEntry<bool> cfgShowTokens;
public static ConfigEntry<bool> cfgShowHolds; public static ConfigEntry<bool> cfgShowHolds;
public static ConfigEntry<bool> cfgShowNumbersAtShop; public static ConfigEntry<bool> cfgShowNumbersAtShop;
@@ -67,6 +69,20 @@ public class MeatKitPlugin : BaseUnityPlugin
private void SceneChanged(Scene from, Scene to) private void SceneChanged(Scene from, Scene to)
{ {
GetFonts();
playerCamera = GameObject.FindGameObjectWithTag("MainCamera");
// apply health counter tweaks globally
var healthCounter = FindObjectOfType<FistVR.FVRHealthBar>();
if (healthCounter != null)
{
HPReadability.ImproveHPTextReadability(healthCounter.transform.GetChild(0).gameObject);
if (cfgHPHiddenWhenAiming.Value)
healthCounter.gameObject.AddComponent<HPHideWhenAiming>();
}
// TNH patches
if (GameObject.Find("_GameManager") != null || FindObjectOfType<FistVR.TNH_Manager>() != null) if (GameObject.Find("_GameManager") != null || FindObjectOfType<FistVR.TNH_Manager>() != null)
{ {
Logger.LogInfo("We are in a TNH game!"); Logger.LogInfo("We are in a TNH game!");
@@ -77,18 +93,6 @@ public class MeatKitPlugin : BaseUnityPlugin
Logger.LogInfo("We are NOT in a TNH game!"); Logger.LogInfo("We are NOT in a TNH game!");
Destroy(instance); Destroy(instance);
} }
playerCamera = GameObject.FindGameObjectWithTag("MainCamera");
// apply health readability globally
var healthCounter = FindObjectOfType<FistVR.FVRHealthBar>();
if (healthCounter != null)
{
if (cfgShowHPBackground.Value || cfgSolidifyHPText.Value)
HPReadability.ImproveHPTextReadability(healthCounter.transform.GetChild(0).gameObject);
}
GetFonts();
} }
// called on scene change, find fonts from game if they're not set // called on scene change, find fonts from game if they're not set
@@ -135,10 +139,14 @@ public class MeatKitPlugin : BaseUnityPlugin
bundle = AssetBundle.LoadFromFile(Path.Combine(BasePath, "tnh_qol_improvements")); bundle = AssetBundle.LoadFromFile(Path.Combine(BasePath, "tnh_qol_improvements"));
SceneManager.activeSceneChanged += SceneChanged; SceneManager.activeSceneChanged += SceneChanged;
fontBombardier = MeatKitPlugin.bundle.LoadAsset<Font>("Bombardier"); fontBombardier = bundle.LoadAsset<Font>("Bombardier");
// setup configuration // setup configuration
//--- Health Counter ---// //--- Health Counter ---//
cfgHPHiddenWhenAiming = Config.Bind("Health Counter",
"Hide HP Counter When Aiming",
true,
"While aiming around the health counter in view, hide it.");
cfgShowHPBackground = Config.Bind("Health Counter", cfgShowHPBackground = Config.Bind("Health Counter",
"Background enabled", "Background enabled",
true, true,
@@ -147,15 +155,21 @@ public class MeatKitPlugin : BaseUnityPlugin
"Background opacity", "Background opacity",
0.74f, 0.74f,
"Set opacity of health text's background (if enabled)."); "Set opacity of health text's background (if enabled).");
cfgSolidifyHPText = Config.Bind("Health Counter", cfgHPTextType = Config.Bind("Health Counter",
"Solidify HP text", "HP Text Type",
true, HPTextType.Solidify,
"Set opacity of HP text to full and give it a shadow."); "Solidify: Set text to full opacity and give it a drop shadow\n" +
"Untouched: Leave text untouched\n" +
"Hidden: Hide health counter completely (will hide background if enabled)");
//--- Take and Hold Info ---// //--- Take and Hold Info ---//
cfgShowLPC = Config.Bind("Take and Hold Info", cfgShowLPC = Config.Bind("Take and Hold Info",
"Show player count in online leaderboards", "Show Player Count in Online Leaderboards",
true, true,
"Shows the number of players in the currently selected TNH leaderboard."); "Shows the number of players in the currently selected TNH leaderboard.");
cfgInfoFollowCamera = Config.Bind("Take and Hold Info",
"Tilt Wrist Stats Towards Camera",
true,
"Tilt the extra wrist statistics from this mod towards the player's camera, allowing for easier readability.");
cfgShowTokens = Config.Bind("Take and Hold Info", cfgShowTokens = Config.Bind("Take and Hold Info",
"Show Tokens", "Show Tokens",
true, true,
+18 -1
View File
@@ -6,6 +6,7 @@ And... wait, which hold are you on again?
## Features ## Features
* Better health counter visibility * Better health counter visibility
* Health counter fade when aiming around it
* Token, hold, and wave counter on radar hand * Token, hold, and wave counter on radar hand
* Player count for online leaderboards; see how you stack up! * Player count for online leaderboards; see how you stack up!
* Won't work with [*TakeAndHoldTweaker*](https://h3vr.thunderstore.io/package/devyndamonster/TakeAndHoldTweaker/) installed * Won't work with [*TakeAndHoldTweaker*](https://h3vr.thunderstore.io/package/devyndamonster/TakeAndHoldTweaker/) installed
@@ -15,9 +16,25 @@ And... wait, which hold are you on again?
Toggle and customize these features in your mod manager's *Config editor*. Toggle and customize these features in your mod manager's *Config editor*.
For any issues/ideas, please create an issue on the GitHub repo (linked on Thunderstore page). **This mod will not disqualify you from Steam or TNHTweaker leaderboards.**
For any issues/ideas, please create an issue at the GitHub repository (linked on Thunderstore page).
## Changelog ## Changelog
1.2.1
* [TNH] Changed leaderboard player count message for unavailability with TNHTweaker
**1.2.0**
* HP counter text can now be hidden completely
* Added HP counter fading when pointing a firearm towards it, allowing better visiblity
* Shrunk borders of health counter's background
1.1.3
* [TNH] Fixed wrist stats still trying to look at the camera in the game over area, resulting in weird rotations
1.1.2
* [TNH] Wrist stats can now tilt towards the camera, making it less awkward to read
1.1.1 1.1.1
* Fixed wave counter text not showing up during a hold * Fixed wave counter text not showing up during a hold
+1 -1
View File
@@ -385,4 +385,4 @@ RectTransform:
m_AnchorMax: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0} m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 580, y: 360} m_SizeDelta: {x: 580, y: 360}
m_Pivot: {x: 1, y: 1} m_Pivot: {x: 1, y: 0.5}
+1 -1
View File
@@ -289,4 +289,4 @@ RectTransform:
m_AnchorMax: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0} m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 500, y: 360} m_SizeDelta: {x: 500, y: 360}
m_Pivot: {x: 0, y: 1} m_Pivot: {x: 0, y: 0.5}
+1 -1
View File
@@ -277,7 +277,7 @@ RectTransform:
m_AnchorMax: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0} m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 580, y: 360} m_SizeDelta: {x: 580, y: 360}
m_Pivot: {x: 0, y: 1} m_Pivot: {x: 0, y: 0.5}
--- !u!224 &224892970170756718 --- !u!224 &224892970170756718
RectTransform: RectTransform:
m_ObjectHideFlags: 1 m_ObjectHideFlags: 1
+127
View File
@@ -0,0 +1,127 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using FistVR;
// To be attached to FVRHealthBar object
public class HPHideWhenAiming : MonoBehaviour {
CanvasGroup canvasGroup;
GameObject gObjHUD;
RawImage background;
FVRViveHand leftHand;
FVRViveHand rightHand;
BoxCollider hudCollider;
GameObject gObjLeftLine;
GameObject gObjRightLine;
LineRenderer leftHandLine;
LineRenderer rightHandLine;
//// Testing renderers
//GameObject gObjColliderRenderer;
//LineRenderer colliderRenderer;
// Use this for initialization
void Start() {
gObjHUD = transform.GetChild(0).gameObject;
leftHand = MeatKitPlugin.playerCamera.transform.parent.GetChild(1).GetComponent<FVRViveHand>();
rightHand = MeatKitPlugin.playerCamera.transform.parent.GetChild(0).GetComponent<FVRViveHand>();
canvasGroup = gameObject.AddComponent<CanvasGroup>();
var gObjBG = gObjHUD.transform.Find("Background");
if (gObjBG != null)
background = gObjBG.GetComponent<RawImage>();
hudCollider = gObjHUD.AddComponent<BoxCollider>();
hudCollider.isTrigger = true;
hudCollider.gameObject.layer = LayerMask.NameToLayer("UI");
hudCollider.size = new Vector3(40, 25, .01f);
//// TESTING: collider visuals
//gObjColliderRenderer = new GameObject();
//gObjColliderRenderer.transform.SetParent(hudCollider.transform, false);
//colliderRenderer = gObjColliderRenderer.AddComponent<LineRenderer>();
//colliderRenderer.SetWidth(.005f, .005f);
//colliderRenderer.SetColors(Color.blue, Color.blue);
//colliderRenderer.positionCount = 8;
//// TESTING: beam from held weapons
//gObjLeftLine = new GameObject();
//leftHandLine = gObjLeftLine.AddComponent<LineRenderer>();
//leftHandLine.positionCount = 2;
//leftHandLine.SetWidth(.008f, .008f);
//leftHandLine.SetColors(Color.blue, Color.blue);
//gObjRightLine = new GameObject();
//rightHandLine = gObjRightLine.AddComponent<LineRenderer>();
//rightHandLine.positionCount = 2;
//rightHandLine.SetWidth(.008f, .008f);
//rightHandLine.SetColors(Color.blue, Color.blue);
}
void FixedUpdate()
{
FVRInteractiveObject[] objs = { leftHand.CurrentInteractable, rightHand.CurrentInteractable };
LineRenderer[] lines = { leftHandLine, rightHandLine };
bool rayHit = false;
for (int i = 0; i < 2; ++i)
{
Transform transMuzzle;
if (objs[i] is FVRFireArm)
{
var firearm = objs[i] as FVRFireArm;
transMuzzle = firearm.CurrentMuzzle;
}
else if (objs[i] is SosigWeaponPlayerInterface)
{
var playerInterface = objs[i] as SosigWeaponPlayerInterface;
var sosigWeapon = playerInterface.gameObject.GetComponent<SosigWeapon>();
transMuzzle = sosigWeapon.Muzzle;
}
else // not a recognized weapon, don't do anything
continue;
// TESTING: draw beam from held wpn muzzle
//Vector3[] beamPos = { transMuzzle.position, transMuzzle.position + 5 * transMuzzle.forward };
//lines[i].SetPositions(beamPos);
RaycastHit hitInfo;
if (hudCollider.Raycast(new Ray(transMuzzle.position, transMuzzle.forward), out hitInfo, 20) ||
hudCollider.Raycast(new Ray(transMuzzle.position, -transMuzzle.forward), out hitInfo, 20))
rayHit = true;
}
if (rayHit)
{
canvasGroup.alpha = 0.1f;
if (background != null)
background.enabled = false;
}
else
{
canvasGroup.alpha = 1f;
if (background != null)
background.enabled = true;
}
// TESTING: draw the collider
//var trans = hudCollider.transform;
//var min = hudCollider.center - hudCollider.size * 0.5f;
//var max = hudCollider.center + hudCollider.size * 0.5f;
//Vector3[] points = {
// trans.TransformPoint(new Vector3(min.x, min.y, min.z)),
// trans.TransformPoint(new Vector3(min.x, min.y, max.z)),
// trans.TransformPoint(new Vector3(min.x, max.y, min.z)),
// trans.TransformPoint(new Vector3(min.x, max.y, max.z)),
// trans.TransformPoint(new Vector3(max.x, min.y, min.z)),
// trans.TransformPoint(new Vector3(max.x, min.y, max.z)),
// trans.TransformPoint(new Vector3(max.x, max.y, min.z)),
// trans.TransformPoint(new Vector3(max.x, max.y, max.z))
//};
//colliderRenderer.SetPositions(points);
}
}
+12
View File
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 4c5bf6246707f204db6b73a5d74267f8
timeCreated: 1644787924
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+30 -10
View File
@@ -3,32 +3,45 @@ using UnityEngine.UI;
namespace TNHQoLImprovements namespace TNHQoLImprovements
{ {
public enum HPTextType
{
Solidify, Untouched, Hidden
}
public static class HPReadability public static class HPReadability
{ {
public static void ImproveHPTextReadability(GameObject gObjHUD) public static void ImproveHPTextReadability(GameObject gObjHUD)
{ {
var canvas = gObjHUD.GetComponent<Canvas>(); var canvas = gObjHUD.GetComponent<Canvas>();
var gObjBG = new GameObject();
Transform[] tranHPText = { Transform[] tranHPText = {
gObjHUD.transform.Find("Label_Title (1)"), gObjHUD.transform.Find("Label_Title (1)"), // header
gObjHUD.transform.Find("Label_Title") gObjHUD.transform.Find("Label_Title") // HP number
}; };
// apply background // apply background only if hp text type is not "Hidden"
if (MeatKitPlugin.cfgShowHPBackground.Value) if (MeatKitPlugin.cfgShowHPBackground.Value &&
MeatKitPlugin.cfgHPTextType.Value != HPTextType.Hidden)
{ {
gObjBG.transform.parent = gObjHUD.transform; var gObjBG = new GameObject();
gObjBG.name = "Background";
gObjBG.transform.SetParent(gObjHUD.transform, false);
gObjBG.transform.SetSiblingIndex(0); gObjBG.transform.SetSiblingIndex(0);
gObjBG.transform.localPosition = new Vector3(0, 1, 0); gObjBG.transform.localPosition = new Vector3(0, 1, 0);
gObjBG.transform.localRotation = Quaternion.identity;
gObjBG.transform.localScale = tranHPText[0].localScale; gObjBG.transform.localScale = tranHPText[0].localScale;
//gObjBG.transform.localRotation = Quaternion.identity;
var rawImage = gObjBG.AddComponent<RawImage>(); var rawImage = gObjBG.AddComponent<RawImage>();
rawImage.color = new Color(0, 0, 0, MeatKitPlugin.cfgHPBackgroundOpacity.Value); rawImage.color = new Color(0, 0, 0, MeatKitPlugin.cfgHPBackgroundOpacity.Value);
rawImage.rectTransform.SetWidth(100); rawImage.rectTransform.SetWidth(85);
rawImage.rectTransform.SetHeight(52); rawImage.rectTransform.SetHeight(44);
} }
if (MeatKitPlugin.cfgSolidifyHPText.Value)
// set text type
if (MeatKitPlugin.cfgHPTextType.Value == HPTextType.Untouched)
return;
switch (MeatKitPlugin.cfgHPTextType.Value)
{ {
case HPTextType.Solidify:
foreach (var text in tranHPText) foreach (var text in tranHPText)
{ {
// full alpha // full alpha
@@ -38,6 +51,13 @@ namespace TNHQoLImprovements
shadow.effectColor = new Color(0, 0, 0, .95f); shadow.effectColor = new Color(0, 0, 0, .95f);
shadow.effectDistance = new Vector2(0.5f, -0.5f); shadow.effectDistance = new Vector2(0.5f, -0.5f);
} }
break;
case HPTextType.Hidden:
foreach (var text in tranHPText)
{
text.GetComponent<Text>().color = new Color(0, 0, 0, 0);
}
break;
} }
} }
} }
-1
View File
@@ -42,7 +42,6 @@ namespace TNHQoLImprovements
int handSide = tnhManager.RadarHand == TNH_RadarHand.Left ? 0 : 1; int handSide = tnhManager.RadarHand == TNH_RadarHand.Left ? 0 : 1;
tnhInfo.transform.SetParent(hands[handSide], false); tnhInfo.transform.SetParent(hands[handSide], false);
tnhInfo.transform.localScale = new Vector3(.0002f, .0002f, .0002f);
tnhInfo.GetComponent<TNHInfo>().GameOverPos(); tnhInfo.GetComponent<TNHInfo>().GameOverPos();
} }
} }
+1 -1
View File
@@ -33,7 +33,7 @@ namespace TNHQoLImprovements
if (Array.Exists<Assembly>(loadedAssemblies, x => x.GetName().Name == "TakeAndHoldTweaker")) if (Array.Exists<Assembly>(loadedAssemblies, x => x.GetName().Name == "TakeAndHoldTweaker"))
{ {
tnhTweakerInstalled = true; tnhTweakerInstalled = true;
this.gObjLoading.transform.GetChild(0).GetComponent<Text>().text = "<color=lightblue><size=30>Online player count is incompatible with TNHTweaker.</size></color>"; this.gObjLoading.transform.GetChild(0).GetComponent<Text>().text = "<color=lightblue><size=30>Online leaderboards player count unavailable for TNHTweaker.</size></color>";
this.gObjLoading.SetActive(true); this.gObjLoading.SetActive(true);
return; return;
} }
+29 -6
View File
@@ -24,23 +24,34 @@ namespace TNHQoLImprovements
public void PlayPos() public void PlayPos()
{ {
transform.localPosition = new Vector3(0, 0, -1.2f);
if (holdCounter != null) if (holdCounter != null)
holdCounter.transform.localPosition = new Vector3(-333, 0, -450); holdCounter.transform.localPosition = new Vector3(-333, 0, 0);
if (tokenCounter != null) if (tokenCounter != null)
tokenCounter.transform.localPosition = new Vector3(333, 0, -450); tokenCounter.transform.localPosition = new Vector3(333, 0, 0);
if (waveCounter != null) if (waveCounter != null)
waveCounter.transform.localPosition = new Vector3(333, 0, -450); waveCounter.transform.localPosition = new Vector3(333, 0, 0);
} }
public void GameOverPos() public void GameOverPos()
{ {
transform.localScale = new Vector3(.0002f, .0002f, .0002f);
transform.localPosition = Vector3.zero;
transform.localRotation = Quaternion.identity;
if (holdCounter != null) if (holdCounter != null)
{
holdCounter.gameObject.GetComponent<RectTransform>().pivot = new Vector2(1, 1);
holdCounter.transform.localPosition = new Vector3(-250, 0, 0); holdCounter.transform.localPosition = new Vector3(-250, 0, 0);
}
if (tokenCounter != null) if (tokenCounter != null)
{
tokenCounter.gameObject.GetComponent<RectTransform>().pivot = new Vector2(0, 1);
tokenCounter.transform.localPosition = new Vector3(250, 0, 0); tokenCounter.transform.localPosition = new Vector3(250, 0, 0);
}
if (waveCounter != null) if (waveCounter != null)
{ {
@@ -51,7 +62,8 @@ namespace TNHQoLImprovements
public void Update() public void Update()
{ {
if (InPlay.tnhManager.Phase == TNH_Phase.Dead) // game over area; do not update anything else
if (InPlay.tnhManager.Phase == TNH_Phase.Dead || InPlay.tnhManager.Phase == TNH_Phase.Completed)
{ {
if (tokenCounter != null) if (tokenCounter != null)
tokenCounter.SetActive(true); tokenCounter.SetActive(true);
@@ -59,7 +71,18 @@ namespace TNHQoLImprovements
return; return;
} }
// we're in a hold; hide token count // TNHInfo rotate to player camera
if (MeatKitPlugin.cfgInfoFollowCamera.Value)
{
transform.LookAt(MeatKitPlugin.playerCamera.transform);
var rotLook = transform.localEulerAngles;
var rot = Vector3.zero;
rot.x = -rotLook.x - 90;
transform.localRotation = Quaternion.Euler(rot);
}
// we're in a hold; hide token count and show wave count
if (InPlay.tnhManager.Phase == TNH_Phase.Hold) if (InPlay.tnhManager.Phase == TNH_Phase.Hold)
{ {
if (tokenCounter != null) if (tokenCounter != null)
@@ -68,7 +91,7 @@ namespace TNHQoLImprovements
if (waveCounter != null) if (waveCounter != null)
waveCounter.SetActive(true); waveCounter.SetActive(true);
} }
else // show token count else // NOT in hold; do the inverse
{ {
if (tokenCounter != null) if (tokenCounter != null)
tokenCounter.SetActive(true); tokenCounter.SetActive(true);
+1 -1
View File
@@ -34,7 +34,7 @@ namespace TNHQoLImprovements
yield return new WaitForEndOfFrame(); yield return new WaitForEndOfFrame();
} }
} }
Debug.Log("Token sprite found after " + debug_iterations.ToString() + " iterations."); //Debug.Log("Token sprite found after " + debug_iterations.ToString() + " iterations.");
transform.GetChild(0).GetComponent<Image>().sprite = tokenSprite; transform.GetChild(0).GetComponent<Image>().sprite = tokenSprite;
} }
+10 -4
View File
@@ -3,16 +3,22 @@ A *Hot Dogs, Horseshoes, and Hand Grenades* mod that adds quality of life improv
Get it on [Thunderstore](https://h3vr.thunderstore.io/package/muskit/TNH_Quality_of_Life_Improvements/). Get it on [Thunderstore](https://h3vr.thunderstore.io/package/muskit/TNH_Quality_of_Life_Improvements/).
## Features ## Features (check Thunderstore for up-to-date list)
More visible HP counter<br/> More visible HP counter<br/>
<img src=preview/hp.png> <img src=preview/hp.png>
Token and hold counter on wrist<br/> Less visible HP counter when aiming around it<br/>
<img src=preview/stats.png> <img src=preview/aimhide.png>
Useful contextual game stats on radar hand<br/>
<img src=preview/stats_new.png>
Numbered tokens at item stations<br/>
<img src="preview/item station.png">
Player count for online leaderboards (won't work w/ *TNHTweaker* installed)<br/> Player count for online leaderboards (won't work w/ *TNHTweaker* installed)<br/>
<img src=preview/leaderboard.png><br/> <img src=preview/leaderboard.png><br/>
Features are modifiable via the mod manager's *Config editor*. Features are modifiable via the mod manager's *Config editor*.
**NOTE: [*TNH Leaderboard Player Count*](https://github.com/muskit/TNH-Leaderboard-Player-Count/) has been merged with this mod.** **This mod is built on [MeatKit](https://github.com/H3VR-Modding/MeatKit).**
Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB