New GUI overhaul!, version 3.0.1.0
This commit is contained in:
parent
c05d6c9d1b
commit
79304baaad
16 changed files with 1579 additions and 4589 deletions
|
|
@ -29,7 +29,7 @@
|
||||||
<DebugType>pdbonly</DebugType>
|
<DebugType>pdbonly</DebugType>
|
||||||
<Optimize>true</Optimize>
|
<Optimize>true</Optimize>
|
||||||
<OutputPath>bin\Release\</OutputPath>
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
<DefineConstants>TRACE;VVS_REFERENCED;DECAL_INTEROP</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
@ -175,14 +175,9 @@
|
||||||
<DesignTime>True</DesignTime>
|
<DesignTime>True</DesignTime>
|
||||||
<DependentUpon>Resources.resx</DependentUpon>
|
<DependentUpon>Resources.resx</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="ViewSystemSelector.cs" />
|
<Compile Include="Views\VVSBaseView.cs" />
|
||||||
<Compile Include="Views\BaseView.cs" />
|
<Compile Include="Views\VVSTabbedMainView.cs" />
|
||||||
<Compile Include="Views\TabbedMainView.cs" />
|
|
||||||
<Compile Include="WebSocket.cs" />
|
<Compile Include="WebSocket.cs" />
|
||||||
<Compile Include="Wrapper.cs" />
|
|
||||||
<Compile Include="Wrapper_Decal.cs" />
|
|
||||||
<Compile Include="Wrapper_MyHuds.cs" />
|
|
||||||
<Compile Include="Wrapper_WireupHelper.cs" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="Properties\Resources.resx">
|
<EmbeddedResource Include="Properties\Resources.resx">
|
||||||
|
|
|
||||||
|
|
@ -48,25 +48,23 @@ namespace MosswartMassacre
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
PluginCore.WriteToChat($"[NavRoute] Loading {FileName}...");
|
PluginCore.WriteToChat($"Navigation: Loading {FileName}...");
|
||||||
|
|
||||||
using (StreamReader sr = File.OpenText(FilePath))
|
using (StreamReader sr = File.OpenText(FilePath))
|
||||||
{
|
{
|
||||||
// Read header
|
// Read header
|
||||||
string header = sr.ReadLine();
|
string header = sr.ReadLine();
|
||||||
PluginCore.WriteToChat($"[NavRoute] Header: '{header}'");
|
|
||||||
if (string.IsNullOrEmpty(header) || !header.StartsWith("uTank2 NAV"))
|
if (string.IsNullOrEmpty(header) || !header.StartsWith("uTank2 NAV"))
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat($"Invalid nav file format: {FileName} (header: {header})");
|
PluginCore.WriteToChat($"Navigation: Invalid file format - {FileName}");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read nav type
|
// Read nav type
|
||||||
string navTypeLine = sr.ReadLine();
|
string navTypeLine = sr.ReadLine();
|
||||||
PluginCore.WriteToChat($"[NavRoute] Nav type line: '{navTypeLine}'");
|
|
||||||
if (string.IsNullOrEmpty(navTypeLine) || !int.TryParse(navTypeLine.Trim(), out int navType))
|
if (string.IsNullOrEmpty(navTypeLine) || !int.TryParse(navTypeLine.Trim(), out int navType))
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat($"Could not parse nav type: {FileName} (line: '{navTypeLine}')");
|
PluginCore.WriteToChat($"Navigation: Failed to parse route type - {FileName}");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,46 +88,43 @@ namespace MosswartMassacre
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
navTypeDescription = $"Unknown ({navType})";
|
navTypeDescription = $"Unknown ({navType})";
|
||||||
PluginCore.WriteToChat($"[NavRoute] WARNING: Unknown nav type {navType} in {FileName}");
|
PluginCore.WriteToChat($"Navigation: Unknown route type {navType} in {FileName}");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
PluginCore.WriteToChat($"[NavRoute] Nav type: {navTypeDescription}");
|
|
||||||
|
|
||||||
// Handle target nav (type 3) - follows a specific player/object
|
// Handle target nav (type 3) - follows a specific player/object
|
||||||
if (navType == 3)
|
if (navType == 3)
|
||||||
{
|
{
|
||||||
if (sr.EndOfStream)
|
if (sr.EndOfStream)
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat($"[NavRoute] Target nav file is empty: {FileName}");
|
PluginCore.WriteToChat($"Navigation: Target route file is empty - {FileName}");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
string targetName = sr.ReadLine();
|
string targetName = sr.ReadLine();
|
||||||
if (sr.EndOfStream)
|
if (sr.EndOfStream)
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat($"[NavRoute] Target nav missing target ID: {FileName}");
|
PluginCore.WriteToChat($"Navigation: Target route missing target ID - {FileName}");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
string targetIdLine = sr.ReadLine();
|
string targetIdLine = sr.ReadLine();
|
||||||
|
|
||||||
PluginCore.WriteToChat($"[NavRoute] Target navigation for '{targetName}' (ID: {targetIdLine}) - No visual representation available");
|
PluginCore.WriteToChat($"Navigation: Target route '{targetName}' cannot be visualized");
|
||||||
return true; // Successfully loaded but can't visualize
|
return true; // Successfully loaded but can't visualize
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read record count
|
// Read record count
|
||||||
string recordCountLine = sr.ReadLine();
|
string recordCountLine = sr.ReadLine();
|
||||||
PluginCore.WriteToChat($"[NavRoute] Record count line: '{recordCountLine}'");
|
|
||||||
if (string.IsNullOrEmpty(recordCountLine) || !int.TryParse(recordCountLine.Trim(), out int recordCount))
|
if (string.IsNullOrEmpty(recordCountLine) || !int.TryParse(recordCountLine.Trim(), out int recordCount))
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat($"Could not parse record count: {FileName} (line: '{recordCountLine}')");
|
PluginCore.WriteToChat($"Navigation: Failed to parse waypoint count - {FileName}");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recordCount <= 0 || recordCount > 10000) // Sanity check
|
if (recordCount <= 0 || recordCount > 10000) // Sanity check
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat($"Invalid record count: {recordCount} in {FileName}");
|
PluginCore.WriteToChat($"Navigation: Invalid waypoint count {recordCount} - {FileName}");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -138,45 +133,24 @@ namespace MosswartMassacre
|
||||||
|
|
||||||
while (!sr.EndOfStream && waypointsRead < recordCount)
|
while (!sr.EndOfStream && waypointsRead < recordCount)
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat($"[NavRoute] Reading waypoint {waypointsRead + 1}/{recordCount}");
|
|
||||||
|
|
||||||
// Read waypoint type
|
// Read waypoint type
|
||||||
string waypointTypeLine = sr.ReadLine();
|
string waypointTypeLine = sr.ReadLine();
|
||||||
PluginCore.WriteToChat($"[NavRoute] Waypoint type line: '{waypointTypeLine}'");
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(waypointTypeLine) || !int.TryParse(waypointTypeLine.Trim(), out int waypointType))
|
if (string.IsNullOrEmpty(waypointTypeLine) || !int.TryParse(waypointTypeLine.Trim(), out int waypointType))
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat($"Could not parse waypoint type at waypoint {waypointsRead}: '{waypointTypeLine}'");
|
PluginCore.WriteToChat($"Navigation: Failed to parse waypoint {waypointsRead + 1} in {FileName}");
|
||||||
break; // Skip this waypoint, don't fail entirely
|
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)
|
// Read coordinates (all waypoint types have EW, NS, Z, Unknown)
|
||||||
string ewLine = sr.ReadLine();
|
string ewLine = sr.ReadLine();
|
||||||
string nsLine = sr.ReadLine();
|
string nsLine = sr.ReadLine();
|
||||||
string zLine = sr.ReadLine();
|
string zLine = sr.ReadLine();
|
||||||
string unknownLine = sr.ReadLine(); // Unknown value (always 0)
|
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))
|
if (string.IsNullOrEmpty(ewLine) || string.IsNullOrEmpty(nsLine) || string.IsNullOrEmpty(zLine) || string.IsNullOrEmpty(unknownLine))
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat($"Missing coordinate lines at waypoint {waypointsRead}");
|
PluginCore.WriteToChat($"Navigation: Missing coordinates at waypoint {waypointsRead + 1} in {FileName}");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -184,7 +158,7 @@ namespace MosswartMassacre
|
||||||
!double.TryParse(nsLine.Trim(), out double ns) ||
|
!double.TryParse(nsLine.Trim(), out double ns) ||
|
||||||
!double.TryParse(zLine.Trim(), out double z))
|
!double.TryParse(zLine.Trim(), out double z))
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat($"Could not parse coordinates at waypoint {waypointsRead}: EW={ewLine}, NS={nsLine}, Z={zLine}");
|
PluginCore.WriteToChat($"Navigation: Invalid coordinates at waypoint {waypointsRead + 1} in {FileName}");
|
||||||
break; // Skip this waypoint
|
break; // Skip this waypoint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -204,17 +178,18 @@ namespace MosswartMassacre
|
||||||
// Skip additional data based on waypoint type
|
// Skip additional data based on waypoint type
|
||||||
if (!SkipWaypointData(sr, waypointType))
|
if (!SkipWaypointData(sr, waypointType))
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat($"Failed to skip waypoint data for type {waypointType} at waypoint {waypointsRead}");
|
PluginCore.WriteToChat($"Navigation: Failed to parse waypoint {waypointsRead + 1} data in {FileName}");
|
||||||
break; // Don't continue if we can't parse properly
|
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)
|
if (waypoints.Count > 0)
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat($"[NavRoute] Route ready for visualization with {waypoints.Count} waypoints");
|
PluginCore.WriteToChat($"Navigation: Loaded {FileName} ({waypoints.Count} waypoints)");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"Navigation: No valid waypoints found in {FileName}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return waypoints.Count > 0;
|
return waypoints.Count > 0;
|
||||||
|
|
@ -222,7 +197,7 @@ namespace MosswartMassacre
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat($"[NavRoute] Error loading nav file {FileName}: {ex.Message}");
|
PluginCore.WriteToChat($"Navigation: Error loading {FileName} - {ex.Message}");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -281,39 +256,31 @@ namespace MosswartMassacre
|
||||||
sr.ReadLine(); // Milliseconds
|
sr.ReadLine(); // Milliseconds
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
PluginCore.WriteToChat($"[NavRoute] Unknown waypoint type: {waypointType}");
|
// Unknown waypoint type - skip silently
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat($"[NavRoute] Error skipping waypoint data: {ex.Message}");
|
// Silently handle parsing errors
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Show()
|
public void Show()
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat($"[NavRoute] Show() called for {FileName}");
|
if (isVisible) return;
|
||||||
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)
|
if (waypoints.Count == 0)
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat($"[NavRoute] Route {FileName} has no waypoints to visualize");
|
PluginCore.WriteToChat($"Navigation: No waypoints to visualize in {FileName}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PluginCore.WriteToChat($"[NavRoute] Creating line objects for {FileName}...");
|
|
||||||
CreateLineObjects();
|
CreateLineObjects();
|
||||||
isVisible = true;
|
isVisible = true;
|
||||||
PluginCore.WriteToChat($"[NavRoute] Route {FileName} visualization complete - isVisible: {isVisible}");
|
PluginCore.WriteToChat($"Navigation: Showing {FileName} ({waypoints.Count} waypoints)");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Hide()
|
public void Hide()
|
||||||
|
|
@ -322,35 +289,29 @@ namespace MosswartMassacre
|
||||||
|
|
||||||
ClearRoute();
|
ClearRoute();
|
||||||
isVisible = false;
|
isVisible = false;
|
||||||
PluginCore.WriteToChat($"Hidden route: {FileName}");
|
PluginCore.WriteToChat($"Navigation: Hidden {FileName}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateLineObjects()
|
private void CreateLineObjects()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat($"[NavRoute] CreateLineObjects() starting for {FileName}");
|
|
||||||
|
|
||||||
// Check D3DService availability
|
// Check D3DService availability
|
||||||
if (CoreManager.Current?.D3DService == null)
|
if (CoreManager.Current?.D3DService == null)
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat($"[NavRoute] ERROR: D3DService is null!");
|
PluginCore.WriteToChat($"Navigation: 3D service unavailable");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Limit the number of lines to prevent lag
|
// Limit the number of lines to prevent lag
|
||||||
int maxLines = Math.Min(waypoints.Count - 1, 500); // Max 500 lines
|
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;
|
int linesCreated = 0;
|
||||||
for (int i = 1; i <= maxLines; i++)
|
for (int i = 1; i <= maxLines; i++)
|
||||||
{
|
{
|
||||||
var current = waypoints[i];
|
var current = waypoints[i];
|
||||||
var previous = waypoints[i - 1];
|
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))
|
if (CreateLineBetweenWaypoints(previous, current))
|
||||||
{
|
{
|
||||||
linesCreated++;
|
linesCreated++;
|
||||||
|
|
@ -363,18 +324,14 @@ 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)
|
if (waypoints.Count > 501)
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat($"[NavRoute] Route has {waypoints.Count} waypoints, showing first {maxLines} segments");
|
PluginCore.WriteToChat($"Navigation: Large route - showing {maxLines} of {waypoints.Count} segments");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat($"[NavRoute] Error creating line objects: {ex.Message}");
|
PluginCore.WriteToChat($"Navigation: Error creating visualization - {ex.Message}");
|
||||||
PluginCore.WriteToChat($"[NavRoute] Stack trace: {ex.StackTrace}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -389,34 +346,20 @@ namespace MosswartMassacre
|
||||||
Math.Pow((to.Z - from.Z) * 240, 2)
|
Math.Pow((to.Z - from.Z) * 240, 2)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (distance <= 0)
|
if (distance <= 0) return false;
|
||||||
{
|
|
||||||
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
|
// Create D3D line object
|
||||||
var lineObj = CoreManager.Current.D3DService.NewD3DObj();
|
var lineObj = CoreManager.Current.D3DService.NewD3DObj();
|
||||||
if (lineObj == null)
|
if (lineObj == null) return false;
|
||||||
{
|
|
||||||
PluginCore.WriteToChat($"[NavRoute] ERROR: Failed to create D3D object!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
PluginCore.WriteToChat($"[NavRoute] D3D object created successfully");
|
|
||||||
|
|
||||||
lineObj.SetShape(D3DShape.Cube);
|
lineObj.SetShape(D3DShape.Cube);
|
||||||
lineObj.Color = routeColor.ToArgb();
|
lineObj.Color = routeColor.ToArgb();
|
||||||
PluginCore.WriteToChat($"[NavRoute] Set shape and color (ARGB: {routeColor.ToArgb()})");
|
|
||||||
|
|
||||||
// Position at midpoint between waypoints
|
// Position at midpoint between waypoints
|
||||||
float midNS = (float)(from.NS + to.NS) / 2;
|
float midNS = (float)(from.NS + to.NS) / 2;
|
||||||
float midEW = (float)(from.EW + to.EW) / 2;
|
float midEW = (float)(from.EW + to.EW) / 2;
|
||||||
float midZ = (float)((from.Z + to.Z) * 120) + 0.1f; // Slightly higher than UtilityBelt
|
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);
|
lineObj.Anchor(midNS, midEW, midZ);
|
||||||
|
|
||||||
// Orient toward destination
|
// Orient toward destination
|
||||||
|
|
@ -424,7 +367,6 @@ namespace MosswartMassacre
|
||||||
float orientEW = (float)from.EW;
|
float orientEW = (float)from.EW;
|
||||||
float orientZ = (float)(from.Z * 240) + 0.1f;
|
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);
|
lineObj.OrientToCoords(orientNS, orientEW, orientZ, true);
|
||||||
|
|
||||||
// Scale to create line effect
|
// Scale to create line effect
|
||||||
|
|
@ -432,20 +374,13 @@ namespace MosswartMassacre
|
||||||
lineObj.ScaleZ = 0.3f;
|
lineObj.ScaleZ = 0.3f;
|
||||||
lineObj.ScaleY = (float)distance;
|
lineObj.ScaleY = (float)distance;
|
||||||
|
|
||||||
PluginCore.WriteToChat($"[NavRoute] Set scale: X={lineObj.ScaleX}, Y={lineObj.ScaleY:F2}, Z={lineObj.ScaleZ}");
|
|
||||||
|
|
||||||
lineObj.Visible = true;
|
lineObj.Visible = true;
|
||||||
PluginCore.WriteToChat($"[NavRoute] Set visible to true");
|
|
||||||
|
|
||||||
lineObjects.Add(lineObj);
|
lineObjects.Add(lineObj);
|
||||||
PluginCore.WriteToChat($"[NavRoute] Added line object to list (total: {lineObjects.Count})");
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat($"[NavRoute] Error creating line: {ex.Message}");
|
|
||||||
PluginCore.WriteToChat($"[NavRoute] Stack trace: {ex.StackTrace}");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ namespace MosswartMassacre
|
||||||
if (!string.IsNullOrEmpty(PluginSettings.Instance?.VTankProfilesPath))
|
if (!string.IsNullOrEmpty(PluginSettings.Instance?.VTankProfilesPath))
|
||||||
{
|
{
|
||||||
vtankProfilesDirectory = PluginSettings.Instance.VTankProfilesPath;
|
vtankProfilesDirectory = PluginSettings.Instance.VTankProfilesPath;
|
||||||
PluginCore.WriteToChat($"[NavViz] Using configured VTank profiles path: {vtankProfilesDirectory}");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,20 +53,17 @@ namespace MosswartMassacre
|
||||||
if (!string.IsNullOrEmpty(profilePath))
|
if (!string.IsNullOrEmpty(profilePath))
|
||||||
{
|
{
|
||||||
vtankProfilesDirectory = profilePath;
|
vtankProfilesDirectory = profilePath;
|
||||||
PluginCore.WriteToChat($"[NavViz] Found VTank profiles from registry: {vtankProfilesDirectory}");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception regEx)
|
catch
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat($"[NavViz] Registry lookup failed: {regEx.Message}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fall back to default path
|
// Fall back to default path
|
||||||
vtankProfilesDirectory = defaultPath;
|
vtankProfilesDirectory = defaultPath;
|
||||||
PluginCore.WriteToChat($"[NavViz] Using default VTank path: {vtankProfilesDirectory}");
|
// Using default path - user can configure in Settings if needed
|
||||||
PluginCore.WriteToChat($"[NavViz] If this is wrong, configure the correct path in Settings tab.");
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
@ -76,6 +72,10 @@ namespace MosswartMassacre
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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()
|
public void RefreshNavFileList()
|
||||||
{
|
{
|
||||||
// Re-initialize directory in case settings changed
|
// Re-initialize directory in case settings changed
|
||||||
|
|
@ -83,43 +83,26 @@ namespace MosswartMassacre
|
||||||
|
|
||||||
availableNavFiles.Clear();
|
availableNavFiles.Clear();
|
||||||
|
|
||||||
PluginCore.WriteToChat($"[NavViz] Refreshing nav files from: {vtankProfilesDirectory}");
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(vtankProfilesDirectory))
|
if (string.IsNullOrEmpty(vtankProfilesDirectory))
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat("[NavViz] No VTank directory set");
|
PluginCore.WriteToChat("VTank directory not configured. Set path in Settings tab.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Directory.Exists(vtankProfilesDirectory))
|
if (!Directory.Exists(vtankProfilesDirectory))
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat($"[NavViz] Directory does not exist: {vtankProfilesDirectory}");
|
PluginCore.WriteToChat($"VTank directory not found: {vtankProfilesDirectory}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Get ALL files first for debugging
|
// Get all files and filter for .nav files only, excluding follow/temporary files
|
||||||
var allFiles = Directory.GetFiles(vtankProfilesDirectory);
|
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
|
var navFiles = allFiles
|
||||||
.Where(file => {
|
.Where(file => Path.GetExtension(file).Equals(".nav", StringComparison.OrdinalIgnoreCase))
|
||||||
var ext = Path.GetExtension(file);
|
.Select(file => Path.GetFileNameWithoutExtension(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) &&
|
.Where(name => !string.IsNullOrEmpty(name) &&
|
||||||
!name.StartsWith("follow", StringComparison.OrdinalIgnoreCase) &&
|
!name.StartsWith("follow", StringComparison.OrdinalIgnoreCase) &&
|
||||||
!name.StartsWith("--", StringComparison.OrdinalIgnoreCase))
|
!name.StartsWith("--", StringComparison.OrdinalIgnoreCase))
|
||||||
|
|
@ -128,14 +111,24 @@ namespace MosswartMassacre
|
||||||
|
|
||||||
availableNavFiles.AddRange(navFiles);
|
availableNavFiles.AddRange(navFiles);
|
||||||
|
|
||||||
PluginCore.WriteToChat($"[NavViz] Found {navFiles.Count} .nav files: {string.Join(", ", 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)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat($"[NavViz] Error refreshing nav files: {ex.Message}");
|
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)
|
public bool LoadRoute(string navFileName)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
@ -149,7 +142,6 @@ namespace MosswartMassacre
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(navFileName) || navFileName == "None")
|
if (string.IsNullOrEmpty(navFileName) || navFileName == "None")
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat("[NavViz] Route cleared");
|
|
||||||
RouteChanged?.Invoke(this, EventArgs.Empty);
|
RouteChanged?.Invoke(this, EventArgs.Empty);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -158,7 +150,7 @@ namespace MosswartMassacre
|
||||||
|
|
||||||
if (!File.Exists(fullPath))
|
if (!File.Exists(fullPath))
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat($"[NavViz] Nav file not found: {navFileName}");
|
PluginCore.WriteToChat($"Navigation file '{navFileName}' not found");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -172,41 +164,35 @@ namespace MosswartMassacre
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show route if visualization is enabled
|
// Show route if visualization is enabled
|
||||||
PluginCore.WriteToChat($"[NavViz] LoadRoute: IsEnabled = {IsEnabled}");
|
|
||||||
if (IsEnabled)
|
if (IsEnabled)
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat($"[NavViz] Calling Show() for route {navFileName}");
|
|
||||||
currentRoute.Show();
|
currentRoute.Show();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
PluginCore.WriteToChat($"[NavViz] Visualization disabled, not showing route");
|
|
||||||
}
|
|
||||||
|
|
||||||
RouteChanged?.Invoke(this, EventArgs.Empty);
|
RouteChanged?.Invoke(this, EventArgs.Empty);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat($"[NavViz] Error loading route {navFileName}: {ex.Message}");
|
PluginCore.WriteToChat($"Navigation: Failed to load '{navFileName}' - {ex.Message}");
|
||||||
return false;
|
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)
|
public void SetEnabled(bool enabled)
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat($"[NavViz] SetEnabled called: current={IsEnabled}, new={enabled}");
|
// No change needed if already in desired state
|
||||||
if (IsEnabled == enabled)
|
if (IsEnabled == enabled) return;
|
||||||
{
|
|
||||||
PluginCore.WriteToChat($"[NavViz] SetEnabled: no change needed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
IsEnabled = enabled;
|
IsEnabled = enabled;
|
||||||
|
|
||||||
if (currentRoute != null)
|
if (currentRoute != null)
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat($"[NavViz] SetEnabled: currentRoute exists, {(enabled ? "showing" : "hiding")}");
|
|
||||||
if (enabled)
|
if (enabled)
|
||||||
{
|
{
|
||||||
currentRoute.Show();
|
currentRoute.Show();
|
||||||
|
|
@ -218,10 +204,9 @@ namespace MosswartMassacre
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat($"[NavViz] SetEnabled: no currentRoute to {(enabled ? "show" : "hide")}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PluginCore.WriteToChat($"[NavViz] Navigation visualization {(enabled ? "enabled" : "disabled")}");
|
PluginCore.WriteToChat($"Navigation visualization {(enabled ? "enabled" : "disabled")}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ToggleEnabled()
|
public void ToggleEnabled()
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,45 @@ namespace MosswartMassacre
|
||||||
internal static DateTime statsStartTime = DateTime.Now;
|
internal static DateTime statsStartTime = DateTime.Now;
|
||||||
internal static Timer updateTimer;
|
internal static Timer updateTimer;
|
||||||
public static bool RareMetaEnabled { get; set; } = true;
|
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 RemoteCommandsEnabled { get; set; } = false;
|
||||||
public static bool HttpServerEnabled { get; set; } = false;
|
public static bool HttpServerEnabled { get; set; } = false;
|
||||||
public static string CharTag { get; set; } = "";
|
public static string CharTag { get; set; } = "";
|
||||||
|
|
@ -63,7 +102,7 @@ namespace MosswartMassacre
|
||||||
updateTimer.Start();
|
updateTimer.Start();
|
||||||
|
|
||||||
// Initialize the view (UI) - use tabbed interface by default
|
// Initialize the view (UI) - use tabbed interface by default
|
||||||
TabbedMainView.ViewInit();
|
ViewManager.ViewInit();
|
||||||
|
|
||||||
// Enable TLS1.2
|
// Enable TLS1.2
|
||||||
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
|
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
|
||||||
|
|
@ -113,7 +152,7 @@ namespace MosswartMassacre
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up the view
|
// Clean up the view
|
||||||
TabbedMainView.ViewDestroy();
|
ViewManager.ViewDestroy();
|
||||||
//Disable vtank interface
|
//Disable vtank interface
|
||||||
vTank.Disable();
|
vTank.Disable();
|
||||||
// sluta lyssna på commands
|
// sluta lyssna på commands
|
||||||
|
|
@ -149,8 +188,8 @@ namespace MosswartMassacre
|
||||||
HttpServerEnabled = PluginSettings.Instance.HttpServerEnabled;
|
HttpServerEnabled = PluginSettings.Instance.HttpServerEnabled;
|
||||||
TelemetryEnabled = PluginSettings.Instance.TelemetryEnabled;
|
TelemetryEnabled = PluginSettings.Instance.TelemetryEnabled;
|
||||||
CharTag = PluginSettings.Instance.CharTag;
|
CharTag = PluginSettings.Instance.CharTag;
|
||||||
TabbedMainView.SetRareMetaToggleState(RareMetaEnabled);
|
ViewManager.SetRareMetaToggleState(RareMetaEnabled);
|
||||||
TabbedMainView.RefreshSettingsFromConfig(); // Refresh all UI settings after loading
|
ViewManager.RefreshSettingsFromConfig(); // Refresh all UI settings after loading
|
||||||
if (TelemetryEnabled)
|
if (TelemetryEnabled)
|
||||||
Telemetry.Start();
|
Telemetry.Start();
|
||||||
if (WebSocketEnabled)
|
if (WebSocketEnabled)
|
||||||
|
|
@ -238,13 +277,13 @@ namespace MosswartMassacre
|
||||||
totalKills++;
|
totalKills++;
|
||||||
lastKillTime = DateTime.Now;
|
lastKillTime = DateTime.Now;
|
||||||
CalculateKillsPerInterval();
|
CalculateKillsPerInterval();
|
||||||
TabbedMainView.UpdateKillStats(totalKills, killsPer5Min, killsPerHour);
|
ViewManager.UpdateKillStats(totalKills, killsPer5Min, killsPerHour);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsRareDiscoveryMessage(e.Text, out string rareText))
|
if (IsRareDiscoveryMessage(e.Text, out string rareText))
|
||||||
{
|
{
|
||||||
rareCount++;
|
rareCount++;
|
||||||
TabbedMainView.UpdateRareCount(rareCount);
|
ViewManager.UpdateRareCount(rareCount);
|
||||||
|
|
||||||
if (RareMetaEnabled)
|
if (RareMetaEnabled)
|
||||||
{
|
{
|
||||||
|
|
@ -324,11 +363,11 @@ namespace MosswartMassacre
|
||||||
{
|
{
|
||||||
// Update the elapsed time
|
// Update the elapsed time
|
||||||
TimeSpan elapsed = DateTime.Now - statsStartTime;
|
TimeSpan elapsed = DateTime.Now - statsStartTime;
|
||||||
TabbedMainView.UpdateElapsedTime(elapsed);
|
ViewManager.UpdateElapsedTime(elapsed);
|
||||||
|
|
||||||
// Recalculate kill rates
|
// Recalculate kill rates
|
||||||
CalculateKillsPerInterval();
|
CalculateKillsPerInterval();
|
||||||
TabbedMainView.UpdateKillStats(totalKills, killsPer5Min, killsPerHour);
|
ViewManager.UpdateKillStats(totalKills, killsPer5Min, killsPerHour);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
@ -426,14 +465,14 @@ namespace MosswartMassacre
|
||||||
killsPerHour = 0;
|
killsPerHour = 0;
|
||||||
|
|
||||||
WriteToChat("Stats have been reset.");
|
WriteToChat("Stats have been reset.");
|
||||||
TabbedMainView.UpdateKillStats(totalKills, killsPer5Min, killsPerHour);
|
ViewManager.UpdateKillStats(totalKills, killsPer5Min, killsPerHour);
|
||||||
TabbedMainView.UpdateRareCount(rareCount);
|
ViewManager.UpdateRareCount(rareCount);
|
||||||
}
|
}
|
||||||
public static void ToggleRareMeta()
|
public static void ToggleRareMeta()
|
||||||
{
|
{
|
||||||
PluginSettings.Instance.RareMetaEnabled = !PluginSettings.Instance.RareMetaEnabled;
|
PluginSettings.Instance.RareMetaEnabled = !PluginSettings.Instance.RareMetaEnabled;
|
||||||
RareMetaEnabled = PluginSettings.Instance.RareMetaEnabled;
|
RareMetaEnabled = PluginSettings.Instance.RareMetaEnabled;
|
||||||
TabbedMainView.SetRareMetaToggleState(RareMetaEnabled);
|
ViewManager.SetRareMetaToggleState(RareMetaEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
[DllImport("Decal.dll")]
|
[DllImport("Decal.dll")]
|
||||||
|
|
@ -563,7 +602,7 @@ namespace MosswartMassacre
|
||||||
case "meta":
|
case "meta":
|
||||||
RareMetaEnabled = !RareMetaEnabled;
|
RareMetaEnabled = !RareMetaEnabled;
|
||||||
WriteToChat($"Rare meta state is now {(RareMetaEnabled ? "ON" : "OFF")}");
|
WriteToChat($"Rare meta state is now {(RareMetaEnabled ? "ON" : "OFF")}");
|
||||||
TabbedMainView.SetRareMetaToggleState(RareMetaEnabled); // <-- sync the UI
|
ViewManager.SetRareMetaToggleState(RareMetaEnabled); // <-- sync the UI
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "http":
|
case "http":
|
||||||
|
|
|
||||||
|
|
@ -26,5 +26,5 @@ using System.Runtime.InteropServices;
|
||||||
// Minor Version
|
// Minor Version
|
||||||
// Build Number
|
// Build Number
|
||||||
// Revision
|
// Revision
|
||||||
[assembly: AssemblyVersion("3.0.0.6")]
|
[assembly: AssemblyVersion("3.0.0.7")]
|
||||||
[assembly: AssemblyFileVersion("3.0.0.6")]
|
[assembly: AssemblyFileVersion("3.0.0.7")]
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -25,19 +25,19 @@
|
||||||
<control progid="DecalControls.StaticText" name="lblSettingsHeader" left="10" top="10" width="200" height="20" text="Plugin Configuration" style="FontBold"/>
|
<control progid="DecalControls.StaticText" name="lblSettingsHeader" left="10" top="10" width="200" height="20" text="Plugin Configuration" style="FontBold"/>
|
||||||
|
|
||||||
<!-- Meta state setting -->
|
<!-- 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"/>
|
<control progid="DecalControls.Checkbox" name="chkRareMetaEnabled" left="20" top="35" width="300" height="20" text="Auto rare meta state" checked="true"/>
|
||||||
|
|
||||||
<!-- Remote commands setting -->
|
<!-- 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"/>
|
<control progid="DecalControls.Checkbox" name="chkRemoteCommandsEnabled" left="20" top="60" width="300" height="20" text="Remote commands (!do/!dot)" checked="false"/>
|
||||||
|
|
||||||
<!-- HTTP server setting -->
|
<!-- 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"/>
|
<control progid="DecalControls.Checkbox" name="chkHttpServerEnabled" left="20" top="85" width="300" height="20" text="HTTP server (port 8085)" checked="false"/>
|
||||||
|
|
||||||
<!-- WebSocket setting -->
|
<!-- WebSocket setting -->
|
||||||
<control progid="DecalControls.Checkbox" name="chkWebSocketEnabled" left="20" top="110" width="300" height="20" text="Enable WebSocket streaming" checked="false"/>
|
<control progid="DecalControls.Checkbox" name="chkWebSocketEnabled" left="20" top="110" width="300" height="20" text="WebSocket streaming" checked="false"/>
|
||||||
|
|
||||||
<!-- Telemetry setting -->
|
<!-- Telemetry setting -->
|
||||||
<control progid="DecalControls.Checkbox" name="chkTelemetryEnabled" left="20" top="135" width="300" height="20" text="Enable telemetry reporting" checked="false"/>
|
<control progid="DecalControls.Checkbox" name="chkTelemetryEnabled" left="20" top="135" width="300" height="20" text="Telemetry reporting" checked="false"/>
|
||||||
|
|
||||||
<!-- Character tag setting -->
|
<!-- Character tag setting -->
|
||||||
<control progid="DecalControls.StaticText" name="lblCharTag" left="20" top="165" width="100" height="16" text="Character Tag:"/>
|
<control progid="DecalControls.StaticText" name="lblCharTag" left="20" top="165" width="100" height="16" text="Character Tag:"/>
|
||||||
|
|
@ -46,7 +46,7 @@
|
||||||
<!-- VTank profiles path setting -->
|
<!-- VTank profiles path setting -->
|
||||||
<control progid="DecalControls.StaticText" name="lblVTankPath" left="20" top="190" width="100" height="16" text="VTank Profiles:"/>
|
<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.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 progid="DecalControls.StaticText" name="lblVTankPathHelp" left="20" top="210" width="350" height="16" text="Leave empty for auto-detection."/>
|
||||||
</control>
|
</control>
|
||||||
</page>
|
</page>
|
||||||
|
|
||||||
|
|
@ -90,10 +90,10 @@
|
||||||
<control progid="DecalControls.StaticText" name="lblNavHeader" left="10" top="10" width="250" height="20" text="Route Comparison Visualization" style="FontBold"/>
|
<control progid="DecalControls.StaticText" name="lblNavHeader" left="10" top="10" width="250" height="20" text="Route Comparison Visualization" style="FontBold"/>
|
||||||
|
|
||||||
<!-- Enable/disable visualization -->
|
<!-- Enable/disable visualization -->
|
||||||
<control progid="DecalControls.Checkbox" name="chkNavVisualizationEnabled" left="20" top="35" width="300" height="20" text="Enable route visualization" checked="false"/>
|
<control progid="DecalControls.Checkbox" name="chkNavVisualizationEnabled" left="20" top="35" width="300" height="20" text="Show route visualization" checked="false"/>
|
||||||
|
|
||||||
<!-- Nav file selection -->
|
<!-- Nav file selection -->
|
||||||
<control progid="DecalControls.StaticText" name="lblNavFile" left="20" top="65" width="120" height="16" text="Comparison Route:"/>
|
<control progid="DecalControls.StaticText" name="lblNavFile" left="20" top="65" width="120" height="16" text="Route:"/>
|
||||||
<control progid="DecalControls.Choice" name="cmbNavFiles" left="145" top="63" width="200" height="20"/>
|
<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"/>
|
<control progid="DecalControls.PushButton" name="btnRefreshNavFiles" left="350" top="62" width="60" height="22" text="Refresh"/>
|
||||||
|
|
||||||
|
|
@ -101,14 +101,14 @@
|
||||||
<control progid="DecalControls.StaticText" name="lblNavStatus" left="20" top="95" width="350" height="16" text="Status: No route loaded"/>
|
<control progid="DecalControls.StaticText" name="lblNavStatus" left="20" top="95" width="350" height="16" text="Status: No route loaded"/>
|
||||||
|
|
||||||
<!-- Color indicator -->
|
<!-- Color indicator -->
|
||||||
<control progid="DecalControls.StaticText" name="lblNavColorInfo" left="20" top="115" width="350" height="16" text="Comparison route will be displayed in red"/>
|
<control progid="DecalControls.StaticText" name="lblNavColorInfo" left="20" top="115" width="350" height="16" text="Route displays in red"/>
|
||||||
|
|
||||||
<!-- Instructions -->
|
<!-- 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 progid="DecalControls.StaticText" name="lblNavInstructions" left="10" top="145" width="370" height="40" text="Visualize VTank nav files alongside UtilityBelt routes. Compare different paths and plan optimal routes."/>
|
||||||
|
|
||||||
<!-- Control buttons -->
|
<!-- Control buttons -->
|
||||||
<control progid="DecalControls.PushButton" name="btnLoadRoute" left="20" top="210" width="80" height="24" text="Load Route"/>
|
<control progid="DecalControls.PushButton" name="btnLoadRoute" left="20" top="210" width="90" height="24" text="Load Route"/>
|
||||||
<control progid="DecalControls.PushButton" name="btnClearRoute" left="110" top="210" width="80" height="24" text="Clear Route"/>
|
<control progid="DecalControls.PushButton" name="btnClearRoute" left="110" top="210" width="90" height="24" text="Clear Route"/>
|
||||||
</control>
|
</control>
|
||||||
</page>
|
</page>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,177 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,794 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
395
MosswartMassacre/Views/VVSBaseView.cs
Normal file
395
MosswartMassacre/Views/VVSBaseView.cs
Normal file
|
|
@ -0,0 +1,395 @@
|
||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Timers;
|
||||||
|
using VirindiViewService;
|
||||||
|
using VirindiViewService.XMLParsers;
|
||||||
|
|
||||||
|
namespace MosswartMassacre.Views
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Base class for VVS (VirindiViewService) based views.
|
||||||
|
/// Replaces the wrapper-based BaseView with direct VVS integration.
|
||||||
|
/// </summary>
|
||||||
|
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
|
||||||
|
/// <summary>
|
||||||
|
/// Get a control by name with proper type casting.
|
||||||
|
/// Usage: var button = GetControl<HudButton>("btnExample");
|
||||||
|
/// </summary>
|
||||||
|
protected T GetControl<T>(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a control by name (alternative syntax)
|
||||||
|
/// Usage: var button = (HudButton)GetControl("btnExample");
|
||||||
|
/// </summary>
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
845
MosswartMassacre/Views/VVSTabbedMainView.cs
Normal file
845
MosswartMassacre/Views/VVSTabbedMainView.cs
Normal file
|
|
@ -0,0 +1,845 @@
|
||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Timers;
|
||||||
|
using VirindiViewService.Controls;
|
||||||
|
|
||||||
|
namespace MosswartMassacre.Views
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// VVS-based tabbed main view - direct VirindiViewService integration.
|
||||||
|
/// Replaces wrapper-based TabbedMainView with modern VVS controls.
|
||||||
|
/// </summary>
|
||||||
|
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<HudStaticText>("lblTotalKills");
|
||||||
|
lblKillsPer5Min = GetControl<HudStaticText>("lblKillsPer5Min");
|
||||||
|
lblKillsPerHour = GetControl<HudStaticText>("lblKillsPerHour");
|
||||||
|
lblElapsedTime = GetControl<HudStaticText>("lblElapsedTime");
|
||||||
|
lblRareCount = GetControl<HudStaticText>("lblRareCount");
|
||||||
|
lblAutoLootRare = GetControl<HudStaticText>("lblAutoLootRare");
|
||||||
|
lblStatus = GetControl<HudStaticText>("lblStatus");
|
||||||
|
lblWebSocketStatus = GetControl<HudStaticText>("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<HudCheckBox>("chkRareMetaEnabled");
|
||||||
|
chkRemoteCommandsEnabled = GetControl<HudCheckBox>("chkRemoteCommandsEnabled");
|
||||||
|
chkHttpServerEnabled = GetControl<HudCheckBox>("chkHttpServerEnabled");
|
||||||
|
chkWebSocketEnabled = GetControl<HudCheckBox>("chkWebSocketEnabled");
|
||||||
|
chkTelemetryEnabled = GetControl<HudCheckBox>("chkTelemetryEnabled");
|
||||||
|
txtCharTag = GetControl<HudTextBox>("txtCharTag");
|
||||||
|
txtVTankPath = GetControl<HudTextBox>("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<HudStaticText>("lblDetailedKillsValue");
|
||||||
|
lblBestHourValue = GetControl<HudStaticText>("lblBestHourValue");
|
||||||
|
lblAverageKillsValue = GetControl<HudStaticText>("lblAverageKillsValue");
|
||||||
|
lblRareStatsValue = GetControl<HudStaticText>("lblRareStatsValue");
|
||||||
|
lblRareRateValue = GetControl<HudStaticText>("lblRareRateValue");
|
||||||
|
lblSessionTimeValue = GetControl<HudStaticText>("lblSessionTimeValue");
|
||||||
|
lblLastKillValue = GetControl<HudStaticText>("lblLastKillValue");
|
||||||
|
btnResetStats = GetControl<HudButton>("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<HudCheckBox>("chkNavVisualizationEnabled");
|
||||||
|
cmbNavFiles = GetControl<HudCombo>("cmbNavFiles");
|
||||||
|
btnRefreshNavFiles = GetControl<HudButton>("btnRefreshNavFiles");
|
||||||
|
lblNavStatus = GetControl<HudStaticText>("lblNavStatus");
|
||||||
|
btnLoadRoute = GetControl<HudButton>("btnLoadRoute");
|
||||||
|
btnClearRoute = GetControl<HudButton>("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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,427 +0,0 @@
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
//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<MVControlEventArgs> 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<MVCheckBoxChangeEventArgs> Change;
|
|
||||||
event EventHandler Change_Old;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if VVS_WRAPPERS_PUBLIC
|
|
||||||
public
|
|
||||||
#else
|
|
||||||
internal
|
|
||||||
#endif
|
|
||||||
interface ITextBox : IControl
|
|
||||||
{
|
|
||||||
string Text { get; set; }
|
|
||||||
event EventHandler<MVTextBoxChangeEventArgs> Change;
|
|
||||||
event EventHandler Change_Old;
|
|
||||||
event EventHandler<MVTextBoxEndEventArgs> 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<MVIndexChangeEventArgs> 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<MVIndexChangeEventArgs> 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<MVListSelectEventArgs> 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<MVControlEventArgs> Click;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if VVS_WRAPPERS_PUBLIC
|
|
||||||
public
|
|
||||||
#else
|
|
||||||
internal
|
|
||||||
#endif
|
|
||||||
interface INotebook : IControl
|
|
||||||
{
|
|
||||||
event EventHandler<MVIndexChangeEventArgs> 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<MVControlEventArgs> 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
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,329 +0,0 @@
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
//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<string> myControls;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructs a new ControlReference array
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="controls">Names of the controls to put in the array</param>
|
|
||||||
public MVControlReferenceArrayAttribute(params string[] controls)
|
|
||||||
: base()
|
|
||||||
{
|
|
||||||
this.myControls = new System.Collections.ObjectModel.Collection<string>(controls);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Control collection
|
|
||||||
/// </summary>
|
|
||||||
public System.Collections.ObjectModel.Collection<string> 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<MyClasses.MetaViewWrappers.IView> Views = new List<IView>();
|
|
||||||
}
|
|
||||||
static Dictionary<object, ViewObjectInfo> VInfo = new Dictionary<object, ViewObjectInfo>();
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
282
README.md
282
README.md
|
|
@ -1,90 +1,220 @@
|
||||||
# Mossy Plugins
|
# MosswartMassacre - Advanced DECAL Plugin for Asheron's Call
|
||||||
|
|
||||||
A collection of DECAL plugins for Asheron's Call, providing utility overlays and automation features.
|
> **Status**: Production Ready | VVS Direct Integration | Navigation Visualization Complete
|
||||||
|
|
||||||
## Contents
|
A comprehensive DECAL plugin for Asheron's Call that tracks monster kills, rare item discoveries, and provides advanced navigation route visualization with 3D rendering.
|
||||||
- `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).
|
|
||||||
|
|
||||||
## Prerequisites
|
## 🚀 Features
|
||||||
- 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)
|
|
||||||
|
|
||||||
## Setup & Build
|
### Core Functionality
|
||||||
1. Clone this repository.
|
- **Kill Tracking**: Real-time monster kill counting with rate calculations (kills/5min, kills/hour)
|
||||||
2. Ensure the DECAL and Virindi DLLs are present under `MosswartMassacre/lib/` and referenced by each project.
|
- **Rare Item Discovery**: Automatic rare detection and counter with optional meta state control
|
||||||
3. Restore NuGet packages if needed (`nuget restore mossy.sln`).
|
- **Statistics Dashboard**: Detailed session statistics with best hourly performance tracking
|
||||||
4. Open `mossy.sln` in Visual Studio and build the solution.
|
- **Multi-System Integration**: WebSocket streaming, HTTP command server, and telemetry support
|
||||||
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.
|
|
||||||
|
|
||||||
## GearCycler
|
### 🗺️ Navigation Visualization
|
||||||
A minimal plugin demonstrating a VirindiViewService-based UI.
|
**Advanced VTank route visualization with 3D rendering capabilities**
|
||||||
- UI layout: `GearCycler/ViewXML/mainView.xml`.
|
- **3D Route Display**: Renders VTank .nav files as red lines in the game world
|
||||||
- Core logic in `GearCycler/GearCore.cs`.
|
- **Route Comparison**: Side-by-side visualization with UtilityBelt's active navigation
|
||||||
- On button click, it logs a chat message; extend the `btnCycle.Hit` handler to add gear-cycling logic.
|
- **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
|
||||||
|
|
||||||
## MosswartMassacre
|
### 🎛️ User Interface
|
||||||
Tracks monster kills and rare drops, with multiple utility features including navigation route visualization.
|
**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
|
||||||
|
|
||||||
### Features
|
## 📥 Installation
|
||||||
- **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 <enable|disable>` : Start/stop local HTTP command server (port 8085).
|
|
||||||
- `/mm remotecommands <enable|disable>` : Listen for remote commands from your allegiance chat.
|
|
||||||
- `/mm telemetry <enable|disable>` : Enable/disable periodic telemetry streaming.
|
|
||||||
|
|
||||||
### HTTP Command Server
|
### Prerequisites
|
||||||
- Listens on `http://localhost:8085/`.
|
- Windows with .NET Framework 4.8
|
||||||
- Accepts POST data: `target=<player>&command=<text>`, then sends a /tell and executes the command.
|
- Asheron's Call with DECAL Adapter installed
|
||||||
|
- VirindiViewService (included in lib/ folder)
|
||||||
|
|
||||||
### Navigation Visualization ✅ NEW
|
### Quick Setup
|
||||||
- **VTank Integration**: Automatically detects VTank installation and loads .nav files.
|
1. Download the latest release from the releases page
|
||||||
- **3D Route Display**: Shows navigation routes as red lines in the game world.
|
2. Extract to your DECAL plugins directory
|
||||||
- **Route Comparison**: Visualize different routes alongside UtilityBelt's active navigation.
|
3. Restart DECAL and enable the plugin
|
||||||
- **Supported Formats**: All VTank nav types (Circular, Linear, Target, Once) and waypoint types.
|
4. Configure settings through the in-game UI
|
||||||
- **Usage**: Enable in Navigation tab, select route from dropdown, click "Load Route".
|
|
||||||
|
|
||||||
### Configuration
|
### Building from Source
|
||||||
- Per-character YAML config stored at `<PluginDir>/<CharacterName>.yaml`.
|
```bash
|
||||||
- Settings include:
|
# Clone the repository
|
||||||
- `remote_commands_enabled`
|
git clone [repository-url]
|
||||||
- `rare_meta_enabled`
|
cd MosswartMassacre
|
||||||
- `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.
|
|
||||||
|
|
||||||
### Telemetry
|
# Restore packages and build
|
||||||
- Periodically posts JSON snapshots of position and stats to a configurable endpoint.
|
nuget restore packages.config
|
||||||
- Configure `Endpoint`, `SharedSecret`, and `IntervalSec` in `Telemetry.cs`.
|
msbuild MosswartMassacre.csproj /p:Configuration=Release /p:Platform=AnyCPU
|
||||||
|
```
|
||||||
|
|
||||||
## Dependencies
|
## 🎮 Usage
|
||||||
- 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)
|
|
||||||
|
|
||||||
## Contributing
|
### Basic Commands
|
||||||
1. Fork the repository.
|
Access all features through the `/mm` command interface:
|
||||||
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.
|
|
||||||
|
|
||||||
--
|
```
|
||||||
_This README provides a high-level overview to get up and running quickly._
|
/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 <on/off> - Control HTTP command server (port 8085)
|
||||||
|
/mm telemetry <on/off> - 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 `<PluginDir>/<CharacterName>.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*
|
||||||
Loading…
Add table
Add a link
Reference in a new issue