296 lines
No EOL
9.1 KiB
C#
296 lines
No EOL
9.1 KiB
C#
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
|
|
}
|
|
} |