diff --git a/RotationSolver.Basic/Actions/ActionCooldownInfo.cs b/RotationSolver.Basic/Actions/ActionCooldownInfo.cs
index ddcdb4a0a..807750ad8 100644
--- a/RotationSolver.Basic/Actions/ActionCooldownInfo.cs
+++ b/RotationSolver.Basic/Actions/ActionCooldownInfo.cs
@@ -58,7 +58,7 @@ namespace RotationSolver.Basic.Actions;
///
///
///
- public float RecastTimeRemainOneCharge => RecastTimeRemainOneChargeRaw + DataCenter.WeaponRemain;
+ public float RecastTimeRemainOneCharge => RecastTimeRemainOneChargeRaw - DataCenter.WeaponRemain;
float RecastTimeRemainOneChargeRaw => RecastTimeRemain % RecastTimeOneChargeRaw;
@@ -130,6 +130,17 @@ public bool WillHaveOneChargeGCD(uint gcdCount = 0, float offset = 0)
public bool WillHaveOneCharge(float remain)
=> HasOneCharge || RecastTimeRemainOneCharge <= remain;
+ ///
+ /// Is this action used after several time.
+ ///
+ ///
+ ///
+ public bool JustUsedAfter(float time)
+ {
+ var elapsed = RecastTimeElapsedRaw % RecastTimeOneChargeRaw;
+ return elapsed + DataCenter.WeaponRemain < time;
+ }
+
internal bool CooldownCheck(bool isEmpty, bool onLastAbility, bool ignoreClippingCheck, byte gcdCountForAbility)
{
if (!_action.Info.IsGeneralGCD)
diff --git a/RotationSolver.Basic/Data/TimelineItem.cs b/RotationSolver.Basic/Data/TimelineItem.cs
index 8b1594e8f..0de27c62e 100644
--- a/RotationSolver.Basic/Data/TimelineItem.cs
+++ b/RotationSolver.Basic/Data/TimelineItem.cs
@@ -1,11 +1,75 @@
-using System.Text.RegularExpressions;
+using ECommons.DalamudServices;
+using Newtonsoft.Json.Linq;
+using System.Text.RegularExpressions;
namespace RotationSolver.Basic.Data;
-internal readonly struct TimelineItem(float time, string name, params string[] ids)
+internal enum TimelineType : byte
{
+ Unknown,
+ InCombat,
+ GameLog,
+ Ability,
+ StartsUsing,
+ SystemLogMessage,
+ ActorControl,
+}
+
+internal readonly struct TimelineItem(float time, string name, TimelineType type, string[] ids, JObject? obj)
+{
+ public TimelineType Type => type;
+
public float Time => time;
- public bool IsShown => !name.StartsWith("--") && !name.EndsWith("--");
+ public JObject? Object => obj;
+
+ public string Name => name;
+
+ public bool IsShown => name is not "--Reset--" and not "--sync--";
+
+ public string this[string propertyName] => Object?[propertyName]?.ToString() ?? string.Empty;
+
+ public TimelineItem(float time, string name, string type, string[] ids, JObject? obj)
+ : this(time, name, GetTypeFromName(type), ids, obj)
+ {
+
+ }
+
+ private static TimelineType GetTypeFromName(string type)
+ {
+ switch(type)
+ {
+ case "":
+ return TimelineType.Unknown;
+
+ case "ActorControl":
+ return TimelineType.ActorControl; //TODO: how to check that command.
+
+ case "InCombat":
+ return TimelineType.InCombat;
+
+ case "GameLog":
+ return TimelineType.GameLog;
+
+ case "Ability":
+ return TimelineType.Ability;
+
+ case "StartsUsing":
+ return TimelineType.StartsUsing;
+
+ case "SystemLogMessage":
+ return TimelineType.SystemLogMessage; //TODO: a6s for testing
+
+ default:
+ Svc.Log.Warning($"New timelinetype: {type}");
+ return TimelineType.Unknown;
+ }
+ }
+
+ public void UpdateRaidTimeOffset()
+ {
+ DataCenter.RaidTimeRaw = Time;
+ Svc.Log.Debug($"Reset the {nameof(DataCenter.RaidTimeRaw)} to {DataCenter.RaidTimeRaw}.");
+ }
public bool IsIdMatched(uint id)
{
@@ -16,8 +80,9 @@ public override string ToString()
{
return $"""
IsShown: {IsShown},
- Time: {time},
- Name: {name},
+ Time: {Time},
+ Name: {Name},
+ Type: {Type},
Ids: {string.Join(", ", ids)}
""";
}
diff --git a/RotationSolver.Basic/DataCenter.cs b/RotationSolver.Basic/DataCenter.cs
index 983b300f7..2ff63fd95 100644
--- a/RotationSolver.Basic/DataCenter.cs
+++ b/RotationSolver.Basic/DataCenter.cs
@@ -263,8 +263,19 @@ internal static SpecialCommandType SpecialType
public static bool NotInCombatDelay => _notInCombatDelay.Delay(!InCombat);
internal static float CombatTimeRaw { get; set; }
- internal static float RaidTimeOffset { get; set; } = 0;
- internal static float RaidTimeRaw => CombatTimeRaw + RaidTimeOffset;
+ private static DateTime _startRaidTime = DateTime.MinValue;
+ internal static float RaidTimeRaw
+ {
+ get
+ {
+ if (_startRaidTime == DateTime.MinValue) return 0;
+ return (float)(DateTime.Now - _startRaidTime).TotalSeconds;
+ }
+ set
+ {
+ _startRaidTime = DateTime.Now - TimeSpan.FromSeconds(value);
+ }
+ }
internal static TimelineItem[] TimelineItems { get; set; } = [];
@@ -423,4 +434,7 @@ internal static void AddDamageRec(float damageRatio)
internal static DateTime KnockbackFinished { get; set; } = DateTime.MinValue;
internal static DateTime KnockbackStart { get; set; } = DateTime.MinValue;
#endregion
+
+ internal static SortedList AuthorHashes { get; set; } = [];
+ internal static string[] ContributorsHash { get; set; } = [];
}
diff --git a/RotationSolver.Basic/Helpers/ObjectHelper.cs b/RotationSolver.Basic/Helpers/ObjectHelper.cs
index 0e121e32f..b30468bd6 100644
--- a/RotationSolver.Basic/Helpers/ObjectHelper.cs
+++ b/RotationSolver.Basic/Helpers/ObjectHelper.cs
@@ -9,6 +9,8 @@
using FFXIVClientStructs.FFXIV.Common.Component.BGCollision;
using Lumina.Excel.GeneratedSheets;
using RotationSolver.Basic.Configuration;
+using System.Security.Cryptography;
+using System.Text;
using System.Text.RegularExpressions;
namespace RotationSolver.Basic.Helpers;
@@ -87,17 +89,16 @@ internal static bool IsAttackable(this BattleChara battleChara)
if (names.Any(n => !string.IsNullOrEmpty(n) && new Regex(n).Match(battleChara.Name.TextValue).Success)) return false;
- //if (gameObject is PlayerCharacter p)
- //{
- // var hash = SocialUpdater.EncryptString(p);
-
- // //Don't attack authors!!
- // if (RotationUpdater.AuthorHashes.ContainsKey(hash)) return false;
+ if (battleChara is PlayerCharacter p)
+ {
+ var hash = EncryptString(p);
- // //Don't attack contributors!!
- // if (DownloadHelper.ContributorsHash.Contains(hash)) return false;
- //}
+ //Don't attack authors!!
+ if (DataCenter.AuthorHashes.ContainsKey(hash)) return false;
+ //Don't attack contributors!!
+ if (DataCenter.ContributorsHash.Contains(hash)) return false;
+ }
//Fate
if (DataCenter.TerritoryContentType != TerritoryContentType.Eureka)
@@ -132,6 +133,27 @@ internal static bool IsAttackable(this BattleChara battleChara)
};
}
+
+ internal static string EncryptString(this PlayerCharacter player)
+ {
+ if (player == null) return string.Empty;
+
+ try
+ {
+ byte[] inputByteArray = Encoding.UTF8.GetBytes(player.HomeWorld.GameData!.InternalName.ToString()
+ + " - " + player.Name.ToString() + "U6Wy.zCG");
+
+ var tmpHash = MD5.HashData(inputByteArray);
+ var retB = Convert.ToBase64String(tmpHash);
+ return retB;
+ }
+ catch (Exception ex)
+ {
+ Svc.Log.Warning(ex, "Failed to read the player's name and world.");
+ return string.Empty;
+ }
+ }
+
internal static unsafe bool IsInEnemiesList(this BattleChara battleChara)
{
var addons = Service.GetAddons();
diff --git a/RotationSolver.SourceGenerators/StaticCodeGenerator.cs b/RotationSolver.SourceGenerators/StaticCodeGenerator.cs
index f1a261659..a95a604b9 100644
--- a/RotationSolver.SourceGenerators/StaticCodeGenerator.cs
+++ b/RotationSolver.SourceGenerators/StaticCodeGenerator.cs
@@ -25,29 +25,29 @@ private static void Execute(SourceProductionContext context)
GenerateActionCate(context);
GenerateBaseRotation(context);
GenerateRotations(context);
- //GenerateOpCode(context);
+ GenerateOpCode(context);
}
- //private static void GenerateOpCode(SourceProductionContext context)
- //{
- // var code = $$"""
- // namespace RotationSolver.Basic.Data;
-
- // ///
- // /// The opcode
- // ///
- // public enum OpCode : ushort
- // {
- // ///
- // ///
- // ///
- // None = 0,
- // {{Properties.Resources.OpCode.Table()}}
- // }
- // """;
-
- // context.AddSource("OpCode.g.cs", code);
- //}
+ private static void GenerateOpCode(SourceProductionContext context)
+ {
+ var code = $$"""
+ namespace RotationSolver.Basic.Data;
+
+ ///
+ /// The opcode
+ ///
+ public enum OpCode : ushort
+ {
+ ///
+ ///
+ ///
+ None = 0,
+ {{Properties.Resources.OpCode.Table()}}
+ }
+ """;
+
+ context.AddSource("OpCode.g.cs", code);
+ }
private static void GenerateStatus(SourceProductionContext context)
{
diff --git a/RotationSolver/Commands/RSCommands_Actions.cs b/RotationSolver/Commands/RSCommands_Actions.cs
index e901d3b01..f99ed22ec 100644
--- a/RotationSolver/Commands/RSCommands_Actions.cs
+++ b/RotationSolver/Commands/RSCommands_Actions.cs
@@ -55,11 +55,11 @@ public static void DoAction()
&& act1.TargetInfo.IsSingleTarget
&& act1.Target?.Target is PlayerCharacter p && p != Player.Object)
{
- var hash = SocialUpdater.EncryptString(p);
+ var hash = p.EncryptString();
//Don't attack authors and contributors!!
- if ((RotationUpdater.AuthorHashes.ContainsKey(hash)
- || DownloadHelper.ContributorsHash.Contains(hash)))
+ if ((DataCenter.AuthorHashes.ContainsKey(hash)
+ || DataCenter.ContributorsHash.Contains(hash)))
{
Svc.Chat.PrintError($"Please don't attack RS developers with RS by {act1}!");
return;
diff --git a/RotationSolver/Helpers/DownloadHelper.cs b/RotationSolver/Helpers/DownloadHelper.cs
index 87f2b5c31..6a7dd1d63 100644
--- a/RotationSolver/Helpers/DownloadHelper.cs
+++ b/RotationSolver/Helpers/DownloadHelper.cs
@@ -10,7 +10,6 @@ public static class DownloadHelper
/// Maximum HP and MP are increased.
///
public static string[] LinkLibraries { get; private set; } = [];
- public static string[] ContributorsHash { get; private set; } = [];
public static string[] UsersHash { get; private set; } = [];
public static string[] Supporters { get; private set; } = [];
public static IncompatiblePlugin[] IncompatiblePlugins { get; private set; } = [];
@@ -20,7 +19,7 @@ public static async Task DownloadAsync()
LinkLibraries = await DownloadOneAsync($"https://raw.githubusercontent.com/{Service.USERNAME}/{Service.REPO}/main/Resources/downloadList.json") ?? [];
IncompatiblePlugins = await DownloadOneAsync($"https://raw.githubusercontent.com/{Service.USERNAME}/{Service.REPO}/main/Resources/IncompatiblePlugins.json") ?? [];
- ContributorsHash = await DownloadOneAsync($"https://raw.githubusercontent.com/{Service.USERNAME}/{Service.REPO}/main/Resources/ContributorsHash.json") ?? [];
+ DataCenter.ContributorsHash = await DownloadOneAsync($"https://raw.githubusercontent.com/{Service.USERNAME}/{Service.REPO}/main/Resources/ContributorsHash.json") ?? [];
UsersHash = await DownloadOneAsync($"https://raw.githubusercontent.com/{Service.USERNAME}/{Service.REPO}/main/Resources/UsersHash.json") ?? [];
diff --git a/RotationSolver/UI/RotationConfigWindow.cs b/RotationSolver/UI/RotationConfigWindow.cs
index ab3a1bd10..ff5d634eb 100644
--- a/RotationSolver/UI/RotationConfigWindow.cs
+++ b/RotationSolver/UI/RotationConfigWindow.cs
@@ -2432,8 +2432,8 @@ private static unsafe void DrawStatus()
}
private static unsafe void DrawParty()
{
- ImGui.Text("Party: " + DataCenter.PartyMembers.Count().ToString());
- ImGui.Text("Alliance: " + DataCenter.AllianceMembers.Count().ToString());
+ ImGui.Text("Party: " + DataCenter.PartyMembers.Length.ToString());
+ ImGui.Text("Alliance: " + DataCenter.AllianceMembers.Length.ToString());
ImGui.Text("PartyMembersAverHP: " + DataCenter.PartyMembersAverHP.ToString());
@@ -2495,7 +2495,7 @@ private static unsafe void DrawTargetData()
}
ImGui.Text("All: " + DataCenter.AllTargets.Count().ToString());
- ImGui.Text("Hostile: " + DataCenter.AllHostileTargets.Count().ToString());
+ ImGui.Text("Hostile: " + DataCenter.AllHostileTargets.Length.ToString());
foreach (var item in DataCenter.AllHostileTargets)
{
ImGui.Text(item.Name.ToString());
diff --git a/RotationSolver/UI/RotationConfigWindow_Config.cs b/RotationSolver/UI/RotationConfigWindow_Config.cs
index 8d3a4bbc4..109a6673b 100644
--- a/RotationSolver/UI/RotationConfigWindow_Config.cs
+++ b/RotationSolver/UI/RotationConfigWindow_Config.cs
@@ -220,7 +220,7 @@ private static void DrawBasicNamedConditions()
{
if (!DataCenter.RightSet.NamedConditions.Any(c => string.IsNullOrEmpty(c.Name)))
{
- DataCenter.RightSet.NamedConditions = DataCenter.RightSet.NamedConditions.Append((string.Empty, new ConditionSet())).ToArray();
+ DataCenter.RightSet.NamedConditions = [.. DataCenter.RightSet.NamedConditions, (string.Empty, new ConditionSet())];
}
ImGui.Spacing();
@@ -271,13 +271,13 @@ private static void DrawBasicOthers()
if (Service.Config.SayHelloToAll)
{
- var str = SocialUpdater.EncryptString(Player.Object);
+ var str = Player.Object.EncryptString();
ImGui.SetNextItemWidth(ImGui.CalcTextSize(str).X + 10);
ImGui.InputText("That is your HASH:", ref str, 100);
- if (!DownloadHelper.ContributorsHash.Contains(str)
+ if (!DataCenter.ContributorsHash.Contains(str)
&& !DownloadHelper.UsersHash.Contains(str)
- && !RotationUpdater.AuthorHashes.ContainsKey(str))
+ && !DataCenter.AuthorHashes.ContainsKey(str))
{
if (ImGui.Button("DM your Hash to ArchiTed for being greeted."))
{
diff --git a/RotationSolver/Updaters/ActionUpdater.cs b/RotationSolver/Updaters/ActionUpdater.cs
index d1cf5cc6f..a32e4f8e2 100644
--- a/RotationSolver/Updaters/ActionUpdater.cs
+++ b/RotationSolver/Updaters/ActionUpdater.cs
@@ -5,6 +5,8 @@
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using RotationSolver.Commands;
+using System.Text.RegularExpressions;
+using System.Windows.Forms;
namespace RotationSolver.Updaters;
@@ -143,10 +145,19 @@ private static void UpdateCombatTime()
if (_startCombatTime == DateTime.MinValue)
{
DataCenter.CombatTimeRaw = 0;
- DataCenter.RaidTimeOffset = 0;
}
else
{
+ if (DataCenter.CombatTimeRaw == 0)
+ {
+ foreach (var item in DataCenter.TimelineItems)
+ {
+ if (item.Type is not TimelineType.InCombat) continue;
+
+ item.UpdateRaidTimeOffset();
+ break;
+ }
+ }
DataCenter.CombatTimeRaw = (float)(DateTime.Now - _startCombatTime).TotalSeconds;
}
}
diff --git a/RotationSolver/Updaters/RaidTimeUpdater.cs b/RotationSolver/Updaters/RaidTimeUpdater.cs
index d698c77d7..bc5b18388 100644
--- a/RotationSolver/Updaters/RaidTimeUpdater.cs
+++ b/RotationSolver/Updaters/RaidTimeUpdater.cs
@@ -16,26 +16,25 @@ internal static void Disable()
Svc.ClientState.TerritoryChanged -= ClientState_TerritoryChanged;
}
- private static void ClientState_TerritoryChanged(ushort obj)
+ private static async void ClientState_TerritoryChanged(ushort obj)
{
try
{
- UpdateRaidTime("06-ew/raid/p9s.txt");
+ DataCenter.TimelineItems = await UpdateRaidTime("06-ew/raid/p9s.txt");
}
catch (Exception e)
{
Svc.Log.Warning(e, "Failed to update the raid timeline!");
}
}
- static async void UpdateRaidTime(string path)
+ static async Task UpdateRaidTime(string path)
{
using var client = new HttpClient();
var message = await client.GetAsync("https://raw.githubusercontent.com/OverlayPlugin/cactbot/main/ui/raidboss/data/" + path);
if (!message.IsSuccessStatusCode)
{
- DataCenter.TimelineItems = [];
- return;
+ return [];
}
var str = await message.Content.ReadAsStringAsync();
@@ -45,32 +44,51 @@ static async void UpdateRaidTime(string path)
{
try
{
- var header = TimeHeader().Match(timelineItem.Value);
- var action = JObject.Parse(ActionGetter().Match(timelineItem.Value).Value)["id"];
- var time = float.Parse(Time().Match(header.Value).Value);
- var name = Name().Match(header.Value).Value[1..^1];
+ var timeline = timelineItem.Value;
+ var header = TimeHeader().Match(timeline).Value;
+ var time = float.Parse(Time().Match(header).Value);
+ var name = Name().Match(header).Value[1..^1];
+
+ var item = JObject.Parse(ActionGetter().Match(timeline).Value);
string[] ids = [];
- if (action is JArray array)
- {
- ids = [..array.Select(i => i.ToString())];
- }
- else
+ if (item != null)
{
- ids = [action?.ToString() ?? string.Empty];
+ var id = item["id"];
+ if (id is JArray array)
+ {
+ ids = [.. array.Select(i => i.ToString())];
+ }
+ else
+ {
+ ids = [id?.ToString() ?? string.Empty];
+ }
}
- result.Add(new (time, name, ids));
+ var rest = timeline[header.Length..];
+ var type = Type().Match(rest)?.Value[1..^1] ?? string.Empty;
+
+ result.Add(new (time, name, type, ids, item));
}
catch (Exception ex)
{
Svc.Log.Warning(ex, "sth wrong with matching!");
}
}
- DataCenter.TimelineItems = [..result];
+
+#if DEBUG
+ foreach (var item in result)
+ {
+ Svc.Log.Debug(item.ToString());
+ }
+#endif
+ return [..result];
}
- [GeneratedRegex("\\d+\\.\\d.*Ability.*")]
+ [GeneratedRegex(" .*? ")]
+ private static partial Regex Type();
+
+ [GeneratedRegex("\\d+\\.\\d.*")]
private static partial Regex TimeLineItem();
[GeneratedRegex("^\\d+\\.\\d")]
diff --git a/RotationSolver/Updaters/RotationUpdater.cs b/RotationSolver/Updaters/RotationUpdater.cs
index 5b04457e8..60a10856f 100644
--- a/RotationSolver/Updaters/RotationUpdater.cs
+++ b/RotationSolver/Updaters/RotationUpdater.cs
@@ -14,7 +14,6 @@ internal static class RotationUpdater
internal record CustomRotationGroup(Job JobId, Job[] ClassJobIds, Type[] Rotations);
internal static SortedList CustomRotationsDict { get; private set; } = [];
- internal static SortedList AuthorHashes { get; private set; } = [];
internal static CustomRotationGroup[] CustomRotations { get; set; } = [];
internal static SortedList DutyRotations { get; set; } = [];
@@ -99,7 +98,7 @@ private static void LoadRotationsFromLocal(string relayFolder)
}
}
- AuthorHashes = [];
+ DataCenter.AuthorHashes = [];
foreach (var assembly in assemblies)
{
try
@@ -112,13 +111,13 @@ private static void LoadRotationsFromLocal(string relayFolder)
var value = $"{assembly.GetInfo().Author} - {assembly.GetInfo().Name}";
- if (AuthorHashes.ContainsKey(key))
+ if (DataCenter.AuthorHashes.ContainsKey(key))
{
- AuthorHashes[key] += $", {value}";
+ DataCenter.AuthorHashes[key] += $", {value}";
}
else
{
- AuthorHashes.Add(key, value);
+ DataCenter.AuthorHashes.Add(key, value);
}
}
}
diff --git a/RotationSolver/Updaters/SocialUpdater.cs b/RotationSolver/Updaters/SocialUpdater.cs
index a2a112c89..e724f9706 100644
--- a/RotationSolver/Updaters/SocialUpdater.cs
+++ b/RotationSolver/Updaters/SocialUpdater.cs
@@ -13,8 +13,6 @@
using RotationSolver.Helpers;
using RotationSolver.Localization;
using System.Diagnostics.CodeAnalysis;
-using System.Security.Cryptography;
-using System.Text;
namespace RotationSolver.Updaters;
@@ -168,21 +166,21 @@ private static async void SayHelloToUsers()
#else
.Where(c => c.ObjectId != Player.Object.ObjectId)
#endif
- .Select(player => (player, EncryptString(player)))
+ .Select(player => (player, player.EncryptString()))
.Where(pair => !saidAuthors.Contains(pair.Item2)
&& !OtherConfiguration.RotationSolverRecord.SaidUsers.Contains(pair.Item2));
IEnumerable entities = players
.Select(c =>
{
- if (!RotationUpdater.AuthorHashes.TryGetValue(c.Item2, out var nameDesc)) nameDesc = string.Empty;
+ if (!DataCenter.AuthorHashes.TryGetValue(c.Item2, out var nameDesc)) nameDesc = string.Empty;
return (c.player, nameDesc);
})
.Where(p => !string.IsNullOrEmpty(p.nameDesc))
.Select(p => new RotationAuthorChatEntity(p.player, p.nameDesc));
entities = entities.Union(players
- .Where(p => DownloadHelper.ContributorsHash.Contains(p.Item2))
+ .Where(p => DataCenter.ContributorsHash.Contains(p.Item2))
.Select(p => new ContributorChatEntity(p.player)), _comparer);
if (Service.Config.SayHelloToUsers)
@@ -218,25 +216,6 @@ private static async void SayHelloToUsers()
}
}
- internal static string EncryptString(PlayerCharacter player)
- {
- if (player == null) return string.Empty;
-
- try
- {
- byte[] inputByteArray = Encoding.UTF8.GetBytes(player.HomeWorld.GameData!.InternalName.ToString()
- + " - " + player.Name.ToString() + "U6Wy.zCG");
-
- var tmpHash = MD5.HashData(inputByteArray);
- var retB = Convert.ToBase64String(tmpHash);
- return retB;
- }
- catch (Exception ex)
- {
- Svc.Log.Warning(ex, "Failed to read the player's name and world.");
- return string.Empty;
- }
- }
internal abstract class ChatEntity(PlayerCharacter character) : IDisposable
{
@@ -276,7 +255,7 @@ public bool CanTarget
public void Dispose()
{
OtherConfiguration.RotationSolverRecord.SayingHelloCount++;
- var hash = EncryptString(player);
+ var hash = player.EncryptString();
saidAuthors.Add(hash);
if (Service.Config.JustSayHelloOnce)
{
diff --git a/RotationSolver/Updaters/StateUpdater.cs b/RotationSolver/Updaters/StateUpdater.cs
index f3eea3fa0..81c6cb9d6 100644
--- a/RotationSolver/Updaters/StateUpdater.cs
+++ b/RotationSolver/Updaters/StateUpdater.cs
@@ -69,7 +69,7 @@ private static AutoStatus StatusFromAutomatic()
var canHealAreaAbility = singleAbility > 2;
var canHealAreaSpell = singleSpell > 2;
- if (DataCenter.PartyMembers.Count() > 2)
+ if (DataCenter.PartyMembers.Length > 2)
{
//TODO: Beneficial area status.
var ratio = GetHealingOfTimeRatio(Player.Object, StatusHelper.AreaHots);
diff --git a/RotationSolver/Updaters/TargetUpdater.cs b/RotationSolver/Updaters/TargetUpdater.cs
index 873cbf036..314d90d4b 100644
--- a/RotationSolver/Updaters/TargetUpdater.cs
+++ b/RotationSolver/Updaters/TargetUpdater.cs
@@ -16,13 +16,41 @@ static readonly ObjectListDelay
_raiseAllTargets = new(() => Service.Config.RaiseDelay);
internal unsafe static void UpdateTarget()
{
- DataCenter.AllTargets.Delay(Svc.Objects.GetObjectInRadius(30).OfType());
+ var battles = Svc.Objects.OfType();
+
+ DataCenter.AllTargets.Delay(battles.GetObjectInRadius(30));
UpdateHostileTargets(DataCenter.AllTargets);
UpdateFriends(DataCenter.AllTargets
.Where(b => b.Character()->CharacterData.OnlineStatus != 15 //Removed the one watching cutscene.
&& b.IsTargetable //Removed the one can't target.
));
- UpdateNamePlate(Svc.Objects.OfType());
+ UpdateNamePlate(battles);
+ }
+
+ static readonly Dictionary _castingTargets = [];
+ private static void UpdateCastingRefine(IEnumerable allTargets)
+ {
+ foreach (BattleChara b in allTargets)
+ {
+ if (!_castingTargets.TryGetValue(b.ObjectId, out var isLastCasting)) continue;
+ if (isLastCasting) continue;
+
+ if (!b.IsCasting) continue;
+
+ foreach (var item in DataCenter.TimelineItems)
+ {
+ if (item.Type is not TimelineType.StartsUsing) continue;
+ if (!item.IsIdMatched(b.CastActionId)) continue;
+
+ item.UpdateRaidTimeOffset();
+ break;
+ }
+ }
+
+ foreach (BattleChara b in allTargets)
+ {
+ _castingTargets[b.ObjectId] = b.IsCasting;
+ }
}
private static DateTime _lastUpdateTimeToKill = DateTime.MinValue;
@@ -96,13 +124,13 @@ private unsafe static void UpdateHostileTargets(IEnumerable allTarg
if (b is PlayerCharacter p)
{
- var hash = SocialUpdater.EncryptString(p);
+ var hash = p.EncryptString();
//Don't attack authors!!
- if (RotationUpdater.AuthorHashes.ContainsKey(hash)) return false;
+ if (DataCenter.AuthorHashes.ContainsKey(hash)) return false;
//Don't attack contributors!!
- if (DownloadHelper.ContributorsHash.Contains(hash)) return false;
+ if (DataCenter.ContributorsHash.Contains(hash)) return false;
}
return true;
}).ToArray();
diff --git a/RotationSolver/Watcher.cs b/RotationSolver/Watcher.cs
index 2d1359adb..4ec1086d1 100644
--- a/RotationSolver/Watcher.cs
+++ b/RotationSolver/Watcher.cs
@@ -8,6 +8,7 @@
using FFXIVClientStructs.FFXIV.Client.Game;
using Lumina.Excel.GeneratedSheets;
using RotationSolver.Basic.Configuration;
+using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject;
@@ -63,20 +64,61 @@ public static void Enable()
#endif
});
- //Svc.GameNetwork.NetworkMessage += GameNetwork_NetworkMessage;
+ Svc.GameNetwork.NetworkMessage += GameNetwork_NetworkMessage;
+ Svc.Chat.ChatMessage += Chat_ChatMessage;
}
-// private static void GameNetwork_NetworkMessage(nint dataPtr, ushort opCode, uint sourceActorId, uint targetActorId, Dalamud.Game.Network.NetworkMessageDirection direction)
-// {
-// if (direction != Dalamud.Game.Network.NetworkMessageDirection.ZoneDown) return;
-// OpCode op = (OpCode)opCode;
+ private static void Chat_ChatMessage(Dalamud.Game.Text.XivChatType type, uint senderId, ref Dalamud.Game.Text.SeStringHandling.SeString sender, ref Dalamud.Game.Text.SeStringHandling.SeString message, ref bool isHandled)
+ {
+ foreach (var item in DataCenter.TimelineItems)
+ {
+ if (item.Type is not TimelineType.GameLog) continue;
+
+ var typeString = ((uint)type).ToString("X4");
+ if (!new Regex(item["code"]).IsMatch(typeString)) continue;
+
+ //TODO: multi language.
+ if (!new Regex(item["line"]).IsMatch(message.TextValue)) continue;
+ item.UpdateRaidTimeOffset();
+ break;
+ }
+ }
+
+ private static void GameNetwork_NetworkMessage(nint dataPtr, ushort opCode, uint sourceActorId, uint targetActorId, Dalamud.Game.Network.NetworkMessageDirection direction)
+ {
+ if (direction != Dalamud.Game.Network.NetworkMessageDirection.ZoneDown) return;
+ OpCode op = (OpCode)opCode;
+
+ switch (op)
+ {
+ case OpCode.SystemLogMessage:
+ OnSystemLogMessage(dataPtr);
+ break;
+ }
//#if DEBUG
// var source = Svc.Objects.SearchById(sourceActorId)?.Name.TextValue;
// var target = Svc.Objects.SearchById(targetActorId)?.Name.TextValue;
// Svc.Log.Debug($"From {source} to {target} by {op}.");
//#endif
-// }
+ }
+
+ private static void OnSystemLogMessage(IntPtr dataPtr)
+ {
+ var bytes = new byte[32];
+ Marshal.Copy(dataPtr, bytes, 0, 32);
+ Svc.Log.Debug("SystemLogMessage: " + Convert.ToHexString(bytes));
+
+ //TODO: the refine to the raidtime.
+
+ //if (logId == DataIds.SystemLogPomanderUsed)
+ // OnPomanderUsed((Pomander)Marshal.ReadByte(dataPtr, 16));
+ //else if (logId == DataIds.SystemLogDutyEnded)
+ // ExitDeepDungeon();
+ //else if (logId == DataIds.SystemLogTransferenceInitiated)
+ // nextFloorTransfer = true;
+
+ }
public static void Disable()
{
@@ -88,7 +130,8 @@ public static void Disable()
MapEffect.Dispose();
ActionEffect.ActionEffectEvent -= ActionFromEnemy;
ActionEffect.ActionEffectEvent -= ActionFromSelf;
- //Svc.GameNetwork.NetworkMessage -= GameNetwork_NetworkMessage;
+ Svc.GameNetwork.NetworkMessage -= GameNetwork_NetworkMessage;
+ Svc.Chat.ChatMessage -= Chat_ChatMessage;
}
private static IntPtr ActorVfxNewHandler(string path, IntPtr a2, IntPtr a3, float a4, char a5, ushort a6, char a7)
@@ -170,10 +213,10 @@ private static void ActionFromEnemy(ActionEffectSet set)
{
foreach (var item in DataCenter.TimelineItems)
{
+ if (item.Type is not TimelineType.Ability) continue;
if (!item.IsIdMatched(set.Action?.RowId ?? 0)) continue;
- DataCenter.RaidTimeOffset = item.Time - DataCenter.CombatTimeRaw;
- Svc.Log.Debug($"Reset the {nameof(DataCenter.RaidTimeOffset)} to {DataCenter.RaidTimeOffset} by the action {set.Action}");
+ item.UpdateRaidTimeOffset();
break;
}
@@ -213,7 +256,7 @@ private static void ActionFromEnemy(ActionEffectSet set)
}
}
- if (set.Header.ActionType == ActionType.Action && DataCenter.PartyMembers.Length >= 4 && set.Action.Cast100ms > 0)
+ if (set.Header.ActionType == ActionType.Action && DataCenter.PartyMembers.Length >= 4 && set.Action?.Cast100ms > 0)
{
var type = set.Action.GetActionCate();