diff --git a/src/Dawntrail/EX2 弑君之怒行 接线死刑.cs b/src/Dawntrail/EX2 弑君之怒行 接线死刑.cs new file mode 100644 index 0000000..4c88666 --- /dev/null +++ b/src/Dawntrail/EX2 弑君之怒行 接线死刑.cs @@ -0,0 +1,208 @@ +using Dalamud.Game.ClientState.Objects.SubKinds; +using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Interface; +using Dalamud.Interface.Colors; +using ECommons; +using ECommons.Configuration; +using ECommons.DalamudServices; +using ECommons.ExcelServices.TerritoryEnumeration; +using ECommons.GameFunctions; +using ECommons.Hooks.ActionEffectTypes; +using ECommons.ImGuiMethods; +using ECommons.Logging; +using ECommons.MathHelpers; +using ImGuiNET; +using Lumina.Excel.GeneratedSheets; +using Splatoon.SplatoonScripting; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Numerics; +using System.Text; +using System.Threading.Tasks; + +namespace SplatoonScriptsOfficial.Duties.Dawntrail +{ + public class EX2_弑君之怒行_接线死刑_For_CN_Client : SplatoonScript + { + // EX2_Regicidal_Rage + // 限制副本地图:1201 永护塔顶层 + public override HashSet? ValidTerritories { get; } = [1201]; + + public override Metadata? Metadata => new(1, "RedOrange"); + HashSet TetheredPlayers = new(); + + public override void OnSetup() + { + Controller.RegisterElement("TetherAOE1", new(1) { thicc = Conf.TetherAOEThick, color = Conf.TetherAOECol.ToUint(), refActorComparisonType = 2, onlyTargetable = true, Filled = false, Enabled = false, radius = 8f}); + Controller.RegisterElement("TetherAOE2", new(1) { thicc = Conf.TetherAOEThick, color = Conf.TetherAOECol.ToUint(), refActorComparisonType = 2, onlyTargetable = true, Filled = false, Enabled = false, radius = 8f}); + Controller.RegisterElement("Tether1", new(2) { thicc = Conf.TetherThick, radius = 0f }); + Controller.RegisterElement("Tether2", new(2) { thicc = Conf.TetherThick, radius = 0f }); + Controller.RegisterElement("hint1", new(1) { thicc = 0f, radius = 0f, refActorComparisonType = 2, overlayPlaceholders = true, overlayBGColor = 4294911232, overlayTextColor = 4294967295, overlayVOffset = 1f}); + Controller.RegisterElement("hint2", new(1) { thicc = 0f, radius = 0f, refActorComparisonType = 2, overlayPlaceholders = true, overlayBGColor = 4294911232, overlayTextColor = 4294967295, overlayVOffset = 1f}); + } + + public override void OnTetherCreate(uint source, uint target, uint data2, uint data3, uint data5) + { + if (IsZoraalja(target, out _)) + { + //DuoLog.Information($"Tether: {data2}, {data3}, {data5}"); + TetheredPlayers.Add(source); + //UpdateTethers(); + } + } + + public override void OnTetherRemoval(uint source, uint data2, uint data3, uint data5) + { + //DuoLog.Information($"Tether rem: {data2}, {data3}, {data5}"); + TetheredPlayers.Remove(source); + //UpdateTethers(); + } + + public override void OnUpdate() + { + UpdateTethers(); + } + + bool IsZoraalja(uint oid, [NotNullWhen(true)] out IBattleChara? zoraalja) + { + if (oid.TryGetObject(out var obj) && obj is IBattleChara o && o.NameId == 12882) + { + zoraalja = o; + return true; + } + zoraalja = null; + return false; + } + + void Reset() + { + TetheredPlayers.Clear(); + } + + IBattleChara? GetZoraalja() + { + return Svc.Objects.FirstOrDefault(x => x is IBattleChara o && o.NameId == 12882 && o.IsTargetable()) as IBattleChara; + } + + void UpdateTethers() + { + if (TetheredPlayers.Count == 2) + { + var tetheredPlayers = TetheredPlayers.ToArray(); + var zoraalja = GetZoraalja(); + { + if (Controller.TryGetElementByName("Tether1", out var e)) + { + e.Enabled = true; + e.SetRefPosition(zoraalja.Position); + e.SetOffPosition(tetheredPlayers[0].GetObject().Position); + var correct = (tetheredPlayers[0].GetObject() as IPlayerCharacter).GetRole() == CombatRole.Tank; + e.color = (correct ? Conf.ValidTetherColor : GradientColor.Get(Conf.InvalidTetherColor1, Conf.InvalidTetherColor2, 500)).ToUint(); + } + } + { + if (Controller.TryGetElementByName("TetherAOE1", out var e)) + { + e.Enabled = true; + e.refActorObjectID = tetheredPlayers[0]; + e.color = Conf.TetherAOECol.ToUint(); + var correct = (tetheredPlayers[0].GetObject() as IPlayerCharacter).GetRole() == CombatRole.Tank; + } + } + { + if (Controller.TryGetElementByName("hint1", out var e)) + { + e.refActorObjectID = tetheredPlayers[0]; + var correct = (tetheredPlayers[0].GetObject() as IPlayerCharacter).GetRole() == CombatRole.Tank; + e.overlayText = (correct ? "死刑 → $NAME" : "坦克接线"); + if (Conf.UseOverlayText) + { + e.Enabled = true; + } + else + { + e.Enabled = false; + } + } + } + { + if (Controller.TryGetElementByName("Tether2", out var e)) + { + e.Enabled = true; + e.SetRefPosition(zoraalja.Position); + e.SetOffPosition(tetheredPlayers[1].GetObject().Position); + var correct = (tetheredPlayers[1].GetObject() as IPlayerCharacter).GetRole() == CombatRole.Tank; + e.color = (correct ? Conf.ValidTetherColor : GradientColor.Get(Conf.InvalidTetherColor1, Conf.InvalidTetherColor2, 500)).ToUint(); + } + } + { + if (Controller.TryGetElementByName("TetherAOE2", out var e)) + { + e.Enabled = true; + e.refActorObjectID = tetheredPlayers[1]; + e.color = Conf.TetherAOECol.ToUint(); + var correct = (tetheredPlayers[1].GetObject() as IPlayerCharacter).GetRole() == CombatRole.Tank; + } + } + { + if (Controller.TryGetElementByName("hint2", out var e)) + { + e.refActorObjectID = tetheredPlayers[1]; + var correct = (tetheredPlayers[1].GetObject() as IPlayerCharacter).GetRole() == CombatRole.Tank; + e.overlayText = (correct ? "死刑 → $NAME" : "坦克接线"); + if (Conf.UseOverlayText) + { + e.Enabled = true; + } + else + { + e.Enabled = false; + } + } + } + } + else + { + Controller.GetRegisteredElements().Each(x => x.Value.Enabled = false); + } + } + + Config Conf => Controller.GetConfig(); + public override void OnSettingsDraw() + { + ImGui.Checkbox("启用 字幕提醒",ref Conf.UseOverlayText); + ImGui.ColorEdit4("死刑范围 颜色", ref Conf.TetherAOECol, ImGuiColorEditFlags.NoInputs); + ImGui.SetNextItemWidth(100f); + ImGui.ColorEdit4("Tank连线时 颜色", ref Conf.ValidTetherColor, ImGuiColorEditFlags.NoInputs); + ImGui.ColorEdit4("##Invalid1", ref Conf.InvalidTetherColor1, ImGuiColorEditFlags.NoInputs); + ImGui.SameLine(); + ImGui.ColorEdit4("非Tank连线时 颜色(渐变)", ref Conf.InvalidTetherColor2, ImGuiColorEditFlags.NoInputs); + ImGui.SetNextItemWidth(100f); + ImGui.SliderFloat("死刑范围 线条厚度", ref Conf.TetherAOEThick, 1f, 10f); + ImGui.SetNextItemWidth(100f); + ImGui.SliderFloat("连线线条厚度", ref Conf.TetherThick, 1f, 10f); + if (ImGui.CollapsingHeader("说明")) + { + ImGuiEx.Text($"1. 按住 Ctrl 键点击滑动条可以输入数字"); + ImGuiEx.Text($"2. 连线逻辑为: 连线坦克时为黄色,连线其他职业时为黑蓝渐变,您可以自行更改"); + ImGuiEx.Text($"3. 此脚本基于 UCOB Tethers 修改,可作为连线机制参考,所有的成果归于 NightmareXIV"); + } + if (ImGui.CollapsingHeader("Debug 调试")) + { + ImGuiEx.Text($"连线:\n{TetheredPlayers.Select(x => x.GetObject()?.ToString() ?? $"unk{x}").Join("\n")}"); + } + } + public class Config : IEzConfig + { + public bool UseOverlayText = true; + public Vector4 TetherAOECol = 0xE6FF00FF.SwapBytes().ToVector4(); + public Vector4 ValidTetherColor = 0xD9FF00FF.SwapBytes().ToVector4(); + public Vector4 InvalidTetherColor1 = 0xFFFFFFFF.SwapBytes().ToVector4(); + public Vector4 InvalidTetherColor2 = new(0, 0, 0, 255); + public float TetherAOEThick = 3f; + public float TetherThick = 5f; + } + } +} diff --git a/src/Dawntrail/EX2 情感投射 利刃.cs b/src/Dawntrail/EX2 情感投射 利刃.cs new file mode 100644 index 0000000..e790cfa --- /dev/null +++ b/src/Dawntrail/EX2 情感投射 利刃.cs @@ -0,0 +1,213 @@ +using Dalamud.Game.ClientState.Objects.Types; +using ECommons; +using ECommons.Configuration; +using ECommons.DalamudServices; +using ECommons.Hooks; +using ECommons.ImGuiMethods; +using ECommons.MathHelpers; +using ImGuiNET; +using Splatoon; +using Splatoon.SplatoonScripting; +using Splatoon.Utility; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Text; +using System.Threading.Tasks; + +namespace SplatoonScriptsOfficial.Duties.Dawntrail; +public class EX2_情感投射_利刃_For_CN_Client : SplatoonScript +{ + // Projection_of_Triumph + public override HashSet? ValidTerritories { get; } = [1201]; + + public override Metadata? Metadata => new(3, "NightmareXIV, RedOrange 修改"); + + private IBattleChara[] Donuts => [.. Svc.Objects.OfType().Where(x => x.DataId == 16727)]; + + private IBattleChara[] Circles => [.. Svc.Objects.OfType().Where(x => x.DataId == 16726)]; + + private IBattleChara[] Towers => [.. Svc.Objects.OfType().Where(x => x.DataId == 17079)]; + + private List RightMovers = []; + private List LeftMovers = []; + private int? RotateModifier = null; + + public override void OnSetup() + { + Controller.RegisterElement("Donut", new(0) + { + color = Conf.DCol.ToUint(), + overrideFillColor = true, + originFillColor = Conf.DColO.ToUint(), + endFillColor = Conf.DColE.ToUint(), + thicc = Conf.DThick, + refX = 100, + refY = 100, + }); + Controller.RegisterElement("Circle", new(0) + { + color = Conf.CCol.ToUint(), + overrideFillColor = true, + originFillColor = Conf.CColO.ToUint(), + endFillColor = Conf.CColE.ToUint(), + thicc = Conf.CThick, + refX = 100, + refY = 100, + }); + } + + //northwest is virtual north + public override void OnUpdate() + { + Controller.GetRegisteredElements().Values.Each(x => x.Enabled = false); + if (Towers.Length == 0) + { + RotateModifier = null; + LeftMovers.Clear(); + RightMovers.Clear(); + return; + } + if (Donuts.Length + Circles.Length >= 2) + { + CalculateModifier(); + } + if (RotateModifier == null) return; + foreach (var x in Donuts.Concat(Circles)) + { + var xPos = RotateRelative(x.Position); + if (xPos.Z > 120f) LeftMovers.Add(x.EntityId); + if (xPos.Z < 80f) RightMovers.Add(x.EntityId); + } + for (var i = 0; i < Towers.Length; i++) + { + var t = Towers[i]; + var name = $"Tower{i}"; + if (!Controller.TryGetElementByName(name, out _)) Controller.RegisterElement(name, new(0)); + var element = Controller.GetElementByName(name); + var originalPos = t.Position; + var rotatedPos = RotateRelative(originalPos); + element.SetRefPosition(originalPos); + //element.overlayText = $"Original: {originalPos}\nRotated:{rotatedPos}"; + element.overlayVOffset = 2; + foreach (var x in Donuts.Concat(Circles)) + { + var xPos = RotateRelative(x.Position); + var isDonut = x.DataId == 16727; + if (!xPos.Z.InRange(80, 120)) continue; + if (LeftMovers.Contains(x.EntityId)) + { + var distance = xPos.Z - rotatedPos.Z; + if (distance.InRange(0, 8)) + { + Assign(element, isDonut, distance); + //element.overlayText += $"\n{(isDonut ? "Donut" : "Circle")}/{xPos}"; + } + } + if (RightMovers.Contains(x.EntityId)) + { + var distance = rotatedPos.Z - xPos.Z; + if (distance.InRange(0, 8)) + { + Assign(element, isDonut, distance); + //element.overlayText += $"\n{(isDonut ? "Donut" : "Circle")}/{xPos}"; + } + } + } + } + } + + void CalculateModifier() + { + if (Donuts.Concat(Circles).Any(x => Vector3.Distance(x.Position, new(85, 0, 115)) < 3)) + { + RotateModifier = -45; + } + else if (Donuts.Concat(Circles).Any(x => Vector3.Distance(x.Position, new(85, 0, 85)) < 3)) + { + RotateModifier = 45; + } + } + + void Assign(Element e, bool isDonut, float distance) + { + e.Enabled = true; + var percent = Math.Clamp(1f - distance / 8f, 0f, 1f); + if (isDonut) + { + e.Filled = false; + e.Donut = 5f; + e.radius = 3f; + e.SetDisplayStyle(Controller.GetElementByName("Donut").GetDisplayStyle()); + e.color = Conf.DCol.ToUint(); + e.overrideFillColor = true; + e.originFillColor = Conf.DColO.ToUint(); + e.endFillColor = Conf.DColE.ToUint(); + e.thicc = Conf.DThick; + } + else + { + e.Filled = true; + e.Donut = 0f; + e.radius = 4f; + e.SetDisplayStyle(Controller.GetElementByName("Circle").GetDisplayStyle()); + e.color = Conf.CCol.ToUint(); + e.overrideFillColor = true; + e.originFillColor = Conf.CColO.ToUint(); + e.endFillColor = Conf.CColE.ToUint(); + e.thicc = Conf.CThick; + } + } + + private Vector3 RotateRelative(Vector3 s) + { + var swapped = new Vector3(s.X, s.Z, s.Y); + var rotated = Utils.RotatePoint(100, 100, this.RotateModifier.Value.DegreesToRadians(), swapped); + return new(rotated.X, rotated.Z, rotated.Y); + } + + public override void OnSettingsDraw() + { + ImGui.ColorEdit4("圆形 颜色", ref Conf.CCol, ImGuiColorEditFlags.NoInputs); + ImGui.ColorEdit4("##CColO", ref Conf.CColO, ImGuiColorEditFlags.NoInputs); + ImGui.SameLine(); + ImGui.ColorEdit4("圆形填充 颜色", ref Conf.CColE, ImGuiColorEditFlags.NoInputs); + ImGui.ColorEdit4("月环 颜色", ref Conf.DCol, ImGuiColorEditFlags.NoInputs); + ImGui.ColorEdit4("##DColO", ref Conf.DColO, ImGuiColorEditFlags.NoInputs); + ImGui.SameLine(); + ImGui.ColorEdit4("月环填充 颜色", ref Conf.DColE, ImGuiColorEditFlags.NoInputs); + ImGui.SetNextItemWidth(100f); + ImGui.SliderFloat("圆形 线条厚度", ref Conf.CThick, 1f, 10f); + ImGui.SetNextItemWidth(100f); + ImGui.SliderFloat("月环 线条厚度", ref Conf.DThick, 1f, 10f); + if (ImGui.CollapsingHeader("说明")) + { + ImGuiEx.Text("1. 按住 Ctrl 键点击滑动条可以输入数字"); + ImGuiEx.Text("2. 如果您不需要渐变填充,请将渐变色设置为两个相同的颜色"); + ImGuiEx.Text("3. 仅修改,所有的成果归于 NightmareXIV"); + } + if (ImGui.CollapsingHeader("Debug 调试")) + { + ImGuiEx.Text($""" + Towers: {Towers.Print()} + Circles: {Circles.Print()} + Donuts: {Donuts.Print()} + RotateModifier: {RotateModifier} + """); + } + } + Config Conf => Controller.GetConfig(); + + public class Config : IEzConfig + { + public Vector4 CCol = 0xFFFF00C8.SwapBytes().ToVector4(); + public Vector4 DCol = 0xFFFF00C8.SwapBytes().ToVector4(); + public Vector4 CColO = 0xFFFF0023.SwapBytes().ToVector4(); + public Vector4 CColE = 0xFFFF0023.SwapBytes().ToVector4(); + public Vector4 DColO = 0xFFFF0023.SwapBytes().ToVector4(); + public Vector4 DColE = 0xFFFF0023.SwapBytes().ToVector4(); + public float CThick = 2f; + public float DThick = 2f; + } +} \ No newline at end of file