Skip to content

Commit ba93f6e

Browse files
authored
docs: add headmarker guide (#5586)
1 parent a44b96f commit ba93f6e

File tree

5 files changed

+206
-0
lines changed

5 files changed

+206
-0
lines changed

docs/Headmarkers.md

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
# Headmarker Guide
2+
3+
This is a guide to finding the real ids for "headmarkers",
4+
i.e. the visual that appears above your player's head when you need to spread, stack, etc.
5+
6+
cactbot calls them "headmarkers",
7+
e.g. `NetRegexes.headMarker()`.
8+
The FFXIV parsing plugin calls them "TargetIcon",
9+
e.g. `[21:40:13.596] TargetIcon 1B:4002ADD7:Arcane Cylinder:0000:0000:00B5:0000:0000:0000`.
10+
FFXIV itself calls them "Lockon",
11+
e.g. <https://github.com/xivapi/ffxiv-datamining/blob/master/csv/Lockon.csv>.
12+
13+
This guide will call them "headmarkers" from here on out.
14+
15+
## History and Motivation
16+
17+
During TEA,
18+
Square Enix changed headmarkers to have a per-instance offset in certain content.
19+
This now happens in all ultimates, extremes, and savage raids.
20+
Once the content is no longer current,
21+
sometimes the offsets are then removed.
22+
23+
This same thing happened with ability ids as well during o4s.
24+
However, the FFXIV parsing plugin and fflogs cannot function without real ability ids,
25+
and so the parsing plugin takes care of this itself.
26+
Headmarkers are not parsing related,
27+
and so headmarker offsets are left to downstream developers to fix themselves.
28+
29+
The offset is sent to the client when zoning into the instance,
30+
and so is the same for every pull while in the same instance.
31+
32+
## Handling this in cactbot
33+
34+
cactbot chooses to handle this by trying to figure out the true headmarker ids.
35+
This is so that it's easy to find different content using the same id,
36+
e.g. limit cut starting at `004F` (usually, but not always).
37+
It's also easier to double check that the values are correct.
38+
Ideally, when finding headmarkers, please leave a comment with the full avfx path.
39+
40+
cactbot handles offsets by looking for headmarkers,
41+
recording the first id it finds,
42+
and then comparing to the expected first headmarker to calculate the offset.
43+
44+
### No Offsets
45+
46+
If the encounter does not have headmarker offsets,
47+
please use headmarker ids directly in triggers,
48+
e.g. [p9n](https://github.com/quisquous/cactbot/blob/7b904e35c7d678013d229080c858f19d35510ac1/ui/raidboss/data/06-ew/raid/p9n.ts#L33-L38).
49+
50+
### Same first headmarker
51+
52+
Most of the time,
53+
if the encounter does have headmarker offsets,
54+
the first headmarker id will always be the same.
55+
56+
Most trigger sets do something like [p11s](https://github.com/quisquous/cactbot/blob/3ca3589/ui/raidboss/data/06-ew/raid/p11s.ts).
57+
58+
There's a helper function to set the headmarker offset if it's not found,
59+
and return the true headmarker id.
60+
61+
```typescript
62+
// Helper functions.
63+
const firstHeadmarker = parseInt(headmarkers.dike, 16);
64+
65+
const getHeadmarkerId = (data: Data, matches: NetMatches['HeadMarker']) => {
66+
if (data.decOffset === undefined)
67+
data.decOffset = parseInt(matches.id, 16) - firstHeadmarker;
68+
return (parseInt(matches.id, 16) - data.decOffset).toString(16).toUpperCase().padStart(4, '0');
69+
};
70+
```
71+
72+
There's usually also a trigger at the top of the file to always try to set the offset.
73+
In the past there's been bugs where `getHeadmarkerId` has been used in `condition` functions.
74+
(See the P9S defamation example below for how the `getHeadmarkerId` call might be skipped.)
75+
76+
```typescript
77+
{
78+
id: 'P11S Headmarker Tracker',
79+
type: 'HeadMarker',
80+
netRegex: {},
81+
condition: (data) => data.decOffset === undefined,
82+
// Unconditionally set the first headmarker here so that future triggers are conditional.
83+
run: (data, matches) => getHeadmarkerId(data, matches),
84+
},
85+
```
86+
87+
Then, any later headmarker trigger has to match all headmarker lines,
88+
and use `getHeadmarkerId` to figure out the correct id.
89+
It's definitely a little bit cumbersome and inefficient, but that's what we got.
90+
91+
```typescript
92+
{
93+
id: 'P9S Defamation',
94+
type: 'HeadMarker',
95+
netRegex: {},
96+
condition: (data, matches) => {
97+
return data.me === matches.target &&
98+
getHeadmarkerId(data, matches) === headmarkers.defamation;
99+
},
100+
alarmText: (_data, _matches, output) => output.defamation!(),
101+
// etc
102+
```
103+
104+
### Different first headmarker
105+
106+
In rare cases, the first headmarker is not consistent.
107+
108+
As cactbot resets all trigger info (including recorded headmarker offset) on wipe,
109+
any trigger file must handle the first headmarker from any door boss and final boss simultaneously.
110+
111+
For example, [P12S](https://github.com/quisquous/cactbot/blob/4700770/ui/raidboss/data/06-ew/raid/p12s.ts#L159-L179)
112+
has a door boss with two different first headmarkers (bottom left / bottom right wing)
113+
and a final boss with one first headmarker.
114+
115+
See that file for how that can be solved.
116+
117+
## Lockon table
118+
119+
All visual effects are avfx game data files and are referenced by the `Lockon` table.
120+
121+
You can `exd Lockon` from a local copy of [SaintCoinach](https://github.com/xivapi/SaintCoinach),
122+
or alternatively you can browse the Lockon table online here: <https://github.com/xivapi/ffxiv-datamining/blob/master/csv/Lockon.csv>
123+
124+
Headmarkers are 2 byte hex values,
125+
and the `key` field in the `Lockon` is the decimal representation of that hex value.
126+
127+
Here is some code from: <https://github.com/quisquous/cactbot/blob/main/ui/raidboss/data/06-ew/raid/p9s.ts>
128+
129+
```typescript
130+
const headmarkers = {
131+
// vfx/lockon/eff/tank_lockonae_0m_5s_01t.avfx
132+
dualityOfDeath: '01D4',
133+
// vfx/lockon/eff/m0361trg_a1t.avfx (through m0361trg_a8t)
134+
limitCut1: '004F',
135+
limitCut2: '0050',
136+
limitCut3: '0051',
137+
limitCut4: '0052',
138+
limitCut5: '0053',
139+
limitCut6: '0054',
140+
limitCut7: '0055',
141+
limitCut8: '0056',
142+
// vfx/lockon/eff/r1fz_skywl_s9x.avfx
143+
defamation: '014A',
144+
// vfx/lockon/eff/n5r9_lockon_bht_c0g.avfx
145+
cometMarker: '01B3',
146+
} as const;
147+
```
148+
149+
The comet marker (that appears on one of the two unbroken meteors during Charybdis in P9S) is `01B3`.
150+
`0x01B3` = 435 in decimal.
151+
In the [LockOn table](https://github.com/xivapi/ffxiv-datamining/blob/master/csv/Lockon.csv#L439),
152+
you can find that key 435 has the string `n5r9_lockon_bht_c0g`.
153+
154+
This corresponds to the game asset `vfx/lockon/eff/n5r9_lockon_bht_c0g.avfx`.
155+
My understanding is that all headmarker effects have this `avfx` extension
156+
and are in the `vfx/lockon/eff/` game directory.
157+
158+
## How to test headmarkers in game
159+
160+
In order to verify that you have the correct headmarker id,
161+
you can use VFXEditor.
162+
163+
Install [FFXIVQuickLauncher](https://github.com/goatcorp/FFXIVQuickLauncher).
164+
165+
Install the [VFXEditor plugin](https://github.com/0ceal0t/Dalamud-VFXEditor).
166+
167+
Type `/vfxedit` to start.
168+
169+
![VFXEditor Initial](images/vfxeditor_initial.png)
170+
171+
Pick a skill to replace that's easy to do in game, like Cure 1.
172+
173+
![VFXEditor Replace](images/vfxeditor_replace.png)
174+
175+
Now, type a vfx to replace it with.
176+
177+
![VFXEditor Loaded](images/vfxeditor_loaded.png)
178+
179+
Finally, cast cure and observe the different animation.
180+
181+
![VFXEditor Result](images/vfxeditor_result.png)
182+
183+
Headmarkers on mobs (see: p12s wings, this meteor marker) may have odd positions and rotations.
184+
185+
## How to Find True Ids
186+
187+
Mostly this is about sleuthing it out.
188+
189+
Here's some suggestions on things to consider:
190+
191+
- look for new Lockon entries that weren't there in previous patches
192+
- check if normal mode ids still apply (e.g. p12s wings)
193+
- check if the same ids from previous encounters apply (e.g. limit cut ids)
194+
- read names in the Lockon table and try to make connections, e.g. `m0515_turning_right01c` is the orange clockwise laser rotation
195+
196+
## Future Work
197+
198+
If somebody tracked down the network data and game code to find the actual offset and math,
199+
then OverlayPlugin could emit a custom log line for the offset
200+
or custom log lines with adjusted headmarker ids.
201+
202+
Barring that, it's also possible that OverlayPlugin could handle all of the
203+
"first headmarker" tracking itself per zone in C# code and emit custom log lines.
204+
205+
Finally, it'd be nice if somebody could figure out how to automatically extract avfx into gifs
206+
and then we could have an online library of headmarker ids mapped to visuals.

docs/images/vfxeditor_initial.png

89.9 KB
Loading

docs/images/vfxeditor_loaded.png

94.5 KB
Loading

docs/images/vfxeditor_replace.png

143 KB
Loading

docs/images/vfxeditor_result.png

617 KB
Loading

0 commit comments

Comments
 (0)