Added Mossy tracker, know issue
This commit is contained in:
parent
7eb98491d3
commit
23e33599ca
8 changed files with 3372 additions and 6 deletions
2008
MosswartMassacre/FlagTrackerData.cs
Normal file
2008
MosswartMassacre/FlagTrackerData.cs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -104,6 +104,9 @@
|
||||||
<Compile Include="..\Shared\Constants\Dictionaries.cs">
|
<Compile Include="..\Shared\Constants\Dictionaries.cs">
|
||||||
<Link>Shared\Constants\Dictionaries.cs</Link>
|
<Link>Shared\Constants\Dictionaries.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="..\Shared\Spells\Spell.cs">
|
||||||
|
<Link>Shared\Spells\Spell.cs</Link>
|
||||||
|
</Compile>
|
||||||
<Compile Include="..\Shared\Constants\DoubleValueKey.cs">
|
<Compile Include="..\Shared\Constants\DoubleValueKey.cs">
|
||||||
<Link>Shared\Constants\DoubleValueKey.cs</Link>
|
<Link>Shared\Constants\DoubleValueKey.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
|
@ -160,9 +163,11 @@
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="ClientTelemetry.cs" />
|
<Compile Include="ClientTelemetry.cs" />
|
||||||
<Compile Include="DecalHarmonyClean.cs" />
|
<Compile Include="DecalHarmonyClean.cs" />
|
||||||
|
<Compile Include="FlagTrackerData.cs" />
|
||||||
<Compile Include="MossyInventory.cs" />
|
<Compile Include="MossyInventory.cs" />
|
||||||
<Compile Include="NavRoute.cs" />
|
<Compile Include="NavRoute.cs" />
|
||||||
<Compile Include="NavVisualization.cs" />
|
<Compile Include="NavVisualization.cs" />
|
||||||
|
<Compile Include="QuestManager.cs" />
|
||||||
<Compile Include="vTank.cs" />
|
<Compile Include="vTank.cs" />
|
||||||
<Compile Include="VtankControl.cs" />
|
<Compile Include="VtankControl.cs" />
|
||||||
<Compile Include="Telemetry.cs" />
|
<Compile Include="Telemetry.cs" />
|
||||||
|
|
@ -179,6 +184,8 @@
|
||||||
<DesignTime>True</DesignTime>
|
<DesignTime>True</DesignTime>
|
||||||
<DependentUpon>Resources.resx</DependentUpon>
|
<DependentUpon>Resources.resx</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="SpellManager.cs" />
|
||||||
|
<Compile Include="Views\FlagTrackerView.cs" />
|
||||||
<Compile Include="Views\VVSBaseView.cs" />
|
<Compile Include="Views\VVSBaseView.cs" />
|
||||||
<Compile Include="Views\VVSTabbedMainView.cs" />
|
<Compile Include="Views\VVSTabbedMainView.cs" />
|
||||||
<Compile Include="WebSocket.cs" />
|
<Compile Include="WebSocket.cs" />
|
||||||
|
|
@ -190,8 +197,12 @@
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="ViewXML\flagTracker.xml" />
|
||||||
<EmbeddedResource Include="ViewXML\mainView.xml" />
|
<EmbeddedResource Include="ViewXML\mainView.xml" />
|
||||||
<EmbeddedResource Include="ViewXML\mainViewTabbed.xml" />
|
<EmbeddedResource Include="ViewXML\mainViewTabbed.xml" />
|
||||||
|
<EmbeddedResource Include="..\Shared\Spells\Spells.csv">
|
||||||
|
<Link>Shared\Spells\Spells.csv</Link>
|
||||||
|
</EmbeddedResource>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="app.config" />
|
<None Include="app.config" />
|
||||||
|
|
|
||||||
|
|
@ -96,15 +96,15 @@ namespace MosswartMassacre
|
||||||
CoreManager.Current.CharacterFilter.LoginComplete += CharacterFilter_LoginComplete;
|
CoreManager.Current.CharacterFilter.LoginComplete += CharacterFilter_LoginComplete;
|
||||||
CoreManager.Current.WorldFilter.CreateObject += OnSpawn;
|
CoreManager.Current.WorldFilter.CreateObject += OnSpawn;
|
||||||
CoreManager.Current.WorldFilter.ReleaseObject += OnDespawn;
|
CoreManager.Current.WorldFilter.ReleaseObject += OnDespawn;
|
||||||
|
// Initialize VVS view after character login
|
||||||
|
ViewManager.ViewInit();
|
||||||
|
|
||||||
// Initialize the timer
|
// Initialize the timer
|
||||||
updateTimer = new Timer(1000); // Update every second
|
updateTimer = new Timer(1000); // Update every second
|
||||||
updateTimer.Elapsed += UpdateStats;
|
updateTimer.Elapsed += UpdateStats;
|
||||||
updateTimer.Start();
|
updateTimer.Start();
|
||||||
|
|
||||||
// Initialize the view (UI) - use tabbed interface by default
|
// Note: View initialization moved to LoginComplete for VVS compatibility
|
||||||
ViewManager.ViewInit();
|
|
||||||
|
|
||||||
// Enable TLS1.2
|
// Enable TLS1.2
|
||||||
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
|
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
|
||||||
|
|
@ -187,6 +187,8 @@ namespace MosswartMassacre
|
||||||
|
|
||||||
WriteToChat("Mosswart Massacre has started!");
|
WriteToChat("Mosswart Massacre has started!");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
PluginSettings.Initialize(); // Safe to call now
|
PluginSettings.Initialize(); // Safe to call now
|
||||||
|
|
||||||
// Apply the values
|
// Apply the values
|
||||||
|
|
@ -605,7 +607,8 @@ namespace MosswartMassacre
|
||||||
WriteToChat("/mm nextwp - Advance VTank to next waypoint");
|
WriteToChat("/mm nextwp - Advance VTank to next waypoint");
|
||||||
WriteToChat("/mm decalstatus - Check Harmony patch status (UtilityBelt version)");
|
WriteToChat("/mm decalstatus - Check Harmony patch status (UtilityBelt version)");
|
||||||
WriteToChat("/mm decaldebug - Enable/disable plugin message debug output + WebSocket streaming");
|
WriteToChat("/mm decaldebug - Enable/disable plugin message debug output + WebSocket streaming");
|
||||||
WriteToChat("/mm harmonyraw - Show raw intercepted messages (debug output)");
|
WriteToChat("/mm harmonyraw - Show raw intercepted messages (debug output)");
|
||||||
|
WriteToChat("/mm gui - Manually initialize/reinitialize GUI");
|
||||||
break;
|
break;
|
||||||
case "report":
|
case "report":
|
||||||
TimeSpan elapsed = DateTime.Now - statsStartTime;
|
TimeSpan elapsed = DateTime.Now - statsStartTime;
|
||||||
|
|
@ -797,6 +800,20 @@ namespace MosswartMassacre
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "initgui":
|
||||||
|
case "gui":
|
||||||
|
try
|
||||||
|
{
|
||||||
|
WriteToChat("Attempting to manually initialize GUI...");
|
||||||
|
ViewManager.ViewDestroy(); // Clean up any existing view
|
||||||
|
ViewManager.ViewInit(); // Reinitialize
|
||||||
|
WriteToChat("GUI initialization attempt completed.");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
WriteToChat($"GUI initialization error: {ex.Message}");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
WriteToChat($"Unknown /mm command: {subCommand}. Try /mm help");
|
WriteToChat($"Unknown /mm command: {subCommand}. Try /mm help");
|
||||||
|
|
|
||||||
|
|
@ -26,5 +26,5 @@ using System.Runtime.InteropServices;
|
||||||
// Minor Version
|
// Minor Version
|
||||||
// Build Number
|
// Build Number
|
||||||
// Revision
|
// Revision
|
||||||
[assembly: AssemblyVersion("3.0.1.1")]
|
[assembly: AssemblyVersion("3.0.1.2")]
|
||||||
[assembly: AssemblyFileVersion("3.0.1.1")]
|
[assembly: AssemblyFileVersion("3.0.1.2")]
|
||||||
296
MosswartMassacre/QuestManager.cs
Normal file
296
MosswartMassacre/QuestManager.cs
Normal file
|
|
@ -0,0 +1,296 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Decal.Adapter;
|
||||||
|
using Decal.Adapter.Wrappers;
|
||||||
|
|
||||||
|
namespace MosswartMassacre
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Quest tracking and management system
|
||||||
|
/// Ported from UBS Lua quest system
|
||||||
|
/// </summary>
|
||||||
|
public class QuestManager : IDisposable
|
||||||
|
{
|
||||||
|
#region Quest Data Structures
|
||||||
|
public class Quest
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
public int Solves { get; set; }
|
||||||
|
public int Timestamp { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
public int MaxSolves { get; set; }
|
||||||
|
public int Delta { get; set; }
|
||||||
|
public int ExpireTime { get; set; }
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Properties
|
||||||
|
public List<Quest> QuestList { get; private set; }
|
||||||
|
public Dictionary<string, Quest> QuestDictionary { get; private set; }
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Events and State
|
||||||
|
private bool isRefreshing = false;
|
||||||
|
private DateTime lastRefreshTime = DateTime.MinValue;
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public QuestManager()
|
||||||
|
{
|
||||||
|
QuestList = new List<Quest>();
|
||||||
|
QuestDictionary = new Dictionary<string, Quest>();
|
||||||
|
|
||||||
|
// Hook into chat events for quest parsing
|
||||||
|
InitializeChatHooks();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Initialization
|
||||||
|
private void InitializeChatHooks()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (CoreManager.Current != null)
|
||||||
|
{
|
||||||
|
CoreManager.Current.ChatBoxMessage += OnChatBoxMessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"Error initializing quest chat hooks: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Quest Parsing
|
||||||
|
private void OnChatBoxMessage(object sender, ChatTextInterceptEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!isRefreshing || string.IsNullOrEmpty(e.Text))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Parse quest information from /myquests output
|
||||||
|
ParseQuestLine(e.Text);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"Error parsing quest line: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ParseQuestLine(string text)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Quest line format: TaskName - Solves solves (Timestamp)"Description" MaxSolves Delta
|
||||||
|
// Example: "SomeQuest - 5 solves (1640995200)"Quest description here" 10 3600
|
||||||
|
var pattern = @"([^\-]+) - (\d+) solves \((\d+)\)""([^""]+)"" (-?\d+) (\d+)";
|
||||||
|
var match = Regex.Match(text, pattern);
|
||||||
|
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
var quest = new Quest
|
||||||
|
{
|
||||||
|
Id = match.Groups[1].Value.Trim(),
|
||||||
|
Solves = int.Parse(match.Groups[2].Value),
|
||||||
|
Timestamp = int.Parse(match.Groups[3].Value),
|
||||||
|
Description = match.Groups[4].Value,
|
||||||
|
MaxSolves = int.Parse(match.Groups[5].Value),
|
||||||
|
Delta = int.Parse(match.Groups[6].Value)
|
||||||
|
};
|
||||||
|
|
||||||
|
quest.ExpireTime = quest.Timestamp + quest.Delta;
|
||||||
|
|
||||||
|
// Add to collections
|
||||||
|
QuestList.Add(quest);
|
||||||
|
QuestDictionary[quest.Id] = quest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"Error parsing quest line '{text}': {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Quest Management
|
||||||
|
public void RefreshQuests()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (isRefreshing)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ClearQuests();
|
||||||
|
isRefreshing = true;
|
||||||
|
|
||||||
|
// Issue /myquests command to refresh quest data
|
||||||
|
CoreManager.Current.Actions.InvokeChatParser("/myquests");
|
||||||
|
|
||||||
|
// Stop listening after a delay
|
||||||
|
System.Threading.Timer stopTimer = null;
|
||||||
|
stopTimer = new System.Threading.Timer(_ =>
|
||||||
|
{
|
||||||
|
isRefreshing = false;
|
||||||
|
stopTimer?.Dispose();
|
||||||
|
lastRefreshTime = DateTime.Now;
|
||||||
|
}, null, 3000, System.Threading.Timeout.Infinite);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
isRefreshing = false;
|
||||||
|
PluginCore.WriteToChat($"Error refreshing quests: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearQuests()
|
||||||
|
{
|
||||||
|
QuestList.Clear();
|
||||||
|
QuestDictionary.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsQuestAvailable(string questStamp)
|
||||||
|
{
|
||||||
|
if (!QuestDictionary.TryGetValue(questStamp, out Quest quest))
|
||||||
|
return true; // If quest not found, assume available
|
||||||
|
|
||||||
|
var currentTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||||
|
return quest.ExpireTime < currentTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsQuestMaxSolved(string questStamp)
|
||||||
|
{
|
||||||
|
if (!QuestDictionary.TryGetValue(questStamp, out Quest quest))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return quest.Solves >= quest.MaxSolves;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasQuestFlag(string questStamp)
|
||||||
|
{
|
||||||
|
return QuestDictionary.ContainsKey(questStamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetTimeUntilExpire(Quest quest)
|
||||||
|
{
|
||||||
|
if (quest == null)
|
||||||
|
return "Unknown";
|
||||||
|
|
||||||
|
var currentTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||||
|
var timeLeft = quest.ExpireTime - currentTime;
|
||||||
|
|
||||||
|
if (timeLeft <= 0)
|
||||||
|
return "Ready";
|
||||||
|
|
||||||
|
return FormatSeconds((int)timeLeft);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string FormatTimeStamp(int timestamp)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var dateTime = DateTimeOffset.FromUnixTimeSeconds(timestamp).DateTime;
|
||||||
|
return dateTime.ToString("MM/dd/yyyy HH:mm:ss");
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return "Invalid";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string FormatSeconds(int seconds)
|
||||||
|
{
|
||||||
|
if (seconds <= 0)
|
||||||
|
return "0s";
|
||||||
|
|
||||||
|
var days = seconds / 86400;
|
||||||
|
seconds %= 86400;
|
||||||
|
var hours = seconds / 3600;
|
||||||
|
seconds %= 3600;
|
||||||
|
var minutes = seconds / 60;
|
||||||
|
seconds %= 60;
|
||||||
|
|
||||||
|
var result = "";
|
||||||
|
if (days > 0) result += $"{days}d ";
|
||||||
|
if (hours > 0) result += $"{hours}h ";
|
||||||
|
if (minutes > 0) result += $"{minutes}m ";
|
||||||
|
if (seconds > 0 || string.IsNullOrEmpty(result)) result += $"{seconds}s";
|
||||||
|
|
||||||
|
return result.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
public object GetFieldByID(Quest quest, int id)
|
||||||
|
{
|
||||||
|
if (quest == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
switch (id)
|
||||||
|
{
|
||||||
|
case 1: return quest.Id;
|
||||||
|
case 2: return quest.Solves;
|
||||||
|
case 3: return quest.Timestamp;
|
||||||
|
case 4: return quest.MaxSolves;
|
||||||
|
case 5: return quest.Delta;
|
||||||
|
case 6: return quest.ExpireTime;
|
||||||
|
default: return quest.Id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Society Quest Helpers
|
||||||
|
public string GetSocietyName(int factionBits)
|
||||||
|
{
|
||||||
|
switch (factionBits)
|
||||||
|
{
|
||||||
|
case 1: return "Celestial Hand";
|
||||||
|
case 2: return "Eldrytch Web";
|
||||||
|
case 4: return "Radiant Blood";
|
||||||
|
default: return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetSocietyRank(int ribbons)
|
||||||
|
{
|
||||||
|
if (ribbons >= 1001) return "Master";
|
||||||
|
if (ribbons >= 601) return "Lord";
|
||||||
|
if (ribbons >= 301) return "Knight";
|
||||||
|
if (ribbons >= 101) return "Adept";
|
||||||
|
if (ribbons >= 1) return "Initiate";
|
||||||
|
return "None";
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetMaxRibbonsPerDay(string rank)
|
||||||
|
{
|
||||||
|
switch (rank)
|
||||||
|
{
|
||||||
|
case "Initiate": return 50;
|
||||||
|
case "Adept": return 100;
|
||||||
|
case "Knight": return 150;
|
||||||
|
case "Lord": return 200;
|
||||||
|
case "Master": return 250;
|
||||||
|
default: return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Cleanup
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (CoreManager.Current != null)
|
||||||
|
{
|
||||||
|
CoreManager.Current.ChatBoxMessage -= OnChatBoxMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClearQuests();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"Error disposing quest manager: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
227
MosswartMassacre/SpellManager.cs
Normal file
227
MosswartMassacre/SpellManager.cs
Normal file
|
|
@ -0,0 +1,227 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using Mag.Shared.Spells;
|
||||||
|
|
||||||
|
namespace MosswartMassacre
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Manages spell identification and cantrip detection for the Flag Tracker
|
||||||
|
/// </summary>
|
||||||
|
public static class SpellManager
|
||||||
|
{
|
||||||
|
private static readonly Dictionary<int, Spell> SpellsById = new Dictionary<int, Spell>();
|
||||||
|
private static readonly List<string[]> SpellData = new List<string[]>();
|
||||||
|
private static bool isInitialized = false;
|
||||||
|
|
||||||
|
static SpellManager()
|
||||||
|
{
|
||||||
|
Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Initialize()
|
||||||
|
{
|
||||||
|
if (isInitialized) return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Load spell data from embedded CSV resource
|
||||||
|
var assembly = Assembly.GetExecutingAssembly();
|
||||||
|
|
||||||
|
// Try to find the resource with different naming patterns
|
||||||
|
var availableResources = assembly.GetManifestResourceNames();
|
||||||
|
var spellResource = availableResources.FirstOrDefault(r => r.Contains("Spells.csv"));
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(spellResource))
|
||||||
|
{
|
||||||
|
// If not embedded, try to load from file system
|
||||||
|
var csvPath = Path.Combine(Path.GetDirectoryName(assembly.Location), "..", "Shared", "Spells", "Spells.csv");
|
||||||
|
if (File.Exists(csvPath))
|
||||||
|
{
|
||||||
|
LoadFromFile(csvPath);
|
||||||
|
isInitialized = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
using (var stream = assembly.GetManifestResourceStream(spellResource))
|
||||||
|
{
|
||||||
|
if (stream != null)
|
||||||
|
{
|
||||||
|
using (var reader = new StreamReader(stream))
|
||||||
|
{
|
||||||
|
LoadFromReader(reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isInitialized = true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"SpellManager initialization error: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void LoadFromFile(string path)
|
||||||
|
{
|
||||||
|
using (var reader = new StreamReader(path))
|
||||||
|
{
|
||||||
|
LoadFromReader(reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void LoadFromReader(StreamReader reader)
|
||||||
|
{
|
||||||
|
// Skip header line
|
||||||
|
var header = reader.ReadLine();
|
||||||
|
|
||||||
|
while (!reader.EndOfStream)
|
||||||
|
{
|
||||||
|
var line = reader.ReadLine();
|
||||||
|
if (!string.IsNullOrWhiteSpace(line))
|
||||||
|
{
|
||||||
|
var parts = line.Split(',');
|
||||||
|
if (parts.Length >= 6) // Minimum required fields
|
||||||
|
{
|
||||||
|
SpellData.Add(parts);
|
||||||
|
|
||||||
|
// Parse spell data
|
||||||
|
if (int.TryParse(parts[0], out int id))
|
||||||
|
{
|
||||||
|
var name = parts[1];
|
||||||
|
int.TryParse(parts[3], out int difficulty);
|
||||||
|
int.TryParse(parts[4], out int duration);
|
||||||
|
int.TryParse(parts[5], out int family);
|
||||||
|
|
||||||
|
var spell = new Spell(id, name, difficulty, duration, family);
|
||||||
|
SpellsById[id] = spell;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a spell by its ID
|
||||||
|
/// </summary>
|
||||||
|
public static Spell GetSpell(int id)
|
||||||
|
{
|
||||||
|
if (SpellsById.TryGetValue(id, out var spell))
|
||||||
|
return spell;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a spell by its name (case-insensitive)
|
||||||
|
/// </summary>
|
||||||
|
public static Spell GetSpell(string name)
|
||||||
|
{
|
||||||
|
return SpellsById.Values.FirstOrDefault(s =>
|
||||||
|
string.Equals(s.Name, name, StringComparison.OrdinalIgnoreCase));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the total number of spells loaded
|
||||||
|
/// </summary>
|
||||||
|
public static int GetSpellCount()
|
||||||
|
{
|
||||||
|
return SpellsById.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Detects if a spell is a cantrip and returns its info
|
||||||
|
/// </summary>
|
||||||
|
public static CantripInfo DetectCantrip(Spell spell)
|
||||||
|
{
|
||||||
|
if (spell == null || spell.CantripLevel == Spell.CantripLevels.None)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var info = new CantripInfo
|
||||||
|
{
|
||||||
|
SpellId = spell.Id,
|
||||||
|
Name = spell.Name,
|
||||||
|
Level = GetCantripLevelName(spell.CantripLevel),
|
||||||
|
Color = GetCantripColor(spell.CantripLevel)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Extract skill/attribute name from spell name
|
||||||
|
info.SkillName = ExtractSkillFromSpellName(spell.Name, info.Level);
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetCantripLevelName(Spell.CantripLevels level)
|
||||||
|
{
|
||||||
|
switch (level)
|
||||||
|
{
|
||||||
|
case Spell.CantripLevels.Minor: return "Minor";
|
||||||
|
case Spell.CantripLevels.Moderate: return "Moderate";
|
||||||
|
case Spell.CantripLevels.Major: return "Major";
|
||||||
|
case Spell.CantripLevels.Epic: return "Epic";
|
||||||
|
case Spell.CantripLevels.Legendary: return "Legendary";
|
||||||
|
default: return "N/A";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static System.Drawing.Color GetCantripColor(Spell.CantripLevels level)
|
||||||
|
{
|
||||||
|
switch (level)
|
||||||
|
{
|
||||||
|
case Spell.CantripLevels.Minor: return System.Drawing.Color.White;
|
||||||
|
case Spell.CantripLevels.Moderate: return System.Drawing.Color.Green;
|
||||||
|
case Spell.CantripLevels.Major: return System.Drawing.Color.Blue;
|
||||||
|
case Spell.CantripLevels.Epic: return System.Drawing.Color.Purple;
|
||||||
|
case Spell.CantripLevels.Legendary: return System.Drawing.Color.Orange;
|
||||||
|
default: return System.Drawing.Color.White;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ExtractSkillFromSpellName(string spellName, string level)
|
||||||
|
{
|
||||||
|
// Remove the cantrip level prefix
|
||||||
|
var skillPart = spellName;
|
||||||
|
if (!string.IsNullOrEmpty(level) && skillPart.StartsWith(level + " "))
|
||||||
|
{
|
||||||
|
skillPart = skillPart.Substring(level.Length + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map common spell name patterns to skill names
|
||||||
|
if (skillPart.Contains("Strength")) return "Strength";
|
||||||
|
if (skillPart.Contains("Endurance")) return "Endurance";
|
||||||
|
if (skillPart.Contains("Coordination")) return "Coordination";
|
||||||
|
if (skillPart.Contains("Quickness")) return "Quickness";
|
||||||
|
if (skillPart.Contains("Focus")) return "Focus";
|
||||||
|
if (skillPart.Contains("Self") || skillPart.Contains("Willpower")) return "Willpower";
|
||||||
|
|
||||||
|
// Protection mappings
|
||||||
|
if (skillPart.Contains("Armor")) return "Armor";
|
||||||
|
if (skillPart.Contains("Bludgeoning")) return "Bludgeoning Ward";
|
||||||
|
if (skillPart.Contains("Piercing")) return "Piercing Ward";
|
||||||
|
if (skillPart.Contains("Slashing")) return "Slashing Ward";
|
||||||
|
if (skillPart.Contains("Flame") || skillPart.Contains("Fire")) return "Flame Ward";
|
||||||
|
if (skillPart.Contains("Frost") || skillPart.Contains("Cold")) return "Frost Ward";
|
||||||
|
if (skillPart.Contains("Acid")) return "Acid Ward";
|
||||||
|
if (skillPart.Contains("Lightning") || skillPart.Contains("Electric")) return "Storm Ward";
|
||||||
|
|
||||||
|
// Return the skill part as-is if no mapping found
|
||||||
|
return skillPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Information about a detected cantrip
|
||||||
|
/// </summary>
|
||||||
|
public class CantripInfo
|
||||||
|
{
|
||||||
|
public int SpellId { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string SkillName { get; set; }
|
||||||
|
public string Level { get; set; }
|
||||||
|
public System.Drawing.Color Color { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
733
MosswartMassacre/Views/FlagTrackerView.cs
Normal file
733
MosswartMassacre/Views/FlagTrackerView.cs
Normal file
|
|
@ -0,0 +1,733 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Linq;
|
||||||
|
using VirindiViewService.Controls;
|
||||||
|
|
||||||
|
namespace MosswartMassacre.Views
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Dedicated Flag Tracker window with comprehensive character tracking
|
||||||
|
/// Ported from UBS Lua flagtracker with full functionality preservation
|
||||||
|
/// </summary>
|
||||||
|
internal class FlagTrackerView : VVSBaseView
|
||||||
|
{
|
||||||
|
private static FlagTrackerView instance;
|
||||||
|
|
||||||
|
#region Tab Control References
|
||||||
|
private HudTabView mainTabView;
|
||||||
|
|
||||||
|
// Augmentations Tab
|
||||||
|
private HudList lstAugmentations;
|
||||||
|
private HudButton btnRefreshAugs;
|
||||||
|
|
||||||
|
// Luminance Tab
|
||||||
|
private HudList lstLuminanceAuras;
|
||||||
|
private HudButton btnRefreshLum;
|
||||||
|
|
||||||
|
// Recalls Tab
|
||||||
|
private HudList lstRecallSpells;
|
||||||
|
private HudButton btnRefreshRecalls;
|
||||||
|
|
||||||
|
|
||||||
|
// Cantrips Tab
|
||||||
|
private HudList lstCantrips;
|
||||||
|
private HudButton btnRefreshCantrips;
|
||||||
|
|
||||||
|
// Weapons Tab
|
||||||
|
private HudList lstWeapons;
|
||||||
|
private HudButton btnRefreshWeapons;
|
||||||
|
|
||||||
|
// Quests Tab
|
||||||
|
private HudList lstQuests;
|
||||||
|
private HudButton btnRefreshQuests;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Data Management
|
||||||
|
private FlagTrackerData data;
|
||||||
|
private QuestManager questManager;
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public FlagTrackerView(PluginCore core) : base(core)
|
||||||
|
{
|
||||||
|
instance = this;
|
||||||
|
data = new FlagTrackerData();
|
||||||
|
questManager = new QuestManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Static Interface
|
||||||
|
public static void OpenFlagTracker()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (instance == null)
|
||||||
|
{
|
||||||
|
instance = new FlagTrackerView(null);
|
||||||
|
instance.InitializeView();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Bring existing window to front
|
||||||
|
if (instance.view != null)
|
||||||
|
{
|
||||||
|
instance.view.Visible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"Error opening Flag Tracker: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CloseFlagTracker()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (instance != null)
|
||||||
|
{
|
||||||
|
instance.Dispose();
|
||||||
|
instance = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"Error closing Flag Tracker: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsOpen()
|
||||||
|
{
|
||||||
|
return instance != null && instance.view != null && instance.view.Visible;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Initialization
|
||||||
|
private void InitializeView()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Create view from XML layout
|
||||||
|
CreateFromXMLResource("MosswartMassacre.ViewXML.flagTracker.xml");
|
||||||
|
|
||||||
|
// Initialize all tab controls
|
||||||
|
InitializeTabControls();
|
||||||
|
InitializeEventHandlers();
|
||||||
|
|
||||||
|
// Initialize the base view
|
||||||
|
Initialize();
|
||||||
|
|
||||||
|
// Make the view visible
|
||||||
|
if (view != null)
|
||||||
|
{
|
||||||
|
view.Visible = true;
|
||||||
|
view.ShowInBar = true;
|
||||||
|
view.Title = "Mossy Tracker v3.0.1.1";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial data refresh
|
||||||
|
RefreshAllData();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"Error initializing Flag Tracker view: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeTabControls()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Get main tab view
|
||||||
|
mainTabView = GetControl<HudTabView>("mainTabView");
|
||||||
|
|
||||||
|
// Augmentations Tab
|
||||||
|
lstAugmentations = GetControl<HudList>("lstAugmentations");
|
||||||
|
btnRefreshAugs = GetControl<HudButton>("btnRefreshAugs");
|
||||||
|
|
||||||
|
// Luminance Tab
|
||||||
|
lstLuminanceAuras = GetControl<HudList>("lstLuminanceAuras");
|
||||||
|
btnRefreshLum = GetControl<HudButton>("btnRefreshLum");
|
||||||
|
|
||||||
|
// Recalls Tab
|
||||||
|
lstRecallSpells = GetControl<HudList>("lstRecallSpells");
|
||||||
|
btnRefreshRecalls = GetControl<HudButton>("btnRefreshRecalls");
|
||||||
|
|
||||||
|
// Cantrips Tab
|
||||||
|
lstCantrips = GetControl<HudList>("lstCantrips");
|
||||||
|
btnRefreshCantrips = GetControl<HudButton>("btnRefreshCantrips");
|
||||||
|
|
||||||
|
// Weapons Tab
|
||||||
|
lstWeapons = GetControl<HudList>("lstWeapons");
|
||||||
|
btnRefreshWeapons = GetControl<HudButton>("btnRefreshWeapons");
|
||||||
|
|
||||||
|
// Quests Tab
|
||||||
|
lstQuests = GetControl<HudList>("lstQuests");
|
||||||
|
btnRefreshQuests = GetControl<HudButton>("btnRefreshQuests");
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"Error initializing tab controls: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeEventHandlers()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Refresh button events
|
||||||
|
if (btnRefreshAugs != null) btnRefreshAugs.Hit += OnRefreshAugmentations;
|
||||||
|
if (btnRefreshLum != null) btnRefreshLum.Hit += OnRefreshLuminance;
|
||||||
|
if (btnRefreshRecalls != null) btnRefreshRecalls.Hit += OnRefreshRecalls;
|
||||||
|
if (btnRefreshCantrips != null) btnRefreshCantrips.Hit += OnRefreshCantrips;
|
||||||
|
if (btnRefreshWeapons != null) btnRefreshWeapons.Hit += OnRefreshWeapons;
|
||||||
|
if (btnRefreshQuests != null) btnRefreshQuests.Hit += OnRefreshQuests;
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"Error initializing event handlers: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Event Handlers
|
||||||
|
private void OnRefreshAugmentations(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
data.RefreshAugmentations();
|
||||||
|
PopulateAugmentationsList();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"Error refreshing augmentations: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRefreshLuminance(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
data.RefreshLuminanceAuras();
|
||||||
|
PopulateLuminanceList();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"Error refreshing luminance auras: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRefreshRecalls(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
data.RefreshRecallSpells();
|
||||||
|
PopulateRecallsList();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"Error refreshing recall spells: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void OnRefreshCantrips(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
data.RefreshCantrips();
|
||||||
|
PopulateCantripsList();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"Error refreshing cantrips: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRefreshWeapons(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
data.RefreshWeapons();
|
||||||
|
PopulateWeaponsList();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"Error refreshing weapons: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRefreshQuests(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
questManager.RefreshQuests();
|
||||||
|
PopulateQuestsList();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"Error refreshing quests: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Helper Methods
|
||||||
|
private void SafeSetListText(HudList.HudListRowAccessor row, int columnIndex, string text)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (row != null && columnIndex >= 0)
|
||||||
|
{
|
||||||
|
// Check if the column exists
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var control = row[columnIndex];
|
||||||
|
if (control != null)
|
||||||
|
{
|
||||||
|
((HudStaticText)control).Text = text ?? "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IndexOutOfRangeException)
|
||||||
|
{
|
||||||
|
// Column doesn't exist - ignore silently
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"Error setting list text at column {columnIndex}: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SafeSetListColor(HudList.HudListRowAccessor row, int columnIndex, Color color)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (row != null && columnIndex >= 0 && row[columnIndex] != null)
|
||||||
|
{
|
||||||
|
((HudStaticText)row[columnIndex]).TextColor = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"Error setting list color at column {columnIndex}: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SafeSetListImage(HudList.HudListRowAccessor row, int columnIndex, int iconId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (row != null && columnIndex >= 0)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var control = row[columnIndex];
|
||||||
|
|
||||||
|
if (control != null && control is VirindiViewService.Controls.HudPictureBox)
|
||||||
|
{
|
||||||
|
var pictureBox = (VirindiViewService.Controls.HudPictureBox)control;
|
||||||
|
pictureBox.Image = iconId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IndexOutOfRangeException)
|
||||||
|
{
|
||||||
|
// Column doesn't exist - ignore silently
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"Error setting list image at column {columnIndex}: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetIconSymbol(string category)
|
||||||
|
{
|
||||||
|
switch (category)
|
||||||
|
{
|
||||||
|
case "Basic Recalls":
|
||||||
|
return "[B]"; // Basic recalls
|
||||||
|
case "Island Recalls":
|
||||||
|
return "[I]"; // Island recalls
|
||||||
|
case "Town Recalls":
|
||||||
|
return "[T]"; // Town recalls
|
||||||
|
case "Special Recalls":
|
||||||
|
return "[S]"; // Special recalls
|
||||||
|
default:
|
||||||
|
return "[?]"; // Unknown category
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Data Population Methods
|
||||||
|
private void RefreshAllData()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
questManager.RefreshQuests();
|
||||||
|
data.RefreshAll();
|
||||||
|
|
||||||
|
PopulateAugmentationsList();
|
||||||
|
PopulateLuminanceList();
|
||||||
|
PopulateRecallsList();
|
||||||
|
PopulateCantripsList();
|
||||||
|
PopulateQuestsList();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"Error refreshing all data: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PopulateAugmentationsList()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (lstAugmentations == null || data?.AugmentationCategories == null) return;
|
||||||
|
|
||||||
|
lstAugmentations.ClearRows();
|
||||||
|
|
||||||
|
foreach (var category in data.AugmentationCategories)
|
||||||
|
{
|
||||||
|
// Add category header
|
||||||
|
var headerRow = lstAugmentations.AddRow();
|
||||||
|
SafeSetListText(headerRow, 0, $"--- {category.Key} ---");
|
||||||
|
SafeSetListText(headerRow, 1, "");
|
||||||
|
SafeSetListText(headerRow, 2, "");
|
||||||
|
SafeSetListText(headerRow, 3, "");
|
||||||
|
|
||||||
|
// Add augmentations in this category
|
||||||
|
foreach (var aug in category.Value)
|
||||||
|
{
|
||||||
|
var row = lstAugmentations.AddRow();
|
||||||
|
|
||||||
|
// Augmentation name with progress indicator
|
||||||
|
string progressText = aug.IsMaxed ? "[MAX]" : $"[{aug.CurrentValue}/{aug.Repeatable}]";
|
||||||
|
SafeSetListText(row, 0, aug.Name);
|
||||||
|
SafeSetListText(row, 1, progressText);
|
||||||
|
SafeSetListText(row, 2, aug.Trainer);
|
||||||
|
SafeSetListText(row, 3, aug.Location);
|
||||||
|
|
||||||
|
// Color code based on completion status
|
||||||
|
Color progressColor = Color.Red;
|
||||||
|
if (aug.IsMaxed)
|
||||||
|
{
|
||||||
|
progressColor = Color.Green;
|
||||||
|
}
|
||||||
|
else if (aug.CurrentValue > 0)
|
||||||
|
{
|
||||||
|
progressColor = Color.Yellow;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply color to progress text
|
||||||
|
SafeSetListColor(row, 1, progressColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"Error populating augmentations list: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PopulateLuminanceList()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (lstLuminanceAuras == null || data?.LuminanceAuraCategories == null) return;
|
||||||
|
|
||||||
|
lstLuminanceAuras.ClearRows();
|
||||||
|
|
||||||
|
foreach (var category in data.LuminanceAuraCategories)
|
||||||
|
{
|
||||||
|
// Add category header
|
||||||
|
var headerRow = lstLuminanceAuras.AddRow();
|
||||||
|
SafeSetListText(headerRow, 0, $"--- {category.Key} ---");
|
||||||
|
SafeSetListText(headerRow, 1, "");
|
||||||
|
SafeSetListText(headerRow, 2, "");
|
||||||
|
|
||||||
|
// Add luminance auras in this category
|
||||||
|
foreach (var aura in category.Value)
|
||||||
|
{
|
||||||
|
var row = lstLuminanceAuras.AddRow();
|
||||||
|
|
||||||
|
// Aura name
|
||||||
|
SafeSetListText(row, 0, aura.Name);
|
||||||
|
|
||||||
|
// Progress (current/cap)
|
||||||
|
string progressText = $"{aura.CurrentValue}/{aura.Cap}";
|
||||||
|
SafeSetListText(row, 1, progressText);
|
||||||
|
|
||||||
|
// Category or quest flag for Seer auras
|
||||||
|
string categoryText = category.Key == "Seer Auras" && !string.IsNullOrEmpty(aura.QuestFlag)
|
||||||
|
? aura.QuestFlag
|
||||||
|
: category.Key;
|
||||||
|
SafeSetListText(row, 2, categoryText);
|
||||||
|
|
||||||
|
// Color code based on progress
|
||||||
|
Color progressColor = Color.Red;
|
||||||
|
if (aura.CurrentValue >= aura.Cap)
|
||||||
|
{
|
||||||
|
progressColor = Color.Green;
|
||||||
|
}
|
||||||
|
else if (aura.CurrentValue > 0)
|
||||||
|
{
|
||||||
|
progressColor = Color.Yellow;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply color to progress text
|
||||||
|
SafeSetListColor(row, 1, progressColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"Error populating luminance list: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PopulateRecallsList()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (lstRecallSpells == null || data?.RecallSpells == null) return;
|
||||||
|
|
||||||
|
lstRecallSpells.ClearRows();
|
||||||
|
|
||||||
|
foreach (var recall in data.RecallSpells)
|
||||||
|
{
|
||||||
|
var row = lstRecallSpells.AddRow();
|
||||||
|
|
||||||
|
|
||||||
|
// Column 0: Spell icon using MagTools approach
|
||||||
|
SafeSetListImage(row, 0, recall.IconId);
|
||||||
|
|
||||||
|
// Column 1: Recall spell name
|
||||||
|
SafeSetListText(row, 1, recall.Name);
|
||||||
|
|
||||||
|
// Column 2: Known status
|
||||||
|
string status = recall.IsKnown ? "Known" : "Unknown";
|
||||||
|
SafeSetListText(row, 2, status);
|
||||||
|
|
||||||
|
// Color code based on known status
|
||||||
|
Color statusColor = recall.IsKnown ? Color.Green : Color.Red;
|
||||||
|
SafeSetListColor(row, 2, statusColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"Error populating recalls list: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void PopulateCantripsList()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (lstCantrips == null || data?.Cantrips == null) return;
|
||||||
|
|
||||||
|
lstCantrips.ClearRows();
|
||||||
|
|
||||||
|
foreach (var category in data.Cantrips)
|
||||||
|
{
|
||||||
|
// Add category header
|
||||||
|
var headerRow = lstCantrips.AddRow();
|
||||||
|
SafeSetListImage(headerRow, 0, 0x6002856); // Star icon for category headers
|
||||||
|
SafeSetListText(headerRow, 1, $"--- {category.Key} ---");
|
||||||
|
SafeSetListText(headerRow, 2, "");
|
||||||
|
|
||||||
|
// Add cantrips in this category
|
||||||
|
foreach (var cantrip in category.Value)
|
||||||
|
{
|
||||||
|
var row = lstCantrips.AddRow();
|
||||||
|
|
||||||
|
// Column 0: Icon (green/red circle based on status)
|
||||||
|
SafeSetListImage(row, 0, cantrip.Value.ComputedIconId);
|
||||||
|
|
||||||
|
// Column 1: Skill/Attribute name
|
||||||
|
SafeSetListText(row, 1, cantrip.Key);
|
||||||
|
|
||||||
|
// Column 2: Cantrip level
|
||||||
|
SafeSetListText(row, 2, cantrip.Value.Value);
|
||||||
|
|
||||||
|
// Apply color coding based on cantrip level
|
||||||
|
SafeSetListColor(row, 2, cantrip.Value.Color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"Error populating cantrips list: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PopulateWeaponsList()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (lstWeapons == null || data?.WeaponCategories == null) return;
|
||||||
|
|
||||||
|
lstWeapons.ClearRows();
|
||||||
|
|
||||||
|
foreach (var category in data.WeaponCategories)
|
||||||
|
{
|
||||||
|
// Add category header
|
||||||
|
var headerRow = lstWeapons.AddRow();
|
||||||
|
SafeSetListText(headerRow, 0, $"--- {category.Key} ---");
|
||||||
|
SafeSetListText(headerRow, 1, "");
|
||||||
|
SafeSetListText(headerRow, 2, "");
|
||||||
|
SafeSetListText(headerRow, 3, "");
|
||||||
|
|
||||||
|
// Add weapons in this category
|
||||||
|
foreach (var weapon in category.Value)
|
||||||
|
{
|
||||||
|
var row = lstWeapons.AddRow();
|
||||||
|
|
||||||
|
// Column 0: Category
|
||||||
|
SafeSetListText(row, 0, weapon.Category);
|
||||||
|
|
||||||
|
// Column 1: Weapon Type
|
||||||
|
SafeSetListText(row, 1, weapon.WeaponType);
|
||||||
|
|
||||||
|
// Column 2: Weapon Name
|
||||||
|
SafeSetListText(row, 2, weapon.Name);
|
||||||
|
|
||||||
|
// Column 3: Status
|
||||||
|
SafeSetListText(row, 3, weapon.Status);
|
||||||
|
|
||||||
|
// Color code based on acquisition status
|
||||||
|
System.Drawing.Color statusColor = weapon.IsAcquired ?
|
||||||
|
System.Drawing.Color.Green : System.Drawing.Color.Red;
|
||||||
|
SafeSetListColor(row, 3, statusColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.WeaponCategories.Count == 0)
|
||||||
|
{
|
||||||
|
var row = lstWeapons.AddRow();
|
||||||
|
SafeSetListText(row, 0, "No weapon data - click Refresh");
|
||||||
|
SafeSetListText(row, 1, "");
|
||||||
|
SafeSetListText(row, 2, "");
|
||||||
|
SafeSetListText(row, 3, "");
|
||||||
|
SafeSetListColor(row, 0, System.Drawing.Color.Gray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"Error populating weapons list: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PopulateQuestsList()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (lstQuests == null)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat("Quest list control is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lstQuests.ClearRows();
|
||||||
|
|
||||||
|
// Always show debug info for now
|
||||||
|
var row = lstQuests.AddRow();
|
||||||
|
SafeSetListText(row, 0, $"Quest Manager: {(questManager != null ? "OK" : "NULL")}");
|
||||||
|
SafeSetListText(row, 1, $"Quest Count: {questManager?.QuestList?.Count ?? 0}");
|
||||||
|
SafeSetListText(row, 2, "Click Refresh to load quest data");
|
||||||
|
SafeSetListText(row, 3, "");
|
||||||
|
SafeSetListText(row, 4, "");
|
||||||
|
SafeSetListText(row, 5, "");
|
||||||
|
|
||||||
|
if (questManager?.QuestList != null && questManager.QuestList.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var quest in questManager.QuestList.OrderBy(q => q.Id))
|
||||||
|
{
|
||||||
|
var questRow = lstQuests.AddRow();
|
||||||
|
|
||||||
|
// Column 0: Quest Name
|
||||||
|
SafeSetListText(questRow, 0, quest.Id);
|
||||||
|
|
||||||
|
// Column 1: Solves
|
||||||
|
SafeSetListText(questRow, 1, quest.Solves.ToString());
|
||||||
|
|
||||||
|
// Column 2: Completed date
|
||||||
|
SafeSetListText(questRow, 2, questManager.FormatTimeStamp(quest.Timestamp));
|
||||||
|
|
||||||
|
// Column 3: Max solves
|
||||||
|
string maxText = quest.MaxSolves < 0 ? "∞" : quest.MaxSolves.ToString();
|
||||||
|
SafeSetListText(questRow, 3, maxText);
|
||||||
|
|
||||||
|
// Column 4: Delta (cooldown in seconds)
|
||||||
|
SafeSetListText(questRow, 4, questManager.FormatSeconds(quest.Delta));
|
||||||
|
|
||||||
|
// Column 5: Expire time
|
||||||
|
string expireText = questManager.GetTimeUntilExpire(quest);
|
||||||
|
SafeSetListText(questRow, 5, expireText);
|
||||||
|
|
||||||
|
// Color coding based on availability
|
||||||
|
var currentTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||||
|
|
||||||
|
if (quest.MaxSolves > 0 && quest.Solves >= quest.MaxSolves)
|
||||||
|
{
|
||||||
|
// Quest is maxed out - red
|
||||||
|
SafeSetListColor(questRow, 1, System.Drawing.Color.Red);
|
||||||
|
SafeSetListColor(questRow, 5, System.Drawing.Color.Red);
|
||||||
|
}
|
||||||
|
else if (quest.ExpireTime <= currentTime)
|
||||||
|
{
|
||||||
|
// Quest is available - green
|
||||||
|
SafeSetListColor(questRow, 5, System.Drawing.Color.Green);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Quest is on cooldown - yellow
|
||||||
|
SafeSetListColor(questRow, 5, System.Drawing.Color.Yellow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"Error populating quests list: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region Cleanup
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (data != null)
|
||||||
|
{
|
||||||
|
data.Dispose();
|
||||||
|
data = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (questManager != null)
|
||||||
|
{
|
||||||
|
questManager.Dispose();
|
||||||
|
questManager = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"Error disposing Flag Tracker: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -54,6 +54,11 @@ namespace MosswartMassacre.Views
|
||||||
private HudButton btnClearRoute;
|
private HudButton btnClearRoute;
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Flag Tracker Tab Controls
|
||||||
|
private HudButton btnOpenFlagTracker;
|
||||||
|
private HudStaticText lblFlagTrackerStatus;
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Statistics Tracking
|
#region Statistics Tracking
|
||||||
private double bestHourlyKills = 0;
|
private double bestHourlyKills = 0;
|
||||||
private DateTime sessionStartTime;
|
private DateTime sessionStartTime;
|
||||||
|
|
@ -70,15 +75,28 @@ namespace MosswartMassacre.Views
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// Check if VVS is available
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var testParser = new VirindiViewService.XMLParsers.Decal3XMLParser();
|
||||||
|
}
|
||||||
|
catch (Exception vvsEx)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat("[ERROR] VVS not available: " + vvsEx.Message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (instance == null)
|
if (instance == null)
|
||||||
{
|
{
|
||||||
instance = new VVSTabbedMainView(null);
|
instance = new VVSTabbedMainView(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
instance.InitializeView();
|
instance.InitializeView();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat("Error initializing VVS tabbed view: " + ex.Message);
|
PluginCore.WriteToChat("Error initializing VVS tabbed view: " + ex.Message);
|
||||||
|
PluginCore.WriteToChat("Stack trace: " + ex.StackTrace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -107,11 +125,18 @@ namespace MosswartMassacre.Views
|
||||||
// Create view from working original XML layout
|
// Create view from working original XML layout
|
||||||
CreateFromXMLResource("MosswartMassacre.ViewXML.mainViewTabbed.xml");
|
CreateFromXMLResource("MosswartMassacre.ViewXML.mainViewTabbed.xml");
|
||||||
|
|
||||||
|
if (view == null)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat("[ERROR] View creation failed - view is null!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize all tab controls
|
// Initialize all tab controls
|
||||||
InitializeMainTabControls();
|
InitializeMainTabControls();
|
||||||
InitializeSettingsTabControls();
|
InitializeSettingsTabControls();
|
||||||
InitializeStatisticsTabControls();
|
InitializeStatisticsTabControls();
|
||||||
InitializeNavigationTabControls();
|
InitializeNavigationTabControls();
|
||||||
|
InitializeFlagTrackerTabControls();
|
||||||
|
|
||||||
// Initialize the base view and set initial position
|
// Initialize the base view and set initial position
|
||||||
Initialize();
|
Initialize();
|
||||||
|
|
@ -121,11 +146,13 @@ namespace MosswartMassacre.Views
|
||||||
{
|
{
|
||||||
view.Visible = true;
|
view.Visible = true;
|
||||||
view.ShowInBar = true;
|
view.ShowInBar = true;
|
||||||
|
PluginCore.WriteToChat("GUI initialized successfully!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat("Error in VVS InitializeView: " + ex.Message);
|
PluginCore.WriteToChat("Error in VVS InitializeView: " + ex.Message);
|
||||||
|
PluginCore.WriteToChat("Stack trace: " + ex.StackTrace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -246,6 +273,28 @@ namespace MosswartMassacre.Views
|
||||||
PluginCore.WriteToChat($"Error initializing navigation controls: {ex.Message}");
|
PluginCore.WriteToChat($"Error initializing navigation controls: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void InitializeFlagTrackerTabControls()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Flag Tracker tab controls
|
||||||
|
btnOpenFlagTracker = GetControl<HudButton>("btnOpenFlagTracker");
|
||||||
|
lblFlagTrackerStatus = GetControl<HudStaticText>("lblFlagTrackerStatus");
|
||||||
|
|
||||||
|
// Hook up Flag Tracker events
|
||||||
|
if (btnOpenFlagTracker != null)
|
||||||
|
btnOpenFlagTracker.Hit += OnOpenFlagTrackerClick;
|
||||||
|
|
||||||
|
// Update initial status
|
||||||
|
if (lblFlagTrackerStatus != null)
|
||||||
|
lblFlagTrackerStatus.Text = "Status: Click to open the Flag Tracker window";
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"Error initializing flag tracker controls: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Event Handlers - Settings Tab
|
#region Event Handlers - Settings Tab
|
||||||
|
|
@ -409,6 +458,31 @@ namespace MosswartMassacre.Views
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Event Handlers - Flag Tracker Tab
|
||||||
|
private void OnOpenFlagTrackerClick(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Update status to show opening
|
||||||
|
if (lblFlagTrackerStatus != null)
|
||||||
|
lblFlagTrackerStatus.Text = "Status: Opening Flag Tracker window...";
|
||||||
|
|
||||||
|
// Open the Flag Tracker window
|
||||||
|
FlagTrackerView.OpenFlagTracker();
|
||||||
|
|
||||||
|
// Update status
|
||||||
|
if (lblFlagTrackerStatus != null)
|
||||||
|
lblFlagTrackerStatus.Text = "Status: Flag Tracker window is open";
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PluginCore.WriteToChat($"Error opening Flag Tracker: {ex.Message}");
|
||||||
|
if (lblFlagTrackerStatus != null)
|
||||||
|
lblFlagTrackerStatus.Text = "Status: Error opening Flag Tracker";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Event Handlers - Navigation Tab
|
#region Event Handlers - Navigation Tab
|
||||||
private void OnNavVisualizationEnabledChanged(object sender, EventArgs e)
|
private void OnNavVisualizationEnabledChanged(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue