added nav 3.0.0.6
This commit is contained in:
parent
1ddfc9fbdf
commit
c05d6c9d1b
6 changed files with 1825 additions and 262 deletions
477
MosswartMassacre/NavRoute.cs
Normal file
477
MosswartMassacre/NavRoute.cs
Normal file
|
|
@ -0,0 +1,477 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using Decal.Adapter;
|
||||
using Decal.Adapter.Wrappers;
|
||||
|
||||
namespace MosswartMassacre
|
||||
{
|
||||
public class NavWaypoint
|
||||
{
|
||||
public double NS { get; set; }
|
||||
public double EW { get; set; }
|
||||
public double Z { get; set; }
|
||||
public int Type { get; set; }
|
||||
public NavWaypoint Previous { get; set; }
|
||||
}
|
||||
|
||||
public class NavRoute : IDisposable
|
||||
{
|
||||
private bool disposed = false;
|
||||
private List<NavWaypoint> waypoints = new List<NavWaypoint>();
|
||||
private List<D3DObj> lineObjects = new List<D3DObj>();
|
||||
private Color routeColor;
|
||||
private bool isVisible = false;
|
||||
|
||||
public string FilePath { get; private set; }
|
||||
public string FileName => Path.GetFileNameWithoutExtension(FilePath);
|
||||
public bool IsVisible => isVisible;
|
||||
public int WaypointCount => waypoints.Count;
|
||||
|
||||
public NavRoute(string filePath, Color color)
|
||||
{
|
||||
FilePath = filePath;
|
||||
routeColor = color;
|
||||
}
|
||||
|
||||
public bool LoadFromFile()
|
||||
{
|
||||
try
|
||||
{
|
||||
ClearRoute();
|
||||
waypoints.Clear();
|
||||
|
||||
if (!File.Exists(FilePath))
|
||||
{
|
||||
PluginCore.WriteToChat($"Nav file not found: {FilePath}");
|
||||
return false;
|
||||
}
|
||||
|
||||
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($"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($"Could not parse nav type: {FileName} (line: '{navTypeLine}')");
|
||||
return false;
|
||||
}
|
||||
|
||||
string navTypeDescription = "";
|
||||
switch (navType)
|
||||
{
|
||||
case 0:
|
||||
navTypeDescription = "Linear";
|
||||
break;
|
||||
case 1:
|
||||
navTypeDescription = "Circular";
|
||||
break;
|
||||
case 2:
|
||||
navTypeDescription = "Linear";
|
||||
break;
|
||||
case 3:
|
||||
navTypeDescription = "Target (follow player/object)";
|
||||
break;
|
||||
case 4:
|
||||
navTypeDescription = "Once";
|
||||
break;
|
||||
default:
|
||||
navTypeDescription = $"Unknown ({navType})";
|
||||
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($"[NavRoute] Target nav file is empty: {FileName}");
|
||||
return false;
|
||||
}
|
||||
|
||||
string targetName = sr.ReadLine();
|
||||
if (sr.EndOfStream)
|
||||
{
|
||||
PluginCore.WriteToChat($"[NavRoute] Target nav missing target ID: {FileName}");
|
||||
return false;
|
||||
}
|
||||
|
||||
string targetIdLine = sr.ReadLine();
|
||||
|
||||
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($"Could not parse record count: {FileName} (line: '{recordCountLine}')");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (recordCount <= 0 || recordCount > 10000) // Sanity check
|
||||
{
|
||||
PluginCore.WriteToChat($"Invalid record count: {recordCount} in {FileName}");
|
||||
return false;
|
||||
}
|
||||
|
||||
NavWaypoint previous = null;
|
||||
int waypointsRead = 0;
|
||||
|
||||
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($"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();
|
||||
string nsLine = sr.ReadLine();
|
||||
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($"Missing coordinate lines at waypoint {waypointsRead}");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!double.TryParse(ewLine.Trim(), out double ew) ||
|
||||
!double.TryParse(nsLine.Trim(), out double ns) ||
|
||||
!double.TryParse(zLine.Trim(), out double z))
|
||||
{
|
||||
PluginCore.WriteToChat($"Could not parse coordinates at waypoint {waypointsRead}: EW={ewLine}, NS={nsLine}, Z={zLine}");
|
||||
break; // Skip this waypoint
|
||||
}
|
||||
|
||||
var waypoint = new NavWaypoint
|
||||
{
|
||||
NS = ns,
|
||||
EW = ew,
|
||||
Z = z,
|
||||
Type = waypointType,
|
||||
Previous = previous
|
||||
};
|
||||
|
||||
waypoints.Add(waypoint);
|
||||
previous = waypoint;
|
||||
waypointsRead++;
|
||||
|
||||
// Skip additional data based on waypoint type
|
||||
if (!SkipWaypointData(sr, waypointType))
|
||||
{
|
||||
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($"[NavRoute] Route ready for visualization with {waypoints.Count} waypoints");
|
||||
}
|
||||
|
||||
return waypoints.Count > 0;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"[NavRoute] Error loading nav file {FileName}: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool SkipWaypointData(StreamReader sr, int waypointType)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Skip additional lines based on waypoint type (base 4 lines already read)
|
||||
switch (waypointType)
|
||||
{
|
||||
case 0: // Point - no additional data (4 lines total)
|
||||
break;
|
||||
case 1: // Portal - 5 additional lines (9 lines total)
|
||||
sr.ReadLine(); // Name
|
||||
sr.ReadLine(); // ObjectClass
|
||||
sr.ReadLine(); // "true"
|
||||
sr.ReadLine(); // PortalNS
|
||||
sr.ReadLine(); // PortalEW
|
||||
sr.ReadLine(); // PortalZ
|
||||
break;
|
||||
case 2: // Recall - 1 additional line (5 lines total)
|
||||
sr.ReadLine(); // RecallSpellId
|
||||
break;
|
||||
case 3: // Pause - 1 additional line (5 lines total)
|
||||
sr.ReadLine(); // Pause milliseconds
|
||||
break;
|
||||
case 4: // ChatCommand - 1 additional line (5 lines total)
|
||||
sr.ReadLine(); // Message
|
||||
break;
|
||||
case 5: // OpenVendor - 2 additional lines (6 lines total)
|
||||
sr.ReadLine(); // Id
|
||||
sr.ReadLine(); // Name
|
||||
break;
|
||||
case 6: // Portal2 - same as Portal (9 lines total)
|
||||
sr.ReadLine(); // Name
|
||||
sr.ReadLine(); // ObjectClass
|
||||
sr.ReadLine(); // "true"
|
||||
sr.ReadLine(); // PortalNS
|
||||
sr.ReadLine(); // PortalEW
|
||||
sr.ReadLine(); // PortalZ
|
||||
break;
|
||||
case 7: // UseNPC - 5 additional lines (9 lines total)
|
||||
sr.ReadLine(); // Name
|
||||
sr.ReadLine(); // ObjectClass
|
||||
sr.ReadLine(); // "true"
|
||||
sr.ReadLine(); // NpcEW
|
||||
sr.ReadLine(); // NpcNS
|
||||
sr.ReadLine(); // NpcZ
|
||||
break;
|
||||
case 8: // Checkpoint - no additional data (4 lines total)
|
||||
break;
|
||||
case 9: // Jump - 3 additional lines (7 lines total)
|
||||
sr.ReadLine(); // Heading
|
||||
sr.ReadLine(); // ShiftJump
|
||||
sr.ReadLine(); // Milliseconds
|
||||
break;
|
||||
default:
|
||||
PluginCore.WriteToChat($"[NavRoute] Unknown waypoint type: {waypointType}");
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"[NavRoute] Error skipping waypoint data: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Show()
|
||||
{
|
||||
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($"[NavRoute] Route {FileName} has no waypoints to visualize");
|
||||
return;
|
||||
}
|
||||
|
||||
PluginCore.WriteToChat($"[NavRoute] Creating line objects for {FileName}...");
|
||||
CreateLineObjects();
|
||||
isVisible = true;
|
||||
PluginCore.WriteToChat($"[NavRoute] Route {FileName} visualization complete - isVisible: {isVisible}");
|
||||
}
|
||||
|
||||
public void Hide()
|
||||
{
|
||||
if (!isVisible) return;
|
||||
|
||||
ClearRoute();
|
||||
isVisible = false;
|
||||
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($"[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))
|
||||
{
|
||||
linesCreated++;
|
||||
}
|
||||
|
||||
// Add small delay every 50 lines to prevent UI freezing
|
||||
if (i % 50 == 0)
|
||||
{
|
||||
System.Threading.Thread.Sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
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($"[NavRoute] Route has {waypoints.Count} waypoints, showing first {maxLines} segments");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"[NavRoute] Error creating line objects: {ex.Message}");
|
||||
PluginCore.WriteToChat($"[NavRoute] Stack trace: {ex.StackTrace}");
|
||||
}
|
||||
}
|
||||
|
||||
private bool CreateLineBetweenWaypoints(NavWaypoint from, NavWaypoint to)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Calculate distance
|
||||
double distance = Math.Sqrt(
|
||||
Math.Pow((to.NS - from.NS) * 240, 2) +
|
||||
Math.Pow((to.EW - from.EW) * 240, 2) +
|
||||
Math.Pow((to.Z - from.Z) * 240, 2)
|
||||
);
|
||||
|
||||
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)
|
||||
{
|
||||
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
|
||||
float orientNS = (float)from.NS;
|
||||
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 (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"[NavRoute] Error creating line: {ex.Message}");
|
||||
PluginCore.WriteToChat($"[NavRoute] Stack trace: {ex.StackTrace}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearRoute()
|
||||
{
|
||||
foreach (var obj in lineObjects)
|
||||
{
|
||||
try
|
||||
{
|
||||
obj.Visible = false;
|
||||
obj.Dispose();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
lineObjects.Clear();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
ClearRoute();
|
||||
waypoints.Clear();
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
261
MosswartMassacre/NavVisualization.cs
Normal file
261
MosswartMassacre/NavVisualization.cs
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
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;
|
||||
PluginCore.WriteToChat($"[NavViz] Using configured VTank profiles path: {vtankProfilesDirectory}");
|
||||
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;
|
||||
PluginCore.WriteToChat($"[NavViz] Found VTank profiles from registry: {vtankProfilesDirectory}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception regEx)
|
||||
{
|
||||
PluginCore.WriteToChat($"[NavViz] Registry lookup failed: {regEx.Message}");
|
||||
}
|
||||
|
||||
// Fall back to default path
|
||||
vtankProfilesDirectory = defaultPath;
|
||||
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)
|
||||
{
|
||||
PluginCore.WriteToChat($"[NavViz] Error finding VTank directory: {ex.Message}");
|
||||
vtankProfilesDirectory = "";
|
||||
}
|
||||
}
|
||||
|
||||
public void RefreshNavFileList()
|
||||
{
|
||||
// Re-initialize directory in case settings changed
|
||||
InitializeVTankDirectory();
|
||||
|
||||
availableNavFiles.Clear();
|
||||
|
||||
PluginCore.WriteToChat($"[NavViz] Refreshing nav files from: {vtankProfilesDirectory}");
|
||||
|
||||
if (string.IsNullOrEmpty(vtankProfilesDirectory))
|
||||
{
|
||||
PluginCore.WriteToChat("[NavViz] No VTank directory set");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Directory.Exists(vtankProfilesDirectory))
|
||||
{
|
||||
PluginCore.WriteToChat($"[NavViz] Directory does not exist: {vtankProfilesDirectory}");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// 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 => {
|
||||
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))
|
||||
.OrderBy(name => name)
|
||||
.ToList();
|
||||
|
||||
availableNavFiles.AddRange(navFiles);
|
||||
|
||||
PluginCore.WriteToChat($"[NavViz] Found {navFiles.Count} .nav files: {string.Join(", ", navFiles)}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"[NavViz] Error refreshing nav files: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public bool LoadRoute(string navFileName)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Clear current route
|
||||
if (currentRoute != null)
|
||||
{
|
||||
currentRoute.Dispose();
|
||||
currentRoute = null;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(navFileName) || navFileName == "None")
|
||||
{
|
||||
PluginCore.WriteToChat("[NavViz] Route cleared");
|
||||
RouteChanged?.Invoke(this, EventArgs.Empty);
|
||||
return true;
|
||||
}
|
||||
|
||||
string fullPath = Path.Combine(vtankProfilesDirectory, navFileName + ".nav");
|
||||
|
||||
if (!File.Exists(fullPath))
|
||||
{
|
||||
PluginCore.WriteToChat($"[NavViz] Nav file not found: {navFileName}");
|
||||
return false;
|
||||
}
|
||||
|
||||
currentRoute = new NavRoute(fullPath, comparisonRouteColor);
|
||||
|
||||
if (!currentRoute.LoadFromFile())
|
||||
{
|
||||
currentRoute.Dispose();
|
||||
currentRoute = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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($"[NavViz] Error loading route {navFileName}: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetEnabled(bool enabled)
|
||||
{
|
||||
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();
|
||||
}
|
||||
else
|
||||
{
|
||||
currentRoute.Hide();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PluginCore.WriteToChat($"[NavViz] SetEnabled: no currentRoute to {(enabled ? "show" : "hide")}");
|
||||
}
|
||||
|
||||
PluginCore.WriteToChat($"[NavViz] 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
116
MosswartMassacre/ViewXML/mainViewTabbed.xml
Normal file
116
MosswartMassacre/ViewXML/mainViewTabbed.xml
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<view icon="7735" title="Mosswart Massacre v3.0" width="420" height="320">
|
||||
<control progid="DecalControls.Notebook" name="MainNotebook">
|
||||
<page label="Main">
|
||||
<control progid="DecalControls.FixedLayout" clipped="">
|
||||
<!-- Current kill tracking display -->
|
||||
<control progid="DecalControls.StaticText" name="lblTotalKills" left="10" top="10" width="250" height="20" text="Total Kills: 0"/>
|
||||
<control progid="DecalControls.StaticText" name="lblKillsPer5Min" left="10" top="30" width="250" height="20" text="Kills per 5 Min: 0"/>
|
||||
<control progid="DecalControls.StaticText" name="lblKillsPerHour" left="10" top="50" width="250" height="20" text="Kills per Hour: 0"/>
|
||||
<control progid="DecalControls.StaticText" name="lblElapsedTime" left="10" top="70" width="250" height="20" text="Time: 00:00:00"/>
|
||||
<control progid="DecalControls.StaticText" name="lblRareCount" left="10" top="90" width="250" height="20" text="Rare Count: 0"/>
|
||||
|
||||
<!-- Auto loot rare indicator -->
|
||||
<control progid="DecalControls.StaticText" name="lblAutoLootRare" left="10" top="120" width="200" height="20" text="Auto Loot Rare: [ON]"/>
|
||||
|
||||
<!-- Enhanced status display -->
|
||||
<control progid="DecalControls.StaticText" name="lblStatus" left="10" top="145" width="380" height="20" text="Status: Ready"/>
|
||||
<control progid="DecalControls.StaticText" name="lblWebSocketStatus" left="10" top="165" width="380" height="20" text="WebSocket: [DISCONNECTED]"/>
|
||||
</control>
|
||||
</page>
|
||||
|
||||
<page label="Settings">
|
||||
<control progid="DecalControls.FixedLayout" clipped="">
|
||||
<!-- Plugin settings -->
|
||||
<control progid="DecalControls.StaticText" name="lblSettingsHeader" left="10" top="10" width="200" height="20" text="Plugin Configuration" style="FontBold"/>
|
||||
|
||||
<!-- Meta state setting -->
|
||||
<control progid="DecalControls.Checkbox" name="chkRareMetaEnabled" left="20" top="35" width="300" height="20" text="Enable rare meta state automation" checked="true"/>
|
||||
|
||||
<!-- Remote commands setting -->
|
||||
<control progid="DecalControls.Checkbox" name="chkRemoteCommandsEnabled" left="20" top="60" width="300" height="20" text="Enable allegiance remote commands (!do/!dot)" checked="false"/>
|
||||
|
||||
<!-- HTTP server setting -->
|
||||
<control progid="DecalControls.Checkbox" name="chkHttpServerEnabled" left="20" top="85" width="300" height="20" text="Enable HTTP command server (port 8085)" checked="false"/>
|
||||
|
||||
<!-- WebSocket setting -->
|
||||
<control progid="DecalControls.Checkbox" name="chkWebSocketEnabled" left="20" top="110" width="300" height="20" text="Enable WebSocket streaming" checked="false"/>
|
||||
|
||||
<!-- Telemetry setting -->
|
||||
<control progid="DecalControls.Checkbox" name="chkTelemetryEnabled" left="20" top="135" width="300" height="20" text="Enable telemetry reporting" checked="false"/>
|
||||
|
||||
<!-- Character tag setting -->
|
||||
<control progid="DecalControls.StaticText" name="lblCharTag" left="20" top="165" width="100" height="16" text="Character Tag:"/>
|
||||
<control progid="DecalControls.Edit" name="txtCharTag" left="125" top="163" width="150" height="20" text="default"/>
|
||||
|
||||
<!-- VTank profiles path setting -->
|
||||
<control progid="DecalControls.StaticText" name="lblVTankPath" left="20" top="190" width="100" height="16" text="VTank Profiles:"/>
|
||||
<control progid="DecalControls.Edit" name="txtVTankPath" left="125" top="188" width="200" height="20" text=""/>
|
||||
<control progid="DecalControls.StaticText" name="lblVTankPathHelp" left="20" top="210" width="350" height="16" text="Leave empty for auto-detection. Set path if nav files not found."/>
|
||||
</control>
|
||||
</page>
|
||||
|
||||
<page label="Statistics">
|
||||
<control progid="DecalControls.FixedLayout" clipped="">
|
||||
<!-- Enhanced statistics display -->
|
||||
<control progid="DecalControls.StaticText" name="lblStatsHeader" left="10" top="10" width="200" height="20" text="Detailed Statistics" style="FontBold"/>
|
||||
|
||||
<!-- Kill statistics -->
|
||||
<control progid="DecalControls.StaticText" name="lblDetailedKills" left="10" top="35" width="180" height="16" text="Total Kills This Session:"/>
|
||||
<control progid="DecalControls.StaticText" name="lblDetailedKillsValue" left="200" top="35" width="100" height="16" text="0"/>
|
||||
|
||||
<control progid="DecalControls.StaticText" name="lblBestHour" left="10" top="55" width="180" height="16" text="Best Hour Performance:"/>
|
||||
<control progid="DecalControls.StaticText" name="lblBestHourValue" left="200" top="55" width="100" height="16" text="0 kills/hr"/>
|
||||
|
||||
<control progid="DecalControls.StaticText" name="lblAverageKills" left="10" top="75" width="180" height="16" text="Average Kills/Hour:"/>
|
||||
<control progid="DecalControls.StaticText" name="lblAverageKillsValue" left="200" top="75" width="100" height="16" text="0 kills/hr"/>
|
||||
|
||||
<!-- Rare statistics -->
|
||||
<control progid="DecalControls.StaticText" name="lblRareStats" left="10" top="105" width="180" height="16" text="Rares Found:"/>
|
||||
<control progid="DecalControls.StaticText" name="lblRareStatsValue" left="200" top="105" width="100" height="16" text="0"/>
|
||||
|
||||
<control progid="DecalControls.StaticText" name="lblRareRate" left="10" top="125" width="180" height="16" text="Rare Drop Rate:"/>
|
||||
<control progid="DecalControls.StaticText" name="lblRareRateValue" left="200" top="125" width="100" height="16" text="0.00%"/>
|
||||
|
||||
<!-- Time statistics -->
|
||||
<control progid="DecalControls.StaticText" name="lblSessionTime" left="10" top="155" width="180" height="16" text="Session Duration:"/>
|
||||
<control progid="DecalControls.StaticText" name="lblSessionTimeValue" left="200" top="155" width="100" height="16" text="00:00:00"/>
|
||||
|
||||
<control progid="DecalControls.StaticText" name="lblLastKill" left="10" top="175" width="180" height="16" text="Last Kill:"/>
|
||||
<control progid="DecalControls.StaticText" name="lblLastKillValue" left="200" top="175" width="100" height="16" text="Never"/>
|
||||
|
||||
<!-- Reset button -->
|
||||
<control progid="DecalControls.PushButton" name="btnResetStats" left="10" top="205" width="80" height="24" text="Reset All"/>
|
||||
</control>
|
||||
</page>
|
||||
|
||||
<page label="Navigation">
|
||||
<control progid="DecalControls.FixedLayout" clipped="">
|
||||
<!-- Navigation visualization controls -->
|
||||
<control progid="DecalControls.StaticText" name="lblNavHeader" left="10" top="10" width="250" height="20" text="Route Comparison Visualization" style="FontBold"/>
|
||||
|
||||
<!-- Enable/disable visualization -->
|
||||
<control progid="DecalControls.Checkbox" name="chkNavVisualizationEnabled" left="20" top="35" width="300" height="20" text="Enable route visualization" checked="false"/>
|
||||
|
||||
<!-- Nav file selection -->
|
||||
<control progid="DecalControls.StaticText" name="lblNavFile" left="20" top="65" width="120" height="16" text="Comparison Route:"/>
|
||||
<control progid="DecalControls.Choice" name="cmbNavFiles" left="145" top="63" width="200" height="20"/>
|
||||
<control progid="DecalControls.PushButton" name="btnRefreshNavFiles" left="350" top="62" width="60" height="22" text="Refresh"/>
|
||||
|
||||
<!-- Route status -->
|
||||
<control progid="DecalControls.StaticText" name="lblNavStatus" left="20" top="95" width="350" height="16" text="Status: No route loaded"/>
|
||||
|
||||
<!-- Color indicator -->
|
||||
<control progid="DecalControls.StaticText" name="lblNavColorInfo" left="20" top="115" width="350" height="16" text="Comparison route will be displayed in red"/>
|
||||
|
||||
<!-- Instructions -->
|
||||
<control progid="DecalControls.StaticText" name="lblNavInstructions" left="10" top="145" width="370" height="60" text="This allows you to visualize a different nav route alongside the one currently loaded in VTank (via UtilityBelt). Use this to compare routes and plan optimal paths. The comparison route will be drawn in red, while your active VTank route (if UtilityBelt VisualNav is enabled) will be in its configured color."/>
|
||||
|
||||
<!-- Control buttons -->
|
||||
<control progid="DecalControls.PushButton" name="btnLoadRoute" left="20" top="210" width="80" height="24" text="Load Route"/>
|
||||
<control progid="DecalControls.PushButton" name="btnClearRoute" left="110" top="210" width="80" height="24" text="Clear Route"/>
|
||||
</control>
|
||||
</page>
|
||||
|
||||
</control>
|
||||
</view>
|
||||
177
MosswartMassacre/Views/BaseView.cs
Normal file
177
MosswartMassacre/Views/BaseView.cs
Normal file
|
|
@ -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
|
||||
}
|
||||
}
|
||||
794
MosswartMassacre/Views/TabbedMainView.cs
Normal file
794
MosswartMassacre/Views/TabbedMainView.cs
Normal file
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,262 +0,0 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
//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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue