Skip to content

Commit 2755f76

Browse files
authored
Clone 1 navigation script (#364)
* add: clones 1 navigation * fix: position * fix: state transition guard
1 parent ed84809 commit 2755f76

File tree

1 file changed

+328
-0
lines changed

1 file changed

+328
-0
lines changed
Lines changed: 328 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,328 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using System.Numerics;
4+
using Dalamud.Bindings.ImGui;
5+
using Dalamud.Game.ClientState.Objects.SubKinds;
6+
using Dalamud.Game.ClientState.Objects.Types;
7+
using Dalamud.Interface.Components;
8+
using ECommons;
9+
using ECommons.Configuration;
10+
using ECommons.DalamudServices;
11+
using ECommons.ImGuiMethods;
12+
using ECommons.MathHelpers;
13+
using Splatoon.Memory;
14+
using Splatoon.SplatoonScripting;
15+
using static Splatoon.Splatoon;
16+
17+
namespace SplatoonScriptsOfficial.Duties.Dawntrail;
18+
19+
public unsafe class M12S_P2_Clones_1_Navigation : SplatoonScript
20+
{
21+
public enum Corner
22+
{
23+
NE,
24+
SE,
25+
SW,
26+
NW
27+
}
28+
29+
public enum RoleSlot
30+
{
31+
MeleeLeft,
32+
MeleeRight,
33+
RangeLeft,
34+
RangeRight
35+
}
36+
37+
private const uint DebuffId = 3323;
38+
private const float PosDetectRadius = 3.5f;
39+
private const uint DarknessCast = 46303;
40+
private const int DebugElementCount = 8;
41+
42+
private static readonly Vector3 PosNE = new(108f, 0f, 92f);
43+
private static readonly Vector3 PosSE = new(108f, 0f, 108f);
44+
private static readonly Vector3 PosSW = new(92f, 0f, 108f);
45+
private static readonly Vector3 PosNW = new(92f, 0f, 92f);
46+
47+
private static readonly Vector3 NavMeleeLeft = new(102.5f, 0f, 93.2f);
48+
private static readonly Vector3 NavMeleeRight = new(93.5f, 0f, 101.8f);
49+
private static readonly Vector3 NavRangeLeft = new(113.5f, 0f, 99f);
50+
private static readonly Vector3 NavRangeRight = new(99f, 0f, 114f);
51+
private static readonly Vector3 NavMeleeDebuff = new(101f, 0f, 101.5f);
52+
private static readonly Vector3 NavRangeDebuff = new(86.5f, 0f, 100.5f);
53+
private static readonly RoleSlot[] DebugRoleOrder =
54+
[
55+
RoleSlot.MeleeLeft,
56+
RoleSlot.MeleeRight,
57+
RoleSlot.RangeLeft,
58+
RoleSlot.RangeRight
59+
];
60+
61+
private Phase CurrentPhase = Phase.Idle;
62+
private List<uint> DarknessClones = [];
63+
private Corner? DetectedNorth;
64+
private uint MasterDarknessClone;
65+
public override Metadata Metadata { get; } = new(2, "Garume");
66+
public override HashSet<uint>? ValidTerritories { get; } = [1327];
67+
68+
private Config C => Controller.GetConfig<Config>();
69+
70+
public override void OnSetup()
71+
{
72+
Controller.RegisterElementFromCode("Nav",
73+
"""{"Name":"","refX":100.0,"refY":100.0,"radius":0.7,"color":3357671168,"Filled":false,"fillIntensity":0.5,"thicc":9.0,"tether":true}""");
74+
for (var i = 0; i < DebugElementCount; i++)
75+
{
76+
Controller.RegisterElementFromCode($"Debug{i}",
77+
"""{"Name":"","radius":0.7,"Filled":false,"fillIntensity":0.5,"thicc":9.0}""");
78+
if (Controller.TryGetElementByName($"Debug{i}", out var e))
79+
e.color = EColor.RedBright.ToUint();
80+
}
81+
}
82+
83+
public override void OnReset()
84+
{
85+
CurrentPhase = Phase.Idle;
86+
DetectedNorth = null;
87+
MasterDarknessClone = 0;
88+
DarknessClones.Clear();
89+
}
90+
91+
public override void OnStartingCast(uint sourceId, PacketActorCast* packet)
92+
{
93+
if (packet->ActionID == 46296 && CurrentPhase == Phase.Idle) CurrentPhase = Phase.WaitForDebuff;
94+
95+
if (packet->ActionID == 46368) CurrentPhase = Phase.End;
96+
}
97+
98+
public override void OnSettingsDraw()
99+
{
100+
ImGui.SetNextItemWidth(200f);
101+
ImGuiEx.EnumCombo("Wait position", ref C.WaitSpot);
102+
ImGui.SetNextItemWidth(200f);
103+
ImGuiEx.EnumCombo("Role position", ref C.RoleSlot);
104+
105+
ImGui.Text("Bait Color:");
106+
ImGuiComponents.HelpMarker(
107+
"Change the color of the bait and the text that will be displayed on your bait.\nSetting different values makes it rainbow.");
108+
ImGui.Indent();
109+
ImGui.ColorEdit4("Color 1", ref C.BaitColor1, ImGuiColorEditFlags.NoInputs);
110+
ImGui.SameLine();
111+
ImGui.ColorEdit4("Color 2", ref C.BaitColor2, ImGuiColorEditFlags.NoInputs);
112+
ImGui.Unindent();
113+
ImGui.Checkbox("Show Debug", ref C.ShowDebug);
114+
115+
if (ImGui.CollapsingHeader("Debug"))
116+
{
117+
ImGui.Text($"Current Phase: {CurrentPhase}");
118+
ImGui.Text($"Detected North: {DetectedNorth}");
119+
ImGui.Text($"Darkness Clones Count: {DarknessClones.Count}");
120+
ImGui.Text($"Master Darkness Clone ID: {MasterDarknessClone}");
121+
}
122+
}
123+
124+
public override void OnUpdate()
125+
{
126+
Controller.Hide();
127+
UpdateDarkClones();
128+
UpdateNavigation();
129+
UpdateDebug();
130+
}
131+
132+
private void UpdateDarkClones()
133+
{
134+
foreach (var x in Svc.Objects.OfType<IBattleNpc>())
135+
if (DarknessClones.Count != 2 && x.IsCasting && x.CastActionId == DarknessCast)
136+
MasterDarknessClone = x.EntityId;
137+
138+
if (DarknessClones.Count < 2 && MasterDarknessClone != 0 &&
139+
MasterDarknessClone.TryGetBattleNpc(out var darkness))
140+
DarknessClones = Svc.Objects.OfType<IBattleNpc>()
141+
.Where(x => x.DataId == 19204 &&
142+
Vector3.Distance(darkness.Position, x.Position).ApproximatelyEquals(5f, 0.1f))
143+
.Select(x => x.EntityId)
144+
.ToList();
145+
}
146+
147+
private void UpdateNavigation()
148+
{
149+
var e = Controller.GetElementByName("Nav");
150+
e.color = GradientColor.Get(C.BaitColor1, C.BaitColor2).ToUint();
151+
switch (CurrentPhase)
152+
{
153+
case Phase.WaitForDebuff:
154+
if (TryGetDarkDebuffHolder(out _))
155+
{
156+
CurrentPhase = Phase.DetectNorth;
157+
}
158+
else
159+
{
160+
e.Enabled = true;
161+
var pos = C.WaitSpot switch
162+
{
163+
Corner.NE => PosNE,
164+
Corner.SE => PosSE,
165+
Corner.SW => PosSW,
166+
Corner.NW => PosNW,
167+
_ => PosNE
168+
};
169+
e.SetRefPosition(pos);
170+
}
171+
172+
break;
173+
case Phase.DetectNorth:
174+
if (!TryGetDarkDebuffHolder(out var reminingTime))
175+
{
176+
CurrentPhase = Phase.Idle;
177+
DetectedNorth = null;
178+
break;
179+
}
180+
181+
if (reminingTime > 8f) break;
182+
Corner north = default;
183+
var bestDistance = float.MaxValue;
184+
foreach (var cloneId in DarknessClones)
185+
{
186+
if (!cloneId.TryGetBattleNpc(out var clone)) continue;
187+
var dist = Vector3.Distance(clone.Position, PosNE);
188+
if (dist <= PosDetectRadius && dist < bestDistance)
189+
{
190+
bestDistance = dist;
191+
north = Corner.NE;
192+
}
193+
194+
dist = Vector3.Distance(clone.Position, PosSE);
195+
if (dist <= PosDetectRadius && dist < bestDistance)
196+
{
197+
bestDistance = dist;
198+
north = Corner.SE;
199+
}
200+
201+
dist = Vector3.Distance(clone.Position, PosSW);
202+
if (dist <= PosDetectRadius && dist < bestDistance)
203+
{
204+
bestDistance = dist;
205+
north = Corner.SW;
206+
}
207+
208+
dist = Vector3.Distance(clone.Position, PosNW);
209+
if (dist <= PosDetectRadius && dist < bestDistance)
210+
{
211+
bestDistance = dist;
212+
north = Corner.NW;
213+
}
214+
}
215+
216+
if (bestDistance != float.MaxValue)
217+
{
218+
DetectedNorth = north;
219+
CurrentPhase = Phase.Navigate;
220+
}
221+
222+
break;
223+
case Phase.Navigate:
224+
if (!TryGetDarkDebuffHolder(out _))
225+
{
226+
CurrentPhase = Phase.End;
227+
DetectedNorth = null;
228+
break;
229+
}
230+
231+
if (DetectedNorth == null) break;
232+
var hasDebuff = BasePlayer.StatusList.Any(s => s.StatusId == DebuffId);
233+
var basePos = GetBasePosition(C.RoleSlot, hasDebuff);
234+
var rotation = GetRotation(DetectedNorth.Value);
235+
var rotated = MathHelper.RotateWorldPoint(new Vector3(100,0,100), rotation.DegToRad(), basePos);
236+
e.Enabled = true;
237+
e.SetRefPosition(rotated);
238+
break;
239+
}
240+
}
241+
242+
private void UpdateDebug()
243+
{
244+
if (!C.ShowDebug) return;
245+
246+
var north = DetectedNorth ?? Corner.SE;
247+
var rotation = GetRotation(north);
248+
var index = 0;
249+
foreach (var role in DebugRoleOrder)
250+
{
251+
SetDebugElement(index++, GetBasePosition(role, false), rotation);
252+
}
253+
254+
foreach (var role in DebugRoleOrder)
255+
{
256+
SetDebugElement(index++, GetBasePosition(role, true), rotation);
257+
}
258+
}
259+
260+
private void SetDebugElement(int index, Vector3 basePos, float rotation)
261+
{
262+
if (!Controller.TryGetElementByName($"Debug{index}", out var e)) return;
263+
e.Enabled = true;
264+
var rotated = MathHelper.RotateWorldPoint(new Vector3(100,0,100),rotation.DegToRad(), basePos);
265+
e.SetRefPosition(rotated);
266+
}
267+
268+
private static Vector3 GetBasePosition(RoleSlot roleSlot, bool hasDebuff)
269+
{
270+
if (hasDebuff)
271+
{
272+
return roleSlot is RoleSlot.MeleeLeft or RoleSlot.MeleeRight ? NavMeleeDebuff : NavRangeDebuff;
273+
}
274+
275+
return roleSlot switch
276+
{
277+
RoleSlot.MeleeLeft => NavMeleeLeft,
278+
RoleSlot.MeleeRight => NavMeleeRight,
279+
RoleSlot.RangeLeft => NavRangeLeft,
280+
RoleSlot.RangeRight => NavRangeRight,
281+
_ => NavMeleeLeft
282+
};
283+
}
284+
285+
private static float GetRotation(Corner north)
286+
{
287+
return north switch
288+
{
289+
Corner.SE => 0f,
290+
Corner.SW => 90f,
291+
Corner.NW => 180f,
292+
Corner.NE => 270f,
293+
_ => 0f
294+
};
295+
}
296+
297+
private bool TryGetDarkDebuffHolder(out float remaining)
298+
{
299+
foreach (var player in Svc.Objects.OfType<IPlayerCharacter>())
300+
{
301+
var status = player.StatusList.FirstOrDefault(s => s.StatusId == DebuffId);
302+
if (status == null) continue;
303+
remaining = status.RemainingTime;
304+
return true;
305+
}
306+
307+
remaining = 0f;
308+
return false;
309+
}
310+
311+
private enum Phase
312+
{
313+
Idle,
314+
WaitForDebuff,
315+
DetectNorth,
316+
Navigate,
317+
End
318+
}
319+
320+
public class Config : IEzConfig
321+
{
322+
public Vector4 BaitColor1 = 0xFFFF00FF.ToVector4();
323+
public Vector4 BaitColor2 = 0xFFFFFF00.ToVector4();
324+
public RoleSlot RoleSlot = RoleSlot.MeleeLeft;
325+
public Corner WaitSpot = Corner.NE;
326+
public bool ShowDebug = false;
327+
}
328+
}

0 commit comments

Comments
 (0)