diff --git a/ChatBoxHud/ChatBoxHud.csproj b/ChatBoxHud/ChatBoxHud.csproj deleted file mode 100644 index 1b27897..0000000 --- a/ChatBoxHud/ChatBoxHud.csproj +++ /dev/null @@ -1,6 +0,0 @@ - - - - ChatBoxHud - - diff --git a/ChatBoxHud/Main.cs b/ChatBoxHud/Main.cs deleted file mode 100644 index 107e8e6..0000000 --- a/ChatBoxHud/Main.cs +++ /dev/null @@ -1,438 +0,0 @@ -using System.Text; -using ABI_RC.Core.Player; -using ABI_RC.Core.Networking.IO.Social; -using ABI_RC.Core.UI; -using ABI_RC.Core.UI.Hud; -using ABI_RC.Systems.ChatBox; -using ABI_RC.Systems.Communications.Settings; -using ABI_RC.Systems.GameEventSystem; -using ABI_RC.Systems.PlayerColors; -using MelonLoader; -using UnityEngine; - -namespace NAK.ChatBoxHud; - -public class ChatBoxHudMod : MelonMod -{ - public enum HudTarget { Disabled, LookAt, Nearest, LookAtThenNearest } - public enum SourceFilter { None, FriendsOnly, Everyone } - public enum MaxUsersOnHud { One, Two, Three } - - private const int MaxLines = 4; - private const int BaseCPL = 62; - private const int BaseSize = 80; - private const int MinSize = 50; - private const int ShrinkAt = 3; - private const float MinLife = 5f; - private const float MaxLife = 20f; - private const float WPM = 250f; - private const float CPW = 5f; - private const float LookAngle = 20f; - private const float FadePct = 0.2f; - private const int MsgCap = 1000; - - private static readonly MelonPreferences_Category Cat = - MelonPreferences.CreateCategory(nameof(ChatBoxHud)); - - internal static readonly MelonPreferences_Entry EntryHudMode = - Cat.CreateEntry("hud_mode", HudTarget.LookAtThenNearest, "HUD Target Mode"); - - internal static readonly MelonPreferences_Entry EntryHudFilter = - Cat.CreateEntry("hud_filter", SourceFilter.Everyone, "Chat Filter", - description: "Whose chat messages appear on the HUD."); - - internal static readonly MelonPreferences_Entry EntryMaxUsersOnHud = - Cat.CreateEntry("max_users_on_hud", MaxUsersOnHud.Three, "Max Users on HUD", - description: "How many users can be stacked on the HUD when multiple users chat at once."); - - internal static readonly MelonPreferences_Entry EntryUsePlayerColors = - Cat.CreateEntry("player_colors", true, "Use Player Colors", - description: "Use player-chosen colors for names instead of friend colors."); - - private static readonly Dictionary Buffers = new(); - private static readonly StringBuilder SB = new(); - private static readonly int[] VisSlots = new int[MaxLines * 3 + 1]; - - private static PortalHudIndicator _hud; - - #region Slot System - - // Stable slot list: once a user occupies an index they keep it until - // all their messages expire or they leave range. New users append to - // the first open position. This prevents blocks from jumping around - // when someone else sends a new message. - private static readonly List _slotIds = new(); - private static bool _hudActive; - - private struct Candidate - { - public string Id, Name; - public Vector3 Pos; - public float Score; // lower = higher priority - } - - private static readonly List _candidates = new(); - private static readonly Dictionary _candidateLookup = new(); - - #endregion - - private struct Incoming - { - public string Id, Name, Text; - public ChatBoxAPI.MessageSource Source; - } - - private class Msg - { - public string[] Lines; - public int Size; - public float Stamp, Life; - public ChatBoxAPI.MessageSource Source; - } - - private class ChatBuf - { - public string Id; - public readonly List Msgs = new(); - } - - public override void OnInitializeMelon() - { - ChatBoxAPI.OnMessageReceived += OnReceive; - CVRGameEventSystem.Initialization.OnPlayerSetupStart.AddListener(OnPlayerSetupStart); - } - - private static void OnPlayerSetupStart() - { - Transform src = HudController.Instance.PortalHudIndicator.transform; - GameObject go = UnityEngine.Object.Instantiate(src.gameObject, src.parent); - go.transform.SetLocalPositionAndRotation(src.localPosition, src.localRotation); - go.transform.localScale = src.localScale; - _hud = go.GetComponent(); - } - - public override void OnUpdate() - { - Tick(); - } - - private static void Tick() - { - if (_hud == null || EntryHudMode.Value == HudTarget.Disabled) { Hide(); return; } - Prune(); - - int maxSlots = (int)EntryMaxUsersOnHud.Value + 1; - GatherCandidates(); - - // Remove slots whose user has no messages left or is no longer - // a valid candidate (walked out of range, left the instance, etc.) - for (int i = _slotIds.Count - 1; i >= 0; i--) - { - string sid = _slotIds[i]; - bool dead = !_candidateLookup.ContainsKey(sid) - || !Buffers.TryGetValue(sid, out ChatBuf b) - || b.Msgs.Count == 0; - if (dead) _slotIds.RemoveAt(i); - } - - // If the setting was lowered at runtime, trim from the end - while (_slotIds.Count > maxSlots) - _slotIds.RemoveAt(_slotIds.Count - 1); - - // Fill empty slots from best-scored candidates not already slotted - if (_slotIds.Count < maxSlots && _candidates.Count > 0) - { - _candidates.Sort((a, b) => a.Score.CompareTo(b.Score)); - foreach (Candidate c in _candidates) - { - if (_slotIds.Count >= maxSlots) break; - if (_slotIds.Contains(c.Id)) continue; - _slotIds.Add(c.Id); - } - } - - if (_slotIds.Count == 0) { Hide(); return; } - - // Render every active slot into one combined string - SB.Clear(); - bool first = true; - foreach (string slotId in _slotIds) - { - if (!_candidateLookup.TryGetValue(slotId, out Candidate cand)) continue; - if (!Buffers.TryGetValue(slotId, out ChatBuf buf) || buf.Msgs.Count == 0) continue; - - if (!first) SB.Append('\n'); - RenderBlock(SB, cand.Name, cand.Id, buf, cand.Pos); - first = false; - } - - _hud.SetText(SB.ToString()); - if (!_hudActive) { _hudActive = true; _hud.Activate(); } - } - - private static void Hide() - { - if (!_hudActive) return; - _hud?.Deactivate(); - _hudActive = false; - _slotIds.Clear(); - } - - // Scores every player who has buffered messages, is in comms range, - // and passes the current HudTarget mode filter. - private static void GatherCandidates() - { - _candidates.Clear(); - _candidateLookup.Clear(); - - HudTarget mode = EntryHudMode.Value; - Camera cam = Camera.main; - if (cam == null) return; - - Vector3 lp = PlayerSetup.Instance.GetPlayerPosition(); - Vector3 cf = cam.transform.forward; - Vector3 cp = cam.transform.position; - float commsRange = Comms_SettingsHandler.FalloffDistance + 1; - - foreach (KeyValuePair kvp in Buffers) - { - if (kvp.Value.Msgs.Count == 0) continue; - if (!CVRPlayerManager.Instance.TryGetPlayerBase(kvp.Key, out PlayerBase pb)) continue; - - Vector3 pp = pb.GetPlayerWorldRootPosition(); - float d = Vector3.Distance(lp, pp); - if (d > commsRange) continue; - - string uid = kvp.Key; - string n = pb.PlayerUsername ?? "???"; - float angle = Vector3.Angle(cf, (pp - cp).normalized); - bool inLook = angle < LookAngle; - - float score; - switch (mode) - { - case HudTarget.LookAt: - if (!inLook) continue; // hard filter - score = angle; - break; - case HudTarget.Nearest: - score = d; - break; - case HudTarget.LookAtThenNearest: - // look-at targets always rank above distance-only - score = inLook ? angle : 1000f + d; - break; - default: - continue; - } - - Candidate c = new Candidate { Id = uid, Name = n, Pos = pp, Score = score }; - _candidates.Add(c); - _candidateLookup[uid] = c; - } - } - - private static void RenderBlock(StringBuilder sb, string playerName, string playerId, - ChatBuf buf, Vector3 tgtPos) - { - Camera cam = PlayerSetup.Instance.activeCam; - float now = Time.time; - - // direction arrows from camera angle to target - float la = 0f, ra = 0f; - { - Vector3 toT = tgtPos - cam.transform.position; - Vector3 fwd = cam.transform.forward; - toT.y = 0f; - fwd.y = 0f; - if (toT.sqrMagnitude > 0.001f && fwd.sqrMagnitude > 0.001f) - { - float sa = Vector3.SignedAngle(fwd.normalized, toT.normalized, Vector3.up); - float abs = Mathf.Abs(sa); - if (abs > LookAngle) - { - float t = Mathf.InverseLerp(LookAngle, 90f, abs); - float v = Mathf.Clamp01(t); - la = sa < 0f ? v : 0f; - ra = sa > 0f ? v : 0f; - } - } - } - - // name color: player colors or friend-based fallback - string hex; - if (EntryUsePlayerColors.Value) - { - PlayerColors pc = PlayerColorsManager.GetPlayerColors(playerId); - hex = "#" + ColorUtility.ToHtmlStringRGB(pc.PrimaryColor); - } - else hex = Friends.FriendsWith(playerId) ? "#4FC3F7" : "#E0E0E0"; - - // header line - AppendAlpha(sb, la); sb.Append("\u25C4 "); - sb.Append("') - .Append(Esc(playerName)).Append(""); - AppendAlpha(sb, ra); sb.Append(" \u25BA\n"); - - // allocate visible line count per message, newest gets priority - int count = 0, budget = MaxLines; - for (int i = buf.Msgs.Count - 1; i >= 0 && budget > 0; i--) - { - int v = Mathf.Min(buf.Msgs[i].Lines.Length, budget); - VisSlots[count++] = v; - budget -= v; - } - - // render oldest to newest (slots were stored newest-first, so reverse) - int startIdx = buf.Msgs.Count - count; - for (int i = 0; i < count; i++) - { - int mi = startIdx + i; - int vis = VisSlots[count - 1 - i]; - Msg m = buf.Msgs[mi]; - float age = now - m.Stamp; - - float fadeAt = m.Life * (1f - FadePct); - float alpha = age < fadeAt ? 1f - : Mathf.Clamp01(1f - (age - fadeAt) / (m.Life * FadePct)); - byte a = (byte)(alpha * 255); - - // smooth line-by-line scroll for overflowing messages - int off = 0; - if (m.Lines.Length > vis) - { - float usable = m.Life * (1f - FadePct); - float perLine = usable / m.Lines.Length; - float initHold = perLine * vis; - off = Mathf.Min( - (int)(Mathf.Max(0f, age - initHold) / perLine), - m.Lines.Length - vis); - } - - for (int j = 0; j < vis; j++) - { - sb.Append("') - .Append("") - .Append(m.Lines[off + j]) - .Append("\n"); - } - } - - // trim trailing newline from this block - if (sb.Length > 0 && sb[sb.Length - 1] == '\n') sb.Length--; - } - - private static void AppendAlpha(StringBuilder sb, float a01) - { - sb.Append("'); - } - - private static void OnReceive(ChatBoxAPI.ChatBoxMessage msg) - { - if (msg.Source != ChatBoxAPI.MessageSource.Internal) return; - if (string.IsNullOrWhiteSpace(msg.Message)) return; - - string sid = msg.SenderGuid; - if (!CVRPlayerManager.Instance.TryGetPlayerBase(sid, out PlayerBase pb)) return; - - SourceFilter filter = EntryHudFilter.Value; - if (filter == SourceFilter.None) return; - if (filter == SourceFilter.FriendsOnly && !Friends.FriendsWith(sid)) return; - - Store(new Incoming - { - Id = sid, - Name = pb.PlayerUsername ?? "???", - Text = msg.Message, - Source = msg.Source - }); - } - - private static void Store(in Incoming inc) - { - if (!Buffers.TryGetValue(inc.Id, out ChatBuf buf)) - { - buf = new ChatBuf { Id = inc.Id }; - Buffers[inc.Id] = buf; - } - - string text = inc.Text; - if (text.Length > MsgCap) text = text.Substring(0, MsgCap); - text = text.Replace("\r", ""); - - Fit(text, out string[] lines, out int size); - for (int i = 0; i < lines.Length; i++) lines[i] = Esc(lines[i]); - - buf.Msgs.Add(new Msg - { - Lines = lines, - Size = size, - Stamp = Time.time, - Life = Life(text.Length), - Source = inc.Source - }); - - while (buf.Msgs.Count > MaxLines * 3) - buf.Msgs.RemoveAt(0); - } - - private static void Prune() - { - float now = Time.time; - foreach (ChatBuf buf in Buffers.Values) - buf.Msgs.RemoveAll(m => now - m.Stamp > m.Life); - } - - // shrink text size until line count is manageable - private static void Fit(string text, out string[] lines, out int size) - { - lines = Wrap(text, BaseCPL); - size = BaseSize; - if (lines.Length <= ShrinkAt) return; - - for (int s = BaseSize - 5; s >= MinSize; s -= 5) - { - int cpl = (int)(BaseCPL * BaseSize / (float)s); - string[] attempt = Wrap(text, cpl); - lines = attempt; size = s; - if (attempt.Length <= ShrinkAt) return; - } - } - - // word wrap respecting embedded newlines - private static string[] Wrap(string text, int max) - { - if (string.IsNullOrEmpty(text)) return new[] { string.Empty }; - - List result = new List(4); - foreach (string seg in text.Split('\n')) - { - if (seg.Length <= max) { result.Add(seg); continue; } - - StringBuilder cur = new StringBuilder(max); - foreach (string w in seg.Split(' ')) - { - if (w.Length == 0) continue; - if (w.Length > max) - { - if (cur.Length > 0) { result.Add(cur.ToString()); cur.Clear(); } - for (int c = 0; c < w.Length; c += max) - result.Add(w.Substring(c, Math.Min(max, w.Length - c))); - continue; - } - if (cur.Length == 0) cur.Append(w); - else if (cur.Length + 1 + w.Length <= max) cur.Append(' ').Append(w); - else { result.Add(cur.ToString()); cur.Clear(); cur.Append(w); } - } - if (cur.Length > 0) result.Add(cur.ToString()); - } - return result.ToArray(); - } - - private static float Life(int chars) - { - return Mathf.Clamp(MinLife + chars * (60f / (WPM * CPW)), MinLife, MaxLife); - } - - private static string Esc(string s) => s.Replace("<", "<\u200B"); -} \ No newline at end of file diff --git a/ChatBoxHud/Properties/AssemblyInfo.cs b/ChatBoxHud/Properties/AssemblyInfo.cs deleted file mode 100644 index a6cd27b..0000000 --- a/ChatBoxHud/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,32 +0,0 @@ -using MelonLoader; -using NAK.ChatBoxHud.Properties; -using System.Reflection; - -[assembly: AssemblyVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] -[assembly: AssemblyTitle(nameof(NAK.ChatBoxHud))] -[assembly: AssemblyCompany(AssemblyInfoParams.Author)] -[assembly: AssemblyProduct(nameof(NAK.ChatBoxHud))] - -[assembly: MelonInfo( - typeof(NAK.ChatBoxHud.ChatBoxHudMod), - nameof(NAK.ChatBoxHud), - AssemblyInfoParams.Version, - AssemblyInfoParams.Author, - downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/ChatBoxHud" -)] - -[assembly: MelonGame("ChilloutVR", "ChilloutVR")] -[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] -[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)] -[assembly: MelonColor(255, 246, 25, 99)] // red-pink -[assembly: MelonAuthorColor(255, 158, 21, 32)] // red -[assembly: HarmonyDontPatchAll] - -namespace NAK.ChatBoxHud.Properties; -internal static class AssemblyInfoParams -{ - public const string Version = "1.0.0"; - public const string Author = "NotAKidoS"; -} \ No newline at end of file diff --git a/ChatBoxHud/README.md b/ChatBoxHud/README.md deleted file mode 100644 index 126be0a..0000000 --- a/ChatBoxHud/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# ChatBoxHud - -Shows nearby ChatBox messages directly on your HUD so you can read them comfortably in VR without breaking your neck. - ---- - -Here is the block of text where I tell you this mod is not affiliated with or endorsed by ABI. -https://documentation.abinteractive.net/official/legal/tos/#7-modding-our-games - -> This mod is an independent creation not affiliated with, supported by, or approved by Alpha Blend Interactive. - -> Use of this mod is done so at the user's own risk and the creator cannot be held responsible for any issues arising from its use. - -> To the best of my knowledge, I have adhered to the Modding Guidelines established by Alpha Blend Interactive. diff --git a/ChatBoxHud/format.json b/ChatBoxHud/format.json deleted file mode 100644 index cd46a8b..0000000 --- a/ChatBoxHud/format.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "_id": -1, - "name": "ChatBoxHud", - "modversion": "1.0.0", - "gameversion": "2026r181", - "loaderversion": "0.7.2", - "modtype": "Mod", - "author": "NotAKidoS", - "description": "Shows nearby ChatBox messages directly on your HUD so you can read them comfortably in VR without breaking your neck.", - "searchtags": [ - "chat", - "box", - "hud", - "indicator", - "captions" - ], - "requirements": [ - ], - "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r49/ChatBoxHud.dll", - "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/ChatBoxHud/", - "changelog": "- Initial release", - "embedcolor": "#f61963" -} \ No newline at end of file diff --git a/README.md b/README.md index 9b18896..79bc57d 100644 --- a/README.md +++ b/README.md @@ -6,26 +6,25 @@ | Name | Description | Download | |------|-------------|----------| -| [ASTExtension](ASTExtension/README.md) | Extension mod for [Avatar Scale Tool](https://github.com/NotAKidoS/AvatarScaleTool): | No Download | -| [ChatBoxHud](ChatBoxHud/README.md) | Shows nearby ChatBox messages directly on your HUD so you can read them comfortably in VR without breaking your neck. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r49/ChatBoxHud.dll) | +| [ASTExtension](ASTExtension/README.md) | Extension mod for [Avatar Scale Tool](https://github.com/NotAKidoS/AvatarScaleTool): | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r48/ASTExtension.dll) | | [ConfigureCalibrationPose](ConfigureCalibrationPose/README.md) | Select FBT calibration pose. | No Download | -| [CustomSpawnPoint](CustomSpawnPoint/README.md) | Replaces the unused Images button in the World Details page with a button to set a custom spawn point. | No Download | -| [DesktopInteractions](DesktopInteractions/README.md) | Adds IK-driven hand gestures to your avatar in Desktop: earpiece grab (GMOD-style) when typing in ChatBox, and binocular cupping when zooming. Both gestures are toggleable in settings. | No Download | -| [DoubleTapJumpToExitSeat](DoubleTapJumpToExitSeat/README.md) | Replaces seat exit controls with a double-tap of the jump button, avoiding accidental exits from joystick drift or opening the menu. | No Download | -| [ESCBothMenus](ESCBothMenus/README.md) | Makes the Quick Menu appear when pressing ESC in Desktop. Pressing twice will open straight to Main Menu. | No Download | -| [FuckToes](FuckToes/README.md) | Prevents VRIK from autodetecting toes in Halfbody or Fullbody. | No Download | -| [PlapPlapForAll](PlapPlapForAll/README.md) | Penetrator SFX mod which adds Noach's PlapPlap prefab to any detected DPS setup on avatars that do not already have it. | No Download | -| [PropLoadingHexagon](PropLoadingHexagon/README.md) | https://github.com/NotAKidoS/NAK_CVR_Mods/assets/37721153/a892c765-71c1-47f3-a781-bdb9b60ba117 | No Download | -| [PropsButBetter](PropsButBetter/README.md) | Prop quality-of-life suite. Adds a few new ways to interact with and manage Props. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r49/PropsButBetter.dll) | -| [RCCVirtualSteeringWheel](RCCVirtualSteeringWheel/README.md) | Allows you to physically grab rigged RCC steering wheels in VR to provide steering input. No explicit setup required other than defining the Steering Wheel transform within the RCC component. | No Download | -| [RelativeSyncJitterFix](RelativeSyncJitterFix/README.md) | Relative sync jitter fix is the single harmony patch that could not make it into the native release of RelativeSync. | No Download | -| [ShareBubbles](ShareBubbles/README.md) | Share Bubbles! Allows you to drop down bubbles containing Avatars & Props. Requires both users to have the mod installed. Synced over Mod Network. | No Download | -| [ShowPlayerInSelfMirror](ShowPlayerInSelfMirror/README.md) | Adds an option in the Quick Menu selected player page to show the target player's avatar in your self mirror. | No Download | -| [SmootherRay](SmootherRay/README.md) | Smoothes your controller while the raycast lines are visible. | No Download | +| [CustomSpawnPoint](CustomSpawnPoint/README.md) | Replaces the unused Images button in the World Details page with a button to set a custom spawn point. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r48/CustomSpawnPoint.dll) | +| [DesktopInteractions](DesktopInteractions/README.md) | Adds IK-driven hand gestures to your avatar in Desktop: earpiece grab (GMOD-style) when typing in ChatBox, and binocular cupping when zooming. Both gestures are toggleable in settings. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r48/DesktopInteractions.dll) | +| [DoubleTapJumpToExitSeat](DoubleTapJumpToExitSeat/README.md) | Replaces seat exit controls with a double-tap of the jump button, avoiding accidental exits from joystick drift or opening the menu. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r48/DoubleTapJumpToExitSeat.dll) | +| [ESCBothMenus](ESCBothMenus/README.md) | Makes the Quick Menu appear when pressing ESC in Desktop. Pressing twice will open straight to Main Menu. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r48/ESCBothMenus.dll) | +| [FuckToes](FuckToes/README.md) | Prevents VRIK from autodetecting toes in Halfbody or Fullbody. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r48/FuckToes.dll) | +| [PlapPlapForAll](PlapPlapForAll/README.md) | Penetrator SFX mod which adds Noach's PlapPlap prefab to any detected DPS setup on avatars that do not already have it. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r48/PlapPlapForAll.dll) | +| [PropLoadingHexagon](PropLoadingHexagon/README.md) | https://github.com/NotAKidoS/NAK_CVR_Mods/assets/37721153/a892c765-71c1-47f3-a781-bdb9b60ba117 | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r48/PropLoadingHexagon.dll) | +| [PropsButBetter](PropsButBetter/README.md) | Prop quality-of-life suite. Adds a few new ways to interact with and manage Props. | No Download | +| [RCCVirtualSteeringWheel](RCCVirtualSteeringWheel/README.md) | Allows you to physically grab rigged RCC steering wheels in VR to provide steering input. No explicit setup required other than defining the Steering Wheel transform within the RCC component. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r48/RCCVirtualSteeringWheel.dll) | +| [RelativeSyncJitterFix](RelativeSyncJitterFix/README.md) | Relative sync jitter fix is the single harmony patch that could not make it into the native release of RelativeSync. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r48/RelativeSyncJitterFix.dll) | +| [ShareBubbles](ShareBubbles/README.md) | Share Bubbles! Allows you to drop down bubbles containing Avatars & Props. Requires both users to have the mod installed. Synced over Mod Network. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r48/ShareBubbles.dll) | +| [ShowPlayerInSelfMirror](ShowPlayerInSelfMirror/README.md) | Adds an option in the Quick Menu selected player page to show the target player's avatar in your self mirror. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r48/ShowPlayerInSelfMirror.dll) | +| [SmootherRay](SmootherRay/README.md) | Smoothes your controller while the raycast lines are visible. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r48/SmootherRay.dll) | | [Stickers](Stickers/README.md) | Stickers! Allows you to place small images on any surface. Requires both users to have the mod installed. Synced over Mod Network. | No Download | -| [ThirdPerson](ThirdPerson/README.md) | Original repo: https://github.com/oestradiol/CVR-Mods | No Download | -| [Tinyboard](Tinyboard/README.md) | Makes the keyboard small and smart. | No Download | -| [YouAreMyPropNowWeAreHavingSoftTacosLater](YouAreMyPropNowWeAreHavingSoftTacosLater/README.md) | Lets you bring held, attached, and occupied props through world loads. This is configurable in the mod settings. | No Download | +| [ThirdPerson](ThirdPerson/README.md) | Original repo: https://github.com/oestradiol/CVR-Mods | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r48/ThirdPerson.dll) | +| [Tinyboard](Tinyboard/README.md) | Makes the keyboard small and smart. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r48/Tinyboard.dll) | +| [YouAreMyPropNowWeAreHavingSoftTacosLater](YouAreMyPropNowWeAreHavingSoftTacosLater/README.md) | Lets you bring held, attached, and occupied props through world loads. This is configurable in the mod settings. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r48/YouAreMyPropNowWeAreHavingSoftTacosLater.dll) | ### Experimental Mods @@ -34,9 +33,9 @@ | [ByeByePerformanceThankYouAMD](.Experimental/ByeByePerformanceThankYouAMD/README.md) | Fixes search terms that use spaces. | No Download | | [CVRLuaToolsExtension](.Experimental/CVRLuaToolsExtension/README.md) | Extension mod for [CVRLuaTools](https://github.com/NotAKidoS/CVRLuaTools) Hot Reload functionality. | No Download | | [CustomRichPresence](.Experimental/CustomRichPresence/README.md) | Lets you customize the Steam & Discord rich presence messages & values. | No Download | -| [LuaNetworkVariables](.Experimental/LuaNetworkVariables/README.md) | Adds a simple module for creating network variables & events *kinda* similar to Garry's Mod. | No Download | +| [LuaNetworkVariables](.Experimental/LuaNetworkVariables/README.md) | Adds a simple module for creating network variables & events *kinda* similar to Garry's Mod. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r48/LuaNetworkVariables.dll) | | [LuaTTS](.Experimental/LuaTTS/README.md) | Provides access to the built-in text-to-speech (TTS) functionality to lua scripts. Allows you to make the local player speak. | No Download | -| [OriginShift](.Experimental/OriginShift/README.md) | Experimental mod that allows world origin to be shifted to prevent floating point precision issues. | No Download | +| [OriginShift](.Experimental/OriginShift/README.md) | Experimental mod that allows world origin to be shifted to prevent floating point precision issues. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r48/OriginShift.dll) | | [ScriptingSpoofer](.Experimental/ScriptingSpoofer/README.md) | Prevents **local** scripts from accessing your Username or UserID by spoofing them with random values each session. | No Download |