diff --git a/MosswartMassacre/MosswartMassacre.csproj b/MosswartMassacre/MosswartMassacre.csproj index b200028..b2d0613 100644 --- a/MosswartMassacre/MosswartMassacre.csproj +++ b/MosswartMassacre/MosswartMassacre.csproj @@ -29,7 +29,7 @@ pdbonly true bin\Release\ - TRACE;VVS_REFERENCED;DECAL_INTEROP + TRACE prompt 4 @@ -175,9 +175,14 @@ True Resources.resx - - + + + + + + + diff --git a/MosswartMassacre/NavRoute.cs b/MosswartMassacre/NavRoute.cs index 6f3ccfa..f1d0fa3 100644 --- a/MosswartMassacre/NavRoute.cs +++ b/MosswartMassacre/NavRoute.cs @@ -48,23 +48,25 @@ namespace MosswartMassacre return false; } - PluginCore.WriteToChat($"Navigation: Loading {FileName}..."); + PluginCore.WriteToChat($"[NavRoute] Loading {FileName}..."); using (StreamReader sr = File.OpenText(FilePath)) { // Read header string header = sr.ReadLine(); + PluginCore.WriteToChat($"[NavRoute] Header: '{header}'"); if (string.IsNullOrEmpty(header) || !header.StartsWith("uTank2 NAV")) { - PluginCore.WriteToChat($"Navigation: Invalid file format - {FileName}"); + PluginCore.WriteToChat($"Invalid nav file format: {FileName} (header: {header})"); return false; } // Read nav type string navTypeLine = sr.ReadLine(); + PluginCore.WriteToChat($"[NavRoute] Nav type line: '{navTypeLine}'"); if (string.IsNullOrEmpty(navTypeLine) || !int.TryParse(navTypeLine.Trim(), out int navType)) { - PluginCore.WriteToChat($"Navigation: Failed to parse route type - {FileName}"); + PluginCore.WriteToChat($"Could not parse nav type: {FileName} (line: '{navTypeLine}')"); return false; } @@ -88,43 +90,46 @@ namespace MosswartMassacre break; default: navTypeDescription = $"Unknown ({navType})"; - PluginCore.WriteToChat($"Navigation: Unknown route type {navType} in {FileName}"); + PluginCore.WriteToChat($"[NavRoute] WARNING: Unknown nav type {navType} in {FileName}"); break; } + + PluginCore.WriteToChat($"[NavRoute] Nav type: {navTypeDescription}"); // Handle target nav (type 3) - follows a specific player/object if (navType == 3) { if (sr.EndOfStream) { - PluginCore.WriteToChat($"Navigation: Target route file is empty - {FileName}"); + PluginCore.WriteToChat($"[NavRoute] Target nav file is empty: {FileName}"); return false; } string targetName = sr.ReadLine(); if (sr.EndOfStream) { - PluginCore.WriteToChat($"Navigation: Target route missing target ID - {FileName}"); + PluginCore.WriteToChat($"[NavRoute] Target nav missing target ID: {FileName}"); return false; } string targetIdLine = sr.ReadLine(); - PluginCore.WriteToChat($"Navigation: Target route '{targetName}' cannot be visualized"); + PluginCore.WriteToChat($"[NavRoute] Target navigation for '{targetName}' (ID: {targetIdLine}) - No visual representation available"); return true; // Successfully loaded but can't visualize } // Read record count string recordCountLine = sr.ReadLine(); + PluginCore.WriteToChat($"[NavRoute] Record count line: '{recordCountLine}'"); if (string.IsNullOrEmpty(recordCountLine) || !int.TryParse(recordCountLine.Trim(), out int recordCount)) { - PluginCore.WriteToChat($"Navigation: Failed to parse waypoint count - {FileName}"); + PluginCore.WriteToChat($"Could not parse record count: {FileName} (line: '{recordCountLine}')"); return false; } if (recordCount <= 0 || recordCount > 10000) // Sanity check { - PluginCore.WriteToChat($"Navigation: Invalid waypoint count {recordCount} - {FileName}"); + PluginCore.WriteToChat($"Invalid record count: {recordCount} in {FileName}"); return false; } @@ -133,14 +138,33 @@ namespace MosswartMassacre while (!sr.EndOfStream && waypointsRead < recordCount) { + PluginCore.WriteToChat($"[NavRoute] Reading waypoint {waypointsRead + 1}/{recordCount}"); + // Read waypoint type string waypointTypeLine = sr.ReadLine(); + PluginCore.WriteToChat($"[NavRoute] Waypoint type line: '{waypointTypeLine}'"); if (string.IsNullOrEmpty(waypointTypeLine) || !int.TryParse(waypointTypeLine.Trim(), out int waypointType)) { - PluginCore.WriteToChat($"Navigation: Failed to parse waypoint {waypointsRead + 1} in {FileName}"); + PluginCore.WriteToChat($"Could not parse waypoint type at waypoint {waypointsRead}: '{waypointTypeLine}'"); break; // Skip this waypoint, don't fail entirely } + + string waypointTypeDesc = waypointType switch + { + 0 => "Point", + 1 => "Portal", + 2 => "Recall", + 3 => "Pause", + 4 => "ChatCommand", + 5 => "OpenVendor", + 6 => "Portal2", + 7 => "UseNPC", + 8 => "Checkpoint", + 9 => "Jump", + _ => $"Unknown ({waypointType})" + }; + PluginCore.WriteToChat($"[NavRoute] Waypoint {waypointsRead + 1}: Type {waypointType} ({waypointTypeDesc})"); // Read coordinates (all waypoint types have EW, NS, Z, Unknown) string ewLine = sr.ReadLine(); @@ -148,9 +172,11 @@ namespace MosswartMassacre string zLine = sr.ReadLine(); string unknownLine = sr.ReadLine(); // Unknown value (always 0) + PluginCore.WriteToChat($"[NavRoute] Coordinates: EW={ewLine}, NS={nsLine}, Z={zLine}, Unknown={unknownLine}"); + if (string.IsNullOrEmpty(ewLine) || string.IsNullOrEmpty(nsLine) || string.IsNullOrEmpty(zLine) || string.IsNullOrEmpty(unknownLine)) { - PluginCore.WriteToChat($"Navigation: Missing coordinates at waypoint {waypointsRead + 1} in {FileName}"); + PluginCore.WriteToChat($"Missing coordinate lines at waypoint {waypointsRead}"); break; } @@ -158,7 +184,7 @@ namespace MosswartMassacre !double.TryParse(nsLine.Trim(), out double ns) || !double.TryParse(zLine.Trim(), out double z)) { - PluginCore.WriteToChat($"Navigation: Invalid coordinates at waypoint {waypointsRead + 1} in {FileName}"); + PluginCore.WriteToChat($"Could not parse coordinates at waypoint {waypointsRead}: EW={ewLine}, NS={nsLine}, Z={zLine}"); break; // Skip this waypoint } @@ -178,18 +204,17 @@ namespace MosswartMassacre // Skip additional data based on waypoint type if (!SkipWaypointData(sr, waypointType)) { - PluginCore.WriteToChat($"Navigation: Failed to parse waypoint {waypointsRead + 1} data in {FileName}"); + PluginCore.WriteToChat($"Failed to skip waypoint data for type {waypointType} at waypoint {waypointsRead}"); break; // Don't continue if we can't parse properly } } + PluginCore.WriteToChat($"[NavRoute] Loaded {waypoints.Count} waypoints from {FileName}"); + + // Debug: Show if we have waypoints to visualize if (waypoints.Count > 0) { - PluginCore.WriteToChat($"Navigation: Loaded {FileName} ({waypoints.Count} waypoints)"); - } - else - { - PluginCore.WriteToChat($"Navigation: No valid waypoints found in {FileName}"); + PluginCore.WriteToChat($"[NavRoute] Route ready for visualization with {waypoints.Count} waypoints"); } return waypoints.Count > 0; @@ -197,7 +222,7 @@ namespace MosswartMassacre } catch (Exception ex) { - PluginCore.WriteToChat($"Navigation: Error loading {FileName} - {ex.Message}"); + PluginCore.WriteToChat($"[NavRoute] Error loading nav file {FileName}: {ex.Message}"); return false; } } @@ -256,31 +281,39 @@ namespace MosswartMassacre sr.ReadLine(); // Milliseconds break; default: - // Unknown waypoint type - skip silently + PluginCore.WriteToChat($"[NavRoute] Unknown waypoint type: {waypointType}"); break; } return true; } - catch + catch (Exception ex) { - // Silently handle parsing errors + PluginCore.WriteToChat($"[NavRoute] Error skipping waypoint data: {ex.Message}"); return false; } } public void Show() { - if (isVisible) return; + PluginCore.WriteToChat($"[NavRoute] Show() called for {FileName}"); + PluginCore.WriteToChat($"[NavRoute] isVisible: {isVisible}, waypoints.Count: {waypoints.Count}"); + + if (isVisible) + { + PluginCore.WriteToChat($"[NavRoute] Route {FileName} is already visible, skipping"); + return; + } if (waypoints.Count == 0) { - PluginCore.WriteToChat($"Navigation: No waypoints to visualize in {FileName}"); + PluginCore.WriteToChat($"[NavRoute] Route {FileName} has no waypoints to visualize"); return; } + PluginCore.WriteToChat($"[NavRoute] Creating line objects for {FileName}..."); CreateLineObjects(); isVisible = true; - PluginCore.WriteToChat($"Navigation: Showing {FileName} ({waypoints.Count} waypoints)"); + PluginCore.WriteToChat($"[NavRoute] Route {FileName} visualization complete - isVisible: {isVisible}"); } public void Hide() @@ -289,28 +322,34 @@ namespace MosswartMassacre ClearRoute(); isVisible = false; - PluginCore.WriteToChat($"Navigation: Hidden {FileName}"); + PluginCore.WriteToChat($"Hidden route: {FileName}"); } private void CreateLineObjects() { try { + PluginCore.WriteToChat($"[NavRoute] CreateLineObjects() starting for {FileName}"); + // Check D3DService availability if (CoreManager.Current?.D3DService == null) { - PluginCore.WriteToChat($"Navigation: 3D service unavailable"); + PluginCore.WriteToChat($"[NavRoute] ERROR: D3DService is null!"); return; } // Limit the number of lines to prevent lag int maxLines = Math.Min(waypoints.Count - 1, 500); // Max 500 lines + PluginCore.WriteToChat($"[NavRoute] Creating {maxLines} navigation lines from {waypoints.Count} waypoints..."); + int linesCreated = 0; for (int i = 1; i <= maxLines; i++) { var current = waypoints[i]; var previous = waypoints[i - 1]; + + PluginCore.WriteToChat($"[NavRoute] Creating line {i}: From ({previous.NS:F2}, {previous.EW:F2}, {previous.Z:F2}) to ({current.NS:F2}, {current.EW:F2}, {current.Z:F2})"); if (CreateLineBetweenWaypoints(previous, current)) { @@ -324,14 +363,18 @@ namespace MosswartMassacre } } + PluginCore.WriteToChat($"[NavRoute] Successfully created {linesCreated} D3D line objects out of {maxLines} attempted"); + PluginCore.WriteToChat($"[NavRoute] Total D3D objects in list: {lineObjects.Count}"); + if (waypoints.Count > 501) { - PluginCore.WriteToChat($"Navigation: Large route - showing {maxLines} of {waypoints.Count} segments"); + PluginCore.WriteToChat($"[NavRoute] Route has {waypoints.Count} waypoints, showing first {maxLines} segments"); } } catch (Exception ex) { - PluginCore.WriteToChat($"Navigation: Error creating visualization - {ex.Message}"); + PluginCore.WriteToChat($"[NavRoute] Error creating line objects: {ex.Message}"); + PluginCore.WriteToChat($"[NavRoute] Stack trace: {ex.StackTrace}"); } } @@ -346,20 +389,34 @@ namespace MosswartMassacre Math.Pow((to.Z - from.Z) * 240, 2) ); - if (distance <= 0) return false; + if (distance <= 0) + { + PluginCore.WriteToChat($"[NavRoute] Skipping line with zero distance"); + return false; + } + + PluginCore.WriteToChat($"[NavRoute] Creating D3D line object with distance: {distance:F2}"); // Create D3D line object var lineObj = CoreManager.Current.D3DService.NewD3DObj(); - if (lineObj == null) return false; + if (lineObj == null) + { + PluginCore.WriteToChat($"[NavRoute] ERROR: Failed to create D3D object!"); + return false; + } + + PluginCore.WriteToChat($"[NavRoute] D3D object created successfully"); lineObj.SetShape(D3DShape.Cube); lineObj.Color = routeColor.ToArgb(); + PluginCore.WriteToChat($"[NavRoute] Set shape and color (ARGB: {routeColor.ToArgb()})"); // Position at midpoint between waypoints float midNS = (float)(from.NS + to.NS) / 2; float midEW = (float)(from.EW + to.EW) / 2; float midZ = (float)((from.Z + to.Z) * 120) + 0.1f; // Slightly higher than UtilityBelt + PluginCore.WriteToChat($"[NavRoute] Anchoring at: NS={midNS:F2}, EW={midEW:F2}, Z={midZ:F2}"); lineObj.Anchor(midNS, midEW, midZ); // Orient toward destination @@ -367,20 +424,28 @@ namespace MosswartMassacre float orientEW = (float)from.EW; float orientZ = (float)(from.Z * 240) + 0.1f; + PluginCore.WriteToChat($"[NavRoute] Orienting to: NS={orientNS:F2}, EW={orientEW:F2}, Z={orientZ:F2}"); lineObj.OrientToCoords(orientNS, orientEW, orientZ, true); // Scale to create line effect lineObj.ScaleX = 0.3f; // Slightly thicker than UtilityBelt lineObj.ScaleZ = 0.3f; lineObj.ScaleY = (float)distance; + + PluginCore.WriteToChat($"[NavRoute] Set scale: X={lineObj.ScaleX}, Y={lineObj.ScaleY:F2}, Z={lineObj.ScaleZ}"); lineObj.Visible = true; + PluginCore.WriteToChat($"[NavRoute] Set visible to true"); + lineObjects.Add(lineObj); + PluginCore.WriteToChat($"[NavRoute] Added line object to list (total: {lineObjects.Count})"); return true; } - catch + catch (Exception ex) { + PluginCore.WriteToChat($"[NavRoute] Error creating line: {ex.Message}"); + PluginCore.WriteToChat($"[NavRoute] Stack trace: {ex.StackTrace}"); return false; } } diff --git a/MosswartMassacre/NavVisualization.cs b/MosswartMassacre/NavVisualization.cs index 824462f..3180d56 100644 --- a/MosswartMassacre/NavVisualization.cs +++ b/MosswartMassacre/NavVisualization.cs @@ -39,6 +39,7 @@ namespace MosswartMassacre if (!string.IsNullOrEmpty(PluginSettings.Instance?.VTankProfilesPath)) { vtankProfilesDirectory = PluginSettings.Instance.VTankProfilesPath; + PluginCore.WriteToChat($"[NavViz] Using configured VTank profiles path: {vtankProfilesDirectory}"); return; } @@ -53,17 +54,20 @@ namespace MosswartMassacre if (!string.IsNullOrEmpty(profilePath)) { vtankProfilesDirectory = profilePath; + PluginCore.WriteToChat($"[NavViz] Found VTank profiles from registry: {vtankProfilesDirectory}"); return; } } } - catch + catch (Exception regEx) { + PluginCore.WriteToChat($"[NavViz] Registry lookup failed: {regEx.Message}"); } // Fall back to default path vtankProfilesDirectory = defaultPath; - // Using default path - user can configure in Settings if needed + PluginCore.WriteToChat($"[NavViz] Using default VTank path: {vtankProfilesDirectory}"); + PluginCore.WriteToChat($"[NavViz] If this is wrong, configure the correct path in Settings tab."); } catch (Exception ex) { @@ -72,10 +76,6 @@ namespace MosswartMassacre } } - /// - /// Scan VTank directory for .nav files and populate available routes list - /// Filters out follow files and temporary files, sorts alphabetically - /// public void RefreshNavFileList() { // Re-initialize directory in case settings changed @@ -83,26 +83,43 @@ namespace MosswartMassacre availableNavFiles.Clear(); + PluginCore.WriteToChat($"[NavViz] Refreshing nav files from: {vtankProfilesDirectory}"); if (string.IsNullOrEmpty(vtankProfilesDirectory)) { - PluginCore.WriteToChat("VTank directory not configured. Set path in Settings tab."); + PluginCore.WriteToChat("[NavViz] No VTank directory set"); return; } if (!Directory.Exists(vtankProfilesDirectory)) { - PluginCore.WriteToChat($"VTank directory not found: {vtankProfilesDirectory}"); + PluginCore.WriteToChat($"[NavViz] Directory does not exist: {vtankProfilesDirectory}"); return; } try { - // Get all files and filter for .nav files only, excluding follow/temporary files + // Get ALL files first for debugging var allFiles = Directory.GetFiles(vtankProfilesDirectory); + PluginCore.WriteToChat($"[NavViz] Directory contents ({allFiles.Length} files):"); + foreach (var file in allFiles.Take(10)) // Show first 10 files + { + PluginCore.WriteToChat($" - {Path.GetFileName(file)}"); + } + + // Filter for .nav files ONLY and exclude follow files var navFiles = allFiles - .Where(file => Path.GetExtension(file).Equals(".nav", StringComparison.OrdinalIgnoreCase)) - .Select(file => Path.GetFileNameWithoutExtension(file)) + .Where(file => { + var ext = Path.GetExtension(file); + var isNav = ext.Equals(".nav", StringComparison.OrdinalIgnoreCase); + PluginCore.WriteToChat($"[NavViz] File: {Path.GetFileName(file)} - Extension: '{ext}' - IsNav: {isNav}"); + return isNav; + }) + .Select(file => { + var name = Path.GetFileNameWithoutExtension(file); + PluginCore.WriteToChat($"[NavViz] Nav file name: {name}"); + return name; + }) .Where(name => !string.IsNullOrEmpty(name) && !name.StartsWith("follow", StringComparison.OrdinalIgnoreCase) && !name.StartsWith("--", StringComparison.OrdinalIgnoreCase)) @@ -111,24 +128,14 @@ namespace MosswartMassacre availableNavFiles.AddRange(navFiles); - // Only report summary - no need to spam chat with every file - if (navFiles.Count > 0) - { - PluginCore.WriteToChat($"Navigation: Found {navFiles.Count} route files"); - } + PluginCore.WriteToChat($"[NavViz] Found {navFiles.Count} .nav files: {string.Join(", ", navFiles)}"); } catch (Exception ex) { - PluginCore.WriteToChat($"Navigation: Error scanning files - {ex.Message}"); + PluginCore.WriteToChat($"[NavViz] Error refreshing nav files: {ex.Message}"); } } - /// - /// Load a specific navigation route file for visualization - /// Clears current route if "None" specified, otherwise loads .nav file - /// - /// Name of .nav file (without extension) or "None" - /// True if route loaded successfully, false otherwise public bool LoadRoute(string navFileName) { try @@ -142,6 +149,7 @@ namespace MosswartMassacre if (string.IsNullOrEmpty(navFileName) || navFileName == "None") { + PluginCore.WriteToChat("[NavViz] Route cleared"); RouteChanged?.Invoke(this, EventArgs.Empty); return true; } @@ -150,7 +158,7 @@ namespace MosswartMassacre if (!File.Exists(fullPath)) { - PluginCore.WriteToChat($"Navigation file '{navFileName}' not found"); + PluginCore.WriteToChat($"[NavViz] Nav file not found: {navFileName}"); return false; } @@ -164,35 +172,41 @@ namespace MosswartMassacre } // Show route if visualization is enabled + PluginCore.WriteToChat($"[NavViz] LoadRoute: IsEnabled = {IsEnabled}"); if (IsEnabled) { + PluginCore.WriteToChat($"[NavViz] Calling Show() for route {navFileName}"); currentRoute.Show(); } + else + { + PluginCore.WriteToChat($"[NavViz] Visualization disabled, not showing route"); + } RouteChanged?.Invoke(this, EventArgs.Empty); return true; } catch (Exception ex) { - PluginCore.WriteToChat($"Navigation: Failed to load '{navFileName}' - {ex.Message}"); + PluginCore.WriteToChat($"[NavViz] Error loading route {navFileName}: {ex.Message}"); return false; } } - /// - /// Enable or disable navigation route visualization in 3D world - /// Shows/hides the currently loaded route based on enabled state - /// - /// True to show route lines, false to hide public void SetEnabled(bool enabled) { - // No change needed if already in desired state - if (IsEnabled == enabled) return; + PluginCore.WriteToChat($"[NavViz] SetEnabled called: current={IsEnabled}, new={enabled}"); + if (IsEnabled == enabled) + { + PluginCore.WriteToChat($"[NavViz] SetEnabled: no change needed"); + return; + } IsEnabled = enabled; if (currentRoute != null) { + PluginCore.WriteToChat($"[NavViz] SetEnabled: currentRoute exists, {(enabled ? "showing" : "hiding")}"); if (enabled) { currentRoute.Show(); @@ -204,9 +218,10 @@ namespace MosswartMassacre } else { + PluginCore.WriteToChat($"[NavViz] SetEnabled: no currentRoute to {(enabled ? "show" : "hide")}"); } - PluginCore.WriteToChat($"Navigation visualization {(enabled ? "enabled" : "disabled")}"); + PluginCore.WriteToChat($"[NavViz] Navigation visualization {(enabled ? "enabled" : "disabled")}"); } public void ToggleEnabled() diff --git a/MosswartMassacre/PluginCore.cs b/MosswartMassacre/PluginCore.cs index cacdeeb..aa05bbb 100644 --- a/MosswartMassacre/PluginCore.cs +++ b/MosswartMassacre/PluginCore.cs @@ -28,45 +28,6 @@ namespace MosswartMassacre internal static DateTime statsStartTime = DateTime.Now; internal static Timer updateTimer; public static bool RareMetaEnabled { get; set; } = true; - - // VVS View Management - private static class ViewManager - { - public static void ViewInit() - { - Views.VVSTabbedMainView.ViewInit(); - } - - public static void ViewDestroy() - { - Views.VVSTabbedMainView.ViewDestroy(); - } - - public static void UpdateKillStats(int totalKills, double killsPer5Min, double killsPerHour) - { - Views.VVSTabbedMainView.UpdateKillStats(totalKills, killsPer5Min, killsPerHour); - } - - public static void UpdateElapsedTime(TimeSpan elapsed) - { - Views.VVSTabbedMainView.UpdateElapsedTime(elapsed); - } - - public static void UpdateRareCount(int rareCount) - { - Views.VVSTabbedMainView.UpdateRareCount(rareCount); - } - - public static void SetRareMetaToggleState(bool enabled) - { - Views.VVSTabbedMainView.SetRareMetaToggleState(enabled); - } - - public static void RefreshSettingsFromConfig() - { - Views.VVSTabbedMainView.RefreshSettingsFromConfig(); - } - } public static bool RemoteCommandsEnabled { get; set; } = false; public static bool HttpServerEnabled { get; set; } = false; public static string CharTag { get; set; } = ""; @@ -102,7 +63,7 @@ namespace MosswartMassacre updateTimer.Start(); // Initialize the view (UI) - use tabbed interface by default - ViewManager.ViewInit(); + TabbedMainView.ViewInit(); // Enable TLS1.2 ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12; @@ -152,7 +113,7 @@ namespace MosswartMassacre } // Clean up the view - ViewManager.ViewDestroy(); + TabbedMainView.ViewDestroy(); //Disable vtank interface vTank.Disable(); // sluta lyssna på commands @@ -188,8 +149,8 @@ namespace MosswartMassacre HttpServerEnabled = PluginSettings.Instance.HttpServerEnabled; TelemetryEnabled = PluginSettings.Instance.TelemetryEnabled; CharTag = PluginSettings.Instance.CharTag; - ViewManager.SetRareMetaToggleState(RareMetaEnabled); - ViewManager.RefreshSettingsFromConfig(); // Refresh all UI settings after loading + TabbedMainView.SetRareMetaToggleState(RareMetaEnabled); + TabbedMainView.RefreshSettingsFromConfig(); // Refresh all UI settings after loading if (TelemetryEnabled) Telemetry.Start(); if (WebSocketEnabled) @@ -277,13 +238,13 @@ namespace MosswartMassacre totalKills++; lastKillTime = DateTime.Now; CalculateKillsPerInterval(); - ViewManager.UpdateKillStats(totalKills, killsPer5Min, killsPerHour); + TabbedMainView.UpdateKillStats(totalKills, killsPer5Min, killsPerHour); } if (IsRareDiscoveryMessage(e.Text, out string rareText)) { rareCount++; - ViewManager.UpdateRareCount(rareCount); + TabbedMainView.UpdateRareCount(rareCount); if (RareMetaEnabled) { @@ -363,11 +324,11 @@ namespace MosswartMassacre { // Update the elapsed time TimeSpan elapsed = DateTime.Now - statsStartTime; - ViewManager.UpdateElapsedTime(elapsed); + TabbedMainView.UpdateElapsedTime(elapsed); // Recalculate kill rates CalculateKillsPerInterval(); - ViewManager.UpdateKillStats(totalKills, killsPer5Min, killsPerHour); + TabbedMainView.UpdateKillStats(totalKills, killsPer5Min, killsPerHour); } catch (Exception ex) { @@ -465,14 +426,14 @@ namespace MosswartMassacre killsPerHour = 0; WriteToChat("Stats have been reset."); - ViewManager.UpdateKillStats(totalKills, killsPer5Min, killsPerHour); - ViewManager.UpdateRareCount(rareCount); + TabbedMainView.UpdateKillStats(totalKills, killsPer5Min, killsPerHour); + TabbedMainView.UpdateRareCount(rareCount); } public static void ToggleRareMeta() { PluginSettings.Instance.RareMetaEnabled = !PluginSettings.Instance.RareMetaEnabled; RareMetaEnabled = PluginSettings.Instance.RareMetaEnabled; - ViewManager.SetRareMetaToggleState(RareMetaEnabled); + TabbedMainView.SetRareMetaToggleState(RareMetaEnabled); } [DllImport("Decal.dll")] @@ -602,7 +563,7 @@ namespace MosswartMassacre case "meta": RareMetaEnabled = !RareMetaEnabled; WriteToChat($"Rare meta state is now {(RareMetaEnabled ? "ON" : "OFF")}"); - ViewManager.SetRareMetaToggleState(RareMetaEnabled); // <-- sync the UI + TabbedMainView.SetRareMetaToggleState(RareMetaEnabled); // <-- sync the UI break; case "http": diff --git a/MosswartMassacre/Properties/AssemblyInfo.cs b/MosswartMassacre/Properties/AssemblyInfo.cs index b7482f9..e084d4c 100644 --- a/MosswartMassacre/Properties/AssemblyInfo.cs +++ b/MosswartMassacre/Properties/AssemblyInfo.cs @@ -26,5 +26,5 @@ using System.Runtime.InteropServices; // Minor Version // Build Number // Revision -[assembly: AssemblyVersion("3.0.1.0")] -[assembly: AssemblyFileVersion("3.0.1.0")] \ No newline at end of file +[assembly: AssemblyVersion("3.0.0.6")] +[assembly: AssemblyFileVersion("3.0.0.6")] \ No newline at end of file diff --git a/MosswartMassacre/ViewSystemSelector.cs b/MosswartMassacre/ViewSystemSelector.cs new file mode 100644 index 0000000..5e75b7a --- /dev/null +++ b/MosswartMassacre/ViewSystemSelector.cs @@ -0,0 +1,262 @@ +/////////////////////////////////////////////////////////////////////////////// +//File: ViewSystemSelector.cs +// +//Description: Contains the MyClasses.MetaViewWrappers.ViewSystemSelector class, +// which is used to determine whether the Virindi View Service is enabled. +// As with all the VVS wrappers, the VVS_REFERENCED compilation symbol must be +// defined for the VVS code to be compiled. Otherwise, only Decal views are used. +// +//References required: +// VirindiViewService (if VVS_REFERENCED is defined) +// Decal.Adapter +// Decal.Interop.Core +// +//This file is Copyright (c) 2009 VirindiPlugins +// +//Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +//The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +/////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Text; +using System.Reflection; + +#if METAVIEW_PUBLIC_NS +namespace MetaViewWrappers +#else +namespace MyClasses.MetaViewWrappers +#endif +{ + internal static class ViewSystemSelector + { + public enum eViewSystem + { + DecalInject, + VirindiViewService, + } + + + ///////////////////////////////System presence detection/////////////////////////////// + + public static bool IsPresent(Decal.Adapter.Wrappers.PluginHost pHost, eViewSystem VSystem) + { + switch (VSystem) + { + case eViewSystem.DecalInject: + return true; + case eViewSystem.VirindiViewService: + return VirindiViewsPresent(pHost); + default: + return false; + } + } + static bool VirindiViewsPresent(Decal.Adapter.Wrappers.PluginHost pHost) + { +#if VVS_REFERENCED + System.Reflection.Assembly[] asms = AppDomain.CurrentDomain.GetAssemblies(); + + foreach (System.Reflection.Assembly a in asms) + { + AssemblyName nmm = a.GetName(); + if ((nmm.Name == "VirindiViewService") && (nmm.Version >= new System.Version("1.0.0.37"))) + { + try + { + return Curtain_VVS_Running(); + } + catch + { + return false; + } + } + } + + return false; +#else + return false; +#endif + } + public static bool VirindiViewsPresent(Decal.Adapter.Wrappers.PluginHost pHost, Version minver) + { +#if VVS_REFERENCED + System.Reflection.Assembly[] asms = AppDomain.CurrentDomain.GetAssemblies(); + + foreach (System.Reflection.Assembly a in asms) + { + AssemblyName nm = a.GetName(); + if ((nm.Name == "VirindiViewService") && (nm.Version >= minver)) + { + try + { + return Curtain_VVS_Running(); + } + catch + { + return false; + } + } + } + + return false; +#else + return false; +#endif + } + +#if VVS_REFERENCED + static bool Curtain_VVS_Running() + { + return VirindiViewService.Service.Running; + } +#endif + + ///////////////////////////////CreateViewResource/////////////////////////////// + + public static IView CreateViewResource(Decal.Adapter.Wrappers.PluginHost pHost, string pXMLResource) + { +#if VVS_REFERENCED + if (IsPresent(pHost, eViewSystem.VirindiViewService)) + return CreateViewResource(pHost, pXMLResource, eViewSystem.VirindiViewService); + else +#endif + return CreateViewResource(pHost, pXMLResource, eViewSystem.DecalInject); + } + public static IView CreateViewResource(Decal.Adapter.Wrappers.PluginHost pHost, string pXMLResource, eViewSystem VSystem) + { + if (!IsPresent(pHost, VSystem)) return null; + switch (VSystem) + { + case eViewSystem.DecalInject: + return CreateDecalViewResource(pHost, pXMLResource); + case eViewSystem.VirindiViewService: +#if VVS_REFERENCED + return CreateMyHudViewResource(pHost, pXMLResource); +#else + break; +#endif + } + return null; + } + static IView CreateDecalViewResource(Decal.Adapter.Wrappers.PluginHost pHost, string pXMLResource) + { + IView ret = new DecalControls.View(); + ret.Initialize(pHost, pXMLResource); + return ret; + } + +#if VVS_REFERENCED + static IView CreateMyHudViewResource(Decal.Adapter.Wrappers.PluginHost pHost, string pXMLResource) + { + IView ret = new VirindiViewServiceHudControls.View(); + ret.Initialize(pHost, pXMLResource); + return ret; + } +#endif + + + ///////////////////////////////CreateViewXML/////////////////////////////// + + public static IView CreateViewXML(Decal.Adapter.Wrappers.PluginHost pHost, string pXML) + { +#if VVS_REFERENCED + if (IsPresent(pHost, eViewSystem.VirindiViewService)) + return CreateViewXML(pHost, pXML, eViewSystem.VirindiViewService); + else +#endif + return CreateViewXML(pHost, pXML, eViewSystem.DecalInject); + } + + public static IView CreateViewXML(Decal.Adapter.Wrappers.PluginHost pHost, string pXML, eViewSystem VSystem) + { + if (!IsPresent(pHost, VSystem)) return null; + switch (VSystem) + { + case eViewSystem.DecalInject: + return CreateDecalViewXML(pHost, pXML); + case eViewSystem.VirindiViewService: +#if VVS_REFERENCED + return CreateMyHudViewXML(pHost, pXML); +#else + break; +#endif + } + return null; + } + static IView CreateDecalViewXML(Decal.Adapter.Wrappers.PluginHost pHost, string pXML) + { + IView ret = new DecalControls.View(); + ret.InitializeRawXML(pHost, pXML); + return ret; + } + +#if VVS_REFERENCED + static IView CreateMyHudViewXML(Decal.Adapter.Wrappers.PluginHost pHost, string pXML) + { + IView ret = new VirindiViewServiceHudControls.View(); + ret.InitializeRawXML(pHost, pXML); + return ret; + } +#endif + + + ///////////////////////////////HasChatOpen/////////////////////////////// + + public static bool AnySystemHasChatOpen(Decal.Adapter.Wrappers.PluginHost pHost) + { + if (IsPresent(pHost, eViewSystem.VirindiViewService)) + if (HasChatOpen_VirindiViews()) return true; + if (pHost.Actions.ChatState) return true; + return false; + } + + static bool HasChatOpen_VirindiViews() + { +#if VVS_REFERENCED + if (VirindiViewService.HudView.FocusControl != null) + { + if (VirindiViewService.HudView.FocusControl.GetType() == typeof(VirindiViewService.Controls.HudTextBox)) + return true; + } + return false; +#else + return false; +#endif + } + + public delegate void delConditionalSplit(object data); + public static void ViewConditionalSplit(IView v, delConditionalSplit onDecal, delConditionalSplit onVVS, object data) + { + Type vtype = v.GetType(); + +#if VVS_REFERENCED + if (vtype == typeof(VirindiViewServiceHudControls.View)) + { + if (onVVS != null) + onVVS(data); + } +#endif + + if (vtype == typeof(DecalControls.View)) + { + if (onDecal != null) + onDecal(data); + } + } + } +} diff --git a/MosswartMassacre/ViewXML/mainViewTabbed.xml b/MosswartMassacre/ViewXML/mainViewTabbed.xml index 2ce4ff4..22303ae 100644 --- a/MosswartMassacre/ViewXML/mainViewTabbed.xml +++ b/MosswartMassacre/ViewXML/mainViewTabbed.xml @@ -25,19 +25,19 @@ - + - + - + - + - + @@ -46,7 +46,7 @@ - + @@ -90,10 +90,10 @@ - + - + @@ -101,14 +101,14 @@ - + - + - - + + diff --git a/MosswartMassacre/Views/BaseView.cs b/MosswartMassacre/Views/BaseView.cs new file mode 100644 index 0000000..2020be7 --- /dev/null +++ b/MosswartMassacre/Views/BaseView.cs @@ -0,0 +1,177 @@ +using System; +using System.Drawing; +using System.IO; +using System.Runtime.InteropServices; +using MyClasses.MetaViewWrappers; + +namespace MosswartMassacre.Views +{ + public class BaseView : IDisposable + { + #region Windows API for boundary checking + [StructLayout(LayoutKind.Sequential)] + public struct RECT + { + public int Left; + public int Top; + public int Right; + public int Bottom; + + public int Width { get { return Right - Left; } } + public int Height { get { return Bottom - Top; } } + } + + [DllImport("user32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect); + #endregion + + internal IView view; + protected PluginCore pluginCore; + + public BaseView(PluginCore core) + { + pluginCore = core; + } + + protected void CreateFromXMLResource(string resourcePath) + { + try + { + view = ViewSystemSelector.CreateViewResource(PluginCore.MyHost, resourcePath); + // Note: IView doesn't have VisibleChanged event, so we'll check bounds manually when needed + } + catch (Exception ex) + { + PluginCore.WriteToChat($"Error creating view from {resourcePath}: {ex.Message}"); + } + } + + protected void KeepWindowInBounds() + { + try + { + RECT rect = new RECT(); + IntPtr gameWindowHandle = PluginCore.MyHost?.Decal?.Hwnd ?? IntPtr.Zero; + + if (gameWindowHandle != IntPtr.Zero && GetWindowRect(gameWindowHandle, ref rect)) + { + Point currentLocation = view.Location; + Size viewSize = view.Size; + + // Check if window is outside right boundary + if (currentLocation.X + viewSize.Width > rect.Width) + { + currentLocation.X = rect.Width - viewSize.Width; + } + // Check if window is outside left boundary + else if (currentLocation.X < 0) + { + currentLocation.X = 20; + } + + // Check if window is outside bottom boundary + if (currentLocation.Y + viewSize.Height > rect.Height) + { + currentLocation.Y = rect.Height - viewSize.Height; + } + // Check if window is outside top boundary + else if (currentLocation.Y < 0) + { + currentLocation.Y = 20; + } + + // Only update location if it changed + if (currentLocation != view.Location) + { + view.Location = currentLocation; + } + } + } + catch + { + // Don't spam chat with boundary check errors, just log silently + } + } + + protected virtual void SaveWindowPosition() + { + // Override in derived classes to save position to settings + } + + protected virtual void RestoreWindowPosition() + { + // Override in derived classes to restore position from settings + } + + public virtual void Initialize() + { + try + { + RestoreWindowPosition(); + } + catch (Exception ex) + { + PluginCore.WriteToChat($"Error initializing view: {ex.Message}"); + } + } + + public virtual void Show() + { + if (view != null) + { + view.Visible = true; + } + } + + public virtual void Hide() + { + if (view != null) + { + view.Visible = false; + } + } + + public virtual void Toggle() + { + if (view != null) + { + view.Visible = !view.Visible; + } + } + + #region IDisposable Support + private bool disposedValue = false; + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + try + { + SaveWindowPosition(); + + if (view != null) + { + view.Dispose(); + } + view = null; + } + catch (Exception ex) + { + PluginCore.WriteToChat($"Error disposing view: {ex.Message}"); + } + } + disposedValue = true; + } + } + + public void Dispose() + { + Dispose(true); + } + #endregion + } +} \ No newline at end of file diff --git a/MosswartMassacre/Views/TabbedMainView.cs b/MosswartMassacre/Views/TabbedMainView.cs new file mode 100644 index 0000000..29f429f --- /dev/null +++ b/MosswartMassacre/Views/TabbedMainView.cs @@ -0,0 +1,794 @@ +using System; +using System.Drawing; +using System.Timers; +using MyClasses.MetaViewWrappers; + +namespace MosswartMassacre.Views +{ + internal class TabbedMainView : BaseView + { + private static TabbedMainView instance; + + // Main tab controls (existing functionality) + private IStaticText lblTotalKills; + private IStaticText lblKillsPer5Min; + private IStaticText lblKillsPerHour; + private IStaticText lblElapsedTime; + private IStaticText lblRareCount; + private IStaticText lblAutoLootRare; + private IStaticText lblStatus; + private IStaticText lblWebSocketStatus; + + // Settings tab controls + private ICheckBox chkRareMetaEnabled; + private ICheckBox chkRemoteCommandsEnabled; + private ICheckBox chkHttpServerEnabled; + private ICheckBox chkWebSocketEnabled; + private ICheckBox chkTelemetryEnabled; + private ITextBox txtCharTag; + private ITextBox txtVTankPath; + + // Statistics tab controls + private IStaticText lblDetailedKillsValue; + private IStaticText lblBestHourValue; + private IStaticText lblAverageKillsValue; + private IStaticText lblRareStatsValue; + private IStaticText lblRareRateValue; + private IStaticText lblSessionTimeValue; + private IStaticText lblLastKillValue; + private IButton btnResetStats; + + // Navigation tab controls + private ICheckBox chkNavVisualizationEnabled; + private ICombo cmbNavFiles; + private IButton btnRefreshNavFiles; + private IStaticText lblNavStatus; + private IButton btnLoadRoute; + private IButton btnClearRoute; + + // Position tracking + private Timer positionSaveTimer; + private Timer positionCheckTimer; + private Point lastKnownPosition; + + // Statistics tracking + private double bestHourlyKills = 0; + private DateTime sessionStartTime; + + public TabbedMainView(PluginCore core) : base(core) + { + instance = this; + sessionStartTime = DateTime.Now; + InitializePositionTimer(); + } + + private void InitializePositionTimer() + { + positionSaveTimer = new Timer(2000); + positionSaveTimer.Elapsed += (s, e) => { + SaveWindowPosition(); + positionSaveTimer.Stop(); + }; + + positionCheckTimer = new Timer(500); + positionCheckTimer.Elapsed += CheckPositionChanged; + positionCheckTimer.Start(); + } + + public static void ViewInit() + { + try + { + if (instance == null) + { + instance = new TabbedMainView(null); + } + instance.InitializeView(); + PluginCore.WriteToChat("Enhanced tabbed view initialized."); + } + catch (Exception ex) + { + PluginCore.WriteToChat("Error initializing tabbed view: " + ex.Message); + } + } + + private void InitializeView() + { + try + { + // Load the new tabbed view + CreateFromXMLResource("MosswartMassacre.ViewXML.mainViewTabbed.xml"); + + InitializeMainTabControls(); + InitializeSettingsTabControls(); + InitializeStatisticsTabControls(); + InitializeNavigationTabControls(); + + // Initialize the base view and set initial position + Initialize(); + lastKnownPosition = view?.Location ?? new Point(100, 100); + } + catch (Exception ex) + { + PluginCore.WriteToChat("Error in InitializeView: " + ex.Message); + } + } + + private void InitializeMainTabControls() + { + try + { + // Main tab - existing functionality + lblTotalKills = (IStaticText)view["lblTotalKills"]; + lblKillsPer5Min = (IStaticText)view["lblKillsPer5Min"]; + lblKillsPerHour = (IStaticText)view["lblKillsPerHour"]; + lblElapsedTime = (IStaticText)view["lblElapsedTime"]; + lblRareCount = (IStaticText)view["lblRareCount"]; + lblAutoLootRare = (IStaticText)view["lblAutoLootRare"]; + lblStatus = (IStaticText)view["lblStatus"]; + lblWebSocketStatus = (IStaticText)view["lblWebSocketStatus"]; + + // Update status, auto loot indicator, and websocket status immediately + UpdateStatus(); + UpdateAutoLootRareIndicator(); + UpdateWebSocketStatus(); + } + catch (Exception ex) + { + PluginCore.WriteToChat($"Error initializing main tab controls: {ex.Message}"); + } + } + + private void InitializeSettingsTabControls() + { + // Settings tab controls + chkRareMetaEnabled = (ICheckBox)view["chkRareMetaEnabled"]; + chkRemoteCommandsEnabled = (ICheckBox)view["chkRemoteCommandsEnabled"]; + chkHttpServerEnabled = (ICheckBox)view["chkHttpServerEnabled"]; + chkWebSocketEnabled = (ICheckBox)view["chkWebSocketEnabled"]; + chkTelemetryEnabled = (ICheckBox)view["chkTelemetryEnabled"]; + txtCharTag = (ITextBox)view["txtCharTag"]; + txtVTankPath = (ITextBox)view["txtVTankPath"]; + + // Hook up settings events + chkRareMetaEnabled.Change += OnRareMetaSettingChanged; + chkRemoteCommandsEnabled.Change += OnRemoteCommandsSettingChanged; + chkHttpServerEnabled.Change += OnHttpServerSettingChanged; + chkWebSocketEnabled.Change += OnWebSocketSettingChanged; + chkTelemetryEnabled.Change += OnTelemetrySettingChanged; + txtCharTag.Change += OnCharTagChanged; + txtVTankPath.Change += OnVTankPathChanged; + + // Load current settings + LoadCurrentSettings(); + } + + private void InitializeStatisticsTabControls() + { + try + { + // Statistics tab controls + lblDetailedKillsValue = (IStaticText)view["lblDetailedKillsValue"]; + lblBestHourValue = (IStaticText)view["lblBestHourValue"]; + lblAverageKillsValue = (IStaticText)view["lblAverageKillsValue"]; + lblRareStatsValue = (IStaticText)view["lblRareStatsValue"]; + lblRareRateValue = (IStaticText)view["lblRareRateValue"]; + lblSessionTimeValue = (IStaticText)view["lblSessionTimeValue"]; + lblLastKillValue = (IStaticText)view["lblLastKillValue"]; + btnResetStats = (IButton)view["btnResetStats"]; + + // Hook up statistics events + if (btnResetStats != null) + btnResetStats.Hit += OnResetStatsClick; + } + catch (Exception ex) + { + PluginCore.WriteToChat($"Error initializing statistics controls: {ex.Message}"); + } + } + + private void InitializeNavigationTabControls() + { + try + { + // Navigation tab controls + chkNavVisualizationEnabled = (ICheckBox)view["chkNavVisualizationEnabled"]; + cmbNavFiles = (ICombo)view["cmbNavFiles"]; + btnRefreshNavFiles = (IButton)view["btnRefreshNavFiles"]; + lblNavStatus = (IStaticText)view["lblNavStatus"]; + btnLoadRoute = (IButton)view["btnLoadRoute"]; + btnClearRoute = (IButton)view["btnClearRoute"]; + + // Hook up navigation events + if (chkNavVisualizationEnabled != null) + chkNavVisualizationEnabled.Change += OnNavVisualizationEnabledChanged; + if (btnRefreshNavFiles != null) + btnRefreshNavFiles.Hit += OnRefreshNavFilesClick; + if (btnLoadRoute != null) + btnLoadRoute.Hit += OnLoadRouteClick; + if (btnClearRoute != null) + btnClearRoute.Hit += OnClearRouteClick; + + // Initialize navigation system + InitializeNavigationSystem(); + } + catch (Exception ex) + { + PluginCore.WriteToChat($"Error initializing navigation controls: {ex.Message}"); + } + } + + + #region Event Handlers - Main Tab + // No main tab event handlers needed anymore - buttons removed + #endregion + + #region Event Handlers - Settings Tab + private void OnRareMetaSettingChanged(object sender, EventArgs e) + { + PluginSettings.Instance.RareMetaEnabled = chkRareMetaEnabled.Checked; + PluginCore.RareMetaEnabled = chkRareMetaEnabled.Checked; // Update the static property too + UpdateAutoLootRareIndicator(); + + // Add chat message like websockets + PluginCore.WriteToChat($"Rare meta automation {(chkRareMetaEnabled.Checked ? "ENABLED" : "DISABLED")}."); + } + + private void OnRemoteCommandsSettingChanged(object sender, EventArgs e) + { + PluginSettings.Instance.RemoteCommandsEnabled = chkRemoteCommandsEnabled.Checked; + } + + private void OnHttpServerSettingChanged(object sender, EventArgs e) + { + PluginSettings.Instance.HttpServerEnabled = chkHttpServerEnabled.Checked; + if (chkHttpServerEnabled.Checked) + HttpCommandServer.Start(); + else + HttpCommandServer.Stop(); + } + + private void OnWebSocketSettingChanged(object sender, EventArgs e) + { + PluginSettings.Instance.WebSocketEnabled = chkWebSocketEnabled.Checked; + if (chkWebSocketEnabled.Checked) + { + WebSocket.Start(); + PluginCore.WriteToChat("WebSocket streaming ENABLED."); + } + else + { + WebSocket.Stop(); + PluginCore.WriteToChat("WebSocket streaming DISABLED."); + } + UpdateWebSocketStatus(); + } + + private void OnTelemetrySettingChanged(object sender, EventArgs e) + { + PluginSettings.Instance.TelemetryEnabled = chkTelemetryEnabled.Checked; + if (chkTelemetryEnabled.Checked) + Telemetry.Start(); + else + Telemetry.Stop(); + } + + private void OnCharTagChanged(object sender, EventArgs e) + { + PluginSettings.Instance.CharTag = txtCharTag.Text; + } + + private void OnVTankPathChanged(object sender, EventArgs e) + { + PluginSettings.Instance.VTankProfilesPath = txtVTankPath.Text; + // Refresh navigation system with new path + if (PluginCore.navVisualization != null) + { + PluginCore.navVisualization.RefreshNavFileList(); + // Also refresh the dropdown list in Navigation tab + RefreshNavFileList(); + UpdateNavigationStatus(); + } + } + + // Settings save automatically via property setters - no manual save/reset buttons needed + + private void LoadCurrentSettings() + { + try + { + if (PluginSettings.Instance != null && chkRareMetaEnabled != null) + { + chkRareMetaEnabled.Checked = PluginSettings.Instance.RareMetaEnabled; + chkRemoteCommandsEnabled.Checked = PluginSettings.Instance.RemoteCommandsEnabled; + chkHttpServerEnabled.Checked = PluginSettings.Instance.HttpServerEnabled; + chkWebSocketEnabled.Checked = PluginSettings.Instance.WebSocketEnabled; + chkTelemetryEnabled.Checked = PluginSettings.Instance.TelemetryEnabled; + txtCharTag.Text = PluginSettings.Instance.CharTag ?? "default"; + txtVTankPath.Text = PluginSettings.Instance.VTankProfilesPath ?? ""; + } + } + catch (Exception ex) + { + PluginCore.WriteToChat($"Error loading settings: {ex.Message}"); + } + } + #endregion + + #region Event Handlers - Statistics Tab + private void OnResetStatsClick(object sender, EventArgs e) + { + // Reset stats functionality moved here from removed OnRestartClick + PluginCore.RestartStats(); + sessionStartTime = DateTime.Now; + bestHourlyKills = 0; + UpdateStatistics(); + } + #endregion + + #region Event Handlers - Navigation Tab + private void OnNavVisualizationEnabledChanged(object sender, EventArgs e) + { + if (PluginCore.navVisualization != null) + { + PluginCore.navVisualization.SetEnabled(chkNavVisualizationEnabled.Checked); + UpdateNavigationStatus(); + } + } + + private void OnRefreshNavFilesClick(object sender, EventArgs e) + { + RefreshNavFileList(); + } + + private void OnLoadRouteClick(object sender, EventArgs e) + { + try + { + if (cmbNavFiles != null && PluginCore.navVisualization != null && + cmbNavFiles.Selected >= 0 && cmbNavFiles.Count > 0 && + cmbNavFiles.Selected < cmbNavFiles.Count) + { + string selectedFile = cmbNavFiles.Text[cmbNavFiles.Selected]; + if (selectedFile != "None" && !string.IsNullOrEmpty(selectedFile)) + { + PluginCore.navVisualization.LoadRoute(selectedFile); + UpdateNavigationStatus(); + } + else + { + PluginCore.WriteToChat("[NavViz] Please select a valid nav file from the dropdown"); + } + } + else + { + PluginCore.WriteToChat("[NavViz] No nav files available. Click Refresh or configure VTank path in Settings tab"); + } + } + catch (Exception ex) + { + PluginCore.WriteToChat($"[NavViz] Error loading route: {ex.Message}"); + } + } + + private void OnClearRouteClick(object sender, EventArgs e) + { + if (PluginCore.navVisualization != null) + { + PluginCore.navVisualization.LoadRoute("None"); + if (cmbNavFiles != null) + { + cmbNavFiles.Selected = 0; // Select "None" which should be at index 0 + } + UpdateNavigationStatus(); + } + } + + private void InitializeNavigationSystem() + { + try + { + RefreshNavFileList(); + UpdateNavigationStatus(); + + // Subscribe to route changes + if (PluginCore.navVisualization != null) + { + PluginCore.navVisualization.RouteChanged += (s, e) => UpdateNavigationStatus(); + } + } + catch (Exception ex) + { + PluginCore.WriteToChat($"Error initializing navigation system: {ex.Message}"); + } + } + + private void RefreshNavFileList() + { + try + { + if (PluginCore.navVisualization != null && cmbNavFiles != null) + { + PluginCore.navVisualization.RefreshNavFileList(); + + cmbNavFiles.Clear(); + cmbNavFiles.Add("None"); + + foreach (string navFile in PluginCore.navVisualization.AvailableNavFiles) + { + PluginCore.WriteToChat($"[NavViz UI] Adding to dropdown: {navFile}"); + cmbNavFiles.Add(navFile); + } + + if (cmbNavFiles.Count > 0) + cmbNavFiles.Selected = 0; // Select "None" which should be at index 0 + + PluginCore.WriteToChat($"[NavViz UI] Dropdown populated with {cmbNavFiles.Count} items (including 'None')"); + } + } + catch (Exception ex) + { + PluginCore.WriteToChat($"Error refreshing nav file list: {ex.Message}"); + } + } + + private void UpdateNavigationStatus() + { + try + { + if (lblNavStatus != null && PluginCore.navVisualization != null) + { + lblNavStatus.Text = $"Status: {PluginCore.navVisualization.GetStatus()}"; + } + } + catch (Exception ex) + { + PluginCore.WriteToChat($"Error updating navigation status: {ex.Message}"); + } + } + #endregion + + + #region Position Management + private void CheckPositionChanged(object sender, ElapsedEventArgs e) + { + try + { + if (view != null) + { + Point currentPosition = view.Location; + if (currentPosition != lastKnownPosition) + { + lastKnownPosition = currentPosition; + if (positionSaveTimer != null) + { + positionSaveTimer.Stop(); + positionSaveTimer.Start(); + } + } + } + } + catch + { + // Ignore errors in position checking + } + } + + protected override void SaveWindowPosition() + { + try + { + if (view != null && PluginSettings.Instance != null) + { + PluginSettings.Instance.MainWindowX = view.Location.X; + PluginSettings.Instance.MainWindowY = view.Location.Y; + } + } + catch (Exception ex) + { + PluginCore.WriteToChat($"Error saving window position: {ex.Message}"); + } + } + + protected override void RestoreWindowPosition() + { + try + { + if (view != null && PluginSettings.Instance != null) + { + view.Location = new Point( + PluginSettings.Instance.MainWindowX, + PluginSettings.Instance.MainWindowY + ); + } + } + catch (Exception ex) + { + PluginCore.WriteToChat($"Error restoring window position: {ex.Message}"); + } + } + #endregion + + #region Public Update Methods (maintain compatibility with existing code) + public static void UpdateKillStats(int totalKills, double killsPer5Min, double killsPerHour) + { + try + { + if (instance?.lblTotalKills != null) + { + instance.lblTotalKills.Text = $"Total Kills: {totalKills}"; + } + if (instance?.lblKillsPer5Min != null) + { + instance.lblKillsPer5Min.Text = $"Kills per 5 Min: {killsPer5Min:F2}"; + } + if (instance?.lblKillsPerHour != null) + { + instance.lblKillsPerHour.Text = $"Kills per Hour: {killsPerHour:F2}"; + } + + // Update detailed statistics + if (instance?.lblDetailedKillsValue != null) + { + instance.lblDetailedKillsValue.Text = totalKills.ToString(); + } + if (instance?.lblAverageKillsValue != null) + { + instance.lblAverageKillsValue.Text = $"{killsPerHour:F2} kills/hr"; + } + + // Track best hourly performance + if (instance != null && killsPerHour > instance.bestHourlyKills) + { + instance.bestHourlyKills = killsPerHour; + if (instance.lblBestHourValue != null) + { + instance.lblBestHourValue.Text = $"{instance.bestHourlyKills:F2} kills/hr"; + } + } + + // Update status and auto loot indicator + instance?.UpdateStatus(); + instance?.UpdateAutoLootRareIndicator(); + instance?.UpdateWebSocketStatus(); + + instance?.UpdateStatistics(); + } + catch (Exception ex) + { + PluginCore.WriteToChat($"Error in UpdateKillStats: {ex.Message}"); + } + } + + public static void UpdateElapsedTime(TimeSpan elapsed) + { + try + { + if (instance?.lblElapsedTime != null) + { + int days = elapsed.Days; + int hours = elapsed.Hours; + int minutes = elapsed.Minutes; + int seconds = elapsed.Seconds; + + if (days > 0) + instance.lblElapsedTime.Text = $"Time: {days}d {hours:D2}:{minutes:D2}:{seconds:D2}"; + else + instance.lblElapsedTime.Text = $"Time: {hours:D2}:{minutes:D2}:{seconds:D2}"; + } + + if (instance?.lblSessionTimeValue != null && instance?.lblElapsedTime != null) + { + instance.lblSessionTimeValue.Text = instance.lblElapsedTime.Text.Replace("Time: ", ""); + } + } + catch (Exception ex) + { + PluginCore.WriteToChat($"Error in UpdateElapsedTime: {ex.Message}"); + } + } + + public static void UpdateRareCount(int rareCount) + { + try + { + if (instance?.lblRareCount != null) + { + instance.lblRareCount.Text = $"Rare Count: {rareCount}"; + } + if (instance?.lblRareStatsValue != null) + { + instance.lblRareStatsValue.Text = rareCount.ToString(); + } + + // Calculate rare drop rate + int totalKills = PluginCore.totalKills; + if (totalKills > 0 && instance?.lblRareRateValue != null) + { + double rareRate = (double)rareCount / totalKills * 100; + instance.lblRareRateValue.Text = $"{rareRate:F2}%"; + } + } + catch (Exception ex) + { + PluginCore.WriteToChat($"Error in UpdateRareCount: {ex.Message}"); + } + } + + public static void SetRareMetaToggleState(bool enabled) + { + if (instance?.chkRareMetaEnabled != null) + { + instance.chkRareMetaEnabled.Checked = enabled; + } + instance?.UpdateAutoLootRareIndicator(); + } + + public static void RefreshSettingsFromConfig() + { + // Call this after settings are loaded to refresh UI + instance?.LoadCurrentSettings(); + instance?.UpdateAutoLootRareIndicator(); + instance?.UpdateWebSocketStatus(); + } + + private void UpdateStatus() + { + try + { + if (lblStatus != null) + { + string metaState = VtankControl.VtGetMetaState(); + lblStatus.Text = $"Meta State: {metaState}"; + } + } + catch + { + if (lblStatus != null) + lblStatus.Text = "Meta State: Unknown"; + } + } + + private void UpdateAutoLootRareIndicator() + { + try + { + if (lblAutoLootRare != null) + { + bool isEnabled = PluginSettings.Instance?.RareMetaEnabled == true; + if (isEnabled) + { + lblAutoLootRare.Text = "Auto Loot Rare: [ON]"; + } + else + { + lblAutoLootRare.Text = "Auto Loot Rare: [OFF]"; + } + } + } + catch + { + if (lblAutoLootRare != null) + lblAutoLootRare.Text = "Auto Loot Rare: Unknown"; + } + } + + private void UpdateWebSocketStatus() + { + try + { + if (lblWebSocketStatus != null) + { + bool isConnected = PluginSettings.Instance?.WebSocketEnabled == true; + if (isConnected) + { + lblWebSocketStatus.Text = "WebSocket: [CONNECTED]"; + } + else + { + lblWebSocketStatus.Text = "WebSocket: [DISCONNECTED]"; + } + } + } + catch + { + if (lblWebSocketStatus != null) + lblWebSocketStatus.Text = "WebSocket: Unknown"; + } + } + #endregion + + private void UpdateStatistics() + { + try + { + // Update last kill time + if (lblLastKillValue != null) + { + if (PluginCore.lastKillTime != DateTime.MinValue) + { + TimeSpan timeSinceLastKill = DateTime.Now - PluginCore.lastKillTime; + if (timeSinceLastKill.TotalMinutes < 60) + lblLastKillValue.Text = $"{timeSinceLastKill.TotalMinutes:F0} min ago"; + else + lblLastKillValue.Text = $"{timeSinceLastKill.TotalHours:F1} hr ago"; + } + else + { + lblLastKillValue.Text = "Never"; + } + } + } + catch (Exception ex) + { + PluginCore.WriteToChat($"Error in UpdateStatistics: {ex.Message}"); + } + } + + public static void ViewDestroy() + { + try + { + if (instance != null) + { + // Save final position before destruction + instance.SaveWindowPosition(); + + // No main tab event handlers to clean up - buttons removed + + // Clean up event handlers - Settings tab + if (instance.chkRareMetaEnabled != null) + instance.chkRareMetaEnabled.Change -= instance.OnRareMetaSettingChanged; + if (instance.chkRemoteCommandsEnabled != null) + instance.chkRemoteCommandsEnabled.Change -= instance.OnRemoteCommandsSettingChanged; + if (instance.chkHttpServerEnabled != null) + instance.chkHttpServerEnabled.Change -= instance.OnHttpServerSettingChanged; + if (instance.chkWebSocketEnabled != null) + instance.chkWebSocketEnabled.Change -= instance.OnWebSocketSettingChanged; + if (instance.chkTelemetryEnabled != null) + instance.chkTelemetryEnabled.Change -= instance.OnTelemetrySettingChanged; + if (instance.txtCharTag != null) + instance.txtCharTag.Change -= instance.OnCharTagChanged; + if (instance.txtVTankPath != null) + instance.txtVTankPath.Change -= instance.OnVTankPathChanged; + + // Clean up event handlers - Statistics tab + if (instance.btnResetStats != null) + instance.btnResetStats.Hit -= instance.OnResetStatsClick; + + // Clean up event handlers - Navigation tab + if (instance.chkNavVisualizationEnabled != null) + instance.chkNavVisualizationEnabled.Change -= instance.OnNavVisualizationEnabledChanged; + if (instance.btnRefreshNavFiles != null) + instance.btnRefreshNavFiles.Hit -= instance.OnRefreshNavFilesClick; + if (instance.btnLoadRoute != null) + instance.btnLoadRoute.Hit -= instance.OnLoadRouteClick; + if (instance.btnClearRoute != null) + instance.btnClearRoute.Hit -= instance.OnClearRouteClick; + + // Clean up timers + if (instance.positionSaveTimer != null) + { + instance.positionSaveTimer.Stop(); + instance.positionSaveTimer.Dispose(); + instance.positionSaveTimer = null; + } + if (instance.positionCheckTimer != null) + { + instance.positionCheckTimer.Stop(); + instance.positionCheckTimer.Dispose(); + instance.positionCheckTimer = null; + } + + instance.Dispose(); + instance = null; + PluginCore.WriteToChat("Enhanced tabbed view destroyed."); + } + } + catch (Exception ex) + { + PluginCore.WriteToChat("Error destroying tabbed view: " + ex.Message); + } + } + } +} \ No newline at end of file diff --git a/MosswartMassacre/Views/VVSBaseView.cs b/MosswartMassacre/Views/VVSBaseView.cs deleted file mode 100644 index e5adc2d..0000000 --- a/MosswartMassacre/Views/VVSBaseView.cs +++ /dev/null @@ -1,395 +0,0 @@ -using System; -using System.Drawing; -using System.Runtime.InteropServices; -using System.Timers; -using VirindiViewService; -using VirindiViewService.XMLParsers; - -namespace MosswartMassacre.Views -{ - /// - /// Base class for VVS (VirindiViewService) based views. - /// Replaces the wrapper-based BaseView with direct VVS integration. - /// - public class VVSBaseView : IDisposable - { - #region Windows API for boundary checking - [StructLayout(LayoutKind.Sequential)] - public struct RECT - { - public int Left; - public int Top; - public int Right; - public int Bottom; - - public int Width { get { return Right - Left; } } - public int Height { get { return Bottom - Top; } } - } - - [DllImport("user32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect); - #endregion - - #region Core VVS Components - protected HudView view; - protected ViewProperties properties; - protected ControlGroup controls; - protected PluginCore pluginCore; - #endregion - - #region Position Management - private Timer positionSaveTimer; - #endregion - - public VVSBaseView(PluginCore core) - { - pluginCore = core; - InitializePositionTimer(); - } - - #region VVS Initialization - protected void CreateFromXMLResource(string resourcePath, bool doIcon = true, bool doTitle = true) - { - try - { - // Parse XML using VVS Decal3XMLParser - new Decal3XMLParser().ParseFromResource(resourcePath, out properties, out controls); - - // Set window properties - if (doTitle) - { - properties.Title = "Mosswart Massacre v3.0"; - } - - if (doIcon) - { - // Use default icon for now - can be customized later - properties.Icon = 7735; // Same icon as in XML - } - - // Create the HudView - view = new HudView(properties, controls); - - // Subscribe to essential events - view.VisibleChanged += View_VisibleChanged; - view.Moved += View_Moved; - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error creating VVS view from {resourcePath}: {ex.Message}"); - PluginCore.WriteToChat($"Stack trace: {ex.StackTrace}"); - } - } - - protected void CreateFromXMLString(string xmlString, bool doIcon = true, bool doTitle = true) - { - try - { - // Parse XML string using VVS Decal3XMLParser - new Decal3XMLParser().Parse(xmlString, out properties, out controls); - - if (doTitle) - { - properties.Title = "Mosswart Massacre v3.0"; - } - - if (doIcon) - { - properties.Icon = 7735; - } - - view = new HudView(properties, controls); - view.VisibleChanged += View_VisibleChanged; - view.Moved += View_Moved; - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error creating VVS view from XML string: {ex.Message}"); - } - } - #endregion - - #region Control Access - /// - /// Get a control by name with proper type casting. - /// Usage: var button = GetControl<HudButton>("btnExample"); - /// - protected T GetControl(string controlName) where T : class - { - try - { - if (view != null && view[controlName] != null) - { - return view[controlName] as T; - } - return null; - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error getting control '{controlName}': {ex.Message}"); - return null; - } - } - - /// - /// Get a control by name (alternative syntax) - /// Usage: var button = (HudButton)GetControl("btnExample"); - /// - protected object GetControl(string controlName) - { - try - { - return view?[controlName]; - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error getting control '{controlName}': {ex.Message}"); - return null; - } - } - #endregion - - #region Window Management - protected virtual void SaveWindowPosition() - { - try - { - if (view != null && PluginSettings.Instance != null) - { - PluginSettings.Instance.MainWindowX = view.Location.X; - PluginSettings.Instance.MainWindowY = view.Location.Y; - } - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error saving window position: {ex.Message}"); - } - } - - protected virtual void RestoreWindowPosition() - { - try - { - if (view != null && PluginSettings.Instance != null) - { - view.Location = new Point( - PluginSettings.Instance.MainWindowX, - PluginSettings.Instance.MainWindowY - ); - } - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error restoring window position: {ex.Message}"); - } - } - - protected void KeepWindowInBounds() - { - try - { - if (view == null) return; - - RECT rect = new RECT(); - IntPtr gameWindowHandle = PluginCore.MyHost?.Decal?.Hwnd ?? IntPtr.Zero; - - if (gameWindowHandle != IntPtr.Zero && GetWindowRect(gameWindowHandle, ref rect)) - { - Point currentLocation = view.Location; - int viewWidth = view.Width; - int viewHeight = view.Height; - - bool needsUpdate = false; - - // Check right boundary - if (currentLocation.X + viewWidth > rect.Width) - { - currentLocation.X = rect.Width - viewWidth; - needsUpdate = true; - } - // Check left boundary - else if (currentLocation.X < 0) - { - currentLocation.X = 20; - needsUpdate = true; - } - - // Check bottom boundary - if (currentLocation.Y + viewHeight > rect.Height) - { - currentLocation.Y = rect.Height - viewHeight; - needsUpdate = true; - } - // Check top boundary - else if (currentLocation.Y < 0) - { - currentLocation.Y = 20; - needsUpdate = true; - } - - if (needsUpdate) - { - view.Location = currentLocation; - } - } - } - catch - { - // Silently ignore boundary check errors - } - } - #endregion - - #region Position Timer Management - private void InitializePositionTimer() - { - positionSaveTimer = new Timer(2000); // 2 second delay after movement stops - positionSaveTimer.Elapsed += (s, e) => { - SaveWindowPosition(); - positionSaveTimer.Stop(); - }; - } - - private void View_Moved(object sender, EventArgs e) - { - try - { - // Reset timer when window moves - if (positionSaveTimer != null) - { - if (positionSaveTimer.Enabled) - positionSaveTimer.Stop(); - - positionSaveTimer.Start(); - } - } - catch - { - // Ignore timer errors - } - } - - private void View_VisibleChanged(object sender, EventArgs e) - { - try - { - if (view.Visible) - { - KeepWindowInBounds(); - } - } - catch - { - // Ignore visibility change errors - } - } - #endregion - - #region Public Interface - public virtual void Initialize() - { - try - { - RestoreWindowPosition(); - KeepWindowInBounds(); - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error initializing VVS view: {ex.Message}"); - } - } - - public virtual void Show() - { - if (view != null) - { - view.Visible = true; - } - } - - public virtual void Hide() - { - if (view != null) - { - view.Visible = false; - } - } - - public virtual void Toggle() - { - if (view != null) - { - view.Visible = !view.Visible; - } - } - - public bool IsVisible - { - get { return view?.Visible ?? false; } - } - - public Point Location - { - get { return view?.Location ?? Point.Empty; } - set { if (view != null) view.Location = value; } - } - - public Size Size - { - get { return view != null ? new Size(view.Width, view.Height) : Size.Empty; } - } - #endregion - - #region IDisposable Support - private bool disposedValue = false; - - protected virtual void Dispose(bool disposing) - { - if (!disposedValue) - { - if (disposing) - { - try - { - // Save final position before disposal - SaveWindowPosition(); - - // Clean up timers - if (positionSaveTimer != null) - { - positionSaveTimer.Stop(); - positionSaveTimer.Dispose(); - positionSaveTimer = null; - } - - // Clean up VVS view - if (view != null) - { - view.VisibleChanged -= View_VisibleChanged; - view.Moved -= View_Moved; - view.Dispose(); - view = null; - } - - // Clean up VVS objects - properties = null; - controls = null; - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error disposing VVS view: {ex.Message}"); - } - } - disposedValue = true; - } - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - #endregion - } -} \ No newline at end of file diff --git a/MosswartMassacre/Views/VVSTabbedMainView.cs b/MosswartMassacre/Views/VVSTabbedMainView.cs deleted file mode 100644 index 0c13665..0000000 --- a/MosswartMassacre/Views/VVSTabbedMainView.cs +++ /dev/null @@ -1,845 +0,0 @@ -using System; -using System.Drawing; -using System.Timers; -using VirindiViewService.Controls; - -namespace MosswartMassacre.Views -{ - /// - /// VVS-based tabbed main view - direct VirindiViewService integration. - /// Replaces wrapper-based TabbedMainView with modern VVS controls. - /// - internal class VVSTabbedMainView : VVSBaseView - { - private static VVSTabbedMainView instance; - - #region Main Tab Controls - private HudStaticText lblTotalKills; - private HudStaticText lblKillsPer5Min; - private HudStaticText lblKillsPerHour; - private HudStaticText lblElapsedTime; - private HudStaticText lblRareCount; - private HudStaticText lblAutoLootRare; - private HudStaticText lblStatus; - private HudStaticText lblWebSocketStatus; - #endregion - - #region Settings Tab Controls - private HudCheckBox chkRareMetaEnabled; - private HudCheckBox chkRemoteCommandsEnabled; - private HudCheckBox chkHttpServerEnabled; - private HudCheckBox chkWebSocketEnabled; - private HudCheckBox chkTelemetryEnabled; - private HudTextBox txtCharTag; - private HudTextBox txtVTankPath; - #endregion - - #region Statistics Tab Controls - private HudStaticText lblDetailedKillsValue; - private HudStaticText lblBestHourValue; - private HudStaticText lblAverageKillsValue; - private HudStaticText lblRareStatsValue; - private HudStaticText lblRareRateValue; - private HudStaticText lblSessionTimeValue; - private HudStaticText lblLastKillValue; - private HudButton btnResetStats; - #endregion - - #region Navigation Tab Controls (Basic) - private HudCheckBox chkNavVisualizationEnabled; - private HudCombo cmbNavFiles; - private HudButton btnRefreshNavFiles; - private HudStaticText lblNavStatus; - private HudButton btnLoadRoute; - private HudButton btnClearRoute; - #endregion - - #region Statistics Tracking - private double bestHourlyKills = 0; - private DateTime sessionStartTime; - #endregion - - public VVSTabbedMainView(PluginCore core) : base(core) - { - instance = this; - sessionStartTime = DateTime.Now; - } - - #region Static Interface Methods - public static void ViewInit() - { - try - { - if (instance == null) - { - instance = new VVSTabbedMainView(null); - } - instance.InitializeView(); - } - catch (Exception ex) - { - PluginCore.WriteToChat("Error initializing VVS tabbed view: " + ex.Message); - } - } - - public static void ViewDestroy() - { - try - { - if (instance != null) - { - instance.Dispose(); - instance = null; - } - } - catch (Exception ex) - { - PluginCore.WriteToChat("Error destroying VVS tabbed view: " + ex.Message); - } - } - #endregion - - #region Initialization - private void InitializeView() - { - try - { - // Create view from working original XML layout - CreateFromXMLResource("MosswartMassacre.ViewXML.mainViewTabbed.xml"); - - // Initialize all tab controls - InitializeMainTabControls(); - InitializeSettingsTabControls(); - InitializeStatisticsTabControls(); - InitializeNavigationTabControls(); - - // Initialize the base view and set initial position - Initialize(); - - // Make the view visible and show in plugin bar - if (view != null) - { - view.Visible = true; - view.ShowInBar = true; - } - } - catch (Exception ex) - { - PluginCore.WriteToChat("Error in VVS InitializeView: " + ex.Message); - } - } - - private void InitializeMainTabControls() - { - try - { - // Main tab - existing functionality using VVS controls - lblTotalKills = GetControl("lblTotalKills"); - lblKillsPer5Min = GetControl("lblKillsPer5Min"); - lblKillsPerHour = GetControl("lblKillsPerHour"); - lblElapsedTime = GetControl("lblElapsedTime"); - lblRareCount = GetControl("lblRareCount"); - lblAutoLootRare = GetControl("lblAutoLootRare"); - lblStatus = GetControl("lblStatus"); - lblWebSocketStatus = GetControl("lblWebSocketStatus"); - - // Update status displays immediately - UpdateStatus(); - UpdateAutoLootRareIndicator(); - UpdateWebSocketStatus(); - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error initializing main tab controls: {ex.Message}"); - } - } - - private void InitializeSettingsTabControls() - { - try - { - // Settings tab controls - chkRareMetaEnabled = GetControl("chkRareMetaEnabled"); - chkRemoteCommandsEnabled = GetControl("chkRemoteCommandsEnabled"); - chkHttpServerEnabled = GetControl("chkHttpServerEnabled"); - chkWebSocketEnabled = GetControl("chkWebSocketEnabled"); - chkTelemetryEnabled = GetControl("chkTelemetryEnabled"); - txtCharTag = GetControl("txtCharTag"); - txtVTankPath = GetControl("txtVTankPath"); - - // Hook up settings events - if (chkRareMetaEnabled != null) - chkRareMetaEnabled.Change += OnRareMetaSettingChanged; - if (chkRemoteCommandsEnabled != null) - chkRemoteCommandsEnabled.Change += OnRemoteCommandsSettingChanged; - if (chkHttpServerEnabled != null) - chkHttpServerEnabled.Change += OnHttpServerSettingChanged; - if (chkWebSocketEnabled != null) - chkWebSocketEnabled.Change += OnWebSocketSettingChanged; - if (chkTelemetryEnabled != null) - chkTelemetryEnabled.Change += OnTelemetrySettingChanged; - if (txtCharTag != null) - txtCharTag.Change += OnCharTagChanged; - if (txtVTankPath != null) - txtVTankPath.Change += OnVTankPathChanged; - - // Load current settings - LoadCurrentSettings(); - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error initializing settings tab controls: {ex.Message}"); - } - } - - private void InitializeStatisticsTabControls() - { - try - { - // Statistics tab controls - lblDetailedKillsValue = GetControl("lblDetailedKillsValue"); - lblBestHourValue = GetControl("lblBestHourValue"); - lblAverageKillsValue = GetControl("lblAverageKillsValue"); - lblRareStatsValue = GetControl("lblRareStatsValue"); - lblRareRateValue = GetControl("lblRareRateValue"); - lblSessionTimeValue = GetControl("lblSessionTimeValue"); - lblLastKillValue = GetControl("lblLastKillValue"); - btnResetStats = GetControl("btnResetStats"); - - // Hook up statistics events - if (btnResetStats != null) - btnResetStats.Hit += OnResetStatsClick; - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error initializing statistics controls: {ex.Message}"); - } - } - - private void InitializeNavigationTabControls() - { - try - { - // Basic navigation tab controls - chkNavVisualizationEnabled = GetControl("chkNavVisualizationEnabled"); - cmbNavFiles = GetControl("cmbNavFiles"); - btnRefreshNavFiles = GetControl("btnRefreshNavFiles"); - lblNavStatus = GetControl("lblNavStatus"); - btnLoadRoute = GetControl("btnLoadRoute"); - btnClearRoute = GetControl("btnClearRoute"); - - // Hook up basic navigation events - if (chkNavVisualizationEnabled != null) - chkNavVisualizationEnabled.Change += OnNavVisualizationEnabledChanged; - if (btnRefreshNavFiles != null) - btnRefreshNavFiles.Hit += OnRefreshNavFilesClick; - if (btnLoadRoute != null) - btnLoadRoute.Hit += OnLoadRouteClick; - if (btnClearRoute != null) - btnClearRoute.Hit += OnClearRouteClick; - - // Initialize navigation system - InitializeNavigationSystem(); - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error initializing navigation controls: {ex.Message}"); - } - } - #endregion - - #region Event Handlers - Settings Tab - private void OnRareMetaSettingChanged(object sender, EventArgs e) - { - try - { - PluginSettings.Instance.RareMetaEnabled = chkRareMetaEnabled.Checked; - PluginCore.RareMetaEnabled = chkRareMetaEnabled.Checked; - UpdateAutoLootRareIndicator(); - PluginCore.WriteToChat($"Rare meta automation {(chkRareMetaEnabled.Checked ? "ENABLED" : "DISABLED")}."); - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error in rare meta setting change: {ex.Message}"); - } - } - - private void OnRemoteCommandsSettingChanged(object sender, EventArgs e) - { - try - { - PluginSettings.Instance.RemoteCommandsEnabled = chkRemoteCommandsEnabled.Checked; - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error in remote commands setting change: {ex.Message}"); - } - } - - private void OnHttpServerSettingChanged(object sender, EventArgs e) - { - try - { - PluginSettings.Instance.HttpServerEnabled = chkHttpServerEnabled.Checked; - if (chkHttpServerEnabled.Checked) - HttpCommandServer.Start(); - else - HttpCommandServer.Stop(); - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error in HTTP server setting change: {ex.Message}"); - } - } - - private void OnWebSocketSettingChanged(object sender, EventArgs e) - { - try - { - PluginSettings.Instance.WebSocketEnabled = chkWebSocketEnabled.Checked; - if (chkWebSocketEnabled.Checked) - { - WebSocket.Start(); - PluginCore.WriteToChat("WebSocket streaming ENABLED."); - } - else - { - WebSocket.Stop(); - PluginCore.WriteToChat("WebSocket streaming DISABLED."); - } - UpdateWebSocketStatus(); - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error in WebSocket setting change: {ex.Message}"); - } - } - - private void OnTelemetrySettingChanged(object sender, EventArgs e) - { - try - { - PluginSettings.Instance.TelemetryEnabled = chkTelemetryEnabled.Checked; - if (chkTelemetryEnabled.Checked) - Telemetry.Start(); - else - Telemetry.Stop(); - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error in telemetry setting change: {ex.Message}"); - } - } - - private void OnCharTagChanged(object sender, EventArgs e) - { - try - { - PluginSettings.Instance.CharTag = txtCharTag.Text; - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error in char tag change: {ex.Message}"); - } - } - - private void OnVTankPathChanged(object sender, EventArgs e) - { - try - { - PluginSettings.Instance.VTankProfilesPath = txtVTankPath.Text; - // Refresh navigation system with new path - if (PluginCore.navVisualization != null) - { - PluginCore.navVisualization.RefreshNavFileList(); - RefreshNavFileList(); - UpdateNavigationStatus(); - } - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error in VTank path change: {ex.Message}"); - } - } - - private void LoadCurrentSettings() - { - try - { - if (PluginSettings.Instance != null) - { - if (chkRareMetaEnabled != null) - chkRareMetaEnabled.Checked = PluginSettings.Instance.RareMetaEnabled; - if (chkRemoteCommandsEnabled != null) - chkRemoteCommandsEnabled.Checked = PluginSettings.Instance.RemoteCommandsEnabled; - if (chkHttpServerEnabled != null) - chkHttpServerEnabled.Checked = PluginSettings.Instance.HttpServerEnabled; - if (chkWebSocketEnabled != null) - chkWebSocketEnabled.Checked = PluginSettings.Instance.WebSocketEnabled; - if (chkTelemetryEnabled != null) - chkTelemetryEnabled.Checked = PluginSettings.Instance.TelemetryEnabled; - if (txtCharTag != null) - txtCharTag.Text = PluginSettings.Instance.CharTag ?? "default"; - if (txtVTankPath != null) - txtVTankPath.Text = PluginSettings.Instance.VTankProfilesPath ?? ""; - } - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error loading settings: {ex.Message}"); - } - } - #endregion - - #region Event Handlers - Statistics Tab - private void OnResetStatsClick(object sender, EventArgs e) - { - try - { - PluginCore.RestartStats(); - sessionStartTime = DateTime.Now; - bestHourlyKills = 0; - UpdateStatistics(); - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error resetting stats: {ex.Message}"); - } - } - #endregion - - #region Event Handlers - Navigation Tab - private void OnNavVisualizationEnabledChanged(object sender, EventArgs e) - { - try - { - if (PluginCore.navVisualization != null) - { - PluginCore.navVisualization.SetEnabled(chkNavVisualizationEnabled.Checked); - UpdateNavigationStatus(); - } - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error in nav visualization change: {ex.Message}"); - } - } - - private void OnRefreshNavFilesClick(object sender, EventArgs e) - { - try - { - RefreshNavFileList(); - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error refreshing nav files: {ex.Message}"); - } - } - - private void OnLoadRouteClick(object sender, EventArgs e) - { - try - { - // Load from dropdown selection - string routeName = null; - - if (cmbNavFiles != null && cmbNavFiles.Current >= 0 && cmbNavFiles.Count > 0 && - cmbNavFiles.Current < cmbNavFiles.Count) - { - routeName = ((HudStaticText)cmbNavFiles[cmbNavFiles.Current]).Text; - } - - if (!string.IsNullOrEmpty(routeName) && routeName != "None") - { - PluginCore.navVisualization.LoadRoute(routeName); - UpdateNavigationStatus(); - } - else - { - PluginCore.WriteToChat("[NavViz] Please select a route from the dropdown"); - } - } - catch (Exception ex) - { - PluginCore.WriteToChat($"[NavViz] Error loading route: {ex.Message}"); - } - } - - private void OnClearRouteClick(object sender, EventArgs e) - { - try - { - if (PluginCore.navVisualization != null) - { - PluginCore.navVisualization.LoadRoute("None"); - // Clear dropdown selection - if (cmbNavFiles != null && cmbNavFiles.Count > 0) - { - cmbNavFiles.Current = 0; // Select "None" which should be at index 0 - } - UpdateNavigationStatus(); - } - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error clearing route: {ex.Message}"); - } - } - - private void InitializeNavigationSystem() - { - try - { - RefreshNavFileList(); - UpdateNavigationStatus(); - - // Subscribe to route changes - if (PluginCore.navVisualization != null) - { - PluginCore.navVisualization.RouteChanged += (s, e) => UpdateNavigationStatus(); - } - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error initializing navigation system: {ex.Message}"); - } - } - - private void RefreshNavFileList() - { - try - { - if (PluginCore.navVisualization != null) - { - PluginCore.navVisualization.RefreshNavFileList(); - - // Update the basic dropdown - PopulateNavFileDropdown(); - - } - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error refreshing nav file list: {ex.Message}"); - } - } - - private void UpdateNavigationStatus() - { - try - { - if (lblNavStatus != null && PluginCore.navVisualization != null) - { - lblNavStatus.Text = $"Status: {PluginCore.navVisualization.GetStatus()}"; - } - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error updating navigation status: {ex.Message}"); - } - } - #endregion - - #region Public Update Methods (maintain compatibility with existing code) - public static void UpdateKillStats(int totalKills, double killsPer5Min, double killsPerHour) - { - try - { - if (instance == null) return; - - if (instance.lblTotalKills != null) - instance.lblTotalKills.Text = $"Total Kills: {totalKills}"; - if (instance.lblKillsPer5Min != null) - instance.lblKillsPer5Min.Text = $"Kills per 5 Min: {killsPer5Min:F2}"; - if (instance.lblKillsPerHour != null) - instance.lblKillsPerHour.Text = $"Kills per Hour: {killsPerHour:F2}"; - - // Update detailed statistics - if (instance.lblDetailedKillsValue != null) - instance.lblDetailedKillsValue.Text = totalKills.ToString(); - if (instance.lblAverageKillsValue != null) - instance.lblAverageKillsValue.Text = $"{killsPerHour:F2} kills/hr"; - - // Track best hourly performance - if (killsPerHour > instance.bestHourlyKills) - { - instance.bestHourlyKills = killsPerHour; - if (instance.lblBestHourValue != null) - instance.lblBestHourValue.Text = $"{instance.bestHourlyKills:F2} kills/hr"; - } - - // Update status displays - instance.UpdateStatus(); - instance.UpdateAutoLootRareIndicator(); - instance.UpdateWebSocketStatus(); - instance.UpdateStatistics(); - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error in UpdateKillStats: {ex.Message}"); - } - } - - public static void UpdateElapsedTime(TimeSpan elapsed) - { - try - { - if (instance?.lblElapsedTime != null) - { - int days = elapsed.Days; - int hours = elapsed.Hours; - int minutes = elapsed.Minutes; - int seconds = elapsed.Seconds; - - if (days > 0) - instance.lblElapsedTime.Text = $"Time: {days}d {hours:D2}:{minutes:D2}:{seconds:D2}"; - else - instance.lblElapsedTime.Text = $"Time: {hours:D2}:{minutes:D2}:{seconds:D2}"; - } - - if (instance?.lblSessionTimeValue != null && instance?.lblElapsedTime != null) - { - instance.lblSessionTimeValue.Text = instance.lblElapsedTime.Text.Replace("Time: ", ""); - } - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error in UpdateElapsedTime: {ex.Message}"); - } - } - - public static void UpdateRareCount(int rareCount) - { - try - { - if (instance?.lblRareCount != null) - instance.lblRareCount.Text = $"Rare Count: {rareCount}"; - if (instance?.lblRareStatsValue != null) - instance.lblRareStatsValue.Text = rareCount.ToString(); - - // Calculate rare drop rate - int totalKills = PluginCore.totalKills; - if (totalKills > 0 && instance?.lblRareRateValue != null) - { - double rareRate = (double)rareCount / totalKills * 100; - instance.lblRareRateValue.Text = $"{rareRate:F2}%"; - } - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error in UpdateRareCount: {ex.Message}"); - } - } - - public static void SetRareMetaToggleState(bool enabled) - { - try - { - if (instance?.chkRareMetaEnabled != null) - instance.chkRareMetaEnabled.Checked = enabled; - instance?.UpdateAutoLootRareIndicator(); - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error setting rare meta toggle state: {ex.Message}"); - } - } - - public static void RefreshSettingsFromConfig() - { - try - { - instance?.LoadCurrentSettings(); - instance?.UpdateAutoLootRareIndicator(); - instance?.UpdateWebSocketStatus(); - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error refreshing settings from config: {ex.Message}"); - } - } - #endregion - - #region Status Update Methods - private void UpdateStatus() - { - try - { - if (lblStatus != null) - { - string metaState = VtankControl.VtGetMetaState(); - lblStatus.Text = $"Meta State: {metaState}"; - } - } - catch - { - if (lblStatus != null) - lblStatus.Text = "Meta State: Unknown"; - } - } - - private void UpdateAutoLootRareIndicator() - { - try - { - if (lblAutoLootRare != null) - { - bool isEnabled = PluginSettings.Instance?.RareMetaEnabled == true; - if (isEnabled) - { - lblAutoLootRare.Text = "Auto Loot Rare: [ON]"; - // Try to set green color for ON status - try { lblAutoLootRare.TextColor = System.Drawing.Color.FromArgb(0, 128, 0); } catch { } - } - else - { - lblAutoLootRare.Text = "Auto Loot Rare: [OFF]"; - // Try to set red color for OFF status - try { lblAutoLootRare.TextColor = System.Drawing.Color.FromArgb(255, 0, 0); } catch { } - } - } - } - catch - { - if (lblAutoLootRare != null) - lblAutoLootRare.Text = "Auto Loot Rare: Unknown"; - } - } - - private void UpdateWebSocketStatus() - { - try - { - if (lblWebSocketStatus != null) - { - bool isConnected = PluginSettings.Instance?.WebSocketEnabled == true; - if (isConnected) - { - lblWebSocketStatus.Text = "WebSocket: [CONNECTED]"; - // Try to set green color for CONNECTED status - try { lblWebSocketStatus.TextColor = System.Drawing.Color.FromArgb(0, 128, 0); } catch { } - } - else - { - lblWebSocketStatus.Text = "WebSocket: [DISCONNECTED]"; - // Try to set red color for DISCONNECTED status - try { lblWebSocketStatus.TextColor = System.Drawing.Color.FromArgb(255, 0, 0); } catch { } - } - } - } - catch - { - if (lblWebSocketStatus != null) - lblWebSocketStatus.Text = "WebSocket: Unknown"; - } - } - - private void UpdateStatistics() - { - try - { - // Update last kill time - if (lblLastKillValue != null) - { - if (PluginCore.lastKillTime != DateTime.MinValue) - { - TimeSpan timeSinceLastKill = DateTime.Now - PluginCore.lastKillTime; - if (timeSinceLastKill.TotalMinutes < 60) - lblLastKillValue.Text = $"{timeSinceLastKill.TotalMinutes:F0} min ago"; - else - lblLastKillValue.Text = $"{timeSinceLastKill.TotalHours:F1} hr ago"; - } - else - { - lblLastKillValue.Text = "Never"; - } - } - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error in UpdateStatistics: {ex.Message}"); - } - } - #endregion - - #region Helper Methods - private void PopulateNavFileDropdown() - { - try - { - if (cmbNavFiles != null && PluginCore.navVisualization != null) - { - cmbNavFiles.Clear(); - cmbNavFiles.AddItem("None", "None"); - - foreach (string navFile in PluginCore.navVisualization.AvailableNavFiles) - { - cmbNavFiles.AddItem(navFile, navFile); - } - - if (cmbNavFiles.Count > 0) - cmbNavFiles.Current = 0; // Select "None" which should be at index 0 - - } - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error populating nav file dropdown: {ex.Message}"); - } - } - #endregion - - #region IDisposable Override - protected override void Dispose(bool disposing) - { - if (disposing) - { - try - { - // Unsubscribe from events to prevent memory leaks - - // Settings tab event cleanup - if (chkRareMetaEnabled != null) - chkRareMetaEnabled.Change -= OnRareMetaSettingChanged; - if (chkRemoteCommandsEnabled != null) - chkRemoteCommandsEnabled.Change -= OnRemoteCommandsSettingChanged; - if (chkHttpServerEnabled != null) - chkHttpServerEnabled.Change -= OnHttpServerSettingChanged; - if (chkWebSocketEnabled != null) - chkWebSocketEnabled.Change -= OnWebSocketSettingChanged; - if (chkTelemetryEnabled != null) - chkTelemetryEnabled.Change -= OnTelemetrySettingChanged; - if (txtCharTag != null) - txtCharTag.Change -= OnCharTagChanged; - if (txtVTankPath != null) - txtVTankPath.Change -= OnVTankPathChanged; - - // Statistics tab event cleanup - if (btnResetStats != null) - btnResetStats.Hit -= OnResetStatsClick; - - // Navigation tab event cleanup - if (chkNavVisualizationEnabled != null) - chkNavVisualizationEnabled.Change -= OnNavVisualizationEnabledChanged; - if (btnRefreshNavFiles != null) - btnRefreshNavFiles.Hit -= OnRefreshNavFilesClick; - if (btnLoadRoute != null) - btnLoadRoute.Hit -= OnLoadRouteClick; - if (btnClearRoute != null) - btnClearRoute.Hit -= OnClearRouteClick; - - // No enhanced events to clean up - } - catch (Exception ex) - { - PluginCore.WriteToChat($"Error cleaning up VVS view events: {ex.Message}"); - } - } - - // Call base dispose - base.Dispose(disposing); - } - #endregion - } -} \ No newline at end of file diff --git a/MosswartMassacre/Wrapper.cs b/MosswartMassacre/Wrapper.cs new file mode 100644 index 0000000..7cca722 --- /dev/null +++ b/MosswartMassacre/Wrapper.cs @@ -0,0 +1,427 @@ +/////////////////////////////////////////////////////////////////////////////// +//File: Wrapper.cs +// +//Description: Contains the interface definitions for the MetaViewWrappers classes. +// +//References required: +// System.Drawing +// +//This file is Copyright (c) 2010 VirindiPlugins +// +//Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +//The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +/////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Text; + +#if METAVIEW_PUBLIC_NS +namespace MetaViewWrappers +#else +namespace MyClasses.MetaViewWrappers +#endif +{ +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + delegate void dClickedList(object sender, int row, int col); + + + #region EventArgs Classes + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + class MVControlEventArgs : EventArgs + { + private int id; + + internal MVControlEventArgs(int ID) + { + this.id = ID; + } + + public int Id + { + get { return this.id; } + } + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + class MVIndexChangeEventArgs : MVControlEventArgs + { + private int index; + + internal MVIndexChangeEventArgs(int ID, int Index) + : base(ID) + { + this.index = Index; + } + + public int Index + { + get { return this.index; } + } + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + class MVListSelectEventArgs : MVControlEventArgs + { + private int row; + private int col; + + internal MVListSelectEventArgs(int ID, int Row, int Column) + : base(ID) + { + this.row = Row; + this.col = Column; + } + + public int Row + { + get { return this.row; } + } + + public int Column + { + get { return this.col; } + } + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + class MVCheckBoxChangeEventArgs : MVControlEventArgs + { + private bool check; + + internal MVCheckBoxChangeEventArgs(int ID, bool Check) + : base(ID) + { + this.check = Check; + } + + public bool Checked + { + get { return this.check; } + } + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + class MVTextBoxChangeEventArgs : MVControlEventArgs + { + private string text; + + internal MVTextBoxChangeEventArgs(int ID, string text) + : base(ID) + { + this.text = text; + } + + public string Text + { + get { return this.text; } + } + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + class MVTextBoxEndEventArgs : MVControlEventArgs + { + private bool success; + + internal MVTextBoxEndEventArgs(int ID, bool success) + : base(ID) + { + this.success = success; + } + + public bool Success + { + get { return this.success; } + } + } + + #endregion EventArgs Classes + + + #region View + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + interface IView: IDisposable + { + void Initialize(Decal.Adapter.Wrappers.PluginHost p, string pXML); + void InitializeRawXML(Decal.Adapter.Wrappers.PluginHost p, string pXML); + void Initialize(Decal.Adapter.Wrappers.PluginHost p, string pXML, string pWindowKey); + void InitializeRawXML(Decal.Adapter.Wrappers.PluginHost p, string pXML, string pWindowKey); + + void SetIcon(int icon, int iconlibrary); + void SetIcon(int portalicon); + + string Title { get; set; } + bool Visible { get; set; } +#if !VVS_WRAPPERS_PUBLIC + ViewSystemSelector.eViewSystem ViewType { get; } +#endif + + System.Drawing.Point Location { get; set; } + System.Drawing.Rectangle Position { get; set; } + System.Drawing.Size Size { get; } + + IControl this[string id] { get; } + + void Activate(); + void Deactivate(); + bool Activated { get; set; } + } + + #endregion View + + #region Controls + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + interface IControl : IDisposable + { + string Name { get; } + bool Visible { get; set; } + string TooltipText { get; set;} + int Id { get; } + System.Drawing.Rectangle LayoutPosition { get; set; } + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + interface IButton : IControl + { + string Text { get; set; } + event EventHandler Hit; + event EventHandler Click; + System.Drawing.Color TextColor { get; set; } + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + interface ICheckBox : IControl + { + string Text { get; set; } + bool Checked { get; set; } + event EventHandler Change; + event EventHandler Change_Old; + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + interface ITextBox : IControl + { + string Text { get; set; } + event EventHandler Change; + event EventHandler Change_Old; + event EventHandler End; + int Caret { get; set; } + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + interface ICombo : IControl + { + IComboIndexer Text { get; } + IComboDataIndexer Data { get; } + int Count { get; } + int Selected { get; set; } + event EventHandler Change; + event EventHandler Change_Old; + void Add(string text); + void Add(string text, object obj); + void Insert(int index, string text); + void RemoveAt(int index); + void Remove(int index); + void Clear(); + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + interface IComboIndexer + { + string this[int index] { get; set; } + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + interface IComboDataIndexer + { + object this[int index] { get; set; } + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + interface ISlider : IControl + { + int Position { get; set; } + event EventHandler Change; + event EventHandler Change_Old; + int Maximum { get; set; } + int Minimum { get; set; } + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + interface IList : IControl + { + event EventHandler Selected; + event dClickedList Click; + void Clear(); + IListRow this[int row] { get; } + IListRow AddRow(); + IListRow Add(); + IListRow InsertRow(int pos); + IListRow Insert(int pos); + int RowCount { get; } + void RemoveRow(int index); + void Delete(int index); + int ColCount { get; } + int ScrollPosition { get; set;} + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + interface IListRow + { + IListCell this[int col] { get; } + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + interface IListCell + { + System.Drawing.Color Color { get; set; } + int Width { get; set; } + object this[int subval] { get; set; } + void ResetColor(); + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + interface IStaticText : IControl + { + string Text { get; set; } + event EventHandler Click; + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + interface INotebook : IControl + { + event EventHandler Change; + int ActiveTab { get; set; } + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + interface IProgressBar : IControl + { + int Position { get; set; } + int Value { get; set; } + string PreText { get; set; } + int MaxValue { get; set; } + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + interface IImageButton : IControl + { + event EventHandler Click; + void SetImages(int unpressed, int pressed); + void SetImages(int hmodule, int unpressed, int pressed); + int Background { set; } + System.Drawing.Color Matte { set; } + } + + #endregion Controls +} diff --git a/MosswartMassacre/Wrapper_Decal.cs b/MosswartMassacre/Wrapper_Decal.cs new file mode 100644 index 0000000..a917ca8 --- /dev/null +++ b/MosswartMassacre/Wrapper_Decal.cs @@ -0,0 +1,1120 @@ +/////////////////////////////////////////////////////////////////////////////// +//File: Wrapper_Decal.cs +// +//Description: Contains MetaViewWrapper classes implementing Decal views. +// +//References required: +// System.Drawing +// Decal.Adapter +// +//This file is Copyright (c) 2010 VirindiPlugins +// +//Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +//The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +/////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Text; + +#if METAVIEW_PUBLIC_NS +namespace MetaViewWrappers.DecalControls +#else +namespace MyClasses.MetaViewWrappers.DecalControls +#endif + +{ +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + class View : IView + { + Decal.Adapter.Wrappers.ViewWrapper myView; + public Decal.Adapter.Wrappers.ViewWrapper Underlying { get { return myView; } } + + #region IView Members + + public void Initialize(Decal.Adapter.Wrappers.PluginHost p, string pXML) + { + myView = p.LoadViewResource(pXML); + } + + public void InitializeRawXML(Decal.Adapter.Wrappers.PluginHost p, string pXML) + { + myView = p.LoadView(pXML); + } + + public void Initialize(Decal.Adapter.Wrappers.PluginHost p, string pXML, string pWindowKey) + { + myView = p.LoadViewResource(pXML); + } + + public void InitializeRawXML(Decal.Adapter.Wrappers.PluginHost p, string pXML, string pWindowKey) + { + myView = p.LoadView(pXML); + } + + public void SetIcon(int icon, int iconlibrary) + { + myView.SetIcon(icon, iconlibrary); + } + + public void SetIcon(int portalicon) + { + //throw new Exception("The method or operation is not implemented."); + } + + public string Title + { + get + { + return myView.Title; + } + set + { + myView.Title = value; + } + } + + public bool Visible + { + get + { + return myView.Activated; + } + set + { + myView.Activated = value; + } + } + + public bool Activated + { + get + { + return Visible; + } + set + { + Visible = value; + } + } + + public void Activate() + { + Visible = true; + } + + public void Deactivate() + { + Visible = false; + } + + public System.Drawing.Point Location + { + get + { + return new System.Drawing.Point(myView.Position.X, myView.Position.Y); + } + set + { + int w = myView.Position.Width; + int h = myView.Position.Height; + myView.Position = new System.Drawing.Rectangle(value.X, value.Y, w, h); + } + } + + public System.Drawing.Rectangle Position + { + get + { + return myView.Position; + } + set + { + myView.Position = value; + } + } + + public System.Drawing.Size Size + { + get + { + return new System.Drawing.Size(myView.Position.Width, myView.Position.Height); + } + } + +#if VVS_WRAPPERS_PUBLIC + internal +#else + public +#endif + ViewSystemSelector.eViewSystem ViewType { get { return ViewSystemSelector.eViewSystem.DecalInject; } } + Dictionary CreatedControlsByName = new Dictionary(); + + public IControl this[string id] + { + get + { + if (CreatedControlsByName.ContainsKey(id)) return CreatedControlsByName[id]; + + Control ret = null; + Decal.Adapter.Wrappers.IControlWrapper iret = myView.Controls[id]; + if (iret.GetType() == typeof(Decal.Adapter.Wrappers.PushButtonWrapper)) + ret = new Button(); + if (iret.GetType() == typeof(Decal.Adapter.Wrappers.CheckBoxWrapper)) + ret = new CheckBox(); + if (iret.GetType() == typeof(Decal.Adapter.Wrappers.TextBoxWrapper)) + ret = new TextBox(); + if (iret.GetType() == typeof(Decal.Adapter.Wrappers.ChoiceWrapper)) + ret = new Combo(); + if (iret.GetType() == typeof(Decal.Adapter.Wrappers.SliderWrapper)) + ret = new Slider(); + if (iret.GetType() == typeof(Decal.Adapter.Wrappers.ListWrapper)) + ret = new List(); + if (iret.GetType() == typeof(Decal.Adapter.Wrappers.StaticWrapper)) + ret = new StaticText(); + if (iret.GetType() == typeof(Decal.Adapter.Wrappers.NotebookWrapper)) + ret = new Notebook(); + if (iret.GetType() == typeof(Decal.Adapter.Wrappers.ProgressWrapper)) + ret = new ProgressBar(); + if (iret.GetType() == typeof(Decal.Adapter.Wrappers.ButtonWrapper)) + ret = new ImageButton(); + + if (ret == null) return null; + + ret.myControl = iret; + ret.myName = id; + ret.Initialize(); + allocatedcontrols.Add(ret); + CreatedControlsByName[id] = ret; + return ret; + } + } + + List allocatedcontrols = new List(); + + #endregion + + #region IDisposable Members + + bool disposed = false; + public void Dispose() + { + if (disposed) return; + disposed = true; + GC.SuppressFinalize(this); + + foreach (Control c in allocatedcontrols) + c.Dispose(); + + myView.Dispose(); + } + + #endregion + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + class Control : IControl + { + internal Decal.Adapter.Wrappers.IControlWrapper myControl; + public Decal.Adapter.Wrappers.IControlWrapper Underlying { get { return myControl; } } + internal string myName; + + public virtual void Initialize() + { + + } + + #region IControl Members + + public string Name + { + get { return myName; } + } + + public bool Visible + { + get { return true; } + set { } + } + + public string TooltipText + { + get + { + return ""; + } + set + { + + } + } + + public int Id + { + get + { + return myControl.Id; + } + } + + public System.Drawing.Rectangle LayoutPosition + { + get + { +#if DECAL_INTEROP + //This is kinda bad, but whatever + Decal.Interop.Inject.ILayer Ly = (Decal.Interop.Inject.ILayer)(Underlying.Underlying); + Decal.Interop.Core.tagRECT rct = Ly.get_Position(); + return new System.Drawing.Rectangle(rct.left, rct.top, rct.right - rct.left, rct.bottom - rct.top); +#else +#warning DECAL_INTEROP not defined, MetaViewWrappers.DecalControls.Control.LayoutPosition will not be available. + return new System.Drawing.Rectangle(); +#endif + } + set + { +#if DECAL_INTEROP + Decal.Interop.Inject.ILayer Ly = (Decal.Interop.Inject.ILayer)(Underlying.Underlying); + Decal.Interop.Core.tagRECT rct = new Decal.Interop.Core.tagRECT(); + rct.left = value.Left; + rct.top = value.Top; + rct.right = value.Right; + rct.bottom = value.Bottom; + Ly.set_Position(ref rct); +#endif + } + } + + #endregion + + #region IDisposable Members + + bool disposed = false; + public virtual void Dispose() + { + if (disposed) return; + disposed = true; + + //myControl.Dispose(); + } + + #endregion + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + class Button : Control, IButton + { + public override void Initialize() + { + base.Initialize(); + ((Decal.Adapter.Wrappers.PushButtonWrapper)myControl).Hit += new EventHandler(Button_Hit); + ((Decal.Adapter.Wrappers.PushButtonWrapper)myControl).Click += new EventHandler(Button_Click); + } + + public override void Dispose() + { + base.Dispose(); + ((Decal.Adapter.Wrappers.PushButtonWrapper)myControl).Hit -= new EventHandler(Button_Hit); + ((Decal.Adapter.Wrappers.PushButtonWrapper)myControl).Click -= new EventHandler(Button_Click); + } + + void Button_Hit(object sender, Decal.Adapter.ControlEventArgs e) + { + if (Hit != null) + Hit(this, null); + } + + void Button_Click(object sender, Decal.Adapter.ControlEventArgs e) + { + if (Click != null) + Click(this, new MVControlEventArgs(this.Id)); + } + + #region IButton Members + + public string Text + { + get + { + return ((Decal.Adapter.Wrappers.PushButtonWrapper)myControl).Text; + //throw new Exception("The method or operation is not implemented."); + } + set + { + ((Decal.Adapter.Wrappers.PushButtonWrapper)myControl).Text = value; + //throw new Exception("The method or operation is not implemented."); + } + } + + public System.Drawing.Color TextColor + { + get + { + return ((Decal.Adapter.Wrappers.PushButtonWrapper)myControl).TextColor; + } + set + { + ((Decal.Adapter.Wrappers.PushButtonWrapper)myControl).TextColor = value; + } + } + + public event EventHandler Hit; + public event EventHandler Click; + + #endregion + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + class CheckBox : Control, ICheckBox + { + public override void Initialize() + { + base.Initialize(); + ((Decal.Adapter.Wrappers.CheckBoxWrapper)myControl).Change += new EventHandler(CheckBox_Change); + } + + public override void Dispose() + { + base.Dispose(); + ((Decal.Adapter.Wrappers.CheckBoxWrapper)myControl).Change -= new EventHandler(CheckBox_Change); + } + + void CheckBox_Change(object sender, Decal.Adapter.CheckBoxChangeEventArgs e) + { + if (Change != null) + Change(this, new MVCheckBoxChangeEventArgs(this.Id, Checked)); + if (Change_Old != null) + Change_Old(this, null); + } + + #region ICheckBox Members + + public string Text + { + get + { + return ((Decal.Adapter.Wrappers.CheckBoxWrapper)myControl).Text; + } + set + { + ((Decal.Adapter.Wrappers.CheckBoxWrapper)myControl).Text = value; + } + } + + public bool Checked + { + get + { + return ((Decal.Adapter.Wrappers.CheckBoxWrapper)myControl).Checked; + } + set + { + ((Decal.Adapter.Wrappers.CheckBoxWrapper)myControl).Checked = value; + } + } + + public event EventHandler Change; + public event EventHandler Change_Old; + + #endregion + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + class TextBox : Control, ITextBox + { + public override void Initialize() + { + base.Initialize(); + ((Decal.Adapter.Wrappers.TextBoxWrapper)myControl).Change += new EventHandler(TextBox_Change); + ((Decal.Adapter.Wrappers.TextBoxWrapper)myControl).End += new EventHandler(TextBox_End); + } + + public override void Dispose() + { + base.Dispose(); + ((Decal.Adapter.Wrappers.TextBoxWrapper)myControl).Change -= new EventHandler(TextBox_Change); + ((Decal.Adapter.Wrappers.TextBoxWrapper)myControl).End -= new EventHandler(TextBox_End); + } + + void TextBox_Change(object sender, Decal.Adapter.TextBoxChangeEventArgs e) + { + if (Change != null) + Change(this, new MVTextBoxChangeEventArgs(this.Id, e.Text)); + if (Change_Old != null) + Change_Old(this, null); + } + + void TextBox_End(object sender, Decal.Adapter.TextBoxEndEventArgs e) + { + if (End != null) + End(this, new MVTextBoxEndEventArgs(this.Id, e.Success)); + } + + #region ITextBox Members + + public string Text + { + get + { + return ((Decal.Adapter.Wrappers.TextBoxWrapper)myControl).Text; + } + set + { + ((Decal.Adapter.Wrappers.TextBoxWrapper)myControl).Text = value; + } + } + + public int Caret + { + get + { + return ((Decal.Adapter.Wrappers.TextBoxWrapper)myControl).Caret; + } + set + { + ((Decal.Adapter.Wrappers.TextBoxWrapper)myControl).Caret = value; + } + } + + public event EventHandler Change; + public event EventHandler Change_Old; + public event EventHandler End; + + #endregion + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + class Combo : Control, ICombo + { + public override void Initialize() + { + base.Initialize(); + ((Decal.Adapter.Wrappers.ChoiceWrapper)myControl).Change += new EventHandler(Combo_Change); + } + + public override void Dispose() + { + base.Dispose(); + ((Decal.Adapter.Wrappers.ChoiceWrapper)myControl).Change -= new EventHandler(Combo_Change); + } + + void Combo_Change(object sender, Decal.Adapter.IndexChangeEventArgs e) + { + if (Change != null) + Change(this, new MVIndexChangeEventArgs(this.Id, e.Index)); + if (Change_Old != null) + Change_Old(this, null); + } + + #region ICombo Members + + public IComboIndexer Text + { + get + { + return new ComboIndexer(this); + } + } + + public IComboDataIndexer Data + { + get + { + return new ComboDataIndexer(this); + } + } + + public int Count + { + get + { + return ((Decal.Adapter.Wrappers.ChoiceWrapper)myControl).Count; + } + } + + public int Selected + { + get + { + return ((Decal.Adapter.Wrappers.ChoiceWrapper)myControl).Selected; + } + set + { + ((Decal.Adapter.Wrappers.ChoiceWrapper)myControl).Selected = value; + } + } + + public event EventHandler Change; + public event EventHandler Change_Old; + + public void Add(string text) + { + ((Decal.Adapter.Wrappers.ChoiceWrapper)myControl).Add(text, null); + } + + public void Add(string text, object obj) + { + ((Decal.Adapter.Wrappers.ChoiceWrapper)myControl).Add(text, obj); + } + + public void Insert(int index, string text) + { + throw new Exception("The method or operation is not implemented."); + } + + public void RemoveAt(int index) + { + ((Decal.Adapter.Wrappers.ChoiceWrapper)myControl).Remove(index); + } + + public void Remove(int index) + { + RemoveAt(index); + } + + public void Clear() + { + ((Decal.Adapter.Wrappers.ChoiceWrapper)myControl).Clear(); + } + + #endregion + + internal class ComboIndexer: IComboIndexer + { + Combo myCombo; + internal ComboIndexer(Combo c) + { + myCombo = c; + } + + #region IComboIndexer Members + + public string this[int index] + { + get + { + return ((Decal.Adapter.Wrappers.ChoiceWrapper)myCombo.myControl).Text[index]; + } + set + { + ((Decal.Adapter.Wrappers.ChoiceWrapper)myCombo.myControl).Text[index] = value; + } + } + + #endregion + } + + internal class ComboDataIndexer : IComboDataIndexer + { + Combo myCombo; + internal ComboDataIndexer(Combo c) + { + myCombo = c; + } + + #region IComboIndexer Members + + public object this[int index] + { + get + { + return ((Decal.Adapter.Wrappers.ChoiceWrapper)myCombo.myControl).Data[index]; + } + set + { + ((Decal.Adapter.Wrappers.ChoiceWrapper)myCombo.myControl).Data[index] = value; + } + } + + #endregion + } + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + class Slider : Control, ISlider + { + public override void Initialize() + { + base.Initialize(); + ((Decal.Adapter.Wrappers.SliderWrapper)myControl).Change += new EventHandler(Slider_Change); + } + + public override void Dispose() + { + base.Dispose(); + ((Decal.Adapter.Wrappers.SliderWrapper)myControl).Change -= new EventHandler(Slider_Change); + } + + void Slider_Change(object sender, Decal.Adapter.IndexChangeEventArgs e) + { + if (Change != null) + Change(this, new MVIndexChangeEventArgs(this.Id, e.Index)); + if (Change_Old != null) + Change_Old(this, null); + } + + #region ISlider Members + + public int Position + { + get + { + return ((Decal.Adapter.Wrappers.SliderWrapper)myControl).SliderPostition; + } + set + { + ((Decal.Adapter.Wrappers.SliderWrapper)myControl).SliderPostition = value; + } + } + + public event EventHandler Change; + public event EventHandler Change_Old; + + public int Maximum + { + get + { + return ((Decal.Adapter.Wrappers.SliderWrapper)myControl).Maximum; + } + set + { + ((Decal.Adapter.Wrappers.SliderWrapper)myControl).Maximum = value; + } + } + public int Minimum + { + get + { + return ((Decal.Adapter.Wrappers.SliderWrapper)myControl).Minimum; + } + set + { + ((Decal.Adapter.Wrappers.SliderWrapper)myControl).Minimum = value; + } + } + + #endregion + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + class List : Control, IList + { + public override void Initialize() + { + base.Initialize(); + ((Decal.Adapter.Wrappers.ListWrapper)myControl).Selected += new EventHandler(List_Selected); + } + + public override void Dispose() + { + base.Dispose(); + ((Decal.Adapter.Wrappers.ListWrapper)myControl).Selected -= new EventHandler(List_Selected); + } + + void List_Selected(object sender, Decal.Adapter.ListSelectEventArgs e) + { + if (Click != null) + Click(this, e.Row, e.Column); + if (Selected != null) + Selected(this, new MVListSelectEventArgs(this.Id, e.Row, e.Column)); + } + + #region IList Members + + public event dClickedList Click; + public event EventHandler Selected; + + public void Clear() + { + ((Decal.Adapter.Wrappers.ListWrapper)myControl).Clear(); + } + + public IListRow this[int row] + { + get + { + return new ListRow(this, row); + } + } + + public IListRow AddRow() + { + ((Decal.Adapter.Wrappers.ListWrapper)myControl).Add(); + return new ListRow(this, ((Decal.Adapter.Wrappers.ListWrapper)myControl).RowCount - 1); + } + + public IListRow Add() + { + return AddRow(); + } + + public IListRow InsertRow(int pos) + { + ((Decal.Adapter.Wrappers.ListWrapper)myControl).Insert(pos); + return new ListRow(this, pos); + } + + public IListRow Insert(int pos) + { + return InsertRow(pos); + } + + public int RowCount + { + get { return ((Decal.Adapter.Wrappers.ListWrapper)myControl).RowCount; } + } + + public void RemoveRow(int index) + { + ((Decal.Adapter.Wrappers.ListWrapper)myControl).Delete(index); + } + + public void Delete(int index) + { + RemoveRow(index); + } + + public int ColCount + { + get + { + return ((Decal.Adapter.Wrappers.ListWrapper)myControl).ColCount; + } + } + + public int ScrollPosition + { + get + { + return ((Decal.Adapter.Wrappers.ListWrapper)myControl).ScrollPosition; + } + set + { + ((Decal.Adapter.Wrappers.ListWrapper)myControl).ScrollPosition = value; + } + } + + #endregion + + public class ListRow : IListRow + { + internal List myList; + internal int myRow; + internal ListRow(List l, int r) + { + myList = l; + myRow = r; + } + + + #region IListRow Members + + public IListCell this[int col] + { + get { return new ListCell(myList, myRow, col); } + } + + #endregion + } + + public class ListCell : IListCell + { + internal List myList; + internal int myRow; + internal int myCol; + public ListCell(List l, int r, int c) + { + myList = l; + myRow = r; + myCol = c; + } + + #region IListCell Members + + public void ResetColor() + { + Color = System.Drawing.Color.White; + } + + public System.Drawing.Color Color + { + get + { + return ((Decal.Adapter.Wrappers.ListWrapper)myList.myControl)[myRow][myCol].Color; + } + set + { + ((Decal.Adapter.Wrappers.ListWrapper)myList.myControl)[myRow][myCol].Color = value; + } + } + + public int Width + { + get + { + return ((Decal.Adapter.Wrappers.ListWrapper)myList.myControl)[myRow][myCol].Width; + } + set + { + ((Decal.Adapter.Wrappers.ListWrapper)myList.myControl)[myRow][myCol].Width = value; + } + } + + public object this[int subval] + { + get + { + return ((Decal.Adapter.Wrappers.ListWrapper)myList.myControl)[myRow][myCol][subval]; + } + set + { + ((Decal.Adapter.Wrappers.ListWrapper)myList.myControl)[myRow][myCol][subval] = value; + } + } + + #endregion + } + + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + class StaticText : Control, IStaticText + { + + #region IStaticText Members + + public string Text + { + get + { + return ((Decal.Adapter.Wrappers.StaticWrapper)myControl).Text; + } + set + { + ((Decal.Adapter.Wrappers.StaticWrapper)myControl).Text = value; + } + } + +#pragma warning disable 0067 + public event EventHandler Click; +#pragma warning restore 0067 + + #endregion + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + class Notebook : Control, INotebook + { + public override void Initialize() + { + base.Initialize(); + ((Decal.Adapter.Wrappers.NotebookWrapper)myControl).Change += new EventHandler(Notebook_Change); + } + + public override void Dispose() + { + base.Dispose(); + ((Decal.Adapter.Wrappers.NotebookWrapper)myControl).Change -= new EventHandler(Notebook_Change); + } + + void Notebook_Change(object sender, Decal.Adapter.IndexChangeEventArgs e) + { + if (Change != null) + Change(this, new MVIndexChangeEventArgs(this.Id, e.Index)); + } + + #region INotebook Members + + public event EventHandler Change; + + public int ActiveTab + { + get + { + return ((Decal.Adapter.Wrappers.NotebookWrapper)myControl).ActiveTab; + } + set + { + ((Decal.Adapter.Wrappers.NotebookWrapper)myControl).ActiveTab = value; + } + } + + #endregion + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + class ProgressBar : Control, IProgressBar + { + + #region IProgressBar Members + + public int Position + { + get + { + return ((Decal.Adapter.Wrappers.ProgressWrapper)myControl).Value; + } + set + { + ((Decal.Adapter.Wrappers.ProgressWrapper)myControl).Value = value; + } + } + + public int Value + { + get + { + return Position; + } + set + { + Position = value; + } + } + + public string PreText + { + get + { + return ((Decal.Adapter.Wrappers.ProgressWrapper)myControl).PreText; + } + set + { + ((Decal.Adapter.Wrappers.ProgressWrapper)myControl).PreText = value; + } + } + + public int MaxValue + { + get + { + return ((Decal.Adapter.Wrappers.ProgressWrapper)myControl).MaxValue; + } + set + { + ((Decal.Adapter.Wrappers.ProgressWrapper)myControl).MaxValue = value; + } + } + + #endregion + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + class ImageButton : Control, IImageButton + { + public override void Initialize() + { + base.Initialize(); + ((Decal.Adapter.Wrappers.ButtonWrapper)myControl).Click += new EventHandler(ImageButton_Click); + } + + public override void Dispose() + { + base.Dispose(); + ((Decal.Adapter.Wrappers.ButtonWrapper)myControl).Click -= new EventHandler(ImageButton_Click); + } + + void ImageButton_Click(object sender, Decal.Adapter.ControlEventArgs e) + { + if (Click != null) + Click(this, new MVControlEventArgs(this.Id)); + } + + + #region IImageButton Members + + public event EventHandler Click; + + public void SetImages(int unpressed, int pressed) + { + ((Decal.Adapter.Wrappers.ButtonWrapper)myControl).SetImages(unpressed, pressed); + } + + public void SetImages(int hmodule, int unpressed, int pressed) + { + ((Decal.Adapter.Wrappers.ButtonWrapper)myControl).SetImages(hmodule, unpressed, pressed); + } + + public int Background + { + set + { + ((Decal.Adapter.Wrappers.ButtonWrapper)myControl).Background = value; + } + } + + public System.Drawing.Color Matte + { + set + { + ((Decal.Adapter.Wrappers.ButtonWrapper)myControl).Matte = value; + } + } + + #endregion + } +} + diff --git a/MosswartMassacre/Wrapper_MyHuds.cs b/MosswartMassacre/Wrapper_MyHuds.cs new file mode 100644 index 0000000..e2e097f --- /dev/null +++ b/MosswartMassacre/Wrapper_MyHuds.cs @@ -0,0 +1,1225 @@ +/////////////////////////////////////////////////////////////////////////////// +//File: Wrapper_MyHuds.cs +// +//Description: Contains MetaViewWrapper classes implementing Virindi View Service +// views. These classes are only compiled if the VVS_REFERENCED symbol is defined. +// +//References required: +// System.Drawing +// VirindiViewService (if VVS_REFERENCED is defined) +// +//This file is Copyright (c) 2010 VirindiPlugins +// +//Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +//The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +/////////////////////////////////////////////////////////////////////////////// + +#if VVS_REFERENCED + +using System; +using System.Collections.Generic; +using System.Text; +using VirindiViewService; + +#if METAVIEW_PUBLIC_NS +namespace MetaViewWrappers.VirindiViewServiceHudControls +#else +namespace MyClasses.MetaViewWrappers.VirindiViewServiceHudControls +#endif +{ +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + class View : IView + { + HudView myView; + public HudView Underlying { get { return myView; } } + + #region IView Members + + public void Initialize(Decal.Adapter.Wrappers.PluginHost p, string pXML) + { + VirindiViewService.XMLParsers.Decal3XMLParser ps = new VirindiViewService.XMLParsers.Decal3XMLParser(); + ViewProperties iprop; + ControlGroup igroup; + ps.ParseFromResource(pXML, out iprop, out igroup); + myView = new VirindiViewService.HudView(iprop, igroup); + } + + public void InitializeRawXML(Decal.Adapter.Wrappers.PluginHost p, string pXML) + { + VirindiViewService.XMLParsers.Decal3XMLParser ps = new VirindiViewService.XMLParsers.Decal3XMLParser(); + ViewProperties iprop; + ControlGroup igroup; + ps.Parse(pXML, out iprop, out igroup); + myView = new VirindiViewService.HudView(iprop, igroup); + } + + public void Initialize(Decal.Adapter.Wrappers.PluginHost p, string pXML, string pWindowKey) + { + VirindiViewService.XMLParsers.Decal3XMLParser ps = new VirindiViewService.XMLParsers.Decal3XMLParser(); + ViewProperties iprop; + ControlGroup igroup; + ps.ParseFromResource(pXML, out iprop, out igroup); + myView = new VirindiViewService.HudView(iprop, igroup, pWindowKey); + } + + public void InitializeRawXML(Decal.Adapter.Wrappers.PluginHost p, string pXML, string pWindowKey) + { + VirindiViewService.XMLParsers.Decal3XMLParser ps = new VirindiViewService.XMLParsers.Decal3XMLParser(); + ViewProperties iprop; + ControlGroup igroup; + ps.Parse(pXML, out iprop, out igroup); + myView = new VirindiViewService.HudView(iprop, igroup, pWindowKey); + } + + public void SetIcon(int icon, int iconlibrary) + { + myView.Icon = ACImage.FromIconLibrary(icon, iconlibrary); + } + + public void SetIcon(int portalicon) + { + myView.Icon = portalicon; + } + + public string Title + { + get + { + return myView.Title; + } + set + { + myView.Title = value; + } + } + + public bool Visible + { + get + { + return myView.Visible; + } + set + { + myView.Visible = value; + } + } + + public bool Activated + { + get + { + return Visible; + } + set + { + Visible = value; + } + } + + public void Activate() + { + Visible = true; + } + + public void Deactivate() + { + Visible = false; + } + + public System.Drawing.Point Location + { + get + { + return myView.Location; + } + set + { + myView.Location = value; + } + } + + public System.Drawing.Size Size + { + get + { + return new System.Drawing.Size(myView.Width, myView.Height); + } + } + + public System.Drawing.Rectangle Position + { + get + { + return new System.Drawing.Rectangle(Location, Size); + } + set + { + Location = value.Location; + myView.ClientArea = value.Size; + } + } + +#if VVS_WRAPPERS_PUBLIC + internal +#else + public +#endif + ViewSystemSelector.eViewSystem ViewType { get { return ViewSystemSelector.eViewSystem.VirindiViewService; } } + Dictionary CreatedControlsByName = new Dictionary(); + + public IControl this[string id] + { + get + { + if (CreatedControlsByName.ContainsKey(id)) return CreatedControlsByName[id]; + + Control ret = null; + VirindiViewService.Controls.HudControl iret = myView[id]; + if (iret.GetType() == typeof(VirindiViewService.Controls.HudButton)) + ret = new Button(); + if (iret.GetType() == typeof(VirindiViewService.Controls.HudCheckBox)) + ret = new CheckBox(); + if (iret.GetType() == typeof(VirindiViewService.Controls.HudTextBox)) + ret = new TextBox(); + if (iret.GetType() == typeof(VirindiViewService.Controls.HudCombo)) + ret = new Combo(); + if (iret.GetType() == typeof(VirindiViewService.Controls.HudHSlider)) + ret = new Slider(); + if (iret.GetType() == typeof(VirindiViewService.Controls.HudList)) + ret = new List(); + if (iret.GetType() == typeof(VirindiViewService.Controls.HudStaticText)) + ret = new StaticText(); + if (iret.GetType() == typeof(VirindiViewService.Controls.HudTabView)) + ret = new Notebook(); + if (iret.GetType() == typeof(VirindiViewService.Controls.HudProgressBar)) + ret = new ProgressBar(); + if (iret.GetType() == typeof(VirindiViewService.Controls.HudImageButton)) + ret = new ImageButton(); + + if (ret == null) return null; + + ret.myControl = iret; + ret.myName = id; + ret.Initialize(); + allocatedcontrols.Add(ret); + CreatedControlsByName[id] = ret; + return ret; + } + } + + #endregion + + #region IDisposable Members + + bool disposed = false; + public void Dispose() + { + if (disposed) return; + disposed = true; + GC.SuppressFinalize(this); + + foreach (Control c in allocatedcontrols) + c.Dispose(); + + myView.Dispose(); + } + + #endregion + + List allocatedcontrols = new List(); + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + class Control : IControl + { + internal VirindiViewService.Controls.HudControl myControl; + internal string myName; + public VirindiViewService.Controls.HudControl Underlying { get { return myControl; } } + + public virtual void Initialize() + { + + } + + #region IControl Members + + public string Name + { + get { return myName; } + } + + public bool Visible + { + get { return myControl.Visible; } + set { myControl.Visible = value; } + } + + VirindiViewService.TooltipSystem.cTooltipInfo itooltipinfo = null; + public string TooltipText + { + get + { + if (itooltipinfo != null) + return itooltipinfo.Text; + else + return ""; + } + set + { + if (itooltipinfo != null) + { + VirindiViewService.TooltipSystem.RemoveTooltip(itooltipinfo); + itooltipinfo = null; + } + if (!String.IsNullOrEmpty(value)) + { + itooltipinfo = VirindiViewService.TooltipSystem.AssociateTooltip(myControl, value); + } + } + } + + public int Id + { + get + { + return myControl.XMLID; + } + } + + public System.Drawing.Rectangle LayoutPosition + { + get + { + //Relative to what?!??! + if (Underlying.Group.HeadControl == null) + return new System.Drawing.Rectangle(); + + if (Underlying.Group.HeadControl.Name == Underlying.Name) + return new System.Drawing.Rectangle(); + + VirindiViewService.Controls.HudControl myparent = Underlying.Group.ParentOf(Underlying.Name); + + if (myparent == null) + return new System.Drawing.Rectangle(); + + //Position only valid inside fixedlayouts + VirindiViewService.Controls.HudFixedLayout layoutparent = myparent as VirindiViewService.Controls.HudFixedLayout; + + if (layoutparent == null) + return new System.Drawing.Rectangle(); + + return layoutparent.GetControlRect(Underlying); + } + set + { + if (Underlying.Group.HeadControl == null) + return; + + if (Underlying.Group.HeadControl.Name == Underlying.Name) + return; + + VirindiViewService.Controls.HudControl myparent = Underlying.Group.ParentOf(Underlying.Name); + + if (myparent == null) + return; + + //Position only valid inside fixedlayouts + VirindiViewService.Controls.HudFixedLayout layoutparent = myparent as VirindiViewService.Controls.HudFixedLayout; + + if (layoutparent == null) + return; + + layoutparent.SetControlRect(Underlying, value); + } + } + + #endregion + + #region IDisposable Members + + bool disposed = false; + public virtual void Dispose() + { + if (disposed) return; + disposed = true; + + myControl.Dispose(); + } + + #endregion + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + class Button : Control, IButton + { + public override void Initialize() + { + base.Initialize(); + ((VirindiViewService.Controls.HudButton)myControl).MouseEvent += new EventHandler(Button_MouseEvent); + } + + public override void Dispose() + { + base.Dispose(); + ((VirindiViewService.Controls.HudButton)myControl).MouseEvent -= new EventHandler(Button_MouseEvent); + } + + void Button_MouseEvent(object sender, VirindiViewService.Controls.ControlMouseEventArgs e) + { + switch (e.EventType) + { + case VirindiViewService.Controls.ControlMouseEventArgs.MouseEventType.MouseHit: + if (Click != null) + Click(this, new MVControlEventArgs(this.Id)); + return; + case VirindiViewService.Controls.ControlMouseEventArgs.MouseEventType.MouseDown: + if (Hit != null) + Hit(this, null); + return; + } + } + + #region IButton Members + + public string Text + { + get + { + return ((VirindiViewService.Controls.HudButton)myControl).Text; + } + set + { + ((VirindiViewService.Controls.HudButton)myControl).Text = value; + } + } + + public System.Drawing.Color TextColor + { + get + { + return System.Drawing.Color.Black; + } + set + { + + } + } + + public event EventHandler Hit; + public event EventHandler Click; + + #endregion + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + class CheckBox : Control, ICheckBox + { + public override void Initialize() + { + base.Initialize(); + ((VirindiViewService.Controls.HudCheckBox)myControl).Change += new EventHandler(CheckBox_Change); + } + + public override void Dispose() + { + base.Dispose(); + ((VirindiViewService.Controls.HudCheckBox)myControl).Change -= new EventHandler(CheckBox_Change); + } + + void CheckBox_Change(object sender, EventArgs e) + { + if (Change != null) + Change(this, new MVCheckBoxChangeEventArgs(this.Id, Checked)); + if (Change_Old != null) + Change_Old(this, null); + } + + #region ICheckBox Members + + public string Text + { + get + { + return ((VirindiViewService.Controls.HudCheckBox)myControl).Text; + } + set + { + ((VirindiViewService.Controls.HudCheckBox)myControl).Text = value; + } + } + + public bool Checked + { + get + { + return ((VirindiViewService.Controls.HudCheckBox)myControl).Checked; + } + set + { + ((VirindiViewService.Controls.HudCheckBox)myControl).Checked = value; + } + } + + public event EventHandler Change; + public event EventHandler Change_Old; + + #endregion + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + class TextBox : Control, ITextBox + { + public override void Initialize() + { + base.Initialize(); + ((VirindiViewService.Controls.HudTextBox)myControl).Change += new EventHandler(TextBox_Change); + myControl.LostFocus += new EventHandler(myControl_LostFocus); + } + + public override void Dispose() + { + base.Dispose(); + ((VirindiViewService.Controls.HudTextBox)myControl).Change -= new EventHandler(TextBox_Change); + myControl.LostFocus -= new EventHandler(myControl_LostFocus); + } + + void TextBox_Change(object sender, EventArgs e) + { + if (Change != null) + Change(this, new MVTextBoxChangeEventArgs(this.Id, Text)); + if (Change_Old != null) + Change_Old(this, null); + } + + void myControl_LostFocus(object sender, EventArgs e) + { + if (!myControl.HasFocus) return; + + if (End != null) + End(this, new MVTextBoxEndEventArgs(this.Id, true)); + } + + #region ITextBox Members + + public string Text + { + get + { + return ((VirindiViewService.Controls.HudTextBox)myControl).Text; + } + set + { + ((VirindiViewService.Controls.HudTextBox)myControl).Text = value; + } + } + + public int Caret + { + get + { + return 0; + } + set + { + + } + } + + public event EventHandler Change; + public event EventHandler Change_Old; + public event EventHandler End; + + #endregion + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + class Combo : Control, ICombo + { + List iData = new List(); + + public Combo() + { + //TODO: add data values from the xml + } + + public class ComboIndexer : IComboIndexer + { + Combo underlying; + internal ComboIndexer(Combo c) + { + underlying = c; + } + + #region IComboIndexer Members + + public string this[int index] + { + get + { + return ((VirindiViewService.Controls.HudStaticText)(((VirindiViewService.Controls.HudCombo)underlying.myControl)[index])).Text; + } + set + { + ((VirindiViewService.Controls.HudStaticText)(((VirindiViewService.Controls.HudCombo)underlying.myControl)[index])).Text = value; + } + } + + #endregion + } + + public class ComboDataIndexer : IComboDataIndexer + { + Combo underlying; + internal ComboDataIndexer(Combo c) + { + underlying = c; + } + + #region IComboIndexer Members + + public object this[int index] + { + get + { + return underlying.iData[index]; + } + set + { + underlying.iData[index] = value; + } + } + + #endregion + } + + public override void Initialize() + { + base.Initialize(); + ((VirindiViewService.Controls.HudCombo)myControl).Change += new EventHandler(Combo_Change); + } + + public override void Dispose() + { + base.Dispose(); + ((VirindiViewService.Controls.HudCombo)myControl).Change -= new EventHandler(Combo_Change); + } + + void Combo_Change(object sender, EventArgs e) + { + if (Change != null) + Change(this, new MVIndexChangeEventArgs(this.Id, Selected)); + if (Change_Old != null) + Change_Old(this, null); + } + + #region ICombo Members + + public IComboIndexer Text + { + get { return new ComboIndexer(this); } + } + + public IComboDataIndexer Data + { + get { return new ComboDataIndexer(this); } + } + + public int Count + { + get { return ((VirindiViewService.Controls.HudCombo)myControl).Count; } + } + + public int Selected + { + get + { + return ((VirindiViewService.Controls.HudCombo)myControl).Current; + } + set + { + ((VirindiViewService.Controls.HudCombo)myControl).Current = value; + } + } + + public event EventHandler Change; + public event EventHandler Change_Old; + + public void Add(string text) + { + ((VirindiViewService.Controls.HudCombo)myControl).AddItem(text, null); + iData.Add(null); + } + + public void Add(string text, object obj) + { + ((VirindiViewService.Controls.HudCombo)myControl).AddItem(text, null); + iData.Add(obj); + } + + public void Insert(int index, string text) + { + ((VirindiViewService.Controls.HudCombo)myControl).InsertItem(index, text, null); + iData.Insert(index, null); + } + + public void RemoveAt(int index) + { + ((VirindiViewService.Controls.HudCombo)myControl).DeleteItem(index); + iData.RemoveAt(index); + } + + public void Remove(int index) + { + RemoveAt(index); + } + + public void Clear() + { + ((VirindiViewService.Controls.HudCombo)myControl).Clear(); + iData.Clear(); + } + + #endregion + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + class Slider : Control, ISlider + { + public override void Initialize() + { + base.Initialize(); + ((VirindiViewService.Controls.HudHSlider)myControl).Changed += new VirindiViewService.Controls.LinearPositionControl.delScrollChanged(Slider_Changed); + } + + public override void Dispose() + { + base.Dispose(); + ((VirindiViewService.Controls.HudHSlider)myControl).Changed -= new VirindiViewService.Controls.LinearPositionControl.delScrollChanged(Slider_Changed); + } + + void Slider_Changed(int min, int max, int pos) + { + if (Change != null) + Change(this, new MVIndexChangeEventArgs(this.Id, pos)); + if (Change_Old != null) + Change_Old(this, null); + } + + #region ISlider Members + + public int Position + { + get + { + return ((VirindiViewService.Controls.HudHSlider)myControl).Position; + } + set + { + ((VirindiViewService.Controls.HudHSlider)myControl).Position = value; + } + } + + public event EventHandler Change; + public event EventHandler Change_Old; + + public int Maximum + { + get + { + return ((VirindiViewService.Controls.HudHSlider)myControl).Max; + } + set + { + ((VirindiViewService.Controls.HudHSlider)myControl).Max = value; + } + } + public int Minimum + { + get + { + return ((VirindiViewService.Controls.HudHSlider)myControl).Min; + } + set + { + ((VirindiViewService.Controls.HudHSlider)myControl).Min = value; + } + } + + #endregion + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + class List : Control, IList + { + public override void Initialize() + { + base.Initialize(); + ((VirindiViewService.Controls.HudList)myControl).Click += new VirindiViewService.Controls.HudList.delClickedControl(List_Click); + } + + public override void Dispose() + { + base.Dispose(); + ((VirindiViewService.Controls.HudList)myControl).Click -= new VirindiViewService.Controls.HudList.delClickedControl(List_Click); + } + + void List_Click(object sender, int row, int col) + { + if (Click != null) + Click(this, row, col); + if (Selected != null) + Selected(this, new MVListSelectEventArgs(this.Id, row, col)); + } + + public class ListRow : IListRow + { + List myList; + int myRow; + internal ListRow(int row, List l) + { + myList = l; + myRow = row; + } + + #region IListRow Members + + public IListCell this[int col] + { + get { return new ListCell(myRow, col, myList); } + } + + #endregion + } + public class ListCell : IListCell + { + List myList; + int myRow; + int myCol; + internal ListCell(int row, int col, List l) + { + myRow = row; + myCol = col; + myList = l; + } + + #region IListCell Members + + public void ResetColor() + { + ((VirindiViewService.Controls.HudStaticText)(((VirindiViewService.Controls.HudList)myList.myControl)[myRow][myCol])).ResetTextColor(); + } + + public System.Drawing.Color Color + { + get + { + return ((VirindiViewService.Controls.HudStaticText)(((VirindiViewService.Controls.HudList)myList.myControl)[myRow][myCol])).TextColor; + } + set + { + ((VirindiViewService.Controls.HudStaticText)(((VirindiViewService.Controls.HudList)myList.myControl)[myRow][myCol])).TextColor = value; + } + } + + public int Width + { + get + { + return ((VirindiViewService.Controls.HudStaticText)(((VirindiViewService.Controls.HudList)myList.myControl)[myRow][myCol])).ClipRegion.Width; + } + set + { + throw new Exception("The method or operation is not implemented."); + } + } + + public object this[int subval] + { + get + { + VirindiViewService.Controls.HudControl c = ((VirindiViewService.Controls.HudList)myList.myControl)[myRow][myCol]; + if (subval == 0) + { + if (c.GetType() == typeof(VirindiViewService.Controls.HudStaticText)) + return ((VirindiViewService.Controls.HudStaticText)c).Text; + if (c.GetType() == typeof(VirindiViewService.Controls.HudCheckBox)) + return ((VirindiViewService.Controls.HudCheckBox)c).Checked; + } + else if (subval == 1) + { + if (c.GetType() == typeof(VirindiViewService.Controls.HudPictureBox)) + return ((VirindiViewService.Controls.HudPictureBox)c).Image.PortalImageID; + } + return null; + } + set + { + VirindiViewService.Controls.HudControl c = ((VirindiViewService.Controls.HudList)myList.myControl)[myRow][myCol]; + if (subval == 0) + { + if (c.GetType() == typeof(VirindiViewService.Controls.HudStaticText)) + ((VirindiViewService.Controls.HudStaticText)c).Text = (string)value; + if (c.GetType() == typeof(VirindiViewService.Controls.HudCheckBox)) + ((VirindiViewService.Controls.HudCheckBox)c).Checked = (bool)value; + } + else if (subval == 1) + { + if (c.GetType() == typeof(VirindiViewService.Controls.HudPictureBox)) + ((VirindiViewService.Controls.HudPictureBox)c).Image = (int)value; + } + } + } + + #endregion + } + + #region IList Members + + public event dClickedList Click; + public event EventHandler Selected; + + public void Clear() + { + ((VirindiViewService.Controls.HudList)myControl).ClearRows(); + } + + public IListRow this[int row] + { + get { return new ListRow(row, this); } + } + + public IListRow AddRow() + { + ((VirindiViewService.Controls.HudList)myControl).AddRow(); + return new ListRow(((VirindiViewService.Controls.HudList)myControl).RowCount - 1, this); + } + + public IListRow Add() + { + return AddRow(); + } + + public IListRow InsertRow(int pos) + { + ((VirindiViewService.Controls.HudList)myControl).InsertRow(pos); + return new ListRow(pos, this); + } + + public IListRow Insert(int pos) + { + return InsertRow(pos); + } + + public int RowCount + { + get { return ((VirindiViewService.Controls.HudList)myControl).RowCount; } + } + + public void RemoveRow(int index) + { + ((VirindiViewService.Controls.HudList)myControl).RemoveRow(index); + } + + public void Delete(int index) + { + RemoveRow(index); + } + + public int ColCount + { + get + { + return ((VirindiViewService.Controls.HudList)myControl).ColumnCount; + } + } + + public int ScrollPosition + { + get + { + return ((VirindiViewService.Controls.HudList)myControl).ScrollPosition; + } + set + { + ((VirindiViewService.Controls.HudList)myControl).ScrollPosition = value; + } + } + + #endregion + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + class StaticText : Control, IStaticText + { + public override void Initialize() + { + base.Initialize(); + //((VirindiViewService.Controls.HudStaticText)myControl) + } + + public override void Dispose() + { + base.Dispose(); + } + + #region IStaticText Members + + public string Text + { + get + { + return ((VirindiViewService.Controls.HudStaticText)myControl).Text; + } + set + { + ((VirindiViewService.Controls.HudStaticText)myControl).Text = value; + } + } + +#pragma warning disable 0067 + public event EventHandler Click; +#pragma warning restore 0067 + + #endregion + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + class Notebook : Control, INotebook + { + public override void Initialize() + { + base.Initialize(); + ((VirindiViewService.Controls.HudTabView)myControl).OpenTabChange += new EventHandler(Notebook_OpenTabChange); + } + + public override void Dispose() + { + ((VirindiViewService.Controls.HudTabView)myControl).OpenTabChange -= new EventHandler(Notebook_OpenTabChange); + base.Dispose(); + } + + void Notebook_OpenTabChange(object sender, EventArgs e) + { + if (Change != null) + Change(this, new MVIndexChangeEventArgs(this.Id, ActiveTab)); + } + + #region INotebook Members + + public event EventHandler Change; + + public int ActiveTab + { + get + { + return ((VirindiViewService.Controls.HudTabView)myControl).CurrentTab; + } + set + { + ((VirindiViewService.Controls.HudTabView)myControl).CurrentTab = value; + ((VirindiViewService.Controls.HudTabView)myControl).Invalidate(); + } + } + + #endregion + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + class ProgressBar : Control, IProgressBar + { + + #region IProgressBar Members + + public int Position + { + get + { + return ((VirindiViewService.Controls.HudProgressBar)myControl).Position; + } + set + { + ((VirindiViewService.Controls.HudProgressBar)myControl).Position = value; + } + } + + public int Value + { + get + { + return Position; + } + set + { + Position = value; + } + } + + public string PreText + { + get + { + return ((VirindiViewService.Controls.HudProgressBar)myControl).PreText; + } + set + { + ((VirindiViewService.Controls.HudProgressBar)myControl).PreText = value; + } + } + + + public int MaxValue + { + get + { + return ((VirindiViewService.Controls.HudProgressBar)myControl).Max; + } + set + { + ((VirindiViewService.Controls.HudProgressBar)myControl).Max = value; + } + } + + #endregion + } + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + class ImageButton : Control, IImageButton + { + public override void Initialize() + { + base.Initialize(); + myControl.MouseEvent += new EventHandler(Button_MouseEvent); + } + + public override void Dispose() + { + base.Dispose(); + myControl.MouseEvent -= new EventHandler(Button_MouseEvent); + } + + void Button_MouseEvent(object sender, VirindiViewService.Controls.ControlMouseEventArgs e) + { + switch (e.EventType) + { + case VirindiViewService.Controls.ControlMouseEventArgs.MouseEventType.MouseHit: + if (Click != null) + Click(this, new MVControlEventArgs(this.Id)); + return; + } + } + + #region IImageButton Members + + public event EventHandler Click; + + public void SetImages(int unpressed, int pressed) + { + ACImage upimg; + if (!VirindiViewService.Service.PortalBitmapExists(unpressed | 0x06000000)) + upimg = new ACImage(); + else + upimg = new ACImage(unpressed, ACImage.eACImageDrawOptions.DrawStretch); + + ACImage pimg; + if (!VirindiViewService.Service.PortalBitmapExists(pressed | 0x06000000)) + pimg = new ACImage(); + else + pimg = new ACImage(pressed, ACImage.eACImageDrawOptions.DrawStretch); + + ((VirindiViewService.Controls.HudImageButton)myControl).Image_Up = upimg; + ((VirindiViewService.Controls.HudImageButton)myControl).Image_Up_Pressing = pimg; + } + + public void SetImages(int hmodule, int unpressed, int pressed) + { + ((VirindiViewService.Controls.HudImageButton)myControl).Image_Up = ACImage.FromIconLibrary(unpressed, hmodule); + ((VirindiViewService.Controls.HudImageButton)myControl).Image_Up_Pressing = ACImage.FromIconLibrary(pressed, hmodule); + } + + public int Background + { + set + { + ((VirindiViewService.Controls.HudImageButton)myControl).Image_Background2 = new ACImage(value, ACImage.eACImageDrawOptions.DrawStretch); + } + } + + public System.Drawing.Color Matte + { + set + { + ((VirindiViewService.Controls.HudImageButton)myControl).Image_Background = new ACImage(value); + } + } + + #endregion + } +} + +#else +#warning VVS_REFERENCED not defined, MetaViewWrappers for VVS will not be available. +#endif \ No newline at end of file diff --git a/MosswartMassacre/Wrapper_WireupHelper.cs b/MosswartMassacre/Wrapper_WireupHelper.cs new file mode 100644 index 0000000..63deeaf --- /dev/null +++ b/MosswartMassacre/Wrapper_WireupHelper.cs @@ -0,0 +1,329 @@ +/////////////////////////////////////////////////////////////////////////////// +//File: Wrapper_WireupHelper.cs +// +//Description: A helper utility that emulates Decal.Adapter's automagic view +// creation and control/event wireup with the MetaViewWrappers. A separate set +// of attributes is used. +// +//References required: +// Wrapper.cs +// +//This file is Copyright (c) 2010 VirindiPlugins +// +//Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +//The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +/////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Text; +using System.Reflection; + +#if METAVIEW_PUBLIC_NS +namespace MetaViewWrappers +#else +namespace MyClasses.MetaViewWrappers +#endif +{ + #region Attribute Definitions + + [AttributeUsage(AttributeTargets.Class)] +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + sealed class MVWireUpControlEventsAttribute : Attribute + { + public MVWireUpControlEventsAttribute() { } + } + + [AttributeUsage(AttributeTargets.Field)] +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + sealed class MVControlReferenceAttribute : Attribute + { + string ctrl; + + // Summary: + // Construct a new ControlReference + // + // Parameters: + // control: + // Control to reference + public MVControlReferenceAttribute(string control) + { + ctrl = control; + } + + // Summary: + // The Control Name + public string Control + { + get + { + return ctrl; + } + } + } + + [AttributeUsage(AttributeTargets.Field)] +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + sealed class MVControlReferenceArrayAttribute : Attribute + { + private System.Collections.ObjectModel.Collection myControls; + + /// + /// Constructs a new ControlReference array + /// + /// Names of the controls to put in the array + public MVControlReferenceArrayAttribute(params string[] controls) + : base() + { + this.myControls = new System.Collections.ObjectModel.Collection(controls); + } + + /// + /// Control collection + /// + public System.Collections.ObjectModel.Collection Controls + { + get + { + return this.myControls; + } + } + } + + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + sealed class MVViewAttribute : Attribute + { + string res; + + // Summary: + // Constructs a new view from the specified resource + // + // Parameters: + // Resource: + // Embedded resource path + public MVViewAttribute(string resource) + { + res = resource; + } + + // Summary: + // The resource to load + public string Resource + { + get + { + return res; + } + } + } + + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + sealed class MVControlEventAttribute : Attribute + { + string c; + string e; + // Summary: + // Constructs the ControlEvent + // + // Parameters: + // control: + // Control Name + // + // controlEvent: + // Event to Wire + public MVControlEventAttribute(string control, string eventName) + { + c = control; + e = eventName; + } + + // Summary: + // Control Name + public string Control + { + get + { + return c; + } + } + + // + // Summary: + // Event to Wire + public string EventName + { + get + { + return e; + } + } + } + + #endregion Attribute Definitions + +#if VVS_WRAPPERS_PUBLIC + public +#else + internal +#endif + static class MVWireupHelper + { + private class ViewObjectInfo + { + public List Views = new List(); + } + static Dictionary VInfo = new Dictionary(); + + public static MyClasses.MetaViewWrappers.IView GetDefaultView(object ViewObj) + { + if (!VInfo.ContainsKey(ViewObj)) + return null; + if (VInfo[ViewObj].Views.Count == 0) + return null; + return VInfo[ViewObj].Views[0]; + } + + public static void WireupStart(object ViewObj, Decal.Adapter.Wrappers.PluginHost Host) + { + if (VInfo.ContainsKey(ViewObj)) + WireupEnd(ViewObj); + ViewObjectInfo info = new ViewObjectInfo(); + VInfo[ViewObj] = info; + + Type ObjType = ViewObj.GetType(); + + //Start views + object[] viewattrs = ObjType.GetCustomAttributes(typeof(MVViewAttribute), true); + foreach (MVViewAttribute a in viewattrs) + { + info.Views.Add(MyClasses.MetaViewWrappers.ViewSystemSelector.CreateViewResource(Host, a.Resource)); + } + + //Wire up control references + foreach (FieldInfo fi in ObjType.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy)) + { + if (Attribute.IsDefined(fi, typeof(MVControlReferenceAttribute))) + { + MVControlReferenceAttribute attr = (MVControlReferenceAttribute)Attribute.GetCustomAttribute(fi, typeof(MVControlReferenceAttribute)); + MetaViewWrappers.IControl mycontrol = null; + + //Try each view + foreach (MyClasses.MetaViewWrappers.IView v in info.Views) + { + try + { + mycontrol = v[attr.Control]; + } + catch { } + if (mycontrol != null) + break; + } + + if (mycontrol == null) + throw new Exception("Invalid control reference \"" + attr.Control + "\""); + + if (!fi.FieldType.IsAssignableFrom(mycontrol.GetType())) + throw new Exception("Control reference \"" + attr.Control + "\" is of wrong type"); + + fi.SetValue(ViewObj, mycontrol); + } + else if (Attribute.IsDefined(fi, typeof(MVControlReferenceArrayAttribute))) + { + MVControlReferenceArrayAttribute attr = (MVControlReferenceArrayAttribute)Attribute.GetCustomAttribute(fi, typeof(MVControlReferenceArrayAttribute)); + + //Only do the first view + if (info.Views.Count == 0) + throw new Exception("No views to which a control reference can attach"); + + Array controls = Array.CreateInstance(fi.FieldType.GetElementType(), attr.Controls.Count); + + IView view = info.Views[0]; + for (int i = 0; i < attr.Controls.Count; ++i) + { + controls.SetValue(view[attr.Controls[i]], i); + } + + fi.SetValue(ViewObj, controls); + } + } + + //Wire up events + foreach (MethodInfo mi in ObjType.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy)) + { + if (!Attribute.IsDefined(mi, typeof(MVControlEventAttribute))) + continue; + Attribute[] attrs = Attribute.GetCustomAttributes(mi, typeof(MVControlEventAttribute)); + + foreach (MVControlEventAttribute attr in attrs) + { + MetaViewWrappers.IControl mycontrol = null; + //Try each view + foreach (MyClasses.MetaViewWrappers.IView v in info.Views) + { + try + { + mycontrol = v[attr.Control]; + } + catch { } + if (mycontrol != null) + break; + } + + if (mycontrol == null) + throw new Exception("Invalid control reference \"" + attr.Control + "\""); + + EventInfo ei = mycontrol.GetType().GetEvent(attr.EventName); + ei.AddEventHandler(mycontrol, Delegate.CreateDelegate(ei.EventHandlerType, ViewObj, mi.Name)); + } + } + } + + public static void WireupEnd(object ViewObj) + { + if (!VInfo.ContainsKey(ViewObj)) + return; + + foreach (MyClasses.MetaViewWrappers.IView v in VInfo[ViewObj].Views) + v.Dispose(); + + VInfo.Remove(ViewObj); + } + } +} \ No newline at end of file diff --git a/README.md b/README.md index c006689..1b74fc4 100644 --- a/README.md +++ b/README.md @@ -1,220 +1,90 @@ -# MosswartMassacre - Advanced DECAL Plugin for Asheron's Call +# Mossy Plugins -> **Status**: Production Ready | VVS Direct Integration | Navigation Visualization Complete +A collection of DECAL plugins for Asheron's Call, providing utility overlays and automation features. -A comprehensive DECAL plugin for Asheron's Call that tracks monster kills, rare item discoveries, and provides advanced navigation route visualization with 3D rendering. + ## Contents + - `mossy.sln`: Visual Studio solution containing both projects. + - `GearCycler/`: Simple plugin with a UI button to cycle gear (placeholder behavior). + - `MosswartMassacre/`: Advanced plugin tracking monster kills, rare discoveries, and offering HTTP/telemetry features. + - `packages/`: Vendored NuGet packages (Newtonsoft.Json, YamlDotNet). -## 🚀 Features + ## Prerequisites + - Windows with .NET Framework 4.8 + - Visual Studio 2017+ (MSBuild Tools 15.0) or equivalent MSBuild environment + - DECAL Adapter installed for Asheron's Call + - VirindiViewService (included in each project's `lib/` folder) -### Core Functionality -- **Kill Tracking**: Real-time monster kill counting with rate calculations (kills/5min, kills/hour) -- **Rare Item Discovery**: Automatic rare detection and counter with optional meta state control -- **Statistics Dashboard**: Detailed session statistics with best hourly performance tracking -- **Multi-System Integration**: WebSocket streaming, HTTP command server, and telemetry support + ## Setup & Build + 1. Clone this repository. + 2. Ensure the DECAL and Virindi DLLs are present under `MosswartMassacre/lib/` and referenced by each project. + 3. Restore NuGet packages if needed (`nuget restore mossy.sln`). + 4. Open `mossy.sln` in Visual Studio and build the solution. + 5. The output DLLs will be in each project’s `bin/Debug/` or `bin/Release/` folder. + 6. Deploy the plugin DLLs (and any required XML or YAML files) to your DECAL plugin directory. -### 🗺️ Navigation Visualization -**Advanced VTank route visualization with 3D rendering capabilities** -- **3D Route Display**: Renders VTank .nav files as red lines in the game world -- **Route Comparison**: Side-by-side visualization with UtilityBelt's active navigation -- **Full Format Support**: All VTank nav types (Circular, Linear, Target, Once) and waypoint types -- **Auto-Discovery**: Automatically detects VTank installation and scans for .nav files -- **Performance Optimized**: Smart rendering limits and memory management + ## GearCycler + A minimal plugin demonstrating a VirindiViewService-based UI. + - UI layout: `GearCycler/ViewXML/mainView.xml`. + - Core logic in `GearCycler/GearCore.cs`. + - On button click, it logs a chat message; extend the `btnCycle.Hit` handler to add gear-cycling logic. -### 🎛️ User Interface -**Modern tabbed interface using direct VirindiViewService integration** -- **Main Tab**: Live kill stats, rare counts, elapsed time, and status indicators -- **Settings Tab**: Plugin configuration with real-time updates -- **Statistics Tab**: Enhanced analytics and session management -- **Navigation Tab**: Route selection, visualization controls, and status display + ## MosswartMassacre + Tracks monster kills and rare drops, with multiple utility features including navigation route visualization. -## 📥 Installation + ### Features + - **Kill Tracking**: Counts total kills and computes rates (kills/5 min, kills/hour). + - **Rare Discoveries**: Increments rare count and can automatically set rare meta state. + - **Navigation Visualization** ✅ **NEW**: Visualize VTank navigation routes in 3D with route comparison capabilities. + - **Tabbed UI Interface**: Enhanced interface with Main, Settings, Statistics, and Navigation tabs. + - **Command Interface** (`/mm` commands): + - `/mm help` : Show available commands. + - `/mm report` : Display current stats in chat. + - `/mm loc` : Show current map coordinates. + - `/mm reset` : Reset kill counters and timers. + - `/mm meta` : Toggle automatic rare meta state. + - `/mm http ` : Start/stop local HTTP command server (port 8085). + - `/mm remotecommands ` : Listen for remote commands from your allegiance chat. + - `/mm telemetry ` : Enable/disable periodic telemetry streaming. -### Prerequisites -- Windows with .NET Framework 4.8 -- Asheron's Call with DECAL Adapter installed -- VirindiViewService (included in lib/ folder) + ### HTTP Command Server + - Listens on `http://localhost:8085/`. + - Accepts POST data: `target=&command=`, then sends a /tell and executes the command. -### Quick Setup -1. Download the latest release from the releases page -2. Extract to your DECAL plugins directory -3. Restart DECAL and enable the plugin -4. Configure settings through the in-game UI + ### Navigation Visualization ✅ NEW + - **VTank Integration**: Automatically detects VTank installation and loads .nav files. + - **3D Route Display**: Shows navigation routes as red lines in the game world. + - **Route Comparison**: Visualize different routes alongside UtilityBelt's active navigation. + - **Supported Formats**: All VTank nav types (Circular, Linear, Target, Once) and waypoint types. + - **Usage**: Enable in Navigation tab, select route from dropdown, click "Load Route". -### Building from Source -```bash -# Clone the repository -git clone [repository-url] -cd MosswartMassacre + ### Configuration + - Per-character YAML config stored at `/.yaml`. + - Settings include: + - `remote_commands_enabled` + - `rare_meta_enabled` + - `http_server_enabled` + - `telemetry_enabled` + - `char_tag` + - `vtank_profiles_path` ✅ **NEW**: Custom VTank profiles directory + - Config is auto-generated on first run; modify it or use UI/commands to update. -# Restore packages and build -nuget restore packages.config -msbuild MosswartMassacre.csproj /p:Configuration=Release /p:Platform=AnyCPU -``` + ### Telemetry + - Periodically posts JSON snapshots of position and stats to a configurable endpoint. + - Configure `Endpoint`, `SharedSecret`, and `IntervalSec` in `Telemetry.cs`. -## 🎮 Usage + ## Dependencies + - Decal.Adapter (v2.9.8.3) + - Decal.Interop.Core & Decal.Interop.Inject + - Decal.Interop.D3DService ✅ **NEW**: For 3D navigation visualization + - VirindiViewService + - Newtonsoft.Json (v13.0.3) + - YamlDotNet (v16.3.0) -### Basic Commands -Access all features through the `/mm` command interface: + ## Contributing + 1. Fork the repository. + 2. Create a feature branch. + 3. Commit your changes and ensure the solution builds. + 4. Submit a pull request with a description of your changes. -``` -/mm help - Show available commands -/mm report - Display current kill statistics -/mm loc - Show current map coordinates -/mm reset - Reset kill counters and timers -/mm meta - Toggle automatic rare meta state -/mm http - Control HTTP command server (port 8085) -/mm telemetry - Control telemetry streaming -``` - -### Navigation Visualization -1. **Enable**: Check "Enable Navigation Visualization" in Navigation tab -2. **Configure**: Set VTank profiles path in Settings (auto-detected) -3. **Select Route**: Choose from dropdown and click "Load Route" -4. **View**: Red route lines appear in 3D game world - -### Configuration -Settings are stored per-character in YAML format at `/.yaml`: - -```yaml -rare_meta_enabled: true -remote_commands_enabled: false -http_server_enabled: false -websocket_enabled: true -telemetry_enabled: false -char_tag: "default" -vtank_profiles_path: "C:\\Games\\VirindiPlugins\\VirindiTank\\" -main_window_x: 100 -main_window_y: 100 -``` - -## 🏗️ Architecture - -### Core Components -- **PluginCore.cs**: Main entry point and event coordination -- **PluginSettings.cs**: YAML-based per-character configuration -- **Views/VVSTabbedMainView.cs**: Main tabbed UI with direct VVS integration -- **Views/VVSBaseView.cs**: Base class for VVS-based views - -### Navigation System -- **NavRoute.cs**: VTank .nav file parser and 3D renderer -- **NavVisualization.cs**: Route management and file discovery -- **Registry Integration**: Automatic VTank directory detection - -### Communication Systems -- **WebSocket.cs**: Real-time data streaming to external services -- **HttpCommandServer.cs**: Local HTTP API for remote control -- **Telemetry.cs**: Periodic statistics reporting - -### Game Integration -- **VtankControl.cs**: vTank automation interface -- **MossyInventory.cs**: Inventory monitoring and rare detection -- **Utils.cs**: Game coordinate systems and utility functions - -## 🔧 Technical Details - -### Dependencies -- **DECAL Framework**: Core plugin system (Decal.Adapter, Decal.Interop.Core, Decal.Interop.D3DService) -- **VirindiViewService**: UI framework for game overlays -- **Newtonsoft.Json**: JSON serialization for APIs -- **YamlDotNet**: Configuration file management - -### Build Configuration -- **Target**: .NET Framework 4.8, x86 platform -- **Architecture**: Direct VVS integration (no wrapper abstraction) -- **Features**: Unsafe blocks enabled for P/Invoke operations - -### Navigation File Format Support -**Complete VTank .nav format compatibility:** -- **Nav Types**: Circular (1), Linear (0/2), Target (3), Once (4) -- **Waypoint Types**: Point, Portal, Recall, Pause, ChatCommand, OpenVendor, Portal2, UseNPC, Checkpoint, Jump -- **Performance**: Optimized for routes up to 10,000 waypoints with 500-segment rendering limit - -## 🔌 API Integration - -### HTTP Command Server -```bash -# Enable server -curl -X POST http://localhost:8085/ -d "target=PlayerName&command=report" - -# Available endpoints -POST / - Execute command for target player -``` - -### WebSocket Streaming -Real-time data streaming to `wss://overlord.snakedesert.se/websocket/` including: -- Monster spawn/despawn events -- Chat messages and rare discoveries -- Player position and statistics -- Session-based authentication with SharedSecret - -### Telemetry Data -Periodic JSON snapshots posted to configurable endpoints: -```json -{ - "timestamp": "2024-12-19T10:30:00Z", - "character": "PlayerName", - "position": {"x": 59.2, "y": -28.7, "z": 0.05}, - "stats": {"kills": 150, "rares": 3, "session_time": "02:15:30"} -} -``` - -## 🛠️ Development - -### Project Structure -``` -MosswartMassacre/ -├── Views/ # VVS-based UI components -│ ├── VVSBaseView.cs # Base view foundation -│ └── VVSTabbedMainView.cs # Main tabbed interface -├── ViewXML/ # UI layout definitions -│ └── mainViewTabbed.xml # Current layout -├── NavRoute.cs # Navigation file parser -├── NavVisualization.cs # Route visualization manager -├── PluginCore.cs # Main plugin logic -├── PluginSettings.cs # Configuration management -└── lib/ # External dependencies -``` - -### Development Environment -- **IDE**: Visual Studio 2017+ or VS Code with C# extension -- **Tools**: MSBuild, NuGet Package Manager -- **Testing**: In-game with Asheron's Call client and DECAL - -### Contributing -1. Fork the repository -2. Create feature branch (`git checkout -b feature/amazing-feature`) -3. Commit changes (`git commit -m 'Add amazing feature'`) -4. Push to branch (`git push origin feature/amazing-feature`) -5. Open a Pull Request - -## 📚 Related Documentation - -- **CLAUDE.md**: Claude AI development guidance and build commands -- **Development History**: Successful VVS migration completed, wrapper system removed -- **Architecture Evolution**: Migrated from wrapper-based to direct VVS integration - -## 📄 License - -This project is licensed under the MIT License - see the LICENSE file for details. - -## 🎯 Roadmap - -### Completed ✅ -- [x] VVS Direct Integration Migration -- [x] Navigation Visualization System -- [x] Tabbed UI Interface -- [x] WebSocket Streaming -- [x] HTTP Command API -- [x] Telemetry System -- [x] Architecture Cleanup (Phase 3) - -### Future Enhancements -- [ ] Multiple route visualization -- [ ] Route analysis and optimization tools -- [ ] Enhanced UI controls and themes -- [ ] Plugin integration marketplace -- [ ] Advanced statistics and reporting - ---- - -*Built with ❤️ for the Asheron's Call community* \ No newline at end of file + -- + _This README provides a high-level overview to get up and running quickly._ \ No newline at end of file