From e2dda9d432484fb9245c5d6d8abccdeb46ebdd19 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Tue, 2 Jun 2026 21:29:18 -0400 Subject: [PATCH 01/54] Add p1 timeline draft and initial trigger file. --- .../data/07-dt/ultimate/dancing_mad.ts | 45 +++++ .../data/07-dt/ultimate/dancing_mad.txt | 186 ++++++++++++++++++ 2 files changed, 231 insertions(+) create mode 100644 ui/raidboss/data/07-dt/ultimate/dancing_mad.ts create mode 100644 ui/raidboss/data/07-dt/ultimate/dancing_mad.txt diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts new file mode 100644 index 0000000000..b481d8e8ca --- /dev/null +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -0,0 +1,45 @@ +import ZoneId from '../../../../../resources/zone_id'; +import { RaidbossData } from '../../../../../types/data'; +import { TriggerSet } from '../../../../../types/trigger'; + +type Phase = 'p1' | 'p2'; +const phases: { [id: string]: Phase } = { + 'C24C': 'p2', // Ultimate Embrace, God Kefka +}; + +//const centerX = 100; +//const centerY = 100; + +export interface Data extends RaidbossData { + // General + phase: Phase | 'unknown'; +} + +const triggerSet: TriggerSet = { + id: 'DancingMadUltimate', + zoneId: ZoneId.DancingMadUltimate, + timelineFile: 'dancing_mad.txt', + initData: () => { + return { + phase: 'p1', + }; + }, + triggers: [ + { + id: 'DMU Phase Tracker', + type: 'StartsUsing', + netRegex: { id: Object.keys(phases) }, + run: (data, matches) => data.phase = phases[matches.id] ?? 'unknown', + }, + ], + timelineReplace: [ + { + 'locale': 'en', + 'replaceText': { + 'Future\'s End/Past\'s End': 'Future/Past\'s End', + }, + }, + ], +}; + +export default triggerSet; \ No newline at end of file diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt new file mode 100644 index 0000000000..f026ff8aa7 --- /dev/null +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt @@ -0,0 +1,186 @@ +### DANCING MAD (ULTIMATE) +# ZoneId: DancingMadUltimate + +# -ii C252 BA9E BAA0 BAAB BA95 BAAD BAD6 BAD8 BAD7 BAD9 +# -p C403:12.1 C24C:216.5 +# -it Kefka + +hideall "--Reset--" +hideall "--sync--" + +0.0 "--Reset--" ActorControl { command: "4000000F" } window 0,100000 jump 0 + +0.0 "--sync--" InCombat { inGameCombat: "1" } window 0,1 + +# TODO: Replace these FFLogs (IINACT uploads) with ACT Network Log timings +### Phase 1 - Kefka +# TODO: Add voiceline capture and headmarker +# https://xivapi.com/NpcYell/?pretty=true +# en (auto-translate): 'This is my first time, so please take it easy!' +#0.0 "--sync--" NpcYell { npcYellId: "" } +12.1 "Revolting Ruin III 1" Ability { id: "C403", source: "Kefka" } window 15,15 +15.2 "Revolting Ruin III 2" Ability { id: "C4E1", source: "Kefka" } +21.3 "Enhanced Thrill Of War II" Ability { id: "C3FD", source: "Kefka" } + +26.0 "Graven Image 1" Ability { id: "BCF2", source: "Kefka" } +31.9 "Pulse Wave" Ability { id: "BAA9", source: "Graven Image" } +34.2 "Mystery Magic" Ability { id: "BA94", source: "Kefka" } +34.2 "Blizzard III Blowout" #Ability { id: ["BA9B", "BA98"], source: "Kefka" } +35.1 "Flagrant Fire III" Ability { id: ["BAA2", "BAA3"], source: "Kefka" } +39.3 "Wave Cannon x4" Ability { id: "BAA8", source: "Graven Image" } +41.4 "Double-Trouble Trap" Ability { id: "BAA6", source: "Kefka" } +43.0 "Explosion x4" Ability { id: "BAAA", source: "Kefka" } +46.7 "Double-Trouble Trap x2" Ability { id: "BAA7", source: "Kefka" } +50.7 "Mystery Magic" Ability { id: "BA94", source: "Kefka" } +50.7 "Thrumming Thunder III" #Ability { id: ["BAA1", "BA9F"], source: "Kefka" } +50.7 "Blizzard III Blowout" #Ability { id: ["BA9B", "BA98"], source: "Kefka" } +59.7 "Light of Judgment" Ability { id: "C622", source: "Kefka" } +62.8 "Hyperdrive 1" #Ability { id: "C24B", source: "Kefka" } +64.8 "Hyperdrive 2" #Ability { id: "C24B", source: "Kefka" } +66.8 "Hyperdrive 3" #Ability { id: "C24B", source: "Kefka" } +72.6 "Enhanced Thrill Of War II" Ability { id: "C3FD", source: "Kefka" } + +77.0 "Graven Image 2" Ability { id: "BCF2", source: "Kefka" } +84.1 "Blizzard III Blowout" Ability { id: ["BA9B", "BA98"], source: "Kefka" } +84.2 "Gravitas x4" Ability { id: "BAAC", source: "Graven Image" } +88.2 "Vitrophyre x4" Ability { id: "BAB0", source: "Graven Image" } +94.3 "Revolting Ruin III 1" Ability { id: "C403", source: "Kefka" } +97.4 "Revolting Ruin III 2" Ability { id: "C4E1", source: "Kefka" } +98.0 "Intemperate Will/Gravitational Wave" Ability { id: ["BAB2", "BAB1"], source: "Graven Image" } +102.7 "Gravitas x4" Ability { id: "BAAC", source: "Graven Image" } +106.7 "Vitrophyre x4" Ability { id: "BAB0", source: "Graven Image" } +111.5 "Intemperate Will/Gravitational Wave" Ability { id: ["BAB2", "BAB1"], source: "Graven Image" } +115.1 "Double-Trouble Trap x2" Ability { id: "BAA7", source: "Kefka" } # NOTE: If it was passed after first set. +129.5 "Light of Judgment" Ability { id: "C622", source: "Kefka" } +132.6 "Hyperdrive 1" #Ability { id: "C24B", source: "Kefka" } +134.6 "Hyperdrive 2" #Ability { id: "C24B", source: "Kefka" } +136.6 "Hyperdrive 3" #Ability { id: "C24B", source: "Kefka" } +148.4 "Tele-Trouncing" Ability { id: "BAB9", source: "Kefka" } +156.3 "Tele-Trouncing 1" Ability { id: "BABA", source: "Kefka" } +159.3 "Tele-Trouncing 2" Ability { id: "BABA", source: "Kefka" } + +160.5 "Graven Image 3" Ability { id: "BCF2", source: "Kefka" } +165.6 "--sync--" Ability { id: "C554", source: "Kefka" } +170.4 "Indulgent Will x4" Ability { id: "BAB5", source: "Graven Image" } +170.4 "Idyllic Will x4" #Ability { id: "BAB6", source: "Graven Image" } +174.7 "--sync--" Ability { id: "C555", source: "Kefka" } +176.7 "Enhanced Thrill Of War II" Ability { id: "C3FD", source: "Kefka" } +183.1 "Mystery Magic" Ability { id: "BA94", source: "Kefka" } +183.1 "Thrumming Thunder III" #Ability { id: ["BAA1", "BA9F"], source: "Kefka" } +183.3 "Indolent Will/Ave Maria" Ability { id: ["BAB4", "BAB3"], source: "Graven Image" } +183.9 "Flagrant Fire III" Ability { id: ["BAA2", "BAA3"], source: "Kefka" } +199.3 "Light of Judgment (enrage?)" Ability { id: "BABB", source: "Kefka" } # Kefka >15% HP + +### Phase 2 - God Kefka +# TODO: Add voiceline +# https://xivapi.com/NpcYell/?pretty=true +# en: 'Yes... I am filled with glorious purpose!' +#200.0 "--sync--" NpcYell { npcYellId: "" } window 200,5 +216.5 "Ultimate Embrace" Ability { id: "C24C", source: "Kefka" } window 220,5 +231.7 "Forsaken" Ability { id: "BABC", source: "Kefka" } +244.9 "The Path of Light 1" Ability { id: "BABE", source: "Kefka" } +245.6 "Spelldriver" #Ability { id: "BAC0", source: "Kefka" } +245.6 "Spellwave" #Ability { id: "BAC2", source: "Kefka" } +245.6 "Spellscatter" #Ability { id: "BAC1", source: "Kefka" } +254.4 "Future's End/Past's End" Ability { id: ["BAD2", "BAD3"], source: "Kefka" } + +254.8 "The Path of Light 2" Ability { id: "BABE", source: "Kefka" } +255.5 "Spellwave" #Ability { id: "BAC2", source: "Kefka" } +255.5 "Spellscatter" #Ability { id: "BAC1", source: "Kefka" } +265.6 "All Things Ending" #Ability { id: ["BACD", "BADD"], source: "Kefka" } +265.7 "The Path of Light" Ability { id: "BABE", source: "Kefka" } +266.4 "Spellwave" #Ability { id: "BAC2", source: "Kefka" } +266.4 "Spelldriver" #Ability { id: "BAC0", source: "Kefka" } +266.4 "Spellscatter" #Ability { id: "BAC1", source: "Kefka" } +275.6 "Future's End/Past's End" Ability { id: ["BAD2", "BAD3"], source: "Kefka" } + +275.6 "The Path of Light 3" Ability { id: "BABE", source: "Kefka" } +276.3 "Spellwave" #Ability { id: "BAC2", source: "Kefka" } +276.3 "Spellscatter" #Ability { id: "BAC1", source: "Kefka" } +276.7 "The River of Light" Ability { id: "BABF", source: "Kefka" } +286.1 "All Things Ending" #Ability { id: ["BACD", "BADD"], source: "Kefka" } +286.4 "The Path of Light" Ability { id: "BABE", source: "Kefka" } +287.1 "Spellwave" #Ability { id: "BAC2", source: "Kefka" } +287.5 "The River of Light" Ability { id: "BABF", source: "Kefka" } +295.5 "Future's End/Past's End" Ability { id: ["BAD2", "BAD3"], source: "Kefka" } + +296.4 "The Path of Light 4" Ability { id: "BABE", source: "Kefka" } +297.1 "Spellwave" #Ability { id: "BAC2", source: "Kefka" } +297.5 "The River of Light" Ability { id: "BABF", source: "Kefka" } +306.7 "All Things Ending" #Ability { id: ["BACD", "BADD"], source: "Kefka" } +307.2 "The Path of Light" Ability { id: "BABE", source: "Kefka" } +308.2 "The River of Light" Ability { id: "BABF", source: "Kefka" } +316.1 "Future's End/Past's End" Ability { id: ["BAD2", "BAD3"], source: "Kefka" } + +317.2 "The Path of Light 5" Ability { id: "BABE", source: "Kefka" } +318.2 "The River of Light" Ability { id: "BABF", source: "Kefka" } +327.8 "All Things Ending" #Ability { id: "BADD", source: "Kefka" } + +# TODO: 2 more sets of towers => aoe => summon trines => left/right => trines + aoes => tankbuster => ... => "enrage", or fake end or phase 3? +# Note: Enrage appears to be at > 0% if getting fake end + +# IGNORED ABILITIES +# C252 Attack: Phase 1 boss attack +# BA9E Blizzard III Blowout: Damage +# BA95 Blizzard III Blowout: VFX +# BAA0 Thrumming Thunder III: VFX +# BAAB Unmitigated Explosion: Failing to soak a tower from BAA8 Wave Cannon +# BAAD Gravitational Explosion: BAB0 Vitrophyre aoe overlaps with BAAC Gravitas puddle +# BAD6 Future's End: Damage (On 1 player) +# BAD7 Past's End: Damage (On 1 player) +# BAD8 Future's End: Damage (On 3 players) +# BAD9 Past's End: Damage (On 3 players) + +# ALL ENCOUNTER ABILITIES +# BA94 Mystery Magic +# BA95 Blizzard III Blowout: VFX (paired with BA98) +# BA98 Blizzard III Blowout: Damage for Fake? (paired with BA95) +# BA9B Blizzard III Blowout: VFX (paired with BA9E) +# BA9E Blizzard III Blowout: Damage (paired with BA9B) +# BA9F Thrumming Thunder III: Damage for Fake? (No corresponding VFX) +# BAA0 Thrumming Thunder III: VFX (paired with BAA1) +# BAA1 Thrumming Thunder III: Damage (paired with BAA0) +# BAA2 Flagrant Fire III: Spread Damage +# BAA3 Flagrant Fire III: Stack Damage +# BAA6 Double-Trouble Trap: VFX +# BAA7 Double-Trouble Trap +# BAA8 Wave Cannon +# BAA9 Pulse Wave +# BAAA Explosion: Cast by towers that are dropped from getting hit by BAA8 Wave Cannon +# BAAB Unmitigated Explosion: Failing to soak a tower from BAA8 Wave Cannon +# BAAC Gravitas +# BAAD Gravitational Explosion: BAB0 Vitrophyre aoe overlaps with BAAC Gravitas puddle +# BAB0 Vitrophyre +# BAB1 Gravitational Wave +# BAB2 Intemperate Will +# BAB3 Ave Maria +# BAB4 Indolent Will +# BAB5 Indulgent Will +# BAB6 Idyllic Will +# BAB9 Tele-Trouncing: VFX +# BABA Tele-Trouncing +# BABB Light of Judgment: P1 Enrage +# BABC Forsaken +# BABE The Path of Light +# BABF The River of Light +# BAC0 Spelldriver +# BAC1 Spellscatter +# BAC2 Spellwave +# BAD2 Future's End: VFX +# BAD3 Past's End: VFX +# BAD6 Future's End: Damage (On 1 player) +# BAD7 Past's End: Damage (On 1 player) +# BAD8 Future's End: Damage (On 3 players) +# BAD9 Past's End: Damage (On 3 players) +# BADC All Things Ending +# BADD All Things Ending +# BCF2 Graven Image +# C24B Hyperdrive +# C24C Ultimate Embrace +# C252 Attack +# C3FD Enhanced Thrill Of War II +# C403 Revolting Ruin III +# C4E1 Revolting Ruin III +# C554 --sync-- +# C555 --sync-- +# C622 Light of Judgment From 3287abc4beba03b23df99cf3c78f740f0e5521df Mon Sep 17 00:00:00 2001 From: Legends0 Date: Tue, 2 Jun 2026 21:35:08 -0400 Subject: [PATCH 02/54] lint --- ui/raidboss/data/07-dt/ultimate/dancing_mad.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index b481d8e8ca..4603456ebb 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -7,8 +7,8 @@ const phases: { [id: string]: Phase } = { 'C24C': 'p2', // Ultimate Embrace, God Kefka }; -//const centerX = 100; -//const centerY = 100; +// const centerX = 100; +// const centerY = 100; export interface Data extends RaidbossData { // General @@ -33,7 +33,7 @@ const triggerSet: TriggerSet = { }, ], timelineReplace: [ - { + { 'locale': 'en', 'replaceText': { 'Future\'s End/Past\'s End': 'Future/Past\'s End', @@ -42,4 +42,4 @@ const triggerSet: TriggerSet = { ], }; -export default triggerSet; \ No newline at end of file +export default triggerSet; From 696de11d30ed7d6ff397d0abfd7698e15a76fe49 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Tue, 2 Jun 2026 23:41:10 -0400 Subject: [PATCH 03/54] adjust p1 to act network log --- .../data/07-dt/ultimate/dancing_mad.txt | 124 +++++++++--------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt index f026ff8aa7..ace3fc7fc5 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt @@ -1,8 +1,8 @@ ### DANCING MAD (ULTIMATE) # ZoneId: DancingMadUltimate -# -ii C252 BA9E BAA0 BAAB BA95 BAAD BAD6 BAD8 BAD7 BAD9 -# -p C403:12.1 C24C:216.5 +# -ii C252 BA9E BAA0 BAAB BA95 BAAF BAAD BAD6 BAD8 BAD7 BAD9 +# -p C403:15.6 C24C:216.5 # -it Kefka hideall "--Reset--" @@ -12,70 +12,68 @@ hideall "--sync--" 0.0 "--sync--" InCombat { inGameCombat: "1" } window 0,1 -# TODO: Replace these FFLogs (IINACT uploads) with ACT Network Log timings ### Phase 1 - Kefka -# TODO: Add voiceline capture and headmarker -# https://xivapi.com/NpcYell/?pretty=true +# TODO: Add voiceline sync? # en (auto-translate): 'This is my first time, so please take it easy!' -#0.0 "--sync--" NpcYell { npcYellId: "" } -12.1 "Revolting Ruin III 1" Ability { id: "C403", source: "Kefka" } window 15,15 -15.2 "Revolting Ruin III 2" Ability { id: "C4E1", source: "Kefka" } -21.3 "Enhanced Thrill Of War II" Ability { id: "C3FD", source: "Kefka" } - -26.0 "Graven Image 1" Ability { id: "BCF2", source: "Kefka" } -31.9 "Pulse Wave" Ability { id: "BAA9", source: "Graven Image" } -34.2 "Mystery Magic" Ability { id: "BA94", source: "Kefka" } -34.2 "Blizzard III Blowout" #Ability { id: ["BA9B", "BA98"], source: "Kefka" } -35.1 "Flagrant Fire III" Ability { id: ["BAA2", "BAA3"], source: "Kefka" } -39.3 "Wave Cannon x4" Ability { id: "BAA8", source: "Graven Image" } -41.4 "Double-Trouble Trap" Ability { id: "BAA6", source: "Kefka" } -43.0 "Explosion x4" Ability { id: "BAAA", source: "Kefka" } -46.7 "Double-Trouble Trap x2" Ability { id: "BAA7", source: "Kefka" } -50.7 "Mystery Magic" Ability { id: "BA94", source: "Kefka" } -50.7 "Thrumming Thunder III" #Ability { id: ["BAA1", "BA9F"], source: "Kefka" } -50.7 "Blizzard III Blowout" #Ability { id: ["BA9B", "BA98"], source: "Kefka" } -59.7 "Light of Judgment" Ability { id: "C622", source: "Kefka" } -62.8 "Hyperdrive 1" #Ability { id: "C24B", source: "Kefka" } -64.8 "Hyperdrive 2" #Ability { id: "C24B", source: "Kefka" } -66.8 "Hyperdrive 3" #Ability { id: "C24B", source: "Kefka" } -72.6 "Enhanced Thrill Of War II" Ability { id: "C3FD", source: "Kefka" } - -77.0 "Graven Image 2" Ability { id: "BCF2", source: "Kefka" } -84.1 "Blizzard III Blowout" Ability { id: ["BA9B", "BA98"], source: "Kefka" } -84.2 "Gravitas x4" Ability { id: "BAAC", source: "Graven Image" } -88.2 "Vitrophyre x4" Ability { id: "BAB0", source: "Graven Image" } -94.3 "Revolting Ruin III 1" Ability { id: "C403", source: "Kefka" } -97.4 "Revolting Ruin III 2" Ability { id: "C4E1", source: "Kefka" } -98.0 "Intemperate Will/Gravitational Wave" Ability { id: ["BAB2", "BAB1"], source: "Graven Image" } -102.7 "Gravitas x4" Ability { id: "BAAC", source: "Graven Image" } -106.7 "Vitrophyre x4" Ability { id: "BAB0", source: "Graven Image" } -111.5 "Intemperate Will/Gravitational Wave" Ability { id: ["BAB2", "BAB1"], source: "Graven Image" } -115.1 "Double-Trouble Trap x2" Ability { id: "BAA7", source: "Kefka" } # NOTE: If it was passed after first set. -129.5 "Light of Judgment" Ability { id: "C622", source: "Kefka" } -132.6 "Hyperdrive 1" #Ability { id: "C24B", source: "Kefka" } -134.6 "Hyperdrive 2" #Ability { id: "C24B", source: "Kefka" } -136.6 "Hyperdrive 3" #Ability { id: "C24B", source: "Kefka" } -148.4 "Tele-Trouncing" Ability { id: "BAB9", source: "Kefka" } -156.3 "Tele-Trouncing 1" Ability { id: "BABA", source: "Kefka" } -159.3 "Tele-Trouncing 2" Ability { id: "BABA", source: "Kefka" } - -160.5 "Graven Image 3" Ability { id: "BCF2", source: "Kefka" } -165.6 "--sync--" Ability { id: "C554", source: "Kefka" } -170.4 "Indulgent Will x4" Ability { id: "BAB5", source: "Graven Image" } -170.4 "Idyllic Will x4" #Ability { id: "BAB6", source: "Graven Image" } -174.7 "--sync--" Ability { id: "C555", source: "Kefka" } -176.7 "Enhanced Thrill Of War II" Ability { id: "C3FD", source: "Kefka" } -183.1 "Mystery Magic" Ability { id: "BA94", source: "Kefka" } -183.1 "Thrumming Thunder III" #Ability { id: ["BAA1", "BA9F"], source: "Kefka" } -183.3 "Indolent Will/Ave Maria" Ability { id: ["BAB4", "BAB3"], source: "Graven Image" } -183.9 "Flagrant Fire III" Ability { id: ["BAA2", "BAA3"], source: "Kefka" } -199.3 "Light of Judgment (enrage?)" Ability { id: "BABB", source: "Kefka" } # Kefka >15% HP +10.6 "--sync--" StartsUsing { id: "C403", source: "Kefka" } window 20,10 +15.6 "Revolting Ruin III 1" Ability { id: "C403", source: "Kefka" } +18.7 "Revolting Ruin III 2" Ability { id: "C4E1", source: "Kefka" } +24.8 "--sync--" Ability { id: "C3FD", source: "Kefka" } + +29.2 "Graven Image 1" Ability { id: "BCF2", source: "Kefka" } +35.1 "Pulse Wave" Ability { id: "BAA9", source: "Graven Image" } +37.4 "Mystery Magic" Ability { id: "BA94", source: "Kefka" } +37.4 "Blizzard III Blowout" #Ability { id: ["BA9B", "BA98"], source: "Kefka" } +38.3 "Flagrant Fire III" Ability { id: ["BAA2", "BAA3"], source: "Kefka" } +42.5 "Wave Cannon x4" Ability { id: "BAA8", source: "Graven Image" } +44.6 "Double-trouble Trap" Ability { id: "BAA6", source: "Kefka" } +46.0 "Explosion x4" Ability { id: "BAAA", source: "Kefka" } +49.7 "Double-trouble Trap x2" Ability { id: "BAA7", source: "Kefka" } +53.7 "Mystery Magic" Ability { id: "BA94", source: "Kefka" } +53.7 "Thrumming Thunder III" #Ability { id: ["BAA1", "BA9F"], source: "Kefka" } +53.7 "Blizzard III Blowout" #Ability { id: ["BA9B", "BA98"], source: "Kefka" } +62.7 "Light of Judgment" Ability { id: "C622", source: "Kefka" } +65.8 "Hyperdrive 1" #Ability { id: "C24B", source: "Kefka" } +67.8 "Hyperdrive 2" #Ability { id: "C24B", source: "Kefka" } +69.8 "Hyperdrive 3" #Ability { id: "C24B", source: "Kefka" } +75.6 "--sync--" Ability { id: "C3FD", source: "Kefka" } + +80.0 "Graven Image 2" Ability { id: "BCF2", source: "Kefka" } +87.1 "Blizzard III Blowout" #Ability { id: ["BA9B", "BA98"], source: "Kefka" } +87.2 "Gravitas x4" Ability { id: "BAAC", source: "Graven Image" } +91.2 "Vitrophyre x4" Ability { id: "BAB0", source: "Graven Image" } +97.1 "Revolting Ruin III 1" Ability { id: "C403", source: "Kefka" } +100.2 "Revolting Ruin III 2" Ability { id: "C4E1", source: "Kefka" } +101.1 "Intemperate Will/Gravitational Wave" Ability { id: ["BAB2", "BAB1"], source: "Graven Image" } +105.8 "Gravitas x4" Ability { id: "BAAC", source: "Graven Image" } +109.8 "Vitrophyre x4" Ability { id: "BAB0", source: "Graven Image" } +114.4 "Intemperate Will/Gravitational Wave" Ability { id: ["BAB2", "BAB1"], source: "Graven Image" } +118.9 "Double-Trouble Trap x2" Ability { id: "BAA7", source: "Kefka" } # NOTE: If it was passed after first set. +121.3 "Gravity III" #Ability { id: "BAAF", source: "Kefka" } # TODO: Adjust timing/wording to puddles safe to pop, make it a duration? +132.4 "Light of Judgment" Ability { id: "C622", source: "Kefka" } +135.6 "Hyperdrive 1" #Ability { id: "C24B", source: "Kefka" } +137.7 "Hyperdrive 2" #Ability { id: "C24B", source: "Kefka" } +139.7 "Hyperdrive 3" #Ability { id: "C24B", source: "Kefka" } +151.5 "Tele-trouncing" Ability { id: "BAB9", source: "Kefka" } +159.4 "Tele-trouncing 1" Ability { id: "BABA", source: "Kefka" } +162.4 "Tele-trouncing 2" Ability { id: "BABA", source: "Kefka" } + +163.6 "Graven Image 3" Ability { id: "BCF2", source: "Kefka" } +168.7 "--sync--" Ability { id: "C554", source: "Kefka" } +173.4 "Indulgent Will x4" Ability { id: "BAB5", source: "Graven Image" } +173.4 "Idyllic Will x4" #Ability { id: "BAB6", source: "Graven Image" } +177.7 "--sync--" Ability { id: "C555", source: "Kefka" } +179.7 "--sync--" Ability { id: "C3FD", source: "Kefka" } +186.3 "Mystery Magic" Ability { id: "BA94", source: "Kefka" } +186.3 "Thrumming Thunder III" #Ability { id: ["BAA1", "BA9F"], source: "Kefka" } +186.3 "Indolent Will/Ave Maria" #Ability { id: ["BAB4", "BAB3"], source: "Graven Image" } +187.1 "Flagrant Fire III" Ability { id: ["BAA2", "BAA3"], source: "Kefka" } +202.5 "Light of Judgment (enrage?)" Ability { id: "BABB", source: "Kefka" } # Kefka >15% HP ### Phase 2 - God Kefka -# TODO: Add voiceline -# https://xivapi.com/NpcYell/?pretty=true +# TODO: Update with network log, this uses FFLOGS uploads from IINACT +# TODO: Add voiceline sync? # en: 'Yes... I am filled with glorious purpose!' -#200.0 "--sync--" NpcYell { npcYellId: "" } window 200,5 216.5 "Ultimate Embrace" Ability { id: "C24C", source: "Kefka" } window 220,5 231.7 "Forsaken" Ability { id: "BABC", source: "Kefka" } 244.9 "The Path of Light 1" Ability { id: "BABE", source: "Kefka" } @@ -126,6 +124,7 @@ hideall "--sync--" # BAA0 Thrumming Thunder III: VFX # BAAB Unmitigated Explosion: Failing to soak a tower from BAA8 Wave Cannon # BAAD Gravitational Explosion: BAB0 Vitrophyre aoe overlaps with BAAC Gravitas puddle +# BAAF Gravity III: Soaking the Gravitas puddles at the correct time # BAD6 Future's End: Damage (On 1 player) # BAD7 Past's End: Damage (On 1 player) # BAD8 Future's End: Damage (On 3 players) @@ -149,6 +148,7 @@ hideall "--sync--" # BAAA Explosion: Cast by towers that are dropped from getting hit by BAA8 Wave Cannon # BAAB Unmitigated Explosion: Failing to soak a tower from BAA8 Wave Cannon # BAAC Gravitas +# BAAF Gravity III: Soaking the Gravitas puddles at the correct time # BAAD Gravitational Explosion: BAB0 Vitrophyre aoe overlaps with BAAC Gravitas puddle # BAB0 Vitrophyre # BAB1 Gravitational Wave @@ -178,7 +178,7 @@ hideall "--sync--" # C24B Hyperdrive # C24C Ultimate Embrace # C252 Attack -# C3FD Enhanced Thrill Of War II +# C3FD --sync-- # C403 Revolting Ruin III # C4E1 Revolting Ruin III # C554 --sync-- From d7ec929f08fd15273ec29fd2a59094c909311ffa Mon Sep 17 00:00:00 2001 From: Legends0 Date: Wed, 3 Jun 2026 01:01:39 -0400 Subject: [PATCH 04/54] change a sync to middle --- ui/raidboss/data/07-dt/ultimate/dancing_mad.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt index ace3fc7fc5..b0bdaf52e3 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt @@ -18,7 +18,7 @@ hideall "--sync--" 10.6 "--sync--" StartsUsing { id: "C403", source: "Kefka" } window 20,10 15.6 "Revolting Ruin III 1" Ability { id: "C403", source: "Kefka" } 18.7 "Revolting Ruin III 2" Ability { id: "C4E1", source: "Kefka" } -24.8 "--sync--" Ability { id: "C3FD", source: "Kefka" } +24.8 "--middle--" Ability { id: "C3FD", source: "Kefka" } 29.2 "Graven Image 1" Ability { id: "BCF2", source: "Kefka" } 35.1 "Pulse Wave" Ability { id: "BAA9", source: "Graven Image" } @@ -36,7 +36,7 @@ hideall "--sync--" 65.8 "Hyperdrive 1" #Ability { id: "C24B", source: "Kefka" } 67.8 "Hyperdrive 2" #Ability { id: "C24B", source: "Kefka" } 69.8 "Hyperdrive 3" #Ability { id: "C24B", source: "Kefka" } -75.6 "--sync--" Ability { id: "C3FD", source: "Kefka" } +75.6 "--middle--" Ability { id: "C3FD", source: "Kefka" } 80.0 "Graven Image 2" Ability { id: "BCF2", source: "Kefka" } 87.1 "Blizzard III Blowout" #Ability { id: ["BA9B", "BA98"], source: "Kefka" } @@ -63,7 +63,7 @@ hideall "--sync--" 173.4 "Indulgent Will x4" Ability { id: "BAB5", source: "Graven Image" } 173.4 "Idyllic Will x4" #Ability { id: "BAB6", source: "Graven Image" } 177.7 "--sync--" Ability { id: "C555", source: "Kefka" } -179.7 "--sync--" Ability { id: "C3FD", source: "Kefka" } +179.7 "--middle--" Ability { id: "C3FD", source: "Kefka" } 186.3 "Mystery Magic" Ability { id: "BA94", source: "Kefka" } 186.3 "Thrumming Thunder III" #Ability { id: ["BAA1", "BA9F"], source: "Kefka" } 186.3 "Indolent Will/Ave Maria" #Ability { id: ["BAB4", "BAB3"], source: "Graven Image" } @@ -178,7 +178,7 @@ hideall "--sync--" # C24B Hyperdrive # C24C Ultimate Embrace # C252 Attack -# C3FD --sync-- +# C3FD --sync--: Boss jumps to middle # C403 Revolting Ruin III # C4E1 Revolting Ruin III # C554 --sync-- From 83f0c7d998791f353051c4289730072848e350b9 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Wed, 3 Jun 2026 02:04:48 -0400 Subject: [PATCH 05/54] add initial triggers --- .../data/07-dt/ultimate/dancing_mad.ts | 300 +++++++++++++++++- 1 file changed, 299 insertions(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index 4603456ebb..927e828c33 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -1,6 +1,8 @@ +import Outputs from '../../../../../resources/outputs'; +import { Responses } from '../../../../../resources/responses'; import ZoneId from '../../../../../resources/zone_id'; import { RaidbossData } from '../../../../../types/data'; -import { TriggerSet } from '../../../../../types/trigger'; +import { OutputStrings, TriggerSet } from '../../../../../types/trigger'; type Phase = 'p1' | 'p2'; const phases: { [id: string]: Phase } = { @@ -13,8 +15,112 @@ const phases: { [id: string]: Phase } = { export interface Data extends RaidbossData { // General phase: Phase | 'unknown'; + // Phase 1 + fireMarker?: string; + isFireTrue?: boolean; + isIceTrue?: boolean; + isThunderTrue?: boolean; } +const headMarkerData = { + // Phase 1 Boss + 'fakeFire': '02A1', + 'trueFire': '02A2', + 'fakeIce': '02A3', + 'trueIce': '02A4', + 'fakeThunder': '02A5', + 'trueThunder': '02A6', + // Phase 1 Players + 'tankbuster': '00DA', // Revolting Ruin III tankbuster + 'dorito': '007F', // spread (real) or stack (fake) + 'stack': '0080', // spread (fake) or stack (real) +} as const; + +const mysteryMagicOutputStrings: OutputStrings = { + spread: Outputs.spread, + stack: { + en: 'Stack', + de: 'Stacken', + fr: 'Packez-vous', + ja: 'スタック', + cn: '集合', + ko: '집합', + tc: '集合', + }, + trueThunder: { + en: 'True Thunder', + de: 'Wahrer Blitz', + fr: 'Vraie foudre', + ja: '真サンダガ', + cn: '真雷', + ko: '진실 선더가', + tc: '真雷', + }, + fakeThunder: { + en: 'Fake Thunder', + de: 'Falscher Blitz', + fr: 'Fausse foudre', + ja: 'にせサンダガ', + cn: '假雷', + ko: '거짓 선더가', + tc: '假雷', + }, + trueIce: { + en: 'True Ice', + de: 'Wahres Eis', + fr: 'Vraie glace', + ja: '真ブリザガ', + cn: '真冰', + ko: '진실 블리자가', + tc: '真冰', + }, + fakeIce: { + en: 'Fake Ice', + de: 'Falsches Eis', + fr: 'Fausse glace', + ja: 'にせブリザガ', + cn: '假冰', + ko: '거짓 블리자가', + tc: '假冰', + }, + stackTrueIce: { + en: '${mech} + ${ice}', + }, + stackFakeIce: { + en: '${mech} + ${ice}', + }, + spreadTrueIce: { + en: '${mech} + ${ice}', + }, + spreadFakeIce: { + en: '${mech} + ${ice}', + }, + trueIceTrueThunder: { + en: '${ice} + ${thunder}', + }, + fakeIceTrueThunder: { + en: '${ice} + ${thunder}', + }, + trueIceFakeThunder: { + en: '${ice} + ${thunder}', + }, + fakeIceFakeThunder: { + en: '${ice} + ${thunder}', + }, + stackTrueThunder: { + en: '${mech} + ${thunder}', + }, + stackFakeThunder: { + en: '${mech} + ${thunder}', + }, + spreadTrueThunder: { + en: '${mech} + ${thunder}', + }, + spreadFakeThunder: { + en: '${mech} + ${thunder}', + }, +}; + const triggerSet: TriggerSet = { id: 'DancingMadUltimate', zoneId: ZoneId.DancingMadUltimate, @@ -31,6 +137,198 @@ const triggerSet: TriggerSet = { netRegex: { id: Object.keys(phases) }, run: (data, matches) => data.phase = phases[matches.id] ?? 'unknown', }, + { + id: 'DMU P1 Revolting Ruin III', + // Tankbuster targets highest enmity then the nearest player that is not the highest enmity + // Offtank can provoke to cause the main tank to take both hits so long as main tank is closest + type: 'HeadMarker', + netRegex: { id: headMarkerData['tankbuster'], capture: true }, + response: Responses.tankBuster(), + }, + { + id: 'DMU P1 Mystery Magic Collect', + type: 'HeadMarker', + netRegex: { + id: [ + headMarkerData['trueFire'], + headMarkerData['trueIce'], + headMarkerData['trueThunder'], + headMarkerData['fakeFire'], + headMarkerData['fakeIce'], + headMarkerData['fakeThunder'], + ], + capture: true, + }, + run: (data, matches) => { + switch (matches.id) { + case headMarkerData['trueFire']: + data.isFireTrue = true; + return; + case headMarkerData['fakeFire']: + data.isFireTrue = false; + return; + case headMarkerData['trueIce']: + data.isIceTrue = true; + return; + case headMarkerData['fakeIce']: + data.isIceTrue = false; + return; + case headMarkerData['trueThunder']: + data.isThunderTrue = true; + return; + case headMarkerData['fakeThunder']: + data.isThunderTrue = false; + return; + } + }, + }, + { + id: 'DMU P1 Fire Head Marker Collect', + type: 'HeadMarker', + netRegex: { id: [headMarkerData['dorito'], headMarkerData['stack']], capture: true }, + suppressSeconds: 2, + run: (data, matches) => data.fireMarker = matches.id, + }, + { + id: 'DMU P1 Mystery Magic Ice and Fire', + // Set 1: Only Ice and Fire should be set + type: 'StartsUsing', + netRegex: { id: 'BA94', source: 'Kefka', capture: false }, + condition: (data) => { + return data.isIceTrue !== undefined && data.isFireTrue !== undefined; + }, + infoText: (data, _matches, output) => { + const fireMarker = data.fireMarker; + if ( + (fireMarker === headMarkerData['dorito'] && data.isFireTrue) || + (fireMarker === headMarkerData['stack'] && !data.isFireTrue) + ) + return data.isIceTrue + ? output.spreadTrueIce!({ mech: output.spread!(), ice: output.trueIce!() }) + : output.spreadFakeIce!({ mech: output.spread!(), ice: output.fakeIce!() }); + + if ( + (fireMarker === headMarkerData['dorito'] && !data.isFireTrue) || + (fireMarker === headMarkerData['stack'] && data.isFireTrue) + ) { + return data.isIceTrue + ? output.stackTrueIce!({ mech: output.stack!(), ice: output.trueIce!() }) + : output.stackFakeIce!({ mech: output.stack!(), ice: output.fakeIce!() }); + } + }, + outputStrings: mysteryMagicOutputStrings, + }, + { + id: 'DMU P1 Mystery Magic Ice and Thunder', + // Set 2: Only Ice and Thunder should be set + type: 'StartsUsing', + netRegex: { id: 'BA94', source: 'Kefka', capture: false }, + condition: (data) => { + return data.isIceTrue !== undefined && data.isThunderTrue !== undefined; + }, + infoText: (data, _matches, output) => { + if (data.isThunderTrue) { + return data.isIceTrue + ? output.trueIceTrueThunder!({ + ice: output.trueIce!(), + thunder: output.trueThunder!(), + }) + : output.fakeIceTrueThunder!({ + ice: output.fakeIce!(), + thunder: output.trueThunder!(), + }); + } + return data.isIceTrue + ? output.trueIceTrueThunder!({ + ice: output.trueIce!(), + thunder: output.fakeThunder!(), + }) + : output.fakeIceFakeThunder!({ + ice: output.fakeIce!(), + thunder: output.fakeThunder!(), + }); + }, + outputStrings: mysteryMagicOutputStrings, + }, + { + id: 'DMU P1 Mystery Magic Fire and Thunder', + // Set 2: Only Ice and Thunder should be set + type: 'StartsUsing', + netRegex: { id: 'BA94', source: 'Kefka', capture: false }, + condition: (data) => { + return data.isFireTrue !== undefined && data.isThunderTrue !== undefined; + }, + infoText: (data, _matches, output) => { + const fireMarker = data.fireMarker; + if ( + (fireMarker === headMarkerData['dorito'] && data.isFireTrue) || + (fireMarker === headMarkerData['stack'] && !data.isFireTrue) + ) + return data.isThunderTrue + ? output.spreadTrueThunder!({ + mech: output.spread!(), + thunder: output.trueThunder!(), + }) + : output.spreadFakeThunder!({ + mech: output.spread!(), + thunder: output.fakeThunder!(), + }); + + if ( + (fireMarker === headMarkerData['dorito'] && !data.isFireTrue) || + (fireMarker === headMarkerData['stack'] && data.isFireTrue) + ) { + return data.isThunderTrue + ? output.stackTrueThunder!({ + mech: output.stack!(), + thunder: output.trueThunder!(), + }) + : output.stackFakeThunder!({ + mech: output.stack!(), + thunder: output.fakeThunder!(), + }); + } + }, + outputStrings: mysteryMagicOutputStrings, + }, + { + id: 'DMU P1 Mystery Magic Cleanup', + // C622 Light of Judgment to reset for the Graven Image 2 + type: 'StartsUsing', + netRegex: { id: ['BA94', 'C622'], source: 'Kefka', capture: false }, + run: (data) => { + delete data.isFireTrue; + delete data.isIceTrue; + delete data.isThunderTrue; + delete data.fireMarker; + }, + }, + { + id: 'DMU P1 Light of Judgment', + type: 'StartsUsing', + netRegex: { id: 'C622', source: 'Kefka', capture: false }, + response: Responses.bigAoe(), + }, + { + id: 'DMU P1 Hyperdrive', + // This hits three times + type: 'StartsUsing', + netRegex: { id: 'C24B', source: 'Kefka' }, + suppressSeconds: 5, + response: Responses.tankBuster(), + }, + { + id: 'DMU P1 Intemperate Will', + type: 'StartsUsing', + netRegex: { id: 'BAB2', source: 'Graven Image', capture: false }, + response: Responses.goWest(), + }, + { + id: 'DMU P1 Gravitational Wave', + type: 'StartsUsing', + netRegex: { id: 'BAB1', source: 'Graven Image', capture: false }, + response: Responses.goEast(), + }, ], timelineReplace: [ { From bcd18ec837bd973e5e2cc9fbfa7ba30b8dbb06f0 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Wed, 3 Jun 2026 19:57:34 -0400 Subject: [PATCH 06/54] Hyperdrive tankbuster update + Remove Intemperate Will/Gravitational Wave --- ui/raidboss/data/07-dt/ultimate/dancing_mad.ts | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index 927e828c33..de738b059a 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -312,23 +312,12 @@ const triggerSet: TriggerSet = { { id: 'DMU P1 Hyperdrive', // This hits three times + // Occurs 3.1s after C622 Light of Judgment, which is a 5s cast type: 'StartsUsing', - netRegex: { id: 'C24B', source: 'Kefka' }, - suppressSeconds: 5, + netRegex: { id: 'C622', source: 'Kefka', capture: true }, + delaySeconds: (_data, matches) => parseFloat(matches.castTime) - 3, // Gives 5.1s delay response: Responses.tankBuster(), }, - { - id: 'DMU P1 Intemperate Will', - type: 'StartsUsing', - netRegex: { id: 'BAB2', source: 'Graven Image', capture: false }, - response: Responses.goWest(), - }, - { - id: 'DMU P1 Gravitational Wave', - type: 'StartsUsing', - netRegex: { id: 'BAB1', source: 'Graven Image', capture: false }, - response: Responses.goEast(), - }, ], timelineReplace: [ { From fd08bb0a19d471cf5e9113cf82572284a2f72fb0 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Wed, 3 Jun 2026 20:11:49 -0400 Subject: [PATCH 07/54] adjust delay --- ui/raidboss/data/07-dt/ultimate/dancing_mad.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index de738b059a..c629cd1700 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -315,7 +315,7 @@ const triggerSet: TriggerSet = { // Occurs 3.1s after C622 Light of Judgment, which is a 5s cast type: 'StartsUsing', netRegex: { id: 'C622', source: 'Kefka', capture: true }, - delaySeconds: (_data, matches) => parseFloat(matches.castTime) - 3, // Gives 5.1s delay + delaySeconds: (_data, matches) => parseFloat(matches.castTime) - 2, // Result in ~5.1s warning response: Responses.tankBuster(), }, ], From 2015b82ed24cf62c8a15c4dfd24898e403e19c2d Mon Sep 17 00:00:00 2001 From: Legends0 Date: Wed, 3 Jun 2026 21:03:57 -0400 Subject: [PATCH 08/54] add double-trouble trap triggers --- .../data/07-dt/ultimate/dancing_mad.ts | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index c629cd1700..b39d64dc00 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -20,6 +20,7 @@ export interface Data extends RaidbossData { isFireTrue?: boolean; isIceTrue?: boolean; isThunderTrue?: boolean; + doubleTroubleTrapTargets: string[]; } const headMarkerData = { @@ -128,6 +129,7 @@ const triggerSet: TriggerSet = { initData: () => { return { phase: 'p1', + doubleTroubleTrapTargets: [], }; }, triggers: [ @@ -303,6 +305,143 @@ const triggerSet: TriggerSet = { delete data.fireMarker; }, }, + { + id: 'DMU P1 Double-trouble Trap Collect', + type: 'GainsEffect', + netRegex: { effectId: '13D6', capture: true }, + run: (data, matches) => data.doubleTroubleTrapTargets.push(matches.target), + }, + { + id: 'DMU P1 Double-trouble Trap Early', + // Times are 5s, 68s, and 49s + type: 'GainsEffect', + netRegex: { effectId: '13D6', capture: true }, + delaySeconds: 0.1, + suppressSeconds: 1, + infoText: (data, matches, output) => { + // Ignore first set + if (parseFloat(matches.duration) < 6) + return; + const target1 = data.doubleTroubleTrapTargets[0]; + if (data.doubleTroubleTrapTargets.length === 2) { + const target2 = data.doubleTroubleTrapTargets[1]; + + if (target1 === data.me) + return output.trapOnYouPlayer!({ + player: data.party.member(target1), + }); + + if (target2 === data.me) + return output.trapOnYouPlayer!({ + player: data.party.member(target2), + }); + + return output.trapOnPlayers!({ + player1: data.party.member(target1), + player2: data.party.member(target2), + }); + } + + if (target1 === data.me) + return output.trapOnYou!(); + return output.trapOnPlayer!({ + player: data.party.member(target1), + }); + }, + outputStrings: { + trapOnYou: { + en: 'Trap on YOU (later)', + }, + trapOnYouPlayer: { + en: 'Traps on YOU, ${player} (later)', + }, + trapOnPlayer: { + en: 'Trap on ${player} (later)', + }, + trapOnPlayers: { + en: 'Traps on ${player1}, ${player2} (later)', + }, + }, + }, + { + id: 'DMU P1 Double-trouble Trap', + type: 'GainsEffect', + netRegex: { effectId: '13D6', capture: true }, + delaySeconds: (_data, matches) => { + const duration = parseFloat(matches.duration); + // Giving a 5s warning + // Second Set + if (duration > 67) + return 63; + + // Last set + if (duration > 48) + return 44; + + // First set + return 0.1; + }, + suppressSeconds: 1, + response: (data, _matches, output) => { + // cactbot-builtin-response + output.responseOutputStrings = { + trapOnYou: { + en: 'Trap on YOU', + }, + trapOnYouPlayer: { + en: 'Traps on YOU, ${player}', + }, + trapOnPlayer: { + en: 'Trap on ${player}', + }, + trapOnPlayers: { + en: 'Traps on ${player1}, ${player2}', + }, + }; + + const target1 = data.doubleTroubleTrapTargets[0]; + if (data.doubleTroubleTrapTargets.length === 2) { + const target2 = data.doubleTroubleTrapTargets[1]; + + if (target1 === data.me) + return { + alertText: output.trapOnYouPlayer!({ + player: data.party.member(target1), + }), + }; + + if (target2 === data.me) + return { + alertText: output.trapOnYouPlayer!({ + player: data.party.member(target2), + }), + }; + + return { + infoText: output.trapOnPlayers!({ + player1: data.party.member(target1), + player2: data.party.member(target2), + }), + }; + } + + if (target1 === data.me) + return { alertText: output.trapOnYou!() }; + return { + infoText: output.trapOnPlayer!({ + player: data.party.member(target1), + }), + }; + }, + }, + { + // Debuffs should expire before the new ones come out + id: 'DMU P1 Double-trouble Trap Cleanup', + type: 'LosesEffect', + netRegex: { effectId: '13D6', capture: false }, + suppressSeconds: 1, + run: (data) => data.doubleTroubleTrapTargets = [], + }, { id: 'DMU P1 Light of Judgment', type: 'StartsUsing', From 47cc7f5d01bc2ef682c3f4846baa25f1e4776e44 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Wed, 3 Jun 2026 23:49:32 -0400 Subject: [PATCH 09/54] tele-portents, add trap outputs, ice only output --- .../data/07-dt/ultimate/dancing_mad.ts | 428 ++++++++++++++++-- 1 file changed, 385 insertions(+), 43 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index b39d64dc00..cdd7b5145d 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -1,9 +1,15 @@ +import Conditions from '../../../../../resources/conditions'; import Outputs from '../../../../../resources/outputs'; import { Responses } from '../../../../../resources/responses'; import ZoneId from '../../../../../resources/zone_id'; import { RaidbossData } from '../../../../../types/data'; import { OutputStrings, TriggerSet } from '../../../../../types/trigger'; +// TODO: P1 Tethers +// TODO: P1 Halfroom Cleaves +// TODO: P1 Replace Mystery Magic Ice Only with tether combination +// TODO: P1 Tele-Portent configuration options + type Phase = 'p1' | 'p2'; const phases: { [id: string]: Phase } = { 'C24C': 'p2', // Ultimate Embrace, God Kefka @@ -21,6 +27,8 @@ export interface Data extends RaidbossData { isIceTrue?: boolean; isThunderTrue?: boolean; doubleTroubleTrapTargets: string[]; + myTelePortent1?: 'up' | 'down' | 'right' | 'left'; + myTelePortent2?: 'up' | 'down' | 'right' | 'left'; } const headMarkerData = { @@ -122,6 +130,36 @@ const mysteryMagicOutputStrings: OutputStrings = { }, }; +const trapEarlyOutputStrings: OutputStrings = { + trapOnYou: { + en: 'Trap on YOU (later)', + }, + trapOnYouPlayer: { + en: 'Traps on YOU, ${player} (later)', + }, + trapOnPlayer: { + en: 'Trap on ${player} (later)', + }, + trapOnPlayers: { + en: 'Traps on ${player1}, ${player2} (later)', + }, +}; + +const trapOutputStrings: OutputStrings = { + trapOnYou: { + en: 'Trap on YOU ', + }, + trapOnYouPlayer: { + en: 'Traps on YOU, ${player}', + }, + trapOnPlayer: { + en: 'Trap on ${player}', + }, + trapOnPlayers: { + en: 'Traps on ${player1}, ${player2}', + }, +}; + const triggerSet: TriggerSet = { id: 'DancingMadUltimate', zoneId: ZoneId.DancingMadUltimate, @@ -129,6 +167,7 @@ const triggerSet: TriggerSet = { initData: () => { return { phase: 'p1', + // Phase 1 doubleTroubleTrapTargets: [], }; }, @@ -252,9 +291,30 @@ const triggerSet: TriggerSet = { }, outputStrings: mysteryMagicOutputStrings, }, + { + id: 'DMU P1 Mystery Magic Ice Only', + // Occurs between Set 2 and Set 3 + // BA95 Blizzard Blowout III cast + type: 'StartsUsing', + netRegex: { id: 'BA95', source: 'Kefka', capture: false }, + condition: (data) => { + if ( + data.isIceTrue !== undefined && + data.isThunderTrue === undefined && + data.isFireTrue === undefined + ) + return true; + }, + infoText: (data, _matches, output) => { + return data.isIceTrue + ? output.trueIce!() + : output.fakeIce!(); + }, + outputStrings: mysteryMagicOutputStrings, + }, { id: 'DMU P1 Mystery Magic Fire and Thunder', - // Set 2: Only Ice and Thunder should be set + // Set 3: Only Fire and Thunder should be set type: 'StartsUsing', netRegex: { id: 'BA94', source: 'Kefka', capture: false }, condition: (data) => { @@ -307,21 +367,22 @@ const triggerSet: TriggerSet = { }, { id: 'DMU P1 Double-trouble Trap Collect', + // Times are 5s, 68s, and 49s type: 'GainsEffect', netRegex: { effectId: '13D6', capture: true }, run: (data, matches) => data.doubleTroubleTrapTargets.push(matches.target), }, { - id: 'DMU P1 Double-trouble Trap Early', - // Times are 5s, 68s, and 49s + id: 'DMU P1 Double-trouble Trap 2 Early', type: 'GainsEffect', netRegex: { effectId: '13D6', capture: true }, delaySeconds: 0.1, suppressSeconds: 1, infoText: (data, matches, output) => { - // Ignore first set - if (parseFloat(matches.duration) < 6) + // Ignore first set and third set + if (parseFloat(matches.duration) < 67) return; + const target1 = data.doubleTroubleTrapTargets[0]; if (data.doubleTroubleTrapTargets.length === 2) { const target2 = data.doubleTroubleTrapTargets[1]; @@ -348,56 +409,153 @@ const triggerSet: TriggerSet = { player: data.party.member(target1), }); }, - outputStrings: { - trapOnYou: { - en: 'Trap on YOU (later)', - }, - trapOnYouPlayer: { - en: 'Traps on YOU, ${player} (later)', - }, - trapOnPlayer: { - en: 'Trap on ${player} (later)', - }, - trapOnPlayers: { - en: 'Traps on ${player1}, ${player2} (later)', - }, - }, + outputStrings: trapEarlyOutputStrings, }, { - id: 'DMU P1 Double-trouble Trap', + id: 'DMU P1 Double-trouble Trap 3 Early', type: 'GainsEffect', netRegex: { effectId: '13D6', capture: true }, - delaySeconds: (_data, matches) => { + delaySeconds: 0.1, + suppressSeconds: 1, + infoText: (data, matches, output) => { const duration = parseFloat(matches.duration); - // Giving a 5s warning - // Second Set - if (duration > 67) - return 63; + // Only capture 3rd set + if (duration < 48 || duration > 50) + return; + + const target1 = data.doubleTroubleTrapTargets[0]; + if (data.doubleTroubleTrapTargets.length === 2) { + const target2 = data.doubleTroubleTrapTargets[1]; + + if (target1 === data.me) + return output.trapOnYouPlayer!({ + player: data.party.member(target1), + }); + + if (target2 === data.me) + return output.trapOnYouPlayer!({ + player: data.party.member(target2), + }); - // Last set - if (duration > 48) - return 44; + return output.trapOnPlayers!({ + player1: data.party.member(target1), + player2: data.party.member(target2), + }); + } - // First set - return 0.1; + if (target1 === data.me) + return output.trapOnYou!(); + return output.trapOnPlayer!({ + player: data.party.member(target1), + }); }, + outputStrings: trapEarlyOutputStrings, + }, + { + id: 'DMU P1 Double-trouble Trap 1', + type: 'GainsEffect', + netRegex: { effectId: '13D6', capture: true }, + condition: (_data, matches) => parseFloat(matches.duration) < 6, + delaySeconds: 0.1, suppressSeconds: 1, response: (data, _matches, output) => { // cactbot-builtin-response - output.responseOutputStrings = { - trapOnYou: { - en: 'Trap on YOU', - }, - trapOnYouPlayer: { - en: 'Traps on YOU, ${player}', - }, - trapOnPlayer: { - en: 'Trap on ${player}', - }, - trapOnPlayers: { - en: 'Traps on ${player1}, ${player2}', - }, + output.responseOutputStrings = trapOutputStrings; + + const target1 = data.doubleTroubleTrapTargets[0]; + if (data.doubleTroubleTrapTargets.length === 2) { + const target2 = data.doubleTroubleTrapTargets[1]; + + if (target1 === data.me) + return { + alertText: output.trapOnYouPlayer!({ + player: data.party.member(target1), + }), + }; + + if (target2 === data.me) + return { + alertText: output.trapOnYouPlayer!({ + player: data.party.member(target2), + }), + }; + + return { + infoText: output.trapOnPlayers!({ + player1: data.party.member(target1), + player2: data.party.member(target2), + }), + }; + } + + if (target1 === data.me) + return { alertText: output.trapOnYou!() }; + return { + infoText: output.trapOnPlayer!({ + player: data.party.member(target1), + }), }; + }, + }, + { + id: 'DMU P1 Double-trouble Trap 2', + type: 'GainsEffect', + netRegex: { effectId: '13D6', capture: true }, + condition: (_data, matches) => parseFloat(matches.duration) > 67, + delaySeconds: (_data, matches) => parseFloat(matches.duration) - 5, + suppressSeconds: 1, + response: (data, _matches, output) => { + // cactbot-builtin-response + output.responseOutputStrings = trapOutputStrings; + + const target1 = data.doubleTroubleTrapTargets[0]; + if (data.doubleTroubleTrapTargets.length === 2) { + const target2 = data.doubleTroubleTrapTargets[1]; + + if (target1 === data.me) + return { + alertText: output.trapOnYouPlayer!({ + player: data.party.member(target1), + }), + }; + + if (target2 === data.me) + return { + alertText: output.trapOnYouPlayer!({ + player: data.party.member(target2), + }), + }; + + return { + infoText: output.trapOnPlayers!({ + player1: data.party.member(target1), + player2: data.party.member(target2), + }), + }; + } + + if (target1 === data.me) + return { alertText: output.trapOnYou!() }; + return { + infoText: output.trapOnPlayer!({ + player: data.party.member(target1), + }), + }; + }, + }, + { + id: 'DMU P1 Double-trouble Trap 3', + type: 'GainsEffect', + netRegex: { effectId: '13D6', capture: true }, + condition: (_data, matches) => { + const duration = parseFloat(matches.duration); + return duration > 48 && duration < 50; + }, + delaySeconds: (_data, matches) => parseFloat(matches.duration) - 5, + suppressSeconds: 1, + response: (data, _matches, output) => { + // cactbot-builtin-response + output.responseOutputStrings = trapOutputStrings; const target1 = data.doubleTroubleTrapTargets[0]; if (data.doubleTroubleTrapTargets.length === 2) { @@ -457,6 +615,190 @@ const triggerSet: TriggerSet = { delaySeconds: (_data, matches) => parseFloat(matches.castTime) - 2, // Result in ~5.1s warning response: Responses.tankBuster(), }, + { + id: 'DMU P1 Tele-Portent Collect', + // Debuffs distributed to 8 players: + // Players with 2 of the same are always: + // 130F Left (7s) + 130F Left (10s) + // 130E Right (7s) + 130E Right (10s) + // 130D Down (7s) + 130D Down (10s) + // 130C Up (7s) + 130C Up (10s) + // + // The remaining players may have differing patterns: + // Pattern 1: + // 130D Down (7s) + 13DA Left (10s) + // 13D9 Right (7s) + 130C Up (10s) + // 13D8 Down (7s) + 130E Right (10s) + // 130F Left (7s) + 13D7 Up (10s) + // + // Pattern 2: + // 130D Down (7s) + 13DA Left (10s) + // 13D9 Right (7s) + 130C Up (10s) + // 130E Right (7s) + 13D8 Down (10s) + // 13D7 Up (7s) + 130F Left (10s) + // + // Pattern 3: + // 130D Down (7s) + 13DA Left (10s) + // 13D9 Right (7s) + 130C Up (10s) + // 130E Right (7s) + 13D8 Down (10s) + // 130F Left (7s) + 13D7 Up (10s) + // + // Pattern 4: + // 13DA Left (7s) + 130D Down (10s) + // 130C Up (7s) + 13D9 Right (10s) + // 130E Right (7s) + 13D8 Down (10s) + // 130F Left (7s) + 13D7 Up (10s) + // + // Possibly More? + // Varying strategies to resolve + // Players with the same arrows will get a 6s 503 Confused which causes them to target nearest players + // Players with different arrows will cause a 6s 131E Sleep aoe + type: 'GainsEffect', + netRegex: { + effectId: [ + '130C', // Up + '130D', // Down + '130E', // Right + '130F', // Left + '13D7', // Up + '13D8', // Down + '13D9', // Right + '13DA', // Left + ], + capture: true, + }, + condition: Conditions.targetIsYou(), + run: (data, matches) => { + const effectMap: { [effectId: string]: typeof data.myTelePortent1 } = { + '130C': 'up', + '130D': 'down', + '130E': 'right', + '130F': 'left', + '13D7': 'up', + '13D8': 'down', + '13D9': 'right', + '13DA': 'left', + }; + const duration = parseFloat(matches.duration); + if (duration < 8) { + data.myTelePortent1 = effectMap[matches.effectId]; + return; + } + data.myTelePortent2 = effectMap[matches.effectId]; + }, + }, + { + id: 'DMU P1 Tele-Portents', + type: 'GainsEffect', + netRegex: { + effectId: [ + '130C', // Up + '130D', // Down + '130E', // Right + '130F', // Left + '13D7', // Up + '13D8', // Down + '13D9', // Right + '13DA', // Left + ], + capture: true, + }, + condition: Conditions.targetIsYou(), + durationSeconds: 7, + infoText: (data, _matches, output) => { + if (data.myTelePortent1 === undefined || data.myTelePortent2 === undefined) + return; + const portents = data.myTelePortent1 + data.myTelePortent2; + return output[portents]!(); + }, + outputStrings: { + upup: { + en: 'Up Portents', + }, + downdown: { + en: 'Down Portents', + }, + rightright: { + en: 'Right Portents', + }, + leftleft: { + en: 'Left Portents', + }, + downleft: { + en: 'Down => Left Portent', + }, + downright: { + en: 'Down => Right Portent', + }, + rightup: { + en: 'Right => Up Portent', + }, + rightdown: { + en: 'Right => Down Portent', + }, + leftup: { + en: 'Left => Up Portent', + }, + leftdown: { + en: 'Left => Down Portent', + }, + upright: { + en: 'Up => Right Portent', + }, + upleft: { + en: 'Up => Left Portent', + }, + }, + }, + { + id: 'DMU P1 Tele-Portent 2', + // Not enough time to have lengthy TTS, but could configure this to give direction instead of move + type: 'LosesEffect', + netRegex: { + effectId: [ + '130C', // Up + '130D', // Down + '130E', // Right + '130F', // Left + '13D7', // Up + '13D8', // Down + '13D9', // Right + '13DA', // Left + ], + capture: true, + }, + condition: (data, matches) => { + if (data.me === matches.target) + if (data.myTelePortent1 !== undefined) + return true; + return false; + }, + durationSeconds: 3, + response: Responses.moveAway('alert'), + }, + { + id: 'DMU P1 Tele-Portent Cleanup', + type: 'LosesEffect', + netRegex: { + effectId: [ + '130C', // Up + '130D', // Down + '130E', // Right + '130F', // Left + '13D7', // Up + '13D8', // Down + '13D9', // Right + '13DA', // Left + ], + capture: true, + }, + condition: Conditions.targetIsYou(), + suppressSeconds: 1, + run: (data) => { + delete data.myTelePortent1; + delete data.myTelePortent2; + }, + }, ], timelineReplace: [ { From e5bc5f9ffb68796e54f319889c587e312b3fb074 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Wed, 3 Jun 2026 23:53:46 -0400 Subject: [PATCH 10/54] lint + missing false condition --- ui/raidboss/data/07-dt/ultimate/dancing_mad.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index cdd7b5145d..ff30d0d188 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -303,7 +303,8 @@ const triggerSet: TriggerSet = { data.isThunderTrue === undefined && data.isFireTrue === undefined ) - return true; + return true; + return false; }, infoText: (data, _matches, output) => { return data.isIceTrue From 5cbfd84943c88210116f86b4cc0f4a30eee9cab5 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 4 Jun 2026 03:48:13 -0400 Subject: [PATCH 11/54] Add halfroom triggers, fixup trap tracking --- .../data/07-dt/ultimate/dancing_mad.ts | 109 +++++++++++++++++- 1 file changed, 105 insertions(+), 4 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index 284df359dd..262c8e8b01 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -23,6 +23,10 @@ export interface Data extends RaidbossData { // General phase: Phase | 'unknown'; // Phase 1 + blueTowerIds: string[]; + yellowTowerIds: string[]; + purpleTowerIds: string[]; + tower?: 'blue' | 'yellow' | 'purple'; fireMarker?: string; isFireTrue?: boolean; isIceTrue?: boolean; @@ -169,6 +173,9 @@ const triggerSet: TriggerSet = { return { phase: 'p1', // Phase 1 + blueTowerIds: [], + yellowTowerIds: [], + purpleTowerIds: [], doubleTroubleTrapTargets: [], }; }, @@ -179,6 +186,63 @@ const triggerSet: TriggerSet = { netRegex: { id: Object.keys(phases) }, run: (data, matches) => data.phase = phases[matches.id] ?? 'unknown', }, + { + id: 'DMU P1 CombatantMemory Tower Tracker', + // 1EBFBB => Wave Cannon entity (blue) + // 1EBFBC => Gravitational Wave entity (purple) + // 1EBFBD => Intemperate Will entity (yellow) + // There are two of each, they are added at start of fight + type: 'CombatantMemory', + netRegex: { + change: 'Add', + pair: [{ key: 'BNpcID', value: ['1EBFBB', '1EBFBC', '1EBFBD'] }], + capture: true, + }, + run: (data, matches) => { + const towerMap = { + '1EBFBB': 'blue', + '1EBFBC': 'purple', + '1EBFBD': 'yellow', + 'unknown': 'unknown', + }; + const bnpcid = matches.pairBNpcID ?? 'unknown'; + const kind = towerMap[bnpcid as keyof typeof towerMap]; + if (kind === 'blue') { + data.blueTowerIds.push(matches.id); + return; + } + if (kind === 'yellow') { + data.yellowTowerIds.push(matches.id); + return; + } + if (kind === 'purple') { + data.purpleTowerIds.push(matches.id); + return; + } + }, + }, + { + id: 'DMU P1 Graven Image Collect', + // Tower entity actions + type: 'ActorControlExtra', + netRegex: { category: '019D', param1: '40', param2: '80', capture: true }, + run: (data, matches) => { + const id = matches.id; + + if (data.yellowTowerIds.indexOf(id) !== -1) { + data.tower = 'yellow'; + return; + } + if (data.purpleTowerIds.indexOf(id) !== -1) { + data.tower = 'purple'; + return; + } + if (data.blueTowerIds.indexOf(id) !== -1) { + data.tower = 'blue'; + return; + } + }, + }, { id: 'DMU P1 Revolting Ruin III', // Tankbuster targets highest enmity then the nearest player that is not the highest enmity @@ -386,6 +450,10 @@ const triggerSet: TriggerSet = { return; const target1 = data.doubleTroubleTrapTargets[0]; + // Check if players died from a knockback + if (target1 === undefined) + return; + if (data.doubleTroubleTrapTargets.length === 2) { const target2 = data.doubleTroubleTrapTargets[1]; @@ -426,6 +494,10 @@ const triggerSet: TriggerSet = { return; const target1 = data.doubleTroubleTrapTargets[0]; + // Check if players died from a knockback + if (target1 === undefined) + return; + if (data.doubleTroubleTrapTargets.length === 2) { const target2 = data.doubleTroubleTrapTargets[1]; @@ -511,6 +583,10 @@ const triggerSet: TriggerSet = { output.responseOutputStrings = trapOutputStrings; const target1 = data.doubleTroubleTrapTargets[0]; + // Check if players died + if (target1 === undefined) + return; + if (data.doubleTroubleTrapTargets.length === 2) { const target2 = data.doubleTroubleTrapTargets[1]; @@ -560,6 +636,10 @@ const triggerSet: TriggerSet = { output.responseOutputStrings = trapOutputStrings; const target1 = data.doubleTroubleTrapTargets[0]; + // Check if players died + if (target1 === undefined) + return; + if (data.doubleTroubleTrapTargets.length === 2) { const target2 = data.doubleTroubleTrapTargets[1]; @@ -595,12 +675,15 @@ const triggerSet: TriggerSet = { }, }, { - // Debuffs should expire before the new ones come out id: 'DMU P1 Double-trouble Trap Cleanup', + // Players dying will also trigger this type: 'LosesEffect', - netRegex: { effectId: '13D6', capture: false }, - suppressSeconds: 1, - run: (data) => data.doubleTroubleTrapTargets = [], + netRegex: { effectId: '13D6', capture: true }, + run: (data, matches) => { + data.doubleTroubleTrapTargets = data.doubleTroubleTrapTargets.filter( + (target) => target !== matches.target + ); + }, }, { id: 'DMU P1 Light of Judgment', @@ -617,6 +700,24 @@ const triggerSet: TriggerSet = { delaySeconds: (_data, matches) => parseFloat(matches.castTime) - 2, // Result in ~5.1s warning response: Responses.tankBuster(), }, + { + id: 'DMU P1 Impertinent Will/Gravitational Wave', + type: 'ActorControlExtra', + netRegex: { category: '019D', param1: '40', param2: '80', capture: true }, + alertText: (data, matches, output) => { + const id = matches.id; + if (data.yellowTowerIds.indexOf(id) !== -1) { + return output.goWest!(); + } + if (data.purpleTowerIds.indexOf(id) !== -1) { + return output.goEast!(); + } + }, + outputStrings: { + goWest: Outputs.getLeftAndWest, + goEast: Outputs.getRightAndEast, + }, + }, { id: 'DMU P1 Tele-Portent Collect', // Debuffs distributed to 8 players: From 30d4d2ee3f511cf207866f8bb46226fc0ebfb8c9 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 4 Jun 2026 03:52:00 -0400 Subject: [PATCH 12/54] lint --- ui/raidboss/data/07-dt/ultimate/dancing_mad.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index 262c8e8b01..79ff8896bb 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -681,7 +681,7 @@ const triggerSet: TriggerSet = { netRegex: { effectId: '13D6', capture: true }, run: (data, matches) => { data.doubleTroubleTrapTargets = data.doubleTroubleTrapTargets.filter( - (target) => target !== matches.target + (target) => target !== matches.target, ); }, }, From 752d62e829488bc5a6f50895cc70668c76aabaa7 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 4 Jun 2026 18:44:28 -0400 Subject: [PATCH 13/54] revolting ruin tankbuster => tank cleave --- .../data/07-dt/ultimate/dancing_mad.ts | 132 +++++++++++++++++- 1 file changed, 131 insertions(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index 79ff8896bb..bfabd6a4c9 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -31,6 +31,7 @@ export interface Data extends RaidbossData { isFireTrue?: boolean; isIceTrue?: boolean; isThunderTrue?: boolean; + waveCannonTargets: string[]; doubleTroubleTrapTargets: string[]; myTelePortent1?: 'up' | 'down' | 'right' | 'left'; myTelePortent2?: 'up' | 'down' | 'right' | 'left'; @@ -176,6 +177,7 @@ const triggerSet: TriggerSet = { blueTowerIds: [], yellowTowerIds: [], purpleTowerIds: [], + waveCannonTargets: [], doubleTroubleTrapTargets: [], }; }, @@ -249,7 +251,62 @@ const triggerSet: TriggerSet = { // Offtank can provoke to cause the main tank to take both hits so long as main tank is closest type: 'HeadMarker', netRegex: { id: headMarkerData['tankbuster'], capture: true }, - response: Responses.tankBuster(), + alertText: (data, matches, output) => { + const target = matches.target; + + // Highest entity player can stand wherever, but if they swap threat + // they should be in to either take the hit or prevent party hit + if (target === data.me) + return output.cleaveOnYouDir!({ + cleave: output.cleaveOnYou!(), + dir: output.in!(), + }); + + // Off tank (second highest enmity player) needs to be in for followup + // Or swap with main tank being in + // If both tanks are in, then it's safe for party in either case + if (data.role === 'tank') + return output.cleaveOnPlayerSwapDir!({ + cleave: output.cleaveOnPlayer!({ + player: data.party.member(target), + }), + dir: output.in!(), + }); + + if (data.role === 'healer') + return output.cleaveOnPlayerDir!({ + cleave: output.cleaveOnPlayer!({ + player: data.party.member(target), + }), + dir: output.out!(), + }); + + return output.avoidCleavesDir!({ + cleave: output.avoidCleaves!(), + dir: output.out!(), + }); + }, + outputStrings: { + in: Outputs.in, + out: Outputs.out, + cleaveOnYou: Outputs.tankCleaveOnYou, + avoidCleaves: Outputs.avoidTankCleaves, + cleaveOnPlayer: { + en: 'Tank Cleave on ${player}', + }, + cleaveOnYouDir: { + en: '${cleave} => ${dir}', + }, + cleaveOnPlayerDir: { + en: '${cleave} + ${dir}', + }, + cleaveOnPlayerSwapDir: { + en: '${cleave} => ${dir}', + }, + avoidCleavesDir: { + en: '${cleave} + ${dir}', + }, + }, }, { id: 'DMU P1 Mystery Magic Collect', @@ -431,6 +488,79 @@ const triggerSet: TriggerSet = { delete data.fireMarker; }, }, + { + id: 'DMU P1 Wave Cannon', + // BAA8 Wave Cannon is an instant cast from Graven Image + // This gives a ~5 second warning to spread + type: 'ActorControlExtra', + netRegex: { category: '019D', param1: '40', param2: '80', capture: true }, + alertText: (data, matches, output) => { + if (data.blueTowerIds.indexOf(matches.id) !== -1) + return output.waveCannonLine!(); + }, + outputStrings: { + waveCannonLine: { + en: 'E/W Spread', + }, + }, + }, + { + id: 'DMU P1 Wave Cannon Collect', + // Collect players hit by Wave Cannon to tell who soaks tower followup and who avoids tower + type: 'Ability', + netRegex: { id: 'BAA8', source: 'Graven Image', capture: true }, + run: (data, matches) => data.waveCannonTargets.push(matches.target), + }, + { + id: 'DMU P1 Wave Cannon Explosion Towers', + // Wave Cannon gives a vulnerability which causes death to BAAA Explosion soaks + // Sacraficing a player who clipped to prevent party 90% damage down from + // BAAB Unmitigated Explosion seems ideal, although different clients may + // get different order + // Suprisingly the Unmitigated Explosion doesn't deal damage + type: 'Ability', + netRegex: { id: 'BAA8', source: 'Graven Image', capture: true }, + delaySeconds: 0.1, + suppressSeconds: 1, + response: (data, _matches, output) => { + // cactbot-builtin-response + output.responseOutputStrings = { + soak: { + en: 'Soak tower', + de: 'Türme nehmen', + fr: 'Prenez une tour', + ja: '塔踏み', + cn: '踩塔击飞', + ko: '기둥 들어가기', + tc: '踩塔擊飛', + }, + avoid: { + en: 'Avoid towers', + de: 'Türme vermeiden', + fr: 'Évitez les tours', + ja: '塔回避', + cn: '远离塔', + ko: '기둥 피하기', + tc: '遠離塔', + }, + extra: { + en: 'Extra Tower', + }, + }; + const avoidedCannon = data.waveCannonTargets.indexOf(data.me) !== -1; + + // Option for player to soak the tower for p1 prog? + if (avoidedCannon && data.waveCannonTargets.length > 4) + return { infoText: output.extra!() }; + + // Avoid the tower + if (avoidedCannon) + return { alertText: output.avoid!() }; + + // Player didn't get hit, they will need to soak a tower + return { alertTest: output.soak!() }; + }, + }, { id: 'DMU P1 Double-trouble Trap Collect', // Times are 5s, 68s, and 49s From b8c884589e348fdc2def8c1736c9896a31f26b7b Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 4 Jun 2026 18:48:14 -0400 Subject: [PATCH 14/54] remove unnecessary capture --- ui/raidboss/data/07-dt/ultimate/dancing_mad.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index bfabd6a4c9..5783c9ff0b 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -519,7 +519,7 @@ const triggerSet: TriggerSet = { // get different order // Suprisingly the Unmitigated Explosion doesn't deal damage type: 'Ability', - netRegex: { id: 'BAA8', source: 'Graven Image', capture: true }, + netRegex: { id: 'BAA8', source: 'Graven Image', capture: false }, delaySeconds: 0.1, suppressSeconds: 1, response: (data, _matches, output) => { From f6b53c06f62cc0593d6a7a64aa4ba8bd067268b3 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 4 Jun 2026 19:02:17 -0400 Subject: [PATCH 15/54] explicit mystery magic output --- .../data/07-dt/ultimate/dancing_mad.ts | 60 ++++--------------- 1 file changed, 12 insertions(+), 48 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index 5783c9ff0b..e0fcd77388 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -63,40 +63,16 @@ const mysteryMagicOutputStrings: OutputStrings = { tc: '集合', }, trueThunder: { - en: 'True Thunder', - de: 'Wahrer Blitz', - fr: 'Vraie foudre', - ja: '真サンダガ', - cn: '真雷', - ko: '진실 선더가', - tc: '真雷', + en: 'In Line', }, fakeThunder: { - en: 'Fake Thunder', - de: 'Falscher Blitz', - fr: 'Fausse foudre', - ja: 'にせサンダガ', - cn: '假雷', - ko: '거짓 선더가', - tc: '假雷', + en: 'Avoid Tell', }, trueIce: { - en: 'True Ice', - de: 'Wahres Eis', - fr: 'Vraie glace', - ja: '真ブリザガ', - cn: '真冰', - ko: '진실 블리자가', - tc: '真冰', + en: 'In Cone', }, fakeIce: { - en: 'Fake Ice', - de: 'Falsches Eis', - fr: 'Fausse glace', - ja: 'にせブリザガ', - cn: '假冰', - ko: '거짓 블리자가', - tc: '假冰', + en: 'Avoid Tell', }, stackTrueIce: { en: '${mech} + ${ice}', @@ -111,16 +87,16 @@ const mysteryMagicOutputStrings: OutputStrings = { en: '${mech} + ${ice}', }, trueIceTrueThunder: { - en: '${ice} + ${thunder}', + en: 'Avoid Tells', }, fakeIceTrueThunder: { - en: '${ice} + ${thunder}', + en: 'Cone (only)', }, trueIceFakeThunder: { - en: '${ice} + ${thunder}', + en: 'Line (only)', }, fakeIceFakeThunder: { - en: '${ice} + ${thunder}', + en: 'Cone + Line', }, stackTrueThunder: { en: '${mech} + ${thunder}', @@ -392,24 +368,12 @@ const triggerSet: TriggerSet = { infoText: (data, _matches, output) => { if (data.isThunderTrue) { return data.isIceTrue - ? output.trueIceTrueThunder!({ - ice: output.trueIce!(), - thunder: output.trueThunder!(), - }) - : output.fakeIceTrueThunder!({ - ice: output.fakeIce!(), - thunder: output.trueThunder!(), - }); + ? output.trueIceTrueThunder!() + : output.fakeIceTrueThunder!(); } return data.isIceTrue - ? output.trueIceTrueThunder!({ - ice: output.trueIce!(), - thunder: output.fakeThunder!(), - }) - : output.fakeIceFakeThunder!({ - ice: output.fakeIce!(), - thunder: output.fakeThunder!(), - }); + ? output.trueIceTrueThunder!() + : output.fakeIceFakeThunder!(); }, outputStrings: mysteryMagicOutputStrings, }, From 2103d4184f85a76d21721ef725a17bfcad2d0798 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 4 Jun 2026 20:56:02 -0400 Subject: [PATCH 16/54] flip fake/true thunder ice --- ui/raidboss/data/07-dt/ultimate/dancing_mad.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index e0fcd77388..b2c66e0228 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -63,16 +63,16 @@ const mysteryMagicOutputStrings: OutputStrings = { tc: '集合', }, trueThunder: { - en: 'In Line', + en: 'Avoid Tell', }, fakeThunder: { - en: 'Avoid Tell', + en: 'In Line', }, trueIce: { - en: 'In Cone', + en: 'Avoid Tell', }, fakeIce: { - en: 'Avoid Tell', + en: 'In Cone', }, stackTrueIce: { en: '${mech} + ${ice}', From 63d60cea61bdd03011c9a53f8243b85eacdc1986 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 4 Jun 2026 21:08:40 -0400 Subject: [PATCH 17/54] remove dir outputs from revolting ruin --- .../data/07-dt/ultimate/dancing_mad.ts | 47 ++++--------------- 1 file changed, 10 insertions(+), 37 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index b2c66e0228..3e304c758a 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -223,44 +223,26 @@ const triggerSet: TriggerSet = { }, { id: 'DMU P1 Revolting Ruin III', - // Tankbuster targets highest enmity then the nearest player that is not the highest enmity - // Offtank can provoke to cause the main tank to take both hits so long as main tank is closest + // Tankbuster targets highest enmity then second highest enmity + // A tank swap can happen to have MT take both hits type: 'HeadMarker', netRegex: { id: headMarkerData['tankbuster'], capture: true }, alertText: (data, matches, output) => { const target = matches.target; - - // Highest entity player can stand wherever, but if they swap threat - // they should be in to either take the hit or prevent party hit if (target === data.me) - return output.cleaveOnYouDir!({ - cleave: output.cleaveOnYou!(), - dir: output.in!(), - }); + return output.cleaveOnYou!(); - // Off tank (second highest enmity player) needs to be in for followup - // Or swap with main tank being in - // If both tanks are in, then it's safe for party in either case if (data.role === 'tank') - return output.cleaveOnPlayerSwapDir!({ - cleave: output.cleaveOnPlayer!({ - player: data.party.member(target), - }), - dir: output.in!(), + return output.cleaveSwap!({ + player: data.party.member(target), }); if (data.role === 'healer') - return output.cleaveOnPlayerDir!({ - cleave: output.cleaveOnPlayer!({ - player: data.party.member(target), - }), - dir: output.out!(), + return output.cleaveOnPlayer!({ + player: data.party.member(target), }); - return output.avoidCleavesDir!({ - cleave: output.avoidCleaves!(), - dir: output.out!(), - }); + return output.avoidCleaves!(); }, outputStrings: { in: Outputs.in, @@ -270,17 +252,8 @@ const triggerSet: TriggerSet = { cleaveOnPlayer: { en: 'Tank Cleave on ${player}', }, - cleaveOnYouDir: { - en: '${cleave} => ${dir}', - }, - cleaveOnPlayerDir: { - en: '${cleave} + ${dir}', - }, - cleaveOnPlayerSwapDir: { - en: '${cleave} => ${dir}', - }, - avoidCleavesDir: { - en: '${cleave} + ${dir}', + cleaveSwap: { // Defaulting to same output as cleaveOnPlayer + en: 'Tank Cleave on ${player}', }, }, }, From cad38384578c527da4a0464da488a16f34d940ee Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 4 Jun 2026 22:08:43 -0400 Subject: [PATCH 18/54] double-trouble trap output refactor --- .../data/07-dt/ultimate/dancing_mad.ts | 242 +++++------------- 1 file changed, 60 insertions(+), 182 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index 3e304c758a..26eaedb845 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -112,33 +112,12 @@ const mysteryMagicOutputStrings: OutputStrings = { }, }; -const trapEarlyOutputStrings: OutputStrings = { - trapOnYou: { - en: 'Trap on YOU (later)', - }, - trapOnYouPlayer: { - en: 'Traps on YOU, ${player} (later)', - }, - trapOnPlayer: { - en: 'Trap on ${player} (later)', - }, - trapOnPlayers: { - en: 'Traps on ${player1}, ${player2} (later)', - }, -}; - const trapOutputStrings: OutputStrings = { - trapOnYou: { - en: 'Trap on YOU ', - }, - trapOnYouPlayer: { - en: 'Traps on YOU, ${player}', + knockbackFrom: { + en: 'Knockback from ${players}', }, - trapOnPlayer: { - en: 'Trap on ${player}', - }, - trapOnPlayers: { - en: 'Traps on ${player1}, ${player2}', + knockbackFromLater: { + en: 'Knockback from ${players} (later)', }, }; @@ -516,37 +495,21 @@ const triggerSet: TriggerSet = { if (parseFloat(matches.duration) < 67) return; - const target1 = data.doubleTroubleTrapTargets[0]; - // Check if players died from a knockback - if (target1 === undefined) + // Check if players died + if (data.doubleTroubleTrapTargets[0] === undefined) return; - if (data.doubleTroubleTrapTargets.length === 2) { - const target2 = data.doubleTroubleTrapTargets[1]; - - if (target1 === data.me) - return output.trapOnYouPlayer!({ - player: data.party.member(target1), - }); - - if (target2 === data.me) - return output.trapOnYouPlayer!({ - player: data.party.member(target2), - }); - - return output.trapOnPlayers!({ - player1: data.party.member(target1), - player2: data.party.member(target2), - }); - } - - if (target1 === data.me) - return output.trapOnYou!(); - return output.trapOnPlayer!({ - player: data.party.member(target1), - }); + const players = data.doubleTroubleTrapTargets.map( + (player) => { + if (player === data.me) + return 'YOU'; + return data.party.member(player); + } + ); + const msg = players?.join(', '); + return output.knockbackFromLater!({ players: msg }); }, - outputStrings: trapEarlyOutputStrings, + outputStrings: trapOutputStrings, }, { id: 'DMU P1 Double-trouble Trap 3 Early', @@ -560,37 +523,21 @@ const triggerSet: TriggerSet = { if (duration < 48 || duration > 50) return; - const target1 = data.doubleTroubleTrapTargets[0]; - // Check if players died from a knockback - if (target1 === undefined) + // Check if players died + if (data.doubleTroubleTrapTargets[0] === undefined) return; - if (data.doubleTroubleTrapTargets.length === 2) { - const target2 = data.doubleTroubleTrapTargets[1]; - - if (target1 === data.me) - return output.trapOnYouPlayer!({ - player: data.party.member(target1), - }); - - if (target2 === data.me) - return output.trapOnYouPlayer!({ - player: data.party.member(target2), - }); - - return output.trapOnPlayers!({ - player1: data.party.member(target1), - player2: data.party.member(target2), - }); - } - - if (target1 === data.me) - return output.trapOnYou!(); - return output.trapOnPlayer!({ - player: data.party.member(target1), - }); + const players = data.doubleTroubleTrapTargets.map( + (player) => { + if (player === data.me) + return 'YOU'; + return data.party.member(player); + } + ); + const msg = players?.join(', '); + return output.knockbackFromLater!({ players: msg }); }, - outputStrings: trapEarlyOutputStrings, + outputStrings: trapOutputStrings, }, { id: 'DMU P1 Double-trouble Trap 1', @@ -603,39 +550,16 @@ const triggerSet: TriggerSet = { // cactbot-builtin-response output.responseOutputStrings = trapOutputStrings; - const target1 = data.doubleTroubleTrapTargets[0]; - if (data.doubleTroubleTrapTargets.length === 2) { - const target2 = data.doubleTroubleTrapTargets[1]; - - if (target1 === data.me) - return { - alertText: output.trapOnYouPlayer!({ - player: data.party.member(target1), - }), - }; - - if (target2 === data.me) - return { - alertText: output.trapOnYouPlayer!({ - player: data.party.member(target2), - }), - }; - - return { - infoText: output.trapOnPlayers!({ - player1: data.party.member(target1), - player2: data.party.member(target2), - }), - }; - } - - if (target1 === data.me) - return { alertText: output.trapOnYou!() }; - return { - infoText: output.trapOnPlayer!({ - player: data.party.member(target1), - }), - }; + const severity = data.doubleTroubleTrapTargets.includes(data.me) ? 'alertText' : 'infoText'; + const players = data.doubleTroubleTrapTargets.map( + (player) => { + if (player === data.me) + return 'YOU'; + return data.party.member(player); + } + ); + const msg = players?.join(', '); + return { [severity]: output.knockbackFrom!({ players: msg }) }; }, }, { @@ -649,43 +573,20 @@ const triggerSet: TriggerSet = { // cactbot-builtin-response output.responseOutputStrings = trapOutputStrings; - const target1 = data.doubleTroubleTrapTargets[0]; // Check if players died - if (target1 === undefined) + if (data.doubleTroubleTrapTargets[0] === undefined) return; - if (data.doubleTroubleTrapTargets.length === 2) { - const target2 = data.doubleTroubleTrapTargets[1]; - - if (target1 === data.me) - return { - alertText: output.trapOnYouPlayer!({ - player: data.party.member(target1), - }), - }; - - if (target2 === data.me) - return { - alertText: output.trapOnYouPlayer!({ - player: data.party.member(target2), - }), - }; - - return { - infoText: output.trapOnPlayers!({ - player1: data.party.member(target1), - player2: data.party.member(target2), - }), - }; - } - - if (target1 === data.me) - return { alertText: output.trapOnYou!() }; - return { - infoText: output.trapOnPlayer!({ - player: data.party.member(target1), - }), - }; + const severity = data.doubleTroubleTrapTargets.includes(data.me) ? 'alertText' : 'infoText'; + const players = data.doubleTroubleTrapTargets.map( + (player) => { + if (player === data.me) + return 'YOU'; + return data.party.member(player); + } + ); + const msg = players?.join(', '); + return { [severity]: output.knockbackFrom!({ players: msg }) }; }, }, { @@ -702,43 +603,20 @@ const triggerSet: TriggerSet = { // cactbot-builtin-response output.responseOutputStrings = trapOutputStrings; - const target1 = data.doubleTroubleTrapTargets[0]; // Check if players died - if (target1 === undefined) + if (data.doubleTroubleTrapTargets[0] === undefined) return; - if (data.doubleTroubleTrapTargets.length === 2) { - const target2 = data.doubleTroubleTrapTargets[1]; - - if (target1 === data.me) - return { - alertText: output.trapOnYouPlayer!({ - player: data.party.member(target1), - }), - }; - - if (target2 === data.me) - return { - alertText: output.trapOnYouPlayer!({ - player: data.party.member(target2), - }), - }; - - return { - infoText: output.trapOnPlayers!({ - player1: data.party.member(target1), - player2: data.party.member(target2), - }), - }; - } - - if (target1 === data.me) - return { alertText: output.trapOnYou!() }; - return { - infoText: output.trapOnPlayer!({ - player: data.party.member(target1), - }), - }; + const severity = data.doubleTroubleTrapTargets.includes(data.me) ? 'alertText' : 'infoText'; + const players = data.doubleTroubleTrapTargets.map( + (player) => { + if (player === data.me) + return 'YOU'; + return data.party.member(player); + } + ); + const msg = players?.join(', '); + return { [severity]: output.knockbackFrom!({ players: msg }) }; }, }, { From 80789139bbcd001c3efe6fdf14f45124596e7499 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 4 Jun 2026 22:11:09 -0400 Subject: [PATCH 19/54] lint --- ui/raidboss/data/07-dt/ultimate/dancing_mad.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index 26eaedb845..fd56d22e6f 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -504,7 +504,7 @@ const triggerSet: TriggerSet = { if (player === data.me) return 'YOU'; return data.party.member(player); - } + }, ); const msg = players?.join(', '); return output.knockbackFromLater!({ players: msg }); @@ -532,7 +532,7 @@ const triggerSet: TriggerSet = { if (player === data.me) return 'YOU'; return data.party.member(player); - } + }, ); const msg = players?.join(', '); return output.knockbackFromLater!({ players: msg }); @@ -556,7 +556,7 @@ const triggerSet: TriggerSet = { if (player === data.me) return 'YOU'; return data.party.member(player); - } + }, ); const msg = players?.join(', '); return { [severity]: output.knockbackFrom!({ players: msg }) }; @@ -583,7 +583,7 @@ const triggerSet: TriggerSet = { if (player === data.me) return 'YOU'; return data.party.member(player); - } + }, ); const msg = players?.join(', '); return { [severity]: output.knockbackFrom!({ players: msg }) }; @@ -613,7 +613,7 @@ const triggerSet: TriggerSet = { if (player === data.me) return 'YOU'; return data.party.member(player); - } + }, ); const msg = players?.join(', '); return { [severity]: output.knockbackFrom!({ players: msg }) }; From 15a1c918b9c1b103fc2c9e3b7264ae10f74382e5 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 4 Jun 2026 23:19:13 -0400 Subject: [PATCH 20/54] add tether triggers --- .../data/07-dt/ultimate/dancing_mad.ts | 286 +++++++++++++++++- 1 file changed, 283 insertions(+), 3 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index fd56d22e6f..8414453f3d 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -27,6 +27,15 @@ export interface Data extends RaidbossData { yellowTowerIds: string[]; purpleTowerIds: string[]; tower?: 'blue' | 'yellow' | 'purple'; + gravenImageCount: number; + actorPositions: { [id: string]: { x: number; y: number; heading: number } }; + gravenImageTether?: + | 'pulse' + | 'gravitas' + | 'vitrophyre' + | 'indulgent' + | 'idyllic' + | 'unknown'; fireMarker?: string; isFireTrue?: boolean; isIceTrue?: boolean; @@ -49,10 +58,22 @@ const headMarkerData = { 'tankbuster': '00DA', // Revolting Ruin III tankbuster 'dorito': '007F', // spread (real) or stack (fake) 'stack': '0080', // spread (fake) or stack (real) + // Phase 1 Tethers + 'imageTether': '002D', } as const; const mysteryMagicOutputStrings: OutputStrings = { + puddle: { + en: 'Bait Puddle', + de: 'Fläche ködern', + fr: 'Déposez', + ja: 'AOE誘導', + cn: '诱导AOE', + ko: '장판 유도', + tc: '誘導AOE', + }, spread: Outputs.spread, + middle: Outputs.goIntoMiddle, stack: { en: 'Stack', de: 'Stacken', @@ -74,6 +95,12 @@ const mysteryMagicOutputStrings: OutputStrings = { fakeIce: { en: 'In Cone', }, + trueIcePuddle: { + en: '${mech1} + ${mech2} => ${mech3}', + }, + fakeIcePuddle: { + en: '${mech1} + ${mech2} => ${mech3}', + }, stackTrueIce: { en: '${mech} + ${ice}', }, @@ -132,6 +159,8 @@ const triggerSet: TriggerSet = { blueTowerIds: [], yellowTowerIds: [], purpleTowerIds: [], + actorPositions: {}, + gravenImageCount: 0, waveCannonTargets: [], doubleTroubleTrapTargets: [], }; @@ -143,6 +172,248 @@ const triggerSet: TriggerSet = { netRegex: { id: Object.keys(phases) }, run: (data, matches) => data.phase = phases[matches.id] ?? 'unknown', }, + { + id: 'DMU ActorSetPos Tracker', + // Only in use for P1 Graven Image tethers + type: 'ActorSetPos', + netRegex: { id: '4[0-9A-Fa-f]{7}', capture: true }, + run: (data, matches) => + data.actorPositions[matches.id] = { + x: parseFloat(matches.x), + y: parseFloat(matches.y), + heading: parseFloat(matches.heading), + }, + }, + { + id: 'DMU P1 Graven Image Counter', + // Used for timing of tether triggers + type: 'StartsUsing', + netRegex: { id: 'BCF2', source: 'Kefka', capture: false }, + run: (data) => data.gravenImageCount = data.gravenImageCount + 1, + }, + { + id: 'DMU Graven Image Tether Collect', + // 271 ActorSetPos lines indicate where the tether is coming from + // 261 CombatantMemory lines may also indicate this + // Graven Image 1: + // (100, 56, 18.5) Center Tether, Will be target of BAA9 Pulse Wave (knockback) + // Graven Image 2: + // (102.5, 27, 22.5) Center Tether, Will be target of BAAC Gravitas (puddles) + // (126, 41.5, 7) Right Tether, Will be target of BAB0 Vitrophyre (rocks) + // Graven Image 3: + // (95, 25, 27) Left Tether, Will be target of BAB5 Indulgent Will which causes 503 Confused + // (107, 43, 8.5) Right tether, Will be target of BAB6 Idyllic Will which causes 131E Sleep + type: 'Tether', + netRegex: { id: headMarkerData['imageTether'], capture: true }, + condition: Conditions.targetIsYou(), + delaySeconds: 0.1, // Actor position data can come after tether in log + run: (data, matches) => { + const actor = data.actorPositions[matches.sourceId]; + if (actor === undefined) { + data.gravenImageTether = 'unknown'; + return; + } + + const x = actor.x; + // Graven Image 1: Pulse Wave target + if (x < 101 && x > 99) + data.gravenImageTether = 'pulse'; + else if (x < 103 && x > 101) // Graven Image 2: Gravitas target + data.gravenImageTether = 'gravitas'; + else if (x > 125) // Graven Image 2: Vitrophyre target + data.gravenImageTether = 'vitrophyre'; + else if (x < 100) // Graven Image 3: Indulgent Will target + data.gravenImageTether = 'indulgent'; + else if (x < 108 && x > 106) // Graven Image 3: Idyllic Will target + data.gravenImageTether = 'idyllic'; + else + data.gravenImageTether = 'unknown'; + }, + }, + { + id: 'DMU Pulse Wave Tethers', + type: 'Tether', + netRegex: { id: headMarkerData['imageTether'], capture: true }, + condition: (data, matches) => { + return data.me === matches.target && data.gravenImageCount === 1; + }, + delaySeconds: 0.1, // Actor position data can come after tether in log + durationSeconds: 7, + infoText: (data, matches, output) => { + const actor = data.actorPositions[matches.sourceId]; + if (actor === undefined) + return output.tetherOnYou!(); + + const x = actor.x; + // Graven Image 1: Pulse Wave target + if (x < 101 && x > 99) + return output.pulse!(); + return output.tetherOnYou!(); + }, + outputStrings: { + tetherOnYou: { + en: 'Tether on YOU', + de: 'Verbindung auf DIR', + fr: 'Lien sur VOUS', + ja: '線ついた', + cn: '连线点名', + ko: '선 대상자 지정됨', + tc: '連線點名', + }, + pulse: Outputs.knockback, // Cannot be immuned, happens within 6s of tether + }, + }, + { + id: 'DMU Gravitas and Vitrophyre Tethers 2', + type: 'Tether', + netRegex: { id: headMarkerData['imageTether'], capture: true }, + condition: (data, matches) => { + return data.me === matches.target && + data.isIceTrue !== undefined && + data.isThunderTrue === undefined && + data.isFireTrue === undefined; + }, + delaySeconds: 2, + durationSeconds: 6, + infoText: (data, matches, output) => { + const actor = data.actorPositions[matches.sourceId]; + if (actor === undefined) + return output.tetherOnYou!(); + + const x = actor.x; + if (x < 103 && x > 101) // Graven Image 2: Gravitas target + return output.gravitas!({ + mech1: output.puddle!(), + mech2: output.middle!(), + }); + if (x > 125) // Graven Image 2: Vitrophyre target + return output.vitrophyre!({ + mech1: output.puddle!(), + mech2: output.spread!(), + }); + return output.tetherOnYou!(); + }, + outputStrings: { + puddle: { + en: 'Bait Puddle', + de: 'Fläche ködern', + fr: 'Déposez', + ja: 'AOE誘導', + cn: '诱导AOE', + ko: '장판 유도', + tc: '誘導AOE', + }, + middle: Outputs.goIntoMiddle, + spread: Outputs.spread, + tetherOnYou: { + en: 'Tether on YOU', + de: 'Verbindung auf DIR', + fr: 'Lien sur VOUS', + ja: '線ついた', + cn: '连线点名', + ko: '선 대상자 지정됨', + tc: '連線點名', + }, + gravitas: { + en: '${mech1} => ${mech2}', + }, + vitrophyre: { + en: '${mech1} => ${mech2}', + }, + indulgent: { + en: 'Confuse Tether on YOU', + }, + idyllic: { + en: 'Sleep Tether on YOU', + }, + }, + }, + { + id: 'DMU P1 Vitrophyre', + // Trigger on BAAC Gravitas, ~4s to get away + type: 'Ability', + netRegex: { id: 'BAAC', source: 'Graven Image', capture: false }, + suppressSeconds: 1, + alertText: (data, _matches, output) => { + if (data.gravenImageTether === 'vitrophyre') + return output.spread!(); + return output.avoidTethers!(); + }, + outputStrings: { + avoidTethers: 'Avoid Tethered Players', + spread: 'Spread (avoid puddles)', + }, + }, + { + id: 'DMU Indulgent Will and Idyllic Will Tethers', + type: 'Tether', + netRegex: { id: headMarkerData['imageTether'], capture: true }, + condition: (data, matches) => { + return data.me === matches.target && data.gravenImageCount === 3; + }, + infoText: (data, matches, output) => { + const actor = data.actorPositions[matches.sourceId]; + if (actor === undefined) + return output.tetherOnYou!(); + + const x = actor.x; + if (x < 100) // Graven Image 3: Indulgent Will target + return output.indulgent!(); + if (x < 108 && x > 106) // Graven Image 3: Idyllic Will target + return output.idyllic!(); + return output.tetherOnYou!(); + }, + outputStrings: { + tetherOnYou: { + en: 'Tether on YOU', + de: 'Verbindung auf DIR', + fr: 'Lien sur VOUS', + ja: '線ついた', + cn: '连线点名', + ko: '선 대상자 지정됨', + tc: '連線點名', + }, + indulgent: { + en: 'Confuse Tether on YOU', + }, + idyllic: { + en: 'Sleep Tether on YOU', + }, + }, + }, + { + id: 'DMU P1 Graven Image Tether Cleanup', + // Clear on Ability: + // BAA9 Pulse Wave + // BAAC Gravitas + // BAB0 vitrophyre + // BAB5 Indulgent Will + // BAB6 Idyllic Will + type: 'Ability', + netRegex: { + id: ['BAA9', 'BAAC', 'BAB0', 'BAB5', 'BAB6'], + source: 'Graven Image', + capture: true, + }, + suppressSeconds: 1, + run: (data, matches) => { + // Player could die and this ability then not target them + // Need intelligent way to remove once related ability has executed + // Clear data if ability matches our tether + const abilityMap = { + 'pulse': 'BAAC', + 'gravitas': 'BAA9', + 'vitrophyre': 'BAB0', + 'indulgent': 'BAB5', + 'idyllic': 'BAB6', + 'unknown': 'unknown', + }; + const tether = data.gravenImageTether ?? 'unknown'; + const tetherAbilityId = abilityMap[tether]; + if (tetherAbilityId === matches.id || tether === 'unknown') + delete data.gravenImageTether; + }, + }, { id: 'DMU P1 CombatantMemory Tower Tracker', // 1EBFBB => Wave Cannon entity (blue) @@ -330,7 +601,7 @@ const triggerSet: TriggerSet = { outputStrings: mysteryMagicOutputStrings, }, { - id: 'DMU P1 Mystery Magic Ice Only', + id: 'DMU P1 Mystery Magic Ice, and Gravitas and Vitrophyre Tethers 1', // Occurs between Set 2 and Set 3 // BA95 Blizzard Blowout III cast type: 'StartsUsing', @@ -345,9 +616,18 @@ const triggerSet: TriggerSet = { return false; }, infoText: (data, _matches, output) => { + const hasVitrophyre = data.gravenImageTether === 'vitrophyre'; return data.isIceTrue - ? output.trueIce!() - : output.fakeIce!(); + ? output.trueIcePuddle!({ + mech1: output.trueIce!(), + mech2: output.puddle!(), + mech3: hasVitrophyre ? output.spread!() : output.middle!(), + }) + : output.fakeIcePuddle!({ + mech1: output.fakeIce!(), + mech2: output.puddle!(), + mech3: hasVitrophyre ? output.spread!() : output.middle!(), + }); }, outputStrings: mysteryMagicOutputStrings, }, From 5b9411b196d8c44741f78d10a1f2495263126ce5 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 4 Jun 2026 23:49:30 -0400 Subject: [PATCH 21/54] reorder triggers, lint, add trap knockback lines --- .../data/07-dt/ultimate/dancing_mad.ts | 822 +++++++++--------- .../data/07-dt/ultimate/dancing_mad.txt | 7 +- 2 files changed, 416 insertions(+), 413 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index 8414453f3d..5b541d474b 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -184,236 +184,6 @@ const triggerSet: TriggerSet = { heading: parseFloat(matches.heading), }, }, - { - id: 'DMU P1 Graven Image Counter', - // Used for timing of tether triggers - type: 'StartsUsing', - netRegex: { id: 'BCF2', source: 'Kefka', capture: false }, - run: (data) => data.gravenImageCount = data.gravenImageCount + 1, - }, - { - id: 'DMU Graven Image Tether Collect', - // 271 ActorSetPos lines indicate where the tether is coming from - // 261 CombatantMemory lines may also indicate this - // Graven Image 1: - // (100, 56, 18.5) Center Tether, Will be target of BAA9 Pulse Wave (knockback) - // Graven Image 2: - // (102.5, 27, 22.5) Center Tether, Will be target of BAAC Gravitas (puddles) - // (126, 41.5, 7) Right Tether, Will be target of BAB0 Vitrophyre (rocks) - // Graven Image 3: - // (95, 25, 27) Left Tether, Will be target of BAB5 Indulgent Will which causes 503 Confused - // (107, 43, 8.5) Right tether, Will be target of BAB6 Idyllic Will which causes 131E Sleep - type: 'Tether', - netRegex: { id: headMarkerData['imageTether'], capture: true }, - condition: Conditions.targetIsYou(), - delaySeconds: 0.1, // Actor position data can come after tether in log - run: (data, matches) => { - const actor = data.actorPositions[matches.sourceId]; - if (actor === undefined) { - data.gravenImageTether = 'unknown'; - return; - } - - const x = actor.x; - // Graven Image 1: Pulse Wave target - if (x < 101 && x > 99) - data.gravenImageTether = 'pulse'; - else if (x < 103 && x > 101) // Graven Image 2: Gravitas target - data.gravenImageTether = 'gravitas'; - else if (x > 125) // Graven Image 2: Vitrophyre target - data.gravenImageTether = 'vitrophyre'; - else if (x < 100) // Graven Image 3: Indulgent Will target - data.gravenImageTether = 'indulgent'; - else if (x < 108 && x > 106) // Graven Image 3: Idyllic Will target - data.gravenImageTether = 'idyllic'; - else - data.gravenImageTether = 'unknown'; - }, - }, - { - id: 'DMU Pulse Wave Tethers', - type: 'Tether', - netRegex: { id: headMarkerData['imageTether'], capture: true }, - condition: (data, matches) => { - return data.me === matches.target && data.gravenImageCount === 1; - }, - delaySeconds: 0.1, // Actor position data can come after tether in log - durationSeconds: 7, - infoText: (data, matches, output) => { - const actor = data.actorPositions[matches.sourceId]; - if (actor === undefined) - return output.tetherOnYou!(); - - const x = actor.x; - // Graven Image 1: Pulse Wave target - if (x < 101 && x > 99) - return output.pulse!(); - return output.tetherOnYou!(); - }, - outputStrings: { - tetherOnYou: { - en: 'Tether on YOU', - de: 'Verbindung auf DIR', - fr: 'Lien sur VOUS', - ja: '線ついた', - cn: '连线点名', - ko: '선 대상자 지정됨', - tc: '連線點名', - }, - pulse: Outputs.knockback, // Cannot be immuned, happens within 6s of tether - }, - }, - { - id: 'DMU Gravitas and Vitrophyre Tethers 2', - type: 'Tether', - netRegex: { id: headMarkerData['imageTether'], capture: true }, - condition: (data, matches) => { - return data.me === matches.target && - data.isIceTrue !== undefined && - data.isThunderTrue === undefined && - data.isFireTrue === undefined; - }, - delaySeconds: 2, - durationSeconds: 6, - infoText: (data, matches, output) => { - const actor = data.actorPositions[matches.sourceId]; - if (actor === undefined) - return output.tetherOnYou!(); - - const x = actor.x; - if (x < 103 && x > 101) // Graven Image 2: Gravitas target - return output.gravitas!({ - mech1: output.puddle!(), - mech2: output.middle!(), - }); - if (x > 125) // Graven Image 2: Vitrophyre target - return output.vitrophyre!({ - mech1: output.puddle!(), - mech2: output.spread!(), - }); - return output.tetherOnYou!(); - }, - outputStrings: { - puddle: { - en: 'Bait Puddle', - de: 'Fläche ködern', - fr: 'Déposez', - ja: 'AOE誘導', - cn: '诱导AOE', - ko: '장판 유도', - tc: '誘導AOE', - }, - middle: Outputs.goIntoMiddle, - spread: Outputs.spread, - tetherOnYou: { - en: 'Tether on YOU', - de: 'Verbindung auf DIR', - fr: 'Lien sur VOUS', - ja: '線ついた', - cn: '连线点名', - ko: '선 대상자 지정됨', - tc: '連線點名', - }, - gravitas: { - en: '${mech1} => ${mech2}', - }, - vitrophyre: { - en: '${mech1} => ${mech2}', - }, - indulgent: { - en: 'Confuse Tether on YOU', - }, - idyllic: { - en: 'Sleep Tether on YOU', - }, - }, - }, - { - id: 'DMU P1 Vitrophyre', - // Trigger on BAAC Gravitas, ~4s to get away - type: 'Ability', - netRegex: { id: 'BAAC', source: 'Graven Image', capture: false }, - suppressSeconds: 1, - alertText: (data, _matches, output) => { - if (data.gravenImageTether === 'vitrophyre') - return output.spread!(); - return output.avoidTethers!(); - }, - outputStrings: { - avoidTethers: 'Avoid Tethered Players', - spread: 'Spread (avoid puddles)', - }, - }, - { - id: 'DMU Indulgent Will and Idyllic Will Tethers', - type: 'Tether', - netRegex: { id: headMarkerData['imageTether'], capture: true }, - condition: (data, matches) => { - return data.me === matches.target && data.gravenImageCount === 3; - }, - infoText: (data, matches, output) => { - const actor = data.actorPositions[matches.sourceId]; - if (actor === undefined) - return output.tetherOnYou!(); - - const x = actor.x; - if (x < 100) // Graven Image 3: Indulgent Will target - return output.indulgent!(); - if (x < 108 && x > 106) // Graven Image 3: Idyllic Will target - return output.idyllic!(); - return output.tetherOnYou!(); - }, - outputStrings: { - tetherOnYou: { - en: 'Tether on YOU', - de: 'Verbindung auf DIR', - fr: 'Lien sur VOUS', - ja: '線ついた', - cn: '连线点名', - ko: '선 대상자 지정됨', - tc: '連線點名', - }, - indulgent: { - en: 'Confuse Tether on YOU', - }, - idyllic: { - en: 'Sleep Tether on YOU', - }, - }, - }, - { - id: 'DMU P1 Graven Image Tether Cleanup', - // Clear on Ability: - // BAA9 Pulse Wave - // BAAC Gravitas - // BAB0 vitrophyre - // BAB5 Indulgent Will - // BAB6 Idyllic Will - type: 'Ability', - netRegex: { - id: ['BAA9', 'BAAC', 'BAB0', 'BAB5', 'BAB6'], - source: 'Graven Image', - capture: true, - }, - suppressSeconds: 1, - run: (data, matches) => { - // Player could die and this ability then not target them - // Need intelligent way to remove once related ability has executed - // Clear data if ability matches our tether - const abilityMap = { - 'pulse': 'BAAC', - 'gravitas': 'BAA9', - 'vitrophyre': 'BAB0', - 'indulgent': 'BAB5', - 'idyllic': 'BAB6', - 'unknown': 'unknown', - }; - const tether = data.gravenImageTether ?? 'unknown'; - const tetherAbilityId = abilityMap[tether]; - if (tetherAbilityId === matches.id || tether === 'unknown') - delete data.gravenImageTether; - }, - }, { id: 'DMU P1 CombatantMemory Tower Tracker', // 1EBFBB => Wave Cannon entity (blue) @@ -507,6 +277,85 @@ const triggerSet: TriggerSet = { }, }, }, + { + id: 'DMU P1 Graven Image Counter', + // Used for timing of tether triggers + type: 'StartsUsing', + netRegex: { id: 'BCF2', source: 'Kefka', capture: false }, + run: (data) => data.gravenImageCount = data.gravenImageCount + 1, + }, + { + id: 'DMU Graven Image Tether Collect', + // 271 ActorSetPos lines indicate where the tether is coming from + // 261 CombatantMemory lines may also indicate this + // Graven Image 1: + // (100, 56, 18.5) Center Tether, Will be target of BAA9 Pulse Wave (knockback) + // Graven Image 2: + // (102.5, 27, 22.5) Center Tether, Will be target of BAAC Gravitas (puddles) + // (126, 41.5, 7) Right Tether, Will be target of BAB0 Vitrophyre (rocks) + // Graven Image 3: + // (95, 25, 27) Left Tether, Will be target of BAB5 Indulgent Will which causes 503 Confused + // (107, 43, 8.5) Right tether, Will be target of BAB6 Idyllic Will which causes 131E Sleep + type: 'Tether', + netRegex: { id: headMarkerData['imageTether'], capture: true }, + condition: Conditions.targetIsYou(), + delaySeconds: 0.1, // Actor position data can come after tether in log + run: (data, matches) => { + const actor = data.actorPositions[matches.sourceId]; + if (actor === undefined) { + data.gravenImageTether = 'unknown'; + return; + } + + const x = actor.x; + // Graven Image 1: Pulse Wave target + if (x < 101 && x > 99) + data.gravenImageTether = 'pulse'; + else if (x < 103 && x > 101) // Graven Image 2: Gravitas target + data.gravenImageTether = 'gravitas'; + else if (x > 125) // Graven Image 2: Vitrophyre target + data.gravenImageTether = 'vitrophyre'; + else if (x < 100) // Graven Image 3: Indulgent Will target + data.gravenImageTether = 'indulgent'; + else if (x < 108 && x > 106) // Graven Image 3: Idyllic Will target + data.gravenImageTether = 'idyllic'; + else + data.gravenImageTether = 'unknown'; + }, + }, + { + id: 'DMU Pulse Wave Tethers', + type: 'Tether', + netRegex: { id: headMarkerData['imageTether'], capture: true }, + condition: (data, matches) => { + return data.me === matches.target && data.gravenImageCount === 1; + }, + delaySeconds: 0.1, // Actor position data can come after tether in log + durationSeconds: 7, + infoText: (data, matches, output) => { + const actor = data.actorPositions[matches.sourceId]; + if (actor === undefined) + return output.tetherOnYou!(); + + const x = actor.x; + // Graven Image 1: Pulse Wave target + if (x < 101 && x > 99) + return output.pulse!(); + return output.tetherOnYou!(); + }, + outputStrings: { + tetherOnYou: { + en: 'Tether on YOU', + de: 'Verbindung auf DIR', + fr: 'Lien sur VOUS', + ja: '線ついた', + cn: '连线点名', + ko: '선 대상자 지정됨', + tc: '連線點名', + }, + pulse: Outputs.knockback, // Cannot be immuned, happens within 6s of tether + }, + }, { id: 'DMU P1 Mystery Magic Collect', type: 'HeadMarker', @@ -580,98 +429,6 @@ const triggerSet: TriggerSet = { }, outputStrings: mysteryMagicOutputStrings, }, - { - id: 'DMU P1 Mystery Magic Ice and Thunder', - // Set 2: Only Ice and Thunder should be set - type: 'StartsUsing', - netRegex: { id: 'BA94', source: 'Kefka', capture: false }, - condition: (data) => { - return data.isIceTrue !== undefined && data.isThunderTrue !== undefined; - }, - infoText: (data, _matches, output) => { - if (data.isThunderTrue) { - return data.isIceTrue - ? output.trueIceTrueThunder!() - : output.fakeIceTrueThunder!(); - } - return data.isIceTrue - ? output.trueIceTrueThunder!() - : output.fakeIceFakeThunder!(); - }, - outputStrings: mysteryMagicOutputStrings, - }, - { - id: 'DMU P1 Mystery Magic Ice, and Gravitas and Vitrophyre Tethers 1', - // Occurs between Set 2 and Set 3 - // BA95 Blizzard Blowout III cast - type: 'StartsUsing', - netRegex: { id: 'BA95', source: 'Kefka', capture: false }, - condition: (data) => { - if ( - data.isIceTrue !== undefined && - data.isThunderTrue === undefined && - data.isFireTrue === undefined - ) - return true; - return false; - }, - infoText: (data, _matches, output) => { - const hasVitrophyre = data.gravenImageTether === 'vitrophyre'; - return data.isIceTrue - ? output.trueIcePuddle!({ - mech1: output.trueIce!(), - mech2: output.puddle!(), - mech3: hasVitrophyre ? output.spread!() : output.middle!(), - }) - : output.fakeIcePuddle!({ - mech1: output.fakeIce!(), - mech2: output.puddle!(), - mech3: hasVitrophyre ? output.spread!() : output.middle!(), - }); - }, - outputStrings: mysteryMagicOutputStrings, - }, - { - id: 'DMU P1 Mystery Magic Fire and Thunder', - // Set 3: Only Fire and Thunder should be set - type: 'StartsUsing', - netRegex: { id: 'BA94', source: 'Kefka', capture: false }, - condition: (data) => { - return data.isFireTrue !== undefined && data.isThunderTrue !== undefined; - }, - infoText: (data, _matches, output) => { - const fireMarker = data.fireMarker; - if ( - (fireMarker === headMarkerData['dorito'] && data.isFireTrue) || - (fireMarker === headMarkerData['stack'] && !data.isFireTrue) - ) - return data.isThunderTrue - ? output.spreadTrueThunder!({ - mech: output.spread!(), - thunder: output.trueThunder!(), - }) - : output.spreadFakeThunder!({ - mech: output.spread!(), - thunder: output.fakeThunder!(), - }); - - if ( - (fireMarker === headMarkerData['dorito'] && !data.isFireTrue) || - (fireMarker === headMarkerData['stack'] && data.isFireTrue) - ) { - return data.isThunderTrue - ? output.stackTrueThunder!({ - mech: output.stack!(), - thunder: output.trueThunder!(), - }) - : output.stackFakeThunder!({ - mech: output.stack!(), - thunder: output.fakeThunder!(), - }); - } - }, - outputStrings: mysteryMagicOutputStrings, - }, { id: 'DMU P1 Mystery Magic Cleanup', // C622 Light of Judgment to reset for the Graven Image 2 @@ -684,6 +441,39 @@ const triggerSet: TriggerSet = { delete data.fireMarker; }, }, + { + id: 'DMU P1 Graven Image Tether Cleanup', + // Clear on Ability: + // BAA9 Pulse Wave + // BAAC Gravitas + // BAB0 vitrophyre + // BAB5 Indulgent Will + // BAB6 Idyllic Will + type: 'Ability', + netRegex: { + id: ['BAA9', 'BAAC', 'BAB0', 'BAB5', 'BAB6'], + source: 'Graven Image', + capture: true, + }, + suppressSeconds: 1, + run: (data, matches) => { + // Player could die and this ability then not target them + // Need intelligent way to remove once related ability has executed + // Clear data if ability matches our tether + const abilityMap = { + 'pulse': 'BAAC', + 'gravitas': 'BAA9', + 'vitrophyre': 'BAB0', + 'indulgent': 'BAB5', + 'idyllic': 'BAB6', + 'unknown': 'unknown', + }; + const tether = data.gravenImageTether ?? 'unknown'; + const tetherAbilityId = abilityMap[tether]; + if (tetherAbilityId === matches.id || tether === 'unknown') + delete data.gravenImageTether; + }, + }, { id: 'DMU P1 Wave Cannon', // BAA8 Wave Cannon is an instant cast from Graven Image @@ -707,6 +497,13 @@ const triggerSet: TriggerSet = { netRegex: { id: 'BAA8', source: 'Graven Image', capture: true }, run: (data, matches) => data.waveCannonTargets.push(matches.target), }, + { + id: 'DMU P1 Double-trouble Trap Collect', + // Times are 5s, 68s, and 49s + type: 'GainsEffect', + netRegex: { effectId: '13D6', capture: true }, + run: (data, matches) => data.doubleTroubleTrapTargets.push(matches.target), + }, { id: 'DMU P1 Wave Cannon Explosion Towers', // Wave Cannon gives a vulnerability which causes death to BAAA Explosion soaks @@ -758,38 +555,147 @@ const triggerSet: TriggerSet = { }, }, { - id: 'DMU P1 Double-trouble Trap Collect', - // Times are 5s, 68s, and 49s + id: 'DMU P1 Double-trouble Trap 1', type: 'GainsEffect', netRegex: { effectId: '13D6', capture: true }, - run: (data, matches) => data.doubleTroubleTrapTargets.push(matches.target), + condition: (_data, matches) => parseFloat(matches.duration) < 6, + delaySeconds: 0.1, + suppressSeconds: 1, + response: (data, _matches, output) => { + // cactbot-builtin-response + output.responseOutputStrings = trapOutputStrings; + + const severity = data.doubleTroubleTrapTargets.includes(data.me) ? 'alertText' : 'infoText'; + const players = data.doubleTroubleTrapTargets.map( + (player) => { + if (player === data.me) + return 'YOU'; + return data.party.member(player); + }, + ); + const msg = players?.join(', '); + return { [severity]: output.knockbackFrom!({ players: msg }) }; + }, + }, + { + id: 'DMU P1 Double-trouble Trap Cleanup', + // Players dying will also trigger this + type: 'LosesEffect', + netRegex: { effectId: '13D6', capture: true }, + run: (data, matches) => { + data.doubleTroubleTrapTargets = data.doubleTroubleTrapTargets.filter( + (target) => target !== matches.target, + ); + }, + }, + { + id: 'DMU P1 Double-trouble Trap 2 Early', + type: 'GainsEffect', + netRegex: { effectId: '13D6', capture: true }, + delaySeconds: 0.1, + suppressSeconds: 1, + infoText: (data, matches, output) => { + // Ignore first set and third set + if (parseFloat(matches.duration) < 67) + return; + + // Check if players died + if (data.doubleTroubleTrapTargets[0] === undefined) + return; + + const players = data.doubleTroubleTrapTargets.map( + (player) => { + if (player === data.me) + return 'YOU'; + return data.party.member(player); + }, + ); + const msg = players?.join(', '); + return output.knockbackFromLater!({ players: msg }); + }, + outputStrings: trapOutputStrings, + }, + { + id: 'DMU P1 Mystery Magic Ice and Thunder', + // Set 2: Only Ice and Thunder should be set + type: 'StartsUsing', + netRegex: { id: 'BA94', source: 'Kefka', capture: false }, + condition: (data) => { + return data.isIceTrue !== undefined && data.isThunderTrue !== undefined; + }, + infoText: (data, _matches, output) => { + if (data.isThunderTrue) { + return data.isIceTrue + ? output.trueIceTrueThunder!() + : output.fakeIceTrueThunder!(); + } + return data.isIceTrue + ? output.trueIceTrueThunder!() + : output.fakeIceFakeThunder!(); + }, + outputStrings: mysteryMagicOutputStrings, + }, + { + id: 'DMU P1 Light of Judgment', + type: 'StartsUsing', + netRegex: { id: 'C622', source: 'Kefka', capture: false }, + response: Responses.bigAoe(), + }, + { + id: 'DMU P1 Hyperdrive', + // This hits three times + // Occurs 3.1s after C622 Light of Judgment, which is a 5s cast + type: 'StartsUsing', + netRegex: { id: 'C622', source: 'Kefka', capture: true }, + delaySeconds: (_data, matches) => parseFloat(matches.castTime) - 2, // Result in ~5.1s warning + response: Responses.tankBuster(), + }, + { + id: 'DMU P1 Mystery Magic Ice, and Gravitas and Vitrophyre Tethers 1', + // Occurs between Set 2 and Set 3 + // BA95 Blizzard Blowout III cast + type: 'StartsUsing', + netRegex: { id: 'BA95', source: 'Kefka', capture: false }, + condition: (data) => { + if ( + data.isIceTrue !== undefined && + data.isThunderTrue === undefined && + data.isFireTrue === undefined + ) + return true; + return false; + }, + infoText: (data, _matches, output) => { + const hasVitrophyre = data.gravenImageTether === 'vitrophyre'; + return data.isIceTrue + ? output.trueIcePuddle!({ + mech1: output.trueIce!(), + mech2: output.puddle!(), + mech3: hasVitrophyre ? output.spread!() : output.middle!(), + }) + : output.fakeIcePuddle!({ + mech1: output.fakeIce!(), + mech2: output.puddle!(), + mech3: hasVitrophyre ? output.spread!() : output.middle!(), + }); + }, + outputStrings: mysteryMagicOutputStrings, }, { - id: 'DMU P1 Double-trouble Trap 2 Early', - type: 'GainsEffect', - netRegex: { effectId: '13D6', capture: true }, - delaySeconds: 0.1, + id: 'DMU P1 Vitrophyre', + // Trigger on BAAC Gravitas, ~4s to get away + type: 'Ability', + netRegex: { id: 'BAAC', source: 'Graven Image', capture: false }, suppressSeconds: 1, - infoText: (data, matches, output) => { - // Ignore first set and third set - if (parseFloat(matches.duration) < 67) - return; - - // Check if players died - if (data.doubleTroubleTrapTargets[0] === undefined) - return; - - const players = data.doubleTroubleTrapTargets.map( - (player) => { - if (player === data.me) - return 'YOU'; - return data.party.member(player); - }, - ); - const msg = players?.join(', '); - return output.knockbackFromLater!({ players: msg }); + alertText: (data, _matches, output) => { + if (data.gravenImageTether === 'vitrophyre') + return output.spread!(); + return output.avoidTethers!(); + }, + outputStrings: { + avoidTethers: 'Avoid Tethered Players', + spread: 'Spread (avoid puddles)', }, - outputStrings: trapOutputStrings, }, { id: 'DMU P1 Double-trouble Trap 3 Early', @@ -820,26 +726,86 @@ const triggerSet: TriggerSet = { outputStrings: trapOutputStrings, }, { - id: 'DMU P1 Double-trouble Trap 1', - type: 'GainsEffect', - netRegex: { effectId: '13D6', capture: true }, - condition: (_data, matches) => parseFloat(matches.duration) < 6, - delaySeconds: 0.1, - suppressSeconds: 1, - response: (data, _matches, output) => { - // cactbot-builtin-response - output.responseOutputStrings = trapOutputStrings; + id: 'DMU P1 Impertinent Will/Gravitational Wave', + type: 'ActorControlExtra', + netRegex: { category: '019D', param1: '40', param2: '80', capture: true }, + alertText: (data, matches, output) => { + const id = matches.id; + if (data.yellowTowerIds.indexOf(id) !== -1) { + return output.goWest!(); + } + if (data.purpleTowerIds.indexOf(id) !== -1) { + return output.goEast!(); + } + }, + outputStrings: { + goWest: Outputs.getLeftAndWest, + goEast: Outputs.getRightAndEast, + }, + }, + { + id: 'DMU Gravitas and Vitrophyre Tethers 2', + type: 'Tether', + netRegex: { id: headMarkerData['imageTether'], capture: true }, + condition: (data, matches) => { + return data.me === matches.target && + data.isIceTrue !== undefined && + data.isThunderTrue === undefined && + data.isFireTrue === undefined; + }, + delaySeconds: 2, + durationSeconds: 6, + infoText: (data, matches, output) => { + const actor = data.actorPositions[matches.sourceId]; + if (actor === undefined) + return output.tetherOnYou!(); - const severity = data.doubleTroubleTrapTargets.includes(data.me) ? 'alertText' : 'infoText'; - const players = data.doubleTroubleTrapTargets.map( - (player) => { - if (player === data.me) - return 'YOU'; - return data.party.member(player); - }, - ); - const msg = players?.join(', '); - return { [severity]: output.knockbackFrom!({ players: msg }) }; + const x = actor.x; + if (x < 103 && x > 101) // Graven Image 2: Gravitas target + return output.gravitas!({ + mech1: output.puddle!(), + mech2: output.middle!(), + }); + if (x > 125) // Graven Image 2: Vitrophyre target + return output.vitrophyre!({ + mech1: output.puddle!(), + mech2: output.spread!(), + }); + return output.tetherOnYou!(); + }, + outputStrings: { + puddle: { + en: 'Bait Puddle', + de: 'Fläche ködern', + fr: 'Déposez', + ja: 'AOE誘導', + cn: '诱导AOE', + ko: '장판 유도', + tc: '誘導AOE', + }, + middle: Outputs.goIntoMiddle, + spread: Outputs.spread, + tetherOnYou: { + en: 'Tether on YOU', + de: 'Verbindung auf DIR', + fr: 'Lien sur VOUS', + ja: '線ついた', + cn: '连线点名', + ko: '선 대상자 지정됨', + tc: '連線點名', + }, + gravitas: { + en: '${mech1} => ${mech2}', + }, + vitrophyre: { + en: '${mech1} => ${mech2}', + }, + indulgent: { + en: 'Confuse Tether on YOU', + }, + idyllic: { + en: 'Sleep Tether on YOU', + }, }, }, { @@ -899,50 +865,6 @@ const triggerSet: TriggerSet = { return { [severity]: output.knockbackFrom!({ players: msg }) }; }, }, - { - id: 'DMU P1 Double-trouble Trap Cleanup', - // Players dying will also trigger this - type: 'LosesEffect', - netRegex: { effectId: '13D6', capture: true }, - run: (data, matches) => { - data.doubleTroubleTrapTargets = data.doubleTroubleTrapTargets.filter( - (target) => target !== matches.target, - ); - }, - }, - { - id: 'DMU P1 Light of Judgment', - type: 'StartsUsing', - netRegex: { id: 'C622', source: 'Kefka', capture: false }, - response: Responses.bigAoe(), - }, - { - id: 'DMU P1 Hyperdrive', - // This hits three times - // Occurs 3.1s after C622 Light of Judgment, which is a 5s cast - type: 'StartsUsing', - netRegex: { id: 'C622', source: 'Kefka', capture: true }, - delaySeconds: (_data, matches) => parseFloat(matches.castTime) - 2, // Result in ~5.1s warning - response: Responses.tankBuster(), - }, - { - id: 'DMU P1 Impertinent Will/Gravitational Wave', - type: 'ActorControlExtra', - netRegex: { category: '019D', param1: '40', param2: '80', capture: true }, - alertText: (data, matches, output) => { - const id = matches.id; - if (data.yellowTowerIds.indexOf(id) !== -1) { - return output.goWest!(); - } - if (data.purpleTowerIds.indexOf(id) !== -1) { - return output.goEast!(); - } - }, - outputStrings: { - goWest: Outputs.getLeftAndWest, - goEast: Outputs.getRightAndEast, - }, - }, { id: 'DMU P1 Tele-Portent Collect', // Debuffs distributed to 8 players: @@ -1127,6 +1049,84 @@ const triggerSet: TriggerSet = { delete data.myTelePortent2; }, }, + { + id: 'DMU Indulgent Will and Idyllic Will Tethers', + type: 'Tether', + netRegex: { id: headMarkerData['imageTether'], capture: true }, + condition: (data, matches) => { + return data.me === matches.target && data.gravenImageCount === 3; + }, + infoText: (data, matches, output) => { + const actor = data.actorPositions[matches.sourceId]; + if (actor === undefined) + return output.tetherOnYou!(); + + const x = actor.x; + if (x < 100) // Graven Image 3: Indulgent Will target + return output.indulgent!(); + if (x < 108 && x > 106) // Graven Image 3: Idyllic Will target + return output.idyllic!(); + return output.tetherOnYou!(); + }, + outputStrings: { + tetherOnYou: { + en: 'Tether on YOU', + de: 'Verbindung auf DIR', + fr: 'Lien sur VOUS', + ja: '線ついた', + cn: '连线点名', + ko: '선 대상자 지정됨', + tc: '連線點名', + }, + indulgent: { + en: 'Confuse Tether on YOU', + }, + idyllic: { + en: 'Sleep Tether on YOU', + }, + }, + }, + { + id: 'DMU P1 Mystery Magic Fire and Thunder', + // Set 3: Only Fire and Thunder should be set + type: 'StartsUsing', + netRegex: { id: 'BA94', source: 'Kefka', capture: false }, + condition: (data) => { + return data.isFireTrue !== undefined && data.isThunderTrue !== undefined; + }, + infoText: (data, _matches, output) => { + const fireMarker = data.fireMarker; + if ( + (fireMarker === headMarkerData['dorito'] && data.isFireTrue) || + (fireMarker === headMarkerData['stack'] && !data.isFireTrue) + ) + return data.isThunderTrue + ? output.spreadTrueThunder!({ + mech: output.spread!(), + thunder: output.trueThunder!(), + }) + : output.spreadFakeThunder!({ + mech: output.spread!(), + thunder: output.fakeThunder!(), + }); + + if ( + (fireMarker === headMarkerData['dorito'] && !data.isFireTrue) || + (fireMarker === headMarkerData['stack'] && data.isFireTrue) + ) { + return data.isThunderTrue + ? output.stackTrueThunder!({ + mech: output.stack!(), + thunder: output.trueThunder!(), + }) + : output.stackFakeThunder!({ + mech: output.stack!(), + thunder: output.fakeThunder!(), + }); + } + }, + outputStrings: mysteryMagicOutputStrings, + }, ], timelineReplace: [ { diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt index 40f49ce8a5..2f89a9b1b3 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt @@ -28,7 +28,8 @@ hideall "--sync--" 42.5 "Wave Cannon" Ability { id: "BAA8", source: "Graven Image" } 44.6 "Double-trouble Trap" Ability { id: "BAA6", source: "Kefka" } 46.0 "Explosion" Ability { id: "BAAA", source: "Kefka" } -49.7 "Double-trouble Trap" Ability { id: "BAA7", source: "Kefka" } +49.6 "--knockback--" # From Double-trouble Trap 1 +49.7 "Double-trouble Trap 2" Ability { id: "BAA7", source: "Kefka" } 53.7 "Mystery Magic" Ability { id: "BA94", source: "Kefka" } 53.7 "Thrumming Thunder III" #Ability { id: ["BAA1", "BA9F"], source: "Kefka" } 53.7 "Blizzard III Blowout" #Ability { id: ["BA9B", "BA98"], source: "Kefka" } @@ -48,7 +49,8 @@ hideall "--sync--" 105.8 "Gravitas" Ability { id: "BAAC", source: "Graven Image" } 109.8 "Vitrophyre" Ability { id: "BAB0", source: "Graven Image" } 114.4 "Intemperate Will/Gravitational Wave" Ability { id: ["BAB2", "BAB1"], source: "Graven Image" } -118.9 "Double-Trouble Trap" Ability { id: "BAA7", source: "Kefka" } # NOTE: If it was passed after first set. +118.0 "--knockback--" # From Double-trouble Trap 2 +118.1 "Double-Trouble Trap" Ability { id: "BAA7", source: "Kefka" } # NOTE: If it was passed after first set. 121.3 "Gravity III" #Ability { id: "BAAF", source: "Kefka" } # TODO: Adjust timing/wording to puddles safe to pop, make it a duration? 132.4 "Light of Judgment" Ability { id: "C622", source: "Kefka" } 135.6 "Hyperdrive 1" #Ability { id: "C24B", source: "Kefka" } @@ -60,6 +62,7 @@ hideall "--sync--" 163.6 "Graven Image 3" Ability { id: "BCF2", source: "Kefka" } 168.7 "--sync--" Ability { id: "C554", source: "Kefka" } +170.6 "--knockback--" # From Double-trouble Trap 3 173.4 "Indulgent Will" Ability { id: "BAB5", source: "Graven Image" } 173.4 "Idyllic Will" #Ability { id: "BAB6", source: "Graven Image" } 177.7 "--sync--" Ability { id: "C555", source: "Kefka" } From fb675b79cf0e832dbb3ec6cf1e7c07f43fcf4aff Mon Sep 17 00:00:00 2001 From: Legends0 Date: Fri, 5 Jun 2026 00:02:27 -0400 Subject: [PATCH 22/54] double-trouble trap and gravity timing adjust --- ui/raidboss/data/07-dt/ultimate/dancing_mad.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt index 2f89a9b1b3..5276443d0c 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt @@ -26,7 +26,7 @@ hideall "--sync--" 37.4 "Blizzard III Blowout" #Ability { id: ["BA9B", "BA98"], source: "Kefka" } 38.3 "Flagrant Fire III" Ability { id: ["BAA2", "BAA3"], source: "Kefka" } 42.5 "Wave Cannon" Ability { id: "BAA8", source: "Graven Image" } -44.6 "Double-trouble Trap" Ability { id: "BAA6", source: "Kefka" } +44.6 "Double-trouble Trap 1" Ability { id: "BAA6", source: "Kefka" } 46.0 "Explosion" Ability { id: "BAAA", source: "Kefka" } 49.6 "--knockback--" # From Double-trouble Trap 1 49.7 "Double-trouble Trap 2" Ability { id: "BAA7", source: "Kefka" } @@ -49,9 +49,9 @@ hideall "--sync--" 105.8 "Gravitas" Ability { id: "BAAC", source: "Graven Image" } 109.8 "Vitrophyre" Ability { id: "BAB0", source: "Graven Image" } 114.4 "Intemperate Will/Gravitational Wave" Ability { id: ["BAB2", "BAB1"], source: "Graven Image" } +117.0 "Gravity III (Pop Window)" #Ability { id: "BAAF", source: "Kefka" } duration 6 # ~1s remaining on debuff and until 45s on next 118.0 "--knockback--" # From Double-trouble Trap 2 -118.1 "Double-Trouble Trap" Ability { id: "BAA7", source: "Kefka" } # NOTE: If it was passed after first set. -121.3 "Gravity III" #Ability { id: "BAAF", source: "Kefka" } # TODO: Adjust timing/wording to puddles safe to pop, make it a duration? +118.1 "Double-Trouble Trap 3" Ability { id: "BAA7", source: "Kefka" } # NOTE: If it was passed after first set. 132.4 "Light of Judgment" Ability { id: "C622", source: "Kefka" } 135.6 "Hyperdrive 1" #Ability { id: "C24B", source: "Kefka" } 137.7 "Hyperdrive 2" #Ability { id: "C24B", source: "Kefka" } From 763996f24901e988177eb0f5012e287606da13a4 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Fri, 5 Jun 2026 00:09:53 -0400 Subject: [PATCH 23/54] fix mystery magic cleanup order --- .../data/07-dt/ultimate/dancing_mad.ts | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index 5b541d474b..35c1e0b8c9 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -429,18 +429,6 @@ const triggerSet: TriggerSet = { }, outputStrings: mysteryMagicOutputStrings, }, - { - id: 'DMU P1 Mystery Magic Cleanup', - // C622 Light of Judgment to reset for the Graven Image 2 - type: 'StartsUsing', - netRegex: { id: ['BA94', 'C622'], source: 'Kefka', capture: false }, - run: (data) => { - delete data.isFireTrue; - delete data.isIceTrue; - delete data.isThunderTrue; - delete data.fireMarker; - }, - }, { id: 'DMU P1 Graven Image Tether Cleanup', // Clear on Ability: @@ -1127,6 +1115,18 @@ const triggerSet: TriggerSet = { }, outputStrings: mysteryMagicOutputStrings, }, + { + id: 'DMU P1 Mystery Magic Cleanup', + // C622 Light of Judgment to reset for the Graven Image 2 + type: 'StartsUsing', + netRegex: { id: ['BA94', 'C622'], source: 'Kefka', capture: false }, + run: (data) => { + delete data.isFireTrue; + delete data.isIceTrue; + delete data.isThunderTrue; + delete data.fireMarker; + }, + }, ], timelineReplace: [ { From acdbfc05ae639190c9bb1360534a1ab73f3d3935 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Fri, 5 Jun 2026 00:13:18 -0400 Subject: [PATCH 24/54] remove done TODOs --- ui/raidboss/data/07-dt/ultimate/dancing_mad.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index 35c1e0b8c9..35ada1b520 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -5,9 +5,6 @@ import ZoneId from '../../../../../resources/zone_id'; import { RaidbossData } from '../../../../../types/data'; import { OutputStrings, TriggerSet } from '../../../../../types/trigger'; -// TODO: P1 Tethers -// TODO: P1 Halfroom Cleaves -// TODO: P1 Replace Mystery Magic Ice Only with tether combination // TODO: P1 Tele-Portent configuration options type Phase = 'p1' | 'p2' | 'p3'; From bc68a25645efe841d9e19ec3e588b4efb508e301 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Fri, 5 Jun 2026 03:49:58 -0400 Subject: [PATCH 25/54] p2 and p3 timelines --- .../data/07-dt/ultimate/dancing_mad.ts | 21 ++ .../data/07-dt/ultimate/dancing_mad.txt | 298 ++++++++++++++---- 2 files changed, 256 insertions(+), 63 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index 35ada1b520..4f3313b2f9 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -41,6 +41,7 @@ export interface Data extends RaidbossData { doubleTroubleTrapTargets: string[]; myTelePortent1?: 'up' | 'down' | 'right' | 'left'; myTelePortent2?: 'up' | 'down' | 'right' | 'left'; + // Phase 2 } const headMarkerData = { @@ -160,6 +161,7 @@ const triggerSet: TriggerSet = { gravenImageCount: 0, waveCannonTargets: [], doubleTroubleTrapTargets: [], + // Phase 2 }; }, triggers: [ @@ -1124,12 +1126,31 @@ const triggerSet: TriggerSet = { delete data.fireMarker; }, }, + { + id: 'DMU P2 Future\'s End/Past\'s End', + // There are four end casts + type: 'StartsUsing', + netRegex: { id: ['BAD2', 'BAD3'], source: 'Kefka', capture: true }, + infoText: (_data, matches, output) => { + return matches.id === 'BAD2' ? output.future!() : output.past!(); + }, + outputStrings: { + future: { + en: 'Future', + }, + past: { + en: 'Past', + }, + }, + }, ], timelineReplace: [ { 'locale': 'en', 'replaceText': { 'Future\'s End/Past\'s End': 'Future/Past\'s End', + 'Spelldriver/Spellscatter/Spellwave': 'Spelldriver/scatter/wave', + 'Longitudinal Implosion/Latitudinal Implosion': 'Long/Lat Implosion', }, }, ], diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt index 5276443d0c..420698430b 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt @@ -1,7 +1,7 @@ ### DANCING MAD (ULTIMATE) # ZoneId: 1363 -# -ii C250 C252 BA9E BAA0 BAAB BA95 BAAF BAAD BAD6 BAD8 BAD7 BAD9 +# -ii C250 C252 BA9E BAA0 BAAB BA95 BAAF BAAD BAD6 BAD8 BAD7 BAD9 BAF9 BAF1 # -p C403:15.6 C24C:216.5 C3F7:500.0 # -it Kefka Chaos Exdeath @@ -75,61 +75,49 @@ hideall "--sync--" ### Phase 2 - God Kefka # TODO: Update with network log, this uses FFLOGS -# TODO: Get enrage # TODO: Get fake ending route? -# TODO: Add voiceline sync? +# TODO: Add voiceline sync / branch to complete? +# Complete Path: # en: 'Yes... I am filled with glorious purpose!' +# Duty Complete Path: +# en: 'How boring. Guess I'll have to spice things up!' 216.5 "Ultimate Embrace" Ability { id: "C24C", source: "Kefka" } window 220,5 231.7 "Forsaken" Ability { id: "BABC", source: "Kefka" } -244.9 "The Path of Light 1" Ability { id: "BABE", source: "Kefka" } -245.6 "Spelldriver" #Ability { id: "BAC0", source: "Kefka" } -245.6 "Spellwave" #Ability { id: "BAC2", source: "Kefka" } -245.6 "Spellscatter" #Ability { id: "BAC1", source: "Kefka" } -245.6 "Spelldriver" #Ability { id: "BAC0", source: "Kefka" } -254.3 "Future's End/Past's End" Ability { id: ["BAD2", "BAD3"], source: "Kefka" } +# Set 1 +# NOTE: The BAC0 Spelldriver, BAC2 Spellwave, BAC0 Spellscatter can be resolved in different orders +244.9 "The Path of Light 1" Ability { id: "BABE", source: "Kefka" } +245.6 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"] source: "Kefka" } +254.3 "Future's End/Past's End 1" Ability { id: ["BAD2", "BAD3"], source: "Kefka" } 255.0 "The Path of Light 2" Ability { id: "BABE", source: "Kefka" } -255.7 "Spellwave" #Ability { id: "BAC2", source: "Kefka" } -255.7 "Spellscatter" #Ability { id: "BAC1", source: "Kefka" } -265.7 "All Things Ending" #Ability { id: ["BACD", "BADD"], source: "Kefka" } +255.7 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"] source: "Kefka" } +265.7 "All Things Ending" #Ability { id: ["BACD", "BADC", "BADD"], source: "Kefka" } +# Set 2 265.9 "The Path of Light 3" Ability { id: "BABE", source: "Kefka" } -266.4 "Spelldriver" #Ability { id: "BAC0", source: "Kefka" } -266.4 "Spellwave" #Ability { id: "BAC2", source: "Kefka" } -266.4 "Spelldriver" #Ability { id: "BAC0", source: "Kefka" } -266.4 "Spellscatter" #Ability { id: "BAC1", source: "Kefka" } -275.4 "Future's End/Past's End" Ability { id: ["BAD2", "BAD3"], source: "Kefka" } - +266.4 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"] source: "Kefka" } +275.4 "Future's End/Past's End 2" Ability { id: ["BAD2", "BAD3"], source: "Kefka" } 275.8 "The Path of Light 4" Ability { id: "BABE", source: "Kefka" } -276.5 "Spellwave" #Ability { id: "BAC2", source: "Kefka" } -276.5 "Spellscatter" #Ability { id: "BAC1", source: "Kefka" } -276.5 "Spellscatter" #Ability { id: "BAC1", source: "Kefka" } +276.5 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"] source: "Kefka" } 286.7 "All Things Ending" #Ability { id: ["BACD", "BADD"], source: "Kefka" } +# Set 3 286.7 "The Path of Light 5" Ability { id: "BABE", source: "Kefka" } -287.1 "Spelldriver" #Ability { id: "BAC0", source: "Kefka" } -287.1 "Spellwave" #Ability { id: "BAC2", source: "Kefka" } -287.1 "Spelldriver" #Ability { id: "BAC0", source: "Kefka" } -287.1 "Spellscatter" #Ability { id: "BAC1", source: "Kefka" } -296.1 "Future's End/Past's End" Ability { id: ["BAD2", "BAD3"], source: "Kefka" } - +287.1 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"] source: "Kefka" } +296.1 "Future's End/Past's End 3" Ability { id: ["BAD2", "BAD3"], source: "Kefka" } 296.4 "The Path of Light 6" Ability { id: "BABE", source: "Kefka" } -297.1 "Spellwave" #Ability { id: "BAC2", source: "Kefka" } -297.1 "Spellscatter" #Ability { id: "BAC1", source: "Kefka" } +297.1 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"] source: "Kefka" } +307.5 "All Things Ending" #Ability { id: ["BACD", "BADC", "BADD"], source: "Kefka" } +# Set 4 307.5 "The Path of Light 7" Ability { id: "BABE", source: "Kefka" } -307.5 "All Things Ending" #Ability { id: ["BACD", "BADD"], source: "Kefka" } -308.0 "Spelldriver" #Ability { id: "BAC0", source: "Kefka" } -308.0 "Spellwave" #Ability { id: "BAC2", source: "Kefka" } -308.0 "Spellscatter" #Ability { id: "BAC1", source: "Kefka" } -308.0 "Spelldriver" #Ability { id: "BAC0", source: "Kefka" } -317.0 "Future's End/Past's End" Ability { id: ["BAD2", "BAD3"], source: "Kefka" } - +308.0 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"] source: "Kefka" } +317.0 "Future's End/Past's End 4" Ability { id: ["BAD2", "BAD3"], source: "Kefka" } 317.4 "The Path of Light 8" Ability { id: "BABE", source: "Kefka" } -318.1 "Spellwave" #Ability { id: "BAC2", source: "Kefka" } -318.1 "Spellscatter" #Ability { id: "BAC1", source: "Kefka" } -328.3 "All Things Ending" #Ability { id: ["BACD", "BADD"], source: "Kefka" } +318.1 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"] source: "Kefka" } +328.3 "All Things Ending" Ability { id: ["BACD", "BADC", "BADD"], source: "Kefka" } +# Trines 337.4 "Light of Judgment" Ability { id: "BABD", source: "Kefka" } 348.6 "Trine" Ability { id: "BADF", source: "Kefka" } 355.7 "Wings of Destruction" Ability { id: "BACD", source: "Kefka" } @@ -145,42 +133,183 @@ hideall "--sync--" 381.2 "Light of Judgment (Enrage)?" #Ability { id: "BAE1", source: "Kefka" } # Kefka > 0% HP 382.3 "Aero III Assault?" Ability { id: "C3F7", source: "Kefka" } forcejump "p2-success" +# TODO: Earlier sync on jump middle 497.0 label "p2-success" 500.0 "Aero III Assault" Ability { id: "C3F7", source: "Kefka" } window 500,20 ### Phase 3 - Chaos and Exdeath +# TODO: Cleanup p3 as there are multiple sequences that could happen # TODO: Update with network log # TODO: Add voiceline sync? -537.6 "Definition of Insanity" Ability { id: "BAE2", source: "Kefka" } -543.7 "the Decisive Battle" Ability { id: "C2E3", source: "Exdeath" } -543.7 "the Decisive Battle" #Ability { id: "C2E2", source: "Chaos" } -547.8 "--sync--" Ability { id: "C554", source: "Kefka" } -562.9 "Bowels of Agony" Ability { id: "BAF2", source: "Chaos" } -581.9 "Thunder III" #Ability { id: "BB12", source: "Exdeath" } -581.9 "Stray Flames" #Ability { id: "BAF3", source: "Kefka" } -582.9 "Inferno" Ability { id: "BAF4", source: "Chaos" } -584.6 "Cyclone" Ability { id: "BAF8", source: "Chaos" } -590.9 "Thunder III" #Ability { id: "BB09", source: "Exdeath" } -590.9 "Thunder III" #Ability { id: "BB0C", source: "Exdeath" } -592.1 "Cyclone" Ability { id: "BAF8", source: "Chaos" } -593.9 "Thunder III" Ability { id: "BB0C", source: "Exdeath" } -595.1 "Cyclone" #Ability { id: "BAF8", source: "Chaos" } -596.5 "--sync--" Ability { id: "C555", source: "Kefka" } -596.7 "Cyclone" #Ability { id: "BAF8", source: "Chaos" } -599.6 "Stray Spray" Ability { id: "BAF6", source: "Chaos" } -600.6 "Cyclone" #Ability { id: "BAF8", source: "Chaos" } -600.6 "Tsunami" Ability { id: "BAF5", source: "Chaos" } -602.3 "Cyclone" #Ability { id: "BAF8", source: "Chaos" } -602.3 "Trance" Ability { id: "C2D6", source: "Kefka" } -603.5 "Longitudinal Implosion" Ability { id: "BAFD", source: "Chaos" } -604.3 "Shockwave" #Ability { id: "BAFF", source: "Chaos" } -606.3 "Shockwave" #Ability { id: "BAFF", source: "Chaos" } +# en: 'Hmph. I see what's going on here.' +537.7 "Definition of Insanity" Ability { id: "BAE2", source: "Kefka" } +543.8 "the Decisive Battle" Ability { id: "C2E3", source: "Exdeath" } +543.8 "the Decisive Battle" #Ability { id: "C2E2", source: "Chaos" } +548.0 "--sync--" Ability { id: "C554", source: "Kefka" } + +# Fire, Water, and Wind Elements +563.1 "Bowels of Agony" Ability { id: "BAF2", source: "Chaos" } +582.1 "Stray Spray" Ability { id: "BAF6", source: "Kefka" } +582.1 "Thunder III" #Ability { id: "BB12", source: "Exdeath" } +583.1 "Tsunami" Ability { id: "BAF5", source: "Kefka" } +584.9 "Cyclone" Ability { id: "BAF8", source: "Chaos" } +591.3 "Thunder III" Ability { id: "BB09", source: "Exdeath" } +591.3 "Thunder III" #Ability { id: "BB0C", source: "Exdeath" } +594.3 "Thunder III" #Ability { id: "BB0C", source: "Exdeath" } +597.0 "--sync--" Ability { id: "C555", source: "Kefka" } +603.0 "Trance" Ability { id: "C2D6", source: "Kefka" } +604.2 "Longitudinal Implosion/Latitudinal Implosion" Ability { id: ["BAFD", "BAFE"], source: "Chaos" } +605.0 "Shockwave" #Ability { id: "BAFF", source: "Chaos" } +607.0 "Shockwave" #Ability { id: "BAFF", source: "Chaos" } +609.0 "Stray Flames" Ability { id: "BAF3", source: "Chaos" } +610.0 "Inferno" Ability { id: "BAF4", source: "Chaos" } +611.7 "Cyclone" Ability { id: "BAF8", source: "Chaos" } +# Limit Cut Preview +620.3 "Ultima Blaster" #Ability { id: "BAE3", source: "Kefka" } +622.3 "Ultima Blaster" #Ability { id: "BAE3", source: "Kefka" } +624.3 "Ultima Blaster" #Ability { id: "BAE3", source: "Kefka" } +624.4 "Umbra Smash" Ability { id: "BB00", source: "Chaos" } +626.3 "Ultima Blaster" #Ability { id: "BAE3", source: "Kefka" } +627.3 "Vacuum Wave" Ability { id: "BB13", source: "Exdeath" } +628.3 "Ultima Blaster" #Ability { id: "BAE3", source: "Kefka" } +630.3 "Ultima Blaster" #Ability { id: "BAE3", source: "Kefka" } +631.3 "Cyclone" Ability { id: "BAF8", source: "Chaos" } +632.4 "Ultima Blaster" #Ability { id: "BAE3", source: "Kefka" } +632.7 "Aetherlink" #Ability { id: "C2E5", source: "Exdeath" } +632.7 "Aetherlink" #Ability { id: "C2E4", source: "Chaos" } + +# Limit Cut +634.4 "Ultima Blaster" #Ability { id: "BAE3", source: "Kefka" } +642.4 "Ultima Blaster 1" #Ability { id: "BAE4", source: "Kefka" } +642.6 "Ultima Blaster 2" #Ability { id: "BAE4", source: "Kefka" } +642.8 "Ultima Blaster 3" #Ability { id: "BAE4", source: "Kefka" } +643.0 "Ultima Blaster 4" #Ability { id: "BAE4", source: "Kefka" } +643.2 "Ultima Blaster 5" #Ability { id: "BAE4", source: "Kefka" } +643.4 "Ultima Blaster 6" #Ability { id: "BAE4", source: "Kefka" } +643.6 "Ultima Blaster 7" #Ability { id: "BAE4", source: "Kefka" } +643.8 "Ultima Blaster 8" #Ability { id: "BAE4", source: "Kefka" } +649.7 "Thunder III" Ability { id: "BB09", source: "Exdeath" } +649.7 "Thunder III" #Ability { id: "BB0C", source: "Exdeath" } +652.7 "Thunder III" #Ability { id: "BB0C", source: "Exdeath" } +657.7 "the Decisive Battle" Ability { id: "C2E3", source: "Exdeath" } +657.7 "the Decisive Battle" #Ability { id: "C2E2", source: "Chaos" } +666.8 "Thunder III" Ability { id: "BB09", source: "Exdeath" } +666.8 "Thunder III" #Ability { id: "BB0C", source: "Exdeath" } + +# Giant Kefka + Earth Element +668.6 "Max" Ability { id: "BAE5", source: "Kefka" } +669.8 "Thunder III" #Ability { id: "BB0C", source: "Exdeath" } +671.8 "Earthquake" Ability { id: "C571", source: "Chaos" } +671.8 "Earthquake" #Ability { id: "C572", source: "Chaos" } +680.5 "Earthquake" Ability { id: "BAFA", source: "Chaos" } +684.1 "Earthquake" Ability { id: "BAFA", source: "Chaos" } +687.4 "Slap Happy" Ability { id: "BAE7", source: "Kefka" } +688.2 "Slap Happy" #Ability { id: "BAE8", source: "Kefka" } +688.9 "Slap Happy" #Ability { id: "BAE8", source: "Kefka" } +689.7 "Slap Happy" #Ability { id: "BAE8", source: "Kefka" } +690.9 "Slap Happy" Ability { id: "BAE9", source: "Kefka" } +690.9 "Shockwave" #Ability { id: "BAEB", source: "Kefka" } + +691.2 "Black Hole" Ability { id: "BAFB", source: "Exdeath" } +698.3 "Nothingness" Ability { id: "BAFC", source: "Black Hole" } +698.3 "Aetherlink" #Ability { id: "C2E5", source: "Exdeath" } +698.3 "Aetherlink" #Ability { id: "C2E4", source: "Chaos" } +705.3 "Nothingness" Ability { id: "BAFC", source: "Black Hole" } +708.4 "Thunder III" Ability { id: "BB09", source: "Exdeath" } +708.4 "Thunder III" #Ability { id: "BB0C", source: "Exdeath" } +711.4 "Thunder III" #Ability { id: "BB0C", source: "Exdeath" } +712.7 "Earthquake" Ability { id: "BAFA", source: "Chaos" } +716.5 "Damning Edict" Ability { id: "BB01", source: "Chaos" } +717.7 "Slap Happy" Ability { id: "BAE7", source: "Kefka" } +718.5 "Slap Happy" #Ability { id: "BAE8", source: "Kefka" } +719.2 "Slap Happy" #Ability { id: "BAE8", source: "Kefka" } +720.0 "Slap Happy" #Ability { id: "BAE8", source: "Kefka" } +721.2 "Slap Happy" Ability { id: "BAE9", source: "Kefka" } +721.2 "Shockwave" #Ability { id: "BAEB", source: "Kefka" } +724.6 "Black Spark" Ability { id: "BCCD", source: "Black Hole" } +728.6 "Nothingness" Ability { id: "BAFC", source: "Black Hole" } +730.2 "Earthquake" Ability { id: "BAFA", source: "Chaos" } +733.8 "Nothingness" #Ability { id: "BAFC", source: "Black Hole" } +733.8 "Nothingness" #Ability { id: "BAFC", source: "Black Hole" } +735.4 "Earthquake" Ability { id: "BAFA", source: "Chaos" } +739.0 "Nothingness" Ability { id: "BAFC", source: "Black Hole" } +744.0 "Damning Edict" Ability { id: "BB01", source: "Chaos" } +744.4 "Look upon Me and Despair" Ability { id: "BAEC", source: "Kefka" } +745.4 "Look upon Me and Despair" Ability { id: "BAEE", source: "Kefka" } +747.5 "Blackblood" Ability { id: "C4BA", source: "Kefka" } +747.5 "Earthquake" Ability { id: "BAFA", source: "Chaos" } +749.7 "Thunder III" Ability { id: "BB09", source: "Exdeath" } +749.7 "Thunder III" #Ability { id: "BB0C", source: "Exdeath" } +752.7 "Thunder III" #Ability { id: "BB0C", source: "Exdeath" } +762.8 "Nothingness" #Ability { id: "BAFC", source: "Black Hole" } +762.8 "Nothingness" #Ability { id: "BAFC", source: "Black Hole" } +764.4 "Earthquake" Ability { id: "BAFA", source: "Chaos" } +765.0 "Aetherlink" Ability { id: "C2E5", source: "Exdeath" } +765.0 "Aetherlink" #Ability { id: "C2E4", source: "Chaos" } +768.0 "Nothingness" #Ability { id: "BAFC", source: "Black Hole" } +768.0 "Nothingness" #Ability { id: "BAFC", source: "Black Hole" } +769.6 "Earthquake" Ability { id: "BAFA", source: "Chaos" } +773.2 "Nothingness" #Ability { id: "BAFC", source: "Black Hole" } +773.2 "Nothingness" #Ability { id: "BAFC", source: "Black Hole" } + +784.2 "White Hole" Ability { id: "BD66", source: "Exdeath" } +784.2 "Longitudinal Implosion/Latitudinal Implosion" Ability { id: ["BAFD", "BAFE"], source: "Chaos" } +785.0 "Shockwave" #Ability { id: "BAFF", source: "Kefka" } +785.9 "Slap Happy" Ability { id: "BAE6", source: "Kefka" } +786.8 "Slap Happy" #Ability { id: "BAE8", source: "Kefka" } +787.2 "Shockwave" #Ability { id: "BAFF", source: "Chaos" } +787.4 "Slap Happy" #Ability { id: "BAE8", source: "Kefka" } +788.1 "Slap Happy" #Ability { id: "BAE8", source: "Kefka" } +789.3 "Slap Happy" Ability { id: "BAE9", source: "Kefka" } +789.3 "Shocking Impact" Ability { id: "BAEA", source: "Kefka" } +792.5 "Black Spark" Ability { id: "BCCD", source: "Kefka" } +796.5 "Nothingness" #Ability { id: "BAFC", source: "Black Hole" } +796.5 "Nothingness" #Ability { id: "BAFC", source: "Black Hole" } +802.2 "Look upon Me and Despair" Ability { id: "BAED", source: "Kefka" } +803.2 "Look upon Me and Despair" Ability { id: "BAEE", source: "Kefka" } +803.5 "Nothingness" #Ability { id: "BAFC", source: "Black Hole" } + +# Blizzards and Towers +# Summary: +# 1. Bait 2 Puddles +# 2. Role 1 4-Stack +# 3. Role 2 2-NW Towers +# 4. Role 2 2-NE Towers +# 5. Role 1 2-NW Towers +# 6. Role 1 2-NE Towers +# 7. Role 2 4-Stack +# 8. Deep Freeze (Blizzard) +805.3 "--sync--" Ability { id: "C533", source: "Kefka" } +812.4 "Blizzard III (castbar)" Ability { id: "BB0F", source: "Exdeath" } +814.3 "Earthquake?" #Ability { id: "BAFA", source: "Chaos" } +815.4 "Blizzard III 1" Ability { id: "BB0D", source: "Exdeath" } +816.3 "Stomp-a-Mole (castbar)" Ability { id: "BAEF", source: "Kefka" } # Possibly determines which foot he starts with and/or its based on facing? +817.9 "Knock Down 1" Ability { id: "BB02", source: "Chaos" } +818.0 "Stomp-a-Mole 1" Ability { id: "BAF0", source: "Kefka" } +818.4 "Blizzard III 2" Ability { id: "BB0D", source: "Exdeath" } +819.3 "Stomp-a-Mole 2" Ability { id: "BAF0", source: "Kefka" } +820.6 "Stomp-a-Mole 3" Ability { id: "BAF0", source: "Kefka" } +821.9 "Stomp-a-Mole 4" Ability { id: "BAF0", source: "Kefka" } +823.3 "Knock Down 2" Ability { id: "BB03", source: "Chaos" } +826.4 "Blizzard III" Ability { id: "BB11", source: "Exdeath" } +827.4 "Big Bang" Ability { id: "BB05", source: "Chaos" } +827.4 "Big Bang" #Ability { id: "BB06", source: "Chaos" } + +840.0 "--sync--" StartsUsing { id: "BAE1", source: "Exdeath" } jump "p3-enrage" +840.0 "--sync--" StartsUsing { id: "C3F7", source: "Chaos" } jump "p3-enrage" +845.8 "Meteor (Enrage)?" Ability { id: "C258", source: "Exdeath" } # Exdeath >0% HP? +845.8 "Bowels of Agony (Enrage)?" Ability { id: "C259", source: "Chaos" } # Chaos >0% HP? # Phase 2 Enrage Sequence 10376.2 label "p2-enrage" 10381.2 "Light of Judgment (Enrage)" Ability { id: "BAE1", source: "Kefka" } # Kefka > 0% HP +# Phase 3 Enrage Sequence +10840.0 label "p3-enrage" +10845.8 "Meteor (Enrage) Ability { id: "C258", source: "Exdeath" } # Exdeath >0% HP? +10845.8 "Bowels of Agony (Enrage)" Ability { id: "C259", source: "Chaos" } # Chaos >0% HP? + # IGNORED ABILITIES # C252 Attack: P1 Kefka attack and P3 Chaos attack # BA9E Blizzard III Blowout: Damage @@ -194,6 +323,8 @@ hideall "--sync--" # BAD8 Future's End: Damage (On 3 players) # BAD9 Past's End: Damage (On 3 players) # C250 Attack: P3 Exdeath attack +# BAF9 Stray Earth: Some sort of failed P3 mechanic +# BAF1 Unmitigated Impact: P3 Failing to soak a tower # ALL ENCOUNTER ABILITIES # BA94 Mystery Magic @@ -232,6 +363,7 @@ hideall "--sync--" # BAC0 Spelldriver # BAC1 Spellscatter # BAC2 Spellwave +# BACD Wings of Destruction # BACE Wings of Destruction # BACF Wings of Destruction # BAD2 Future's End: VFX @@ -245,31 +377,71 @@ hideall "--sync--" # BADF Trine # BAE0 Trine # BAE2 Definition of Insanity +# BAE3 Ultima Blaster +# BAE4 Ultima Blaster +# BAE5 Max +# BAE6 Slap Happy +# BAE7 Slap Happy +# BAE8 Slap Happy +# BAE9 Slap Happy +# BAEA Shocking Impact +# BAEB Shockwave +# BAEC Look upon Me and Despair +# BAED Look upon Me and Despair +# BAEE Look upon Me and Despair +# BAEF Stomp-a-Mole +# BAF0 Stomp-a-Mole +# BAF1 Unmitigated Impact # BAF2 Bowels of Agony # BAF3 Stray Flames # BAF4 Inferno # BAF5 Tsunami # BAF6 Stray Spray # BAF8 Cyclone +# BAF9 Stray Earth: Some sort of failed P3 mechanic +# BAFA Earthquake +# BAFB Black Hole +# BAFC Nothingness # BAFD Longitudinal Implosion +# BAFE Latitudinal Implosion # BAFF Shockwave +# BB00 Umbra Smash +# BB01 Damning Edict +# BB02 Knock Down: VFX +# BB03 Knock Down: Damage +# BB05 Big Bang +# BB06 Big Bang # BB09 Thunder III # BB0C Thunder III +# BB0D Blizzard III +# BB0F Blizzard III +# BB11 Blizzard III # BB12 Thunder III +# BB13 Vacuum Wave # BAE1 Light of Judgment: P2 Enrage +# BCCD Black Spark # BCF2 Graven Image +# BD66 White Hole # C24B Hyperdrive # C24C Ultimate Embrace # C250 Attack: P3 Exdeath attack # C252 Attack: P1 Kefka attack and P3 Chaos attack +# C258 Meteor: P3 Chaos Enrage +# C259 Bowels of Agony: P3 Exdeath Enrage # C2D6 Trance # C2E2 the Decisive Battle # C2E3 the Decisive Battle +# C2E4 Aetherlink +# C2E5 Aetherlink # C3F7 Aero III Assault # C3FD --sync--: Boss jumps to middle # C403 Revolting Ruin III # C487 Wings of Destruction +# C4BA Blackblood # C4E1 Revolting Ruin III +# C533 --sync-- # C554 --sync-- # C555 --sync-- +# C571 Earthquake +# C572 Earthquake # C622 Light of Judgment: P1 version From 54607312955e025614a14d8e3d87434c8c9fd6bf Mon Sep 17 00:00:00 2001 From: Legends0 Date: Fri, 5 Jun 2026 04:12:26 -0400 Subject: [PATCH 26/54] missing comma --- ui/raidboss/data/07-dt/ultimate/dancing_mad.txt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt index 420698430b..90feff0047 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt @@ -87,34 +87,34 @@ hideall "--sync--" # Set 1 # NOTE: The BAC0 Spelldriver, BAC2 Spellwave, BAC0 Spellscatter can be resolved in different orders 244.9 "The Path of Light 1" Ability { id: "BABE", source: "Kefka" } -245.6 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"] source: "Kefka" } +245.6 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"], source: "Kefka" } 254.3 "Future's End/Past's End 1" Ability { id: ["BAD2", "BAD3"], source: "Kefka" } 255.0 "The Path of Light 2" Ability { id: "BABE", source: "Kefka" } -255.7 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"] source: "Kefka" } +255.7 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"], source: "Kefka" } 265.7 "All Things Ending" #Ability { id: ["BACD", "BADC", "BADD"], source: "Kefka" } # Set 2 265.9 "The Path of Light 3" Ability { id: "BABE", source: "Kefka" } -266.4 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"] source: "Kefka" } +266.4 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"], source: "Kefka" } 275.4 "Future's End/Past's End 2" Ability { id: ["BAD2", "BAD3"], source: "Kefka" } 275.8 "The Path of Light 4" Ability { id: "BABE", source: "Kefka" } -276.5 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"] source: "Kefka" } +276.5 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"], source: "Kefka" } 286.7 "All Things Ending" #Ability { id: ["BACD", "BADD"], source: "Kefka" } # Set 3 286.7 "The Path of Light 5" Ability { id: "BABE", source: "Kefka" } -287.1 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"] source: "Kefka" } +287.1 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"], source: "Kefka" } 296.1 "Future's End/Past's End 3" Ability { id: ["BAD2", "BAD3"], source: "Kefka" } 296.4 "The Path of Light 6" Ability { id: "BABE", source: "Kefka" } -297.1 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"] source: "Kefka" } +297.1 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"], source: "Kefka" } 307.5 "All Things Ending" #Ability { id: ["BACD", "BADC", "BADD"], source: "Kefka" } # Set 4 307.5 "The Path of Light 7" Ability { id: "BABE", source: "Kefka" } -308.0 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"] source: "Kefka" } +308.0 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"], source: "Kefka" } 317.0 "Future's End/Past's End 4" Ability { id: ["BAD2", "BAD3"], source: "Kefka" } 317.4 "The Path of Light 8" Ability { id: "BABE", source: "Kefka" } -318.1 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"] source: "Kefka" } +318.1 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"], source: "Kefka" } 328.3 "All Things Ending" Ability { id: ["BACD", "BADC", "BADD"], source: "Kefka" } # Trines From 24aa5f0789616216c9c5943383352a976cfc3df9 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Fri, 5 Jun 2026 04:17:37 -0400 Subject: [PATCH 27/54] missing quote and fix enrage startsUsing timing --- ui/raidboss/data/07-dt/ultimate/dancing_mad.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt index 90feff0047..66755ccf90 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt @@ -296,8 +296,8 @@ hideall "--sync--" 827.4 "Big Bang" Ability { id: "BB05", source: "Chaos" } 827.4 "Big Bang" #Ability { id: "BB06", source: "Chaos" } -840.0 "--sync--" StartsUsing { id: "BAE1", source: "Exdeath" } jump "p3-enrage" -840.0 "--sync--" StartsUsing { id: "C3F7", source: "Chaos" } jump "p3-enrage" +840.8 "--sync--" StartsUsing { id: "BAE1", source: "Exdeath" } jump "p3-enrage" +840.8 "--sync--" StartsUsing { id: "C3F7", source: "Chaos" } jump "p3-enrage" 845.8 "Meteor (Enrage)?" Ability { id: "C258", source: "Exdeath" } # Exdeath >0% HP? 845.8 "Bowels of Agony (Enrage)?" Ability { id: "C259", source: "Chaos" } # Chaos >0% HP? @@ -306,8 +306,8 @@ hideall "--sync--" 10381.2 "Light of Judgment (Enrage)" Ability { id: "BAE1", source: "Kefka" } # Kefka > 0% HP # Phase 3 Enrage Sequence -10840.0 label "p3-enrage" -10845.8 "Meteor (Enrage) Ability { id: "C258", source: "Exdeath" } # Exdeath >0% HP? +10840.8 label "p3-enrage" +10845.8 "Meteor (Enrage)" Ability { id: "C258", source: "Exdeath" } # Exdeath >0% HP? 10845.8 "Bowels of Agony (Enrage)" Ability { id: "C259", source: "Chaos" } # Chaos >0% HP? # IGNORED ABILITIES From ab0e3d2471f4fa6a5d763beae6593cfca794ca19 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Fri, 5 Jun 2026 04:18:47 -0400 Subject: [PATCH 28/54] spell id fix --- ui/raidboss/data/07-dt/ultimate/dancing_mad.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt index 66755ccf90..421949c96a 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt @@ -296,8 +296,8 @@ hideall "--sync--" 827.4 "Big Bang" Ability { id: "BB05", source: "Chaos" } 827.4 "Big Bang" #Ability { id: "BB06", source: "Chaos" } -840.8 "--sync--" StartsUsing { id: "BAE1", source: "Exdeath" } jump "p3-enrage" -840.8 "--sync--" StartsUsing { id: "C3F7", source: "Chaos" } jump "p3-enrage" +840.8 "--sync--" StartsUsing { id: "C258", source: "Exdeath" } jump "p3-enrage" +840.8 "--sync--" StartsUsing { id: "C259", source: "Chaos" } jump "p3-enrage" 845.8 "Meteor (Enrage)?" Ability { id: "C258", source: "Exdeath" } # Exdeath >0% HP? 845.8 "Bowels of Agony (Enrage)?" Ability { id: "C259", source: "Chaos" } # Chaos >0% HP? From 72298a1a4e7068dad8f87415831d14b6b20113b0 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Fri, 5 Jun 2026 13:38:27 -0400 Subject: [PATCH 29/54] replace CombatantMemory with OverlayHandler --- .../data/07-dt/ultimate/dancing_mad.ts | 58 +++++++++---------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index 35ada1b520..da4f551360 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -1,4 +1,5 @@ import Conditions from '../../../../../resources/conditions'; +import { callOverlayHandler } from '../../../../../resources/overlay_plugin_api'; import Outputs from '../../../../../resources/outputs'; import { Responses } from '../../../../../resources/responses'; import ZoneId from '../../../../../resources/zone_id'; @@ -182,45 +183,42 @@ const triggerSet: TriggerSet = { }, }, { - id: 'DMU P1 CombatantMemory Tower Tracker', - // 1EBFBB => Wave Cannon entity (blue) - // 1EBFBC => Gravitational Wave entity (purple) - // 1EBFBD => Intemperate Will entity (yellow) + id: 'DMU P1 Graven Image Collect', + // Tower entity actions + // The CombatantMemory Add lines are added prior to combat + // OverlayPlugin call used to retrieve the matching BNpcID + // 1EBFBB (2015163) => Wave Cannon entity (blue) + // 1EBFBC (2015164) => Gravitational Wave entity (purple) + // 1EBFBD (2015165) => Intemperate Will entity (yellow) // There are two of each, they are added at start of fight - type: 'CombatantMemory', - netRegex: { - change: 'Add', - pair: [{ key: 'BNpcID', value: ['1EBFBB', '1EBFBC', '1EBFBD'] }], - capture: true, - }, - run: (data, matches) => { + type: 'ActorControlExtra', + netRegex: { category: '019D', param1: '40', param2: '80', capture: true }, + promise: async (data, matches) => { + const ids = [parseInt((matches.id), 16)]; + const actors = (await callOverlayHandler({ + call: 'getCombatants', + ids: ids, + })).combatants; + const image = actors[0]; + if (image === undefined) + return; + const towerMap = { - '1EBFBB': 'blue', - '1EBFBC': 'purple', - '1EBFBD': 'yellow', + '2015163': 'blue', + '2015164': 'purple', + '2015165': 'yellow', 'unknown': 'unknown', }; - const bnpcid = matches.pairBNpcID ?? 'unknown'; + + const bnpcid = image.BNpcID ?? 'unknown'; const kind = towerMap[bnpcid as keyof typeof towerMap]; - if (kind === 'blue') { + if (kind === 'blue') data.blueTowerIds.push(matches.id); - return; - } - if (kind === 'yellow') { + else if (kind === 'yellow') data.yellowTowerIds.push(matches.id); - return; - } - if (kind === 'purple') { + else if (kind === 'purple') data.purpleTowerIds.push(matches.id); - return; - } }, - }, - { - id: 'DMU P1 Graven Image Collect', - // Tower entity actions - type: 'ActorControlExtra', - netRegex: { category: '019D', param1: '40', param2: '80', capture: true }, run: (data, matches) => { const id = matches.id; From fad1b17f7d457db3493afdc804a0e79e4732bf22 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Fri, 5 Jun 2026 13:51:09 -0400 Subject: [PATCH 30/54] change string[] to string + lint --- .../data/07-dt/ultimate/dancing_mad.ts | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index da4f551360..9dd7b913ee 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -1,6 +1,6 @@ import Conditions from '../../../../../resources/conditions'; -import { callOverlayHandler } from '../../../../../resources/overlay_plugin_api'; import Outputs from '../../../../../resources/outputs'; +import { callOverlayHandler } from '../../../../../resources/overlay_plugin_api'; import { Responses } from '../../../../../resources/responses'; import ZoneId from '../../../../../resources/zone_id'; import { RaidbossData } from '../../../../../types/data'; @@ -21,9 +21,9 @@ export interface Data extends RaidbossData { // General phase: Phase | 'unknown'; // Phase 1 - blueTowerIds: string[]; - yellowTowerIds: string[]; - purpleTowerIds: string[]; + blueTowerId?: string; + yellowTowerId?: string; + purpleTowerId?: string; tower?: 'blue' | 'yellow' | 'purple'; gravenImageCount: number; actorPositions: { [id: string]: { x: number; y: number; heading: number } }; @@ -154,9 +154,6 @@ const triggerSet: TriggerSet = { return { phase: 'p1', // Phase 1 - blueTowerIds: [], - yellowTowerIds: [], - purpleTowerIds: [], actorPositions: {}, gravenImageCount: 0, waveCannonTargets: [], @@ -194,10 +191,10 @@ const triggerSet: TriggerSet = { type: 'ActorControlExtra', netRegex: { category: '019D', param1: '40', param2: '80', capture: true }, promise: async (data, matches) => { - const ids = [parseInt((matches.id), 16)]; + const id = matches.id; const actors = (await callOverlayHandler({ call: 'getCombatants', - ids: ids, + ids: [parseInt(id, 16)], })).combatants; const image = actors[0]; if (image === undefined) @@ -213,24 +210,24 @@ const triggerSet: TriggerSet = { const bnpcid = image.BNpcID ?? 'unknown'; const kind = towerMap[bnpcid as keyof typeof towerMap]; if (kind === 'blue') - data.blueTowerIds.push(matches.id); + data.blueTowerId = id; else if (kind === 'yellow') - data.yellowTowerIds.push(matches.id); + data.yellowTowerId = id; else if (kind === 'purple') - data.purpleTowerIds.push(matches.id); + data.purpleTowerId = id; }, run: (data, matches) => { const id = matches.id; - if (data.yellowTowerIds.indexOf(id) !== -1) { + if (data.yellowTowerId === id) { data.tower = 'yellow'; return; } - if (data.purpleTowerIds.indexOf(id) !== -1) { + if (data.purpleTowerId === id) { data.tower = 'purple'; return; } - if (data.blueTowerIds.indexOf(id) !== -1) { + if (data.blueTowerId === id) { data.tower = 'blue'; return; } @@ -464,7 +461,7 @@ const triggerSet: TriggerSet = { type: 'ActorControlExtra', netRegex: { category: '019D', param1: '40', param2: '80', capture: true }, alertText: (data, matches, output) => { - if (data.blueTowerIds.indexOf(matches.id) !== -1) + if (data.blueTowerId === matches.id) return output.waveCannonLine!(); }, outputStrings: { @@ -714,10 +711,10 @@ const triggerSet: TriggerSet = { netRegex: { category: '019D', param1: '40', param2: '80', capture: true }, alertText: (data, matches, output) => { const id = matches.id; - if (data.yellowTowerIds.indexOf(id) !== -1) { + if (data.yellowTowerId === id) { return output.goWest!(); } - if (data.purpleTowerIds.indexOf(id) !== -1) { + if (data.purpleTowerId === id) { return output.goEast!(); } }, From 52687c9c1f344b9f2ae78d6985e2f06ada9766e1 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Fri, 5 Jun 2026 14:05:33 -0400 Subject: [PATCH 31/54] some cleanup --- .../data/07-dt/ultimate/dancing_mad.ts | 48 ++++++++----------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index 9dd7b913ee..3dcec16c14 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -219,18 +219,12 @@ const triggerSet: TriggerSet = { run: (data, matches) => { const id = matches.id; - if (data.yellowTowerId === id) { + if (data.yellowTowerId === id) data.tower = 'yellow'; - return; - } - if (data.purpleTowerId === id) { + else if (data.purpleTowerId === id) data.tower = 'purple'; - return; - } - if (data.blueTowerId === id) { + else if (data.blueTowerId === id) data.tower = 'blue'; - return; - } }, }, { @@ -277,7 +271,7 @@ const triggerSet: TriggerSet = { run: (data) => data.gravenImageCount = data.gravenImageCount + 1, }, { - id: 'DMU Graven Image Tether Collect', + id: 'DMU P1 Graven Image Tether Collect', // 271 ActorSetPos lines indicate where the tether is coming from // 261 CombatantMemory lines may also indicate this // Graven Image 1: @@ -316,7 +310,7 @@ const triggerSet: TriggerSet = { }, }, { - id: 'DMU Pulse Wave Tethers', + id: 'DMU P1 Pulse Wave Tethers', type: 'Tether', netRegex: { id: headMarkerData['imageTether'], capture: true }, condition: (data, matches) => { @@ -460,10 +454,8 @@ const triggerSet: TriggerSet = { // This gives a ~5 second warning to spread type: 'ActorControlExtra', netRegex: { category: '019D', param1: '40', param2: '80', capture: true }, - alertText: (data, matches, output) => { - if (data.blueTowerId === matches.id) - return output.waveCannonLine!(); - }, + condition: (data, matches) => data.blueTowerId === matches.id, + alertText: (data, _matches, output) => output.waveCannonLine!(), outputStrings: { waveCannonLine: { en: 'E/W Spread', @@ -706,25 +698,27 @@ const triggerSet: TriggerSet = { outputStrings: trapOutputStrings, }, { - id: 'DMU P1 Impertinent Will/Gravitational Wave', + id: 'DMU P1 Impertinent Will', type: 'ActorControlExtra', netRegex: { category: '019D', param1: '40', param2: '80', capture: true }, - alertText: (data, matches, output) => { - const id = matches.id; - if (data.yellowTowerId === id) { - return output.goWest!(); - } - if (data.purpleTowerId === id) { - return output.goEast!(); - } - }, + condition: (data, matches) => data.yellowTowerId === matches.id, + alertText: (data, _matches, output) => output.goWest!(), outputStrings: { goWest: Outputs.getLeftAndWest, + }, + }, + { + id: 'DMU P1 Gravitational Wave', + type: 'ActorControlExtra', + netRegex: { category: '019D', param1: '40', param2: '80', capture: true }, + condition: (data, matches) => data.purpleTowerId === matches.id, + alertText: (data, _matches, output) => output.goEast!(), + outputStrings: { goEast: Outputs.getRightAndEast, }, }, { - id: 'DMU Gravitas and Vitrophyre Tethers 2', + id: 'DMU P1 Gravitas and Vitrophyre Tethers 2', type: 'Tether', netRegex: { id: headMarkerData['imageTether'], capture: true }, condition: (data, matches) => { @@ -1030,7 +1024,7 @@ const triggerSet: TriggerSet = { }, }, { - id: 'DMU Indulgent Will and Idyllic Will Tethers', + id: 'DMU P1 Indulgent Will and Idyllic Will Tethers', type: 'Tether', netRegex: { id: headMarkerData['imageTether'], capture: true }, condition: (data, matches) => { From 8080fe5f5659e3b2fadbd2fbd68dae6648b0383b Mon Sep 17 00:00:00 2001 From: Legends0 Date: Fri, 5 Jun 2026 14:08:35 -0400 Subject: [PATCH 32/54] unused data --- ui/raidboss/data/07-dt/ultimate/dancing_mad.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index 3dcec16c14..d196a6a108 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -455,7 +455,7 @@ const triggerSet: TriggerSet = { type: 'ActorControlExtra', netRegex: { category: '019D', param1: '40', param2: '80', capture: true }, condition: (data, matches) => data.blueTowerId === matches.id, - alertText: (data, _matches, output) => output.waveCannonLine!(), + alertText: (_data, _matches, output) => output.waveCannonLine!(), outputStrings: { waveCannonLine: { en: 'E/W Spread', @@ -702,7 +702,7 @@ const triggerSet: TriggerSet = { type: 'ActorControlExtra', netRegex: { category: '019D', param1: '40', param2: '80', capture: true }, condition: (data, matches) => data.yellowTowerId === matches.id, - alertText: (data, _matches, output) => output.goWest!(), + alertText: (_data, _matches, output) => output.goWest!(), outputStrings: { goWest: Outputs.getLeftAndWest, }, @@ -712,7 +712,7 @@ const triggerSet: TriggerSet = { type: 'ActorControlExtra', netRegex: { category: '019D', param1: '40', param2: '80', capture: true }, condition: (data, matches) => data.purpleTowerId === matches.id, - alertText: (data, _matches, output) => output.goEast!(), + alertText: (_data, _matches, output) => output.goEast!(), outputStrings: { goEast: Outputs.getRightAndEast, }, From 4809a769a4c350ef3aa3f0c4408014db37b72e49 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Fri, 5 Jun 2026 15:43:40 -0400 Subject: [PATCH 33/54] p3 clear with chaos first + p4 draft --- .../data/07-dt/ultimate/dancing_mad.ts | 3 +- .../data/07-dt/ultimate/dancing_mad.txt | 66 +++++++++++++++++-- 2 files changed, 62 insertions(+), 7 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index 27f1117f53..f47023bf4e 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -8,10 +8,11 @@ import { OutputStrings, TriggerSet } from '../../../../../types/trigger'; // TODO: P1 Tele-Portent configuration options -type Phase = 'p1' | 'p2' | 'p3'; +type Phase = 'p1' | 'p2' | 'p3' | 'p4'; const phases: { [id: string]: Phase } = { 'C24C': 'p2', // Ultimate Embrace, God Kefka 'C3F7': 'p3', // Aero III Assault (from Kefka), Chaos and Exdeath + 'C2DC': 'p4', // Kefka Says, Kefka with Chaos and Neo Exdeath }; // const centerX = 100; diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt index 421949c96a..052c396538 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt @@ -2,7 +2,7 @@ # ZoneId: 1363 # -ii C250 C252 BA9E BAA0 BAAB BA95 BAAF BAAD BAD6 BAD8 BAD7 BAD9 BAF9 BAF1 -# -p C403:15.6 C24C:216.5 C3F7:500.0 +# -p C403:15.6 C24C:216.5 C3F7:500.0 C2DC:1000.0 # -it Kefka Chaos Exdeath hideall "--Reset--" @@ -296,10 +296,46 @@ hideall "--sync--" 827.4 "Big Bang" Ability { id: "BB05", source: "Chaos" } 827.4 "Big Bang" #Ability { id: "BB06", source: "Chaos" } +839.0 "--sync--" StartsUsing { id: "C61E", source: "Exdeath" } jump "p3-exdeath-enrage" window 839,3 # Possibly this starts when Chaos dies? +# Possibly there is another if Exdeath is instead killed first, but Chaos triggers untargetable? + 840.8 "--sync--" StartsUsing { id: "C258", source: "Exdeath" } jump "p3-enrage" 840.8 "--sync--" StartsUsing { id: "C259", source: "Chaos" } jump "p3-enrage" -845.8 "Meteor (Enrage)?" Ability { id: "C258", source: "Exdeath" } # Exdeath >0% HP? -845.8 "Bowels of Agony (Enrage)?" Ability { id: "C259", source: "Chaos" } # Chaos >0% HP? +840.8 "--Chaos untargetable--" +840.8 "--Exdeath untargetable?--" +845.8 "Bowels of Agony (Enrage)?" #Ability { id: "C259", source: "Chaos" } # Chaos >0% HP +845.8 "Meteor (Enrage)?" #Ability { id: "C258", source: "Exdeath" } # Exdeath >0% HP + + +### Phase 4 - Kefka + Neo Exdeath + Chaos +# TODO: Cleanup p4 as there are multiple sequences that could happen +# TODO: Update with network log +# TODO: Add voiceline sync? +# en: 'Boring!' +# NOTE: Label not used due to unknown timing between Chaos/Exdeath kill order and time from Kefka Says +995.0 "--sync--" StartsUsing { id: "C2DC", source: "Kefka" } window 995,12000 # Escape from the p3 exdeath enrage or chaos +1000.0 "Kefka Says" Ability { id: "C2DC", source: "Kefka" } +1003.1 "--middle--" Ability { id: "C3FD", source: "Kefka" } + +1009.4 "Mystery Magic 1" Ability { id: "BA94", source: "Kefka" } +1009.4 "Thrumming Thunder III" #Ability { id: "BA9F", source: "Kefka" } +1009.4 "Blizzard III Blowout" #Ability { id: "BA9B", source: "Kefka" } +1013.7 "Grand Cross" Ability { id: "BB14", source: "Neo Exdeath" } +1018.7 "Tsunami" Ability { id: "BB21", source: "Chaos" } + +1024.1 "Mystery Magic 2" Ability { id: "BA94", source: "Kefka" } +1024.1 "Thrumming Thunder III" #Ability { id: "BAA1", source: "Kefka" } +1024.1 "Blizzard III Blowout" #Ability { id: "BA9B", source: "Kefka" } +1028.4 "Grand Cross" Ability { id: "BB14", source: "Neo Exdeath" } +1033.5 "Inferno" Ability { id: "BB20", source: "Chaos" } + +1039.2 "Mystery Magic 3" Ability { id: "BA94", source: "Kefka" } +1039.2 "Thrumming Thunder III" #Ability { id: "BAA1", source: "Kefka" } +1039.2 "Blizzard III Blowout" #Ability { id: "BA9B", source: "Kefka" } +1043.3 "Grand Cross" Ability { id: "BB14", source: "Neo Exdeath" } + +1045.3 "--sync--" Ability { id: "C554", source: "Kefka" } + # Phase 2 Enrage Sequence 10376.2 label "p2-enrage" @@ -307,8 +343,13 @@ hideall "--sync--" # Phase 3 Enrage Sequence 10840.8 label "p3-enrage" -10845.8 "Meteor (Enrage)" Ability { id: "C258", source: "Exdeath" } # Exdeath >0% HP? -10845.8 "Bowels of Agony (Enrage)" Ability { id: "C259", source: "Chaos" } # Chaos >0% HP? +10845.8 "Bowels of Agony (Enrage)" Ability { id: "C259", source: "Chaos" } # Chaos >0% HP +10845.8 "Meteor (Enrage)" Ability { id: "C258", source: "Exdeath" } # Exdeath >0% HP + +# Phase 3 Exdeath Enrage Sequence +11839.0 "p3-exdeath-enrage" +11849.0 "Meteor (Exdeath Enrage)" StartsUsing { id: "C61E", source: "Exdeath" } # Exdeath >0% HP +# NOTE: Using a window to jump back to p4, TBD if killing Exdeath first triggers a separate sequence # IGNORED ABILITIES # C252 Attack: P1 Kefka attack and P3 Chaos attack @@ -376,6 +417,7 @@ hideall "--sync--" # BADD All Things Ending # BADF Trine # BAE0 Trine +# BAE1 Light of Judgment: P2 Enrage # BAE2 Definition of Insanity # BAE3 Ultima Blaster # BAE4 Ultima Blaster @@ -418,7 +460,18 @@ hideall "--sync--" # BB11 Blizzard III # BB12 Thunder III # BB13 Vacuum Wave -# BAE1 Light of Judgment: P2 Enrage +# BB14 Grand Cross +# BB16 Death Shriek +# BB17 Death Shriek +# BB18 Death Bolt +# BB19 Death Bolt +# BB1A Death Wave +# BB1B Death Wave +# BB1D Death Surge +# BB20 Inferno +# BB21 Tsunami +# BB23 Stray Flames +# BB25 Stray Spray # BCCD Black Spark # BCF2 Graven Image # BD66 White Hole @@ -428,6 +481,7 @@ hideall "--sync--" # C252 Attack: P1 Kefka attack and P3 Chaos attack # C258 Meteor: P3 Chaos Enrage # C259 Bowels of Agony: P3 Exdeath Enrage +# C2DC Kefka Says # C2D6 Trance # C2E2 the Decisive Battle # C2E3 the Decisive Battle From 1364ad0aca4d15f3043450f7f62e1a1f38d4c2b6 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Fri, 5 Jun 2026 15:45:01 -0400 Subject: [PATCH 34/54] phase label --- ui/raidboss/data/07-dt/ultimate/dancing_mad.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt index 052c396538..9d94cfd3f8 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt @@ -307,7 +307,7 @@ hideall "--sync--" 845.8 "Meteor (Enrage)?" #Ability { id: "C258", source: "Exdeath" } # Exdeath >0% HP -### Phase 4 - Kefka + Neo Exdeath + Chaos +### Phase 4 - Kefka with Chaos and Neo Exdeath # TODO: Cleanup p4 as there are multiple sequences that could happen # TODO: Update with network log # TODO: Add voiceline sync? From bc48ba97728427e14212bac397a1f359d914244e Mon Sep 17 00:00:00 2001 From: Legends0 Date: Fri, 5 Jun 2026 15:48:30 -0400 Subject: [PATCH 35/54] fix label and window/jump order --- ui/raidboss/data/07-dt/ultimate/dancing_mad.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt index 9d94cfd3f8..aca19b9880 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt @@ -296,7 +296,7 @@ hideall "--sync--" 827.4 "Big Bang" Ability { id: "BB05", source: "Chaos" } 827.4 "Big Bang" #Ability { id: "BB06", source: "Chaos" } -839.0 "--sync--" StartsUsing { id: "C61E", source: "Exdeath" } jump "p3-exdeath-enrage" window 839,3 # Possibly this starts when Chaos dies? +839.0 "--sync--" StartsUsing { id: "C61E", source: "Exdeath" } window 839,3 jump "p3-exdeath-enrage" # Possibly this starts when Chaos dies? # Possibly there is another if Exdeath is instead killed first, but Chaos triggers untargetable? 840.8 "--sync--" StartsUsing { id: "C258", source: "Exdeath" } jump "p3-enrage" @@ -347,7 +347,7 @@ hideall "--sync--" 10845.8 "Meteor (Enrage)" Ability { id: "C258", source: "Exdeath" } # Exdeath >0% HP # Phase 3 Exdeath Enrage Sequence -11839.0 "p3-exdeath-enrage" +11839.0 label "p3-exdeath-enrage" 11849.0 "Meteor (Exdeath Enrage)" StartsUsing { id: "C61E", source: "Exdeath" } # Exdeath >0% HP # NOTE: Using a window to jump back to p4, TBD if killing Exdeath first triggers a separate sequence From 127e0accf586a30944d17f0cd7400299c7b89d12 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sat, 6 Jun 2026 00:06:00 -0400 Subject: [PATCH 36/54] p2 tb, p3 implosion + edict, add window to Aero III --- .../data/07-dt/ultimate/dancing_mad.ts | 30 +++++++++++++++++++ .../data/07-dt/ultimate/dancing_mad.txt | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index f47023bf4e..3db64e5eb6 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -1116,6 +1116,12 @@ const triggerSet: TriggerSet = { delete data.fireMarker; }, }, + { + id: 'DMU P2 Ultimate Embrace', + type: 'StartsUsing', + netRegex: { id: 'C24C', source: 'Kefka', capture: true }, + response: Responses.sharedTankBuster(), + }, { id: 'DMU P2 Future\'s End/Past\'s End', // There are four end casts @@ -1133,6 +1139,30 @@ const triggerSet: TriggerSet = { }, }, }, + { + id: 'DMU P3 Longitudinal Implosion', + type: 'StartsUsing', + netRegex: { id: 'BAFD', source: 'Chaos', capture: false }, + infoText: (_data, _matches, output) => output.sides!(), + outputStrings: { + sides: Outputs.sidesThenFrontBack, + }, + }, + { + id: 'DMU P3 Latitudinal Implosion', + type: 'StartsUsing', + netRegex: { id: 'BAFE', source: 'Chaos', capture: false }, + infoText: (_data, _matches, output) => output.frontBack!(), + outputStrings: { + frontBack: Outputs.frontBackThenSides, + }, + }, + { + id: 'DMU P3 Damning Edict', + type: 'StartsUsing', + netRegex: { id: 'BB01', source: 'Chaos', capture: false }, + response: Responses.getBehind(), + }, ], timelineReplace: [ { diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt index aca19b9880..a4ccc019bd 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt @@ -129,7 +129,7 @@ hideall "--sync--" 372.9 "Ultimate Embrace" Ability { id: "C24C", source: "Kefka" } 376.2 "--sync--" StartsUsing { id: "BAE1", source: "Kefka" } jump "p2-enrage" -379.3 "--sync--" StartsUsing { id: "C3F7", source: "Kefka" } jump "p2-success" +379.3 "--sync--" StartsUsing { id: "C3F7", source: "Kefka" } jump "p2-success" window 220,5 # Kefka will skip mechanics at 0% HP 381.2 "Light of Judgment (Enrage)?" #Ability { id: "BAE1", source: "Kefka" } # Kefka > 0% HP 382.3 "Aero III Assault?" Ability { id: "C3F7", source: "Kefka" } forcejump "p2-success" From 0fcf3a0b854bd34fba9d66582d681ae17bf17606 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sat, 6 Jun 2026 00:42:14 -0400 Subject: [PATCH 37/54] ave maria and indolent will triggers --- .../data/07-dt/ultimate/dancing_mad.ts | 54 ++++++++++++++++++- .../data/07-dt/ultimate/dancing_mad.txt | 2 +- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index d196a6a108..bad7fdbcca 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -24,7 +24,9 @@ export interface Data extends RaidbossData { blueTowerId?: string; yellowTowerId?: string; purpleTowerId?: string; - tower?: 'blue' | 'yellow' | 'purple'; + eyeTowerId?: string; + fakeEyeTowerId?: string; + tower?: 'blue' | 'yellow' | 'purple' | 'eye' | 'fakeeye'; gravenImageCount: number; actorPositions: { [id: string]: { x: number; y: number; heading: number } }; gravenImageTether?: @@ -187,6 +189,8 @@ const triggerSet: TriggerSet = { // 1EBFBB (2015163) => Wave Cannon entity (blue) // 1EBFBC (2015164) => Gravitational Wave entity (purple) // 1EBFBD (2015165) => Intemperate Will entity (yellow) + // 1EBFBE (2015166) => Indolent Will entity (eye) + // 1EBFBF (2015167) => Ave Maria entity (fake eye) // There are two of each, they are added at start of fight type: 'ActorControlExtra', netRegex: { category: '019D', param1: '40', param2: '80', capture: true }, @@ -204,6 +208,8 @@ const triggerSet: TriggerSet = { '2015163': 'blue', '2015164': 'purple', '2015165': 'yellow', + '2015166': 'eye', + '2015167': 'fakeeye', 'unknown': 'unknown', }; @@ -215,6 +221,10 @@ const triggerSet: TriggerSet = { data.yellowTowerId = id; else if (kind === 'purple') data.purpleTowerId = id; + else if (kind === 'eye') + data.eyeTowerId = id; + else if (kind === 'fakeeye') + data.fakeEyeTowerId = id; }, run: (data, matches) => { const id = matches.id; @@ -225,6 +235,10 @@ const triggerSet: TriggerSet = { data.tower = 'purple'; else if (data.blueTowerId === id) data.tower = 'blue'; + else if (data.eyeTowerId === id) + data.tower = 'eye'; + else if (data.fakeEyeTowerId === id) + data.tower = 'fakeeye'; }, }, { @@ -1060,6 +1074,44 @@ const triggerSet: TriggerSet = { }, }, }, + { + id: 'DMU P1 Ave Maria', + // BAB3 Ave Maria + type: 'ActorControlExtra', + netRegex: { category: '019D', param1: '40', param2: '80', capture: true }, + condition: (data, matches) => data.fakeEyeTowerId === matches.id, + alertText: (_data, _matches, output) => output.lookAt!(), + outputStrings: { + lookAt: { + en: 'Look At Statue', + de: 'Statue anschauen', + fr: 'Regardez la statue', + ja: '像を見る!', + cn: '面对神像', + ko: '시선 바라보기', + tc: '面對神像', + }, + }, + }, + { + id: 'DMU P1 Indolent Will', + // BAB4 Indolent Will + type: 'ActorControlExtra', + netRegex: { category: '019D', param1: '40', param2: '80', capture: true }, + condition: (data, matches) => data.eyeTowerId === matches.id, + alertText: (_data, _matches, output) => output.lookAway!(), + outputStrings: { + lookAway: { + en: 'Look Away From Statue', + de: 'Von Statue wegschauen', + fr: 'Ne regardez pas la statue', + ja: '塔を見ない!', + cn: '背对神像', + ko: '시선 피하기', + tc: '背對神像', + }, + }, + }, { id: 'DMU P1 Mystery Magic Fire and Thunder', // Set 3: Only Fire and Thunder should be set diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt index 5276443d0c..5204c53bb9 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt @@ -69,7 +69,7 @@ hideall "--sync--" 179.7 "--middle--" Ability { id: "C3FD", source: "Kefka" } 186.3 "Mystery Magic" Ability { id: "BA94", source: "Kefka" } 186.3 "Thrumming Thunder III" #Ability { id: ["BAA1", "BA9F"], source: "Kefka" } -186.3 "Indolent Will/Ave Maria" #Ability { id: ["BAB4", "BAB3"], source: "Graven Image" } +186.3 "Ave Maria/Indolent Will" #Ability { id: ["BAB3", "BAB4"], source: "Graven Image" } 187.1 "Flagrant Fire III" Ability { id: ["BAA2", "BAA3"], source: "Kefka" } 202.5 "Light of Judgment (Enrage)?" Ability { id: "BABB", source: "Kefka" } # Kefka >15% HP From 1ee147aee2d287823b9ae90b39f211874e47a2ff Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sat, 6 Jun 2026 00:44:46 -0400 Subject: [PATCH 38/54] remove extra space --- ui/raidboss/data/07-dt/ultimate/dancing_mad.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt index a4ccc019bd..71b5971fd1 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt @@ -129,7 +129,7 @@ hideall "--sync--" 372.9 "Ultimate Embrace" Ability { id: "C24C", source: "Kefka" } 376.2 "--sync--" StartsUsing { id: "BAE1", source: "Kefka" } jump "p2-enrage" -379.3 "--sync--" StartsUsing { id: "C3F7", source: "Kefka" } jump "p2-success" window 220,5 # Kefka will skip mechanics at 0% HP +379.3 "--sync--" StartsUsing { id: "C3F7", source: "Kefka" } jump "p2-success" window 220,5 # Kefka will skip mechanics at 0% HP 381.2 "Light of Judgment (Enrage)?" #Ability { id: "BAE1", source: "Kefka" } # Kefka > 0% HP 382.3 "Aero III Assault?" Ability { id: "C3F7", source: "Kefka" } forcejump "p2-success" From 7a40bcb83b7ba17fe70abd0f28e8142f3b5b4e92 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sat, 6 Jun 2026 01:08:59 -0400 Subject: [PATCH 39/54] reorder jump/window --- ui/raidboss/data/07-dt/ultimate/dancing_mad.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt index d6759f2fd0..f50d76efbc 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt @@ -129,7 +129,7 @@ hideall "--sync--" 372.9 "Ultimate Embrace" Ability { id: "C24C", source: "Kefka" } 376.2 "--sync--" StartsUsing { id: "BAE1", source: "Kefka" } jump "p2-enrage" -379.3 "--sync--" StartsUsing { id: "C3F7", source: "Kefka" } jump "p2-success" window 220,5 # Kefka will skip mechanics at 0% HP +379.3 "--sync--" StartsUsing { id: "C3F7", source: "Kefka" } window 220,5 jump "p2-success" # Kefka will skip mechanics at 0% HP 381.2 "Light of Judgment (Enrage)?" #Ability { id: "BAE1", source: "Kefka" } # Kefka > 0% HP 382.3 "Aero III Assault?" Ability { id: "C3F7", source: "Kefka" } forcejump "p2-success" From 568cb78126f68875801dfef11e498890c5c375f8 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sat, 6 Jun 2026 01:34:32 -0400 Subject: [PATCH 40/54] reduce ave maria/indolent will to info, add duration/cdseconds The animation is visible ~9.89s before cast goes off, however When animation becomes visible, the players will bbe asleep or confused for another ~3.4s. Once the debuff ends the players have ~6.4s to turn character --- ui/raidboss/data/07-dt/ultimate/dancing_mad.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index bad7fdbcca..26ae6f9557 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -1077,10 +1077,16 @@ const triggerSet: TriggerSet = { { id: 'DMU P1 Ave Maria', // BAB3 Ave Maria + // The animation is visible ~9.89s before cast goes off, however + // When animation becomes visible, the players will bbe asleep or + // confused for another ~3.4s. Once the debuff ends the players have + // ~6.4s to turn character type: 'ActorControlExtra', netRegex: { category: '019D', param1: '40', param2: '80', capture: true }, condition: (data, matches) => data.fakeEyeTowerId === matches.id, - alertText: (_data, _matches, output) => output.lookAt!(), + durationSeconds: 9.5, + countdownSeconds: 3.5, // Estimated time debuff would expire + infoText: (_data, _matches, output) => output.lookAt!(), outputStrings: { lookAt: { en: 'Look At Statue', @@ -1099,7 +1105,9 @@ const triggerSet: TriggerSet = { type: 'ActorControlExtra', netRegex: { category: '019D', param1: '40', param2: '80', capture: true }, condition: (data, matches) => data.eyeTowerId === matches.id, - alertText: (_data, _matches, output) => output.lookAway!(), + durationSeconds: 9.5, + countdownSeconds: 3.5, // Estimated time debuff would expire + infoText: (_data, _matches, output) => output.lookAway!(), outputStrings: { lookAway: { en: 'Look Away From Statue', From 20b855e18752032294c8151a670dca15ef9fce8a Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sat, 6 Jun 2026 01:35:47 -0400 Subject: [PATCH 41/54] typo in comment --- ui/raidboss/data/07-dt/ultimate/dancing_mad.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index 26ae6f9557..fba104fb93 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -1078,14 +1078,14 @@ const triggerSet: TriggerSet = { id: 'DMU P1 Ave Maria', // BAB3 Ave Maria // The animation is visible ~9.89s before cast goes off, however - // When animation becomes visible, the players will bbe asleep or + // When animation becomes visible, the players will be asleep or // confused for another ~3.4s. Once the debuff ends the players have // ~6.4s to turn character type: 'ActorControlExtra', netRegex: { category: '019D', param1: '40', param2: '80', capture: true }, condition: (data, matches) => data.fakeEyeTowerId === matches.id, durationSeconds: 9.5, - countdownSeconds: 3.5, // Estimated time debuff would expire + countdownSeconds: 3.4, // Estimated time debuff would expire infoText: (_data, _matches, output) => output.lookAt!(), outputStrings: { lookAt: { @@ -1106,7 +1106,7 @@ const triggerSet: TriggerSet = { netRegex: { category: '019D', param1: '40', param2: '80', capture: true }, condition: (data, matches) => data.eyeTowerId === matches.id, durationSeconds: 9.5, - countdownSeconds: 3.5, // Estimated time debuff would expire + countdownSeconds: 3.4, // Estimated time debuff would expire infoText: (_data, _matches, output) => output.lookAway!(), outputStrings: { lookAway: { From 79f81b4f9a70a70bb6e6019f9b1ebe9c49ebaf81 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sat, 6 Jun 2026 04:52:06 -0400 Subject: [PATCH 42/54] fix graven image collector, output fixes --- .../data/07-dt/ultimate/dancing_mad.ts | 95 +++++++------------ 1 file changed, 36 insertions(+), 59 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index fba104fb93..c4af7a8868 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -1,6 +1,5 @@ import Conditions from '../../../../../resources/conditions'; import Outputs from '../../../../../resources/outputs'; -import { callOverlayHandler } from '../../../../../resources/overlay_plugin_api'; import { Responses } from '../../../../../resources/responses'; import ZoneId from '../../../../../resources/zone_id'; import { RaidbossData } from '../../../../../types/data'; @@ -21,12 +20,11 @@ export interface Data extends RaidbossData { // General phase: Phase | 'unknown'; // Phase 1 - blueTowerId?: string; - yellowTowerId?: string; - purpleTowerId?: string; - eyeTowerId?: string; - fakeEyeTowerId?: string; - tower?: 'blue' | 'yellow' | 'purple' | 'eye' | 'fakeeye'; + blueTowerIds: string[]; + purpleTowerIds: string[]; + yellowTowerIds: string[]; + eyeTowerIds: string[]; + fakeEyeTowerIds: string[]; gravenImageCount: number; actorPositions: { [id: string]: { x: number; y: number; heading: number } }; gravenImageTether?: @@ -185,7 +183,10 @@ const triggerSet: TriggerSet = { id: 'DMU P1 Graven Image Collect', // Tower entity actions // The CombatantMemory Add lines are added prior to combat - // OverlayPlugin call used to retrieve the matching BNpcID + // OverlayPlugin can retrieve the matching BNpcID + // However, these entities seem to always spawn in the same order and the + // first tower is the highest ID and the towers are in sequential order + // These are the BNpcID values: // 1EBFBB (2015163) => Wave Cannon entity (blue) // 1EBFBC (2015164) => Gravitational Wave entity (purple) // 1EBFBD (2015165) => Intemperate Will entity (yellow) @@ -194,51 +195,23 @@ const triggerSet: TriggerSet = { // There are two of each, they are added at start of fight type: 'ActorControlExtra', netRegex: { category: '019D', param1: '40', param2: '80', capture: true }, - promise: async (data, matches) => { - const id = matches.id; - const actors = (await callOverlayHandler({ - call: 'getCombatants', - ids: [parseInt(id, 16)], - })).combatants; - const image = actors[0]; - if (image === undefined) - return; + suppressSeconds: 99999, + preRun: (data, matches) => { + const id = parseInt(matches.id, 16); + const blueTowers = [id, id - 1]; // First tower is blue and highest ID + const purpleTowers = [id - 2, id - 4]; // Next are in pair with yellow + const yellowTowers = [id - 3, id - 5]; + const eyeTowers = [id - 7, id - 9]; // Next are in paire with fake + const fakeEyeTowers = [id - 6, id - 8]; - const towerMap = { - '2015163': 'blue', - '2015164': 'purple', - '2015165': 'yellow', - '2015166': 'eye', - '2015167': 'fakeeye', - 'unknown': 'unknown', + const toStringId = (id: number): string => { + return id.toString(16).toUpperCase(); }; - - const bnpcid = image.BNpcID ?? 'unknown'; - const kind = towerMap[bnpcid as keyof typeof towerMap]; - if (kind === 'blue') - data.blueTowerId = id; - else if (kind === 'yellow') - data.yellowTowerId = id; - else if (kind === 'purple') - data.purpleTowerId = id; - else if (kind === 'eye') - data.eyeTowerId = id; - else if (kind === 'fakeeye') - data.fakeEyeTowerId = id; - }, - run: (data, matches) => { - const id = matches.id; - - if (data.yellowTowerId === id) - data.tower = 'yellow'; - else if (data.purpleTowerId === id) - data.tower = 'purple'; - else if (data.blueTowerId === id) - data.tower = 'blue'; - else if (data.eyeTowerId === id) - data.tower = 'eye'; - else if (data.fakeEyeTowerId === id) - data.tower = 'fakeeye'; + data.blueTowerIds = blueTowers.map((id) => toStringId(id)); + data.purpleTowerIds = purpleTowers.map((id) => toStringId(id)) + data.yellowTowerIds = yellowTowers.map((id) => toStringId(id)) + data.eyeTowerIds = eyeTowers.map((id) => toStringId(id)) + data.fakeEyeTowerIds = fakeEyeTowers.map((id) => toStringId(id)) }, }, { @@ -468,7 +441,7 @@ const triggerSet: TriggerSet = { // This gives a ~5 second warning to spread type: 'ActorControlExtra', netRegex: { category: '019D', param1: '40', param2: '80', capture: true }, - condition: (data, matches) => data.blueTowerId === matches.id, + condition: (data, matches) => data.blueTowerIds.includes(matches.id), alertText: (_data, _matches, output) => output.waveCannonLine!(), outputStrings: { waveCannonLine: { @@ -616,7 +589,7 @@ const triggerSet: TriggerSet = { : output.fakeIceTrueThunder!(); } return data.isIceTrue - ? output.trueIceTrueThunder!() + ? output.trueIceFakeThunder!() : output.fakeIceFakeThunder!(); }, outputStrings: mysteryMagicOutputStrings, @@ -679,8 +652,12 @@ const triggerSet: TriggerSet = { return output.avoidTethers!(); }, outputStrings: { - avoidTethers: 'Avoid Tethered Players', - spread: 'Spread (avoid puddles)', + avoidTethers: { + en: 'Avoid Tethered Players', + }, + spread: { + en: 'Spread (avoid puddles)', + }, }, }, { @@ -715,7 +692,7 @@ const triggerSet: TriggerSet = { id: 'DMU P1 Impertinent Will', type: 'ActorControlExtra', netRegex: { category: '019D', param1: '40', param2: '80', capture: true }, - condition: (data, matches) => data.yellowTowerId === matches.id, + condition: (data, matches) => data.yellowTowerIds.includes(matches.id), alertText: (_data, _matches, output) => output.goWest!(), outputStrings: { goWest: Outputs.getLeftAndWest, @@ -725,7 +702,7 @@ const triggerSet: TriggerSet = { id: 'DMU P1 Gravitational Wave', type: 'ActorControlExtra', netRegex: { category: '019D', param1: '40', param2: '80', capture: true }, - condition: (data, matches) => data.purpleTowerId === matches.id, + condition: (data, matches) => data.purpleTowerIds.includes(matches.id), alertText: (_data, _matches, output) => output.goEast!(), outputStrings: { goEast: Outputs.getRightAndEast, @@ -1083,7 +1060,7 @@ const triggerSet: TriggerSet = { // ~6.4s to turn character type: 'ActorControlExtra', netRegex: { category: '019D', param1: '40', param2: '80', capture: true }, - condition: (data, matches) => data.fakeEyeTowerId === matches.id, + condition: (data, matches) => data.fakeEyeTowerIds.includes(matches.id), durationSeconds: 9.5, countdownSeconds: 3.4, // Estimated time debuff would expire infoText: (_data, _matches, output) => output.lookAt!(), @@ -1104,7 +1081,7 @@ const triggerSet: TriggerSet = { // BAB4 Indolent Will type: 'ActorControlExtra', netRegex: { category: '019D', param1: '40', param2: '80', capture: true }, - condition: (data, matches) => data.eyeTowerId === matches.id, + condition: (data, matches) => data.eyeTowerIds.includes(matches.id), durationSeconds: 9.5, countdownSeconds: 3.4, // Estimated time debuff would expire infoText: (_data, _matches, output) => output.lookAway!(), From b6b78ed721e45f8f062e27d3d127dc07d37c8aff Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sat, 6 Jun 2026 04:55:45 -0400 Subject: [PATCH 43/54] missing initData --- ui/raidboss/data/07-dt/ultimate/dancing_mad.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index c4af7a8868..61a9846d29 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -155,6 +155,11 @@ const triggerSet: TriggerSet = { phase: 'p1', // Phase 1 actorPositions: {}, + blueTowerIds: [], + purpleTowerIds: [], + yellowTowerIds: [], + eyeTowerIds: [], + fakeEyeTowerIds: [], gravenImageCount: 0, waveCannonTargets: [], doubleTroubleTrapTargets: [], From 891e587da71aa6612b1c4c473fe33be1ce920f24 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sat, 6 Jun 2026 05:07:07 -0400 Subject: [PATCH 44/54] lint + reorder Since first trigger of the ActorControlExtra is a blue tower, can skip the check and suppress since this only happens once in P1. --- .../data/07-dt/ultimate/dancing_mad.ts | 80 +++++++++---------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index 61a9846d29..584a128c97 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -20,13 +20,13 @@ export interface Data extends RaidbossData { // General phase: Phase | 'unknown'; // Phase 1 + actorPositions: { [id: string]: { x: number; y: number; heading: number } }; + gravenImageCount: number; blueTowerIds: string[]; purpleTowerIds: string[]; yellowTowerIds: string[]; eyeTowerIds: string[]; fakeEyeTowerIds: string[]; - gravenImageCount: number; - actorPositions: { [id: string]: { x: number; y: number; heading: number } }; gravenImageTether?: | 'pulse' | 'gravitas' @@ -155,12 +155,12 @@ const triggerSet: TriggerSet = { phase: 'p1', // Phase 1 actorPositions: {}, + gravenImageCount: 0, blueTowerIds: [], purpleTowerIds: [], yellowTowerIds: [], eyeTowerIds: [], fakeEyeTowerIds: [], - gravenImageCount: 0, waveCannonTargets: [], doubleTroubleTrapTargets: [], }; @@ -184,41 +184,6 @@ const triggerSet: TriggerSet = { heading: parseFloat(matches.heading), }, }, - { - id: 'DMU P1 Graven Image Collect', - // Tower entity actions - // The CombatantMemory Add lines are added prior to combat - // OverlayPlugin can retrieve the matching BNpcID - // However, these entities seem to always spawn in the same order and the - // first tower is the highest ID and the towers are in sequential order - // These are the BNpcID values: - // 1EBFBB (2015163) => Wave Cannon entity (blue) - // 1EBFBC (2015164) => Gravitational Wave entity (purple) - // 1EBFBD (2015165) => Intemperate Will entity (yellow) - // 1EBFBE (2015166) => Indolent Will entity (eye) - // 1EBFBF (2015167) => Ave Maria entity (fake eye) - // There are two of each, they are added at start of fight - type: 'ActorControlExtra', - netRegex: { category: '019D', param1: '40', param2: '80', capture: true }, - suppressSeconds: 99999, - preRun: (data, matches) => { - const id = parseInt(matches.id, 16); - const blueTowers = [id, id - 1]; // First tower is blue and highest ID - const purpleTowers = [id - 2, id - 4]; // Next are in pair with yellow - const yellowTowers = [id - 3, id - 5]; - const eyeTowers = [id - 7, id - 9]; // Next are in paire with fake - const fakeEyeTowers = [id - 6, id - 8]; - - const toStringId = (id: number): string => { - return id.toString(16).toUpperCase(); - }; - data.blueTowerIds = blueTowers.map((id) => toStringId(id)); - data.purpleTowerIds = purpleTowers.map((id) => toStringId(id)) - data.yellowTowerIds = yellowTowers.map((id) => toStringId(id)) - data.eyeTowerIds = eyeTowers.map((id) => toStringId(id)) - data.fakeEyeTowerIds = fakeEyeTowers.map((id) => toStringId(id)) - }, - }, { id: 'DMU P1 Revolting Ruin III', // Tankbuster targets highest enmity then second highest enmity @@ -440,13 +405,48 @@ const triggerSet: TriggerSet = { delete data.gravenImageTether; }, }, + { + id: 'DMU P1 Graven Image Collect', + // Tower entity actions + // The CombatantMemory Add lines are added prior to combat + // OverlayPlugin can retrieve the matching BNpcID + // However, these entities seem to always spawn in the same order and the + // first tower is the highest ID and the towers are in sequential order + // These are the BNpcID values: + // 1EBFBB (2015163) => Wave Cannon entity (blue) + // 1EBFBC (2015164) => Gravitational Wave entity (purple) + // 1EBFBD (2015165) => Intemperate Will entity (yellow) + // 1EBFBE (2015166) => Indolent Will entity (eye) + // 1EBFBF (2015167) => Ave Maria entity (fake eye) + // There are two of each, they are added at start of fight + type: 'ActorControlExtra', + netRegex: { category: '019D', param1: '40', param2: '80', capture: true }, + preRun: (data, matches) => { + const id = parseInt(matches.id, 16); + const blueTowers = [id, id - 1]; // First tower is blue and highest ID + const purpleTowers = [id - 2, id - 4]; // Next are in pair with yellow + const yellowTowers = [id - 3, id - 5]; + const eyeTowers = [id - 7, id - 9]; // Next are in paire with fake + const fakeEyeTowers = [id - 6, id - 8]; + + const toStringId = (id: number): string => { + return id.toString(16).toUpperCase(); + }; + data.blueTowerIds = blueTowers.map((id) => toStringId(id)); + data.purpleTowerIds = purpleTowers.map((id) => toStringId(id)); + data.yellowTowerIds = yellowTowers.map((id) => toStringId(id)); + data.eyeTowerIds = eyeTowers.map((id) => toStringId(id)); + data.fakeEyeTowerIds = fakeEyeTowers.map((id) => toStringId(id)); + }, + suppressSeconds: 99999, + }, { id: 'DMU P1 Wave Cannon', // BAA8 Wave Cannon is an instant cast from Graven Image // This gives a ~5 second warning to spread type: 'ActorControlExtra', - netRegex: { category: '019D', param1: '40', param2: '80', capture: true }, - condition: (data, matches) => data.blueTowerIds.includes(matches.id), + netRegex: { category: '019D', param1: '40', param2: '80', capture: false }, + suppressSeconds: 99999, // First instance is a blue tower alertText: (_data, _matches, output) => output.waveCannonLine!(), outputStrings: { waveCannonLine: { From 79b24499e0274b25011c62bd67e3b919437b2634 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sat, 6 Jun 2026 07:35:03 -0400 Subject: [PATCH 45/54] p4 up to enrage --- .../data/07-dt/ultimate/dancing_mad.txt | 51 +++++++++++++++++-- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt index f50d76efbc..422bae439f 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt @@ -77,7 +77,7 @@ hideall "--sync--" # TODO: Update with network log, this uses FFLOGS # TODO: Get fake ending route? # TODO: Add voiceline sync / branch to complete? -# Complete Path: +# Fake Duty Complete Path: # en: 'Yes... I am filled with glorious purpose!' # Duty Complete Path: # en: 'How boring. Guess I'll have to spice things up!' @@ -321,13 +321,13 @@ hideall "--sync--" 1009.4 "Thrumming Thunder III" #Ability { id: "BA9F", source: "Kefka" } 1009.4 "Blizzard III Blowout" #Ability { id: "BA9B", source: "Kefka" } 1013.7 "Grand Cross" Ability { id: "BB14", source: "Neo Exdeath" } -1018.7 "Tsunami" Ability { id: "BB21", source: "Chaos" } +1018.7 "Inferno/Tsunami" Ability { id: ["BB20","BB21"], source: "Chaos" } 1024.1 "Mystery Magic 2" Ability { id: "BA94", source: "Kefka" } 1024.1 "Thrumming Thunder III" #Ability { id: "BAA1", source: "Kefka" } 1024.1 "Blizzard III Blowout" #Ability { id: "BA9B", source: "Kefka" } 1028.4 "Grand Cross" Ability { id: "BB14", source: "Neo Exdeath" } -1033.5 "Inferno" Ability { id: "BB20", source: "Chaos" } +1033.5 "Tsunami/Inferno" Ability { id: ["BB21", "BB20"], source: "Chaos" } 1039.2 "Mystery Magic 3" Ability { id: "BA94", source: "Kefka" } 1039.2 "Thrumming Thunder III" #Ability { id: "BAA1", source: "Kefka" } @@ -336,6 +336,35 @@ hideall "--sync--" 1045.3 "--sync--" Ability { id: "C554", source: "Kefka" } +1055.4 "Flood of Naught" Ability { id: "C393", source: "Neo Exdeath" } +1055.8 "Edge of Death" Ability { id: "C396", source: "Neo Exdeath" } +1055.8 "White Antilight" #Ability { id: "C394", source: "Neo Exdeath" } +1055.8 "Black Antilight" #Ability { id: "C395", source: "Neo Exdeath" } +1059.0 "--sync--" Ability { id: "C555", source: "Kefka" } +1059.7 "Death Surge" Ability { id: "BB1D", source: "Neo Exdeath" } +1064.1 "Mana Charge" Ability { id: "BAA4", source: "Kefka" } +1064.4 "Death Bolt" #Ability { id: "BB18", source: "Neo Exdeath" } +1064.4 "Death Wave" #Ability { id: "BB1A", source: "Neo Exdeath" } +1072.4 "Thrumming Thunder III" Ability { id: "C5DE", source: "Kefka" } +1072.4 "Thrumming Thunder III" #Ability { id: "BA9F", source: "Kefka" } +1073.4 "Death Shriek" Ability { id: "BB16", source: "Neo Exdeath" } +1082.5 "Ultima Upsurge" Ability { id: "C24A", source: "Kefka" } +1085.3 "Stray Flames" Ability { id: "BB23", source: "Chaos" } +1089.4 "Death Wave" Ability { id: "BB1B", source: "Neo Exdeath" } +1089.4 "Death Bolt" #Ability { id: "BB19", source: "Neo Exdeath" } +1089.4 "Death Bomb" #Ability { id: "BB15", source: "Neo Exdeath" } +1090.4 "Blizzard III Blowout" Ability { id: "BA9B", source: "Kefka" } +1094.6 "Stray Spray" #Ability { id: "BB25", source: "Chaos" } +1095.9 "Stray Spray" #Ability { id: "BB25", source: "Chaos" } +1097.2 "Death Shriek" Ability { id: "BB17", source: "Neo Exdeath" } +1103.5 "Mana Release" Ability { id: "BAA5", source: "Kefka" } +1107.6 "Stray Spray" Ability { id: "BB25", source: "Chaos" } +1108.5 "Thrumming Thunder III" #Ability { id: "BA9F", source: "Kefka" } +1108.5 "Blizzard III Blowout" #Ability { id: "BA9B", source: "Kefka" } + +1112.8 "--sync--" StartsUsing { id: "BABB", source: "Kefka" } jump "p4-enrage" +1112.8 "--untargetable?--" +1117.8 "Light of Judgment (Enrage)?" Ability { id: "BABB", source: "Kefka" } # Kefka >15% HP? # Phase 2 Enrage Sequence 10376.2 label "p2-enrage" @@ -351,6 +380,11 @@ hideall "--sync--" 11849.0 "Meteor (Exdeath Enrage)" StartsUsing { id: "C61E", source: "Exdeath" } # Exdeath >0% HP # NOTE: Using a window to jump back to p4, TBD if killing Exdeath first triggers a separate sequence +# Phase 4 Enrage Sequence +12000.0 label "p4-enrage" +12005.0 "Light of Judgment (Enrage)" Ability { id: "BABB", source: "Kefka" } # Kefka >15% HP? + + # IGNORED ABILITIES # C252 Attack: P1 Kefka attack and P3 Chaos attack # BA9E Blizzard III Blowout: Damage @@ -378,6 +412,8 @@ hideall "--sync--" # BAA1 Thrumming Thunder III: Damage (paired with BAA0) # BAA2 Flagrant Fire III: Spread Damage # BAA3 Flagrant Fire III: Stack Damage +# BAA4 Mana Charge +# BAA5 Mana Release # BAA6 Double-Trouble Trap: VFX # BAA7 Double-Trouble Trap # BAA8 Wave Cannon @@ -396,7 +432,7 @@ hideall "--sync--" # BAB6 Idyllic Will # BAB9 Tele-Trouncing: VFX # BABA Tele-Trouncing -# BABB Light of Judgment: P1 Enrage +# BABB Light of Judgment: P1 Enrage and P4 Enrage # BABC Forsaken # BABD Light of Judgment: P2 version # BABE The Path of Light @@ -461,6 +497,7 @@ hideall "--sync--" # BB12 Thunder III # BB13 Vacuum Wave # BB14 Grand Cross +# BB15 Death Bomb # BB16 Death Shriek # BB17 Death Shriek # BB18 Death Bolt @@ -475,6 +512,7 @@ hideall "--sync--" # BCCD Black Spark # BCF2 Graven Image # BD66 White Hole +# C24A Ultima Upsurge # C24B Hyperdrive # C24C Ultimate Embrace # C250 Attack: P3 Exdeath attack @@ -487,6 +525,10 @@ hideall "--sync--" # C2E3 the Decisive Battle # C2E4 Aetherlink # C2E5 Aetherlink +# C393 Flood of Naught +# C394 White Antilight +# C395 Black Antilight +# C396 Edge of Death # C3F7 Aero III Assault # C3FD --sync--: Boss jumps to middle # C403 Revolting Ruin III @@ -498,4 +540,5 @@ hideall "--sync--" # C555 --sync-- # C571 Earthquake # C572 Earthquake +# C5DE Thrumming Thunder III # C622 Light of Judgment: P1 version From ded6f5bfe2ad236539b275464623a73b1f0bcc0b Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sat, 6 Jun 2026 20:36:32 -0400 Subject: [PATCH 46/54] double-trouble trap (knockback) merge There were also cases where player could die to the knockback and still be called in the early trigger. --- .../data/07-dt/ultimate/dancing_mad.ts | 100 ++++++------------ 1 file changed, 33 insertions(+), 67 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index 584a128c97..a03326848d 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -138,9 +138,21 @@ const mysteryMagicOutputStrings: OutputStrings = { }; const trapOutputStrings: OutputStrings = { - knockbackFrom: { + knockbackFrom1: { en: 'Knockback from ${players}', }, + knockbackFrom2: { + en: 'Knockback from ${players}', + }, + knockbackFrom3: { + en: 'Knockback from ${players} => Debuffs', + }, + knockbackFrom3Sleep: { + en: 'Knockback from ${players} => Sleep', + }, + knockbackFrom3Confuse: { + en: 'Knockback from ${players} => Confuse', + }, knockbackFromLater: { en: 'Knockback from ${players} (later)', }, @@ -519,13 +531,13 @@ const triggerSet: TriggerSet = { }, }, { - id: 'DMU P1 Double-trouble Trap 1', + id: 'DMU P1 Double-trouble Trap Knockback', type: 'GainsEffect', netRegex: { effectId: '13D6', capture: true }, - condition: (_data, matches) => parseFloat(matches.duration) < 6, - delaySeconds: 0.1, + delaySeconds: (_data, matches) => parseFloat(matches.duration) - 5.9, // First one needs 0.1 delay for collect + durationSeconds: 5.9, suppressSeconds: 1, - response: (data, _matches, output) => { + response: (data, matches, output) => { // cactbot-builtin-response output.responseOutputStrings = trapOutputStrings; @@ -538,7 +550,18 @@ const triggerSet: TriggerSet = { }, ); const msg = players?.join(', '); - return { [severity]: output.knockbackFrom!({ players: msg }) }; + + const duration = parseFloat(matches.duration); + if (duration < 6) + return { [severity]: output.knockbackFrom1!({ players: msg }) }; + if (duration > 67) + return { [severity]: output.knockbackFrom2!({ players: msg }) }; + + if (data.gravenImageTether === 'idyllic') + return { [severity]: output.knockbackFrom3Sleep!({ players: msg }) }; + if (data.gravenImageTether === 'indulgent') + return { [severity]: output.knockbackFrom3Confuse!({ players: msg }) }; + return { [severity]: output.knockbackFrom3!({ players: msg }) }; }, }, { @@ -556,7 +579,7 @@ const triggerSet: TriggerSet = { id: 'DMU P1 Double-trouble Trap 2 Early', type: 'GainsEffect', netRegex: { effectId: '13D6', capture: true }, - delaySeconds: 0.1, + delaySeconds: 0.3, // Time between debuff and dying from the application suppressSeconds: 1, infoText: (data, matches, output) => { // Ignore first set and third set @@ -564,7 +587,7 @@ const triggerSet: TriggerSet = { return; // Check if players died - if (data.doubleTroubleTrapTargets[0] === undefined) + if (data.doubleTroubleTrapTargets.length === 0) return; const players = data.doubleTroubleTrapTargets.map( @@ -669,7 +692,7 @@ const triggerSet: TriggerSet = { id: 'DMU P1 Double-trouble Trap 3 Early', type: 'GainsEffect', netRegex: { effectId: '13D6', capture: true }, - delaySeconds: 0.1, + delaySeconds: 0.3, // Time between debuff and dying from the application suppressSeconds: 1, infoText: (data, matches, output) => { const duration = parseFloat(matches.duration); @@ -678,7 +701,7 @@ const triggerSet: TriggerSet = { return; // Check if players died - if (data.doubleTroubleTrapTargets[0] === undefined) + if (data.doubleTroubleTrapTargets.length === 0) return; const players = data.doubleTroubleTrapTargets.map( @@ -778,63 +801,6 @@ const triggerSet: TriggerSet = { }, }, }, - { - id: 'DMU P1 Double-trouble Trap 2', - type: 'GainsEffect', - netRegex: { effectId: '13D6', capture: true }, - condition: (_data, matches) => parseFloat(matches.duration) > 67, - delaySeconds: (_data, matches) => parseFloat(matches.duration) - 5, - suppressSeconds: 1, - response: (data, _matches, output) => { - // cactbot-builtin-response - output.responseOutputStrings = trapOutputStrings; - - // Check if players died - if (data.doubleTroubleTrapTargets[0] === undefined) - return; - - const severity = data.doubleTroubleTrapTargets.includes(data.me) ? 'alertText' : 'infoText'; - const players = data.doubleTroubleTrapTargets.map( - (player) => { - if (player === data.me) - return 'YOU'; - return data.party.member(player); - }, - ); - const msg = players?.join(', '); - return { [severity]: output.knockbackFrom!({ players: msg }) }; - }, - }, - { - id: 'DMU P1 Double-trouble Trap 3', - type: 'GainsEffect', - netRegex: { effectId: '13D6', capture: true }, - condition: (_data, matches) => { - const duration = parseFloat(matches.duration); - return duration > 48 && duration < 50; - }, - delaySeconds: (_data, matches) => parseFloat(matches.duration) - 5, - suppressSeconds: 1, - response: (data, _matches, output) => { - // cactbot-builtin-response - output.responseOutputStrings = trapOutputStrings; - - // Check if players died - if (data.doubleTroubleTrapTargets[0] === undefined) - return; - - const severity = data.doubleTroubleTrapTargets.includes(data.me) ? 'alertText' : 'infoText'; - const players = data.doubleTroubleTrapTargets.map( - (player) => { - if (player === data.me) - return 'YOU'; - return data.party.member(player); - }, - ); - const msg = players?.join(', '); - return { [severity]: output.knockbackFrom!({ players: msg }) }; - }, - }, { id: 'DMU P1 Tele-Portent Collect', // Debuffs distributed to 8 players: From c8e0a348ad062b2d5a4407acf5df4caa55ad4b3a Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sat, 6 Jun 2026 20:39:24 -0400 Subject: [PATCH 47/54] lint --- ui/raidboss/data/07-dt/ultimate/dancing_mad.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index a03326848d..597db7aff0 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -534,7 +534,7 @@ const triggerSet: TriggerSet = { id: 'DMU P1 Double-trouble Trap Knockback', type: 'GainsEffect', netRegex: { effectId: '13D6', capture: true }, - delaySeconds: (_data, matches) => parseFloat(matches.duration) - 5.9, // First one needs 0.1 delay for collect + delaySeconds: (_data, matches) => parseFloat(matches.duration) - 5.9, // First one needs 0.1 delay for collect durationSeconds: 5.9, suppressSeconds: 1, response: (data, matches, output) => { From a110eab346ea2164f6e1bdc3d7ae96bd61d7d148 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 7 Jun 2026 01:58:15 -0400 Subject: [PATCH 48/54] change delay on double-trouble trap --- ui/raidboss/data/07-dt/ultimate/dancing_mad.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index 597db7aff0..c126c78b58 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -534,8 +534,8 @@ const triggerSet: TriggerSet = { id: 'DMU P1 Double-trouble Trap Knockback', type: 'GainsEffect', netRegex: { effectId: '13D6', capture: true }, - delaySeconds: (_data, matches) => parseFloat(matches.duration) - 5.9, // First one needs 0.1 delay for collect - durationSeconds: 5.9, + delaySeconds: (_data, matches) => parseFloat(matches.duration) - 3.9, // First one needs 0.1 delay for collect + durationSeconds: 3.9, suppressSeconds: 1, response: (data, matches, output) => { // cactbot-builtin-response From fa2df4e609f841851d4abd0dd4f265a4e889d4f8 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 7 Jun 2026 04:20:44 -0400 Subject: [PATCH 49/54] additional fflogs lines into p5 --- .../data/07-dt/ultimate/dancing_mad.ts | 4 +- .../data/07-dt/ultimate/dancing_mad.txt | 83 +++++++++++++++++-- 2 files changed, 81 insertions(+), 6 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index 6cef2d2def..d3442f89aa 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -6,12 +6,14 @@ import { RaidbossData } from '../../../../../types/data'; import { OutputStrings, TriggerSet } from '../../../../../types/trigger'; // TODO: P1 Tele-Portent configuration options +// TODO: Earlier phase tracking for P5 (counting the jumps to middle?) -type Phase = 'p1' | 'p2' | 'p3' | 'p4'; +type Phase = 'p1' | 'p2' | 'p3' | 'p4' | 'p5'; const phases: { [id: string]: Phase } = { 'C24C': 'p2', // Ultimate Embrace, God Kefka 'C3F7': 'p3', // Aero III Assault (from Kefka), Chaos and Exdeath 'C2DC': 'p4', // Kefka Says, Kefka with Chaos and Neo Exdeath + 'BB40': 'p5', // Ultima Repeater, Ultima Kefka }; // const centerX = 100; diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt index 422bae439f..ee86805384 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt @@ -2,7 +2,7 @@ # ZoneId: 1363 # -ii C250 C252 BA9E BAA0 BAAB BA95 BAAF BAAD BAD6 BAD8 BAD7 BAD9 BAF9 BAF1 -# -p C403:15.6 C24C:216.5 C3F7:500.0 C2DC:1000.0 +# -p C403:15.6 C24C:216.5 C3F7:500.0 C2DC:1000.0 BB40:1300.0 # -it Kefka Chaos Exdeath hideall "--Reset--" @@ -364,11 +364,65 @@ hideall "--sync--" 1112.8 "--sync--" StartsUsing { id: "BABB", source: "Kefka" } jump "p4-enrage" 1112.8 "--untargetable?--" -1117.8 "Light of Judgment (Enrage)?" Ability { id: "BABB", source: "Kefka" } # Kefka >15% HP? +1114.8 "--middle?--" Ability { id: "C3FD", source: "Kefka" } window 90,5 jump "p4-success" # Kefka <20% HP +1117.8 "Light of Judgment (Enrage)?" #Ability { id: "BABB", source: "Kefka" } # Kefka >20% HP +1121.3 "Ultima Upsurge?" #Ability { id: "C24A", source: "Kefka" } +1159.2 "Ultima Repeater (castbar)?" #Ability { id: "BB40", source: "Kefka" } + + +### Phase 5 - Kefka +# TODO: Add voiceline sync? +# en: 'Destruction is the fate of all things.' +1255.6 label "p4-success" +1255.6 "--middle--" Ability { id: "C3FD", source: "Kefka" } +1261.9 "Ultima Upsurge" Ability { id: "C24A", source: "Kefka" } +1300.0 "Ultima Repeater (castbar)" Ability { id: "BB40", source: "Kefka" } +1301.1 "Ultima Repeater 1" #Ability { id: "BB41", source: "Kefka" } +1301.9 "Ultima Repeater 2" #Ability { id: "BB41", source: "Kefka" } +1302.6 "Ultima Repeater 3" #Ability { id: "BB41", source: "Kefka" } +1303.4 "Ultima Repeater 4" #Ability { id: "BB41", source: "Kefka" } +1305.4 "--sync--" Ability { id: "C652", source: "Kefka" } +1306.3 "Fell Forces" Ability { id: "C654", source: "Kefka" } +1306.3 "Fell Forces" #Ability { id: "C655", source: "Kefka" } +1306.3 "Fell Forces" #Ability { id: "C653", source: "Kefka" } +1308.6 "--sync--" Ability { id: "C652", source: "Kefka" } +1309.5 "Fell Forces" Ability { id: "C654", source: "Kefka" } +1309.5 "Fell Forces" #Ability { id: "C655", source: "Kefka" } +1309.5 "Fell Forces" #Ability { id: "C653", source: "Kefka" } +1311.8 "--sync--" Ability { id: "C652", source: "Kefka" } +1312.7 "Fell Forces" Ability { id: "C654", source: "Kefka" } +1312.7 "Fell Forces" #Ability { id: "C655", source: "Kefka" } +1312.7 "Fell Forces" #Ability { id: "C653", source: "Kefka" } +1314.6 "--sync--" #Ability { id: "C183", source: "Kefka" } +1315.6 "--sync--" #Ability { id: "C183", source: "Kefka" } +1316.6 "--sync--" #Ability { id: "C183", source: "Kefka" } +1317.6 "--sync--" #Ability { id: "C183", source: "Kefka" } +1317.9 "Flood" Ability { id: "C13F", source: "Kefka" } +1318.9 "Chaotic Flood" #Ability { id: "BB4F", source: "Kefka" } +1318.9 "Flood" #Ability { id: "C269", source: "Kefka" } +1319.9 "Chaotic Flood" #Ability { id: "BB4F", source: "Kefka" } +1319.9 "Flood" #Ability { id: "C269", source: "Kefka" } +1320.8 "Chaotic Flood" #Ability { id: "BB4F", source: "Kefka" } +1320.9 "Flood" #Ability { id: "C269", source: "Kefka" } +1321.8 "Chaotic Flood" #Ability { id: "BB4F", source: "Kefka" } +1321.9 "Flood" #Ability { id: "C269", source: "Kefka" } +1330.9 "Maddening Orchestra" Ability { id: "BB50", source: "Kefka" } +1331.8 "Holy" Ability { id: "BB54", source: "Kefka" } +1331.8 "Flare" #Ability { id: "BB52", source: "Kefka" } +1334.0 "Maddening Orchestra" Ability { id: "BB51", source: "Kefka" } +1334.9 "Holy" Ability { id: "BB54", source: "Kefka" } +1334.9 "Chaotic Flare" #Ability { id: "BB53", source: "Kefka" } +1335.6 "Chaotic Holy" Ability { id: "BB56", source: "Kefka" } +1336.3 "Flare Diffusion" Ability { id: "BB55", source: "Kefka" } +1342.3 "--sync--" Ability { id: "C652", source: "Kefka" } +1343.2 "Fell Forces" Ability { id: "C653", source: "Kefka" } +1345.5 "--sync--" Ability { id: "C652", source: "Kefka" +1352.3 "Celestriad" Ability { id: "BB42", source: "Kefka" } +# TODO: Get up to the enrage # Phase 2 Enrage Sequence 10376.2 label "p2-enrage" -10381.2 "Light of Judgment (Enrage)" Ability { id: "BAE1", source: "Kefka" } # Kefka > 0% HP +10381.2 "Light of Judgment (Enrage)" Ability { id: "BAE1", source: "Kefka" } # Kefka >0% HP # Phase 3 Enrage Sequence 10840.8 label "p3-enrage" @@ -382,7 +436,7 @@ hideall "--sync--" # Phase 4 Enrage Sequence 12000.0 label "p4-enrage" -12005.0 "Light of Judgment (Enrage)" Ability { id: "BABB", source: "Kefka" } # Kefka >15% HP? +12005.0 "Light of Judgment (Enrage)" Ability { id: "BABB", source: "Kefka" } # Kefka >20% HP # IGNORED ABILITIES @@ -510,8 +564,21 @@ hideall "--sync--" # BB23 Stray Flames # BB25 Stray Spray # BCCD Black Spark +# BB40 Ultima Repeater +# BB41 Ultima Repeater +# BB42 Celestriad +# BB4F Chaotic Flood +# BB50 Maddening Orchestra +# BB51 Maddening Orchestra +# BB52 Flare +# BB53 Chaotic Flare +# BB54 Holy +# BB55 Flare Diffusion +# BB56 Chaotic Holy # BCF2 Graven Image # BD66 White Hole +# C13F Flood +# C183 --sync-- # C24A Ultima Upsurge # C24B Hyperdrive # C24C Ultimate Embrace @@ -519,12 +586,14 @@ hideall "--sync--" # C252 Attack: P1 Kefka attack and P3 Chaos attack # C258 Meteor: P3 Chaos Enrage # C259 Bowels of Agony: P3 Exdeath Enrage -# C2DC Kefka Says +# C269 Flood # C2D6 Trance +# C2DC Kefka Says # C2E2 the Decisive Battle # C2E3 the Decisive Battle # C2E4 Aetherlink # C2E5 Aetherlink +# C392 Flood of Naught # C393 Flood of Naught # C394 White Antilight # C395 Black Antilight @@ -542,3 +611,7 @@ hideall "--sync--" # C572 Earthquake # C5DE Thrumming Thunder III # C622 Light of Judgment: P1 version +# C652 --sync-- +# C653 Fell Forces +# C654 Fell Forces +# C655 Fell Forces From 6b8b019d6dcb82ed8591a7689f3c742d72fdee59 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 7 Jun 2026 04:23:23 -0400 Subject: [PATCH 50/54] minor p2/p3 triggers --- .../data/07-dt/ultimate/dancing_mad.ts | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index d3442f89aa..a6cdea726d 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -1132,6 +1132,14 @@ const triggerSet: TriggerSet = { netRegex: { id: 'C24C', source: 'Kefka', capture: true }, response: Responses.sharedTankBuster(), }, + { + id: 'DMU P2 Forsaken', + // 7s cast + type: 'StartsUsing', + netRegex: { id: 'BABC', source: 'Kefka', capture: false }, + durationSeconds: 6.7, + response: Responses.bigAoe('alert'), + }, { id: 'DMU P2 Future\'s End/Past\'s End', // There are four end casts @@ -1149,6 +1157,49 @@ const triggerSet: TriggerSet = { }, }, }, + { + id: 'DMU P3 Epic Hero/Fated Hero Debuffs', + // Applied to 4 nearest players when Chaos and Exdeath finish casting + // C2E2/C2E3 The Decisive Battle + // 1060 Epic Hero: Can only damage Chaos, preferred by Melee DPS + // 1062 Fated Hero: Can only damage Exdeath, preferred by Ranged DPS + // These fall off once Exdeath casts BB12 Thunder III + type: 'GainsEffect', + netRegex: { effectId: ['1060', '1062'], capture: true }, + condition: Conditions.targetIsYou(), + infoText: (_data, matches, output) => { + return matches.effectId === '1060' ? output.epic!() : output.fated!(); + }, + outputStrings: { + epic: { + en: 'Attack Chaos', + }, + fated: { + en: 'Attack Exdeath', + }, + }, + }, + { + id: 'DMU P3 Headwind/Tailwind Debuffs', + // Applied at BAF2 Bowels of Agony + // Debuffs trigger if hit by certain sources, causing a knockback + // 642 Headwind: Face away from damage source + // 643 Tailwind: Face towards damage source + type: 'GainsEffect', + netRegex: { effectId: ['642', '643'], capture: true }, + condition: Conditions.targetIsYou(), + infoText: (_data, matches, output) => { + return matches.effectId === '642' ? output.headwind!() : output.tailwind!(); + }, + outputStrings: { + headwind: { + en: 'Headwind on YOU', + }, + tailwind: { + en: 'Tailwind on You', + }, + }, + }, { id: 'DMU P3 Longitudinal Implosion', type: 'StartsUsing', @@ -1167,6 +1218,19 @@ const triggerSet: TriggerSet = { frontBack: Outputs.frontBackThenSides, }, }, + { + id: 'DMU P3 Vaccuum Wave', + type: 'StartsUsing', + netRegex: { id: 'BB13', source: 'Chaos', capture: true }, + infoText: (data, matches, output) => { + return output.knockbackFromBoss!({ chaos: matches.source }); + }, + outputStrings: { + knockbackFromBoss: { + en: 'Knockback from ${chaos}', + }, + }, + }, { id: 'DMU P3 Damning Edict', type: 'StartsUsing', From bf29dddaf9a7f6bb3ec491990349df09f2e9b688 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 7 Jun 2026 04:52:12 -0400 Subject: [PATCH 51/54] retimed start of p2 with network log --- .../data/07-dt/ultimate/dancing_mad.txt | 103 +++++++++--------- 1 file changed, 54 insertions(+), 49 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt index ee86805384..bb6880c9ac 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt @@ -1,8 +1,8 @@ ### DANCING MAD (ULTIMATE) # ZoneId: 1363 -# -ii C250 C252 BA9E BAA0 BAAB BA95 BAAF BAAD BAD6 BAD8 BAD7 BAD9 BAF9 BAF1 -# -p C403:15.6 C24C:216.5 C3F7:500.0 C2DC:1000.0 BB40:1300.0 +# -ii C250 C252 BA9E BAA0 BAAB BA95 BAAF BAAD BABF BAD6 BAD8 BAD7 BAD9 BAF9 BAF1 +# -p C403:15.6 C24C:219.8 C3F7:500.0 C2DC:1000.0 BB40:1300.0 # -it Kefka Chaos Exdeath hideall "--Reset--" @@ -71,7 +71,10 @@ hideall "--sync--" 186.3 "Thrumming Thunder III" #Ability { id: ["BAA1", "BA9F"], source: "Kefka" } 186.3 "Ave Maria/Indolent Will" #Ability { id: ["BAB3", "BAB4"], source: "Graven Image" } 187.1 "Flagrant Fire III" Ability { id: ["BAA2", "BAA3"], source: "Kefka" } -202.5 "Light of Judgment (Enrage)?" Ability { id: "BABB", source: "Kefka" } # Kefka >15% HP +197.3 "--untargetable--" +197.5 "--sync--" StartsUsing { id: "BABB", source: "Kefka" } jump "p1-and-p4-enrage" +202.4 "Light of Judgment (Enrage)?" Ability { id: "BABB", source: "Kefka" } # Kefka >15% HP + ### Phase 2 - God Kefka # TODO: Update with network log, this uses FFLOGS @@ -81,57 +84,58 @@ hideall "--sync--" # en: 'Yes... I am filled with glorious purpose!' # Duty Complete Path: # en: 'How boring. Guess I'll have to spice things up!' -216.5 "Ultimate Embrace" Ability { id: "C24C", source: "Kefka" } window 220,5 -231.7 "Forsaken" Ability { id: "BABC", source: "Kefka" } +207.6 "--targetable--" +219.8 "Ultimate Embrace" Ability { id: "C24C", source: "Kefka" } window 220,5 +234.9 "Forsaken" Ability { id: "BABC", source: "Kefka" } # Set 1 # NOTE: The BAC0 Spelldriver, BAC2 Spellwave, BAC0 Spellscatter can be resolved in different orders -244.9 "The Path of Light 1" Ability { id: "BABE", source: "Kefka" } -245.6 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"], source: "Kefka" } -254.3 "Future's End/Past's End 1" Ability { id: ["BAD2", "BAD3"], source: "Kefka" } -255.0 "The Path of Light 2" Ability { id: "BABE", source: "Kefka" } -255.7 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"], source: "Kefka" } -265.7 "All Things Ending" #Ability { id: ["BACD", "BADC", "BADD"], source: "Kefka" } +248.1 "The Path of Light 1" Ability { id: "BABE", source: "Kefka" } +248.8 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"], source: "Kefka" } +257.5 "Future's End/Past's End 1" Ability { id: ["BAD2", "BAD3"], source: "Kefka" } +258.2 "The Path of Light 2" Ability { id: "BABE", source: "Kefka" } +259.0 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"], source: "Kefka" } +269.0 "All Things Ending" #Ability { id: ["BACD", "BADC", "BADD"], source: "Kefka" } # Set 2 -265.9 "The Path of Light 3" Ability { id: "BABE", source: "Kefka" } -266.4 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"], source: "Kefka" } -275.4 "Future's End/Past's End 2" Ability { id: ["BAD2", "BAD3"], source: "Kefka" } -275.8 "The Path of Light 4" Ability { id: "BABE", source: "Kefka" } -276.5 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"], source: "Kefka" } -286.7 "All Things Ending" #Ability { id: ["BACD", "BADD"], source: "Kefka" } +269.2 "The Path of Light 3" Ability { id: "BABE", source: "Kefka" } +269.9 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"], source: "Kefka" } +278.5 "Future's End/Past's End 2" Ability { id: ["BAD2", "BAD3"], source: "Kefka" } +279.4 "The Path of Light 4" Ability { id: "BABE", source: "Kefka" } +280.1 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"], source: "Kefka" } +289.9 "All Things Ending" #Ability { id: ["BACD", "BADC", "BADD"], source: "Kefka" } # Set 3 -286.7 "The Path of Light 5" Ability { id: "BABE", source: "Kefka" } -287.1 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"], source: "Kefka" } -296.1 "Future's End/Past's End 3" Ability { id: ["BAD2", "BAD3"], source: "Kefka" } -296.4 "The Path of Light 6" Ability { id: "BABE", source: "Kefka" } -297.1 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"], source: "Kefka" } -307.5 "All Things Ending" #Ability { id: ["BACD", "BADC", "BADD"], source: "Kefka" } +290.4 "The Path of Light 5" Ability { id: "BABE", source: "Kefka" } +291.1 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"], source: "Kefka" } +299.7 "Future's End/Past's End 3" Ability { id: ["BAD2", "BAD3"], source: "Kefka" } +300.6 "The Path of Light 6" Ability { id: "BABE", source: "Kefka" } +301.3 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"], source: "Kefka" } +311.3 "All Things Ending" #Ability { id: ["BACD", "BADC", "BADD"], source: "Kefka" } # Set 4 -307.5 "The Path of Light 7" Ability { id: "BABE", source: "Kefka" } -308.0 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"], source: "Kefka" } -317.0 "Future's End/Past's End 4" Ability { id: ["BAD2", "BAD3"], source: "Kefka" } -317.4 "The Path of Light 8" Ability { id: "BABE", source: "Kefka" } -318.1 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"], source: "Kefka" } -328.3 "All Things Ending" Ability { id: ["BACD", "BADC", "BADD"], source: "Kefka" } +311.5 "The Path of Light 7" Ability { id: "BABE", source: "Kefka" } +312.2 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"], source: "Kefka" } +321.0 "Future's End/Past's End 4" Ability { id: ["BAD2", "BAD3"], source: "Kefka" } +321.7 "The Path of Light 8" Ability { id: "BABE", source: "Kefka" } +322.4 "Spelldriver/Spellscatter/Spellwave" Ability { id: ["BAC0", "BAC1", "BAC2"], source: "Kefka" } +332.5 "All Things Ending" Ability { id: ["BACD", "BADC", "BADD"], source: "Kefka" } # Trines -337.4 "Light of Judgment" Ability { id: "BABD", source: "Kefka" } -348.6 "Trine" Ability { id: "BADF", source: "Kefka" } -355.7 "Wings of Destruction" Ability { id: "BACD", source: "Kefka" } -361.5 "Trine 1" #Ability { id: "BAE0", source: "Kefka" } -363.5 "Trine 2" #Ability { id: "BAE0", source: "Kefka" } -365.5 "Trine 3" #Ability { id: "BAE0", source: "Kefka" } -365.9 "Wings of Destruction" Ability { id: "C487", source: "Kefka" } -365.9 "Wings of Destruction" #Ability { id: "BACF", source: "Kefka" } -372.9 "Ultimate Embrace" Ability { id: "C24C", source: "Kefka" } - -376.2 "--sync--" StartsUsing { id: "BAE1", source: "Kefka" } jump "p2-enrage" -379.3 "--sync--" StartsUsing { id: "C3F7", source: "Kefka" } window 220,5 jump "p2-success" # Kefka will skip mechanics at 0% HP -381.2 "Light of Judgment (Enrage)?" #Ability { id: "BAE1", source: "Kefka" } # Kefka > 0% HP -382.3 "Aero III Assault?" Ability { id: "C3F7", source: "Kefka" } forcejump "p2-success" +341.5 "Light of Judgment" Ability { id: "BABD", source: "Kefka" } +352.7 "Trine" Ability { id: "BADF", source: "Kefka" } +359.8 "Wings of Destruction" Ability { id: "BACD", source: "Kefka" } +365.6 "Trine 1" #Ability { id: "BAE0", source: "Kefka" } +367.6 "Trine 2" #Ability { id: "BAE0", source: "Kefka" } +369.6 "Trine 3" #Ability { id: "BAE0", source: "Kefka" } +370.0 "Wings of Destruction" Ability { id: "C487", source: "Kefka" } +370.0 "Wings of Destruction" #Ability { id: "BACF", source: "Kefka" } +377.1 "Ultimate Embrace" Ability { id: "C24C", source: "Kefka" } + +380.5 "--sync--" StartsUsing { id: "BAE1", source: "Kefka" } jump "p2-enrage" +383.7 "--sync--" StartsUsing { id: "C3F7", source: "Kefka" } window 220,5 jump "p2-success" # Kefka will skip mechanics at 0% HP +385.5 "Light of Judgment (Enrage)?" #Ability { id: "BAE1", source: "Kefka" } # Kefka > 0% HP +386.7 "Aero III Assault?" Ability { id: "C3F7", source: "Kefka" } forcejump "p2-success" # TODO: Earlier sync on jump middle 497.0 label "p2-success" @@ -362,7 +366,7 @@ hideall "--sync--" 1108.5 "Thrumming Thunder III" #Ability { id: "BA9F", source: "Kefka" } 1108.5 "Blizzard III Blowout" #Ability { id: "BA9B", source: "Kefka" } -1112.8 "--sync--" StartsUsing { id: "BABB", source: "Kefka" } jump "p4-enrage" +1112.8 "--sync--" StartsUsing { id: "BABB", source: "Kefka" } jump "p1-and-p4-enrage" 1112.8 "--untargetable?--" 1114.8 "--middle?--" Ability { id: "C3FD", source: "Kefka" } window 90,5 jump "p4-success" # Kefka <20% HP 1117.8 "Light of Judgment (Enrage)?" #Ability { id: "BABB", source: "Kefka" } # Kefka >20% HP @@ -420,6 +424,10 @@ hideall "--sync--" 1352.3 "Celestriad" Ability { id: "BB42", source: "Kefka" } # TODO: Get up to the enrage +# Phase 1 and Phase 4 Enrage Sequence +10000.0 label "p1-and-p4-enrage" +10005.0 "Light of Judgment (Enrage)" Ability { id: "BABB", source: "Kefka" } # P1 Kefka > 15%HP / P4 Kefka >20% HP + # Phase 2 Enrage Sequence 10376.2 label "p2-enrage" 10381.2 "Light of Judgment (Enrage)" Ability { id: "BAE1", source: "Kefka" } # Kefka >0% HP @@ -434,10 +442,6 @@ hideall "--sync--" 11849.0 "Meteor (Exdeath Enrage)" StartsUsing { id: "C61E", source: "Exdeath" } # Exdeath >0% HP # NOTE: Using a window to jump back to p4, TBD if killing Exdeath first triggers a separate sequence -# Phase 4 Enrage Sequence -12000.0 label "p4-enrage" -12005.0 "Light of Judgment (Enrage)" Ability { id: "BABB", source: "Kefka" } # Kefka >20% HP - # IGNORED ABILITIES # C252 Attack: P1 Kefka attack and P3 Chaos attack @@ -447,6 +451,7 @@ hideall "--sync--" # BAAB Unmitigated Explosion: Failing to soak a tower from BAA8 Wave Cannon # BAAD Gravitational Explosion: BAB0 Vitrophyre aoe overlaps with BAAC Gravitas puddle # BAAF Gravity III: Soaking the Gravitas puddles at the correct time +# BABF The River of Light: Failing to soak a tower in P2, applies a 3-minute damage down # BAD6 Future's End: Damage (On 1 player) # BAD7 Past's End: Damage (On 1 player) # BAD8 Future's End: Damage (On 3 players) @@ -490,7 +495,7 @@ hideall "--sync--" # BABC Forsaken # BABD Light of Judgment: P2 version # BABE The Path of Light -# BABF The River of Light +# BABF The River of Light: Failing to soak a tower in P2, applies a 3-minute damage down # BAC0 Spelldriver # BAC1 Spellscatter # BAC2 Spellwave From 9a44485ddd253f5314eeb7bbfc0c78931861b15c Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 7 Jun 2026 05:59:57 -0400 Subject: [PATCH 52/54] add p2 forsaken headmarkers --- .../data/07-dt/ultimate/dancing_mad.ts | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index a6cdea726d..729cb81290 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -62,6 +62,11 @@ const headMarkerData = { 'stack': '0080', // spread (fake) or stack (real) // Phase 1 Tethers 'imageTether': '002D', + // Phase 2 + 'sharedBuster': '0103', // Ultimate Embrace shared tankbuster + 'stackPath': '02CB', // When standing in Path of Light tower, causes BAC0 Spelldriver (3-person stack) + 'conePath': '02CD', // When standing in Path of Light tower, causes BAC2 Spellwave (cone targetting nearest player) + 'spreadPath': '02CC', // When standing in Path of Light tower, causes BAC1 Spellscatter (small aoe on the player) } as const; const mysteryMagicOutputStrings: OutputStrings = { @@ -1140,6 +1145,45 @@ const triggerSet: TriggerSet = { durationSeconds: 6.7, response: Responses.bigAoe('alert'), }, + { + id: 'DMU P2 Path of Light Headmarker', + type: 'HeadMarker', + netRegex: { + id: [ + headMarkerData['stackPath'], + headMarkerData['conePath'], + headMarkerData['spreadPath'], + ], + capture: true, + }, + condition: Conditions.targetIsYou(), + infoText: (_data, matches, output) => { + const id = matches.id; + type markerMap = { + [key: string]: 'stack' | 'cone' | 'spread'; + }; + const markers: markerMap = { + '02CB': 'stack', + '02CD': 'cone', + '02CC': 'spread', + }; + const marker = markers[id]; + if (marker === undefined) + return; + return output[marker]!(); + }, + outputStrings: { + stack: { + en: 'Stack Path on YOU', + }, + cone: { + en: 'Cone Path on YOU', + }, + spread: { + en: 'Spread Path on YOU', + }, + }, + }, { id: 'DMU P2 Future\'s End/Past\'s End', // There are four end casts From 3981951216e85a326381b8aa85298ac9967bb251 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 7 Jun 2026 06:02:02 -0400 Subject: [PATCH 53/54] double-trouble trap update Remove players from the output if they died between receiving the buff and knockback call. --- ui/raidboss/data/07-dt/ultimate/dancing_mad.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index c126c78b58..071f19fb81 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -541,6 +541,10 @@ const triggerSet: TriggerSet = { // cactbot-builtin-response output.responseOutputStrings = trapOutputStrings; + // If players died before the duration ended + if (data.doubleTroubleTrapTargets.length === 0) + return; + const severity = data.doubleTroubleTrapTargets.includes(data.me) ? 'alertText' : 'infoText'; const players = data.doubleTroubleTrapTargets.map( (player) => { From d29540840267d71094987c8d6408113883c3a27a Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 7 Jun 2026 06:12:51 -0400 Subject: [PATCH 54/54] lint and missing bracket --- ui/raidboss/data/07-dt/ultimate/dancing_mad.ts | 2 +- ui/raidboss/data/07-dt/ultimate/dancing_mad.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts index e0538d32f4..ce5de4d89f 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.ts @@ -1270,7 +1270,7 @@ const triggerSet: TriggerSet = { id: 'DMU P3 Vaccuum Wave', type: 'StartsUsing', netRegex: { id: 'BB13', source: 'Chaos', capture: true }, - infoText: (data, matches, output) => { + infoText: (_data, matches, output) => { return output.knockbackFromBoss!({ chaos: matches.source }); }, outputStrings: { diff --git a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt index bb6880c9ac..c61c68899a 100644 --- a/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt +++ b/ui/raidboss/data/07-dt/ultimate/dancing_mad.txt @@ -420,7 +420,7 @@ hideall "--sync--" 1336.3 "Flare Diffusion" Ability { id: "BB55", source: "Kefka" } 1342.3 "--sync--" Ability { id: "C652", source: "Kefka" } 1343.2 "Fell Forces" Ability { id: "C653", source: "Kefka" } -1345.5 "--sync--" Ability { id: "C652", source: "Kefka" +1345.5 "--sync--" Ability { id: "C652", source: "Kefka" } 1352.3 "Celestriad" Ability { id: "BB42", source: "Kefka" } # TODO: Get up to the enrage