MosswartMassacre/MosswartMassacre/NavVisualization.cs
2025-05-29 21:34:16 +02:00

246 lines
No EOL
8.4 KiB
C#

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<string> availableNavFiles = new List<string>();
// 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<string> 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 = "";
}
}
/// <summary>
/// Scan VTank directory for .nav files and populate available routes list
/// Filters out follow files and temporary files, sorts alphabetically
/// </summary>
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}");
}
}
/// <summary>
/// Load a specific navigation route file for visualization
/// Clears current route if "None" specified, otherwise loads .nav file
/// </summary>
/// <param name="navFileName">Name of .nav file (without extension) or "None"</param>
/// <returns>True if route loaded successfully, false otherwise</returns>
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;
}
}
/// <summary>
/// Enable or disable navigation route visualization in 3D world
/// Shows/hides the currently loaded route based on enabled state
/// </summary>
/// <param name="enabled">True to show route lines, false to hide</param>
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;
}
}
}
}