Phase 3: Extract RareTracker and InventoryMonitor

- RareTracker.cs: owns rare discovery detection, meta state toggle, WebSocket/allegiance notifications
- InventoryMonitor.cs: owns Prismatic Taper tracking with event-driven delta math
- PluginCore no longer contains inventory event handlers or rare detection logic
- Bridge properties maintain backward compat for WebSocket telemetry

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
erik 2026-02-27 07:33:44 +00:00
parent 366cca8cb6
commit c90e888d32
4 changed files with 306 additions and 232 deletions

View file

@ -0,0 +1,184 @@
using System;
using System.Collections.Generic;
using Decal.Adapter;
using Decal.Adapter.Wrappers;
namespace MosswartMassacre
{
/// <summary>
/// Tracks Prismatic Taper inventory counts using event-driven delta math.
/// Avoids expensive inventory scans during gameplay.
/// </summary>
internal class InventoryMonitor
{
private readonly IPluginLogger _logger;
private readonly Dictionary<int, int> _trackedTaperContainers = new Dictionary<int, int>();
private readonly Dictionary<int, int> _lastKnownStackSizes = new Dictionary<int, int>();
internal int CachedPrismaticCount { get; private set; }
internal int LastPrismaticCount { get; private set; }
internal InventoryMonitor(IPluginLogger logger)
{
_logger = logger;
}
internal void Initialize()
{
try
{
LastPrismaticCount = CachedPrismaticCount;
CachedPrismaticCount = Utils.GetItemStackSize("Prismatic Taper");
_trackedTaperContainers.Clear();
_lastKnownStackSizes.Clear();
foreach (WorldObject wo in CoreManager.Current.WorldFilter.GetInventory())
{
if (wo.Name.Equals("Prismatic Taper", StringComparison.OrdinalIgnoreCase) &&
IsPlayerOwnedContainer(wo.Container))
{
int stackCount = wo.Values(LongValueKey.StackCount, 1);
_trackedTaperContainers[wo.Id] = wo.Container;
_lastKnownStackSizes[wo.Id] = stackCount;
}
}
}
catch (Exception ex)
{
_logger?.Log($"[TAPER] Error initializing count: {ex.Message}");
CachedPrismaticCount = 0;
LastPrismaticCount = 0;
_trackedTaperContainers.Clear();
_lastKnownStackSizes.Clear();
}
}
internal void OnInventoryCreate(object sender, CreateObjectEventArgs e)
{
try
{
var item = e.New;
if (IsPlayerOwnedContainer(item.Container) &&
item.Name.Equals("Prismatic Taper", StringComparison.OrdinalIgnoreCase))
{
LastPrismaticCount = CachedPrismaticCount;
int stackCount = item.Values(LongValueKey.StackCount, 1);
CachedPrismaticCount += stackCount;
_trackedTaperContainers[item.Id] = item.Container;
_lastKnownStackSizes[item.Id] = stackCount;
}
}
catch (Exception ex)
{
_logger?.Log($"[TAPER] Error in OnInventoryCreate: {ex.Message}");
}
}
internal void OnInventoryRelease(object sender, ReleaseObjectEventArgs e)
{
try
{
var item = e.Released;
if (item.Name.Equals("Prismatic Taper", StringComparison.OrdinalIgnoreCase))
{
if (_trackedTaperContainers.TryGetValue(item.Id, out int previousContainer))
{
if (IsPlayerOwnedContainer(previousContainer))
{
LastPrismaticCount = CachedPrismaticCount;
int stackCount = item.Values(LongValueKey.StackCount, 1);
CachedPrismaticCount -= stackCount;
}
_trackedTaperContainers.Remove(item.Id);
_lastKnownStackSizes.Remove(item.Id);
}
else
{
LastPrismaticCount = CachedPrismaticCount;
CachedPrismaticCount = Utils.GetItemStackSize("Prismatic Taper");
}
}
}
catch (Exception ex)
{
_logger?.Log($"[TAPER] Error in OnInventoryRelease: {ex.Message}");
}
}
internal void OnInventoryChange(object sender, ChangeObjectEventArgs e)
{
try
{
var item = e.Changed;
if (item.Name.Equals("Prismatic Taper", StringComparison.OrdinalIgnoreCase))
{
bool isInPlayerContainer = IsPlayerOwnedContainer(item.Container);
if (isInPlayerContainer)
{
bool wasAlreadyTracked = _trackedTaperContainers.ContainsKey(item.Id);
_trackedTaperContainers[item.Id] = item.Container;
int currentStack = item.Values(LongValueKey.StackCount, 1);
if (!wasAlreadyTracked)
{
LastPrismaticCount = CachedPrismaticCount;
CachedPrismaticCount += currentStack;
}
else if (_lastKnownStackSizes.TryGetValue(item.Id, out int previousStack))
{
int stackDelta = currentStack - previousStack;
if (stackDelta != 0)
{
LastPrismaticCount = CachedPrismaticCount;
CachedPrismaticCount += stackDelta;
}
}
_lastKnownStackSizes[item.Id] = currentStack;
}
}
}
catch (Exception ex)
{
_logger?.Log($"[TAPER] Error in OnInventoryChange: {ex.Message}");
}
}
internal void Cleanup()
{
_trackedTaperContainers.Clear();
_lastKnownStackSizes.Clear();
}
internal int TrackedTaperCount => _trackedTaperContainers.Count;
internal int KnownStackSizesCount => _lastKnownStackSizes.Count;
private static bool IsPlayerOwnedContainer(int containerId)
{
try
{
if (containerId == CoreManager.Current.CharacterFilter.Id)
return true;
WorldObject container = CoreManager.Current.WorldFilter[containerId];
if (container != null &&
container.ObjectClass == ObjectClass.Container &&
container.Container == CoreManager.Current.CharacterFilter.Id)
{
return true;
}
return false;
}
catch
{
return false;
}
}
}
}

