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">
|
||||
<Link>Shared\Constants\Dictionaries.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Shared\Spells\Spell.cs">
|
||||
<Link>Shared\Spells\Spell.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Shared\Constants\DoubleValueKey.cs">
|
||||
<Link>Shared\Constants\DoubleValueKey.cs</Link>
|
||||
</Compile>
|
||||
|
|
@ -160,9 +163,11 @@
|
|||
</Compile>
|
||||
<Compile Include="ClientTelemetry.cs" />
|
||||
<Compile Include="DecalHarmonyClean.cs" />
|
||||
<Compile Include="FlagTrackerData.cs" />
|
||||
<Compile Include="MossyInventory.cs" />
|
||||
<Compile Include="NavRoute.cs" />
|
||||
<Compile Include="NavVisualization.cs" />
|
||||
<Compile Include="QuestManager.cs" />
|
||||
<Compile Include="vTank.cs" />
|
||||
<Compile Include="VtankControl.cs" />
|
||||
<Compile Include="Telemetry.cs" />
|
||||
|
|
@ -179,6 +184,8 @@
|
|||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="SpellManager.cs" />
|
||||
<Compile Include="Views\FlagTrackerView.cs" />
|
||||
<Compile Include="Views\VVSBaseView.cs" />
|
||||
<Compile Include="Views\VVSTabbedMainView.cs" />
|
||||
<Compile Include="WebSocket.cs" />
|
||||
|
|
@ -190,8 +197,12 @@
|
|||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="ViewXML\flagTracker.xml" />
|
||||
<EmbeddedResource Include="ViewXML\mainView.xml" />
|
||||
<EmbeddedResource Include="ViewXML\mainViewTabbed.xml" />
|
||||
<EmbeddedResource Include="..\Shared\Spells\Spells.csv">
|
||||
<Link>Shared\Spells\Spells.csv</Link>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
|
|
|
|||
|
|
@ -96,15 +96,15 @@ namespace MosswartMassacre
|
|||
CoreManager.Current.CharacterFilter.LoginComplete += CharacterFilter_LoginComplete;
|
||||
CoreManager.Current.WorldFilter.CreateObject += OnSpawn;
|
||||
CoreManager.Current.WorldFilter.ReleaseObject += OnDespawn;
|
||||
|
||||
// Initialize VVS view after character login
|
||||
ViewManager.ViewInit();
|
||||
|
||||
// Initialize the timer
|
||||
updateTimer = new Timer(1000); // Update every second
|
||||
updateTimer.Elapsed += UpdateStats;
|
||||
updateTimer.Start();
|
||||
|
||||
// Initialize the view (UI) - use tabbed interface by default
|
||||
ViewManager.ViewInit();
|
||||
// Note: View initialization moved to LoginComplete for VVS compatibility
|
||||
|
||||
// Enable TLS1.2
|
||||
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
|
||||
|
|
@ -187,6 +187,8 @@ namespace MosswartMassacre
|
|||
|
||||
WriteToChat("Mosswart Massacre has started!");
|
||||
|
||||
|
||||
|
||||
PluginSettings.Initialize(); // Safe to call now
|
||||
|
||||
// Apply the values
|
||||
|
|
@ -605,7 +607,8 @@ namespace MosswartMassacre
|
|||
WriteToChat("/mm nextwp - Advance VTank to next waypoint");
|
||||
WriteToChat("/mm decalstatus - Check Harmony patch status (UtilityBelt version)");
|
||||
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;
|
||||
case "report":
|
||||
TimeSpan elapsed = DateTime.Now - statsStartTime;
|
||||
|
|
@ -797,6 +800,20 @@ namespace MosswartMassacre
|
|||
}
|
||||
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:
|
||||
WriteToChat($"Unknown /mm command: {subCommand}. Try /mm help");
|
||||
|
|
|
|||
|
|
@ -26,5 +26,5 @@ using System.Runtime.InteropServices;
|
|||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
[assembly: AssemblyVersion("3.0.1.1")]
|
||||
[assembly: AssemblyFileVersion("3.0.1.1")]
|
||||
[assembly: AssemblyVersion("3.0.1.2")]
|
||||
[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;
|
||||
#endregion
|
||||
|
||||
#region Flag Tracker Tab Controls
|
||||
private HudButton btnOpenFlagTracker;
|
||||
private HudStaticText lblFlagTrackerStatus;
|
||||
#endregion
|
||||
|
||||
#region Statistics Tracking
|
||||
private double bestHourlyKills = 0;
|
||||
private DateTime sessionStartTime;
|
||||
|
|
@ -70,15 +75,28 @@ namespace MosswartMassacre.Views
|
|||
{
|
||||
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)
|
||||
{
|
||||
instance = new VVSTabbedMainView(null);
|
||||
}
|
||||
|
||||
instance.InitializeView();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
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
|
||||
CreateFromXMLResource("MosswartMassacre.ViewXML.mainViewTabbed.xml");
|
||||
|
||||
if (view == null)
|
||||
{
|
||||
PluginCore.WriteToChat("[ERROR] View creation failed - view is null!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize all tab controls
|
||||
InitializeMainTabControls();
|
||||
InitializeSettingsTabControls();
|
||||
InitializeStatisticsTabControls();
|
||||
InitializeNavigationTabControls();
|
||||
InitializeFlagTrackerTabControls();
|
||||
|
||||
// Initialize the base view and set initial position
|
||||
Initialize();
|
||||
|
|
@ -121,11 +146,13 @@ namespace MosswartMassacre.Views
|
|||
{
|
||||
view.Visible = true;
|
||||
view.ShowInBar = true;
|
||||
PluginCore.WriteToChat("GUI initialized successfully!");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
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}");
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
#region Event Handlers - Settings Tab
|
||||
|
|
@ -409,6 +458,31 @@ namespace MosswartMassacre.Views
|
|||
}
|
||||
#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
|
||||
private void OnNavVisualizationEnabledChanged(object sender, EventArgs e)
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue