Skip to content

Commit 9cc3bc3

Browse files
authored
feat: tether navigation (#338)
1 parent 1ff7a87 commit 9cc3bc3

File tree

1 file changed

+128
-76
lines changed

1 file changed

+128
-76
lines changed

SplatoonScripts/Duties/Dawntrail/M11S Meteorain.cs

Lines changed: 128 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Linq;
34
using System.Numerics;
45
using Dalamud.Bindings.ImGui;
6+
using Dalamud.Game.ClientState.Objects.Types;
57
using Dalamud.Interface.Colors;
68
using ECommons;
79
using ECommons.Configuration;
10+
using ECommons.DalamudServices;
11+
using ECommons.GameFunctions;
812
using ECommons.Hooks;
913
using ECommons.Hooks.ActionEffectTypes;
1014
using ECommons.ImGuiMethods;
15+
using ECommons.Logging;
16+
using ECommons.MathHelpers;
1117
using Splatoon;
1218
using Splatoon.SplatoonScripting;
1319

@@ -45,12 +51,6 @@ public enum Role
4551
Tether
4652
}
4753

48-
public enum TetherOrigin
49-
{
50-
NorthWest,
51-
SouthEast
52-
}
53-
5454
private readonly uint _actionAfterWave3 = 46134;
5555
private readonly uint _actionEnd = 46139;
5656
private readonly uint _actionMeteorPlace = 46133;
@@ -70,14 +70,18 @@ public enum TetherOrigin
7070
private readonly Vector2 _posSW = new(93f, 107f);
7171
private readonly float _southPullDist = 18f;
7272

73+
private HashSet<(uint source,uint target)> _tetherMaps = new();
74+
private int _tetherCount = 0;
75+
private uint _correctSources;
76+
7377
private readonly string _vfxLockon = "vfx/lockon/eff/lockon8_t0w.avfx";
7478

7579
private Element? _eNav;
7680
private Element? _eTether;
7781

7882
private State _state = State.Idle;
7983

80-
public override Metadata Metadata => new(1, "Garume");
84+
public override Metadata Metadata => new(2, "Garume");
8185
public override HashSet<uint>? ValidTerritories { get; } = [1325];
8286

8387
private Config C => Controller.GetConfig<Config>();
@@ -89,7 +93,8 @@ public override void OnSetup()
8993
radius = 2f,
9094
thicc = 10f,
9195
overlayVOffset = 3f,
92-
overlayFScale = 2.6f
96+
overlayFScale = 2.6f,
97+
tether = true
9398
});
9499

95100
Controller.RegisterElement("Tether", new Element(2)
@@ -149,12 +154,50 @@ public override void OnVFXSpawn(uint target, string vfxPath)
149154
if (!vfxPath.Equals(_vfxLockon, StringComparison.OrdinalIgnoreCase) &&
150155
!vfxPath.StartsWith(_vfxLockon, StringComparison.OrdinalIgnoreCase))
151156
return;
157+
152158

153159
if (_state == State.Wave1Prepare) _state = State.Wave1Active;
154160
else if (_state == State.Wave2Prepare) _state = State.Wave2Active;
155161
else if (_state == State.Wave3Prepare) _state = State.Wave3Active;
156162
}
157163

164+
public override void OnTetherCreate(uint source, uint target, uint data2, uint data3, uint data5)
165+
{
166+
if (data3 != 356) return;
167+
168+
if (_state != State.Idle && _state != State.Done)
169+
{
170+
_tetherCount++;
171+
_tetherMaps.Add((source, target));
172+
if (_tetherCount == 2)
173+
{
174+
var desiredDirections = C.UseSecondMeteorDirection ? new[]
175+
{
176+
C.MyTetherOrigin,
177+
C.MySecondTetherOrigin
178+
}: new []{ C.MyTetherOrigin};
179+
var normalizedDirections = desiredDirections.Select(Dir).ToArray();
180+
foreach (var (s, t) in _tetherMaps)
181+
{
182+
var targetObject = t.GetObject();
183+
var targetDirection = Dir(targetObject!.Position.ToVector2());
184+
185+
if (normalizedDirections.Any(d => Vector2.Dot(d, targetDirection) > 0.95f))
186+
{
187+
_correctSources = s;
188+
}
189+
}
190+
}
191+
}
192+
}
193+
194+
public override void OnTetherRemoval(uint source, uint data2, uint data3, uint data5)
195+
{
196+
if (data2 != 356) return;
197+
198+
_tetherMaps.RemoveWhere(x => x.source == source);
199+
}
200+
158201
public override void OnActionEffectEvent(ActionEffectSet set)
159202
{
160203
if (set.Action == null) return;
@@ -163,6 +206,7 @@ public override void OnActionEffectEvent(ActionEffectSet set)
163206

164207
if (actionId == _actionMeteorPlace)
165208
{
209+
_tetherCount = 0;
166210
if (_state == State.Wave1Active) _state = State.Wave2Prepare;
167211
else if (_state == State.Wave2Active) _state = State.Wave3Prepare;
168212
else if (_state == State.Wave3Active) _state = State.Wave4StackOnly;
@@ -189,60 +233,50 @@ public override void OnUpdate()
189233
if (_state is State.Idle or State.Done) return;
190234

191235
var grad = GradientColor.Get(C.GradientA, C.GradientB, 333).ToUint();
192-
193-
if (_eNav != null)
236+
var (wave, phase) = GetWavePhase();
237+
238+
if (C.MyRole == Role.Tether && (wave == 2 || wave == 3 || wave == 4))
239+
{
240+
ApplyCommonStyle(_eTether, grad);
241+
var t = GetTetherInstruction();
242+
_eTether.SetOffPosition(new Vector3(t.sourcePos.X, 0f, t.sourcePos.Y));
243+
_eTether.SetRefPosition(new Vector3(t.targetPos.X, 0f, t.targetPos.Y));
244+
}
245+
else
194246
{
195247
ApplyCommonStyle(_eNav, grad);
196248

197-
var nav = GetNavInstruction();
249+
var nav = GetNavInstruction(wave, phase);
198250
SetElement(_eNav, nav.pos, nav.text);
199251
}
200-
201-
if (_eTether != null)
202-
if (ShouldShowTether())
203-
{
204-
ApplyCommonStyle(_eTether, grad);
205-
var t = GetTetherInstruction();
206-
SetElement(_eTether, t.pos, t.text);
207-
}
208-
}
209-
210-
private bool ShouldShowTether()
211-
{
212-
if (C.MyRole != Role.Tether) return false;
213-
return _state == State.Wave2Active || _state == State.Wave3Active;
214252
}
215253

216-
private (Vector2 pos, string text) GetTetherInstruction()
254+
private unsafe (Vector2 sourcePos,Vector2 targetPos, string text) GetTetherInstruction()
217255
{
218-
var originPos = C.MyTetherOrigin == TetherOrigin.NorthWest
219-
? PosDir(Dir(Corner.NorthWest), _distMeteor)
220-
: PosDir(Dir(Corner.SouthEast), _distMeteor);
221-
var pullPos = C.MyTetherOrigin == TetherOrigin.NorthWest
222-
? _center with { Y = _center.Y - _northPullDist }
223-
: _center with { Y = _center.Y + _southPullDist };
224-
225-
var txt = C.MyTetherOrigin == TetherOrigin.NorthWest
226-
? "Tether: Take NW -> Pull N"
227-
: "Tether: Take SE -> Pull S";
228-
229-
var useOrigin = true;
230-
if (_state == State.Wave2Active || _state == State.Wave3Active) useOrigin = true;
231-
232-
return useOrigin ? (originPos, txt) : (pullPos, txt);
256+
var sourceObj = _correctSources.GetObject() as IBattleNpc;
257+
if (sourceObj == null)
258+
return (Vector2.Zero, Vector2.Zero, "");
259+
var targetObj = sourceObj.Struct()->Vfx.Tethers.ToArray().ToArray().First().TargetId.ObjectId.GetObject();
260+
261+
var text = targetObj != null && targetObj.EntityId == Controller.BasePlayer.EntityId ? "Correct !!!" : "Pick this";
262+
if (targetObj == null)
263+
return (Vector2.Zero, Vector2.Zero, text);
264+
265+
var sourcePos = sourceObj.Position.ToVector2();
266+
var targetPos = targetObj.Position.ToVector2();
267+
return (sourcePos, targetPos, text);
233268
}
234269

235-
private (Vector2 pos, string text) GetNavInstruction()
270+
private (Vector2 pos, string text) GetNavInstruction(int wave, Phase phase)
236271
{
237-
var (wave, phase) = GetWavePhase();
238-
239272
if (phase == Phase.Final)
240-
return (PosDir(Dir(MeteorSpot.SouthWest), _distFinal), "Final: SW");
273+
return (PosDir(Dir(MeteorSpot.SouthWest), _distFinal), "Final");
241274

275+
276+
var p = CornerToPos(C.Stack4);
242277
if (phase == Phase.StackOnly)
243278
{
244-
var p = CornerToPos(C.Stack4);
245-
return (p, $"Stack4 {Short(C.Stack4)}");
279+
return (p, $"Stack4");
246280
}
247281

248282
var stackCorner = wave switch
@@ -252,25 +286,35 @@ private bool ShouldShowTether()
252286
_ => C.Stack3
253287
};
254288

255-
var isMyMeteorWave = C.MyRole == Role.Meteor && C.MyMeteorWave ==
289+
var isMyMeteorWave = C.MyMeteorWave ==
256290
(wave == 1 ? MeteorWave.Wave1 : wave == 2 ? MeteorWave.Wave2 : MeteorWave.Wave3);
257-
291+
var isPrepareMeteorWave = C.MyMeteorWave ==
292+
(wave == 1 ? MeteorWave.Wave2 : MeteorWave.Wave3);
258293
if (phase == Phase.Prepare)
259294
{
260295
if (isMyMeteorWave)
261-
return (_center, $"Wave{wave}: Near Boss");
262-
var p = CornerToPos(stackCorner);
263-
return (p, $"Stack{wave} {Short(stackCorner)}");
296+
return (_center, $"Wave{wave}: {C.NearBoss.Get()}");
297+
return (p, $"Stack");
264298
}
265-
266-
if (isMyMeteorWave)
299+
else
267300
{
268-
var (mp, mt) = MeteorToNav(C.MyMeteorSpot, wave);
269-
return (mp, mt);
301+
if (isMyMeteorWave)
302+
{
303+
var (mp, mt) = MeteorToNav(C.MyMeteorSpot, wave);
304+
return (mp, mt);
305+
}
306+
else if (isPrepareMeteorWave)
307+
{
308+
return (p, $"Wave{wave}: {C.PrepareNearBoss.Get()}");
309+
}
310+
else
311+
{
312+
return (p, $"Stack");
313+
}
270314
}
271315

272316
var sp = CornerToPos(stackCorner);
273-
return (sp, $"Stack{wave} {Short(stackCorner)}");
317+
return (sp, $"Stack");
274318
}
275319

