diff --git a/MosswartMassacre/CharacterStats.cs b/MosswartMassacre/CharacterStats.cs index 4a7248c..4c8b42e 100644 --- a/MosswartMassacre/CharacterStats.cs +++ b/MosswartMassacre/CharacterStats.cs @@ -134,9 +134,9 @@ namespace MosswartMassacre long key = tmpStruct.Value("key"); long value = tmpStruct.Value("value"); - if (key == 6) // AvailableLuminance + if (key == Constants.AvailableLuminanceKey) luminanceEarned = value; - else if (key == 7) // MaximumLuminance + else if (key == Constants.MaximumLuminanceKey) luminanceTotal = value; } } @@ -162,9 +162,9 @@ namespace MosswartMassacre int key = BitConverter.ToInt32(raw, 5); long value = BitConverter.ToInt64(raw, 9); - if (key == 6) // AvailableLuminance + if (key == Constants.AvailableLuminanceKey) luminanceEarned = value; - else if (key == 7) // MaximumLuminance + else if (key == Constants.MaximumLuminanceKey) luminanceTotal = value; } catch (Exception ex) diff --git a/MosswartMassacre/CommandRouter.cs b/MosswartMassacre/CommandRouter.cs new file mode 100644 index 0000000..edba553 --- /dev/null +++ b/MosswartMassacre/CommandRouter.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace MosswartMassacre +{ + /// + /// Dictionary-based /mm command dispatcher. Commands are registered with descriptions + /// and routed by name lookup instead of a giant switch statement. + /// + internal class CommandRouter + { + private readonly Dictionary handler, string description)> _commands + = new Dictionary, string)>(StringComparer.OrdinalIgnoreCase); + + /// + /// Register a command with its handler and help description. + /// + internal void Register(string name, Action handler, string description) + { + _commands[name] = (handler, description); + } + + /// + /// Dispatch a raw /mm command string. Returns false if the command was not found. + /// + internal bool Dispatch(string rawText) + { + string[] args = rawText.Substring(3).Trim().Split(' '); + + if (args.Length == 0 || string.IsNullOrEmpty(args[0])) + { + PluginCore.WriteToChat("Usage: /mm . Try /mm help"); + return true; + } + + string subCommand = args[0].ToLower(); + + if (subCommand == "help") + { + PrintHelp(); + return true; + } + + if (_commands.TryGetValue(subCommand, out var entry)) + { + entry.handler(args); + return true; + } + + PluginCore.WriteToChat($"Unknown /mm command: {subCommand}. Try /mm help"); + return false; + } + + private void PrintHelp() + { + PluginCore.WriteToChat("Mosswart Massacre Commands:"); + foreach (var kvp in _commands) + { + if (!string.IsNullOrEmpty(kvp.Value.description)) + { + PluginCore.WriteToChat($"/mm {kvp.Key,-18} - {kvp.Value.description}"); + } + } + } + } +} diff --git a/MosswartMassacre/Constants.cs b/MosswartMassacre/Constants.cs new file mode 100644 index 0000000..8433dc2 --- /dev/null +++ b/MosswartMassacre/Constants.cs @@ -0,0 +1,30 @@ +namespace MosswartMassacre +{ + /// + /// Centralized constants for timer intervals, message type IDs, and property keys. + /// + internal static class Constants + { + // Timer intervals (milliseconds) + internal const int StatsUpdateIntervalMs = 1000; + internal const int VitalsUpdateIntervalMs = 5000; + internal const int CommandProcessIntervalMs = 10; + internal const int QuestStreamingIntervalMs = 30000; + internal const int CharacterStatsIntervalMs = 600000; // 10 minutes + internal const int LoginDelayMs = 5000; + + // Network message types + internal const int GameEventMessageType = 0xF7B0; + internal const int PrivateUpdatePropertyInt64 = 0x02CF; + + // Game event IDs (sub-events within 0xF7B0) + internal const int AllegianceInfoEvent = 0x0020; + internal const int LoginCharacterEvent = 0x0013; + internal const int TitlesListEvent = 0x0029; + internal const int SetTitleEvent = 0x002b; + + // Int64 property keys + internal const int AvailableLuminanceKey = 6; + internal const int MaximumLuminanceKey = 7; + } +} diff --git a/MosswartMassacre/MosswartMassacre.csproj b/MosswartMassacre/MosswartMassacre.csproj index 2e812f9..8dad1ed 100644 --- a/MosswartMassacre/MosswartMassacre.csproj +++ b/MosswartMassacre/MosswartMassacre.csproj @@ -304,6 +304,8 @@ Shared\VCS_Connector.cs + + diff --git a/MosswartMassacre/PluginCore.cs b/MosswartMassacre/PluginCore.cs index 522111f..602bf30 100644 --- a/MosswartMassacre/PluginCore.cs +++ b/MosswartMassacre/PluginCore.cs @@ -127,6 +127,9 @@ namespace MosswartMassacre private static DateTime _lastSent = DateTime.MinValue; private static readonly Queue _chatQueue = new Queue(); + // Command routing + private CommandRouter _commandRouter; + protected override void Startup() { try @@ -184,18 +187,18 @@ namespace MosswartMassacre ViewManager.ViewInit(); // Initialize the timer - updateTimer = new Timer(1000); // Update every second + updateTimer = new Timer(Constants.StatsUpdateIntervalMs); updateTimer.Elapsed += UpdateStats; updateTimer.Start(); // Initialize vitals streaming timer - vitalsTimer = new Timer(5000); // Send vitals every 5 seconds + vitalsTimer = new Timer(Constants.VitalsUpdateIntervalMs); vitalsTimer.Elapsed += SendVitalsUpdate; vitalsTimer.Start(); // Initialize command processing timer (Windows Forms timer for main thread) commandTimer = new System.Windows.Forms.Timer(); - commandTimer.Interval = 10; // Process commands every 10ms + commandTimer.Interval = Constants.CommandProcessIntervalMs; commandTimer.Tick += ProcessPendingCommands; commandTimer.Start(); @@ -220,6 +223,10 @@ namespace MosswartMassacre // Initialize navigation visualization system navVisualization = new NavVisualization(); + // Initialize command router + _commandRouter = new CommandRouter(); + RegisterCommands(); + // Note: ChestLooter is initialized in LoginComplete after PluginSettings.Initialize() // Note: DECAL Harmony patches will be initialized in LoginComplete event @@ -398,11 +405,11 @@ namespace MosswartMassacre Views.FlagTrackerView.RefreshQuestData(); // Initialize quest streaming timer (30 seconds) - questStreamingTimer = new Timer(30000); + questStreamingTimer = new Timer(Constants.QuestStreamingIntervalMs); questStreamingTimer.Elapsed += OnQuestStreamingUpdate; questStreamingTimer.AutoReset = true; questStreamingTimer.Start(); - + WriteToChat("[OK] Quest streaming initialized with full data refresh"); } catch (Exception ex) @@ -416,13 +423,13 @@ namespace MosswartMassacre try { // Start 10-minute character stats timer - characterStatsTimer = new Timer(600000); // 10 minutes + characterStatsTimer = new Timer(Constants.CharacterStatsIntervalMs); characterStatsTimer.Elapsed += OnCharacterStatsUpdate; characterStatsTimer.AutoReset = true; characterStatsTimer.Start(); // Send initial stats after 5-second delay (let CharacterFilter populate) - var initialDelay = new Timer(5000); + var initialDelay = new Timer(Constants.LoginDelayMs); initialDelay.AutoReset = false; initialDelay.Elapsed += (s, args) => { @@ -638,11 +645,11 @@ namespace MosswartMassacre } // Create new timer - questStreamingTimer = new Timer(30000); // 30 seconds + questStreamingTimer = new Timer(Constants.QuestStreamingIntervalMs); questStreamingTimer.Elapsed += OnQuestStreamingUpdate; questStreamingTimer.AutoReset = true; questStreamingTimer.Start(); - + WriteToChat("[OK] Quest streaming timer reinitialized (30s interval)"); } catch (Exception ex) @@ -1172,28 +1179,28 @@ namespace MosswartMassacre { try { - if (e.Message.Type == 0xF7B0) // Game Event + if (e.Message.Type == Constants.GameEventMessageType) { int eventId = (int)e.Message["event"]; - if (eventId == 0x0020) // Allegiance info + if (eventId == Constants.AllegianceInfoEvent) { CharacterStats.ProcessAllegianceInfoMessage(e); } - else if (eventId == 0x0013) // Login Character (properties) + else if (eventId == Constants.LoginCharacterEvent) { CharacterStats.ProcessCharacterPropertyData(e); } - else if (eventId == 0x0029) // Titles list + else if (eventId == Constants.TitlesListEvent) { CharacterStats.ProcessTitlesMessage(e); } - else if (eventId == 0x002b) // Set title + else if (eventId == Constants.SetTitleEvent) { CharacterStats.ProcessSetTitleMessage(e); } } - else if (e.Message.Type == 0x02CF) // PrivateUpdatePropertyInt64 (runtime luminance changes) + else if (e.Message.Type == Constants.PrivateUpdatePropertyInt64) { CharacterStats.ProcessPropertyInt64Update(e); } @@ -1353,568 +1360,531 @@ namespace MosswartMassacre } private void HandleMmCommand(string text) { - // Remove the /mm prefix and trim extra whitespace test - string[] args = text.Substring(3).Trim().Split(' '); + _commandRouter.Dispatch(text); + } - if (args.Length == 0 || string.IsNullOrEmpty(args[0])) + private void RegisterCommands() + { + _commandRouter.Register("ws", args => { - WriteToChat("Usage: /mm . Try /mm help"); - return; - } - - string subCommand = args[0].ToLower(); - - switch (subCommand) - { - case "ws": - if (args.Length > 1) + if (args.Length > 1) + { + if (args[1].Equals("enable", StringComparison.OrdinalIgnoreCase)) { - if (args[1].Equals("enable", StringComparison.OrdinalIgnoreCase)) - { - WebSocketEnabled = true; - WebSocket.Start(); - PluginSettings.Instance.WebSocketEnabled = true; - WriteToChat("WS streaming ENABLED."); - } - else if (args[1].Equals("disable", StringComparison.OrdinalIgnoreCase)) - { - WebSocketEnabled = false; - WebSocket.Stop(); - PluginSettings.Instance.WebSocketEnabled = false; - WriteToChat("WS streaming DISABLED."); - } - else - { - WriteToChat("Usage: /mm ws "); - } + WebSocketEnabled = true; + WebSocket.Start(); + PluginSettings.Instance.WebSocketEnabled = true; + WriteToChat("WS streaming ENABLED."); + } + else if (args[1].Equals("disable", StringComparison.OrdinalIgnoreCase)) + { + WebSocketEnabled = false; + WebSocket.Stop(); + PluginSettings.Instance.WebSocketEnabled = false; + WriteToChat("WS streaming DISABLED."); } - else { WriteToChat("Usage: /mm ws "); } - break; - case "help": - WriteToChat("Mosswart Massacre Commands:"); - WriteToChat("/mm report - Show current stats"); - WriteToChat("/mm loc - Show current location"); - WriteToChat("/mm ws - Websocket streaming enable|disable"); - WriteToChat("/mm reset - Reset all counters"); - WriteToChat("/mm meta - Toggle rare meta state!!"); - WriteToChat("/mm getmetastate - Gets the current metastate"); - WriteToChat("/mm setchest - Set chest name for looter"); - WriteToChat("/mm setkey - Set key name for looter"); - WriteToChat("/mm lootchest - Start chest looting"); - WriteToChat("/mm stoploot - Stop chest looting"); - WriteToChat("/mm nextwp - Advance VTank to next waypoint"); - WriteToChat("/mm decalstatus - Check Harmony patch status (UtilityBelt version)"); - WriteToChat("/mm decaldebug - Enable/disable plugin message debug output + WebSocket streaming"); - WriteToChat("/mm harmonyraw - Show raw intercepted messages (debug output)"); - WriteToChat("/mm testprismatic - Test Prismatic Taper detection and icon lookup"); - WriteToChat("/mm deathstats - Show current death tracking statistics"); - WriteToChat("/mm testtaper - Test cached Prismatic Taper tracking"); - WriteToChat("/mm debugtaper - Show detailed taper tracking debug info"); - WriteToChat("/mm gui - Manually initialize/reinitialize GUI!!!"); - WriteToChat("/mm checkforupdate - Check for plugin updates"); - WriteToChat("/mm update - Download and install update (if available)"); - WriteToChat("/mm debugupdate - Debug update UI controls"); - WriteToChat("/mm sendinventory - Force inventory upload with ID requests"); - WriteToChat("/mm refreshquests - Force quest data refresh for Flag Tracker"); - WriteToChat("/mm queststatus - Show quest streaming status and diagnostics"); - WriteToChat("/mm verbose - Toggle verbose debug logging"); - break; - case "report": - TimeSpan elapsed = DateTime.Now - statsStartTime; - string reportMessage = $"Total Kills: {totalKills}, Kills per Hour: {killsPerHour:F2}, Elapsed Time: {elapsed:dd\\.hh\\:mm\\:ss}, Rares Found: {rareCount}, Session Deaths: {sessionDeaths}, Total Deaths: {totalDeaths}"; - WriteToChat(reportMessage); - break; - case "getmetastate": - string metaState = VtankControl.VtGetMetaState(); - WriteToChat(metaState); - break; + } + else + { + WriteToChat("Usage: /mm ws "); + } + }, "Websocket streaming enable|disable"); - case "loc": - Coordinates here = Coordinates.Me; - var pos = Utils.GetPlayerPosition(); - WriteToChat($"Location: {here} (X={pos.X:F1}, Y={pos.Y:F1}, Z={pos.Z:F1})"); - break; - case "reset": - RestartStats(); - break; - case "meta": - RareMetaEnabled = !RareMetaEnabled; - WriteToChat($"Rare meta state is now {(RareMetaEnabled ? "ON" : "OFF")}"); - ViewManager.SetRareMetaToggleState(RareMetaEnabled); // <-- sync the UI - break; - case "nextwp": - double result = VtankControl.VtAdvanceWaypoint(); - if (result == 1) - { - WriteToChat("Advanced VTank to next waypoint."); - } - else - { - WriteToChat("Failed to advance VTank waypoint. Is VTank running?"); - } - break; + _commandRouter.Register("report", args => + { + TimeSpan elapsed = DateTime.Now - statsStartTime; + string reportMessage = $"Total Kills: {totalKills}, Kills per Hour: {killsPerHour:F2}, Elapsed Time: {elapsed:dd\\.hh\\:mm\\:ss}, Rares Found: {rareCount}, Session Deaths: {sessionDeaths}, Total Deaths: {totalDeaths}"; + WriteToChat(reportMessage); + }, "Show current stats"); - case "setchest": - if (args.Length < 2) - { - WriteToChat("[ChestLooter] Usage: /mm setchest "); - return; - } - string chestName = string.Join(" ", args.Skip(1)); - if (chestLooter != null) - { - chestLooter.SetChestName(chestName); - if (PluginSettings.Instance?.ChestLooterSettings != null) - { - PluginSettings.Instance.ChestLooterSettings.ChestName = chestName; - PluginSettings.Save(); - } - Views.VVSTabbedMainView.RefreshChestLooterUI(); - } - break; + _commandRouter.Register("getmetastate", args => + { + string metaState = VtankControl.VtGetMetaState(); + WriteToChat(metaState); + }, "Gets the current metastate"); - case "setkey": - if (args.Length < 2) - { - WriteToChat("[ChestLooter] Usage: /mm setkey "); - return; - } - string keyName = string.Join(" ", args.Skip(1)); - if (chestLooter != null) - { - chestLooter.SetKeyName(keyName); - if (PluginSettings.Instance?.ChestLooterSettings != null) - { - PluginSettings.Instance.ChestLooterSettings.KeyName = keyName; - PluginSettings.Save(); - } - Views.VVSTabbedMainView.RefreshChestLooterUI(); - } - break; + _commandRouter.Register("loc", args => + { + Coordinates here = Coordinates.Me; + var pos = Utils.GetPlayerPosition(); + WriteToChat($"Location: {here} (X={pos.X:F1}, Y={pos.Y:F1}, Z={pos.Z:F1})"); + }, "Show current location"); - case "lootchest": - if (chestLooter != null) - { - if (!chestLooter.StartByName()) - { - WriteToChat("[ChestLooter] Failed to start. Check chest/key names are set."); - } - } - else - { - WriteToChat("[ChestLooter] Chest looter not initialized"); - } - break; + _commandRouter.Register("reset", args => + { + RestartStats(); + }, "Reset all counters"); - case "stoploot": - if (chestLooter != null) - { - chestLooter.Stop(); - } - else - { - WriteToChat("[ChestLooter] Chest looter not initialized"); - } - break; + _commandRouter.Register("meta", args => + { + RareMetaEnabled = !RareMetaEnabled; + WriteToChat($"Rare meta state is now {(RareMetaEnabled ? "ON" : "OFF")}"); + ViewManager.SetRareMetaToggleState(RareMetaEnabled); + }, "Toggle rare meta state"); - case "vtanktest": + _commandRouter.Register("nextwp", args => + { + double result = VtankControl.VtAdvanceWaypoint(); + if (result == 1) + WriteToChat("Advanced VTank to next waypoint."); + else + WriteToChat("Failed to advance VTank waypoint. Is VTank running?"); + }, "Advance VTank to next waypoint"); + + _commandRouter.Register("setchest", args => + { + if (args.Length < 2) + { + WriteToChat("[ChestLooter] Usage: /mm setchest "); + return; + } + string chestName = string.Join(" ", args.Skip(1)); + if (chestLooter != null) + { + chestLooter.SetChestName(chestName); + if (PluginSettings.Instance?.ChestLooterSettings != null) + { + PluginSettings.Instance.ChestLooterSettings.ChestName = chestName; + PluginSettings.Save(); + } + Views.VVSTabbedMainView.RefreshChestLooterUI(); + } + }, "Set chest name for looter"); + + _commandRouter.Register("setkey", args => + { + if (args.Length < 2) + { + WriteToChat("[ChestLooter] Usage: /mm setkey "); + return; + } + string keyName = string.Join(" ", args.Skip(1)); + if (chestLooter != null) + { + chestLooter.SetKeyName(keyName); + if (PluginSettings.Instance?.ChestLooterSettings != null) + { + PluginSettings.Instance.ChestLooterSettings.KeyName = keyName; + PluginSettings.Save(); + } + Views.VVSTabbedMainView.RefreshChestLooterUI(); + } + }, "Set key name for looter"); + + _commandRouter.Register("lootchest", args => + { + if (chestLooter != null) + { + if (!chestLooter.StartByName()) + WriteToChat("[ChestLooter] Failed to start. Check chest/key names are set."); + } + else + { + WriteToChat("[ChestLooter] Chest looter not initialized"); + } + }, "Start chest looting"); + + _commandRouter.Register("stoploot", args => + { + if (chestLooter != null) + chestLooter.Stop(); + else + WriteToChat("[ChestLooter] Chest looter not initialized"); + }, "Stop chest looting"); + + _commandRouter.Register("vtanktest", args => + { + try + { + WriteToChat("Testing VTank interface..."); + WriteToChat($"VTank Instance: {(vTank.Instance != null ? "Found" : "NULL")}"); + WriteToChat($"VTank Type: {vTank.Instance?.GetType()?.Name ?? "NULL"}"); + WriteToChat($"NavCurrent: {vTank.Instance?.NavCurrent ?? -1}"); + WriteToChat($"NavNumPoints: {vTank.Instance?.NavNumPoints ?? -1}"); + WriteToChat($"NavType: {vTank.Instance?.NavType}"); + WriteToChat($"MacroEnabled: {vTank.Instance?.MacroEnabled}"); + } + catch (Exception ex) + { + WriteToChat($"VTank test error: {ex.Message}"); + } + }, "Test VTank interface"); + + _commandRouter.Register("decalstatus", args => + { + try + { + WriteToChat("=== Harmony Patch Status (UtilityBelt Pattern) ==="); + WriteToChat($"Patches Active: {DecalHarmonyClean.IsActive()}"); + WriteToChat($"Messages Intercepted: {DecalHarmonyClean.GetMessagesIntercepted()}"); + WriteToChat($"WebSocket Streaming: {(AggressiveChatStreamingEnabled && WebSocketEnabled ? "ACTIVE" : "INACTIVE")}"); + + WriteToChat("=== Harmony Version Status ==="); try { - WriteToChat("Testing VTank interface..."); - WriteToChat($"VTank Instance: {(vTank.Instance != null ? "Found" : "NULL")}"); - WriteToChat($"VTank Type: {vTank.Instance?.GetType()?.Name ?? "NULL"}"); - WriteToChat($"NavCurrent: {vTank.Instance?.NavCurrent ?? -1}"); - WriteToChat($"NavNumPoints: {vTank.Instance?.NavNumPoints ?? -1}"); - WriteToChat($"NavType: {vTank.Instance?.NavType}"); - WriteToChat($"MacroEnabled: {vTank.Instance?.MacroEnabled}"); + var harmonyTest = Harmony.HarmonyInstance.Create("test.version.check"); + WriteToChat($"[OK] Harmony Available (ID: {harmonyTest.Id})"); + var harmonyAssembly = typeof(Harmony.HarmonyInstance).Assembly; + WriteToChat($"[OK] Harmony Version: {harmonyAssembly.GetName().Version}"); + WriteToChat($"[OK] Harmony Location: {harmonyAssembly.Location}"); } - catch (Exception ex) + catch (Exception harmonyEx) { - WriteToChat($"VTank test error: {ex.Message}"); + WriteToChat($"[FAIL] Harmony Test Failed: {harmonyEx.Message}"); } - break; + } + catch (Exception ex) + { + WriteToChat($"Status check error: {ex.Message}"); + } + }, "Check Harmony patch status"); - case "decalstatus": - try + _commandRouter.Register("decaldebug", args => + { + if (args.Length > 1) + { + if (args[1].Equals("enable", StringComparison.OrdinalIgnoreCase)) { - WriteToChat("=== Harmony Patch Status (UtilityBelt Pattern) ==="); - WriteToChat($"Patches Active: {DecalHarmonyClean.IsActive()}"); - WriteToChat($"Messages Intercepted: {DecalHarmonyClean.GetMessagesIntercepted()}"); - WriteToChat($"WebSocket Streaming: {(AggressiveChatStreamingEnabled && WebSocketEnabled ? "ACTIVE" : "INACTIVE")}"); - - // Test Harmony availability - WriteToChat("=== Harmony Version Status ==="); - try - { - var harmonyTest = Harmony.HarmonyInstance.Create("test.version.check"); - WriteToChat($"[OK] Harmony Available (ID: {harmonyTest.Id})"); - - // Check Harmony assembly version - var harmonyAssembly = typeof(Harmony.HarmonyInstance).Assembly; - WriteToChat($"[OK] Harmony Version: {harmonyAssembly.GetName().Version}"); - WriteToChat($"[OK] Harmony Location: {harmonyAssembly.Location}"); - } - catch (Exception harmonyEx) - { - WriteToChat($"[FAIL] Harmony Test Failed: {harmonyEx.Message}"); - } + AggressiveChatStreamingEnabled = true; + WriteToChat("[OK] DECAL debug streaming ENABLED - will show captured messages + stream via WebSocket"); } - catch (Exception ex) + else if (args[1].Equals("disable", StringComparison.OrdinalIgnoreCase)) { - WriteToChat($"Status check error: {ex.Message}"); - } - break; - - case "decaldebug": - if (args.Length > 1) - { - if (args[1].Equals("enable", StringComparison.OrdinalIgnoreCase)) - { - AggressiveChatStreamingEnabled = true; - WriteToChat("[OK] DECAL debug streaming ENABLED - will show captured messages + stream via WebSocket"); - } - else if (args[1].Equals("disable", StringComparison.OrdinalIgnoreCase)) - { - AggressiveChatStreamingEnabled = false; - WriteToChat("[FAIL] DECAL debug streaming DISABLED - WebSocket streaming also disabled"); - } - else - { - WriteToChat("Usage: /mm decaldebug "); - } + AggressiveChatStreamingEnabled = false; + WriteToChat("[FAIL] DECAL debug streaming DISABLED - WebSocket streaming also disabled"); } else { WriteToChat("Usage: /mm decaldebug "); } - break; + } + else + { + WriteToChat("Usage: /mm decaldebug "); + } + }, "Enable/disable plugin message debug output"); + _commandRouter.Register("harmonyraw", args => { }, ""); - case "harmonyraw": - // Debug functionality removed - break; + _commandRouter.Register("gui", args => + { + try + { + WriteToChat("Attempting to manually initialize GUI..."); + ViewManager.ViewDestroy(); + ViewManager.ViewInit(); + WriteToChat("GUI initialization attempt completed."); + } + catch (Exception ex) + { + WriteToChat($"GUI initialization error: {ex.Message}"); + } + }, "Manually initialize/reinitialize GUI"); - case "initgui": - case "gui": - try + _commandRouter.Register("initgui", args => + { + try + { + WriteToChat("Attempting to manually initialize GUI..."); + ViewManager.ViewDestroy(); + ViewManager.ViewInit(); + WriteToChat("GUI initialization attempt completed."); + } + catch (Exception ex) + { + WriteToChat($"GUI initialization error: {ex.Message}"); + } + }, ""); + + _commandRouter.Register("testprismatic", args => + { + try + { + WriteToChat("=== FULL INVENTORY DUMP ==="); + var worldFilter = CoreManager.Current.WorldFilter; + var playerInv = CoreManager.Current.CharacterFilter.Id; + + WriteToChat("Listing ALL items in your main inventory:"); + int itemNum = 1; + + foreach (WorldObject item in worldFilter.GetByContainer(playerInv)) { - WriteToChat("Attempting to manually initialize GUI..."); - ViewManager.ViewDestroy(); // Clean up any existing view - ViewManager.ViewInit(); // Reinitialize - WriteToChat("GUI initialization attempt completed."); - } - catch (Exception ex) - { - WriteToChat($"GUI initialization error: {ex.Message}"); - } - break; - - case "testprismatic": - try - { - WriteToChat("=== FULL INVENTORY DUMP ==="); - var worldFilter = CoreManager.Current.WorldFilter; - var playerInv = CoreManager.Current.CharacterFilter.Id; - - WriteToChat("Listing ALL items in your main inventory:"); - int itemNum = 1; - - foreach (WorldObject item in worldFilter.GetByContainer(playerInv)) + if (!string.IsNullOrEmpty(item.Name)) { - if (!string.IsNullOrEmpty(item.Name)) + int stackCount = item.Values(LongValueKey.StackCount, 0); + WriteToChat($"{itemNum:D2}: '{item.Name}' (count: {stackCount}, icon: 0x{item.Icon:X}, class: {item.ObjectClass})"); + itemNum++; + + string nameLower = item.Name.ToLower(); + if (nameLower.Contains("taper") || nameLower.Contains("prismatic") || + nameLower.Contains("prism") || nameLower.Contains("component")) { - int stackCount = item.Values(LongValueKey.StackCount, 0); - WriteToChat($"{itemNum:D2}: '{item.Name}' (count: {stackCount}, icon: 0x{item.Icon:X}, class: {item.ObjectClass})"); - itemNum++; - - // Highlight anything that might be a taper - string nameLower = item.Name.ToLower(); - if (nameLower.Contains("taper") || nameLower.Contains("prismatic") || - nameLower.Contains("prism") || nameLower.Contains("component")) - { - WriteToChat($" *** POSSIBLE MATCH: '{item.Name}' ***"); - } + WriteToChat($" *** POSSIBLE MATCH: '{item.Name}' ***"); } } - - WriteToChat($"=== Total items listed: {itemNum - 1} ==="); - - // Now test our utility functions on the found Prismatic Taper - WriteToChat("=== Testing Utility Functions on Prismatic Taper ==="); - var foundItem = Utils.FindItemByName("Prismatic Taper"); - if (foundItem != null) - { - WriteToChat($"SUCCESS! Found: '{foundItem.Name}'"); - WriteToChat($"Utils.GetItemStackSize: {Utils.GetItemStackSize("Prismatic Taper")}"); - WriteToChat($"Utils.GetItemIcon: 0x{Utils.GetItemIcon("Prismatic Taper"):X}"); - WriteToChat($"Utils.GetItemDisplayIcon: 0x{Utils.GetItemDisplayIcon("Prismatic Taper"):X}"); - WriteToChat("=== TELEMETRY WILL NOW WORK! ==="); - } - else - { - WriteToChat("ERROR: Still can't find Prismatic Taper with utility functions!"); - } } - catch (Exception ex) - { - WriteToChat($"Search error: {ex.Message}"); - } - break; - case "deathstats": - try - { - WriteToChat("=== Death Tracking Statistics ==="); - WriteToChat($"Session Deaths: {sessionDeaths}"); - WriteToChat($"Total Deaths: {totalDeaths}"); - - // Get current character death count to verify sync - int currentCharDeaths = CoreManager.Current.CharacterFilter.GetCharProperty((int)IntValueKey.NumDeaths); - WriteToChat($"Character Property NumDeaths: {currentCharDeaths}"); - - if (currentCharDeaths != totalDeaths) - { - WriteToChat($"[WARNING] Death count sync issue detected!"); - WriteToChat($"Updating totalDeaths from {totalDeaths} to {currentCharDeaths}"); - totalDeaths = currentCharDeaths; - } - - WriteToChat("Death tracking is active and will increment on character death."); - } - catch (Exception ex) - { - WriteToChat($"Death stats error: {ex.Message}"); - } - break; + WriteToChat($"=== Total items listed: {itemNum - 1} ==="); - case "testdeath": - try + WriteToChat("=== Testing Utility Functions on Prismatic Taper ==="); + var foundItem = Utils.FindItemByName("Prismatic Taper"); + if (foundItem != null) { - WriteToChat("=== Manual Death Test ==="); - WriteToChat($"Current sessionDeaths variable: {sessionDeaths}"); - WriteToChat($"Current totalDeaths variable: {totalDeaths}"); - - // Read directly from character property - int currentCharDeaths = CoreManager.Current.CharacterFilter.GetCharProperty((int)IntValueKey.NumDeaths); - WriteToChat($"Character Property NumDeaths (43): {currentCharDeaths}"); - - // Manually increment session deaths for testing - sessionDeaths++; - WriteToChat($"Manually incremented sessionDeaths to: {sessionDeaths}"); - WriteToChat("Note: This doesn't simulate a real death, just tests the tracking variables."); - - // Check if death event is properly subscribed - WriteToChat($"Death event subscription check:"); - var deathEvent = typeof(Decal.Adapter.Wrappers.CharacterFilter).GetEvent("Death"); - WriteToChat($"Death event exists: {deathEvent != null}"); - } - catch (Exception ex) - { - WriteToChat($"Test death error: {ex.Message}"); - } - break; - - case "testtaper": - try - { - WriteToChat("=== Cached Taper Tracking Test ==="); - WriteToChat($"Cached Count: {cachedPrismaticCount}"); - WriteToChat($"Last Count: {lastPrismaticCount}"); - - // Compare with Utils function - int utilsCount = Utils.GetItemStackSize("Prismatic Taper"); - WriteToChat($"Utils Count: {utilsCount}"); - - if (cachedPrismaticCount == utilsCount) - { - WriteToChat("[OK] Cached count matches Utils count"); - } - else - { - WriteToChat($"[WARNING] Count mismatch! Cached: {cachedPrismaticCount}, Utils: {utilsCount}"); - WriteToChat("Refreshing cached count..."); - InitializePrismaticTaperCount(); - } - - WriteToChat("=== Container Analysis ==="); - int mainPackCount = 0; - int sidePackCount = 0; - int playerId = CoreManager.Current.CharacterFilter.Id; - - foreach (WorldObject wo in CoreManager.Current.WorldFilter.GetInventory()) - { - if (wo.Name.Equals("Prismatic Taper", StringComparison.OrdinalIgnoreCase)) - { - int stackCount = wo.Values(LongValueKey.StackCount, 1); - if (wo.Container == playerId) - { - mainPackCount += stackCount; - } - else - { - sidePackCount += stackCount; - } - } - } - - WriteToChat($"Main Pack Tapers: {mainPackCount}"); - WriteToChat($"Side Pack Tapers: {sidePackCount}"); - 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("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!"); - } - catch (Exception ex) - { - WriteToChat($"Taper test error: {ex.Message}"); - } - break; - - case "debugtaper": - // Debug functionality removed - break; - - case "finditem": - if (args.Length > 1) - { - string itemName = string.Join(" ", args, 1, args.Length - 1).Trim('"'); - WriteToChat($"=== Searching for: '{itemName}' ==="); - - var foundItem = Utils.FindItemByName(itemName); - if (foundItem != null) - { - WriteToChat($"FOUND: '{foundItem.Name}'"); - WriteToChat($"Count: {foundItem.Values(LongValueKey.StackCount, 0)}"); - WriteToChat($"Icon: 0x{foundItem.Icon:X}"); - WriteToChat($"Display Icon: 0x{(foundItem.Icon + 0x6000000):X}"); - WriteToChat($"Object Class: {foundItem.ObjectClass}"); - } - else - { - WriteToChat($"NOT FOUND: '{itemName}'"); - WriteToChat("Make sure the name is exactly as it appears in-game."); - } + WriteToChat($"SUCCESS! Found: '{foundItem.Name}'"); + WriteToChat($"Utils.GetItemStackSize: {Utils.GetItemStackSize("Prismatic Taper")}"); + WriteToChat($"Utils.GetItemIcon: 0x{Utils.GetItemIcon("Prismatic Taper"):X}"); + WriteToChat($"Utils.GetItemDisplayIcon: 0x{Utils.GetItemDisplayIcon("Prismatic Taper"):X}"); + WriteToChat("=== TELEMETRY WILL NOW WORK! ==="); } else { - WriteToChat("Usage: /mm finditem \"Item Name\""); - WriteToChat("Example: /mm finditem \"Prismatic Taper\""); + WriteToChat("ERROR: Still can't find Prismatic Taper with utility functions!"); } - break; + } + catch (Exception ex) + { + WriteToChat($"Search error: {ex.Message}"); + } + }, "Test Prismatic Taper detection and icon lookup"); - case "checkforupdate": - // Run the update check asynchronously - Task.Run(async () => + _commandRouter.Register("deathstats", args => + { + try + { + WriteToChat("=== Death Tracking Statistics ==="); + WriteToChat($"Session Deaths: {sessionDeaths}"); + WriteToChat($"Total Deaths: {totalDeaths}"); + + int currentCharDeaths = CoreManager.Current.CharacterFilter.GetCharProperty((int)IntValueKey.NumDeaths); + WriteToChat($"Character Property NumDeaths: {currentCharDeaths}"); + + if (currentCharDeaths != totalDeaths) { - await UpdateManager.CheckForUpdateAsync(); - // Update UI if available - try - { - ViewManager.RefreshUpdateStatus(); - } - catch (Exception ex) - { - WriteToChat($"Error refreshing UI: {ex.Message}"); - } - }); - break; + WriteToChat($"[WARNING] Death count sync issue detected!"); + WriteToChat($"Updating totalDeaths from {totalDeaths} to {currentCharDeaths}"); + totalDeaths = currentCharDeaths; + } - case "update": - // Run the update installation asynchronously - Task.Run(async () => + WriteToChat("Death tracking is active and will increment on character death."); + } + catch (Exception ex) + { + WriteToChat($"Death stats error: {ex.Message}"); + } + }, "Show current death tracking statistics"); + + _commandRouter.Register("testdeath", args => + { + try + { + WriteToChat("=== Manual Death Test ==="); + WriteToChat($"Current sessionDeaths variable: {sessionDeaths}"); + WriteToChat($"Current totalDeaths variable: {totalDeaths}"); + + int currentCharDeaths = CoreManager.Current.CharacterFilter.GetCharProperty((int)IntValueKey.NumDeaths); + WriteToChat($"Character Property NumDeaths (43): {currentCharDeaths}"); + + sessionDeaths++; + WriteToChat($"Manually incremented sessionDeaths to: {sessionDeaths}"); + WriteToChat("Note: This doesn't simulate a real death, just tests the tracking variables."); + + WriteToChat($"Death event subscription check:"); + var deathEvent = typeof(Decal.Adapter.Wrappers.CharacterFilter).GetEvent("Death"); + WriteToChat($"Death event exists: {deathEvent != null}"); + } + catch (Exception ex) + { + WriteToChat($"Test death error: {ex.Message}"); + } + }, "Test death tracking variables"); + + _commandRouter.Register("testtaper", args => + { + try + { + WriteToChat("=== Cached Taper Tracking Test ==="); + WriteToChat($"Cached Count: {cachedPrismaticCount}"); + WriteToChat($"Last Count: {lastPrismaticCount}"); + + int utilsCount = Utils.GetItemStackSize("Prismatic Taper"); + WriteToChat($"Utils Count: {utilsCount}"); + + if (cachedPrismaticCount == utilsCount) { - await UpdateManager.DownloadAndInstallUpdateAsync(); - }); - break; - - case "debugupdate": - Views.VVSTabbedMainView.DebugUpdateControls(); - break; - - case "sendinventory": - // Force inventory upload with ID requests - if (_inventoryLogger != null) - { - _inventoryLogger.ForceInventoryUpload(); + WriteToChat("[OK] Cached count matches Utils count"); } else { - WriteToChat("[INV] Inventory system not initialized"); + WriteToChat($"[WARNING] Count mismatch! Cached: {cachedPrismaticCount}, Utils: {utilsCount}"); + WriteToChat("Refreshing cached count..."); + InitializePrismaticTaperCount(); } - break; - case "refreshquests": - // Force quest data refresh (same as clicking refresh button) - try - { - WriteToChat("[QUEST] Refreshing quest data..."); - Views.FlagTrackerView.RefreshQuestData(); - } - catch (Exception ex) - { - WriteToChat($"[QUEST] Refresh failed: {ex.Message}"); - } - break; + WriteToChat("=== Container Analysis ==="); + int mainPackCount = 0; + int sidePackCount = 0; + int playerId = CoreManager.Current.CharacterFilter.Id; - case "queststatus": - // Show quest streaming status - try + foreach (WorldObject wo in CoreManager.Current.WorldFilter.GetInventory()) { - WriteToChat("=== Quest Streaming Status ==="); - WriteToChat($"Timer Active: {questStreamingTimer != null && questStreamingTimer.Enabled}"); - WriteToChat($"WebSocket Enabled: {WebSocketEnabled}"); - WriteToChat($"Quest Manager: {(questManager != null ? "Active" : "Not Active")}"); - WriteToChat($"Quest Count: {questManager?.QuestList?.Count ?? 0}"); - - if (questManager?.QuestList != null) + if (wo.Name.Equals("Prismatic Taper", StringComparison.OrdinalIgnoreCase)) { - var priorityQuests = questManager.QuestList - .Where(q => IsHighPriorityQuest(q.Id)) - .GroupBy(q => q.Id) - .Select(g => g.First()) - .ToList(); - WriteToChat($"Priority Quests Found: {priorityQuests.Count}"); - foreach (var quest in priorityQuests) - { - string questName = questManager.GetFriendlyQuestName(quest.Id); - WriteToChat($" - {questName} ({quest.Id})"); - } + int stackCount = wo.Values(LongValueKey.StackCount, 1); + if (wo.Container == playerId) + mainPackCount += stackCount; + else + sidePackCount += stackCount; } - - WriteToChat($"Verbose Logging: {PluginSettings.Instance?.VerboseLogging ?? false}"); - WriteToChat("Use '/mm verbose' to toggle debug logging"); } - catch (Exception ex) - { - WriteToChat($"[QUEST] Status check failed: {ex.Message}"); - } - break; - case "verbose": - // Toggle verbose logging - if (PluginSettings.Instance != null) + WriteToChat($"Main Pack Tapers: {mainPackCount}"); + WriteToChat($"Side Pack Tapers: {sidePackCount}"); + 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("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!"); + } + catch (Exception ex) + { + WriteToChat($"Taper test error: {ex.Message}"); + } + }, "Test cached Prismatic Taper tracking"); + + _commandRouter.Register("debugtaper", args => { }, ""); + + _commandRouter.Register("finditem", args => + { + if (args.Length > 1) + { + string itemName = string.Join(" ", args, 1, args.Length - 1).Trim('"'); + WriteToChat($"=== Searching for: '{itemName}' ==="); + + var foundItem = Utils.FindItemByName(itemName); + if (foundItem != null) { - PluginSettings.Instance.VerboseLogging = !PluginSettings.Instance.VerboseLogging; - WriteToChat($"Verbose logging: {(PluginSettings.Instance.VerboseLogging ? "ENABLED" : "DISABLED")}"); + WriteToChat($"FOUND: '{foundItem.Name}'"); + WriteToChat($"Count: {foundItem.Values(LongValueKey.StackCount, 0)}"); + WriteToChat($"Icon: 0x{foundItem.Icon:X}"); + WriteToChat($"Display Icon: 0x{(foundItem.Icon + 0x6000000):X}"); + WriteToChat($"Object Class: {foundItem.ObjectClass}"); } else { - WriteToChat("Settings not initialized"); + WriteToChat($"NOT FOUND: '{itemName}'"); + WriteToChat("Make sure the name is exactly as it appears in-game."); } - break; + } + else + { + WriteToChat("Usage: /mm finditem \"Item Name\""); + WriteToChat("Example: /mm finditem \"Prismatic Taper\""); + } + }, "Find item in inventory by name"); - default: - WriteToChat($"Unknown /mm command: {subCommand}. Try /mm help"); - break; - } + _commandRouter.Register("checkforupdate", args => + { + Task.Run(async () => + { + await UpdateManager.CheckForUpdateAsync(); + try + { + ViewManager.RefreshUpdateStatus(); + } + catch (Exception ex) + { + WriteToChat($"Error refreshing UI: {ex.Message}"); + } + }); + }, "Check for plugin updates"); + + _commandRouter.Register("update", args => + { + Task.Run(async () => + { + await UpdateManager.DownloadAndInstallUpdateAsync(); + }); + }, "Download and install update"); + + _commandRouter.Register("debugupdate", args => + { + Views.VVSTabbedMainView.DebugUpdateControls(); + }, "Debug update UI controls"); + + _commandRouter.Register("sendinventory", args => + { + if (_inventoryLogger != null) + _inventoryLogger.ForceInventoryUpload(); + else + WriteToChat("[INV] Inventory system not initialized"); + }, "Force inventory upload with ID requests"); + + _commandRouter.Register("refreshquests", args => + { + try + { + WriteToChat("[QUEST] Refreshing quest data..."); + Views.FlagTrackerView.RefreshQuestData(); + } + catch (Exception ex) + { + WriteToChat($"[QUEST] Refresh failed: {ex.Message}"); + } + }, "Force quest data refresh for Flag Tracker"); + + _commandRouter.Register("queststatus", args => + { + try + { + WriteToChat("=== Quest Streaming Status ==="); + WriteToChat($"Timer Active: {questStreamingTimer != null && questStreamingTimer.Enabled}"); + WriteToChat($"WebSocket Enabled: {WebSocketEnabled}"); + WriteToChat($"Quest Manager: {(questManager != null ? "Active" : "Not Active")}"); + WriteToChat($"Quest Count: {questManager?.QuestList?.Count ?? 0}"); + + if (questManager?.QuestList != null) + { + var priorityQuests = questManager.QuestList + .Where(q => IsHighPriorityQuest(q.Id)) + .GroupBy(q => q.Id) + .Select(g => g.First()) + .ToList(); + WriteToChat($"Priority Quests Found: {priorityQuests.Count}"); + foreach (var quest in priorityQuests) + { + string questName = questManager.GetFriendlyQuestName(quest.Id); + WriteToChat($" - {questName} ({quest.Id})"); + } + } + + WriteToChat($"Verbose Logging: {PluginSettings.Instance?.VerboseLogging ?? false}"); + WriteToChat("Use '/mm verbose' to toggle debug logging"); + } + catch (Exception ex) + { + WriteToChat($"[QUEST] Status check failed: {ex.Message}"); + } + }, "Show quest streaming status and diagnostics"); + + _commandRouter.Register("verbose", args => + { + if (PluginSettings.Instance != null) + { + PluginSettings.Instance.VerboseLogging = !PluginSettings.Instance.VerboseLogging; + WriteToChat($"Verbose logging: {(PluginSettings.Instance.VerboseLogging ? "ENABLED" : "DISABLED")}"); + } + else + { + WriteToChat("Settings not initialized"); + } + }, "Toggle verbose debug logging"); }