View file

@ -307,7 +307,9 @@
<Compile Include="CommandRouter.cs" />
<Compile Include="Constants.cs" />
<Compile Include="IPluginLogger.cs" />
<Compile Include="InventoryMonitor.cs" />
<Compile Include="KillTracker.cs" />
<Compile Include="RareTracker.cs" />
<Compile Include="ClientTelemetry.cs" />
<Compile Include="DecalHarmonyClean.cs" />
<Compile Include="FlagTrackerData.cs" />

View file

@ -47,13 +47,9 @@ namespace MosswartMassacre
public static bool IsHotReload { get; set; }
internal static PluginHost MyHost;
internal static int rareCount = 0;
internal static int cachedPrismaticCount = 0; // Cached Prismatic Taper count
internal static int lastPrismaticCount = 0; // For delta calculation
// Track taper items and their containers for accurate release detection
private static readonly Dictionary<int, int> trackedTaperContainers = new Dictionary<int, int>();
private static readonly Dictionary<int, int> lastKnownStackSizes = new Dictionary<int, int>();
// Bridge properties for WebSocket telemetry until IGameStats migration (Phase 5)
private static InventoryMonitor _staticInventoryMonitor;
internal static int cachedPrismaticCount => _staticInventoryMonitor?.CachedPrismaticCount ?? 0;
// Bridge properties for WebSocket telemetry until IGameStats migration (Phase 5)
private static KillTracker _staticKillTracker;
internal static int totalKills => _staticKillTracker?.TotalKills ?? 0;
@ -66,7 +62,12 @@ namespace MosswartMassacre
private static System.Windows.Forms.Timer commandTimer;
private static Timer characterStatsTimer;
private static readonly Queue<string> pendingCommands = new Queue<string>();
public static bool RareMetaEnabled { get; set; } = true;
private static RareTracker _staticRareTracker;
public static bool RareMetaEnabled
{
get => _staticRareTracker?.RareMetaEnabled ?? true;
set { if (_staticRareTracker != null) _staticRareTracker.RareMetaEnabled = value; }
}
// VVS View Management
private static class ViewManager
@ -123,12 +124,12 @@ namespace MosswartMassacre
public static QuestManager questManager;
private static Timer questStreamingTimer;
private static Queue<string> rareMessageQueue = new Queue<string>();
private static DateTime _lastSent = DateTime.MinValue;
private static readonly Queue<string> _chatQueue = new Queue<string>();
// Managers
private KillTracker _killTracker;
private RareTracker _rareTracker;
private InventoryMonitor _inventoryMonitor;
private CommandRouter _commandRouter;
protected override void Startup()
@ -180,10 +181,12 @@ namespace MosswartMassacre
CoreManager.Current.WorldFilter.CreateObject += OnSpawn;
CoreManager.Current.WorldFilter.CreateObject += OnPortalDetected;
CoreManager.Current.WorldFilter.ReleaseObject += OnDespawn;
// Subscribe to inventory change events for taper tracking
CoreManager.Current.WorldFilter.CreateObject += OnInventoryCreate;
CoreManager.Current.WorldFilter.ReleaseObject += OnInventoryRelease;
CoreManager.Current.WorldFilter.ChangeObject += OnInventoryChange;
// Initialize inventory monitor (taper tracking)
_inventoryMonitor = new InventoryMonitor(this);
_staticInventoryMonitor = _inventoryMonitor;
CoreManager.Current.WorldFilter.CreateObject += _inventoryMonitor.OnInventoryCreate;
CoreManager.Current.WorldFilter.ReleaseObject += _inventoryMonitor.OnInventoryRelease;
CoreManager.Current.WorldFilter.ChangeObject += _inventoryMonitor.OnInventoryChange;
// Initialize VVS view after character login
ViewManager.ViewInit();
@ -262,10 +265,13 @@ namespace MosswartMassacre
CoreManager.Current.WorldFilter.CreateObject -= OnSpawn;
CoreManager.Current.WorldFilter.CreateObject -= OnPortalDetected;
CoreManager.Current.WorldFilter.ReleaseObject -= OnDespawn;
// Unsubscribe from inventory change events
CoreManager.Current.WorldFilter.CreateObject -= OnInventoryCreate;
CoreManager.Current.WorldFilter.ReleaseObject -= OnInventoryRelease;
CoreManager.Current.WorldFilter.ChangeObject -= OnInventoryChange;
// Unsubscribe inventory monitor
if (_inventoryMonitor != null)
{
CoreManager.Current.WorldFilter.CreateObject -= _inventoryMonitor.OnInventoryCreate;
CoreManager.Current.WorldFilter.ReleaseObject -= _inventoryMonitor.OnInventoryRelease;
CoreManager.Current.WorldFilter.ChangeObject -= _inventoryMonitor.OnInventoryChange;
}
// Unsubscribe from server dispatch
CoreManager.Current.EchoFilter.ServerDispatch -= EchoFilter_ServerDispatch;
@ -329,8 +335,7 @@ namespace MosswartMassacre
}
// Clean up taper tracking
trackedTaperContainers.Clear();
lastKnownStackSizes.Clear();
_inventoryMonitor?.Cleanup();
// Clean up Harmony patches
DecalHarmonyClean.Cleanup();
@ -367,8 +372,12 @@ namespace MosswartMassacre
WriteToChat($"[ChestLooter] Initialization failed: {ex.Message}");
}
// Initialize rare tracker
_rareTracker = new RareTracker(this);
_staticRareTracker = _rareTracker;
// Apply the values
RareMetaEnabled = PluginSettings.Instance.RareMetaEnabled;
_rareTracker.RareMetaEnabled = PluginSettings.Instance.RareMetaEnabled;
WebSocketEnabled = PluginSettings.Instance.WebSocketEnabled;
CharTag = PluginSettings.Instance.CharTag;
ViewManager.SetRareMetaToggleState(RareMetaEnabled);
@ -394,7 +403,7 @@ namespace MosswartMassacre
_killTracker.SetTotalDeaths(CoreManager.Current.CharacterFilter.GetCharProperty((int)IntValueKey.NumDeaths));
// Initialize cached Prismatic Taper count
InitializePrismaticTaperCount();
_inventoryMonitor.Initialize();
// Initialize quest manager for always-on quest streaming
try
@ -570,7 +579,7 @@ namespace MosswartMassacre
}
// 2. Apply the values from settings
RareMetaEnabled = PluginSettings.Instance.RareMetaEnabled;
if (_rareTracker != null) _rareTracker.RareMetaEnabled = PluginSettings.Instance.RareMetaEnabled;
WebSocketEnabled = PluginSettings.Instance.WebSocketEnabled;
CharTag = PluginSettings.Instance.CharTag;
@ -607,7 +616,7 @@ namespace MosswartMassacre
_killTracker?.SetTotalDeaths(CoreManager.Current.CharacterFilter.GetCharProperty((int)IntValueKey.NumDeaths));
// 7. Reinitialize cached Prismatic Taper count
InitializePrismaticTaperCount();
_inventoryMonitor?.Initialize();
// 8. Reinitialize quest manager for hot reload
try
@ -659,173 +668,6 @@ namespace MosswartMassacre
WriteToChat("Hot reload initialization completed!");
}
private void InitializePrismaticTaperCount()
{
try
{
lastPrismaticCount = cachedPrismaticCount;
cachedPrismaticCount = Utils.GetItemStackSize("Prismatic Taper");
// Initialize tracking for existing tapers
trackedTaperContainers.Clear();
lastKnownStackSizes.Clear();
foreach (WorldObject wo in CoreManager.Current.WorldFilter.GetInventory())
{
if (wo.Name.Equals("Prismatic Taper", StringComparison.OrdinalIgnoreCase) &&
IsPlayerOwnedContainer(wo.Container))
{
int stackCount = wo.Values(LongValueKey.StackCount, 1);
trackedTaperContainers[wo.Id] = wo.Container;
lastKnownStackSizes[wo.Id] = stackCount;
}
}
}
catch (Exception ex)
{
WriteToChat($"[TAPER] Error initializing count: {ex.Message}");
cachedPrismaticCount = 0;
lastPrismaticCount = 0;
trackedTaperContainers.Clear();
lastKnownStackSizes.Clear();
}
}
private bool IsPlayerOwnedContainer(int containerId)
{
try
{
// Check if it's the player's main inventory
if (containerId == CoreManager.Current.CharacterFilter.Id)
return true;
// Check if it's one of the player's containers (side packs)
// Get the container object to verify it belongs to the player
WorldObject container = CoreManager.Current.WorldFilter[containerId];
if (container != null &&
container.ObjectClass == ObjectClass.Container &&
container.Container == CoreManager.Current.CharacterFilter.Id)
{
return true;
}
return false;
}
catch
{
return false;
}
}
private void OnInventoryCreate(object sender, CreateObjectEventArgs e)
{
try
{
var item = e.New;
if (IsPlayerOwnedContainer(item.Container) &&
item.Name.Equals("Prismatic Taper", StringComparison.OrdinalIgnoreCase))
{
lastPrismaticCount = cachedPrismaticCount;
int stackCount = item.Values(LongValueKey.StackCount, 1);
cachedPrismaticCount += stackCount;
int delta = cachedPrismaticCount - lastPrismaticCount;
// Initialize tracking for this new taper
trackedTaperContainers[item.Id] = item.Container;
lastKnownStackSizes[item.Id] = stackCount;
}
}
catch (Exception ex)
{
WriteToChat($"[TAPER] Error in OnInventoryCreate: {ex.Message}");
}
}
private void OnInventoryRelease(object sender, ReleaseObjectEventArgs e)
{
try
{
var item = e.Released;
if (item.Name.Equals("Prismatic Taper", StringComparison.OrdinalIgnoreCase))
{
// Check where this taper WAS before being released (not where it's going)
if (trackedTaperContainers.TryGetValue(item.Id, out int previousContainer))
{
if (IsPlayerOwnedContainer(previousContainer))
{
// This taper was in our inventory and is now being released
lastPrismaticCount = cachedPrismaticCount;
int stackCount = item.Values(LongValueKey.StackCount, 1);
cachedPrismaticCount -= stackCount;
}
// Clean up tracking
trackedTaperContainers.Remove(item.Id);
lastKnownStackSizes.Remove(item.Id);
}
else
{
// Fallback: recalculate total count when untracked taper is released
lastPrismaticCount = cachedPrismaticCount;
cachedPrismaticCount = Utils.GetItemStackSize("Prismatic Taper");
}
}
}
catch (Exception ex)
{
WriteToChat($"[TAPER] Error in OnInventoryRelease: {ex.Message}");
}
}
private void OnInventoryChange(object sender, ChangeObjectEventArgs e)
{
try
{
var item = e.Changed;
if (item.Name.Equals("Prismatic Taper", StringComparison.OrdinalIgnoreCase))
{
bool isInPlayerContainer = IsPlayerOwnedContainer(item.Container);
// Track container location for release detection
if (isInPlayerContainer)
{
bool wasAlreadyTracked = trackedTaperContainers.ContainsKey(item.Id);
trackedTaperContainers[item.Id] = item.Container;
// Handle stack size changes with pure delta math
int currentStack = item.Values(LongValueKey.StackCount, 1);
// Check if this is a pickup from ground (item not previously tracked)
if (!wasAlreadyTracked)
{
// This is likely a pickup from ground - increment count
lastPrismaticCount = cachedPrismaticCount;
cachedPrismaticCount += currentStack;
}
else if (lastKnownStackSizes.TryGetValue(item.Id, out int previousStack))
{
int stackDelta = currentStack - previousStack;
if (stackDelta != 0)
{
lastPrismaticCount = cachedPrismaticCount;
cachedPrismaticCount += stackDelta;
}
}
lastKnownStackSizes[item.Id] = currentStack;
}
// Item is no longer in player containers
// DON'T clean up tracking here - let OnInventoryRelease handle cleanup
// This ensures tracking data is available for the Release event
}
}
catch (Exception ex)
{
WriteToChat($"[TAPER] Error in OnInventoryChange: {ex.Message}");
}
}
private async void OnSpawn(object sender, CreateObjectEventArgs e)
{
@ -1039,26 +881,16 @@ namespace MosswartMassacre
_killTracker.CheckForKill(e.Text);
if (IsRareDiscoveryMessage(e.Text, out string rareText))
if (_rareTracker != null && _rareTracker.CheckForRare(e.Text, out string rareText))
{
_killTracker.RareCount++;
rareCount = _killTracker.RareCount; // sync static for now
ViewManager.UpdateRareCount(_killTracker.RareCount);
if (RareMetaEnabled)
{
Decal_DispatchOnChatCommand("/vt setmetastate loot_rare");
}
DelayedCommandManager.AddDelayedCommand($"/a {rareText}", 3000);
// Fire and forget: we don't await, since sending is not critical and we don't want to block.
_ = WebSocket.SendRareAsync(rareText);
_killTracker.RareCount = _rareTracker.RareCount;
ViewManager.UpdateRareCount(_rareTracker.RareCount);
}
if (e.Color == 18 && e.Text.EndsWith("!report\""))
{
TimeSpan elapsed = DateTime.Now - _killTracker.StatsStartTime;
string reportMessage = $"Total Kills: {_killTracker.TotalKills}, Kills per Hour: {_killTracker.KillsPerHour:F2}, Elapsed Time: {elapsed:dd\\.hh\\:mm\\:ss}, Rares Found: {_killTracker.RareCount}";
string reportMessage = $"Total Kills: {_killTracker.TotalKills}, Kills per Hour: {_killTracker.KillsPerHour:F2}, Elapsed Time: {elapsed:dd\\.hh\\:mm\\:ss}, Rares Found: {_rareTracker?.RareCount ?? 0}";
WriteToChat($"[Mosswart Massacre] Reporting to allegiance: {reportMessage}");
MyHost.Actions.InvokeChatParser($"/a {reportMessage}");
}
@ -1187,22 +1019,6 @@ namespace MosswartMassacre
}
}
private bool IsRareDiscoveryMessage(string text, out string rareTextOnly)
{
rareTextOnly = null;
// Match pattern: "<name> has discovered the <something>!"
string pattern = @"^(?<name>['A-Za-z ]+)\shas discovered the (?<item>.*?)!$";
Match match = Regex.Match(text, pattern);
if (match.Success && match.Groups["name"].Value == CoreManager.Current.CharacterFilter.Name)
{
rareTextOnly = match.Groups["item"].Value; // just "Ancient Pickle"
return true;
}
return false;
}
public static void WriteToChat(string message)
{
try
@ -1237,12 +1053,13 @@ namespace MosswartMassacre
public static void RestartStats()
{
_staticKillTracker?.RestartStats();
ViewManager.UpdateRareCount(_staticKillTracker?.RareCount ?? 0);
if (_staticRareTracker != null)
_staticRareTracker.RareCount = 0;
ViewManager.UpdateRareCount(0);
}
public static void ToggleRareMeta()
{
PluginSettings.Instance.RareMetaEnabled = !PluginSettings.Instance.RareMetaEnabled;
RareMetaEnabled = PluginSettings.Instance.RareMetaEnabled;
_staticRareTracker?.ToggleRareMeta();
ViewManager.SetRareMetaToggleState(RareMetaEnabled);
}
@ -1307,7 +1124,7 @@ namespace MosswartMassacre
_commandRouter.Register("report", args =>
{
TimeSpan elapsed = DateTime.Now - _killTracker.StatsStartTime;
string reportMessage = $"Total Kills: {_killTracker.TotalKills}, Kills per Hour: {_killTracker.KillsPerHour:F2}, Elapsed Time: {elapsed:dd\\.hh\\:mm\\:ss}, Rares Found: {_killTracker.RareCount}, Session Deaths: {_killTracker.SessionDeaths}, Total Deaths: {_killTracker.TotalDeaths}";
string reportMessage = $"Total Kills: {_killTracker.TotalKills}, Kills per Hour: {_killTracker.KillsPerHour:F2}, Elapsed Time: {elapsed:dd\\.hh\\:mm\\:ss}, Rares Found: {_rareTracker?.RareCount ?? 0}, Session Deaths: {_killTracker.SessionDeaths}, Total Deaths: {_killTracker.TotalDeaths}";
WriteToChat(reportMessage);
}, "Show current stats");
@ -1617,21 +1434,21 @@ namespace MosswartMassacre
try
{
WriteToChat("=== Cached Taper Tracking Test ===");
WriteToChat($"Cached Count: {cachedPrismaticCount}");
WriteToChat($"Last Count: {lastPrismaticCount}");
WriteToChat($"Cached Count: {_inventoryMonitor.CachedPrismaticCount}");
WriteToChat($"Last Count: {_inventoryMonitor.LastPrismaticCount}");
int utilsCount = Utils.GetItemStackSize("Prismatic Taper");
WriteToChat($"Utils Count: {utilsCount}");
if (cachedPrismaticCount == utilsCount)
if (_inventoryMonitor.CachedPrismaticCount == utilsCount)
{
WriteToChat("[OK] Cached count matches Utils count");
}
else
{
WriteToChat($"[WARNING] Count mismatch! Cached: {cachedPrismaticCount}, Utils: {utilsCount}");
WriteToChat($"[WARNING] Count mismatch! Cached: {_inventoryMonitor.CachedPrismaticCount}, Utils: {utilsCount}");
WriteToChat("Refreshing cached count...");
InitializePrismaticTaperCount();
_inventoryMonitor.Initialize();
}
WriteToChat("=== Container Analysis ===");
@ -1656,8 +1473,8 @@ namespace MosswartMassacre
WriteToChat($"Total: {mainPackCount + sidePackCount}");
WriteToChat("=== Event System Status ===");
WriteToChat($"Tracking {trackedTaperContainers.Count} taper stacks for delta detection");
WriteToChat($"Known stack sizes: {lastKnownStackSizes.Count} items");
WriteToChat($"Tracking {_inventoryMonitor.TrackedTaperCount} taper stacks for delta detection");
WriteToChat($"Known stack sizes: {_inventoryMonitor.KnownStackSizesCount} items");
WriteToChat("Pure delta tracking - NO expensive inventory scans during events!");
WriteToChat("Now tracks: consumption, drops, trades, container moves");
WriteToChat("Try moving tapers between containers and casting spells!");

View file

@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Decal.Adapter;
namespace MosswartMassacre
{
/// <summary>
/// Tracks rare item discoveries, handles rare meta state toggles,
/// and sends rare notifications via WebSocket.
/// </summary>
internal class RareTracker
{
private readonly IPluginLogger _logger;
private readonly string _characterName;
internal int RareCount { get; set; }
internal bool RareMetaEnabled { get; set; } = true;
internal RareTracker(IPluginLogger logger)
{
_logger = logger;
_characterName = CoreManager.Current.CharacterFilter.Name;
}
/// <summary>
/// Check if the chat text is a rare discovery by this character.
/// If so, increments count, triggers meta switch, allegiance announce, and WebSocket notification.
/// Returns true if a rare was found.
/// </summary>
internal bool CheckForRare(string text, out string rareText)
{
if (IsRareDiscoveryMessage(text, out rareText))
{
RareCount++;
if (RareMetaEnabled)
{
PluginCore.Decal_DispatchOnChatCommand("/vt setmetastate loot_rare");
}
DelayedCommandManager.AddDelayedCommand($"/a {rareText}", 3000);
_ = WebSocket.SendRareAsync(rareText);
return true;
}
return false;
}
internal void ToggleRareMeta()
{
PluginSettings.Instance.RareMetaEnabled = !PluginSettings.Instance.RareMetaEnabled;
RareMetaEnabled = PluginSettings.Instance.RareMetaEnabled;
}
private bool IsRareDiscoveryMessage(string text, out string rareTextOnly)
{
rareTextOnly = null;
string pattern = @"^(?<name>['A-Za-z ]+)\shas discovered the (?<item>.*?)!$";
Match match = Regex.Match(text, pattern);
if (match.Success && match.Groups["name"].Value == _characterName)
{
rareTextOnly = match.Groups["item"].Value;
return true;
}
return false;
}
}
}