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:
parent
366cca8cb6
commit
c90e888d32
4 changed files with 306 additions and 232 deletions
184
MosswartMassacre/InventoryMonitor.cs
Normal file
184
MosswartMassacre/InventoryMonitor.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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" />
|
||||
|
|
|
|||
|
|
@ -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!");
|
||||
|
|
|
|||
71
MosswartMassacre/RareTracker.cs
Normal file
71
MosswartMassacre/RareTracker.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue