mirror of
https://github.com/NotAKidoS/NAK_CVR_Mods.git
synced 2026-04-16 22:27:02 +00:00
Compare commits
No commits in common. "2b8f17b2872ad8e65cb11a8e4b2da200b8176e9b" and "21a5d38af43fd467f5e89bb5eeedbe0a82d1f0c3" have entirely different histories.
2b8f17b287
...
21a5d38af4
6 changed files with 19 additions and 533 deletions
|
|
@ -1,6 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
<PropertyGroup>
|
|
||||||
<RootNamespace>ChatBoxHud</RootNamespace>
|
|
||||||
</PropertyGroup>
|
|
||||||
</Project>
|
|
||||||
|
|
@ -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<HudTarget> EntryHudMode =
|
|
||||||
Cat.CreateEntry("hud_mode", HudTarget.LookAtThenNearest, "HUD Target Mode");
|
|
||||||
|
|
||||||
internal static readonly MelonPreferences_Entry<SourceFilter> EntryHudFilter =
|
|
||||||
Cat.CreateEntry("hud_filter", SourceFilter.Everyone, "Chat Filter",
|
|
||||||
description: "Whose chat messages appear on the HUD.");
|
|
||||||
|
|
||||||
internal static readonly MelonPreferences_Entry<MaxUsersOnHud> 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<bool> EntryUsePlayerColors =
|
|
||||||
Cat.CreateEntry("player_colors", true, "Use Player Colors",
|
|
||||||
description: "Use player-chosen colors for names instead of friend colors.");
|
|
||||||
|
|
||||||
private static readonly Dictionary<string, ChatBuf> 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<string> _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<Candidate> _candidates = new();
|
|
||||||
private static readonly Dictionary<string, Candidate> _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<Msg> 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<PortalHudIndicator>();
|
|
||||||
}
|
|
||||||
|
|
||||||
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<string, ChatBuf> 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("<b>\u25C4 </b>");
|
|
||||||
sb.Append("<alpha=#FF><b><color=").Append(hex).Append('>')
|
|
||||||
.Append(Esc(playerName)).Append("</color></b>");
|
|
||||||
AppendAlpha(sb, ra); sb.Append("<b> \u25BA</b><alpha=#FF>\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("<alpha=#").Append(a.ToString("X2")).Append('>')
|
|
||||||
.Append("<size=").Append(m.Size).Append("%>")
|
|
||||||
.Append(m.Lines[off + j])
|
|
||||||
.Append("</size>\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("<alpha=#").Append(((byte)(a01 * 255)).ToString("X2")).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<string> result = new List<string>(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");
|
|
||||||
}
|
|
||||||
|
|
@ -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";
|
|
||||||
}
|
|
||||||
|
|
@ -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.
|
|
||||||
|
|
@ -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"
|
|
||||||
}
|
|
||||||
39
README.md
39
README.md
|
|
@ -6,26 +6,25 @@
|
||||||
|
|
||||||
| Name | Description | Download |
|
| Name | Description | Download |
|
||||||
|------|-------------|----------|
|
|------|-------------|----------|
|
||||||
| [ASTExtension](ASTExtension/README.md) | Extension mod for [Avatar Scale Tool](https://github.com/NotAKidoS/AvatarScaleTool): | No Download |
|
| [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) |
|
||||||
| [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) |
|
|
||||||
| [ConfigureCalibrationPose](ConfigureCalibrationPose/README.md) | Select FBT calibration pose. | No Download |
|
| [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 |
|
| [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. | 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. | [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. | 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. | [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. | No Download |
|
| [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. | No Download |
|
| [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. | 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. | [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 | No Download |
|
| [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. | [Download](https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r49/PropsButBetter.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. | 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. | 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. | [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. | 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. | [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. | 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. | [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. | No Download |
|
| [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 |
|
| [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 |
|
| [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. | No Download |
|
| [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. | No Download |
|
| [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
|
### Experimental Mods
|
||||||
|
|
||||||
|
|
@ -34,9 +33,9 @@
|
||||||
| [ByeByePerformanceThankYouAMD](.Experimental/ByeByePerformanceThankYouAMD/README.md) | Fixes search terms that use spaces. | No Download |
|
| [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 |
|
| [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 |
|
| [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 |
|
| [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 |
|
| [ScriptingSpoofer](.Experimental/ScriptingSpoofer/README.md) | Prevents **local** scripts from accessing your Username or UserID by spoofing them with random values each session. | No Download |
|
||||||
|
|
||||||
<!-- END MOD LIST -->
|
<!-- END MOD LIST -->
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue