From b09735b2f92343f844f9f74bab622ec1db70b0c3 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 10 Dec 2025 00:07:36 -0800 Subject: [PATCH 1/4] raidboss: add basic triggers for Arkveld Extreme Add several basic triggers for various mechanics in the Arkveld Extreme fight. This includes some callouts for aoes, the chainblade blow, the limit cut mechanic, and more. The head markers do appear to be static, at least across multiple logs I have. I've tested this in game with a few pulls and things seem to be working now, but it likely needs more testing. There are also a few mechanics which I didn't find a good way to make a trigger and left them out, including the baits and towers, as well as all of the expanding exaline mechanics. Signed-off-by: Jacob Keller --- ui/raidboss/data/07-dt/trial/arkveld-ex.ts | 229 +++++++++++++++++++- ui/raidboss/data/07-dt/trial/arkveld-ex.txt | 4 +- 2 files changed, 229 insertions(+), 4 deletions(-) diff --git a/ui/raidboss/data/07-dt/trial/arkveld-ex.ts b/ui/raidboss/data/07-dt/trial/arkveld-ex.ts index 9c2b2e8df38..ce20b40345e 100644 --- a/ui/raidboss/data/07-dt/trial/arkveld-ex.ts +++ b/ui/raidboss/data/07-dt/trial/arkveld-ex.ts @@ -1,14 +1,239 @@ +import Conditions from '../../../../../resources/conditions'; +import Outputs from '../../../../../resources/outputs'; +import { Responses } from '../../../../../resources/responses'; +import { DirectionOutputCardinal, Directions } from '../../../../../resources/util'; import ZoneId from '../../../../../resources/zone_id'; import { RaidbossData } from '../../../../../types/data'; import { TriggerSet } from '../../../../../types/trigger'; -export type Data = RaidbossData; +const limitCutMap: { [id: string]: number } = { + ['0194']: 1, + ['0195']: 2, + ['0196']: 3, + ['0197']: 4, + ['0198']: 5, + ['0199']: 6, + ['019A']: 7, + ['019B']: 8, +} as const; + +const limitCutIds: readonly string[] = Object.keys(limitCutMap); + +export interface Data extends RaidbossData { + limitCutNumber?: number; +} + +// TODO: baits + towers +// TODO: make Ouroblade non-boss relative by using heading const triggerSet: TriggerSet = { id: 'TheWindwardWildsExtreme', zoneId: ZoneId.TheWindwardWildsExtreme, timelineFile: 'arkveld-ex.txt', - triggers: [], + triggers: [ + { + id: 'Arkveld Ex Roar', + type: 'StartsUsing', + netRegex: { source: 'Guardian Arkveld', id: 'ABA[EF]', capture: false }, + response: Responses.aoe(), + }, + { + id: 'Arkveld Ex Chainblade Blow Left', + type: 'StartsUsing', + // This is relative to the front of the boss, even when he's not on + // the edge. + netRegex: { source: 'Guardian Arkveld', id: ['AB6F', 'B019'], capture: false }, + response: Responses.goLeftThenRight(), + }, + { + id: 'Arkveld Ex Chainblade Blow Right', + type: 'StartsUsing', + // This is relative to the front of the boss, even when he's not on + // the edge. + netRegex: { source: 'Guardian Arkveld', id: ['AB70', 'B01A'], capture: false }, + response: Responses.goRightThenLeft(), + }, + { + id: 'Arkveld Ex Guardian Siegeflight', + type: 'StartsUsing', + netRegex: { source: 'Guardian Arkveld', id: ['AB7B', 'B029'], capture: false }, + alertText: (_data, _matches, output) => output.text!(), + outputStrings: { + text: { + en: 'Follow Dash => Out + Healer Stacks', + }, + }, + }, + { + id: 'Arkveld Ex Wyvern Siegeflight', + type: 'StartsUsing', + netRegex: { source: 'Guardian Arkveld', id: ['AB7E', 'B02A'], capture: false }, + alertText: (_data, _matches, output) => output.text!(), + outputStrings: { + text: { + en: 'Follow Dash => In + Healer Stacks', + }, + }, + }, + { + id: 'Arkveld Ex Wyvern Dragonspark / White Flash', + type: 'StartsUsing', + netRegex: { source: 'Guardian Arkveld', id: 'ABB[23]', capture: false }, + suppressSeconds: 1, + alertText: (_data, _matches, output) => output.text!(), + outputStrings: { + text: { + en: 'Healer Stacks', + }, + }, + }, + { + id: 'Arkveld Ex Wyverns Ouroblade Left', + type: 'StartsUsing', + // TODO: make this use heading to choose a cardinal direction + netRegex: { source: 'Guardian Arkveld', id: ['AB8B', 'B031'], capture: false }, + suppressSeconds: 1, + alertText: (_data, _matches, output) => output.text!(), + outputStrings: { + text: { + en: 'Right + Spread', + }, + }, + }, + { + id: 'Arkveld Ex Wyverns Ouroblade Right', + type: 'StartsUsing', + // TODO: make this use heading to choose a cardinal direction + netRegex: { source: 'Guardian Arkveld', id: ['AB8D', 'B032'], capture: false }, + suppressSeconds: 1, + alertText: (_data, _matches, output) => output.text!(), + outputStrings: { + text: { + en: 'Left + Spread', + }, + }, + }, + { + id: 'Arkveld Ex Spread Marker', + type: 'HeadMarker', + netRegex: { id: '0065', capture: false }, + suppressSeconds: 1, + response: Responses.spread(), + }, + { + id: 'Arkveld Ex Steeltail Thrust', + type: 'StartsUsing', + netRegex: { source: 'Guardian Arkveld', id: ['ABAD', 'B035'], capture: false }, + response: Responses.goFrontOrSides(), + }, + { + id: 'Arkveld Ex Forged Fury', + type: 'StartsUsing', + // Appears alongside AB9E, AEF9, and AB9F + netRegex: { source: 'Guardian Arkveld', id: 'AEF8', capture: false }, + response: Responses.bigAoe(), + }, + { + id: 'Arkveld Ex Limit Cut', + type: 'HeadMarker', + netRegex: { id: limitCutIds, capture: true }, + condition: Conditions.targetIsYou(), + suppressSeconds: 1, + run: (data, matches) => { + if (!limitCutIds.includes(matches.id)) + return; + const num = limitCutMap[matches.id]; + data.limitCutNumber = num; + }, + }, + { + id: 'Arkveld Ex Roar 2', + type: 'StartsUsing', + netRegex: { source: 'Guardian Arkveld', id: 'B092', capture: false }, + response: Responses.bigAoe(), + }, + { + id: 'Arkveld Ex Clamorous Chase', + type: 'StartsUsing', + // This assumes the popular Diamond Cut strategy + netRegex: { source: 'Guardian Arkveld', id: ['ABB3', 'ABB6'], capture: true }, + delaySeconds: 0.1, + durationSeconds: 20, + alertText: (data, matches, output) => { + const startEast: { [id: number]: DirectionOutputCardinal } = { + [1]: 'dirE', + [2]: 'dirS', + [3]: 'dirW', + [4]: 'dirN', + [5]: 'dirE', + [6]: 'dirS', + [7]: 'dirW', + [8]: 'dirN', + } as const; + const startWest: { [id: number]: DirectionOutputCardinal } = { + [1]: 'dirW', + [2]: 'dirS', + [3]: 'dirE', + [4]: 'dirN', + [5]: 'dirW', + [6]: 'dirS', + [7]: 'dirE', + [8]: 'dirN', + } as const; + + const directions = matches.id === 'ABB3' ? startEast : startWest; + const num = data.limitCutNumber; + const dir = num === undefined ? directions[1] : directions[num]; + if (dir === undefined) { + if (num !== undefined) { + return output.number!({ num: num }); + } + return output.unknown!(); + } + + const dirStr = output[dir]!(); + + if (num === undefined) { + return output.first!({ dir: dirStr }); + } + + return output.text!({ dir: dir, num: num }); + }, + outputStrings: { + ...Directions.outputStringsCardinalDir, + text: { + en: '${dir} (${num})', + }, + number: { + en: '${num}', + }, + first: { + en: '1 starts ${dir}', + }, + unknown: Outputs.unknown, + }, + }, + { + id: 'Arkveld Ex Laser Target', + type: 'HeadMarker', + netRegex: { id: '01D6', capture: true }, + suppressSeconds: 1, + alertText: (data, matches, output) => { + if (matches.target === data.me) + return output.laserOnYou!(); + + return output.laserOnPlayer!({ player: data.party.member(matches.target) }); + }, + outputStrings: { + laserOnYou: { + en: 'Laser on you', + }, + laserOnPlayer: { + en: 'Laser on ${player}', + }, + }, + }, + ], timelineReplace: [ { 'locale': 'de', diff --git a/ui/raidboss/data/07-dt/trial/arkveld-ex.txt b/ui/raidboss/data/07-dt/trial/arkveld-ex.txt index ed3489ad209..f12de301790 100644 --- a/ui/raidboss/data/07-dt/trial/arkveld-ex.txt +++ b/ui/raidboss/data/07-dt/trial/arkveld-ex.txt @@ -130,7 +130,7 @@ hideall "--sync--" # AB8C and AB8E are the secondary follow-up hit similar to chainblade blow # # After the power-up: -# B032 and B032 are the first hit, each being a different half +# B031 and B032 are the first hit, each being a different half # B033 and B034 are the secondary follow-up hit similar to chainblade blow # # AB96 Wyvern's Vengeance @@ -169,7 +169,7 @@ hideall "--sync--" # AF05 Steeltail Thrust # AF06 Steeltail Thrust # B035 Steeltail Thrust -# Tail thrust. One might be the animation while one is thie hit, not sure. +# Tail thrust. One might be the animation while one is the hit, not sure. # It looks like damage is on the second skill. # # ABAE Roar From 0236a0ff73e94fec95b5ad0003d3b9feaa8360de Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 15 Dec 2025 14:25:40 -0800 Subject: [PATCH 2/4] fix direction callout for clamorous chase Signed-off-by: Jacob Keller --- ui/raidboss/data/07-dt/trial/arkveld-ex.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/trial/arkveld-ex.ts b/ui/raidboss/data/07-dt/trial/arkveld-ex.ts index ce20b40345e..651a928427f 100644 --- a/ui/raidboss/data/07-dt/trial/arkveld-ex.ts +++ b/ui/raidboss/data/07-dt/trial/arkveld-ex.ts @@ -197,7 +197,7 @@ const triggerSet: TriggerSet = { return output.first!({ dir: dirStr }); } - return output.text!({ dir: dir, num: num }); + return output.text!({ dir: dirStr, num: num }); }, outputStrings: { ...Directions.outputStringsCardinalDir, From c50d289d07150a0fb9522933d77b821e25dde8b5 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 15 Dec 2025 14:26:42 -0800 Subject: [PATCH 3/4] remove redundant healer stack callout There is a redundant healer stacks callout that sometimes also triggers during Clamorous chase. Its unclear why it triggers there, but its not helpful anyways, since we already call out the healer stack mechanics ahead of time. Signed-off-by: Jacob Keller --- ui/raidboss/data/07-dt/trial/arkveld-ex.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/ui/raidboss/data/07-dt/trial/arkveld-ex.ts b/ui/raidboss/data/07-dt/trial/arkveld-ex.ts index 651a928427f..33a2c7363f5 100644 --- a/ui/raidboss/data/07-dt/trial/arkveld-ex.ts +++ b/ui/raidboss/data/07-dt/trial/arkveld-ex.ts @@ -75,18 +75,6 @@ const triggerSet: TriggerSet = { }, }, }, - { - id: 'Arkveld Ex Wyvern Dragonspark / White Flash', - type: 'StartsUsing', - netRegex: { source: 'Guardian Arkveld', id: 'ABB[23]', capture: false }, - suppressSeconds: 1, - alertText: (_data, _matches, output) => output.text!(), - outputStrings: { - text: { - en: 'Healer Stacks', - }, - }, - }, { id: 'Arkveld Ex Wyverns Ouroblade Left', type: 'StartsUsing', From d8e0b315cde52c068a007bfa6bbff0e485aad4cd Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 15 Dec 2025 22:29:01 -0800 Subject: [PATCH 4/4] use the heading direction to call safe direction for Ouroblade This is a work in progress attempt at parsing the heading and calling the safe direction for the Wyvern's Ouroblade. Needs to be tested in game, as I do not have footage to line up with simulator. Signed-off-by: Jacob Keller --- ui/raidboss/data/07-dt/trial/arkveld-ex.ts | 31 +++++++++++++++------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/ui/raidboss/data/07-dt/trial/arkveld-ex.ts b/ui/raidboss/data/07-dt/trial/arkveld-ex.ts index 33a2c7363f5..98387d0e9e3 100644 --- a/ui/raidboss/data/07-dt/trial/arkveld-ex.ts +++ b/ui/raidboss/data/07-dt/trial/arkveld-ex.ts @@ -24,7 +24,6 @@ export interface Data extends RaidbossData { } // TODO: baits + towers -// TODO: make Ouroblade non-boss relative by using heading const triggerSet: TriggerSet = { id: 'TheWindwardWildsExtreme', @@ -78,26 +77,40 @@ const triggerSet: TriggerSet = { { id: 'Arkveld Ex Wyverns Ouroblade Left', type: 'StartsUsing', - // TODO: make this use heading to choose a cardinal direction - netRegex: { source: 'Guardian Arkveld', id: ['AB8B', 'B031'], capture: false }, + netRegex: { source: 'Guardian Arkveld', id: ['AB8B', 'B031'], capture: true }, suppressSeconds: 1, - alertText: (_data, _matches, output) => output.text!(), + alertText: (_data, matches, output) => { + const bossDirNum = Directions.hdgTo8DirNum(parseFloat(matches.heading)); + // Safe area is 90* clockwise of heading + const safeDirNum = (bossDirNum + 2) % 8; + const safeDir = Directions.outputFrom8DirNum(safeDirNum); + + return output.text!({ dir: output[safeDir]!() }); + }, outputStrings: { + ...Directions.outputStrings8Dir, text: { - en: 'Right + Spread', + en: '${dir} (Right) + Spread', }, }, }, { id: 'Arkveld Ex Wyverns Ouroblade Right', type: 'StartsUsing', - // TODO: make this use heading to choose a cardinal direction - netRegex: { source: 'Guardian Arkveld', id: ['AB8D', 'B032'], capture: false }, + netRegex: { source: 'Guardian Arkveld', id: ['AB8D', 'B032'], capture: true }, suppressSeconds: 1, - alertText: (_data, _matches, output) => output.text!(), + alertText: (_data, matches, output) => { + const bossDirNum = Directions.hdgTo8DirNum(parseFloat(matches.heading)); + // Safe area is 270* clockwise of heading (90* ccw) + const safeDirNum = (bossDirNum + 6) % 8; + const safeDir = Directions.outputFrom8DirNum(safeDirNum); + + return output.text!({ dir: output[safeDir]!() }); + }, outputStrings: { + ...Directions.outputStrings8Dir, text: { - en: 'Left + Spread', + en: '${dir} (Left) + Spread', }, }, },