276320
private (int wave, Phase phase) GetWavePhase()
@@ -322,16 +366,6 @@ private void SetElement(Element e, Vector2 xz, string text)
322366
e.Enabled = true;
323367
}
324368

325-
private string Short(Corner c)
326-
{
327-
return c switch
328-
{
329-
Corner.NorthWest => "NW",
330-
Corner.NorthEast => "NE",
331-
_ => "SE"
332-
};
333-
}
334-
335369
private string Short(MeteorSpot s)
336370
{
337371
return s switch
@@ -353,8 +387,10 @@ public override void OnSettingsDraw()
353387

354388
if (C.MyRole == Role.Tether)
355389
{
356-
ImGui.TextColored(ImGuiColors.DalamudRed, "対応していません。/ Not supported.");
357-
ImGuiEx.EnumCombo("Tether Origin", ref C.MyTetherOrigin);
390+
ImGuiEx.EnumCombo("Meteor Direction", ref C.MyTetherOrigin);
391+
ImGuiEx.HelpMarker("Please select which direction’s meteor tether to track.\nどの方角のメテオについたテザーを見るか選択してください");
392+
ImGui.Checkbox("Use Second Direction", ref C.UseSecondMeteorDirection);
393+
ImGuiEx.EnumCombo("Second Meteor Direction", ref C.MySecondTetherOrigin);
358394
}
359395
else
360396
{
@@ -373,8 +409,13 @@ public override void OnSettingsDraw()
373409
ImGuiEx.Text("Gradient (2 colors)");
374410
ImGui.ColorEdit4("Color A", ref C.GradientA, ImGuiColorEditFlags.NoInputs);
375411
ImGui.ColorEdit4("Color B", ref C.GradientB, ImGuiColorEditFlags.NoInputs);
376-
377412
ImGui.Separator();
413+
414+
var prepareNearBoss = C.PrepareNearBoss.Get();
415+
C.PrepareNearBoss.ImGuiEdit(ref prepareNearBoss);
416+
var nearBoss = C.NearBoss.Get();
417+
C.NearBoss.ImGuiEdit(ref nearBoss);
418+
378419
if (ImGui.CollapsingHeader("Guide (JP)"))
379420
{
380421
ImGui.BulletText("頭割りの位置は 1〜4回目すべて選択してください。");
@@ -388,9 +429,9 @@ public override void OnSettingsDraw()
388429
{
389430
ImGui.BulletText(" Please select the stack position for all four stacks (Stack 1–4).");
390431
ImGui.BulletText(" Example: If you take the first stack at the northeast, choose NorthEast.");
391-
ImGui.BulletText(" If you are baiting/placing a meteor, set MyRole to Meteor, then choose which wave (1–3) and which direction you will place it.");
432+
ImGui.BulletText(" If you are baiting/placing a meteor, set MyRole to Meteor, then choose which wave (1–3) and which direction you will place it.");
392433
ImGui.BulletText(" Example: If you want to place the second meteor to the southwest, select Wave2 and then choose one of SouthWest / SouthWestAlt1 / SouthWestAlt2.");
393-
ImGui.BulletText(" For the southwest options, the placement is farther out in this order: SouthWest < SouthWestAlt1 < SouthWestAlt2.");
434+
ImGui.BulletText(" For the southwest options, the placement is farther out in this order: SouthWest < SouthWestAlt1 < SouthWestAlt2.");
394435
}
395436

396437
if (ImGui.CollapsingHeader("Debug"))
@@ -421,17 +462,28 @@ public class Config : IEzConfig
421462
public Vector4 GradientA = ImGuiColors.DalamudYellow;
422463
public Vector4 GradientB = ImGuiColors.DalamudRed;
423464
public MeteorSpot MyMeteorSpot = MeteorSpot.NorthWest;
424-
425465
public MeteorWave MyMeteorWave = MeteorWave.Wave1;
426-
427466
public Role MyRole = Role.Meteor;
428-
429-
public TetherOrigin MyTetherOrigin = TetherOrigin.NorthWest;
467+
public MeteorSpot MyTetherOrigin = MeteorSpot.NorthEast;
468+
public MeteorSpot MySecondTetherOrigin = MeteorSpot.SouthEast;
469+
public bool UseSecondMeteorDirection;
430470

431471
public Corner Stack1 = Corner.NorthEast;
432472
public Corner Stack2 = Corner.NorthWest;
433473
public Corner Stack3 = Corner.SouthEast;
434474
public Corner Stack4 = Corner.NorthEast;
475+
476+
public InternationalString PrepareNearBoss = new()
477+
{
478+
En = "Next, near boss",
479+
Jp = "次はボスの足元へ"
480+
};
481+
482+
public InternationalString NearBoss = new()
483+
{
484+
En = "Near boss",
485+
Jp = "足元へ"
486+
};
435487
}
436488

437489
private enum Phase

0 commit comments

Comments
 (0)