using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; using Decal.Adapter; using Microsoft.Win32; namespace MosswartMassacre { public class NavVisualization : IDisposable { private bool disposed = false; private NavRoute currentRoute = null; private string vtankProfilesDirectory = ""; private List availableNavFiles = new List(); // Default comparison route color (red) private readonly Color comparisonRouteColor = Color.FromArgb(255, 255, 100, 100); public bool IsEnabled { get; private set; } = false; public bool HasRouteLoaded => currentRoute != null && currentRoute.WaypointCount > 0; public string CurrentRouteFile => currentRoute?.FileName ?? "None"; public List AvailableNavFiles => availableNavFiles.ToList(); public event EventHandler RouteChanged; public NavVisualization() { InitializeVTankDirectory(); RefreshNavFileList(); } private void InitializeVTankDirectory() { try { // First, check if user has configured a custom path if (!string.IsNullOrEmpty(PluginSettings.Instance?.VTankProfilesPath)) { vtankProfilesDirectory = PluginSettings.Instance.VTankProfilesPath; return; } // Try to get VTank directory from Windows Registry (same method as UtilityBelt) var defaultPath = @"C:\Games\VirindiPlugins\VirindiTank\"; try { var regKey = Registry.LocalMachine.OpenSubKey("Software\\Decal\\Plugins\\{642F1F48-16BE-48BF-B1D4-286652C4533E}"); if (regKey != null) { var profilePath = regKey.GetValue("ProfilePath")?.ToString(); if (!string.IsNullOrEmpty(profilePath)) { vtankProfilesDirectory = profilePath; return; } } } catch { } // Fall back to default path vtankProfilesDirectory = defaultPath; // Using default path - user can configure in Settings if needed } catch (Exception ex) { PluginCore.WriteToChat($"[NavViz] Error finding VTank directory: {ex.Message}"); vtankProfilesDirectory = ""; } } /// /// 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 InitializeVTankDirectory(); availableNavFiles.Clear(); if (string.IsNullOrEmpty(vtankProfilesDirectory)) { PluginCore.WriteToChat("VTank directory not configured. Set path in Settings tab."); return; } if (!Directory.Exists(vtankProfilesDirectory)) { PluginCore.WriteToChat($"VTank directory not found: {vtankProfilesDirectory}"); return; } try { // Get all files and filter for .nav files only, excluding follow/temporary files var allFiles = Directory.GetFiles(vtankProfilesDirectory); var navFiles = allFiles .Where(file => Path.GetExtension(file).Equals(".nav", StringComparison.OrdinalIgnoreCase)) .Select(file => Path.GetFileNameWithoutExtension(file)) .Where(name => !string.IsNullOrEmpty(name) && !name.StartsWith("follow", StringComparison.OrdinalIgnoreCase) && !name.StartsWith("--", StringComparison.OrdinalIgnoreCase)) .OrderBy(name => name) .ToList(); 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"); } } catch (Exception ex) { PluginCore.WriteToChat($"Navigation: Error scanning 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 { // Clear current route if (currentRoute != null) { currentRoute.Dispose(); currentRoute = null; } if (string.IsNullOrEmpty(navFileName) || navFileName == "None") { RouteChanged?.Invoke(this, EventArgs.Empty); return true; } string fullPath = Path.Combine(vtankProfilesDirectory, navFileName + ".nav"); if (!File.Exists(fullPath)) { PluginCore.WriteToChat($"Navigation file '{navFileName}' not found"); return false; } currentRoute = new NavRoute(fullPath, comparisonRouteColor); if (!currentRoute.LoadFromFile()) { currentRoute.Dispose(); currentRoute = null; return false; } // Show route if visualization is enabled if (IsEnabled) { currentRoute.Show(); } RouteChanged?.Invoke(this, EventArgs.Empty); return true; } catch (Exception ex) { PluginCore.WriteToChat($"Navigation: Failed to load '{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; IsEnabled = enabled; if (currentRoute != null) { if (enabled) { currentRoute.Show(); } else { currentRoute.Hide(); } } else { } PluginCore.WriteToChat($"Navigation visualization {(enabled ? "enabled" : "disabled")}"); } public void ToggleEnabled() { SetEnabled(!IsEnabled); } public string GetStatus() { if (currentRoute == null) return "No route loaded"; string status = $"{currentRoute.FileName} ({currentRoute.WaypointCount} points)"; if (IsEnabled && currentRoute.IsVisible) status += " - Visible"; else if (IsEnabled) status += " - Hidden"; else status += " - Disabled"; return status; } public void Dispose() { if (!disposed) { if (currentRoute != null) { currentRoute.Dispose(); currentRoute = null; } disposed = true; } } } }