From 7b2cb9a14315fcd4e60f1d6f9f450c4402f608bc Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 19 Jan 2026 08:14:23 -0500 Subject: [PATCH 001/139] add initial triggers and timeline for p1 --- ui/raidboss/data/07-dt/raid/r12s.ts | 1103 ++++++++++++++++++++++++++ ui/raidboss/data/07-dt/raid/r12s.txt | 454 +++++++++++ 2 files changed, 1557 insertions(+) create mode 100644 ui/raidboss/data/07-dt/raid/r12s.ts create mode 100644 ui/raidboss/data/07-dt/raid/r12s.txt diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts new file mode 100644 index 00000000000..beec50f1111 --- /dev/null +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -0,0 +1,1103 @@ +import Conditions from '../../../../../resources/conditions'; +import { UnreachableCode } from '../../../../../resources/not_reached'; +import Outputs from '../../../../../resources/outputs'; +import { Responses } from '../../../../../resources/responses'; +import { Directions } from '../../../../../resources/util'; +import ZoneId from '../../../../../resources/zone_id'; +import { RaidbossData } from '../../../../../types/data'; +import { TriggerSet } from '../../../../../types/trigger'; + +export type Phase = 'doorboss' | 'curtainCall' | 'slaughtershed' | 'two'; + +export interface Data extends RaidbossData { + phase: Phase; + // Phase 1 + grotesquerieCleave?: + | 'rightCleave' + | 'leftCleave' + | 'frontCleave' + | 'rearCleave'; + myFleshBonds?: 'alpha' | 'beta'; + inLine: { [name: string]: number }; + blobTowerDirs: string[]; + fleshBondsCount: number; + cellChainCount: number; + cellTowerCount: number; + myMitoticPhase?: string; + hasRot: boolean; + // Phase 2 +} + +const headMarkerData = { + // Phase 1 + // VFX: com_share3t + 'stack': '00A1', + // VFX: tank_lockonae_6m_5s_01t + 'tankbuster': '0158', + // VFX: VFX: x6rc_cellchain_01x + 'cellChain': '0291', + // VFX: com_share3_7s0p + 'slaughterStack': '013D', + // VFX: target_ae_s7k1 + 'slaughterSpread': '0177', + 'cellChainTether': '016E', + // Phase 2 +} as const; + +const center = { + x: 100, + y: 100, +} as const; + +const phaseMap: { [id: string]: Phase } = { + 'BEC0': 'curtainCall', + 'B4C6': 'slaughtershed', +}; + +const triggerSet: TriggerSet = { + id: 'AacHeavyweightM4Savage', + zoneId: ZoneId.AacHeavyweightM4Savage, + timelineFile: 'r12s.txt', + initData: () => ({ + phase: 'doorboss', + // Phase 1 + inLine: {}, + blobTowerDirs: [], + fleshBondsCount: 0, + cellChainCount: 0, + cellTowerCount: 0, + hasRot: false, + // Phase 2 + }), + triggers: [ + { + id: 'R12S Phase Tracker', + type: 'StartsUsing', + netRegex: { id: Object.keys(phaseMap), source: 'Lindwurm' }, + suppressSeconds: 1, + run: (data, matches) => { + const phase = phaseMap[matches.id]; + if (phase === undefined) + throw new UnreachableCode(); + + data.phase = phase; + }, + }, + { + id: 'R12S The Fixer', + type: 'StartsUsing', + netRegex: { id: 'B4D7', source: 'Lindwurm', capture: false }, + durationSeconds: 4.7, + response: Responses.bigAoe('alert'), + }, + { + id: 'R12S Directed Grotesquerie Direction Collect', + // Unknown_DE6 spell contains data in its count: + // 40C, Front Cone + // 40D, Right Cone + // 40E, Rear Cone + // 40F, Left Cone + type: 'GainsEffect', + netRegex: { effectId: 'DE6', capture: true }, + condition: Conditions.targetIsYou(), + run: (data, matches) => { + switch (matches.count) { + case '40C': + data.grotesquerieCleave = 'frontCleave'; + return; + case '40D': + data.grotesquerieCleave = 'rightCleave'; + return; + case '40E': + data.grotesquerieCleave = 'rearCleave'; + return; + case '40F': + data.grotesquerieCleave = 'leftCleave'; + return; + } + }, + }, + { + id: 'R12S Shared Grotesquerie', + type: 'GainsEffect', + netRegex: { effectId: '129A', capture: true }, + delaySeconds: 0.2, + durationSeconds: 17, + infoText: (data, matches, output) => { + const cleave = data.grotesquerieCleave; + const target = matches.target; + if (target === data.me) { + if (cleave === undefined) + return output.baitThenStack!({ stack: output.stackOnYou!() }); + return output.baitThenStackCleave!({ + stack: output.stackOnYou!(), + cleave: output[cleave]!(), + }); + } + + const player = data.party.member(target); + const isDPS = data.party.isDPS(target); + if (isDPS && data.role === 'dps') { + if (cleave === undefined) + return output.baitThenStack!({ + stack: output.stackOnPlayer!({ player: player }), + }); + return output.baitThenStackCleave!({ + stack: output.stackOnPlayer!({ player: player }), + cleave: output[cleave]!(), + }); + } + if (!isDPS && data.role !== 'dps') { + if (cleave === undefined) + return output.baitThenStack!({ + stack: output.stackOnPlayer!({ player: player }), + }); + return output.baitThenStackCleave!({ + stack: output.stackOnPlayer!({ player: player }), + cleave: output[cleave]!(), + }); + } + }, + outputStrings: { + stackOnYou: Outputs.stackOnYou, + stackOnPlayer: Outputs.stackOnPlayer, + frontCleave: { + en: 'Front Cleave', + de: 'Kegel Aoe nach Vorne', + fr: 'Cleave Avant', + ja: '口からおくび', + cn: '前方扇形', + ko: '전방 부채꼴 장판', + tc: '前方扇形', + }, + rearCleave: { + en: 'Rear Cleave', + de: 'Kegel Aoe nach Hinten', + fr: 'Cleave Arrière', + ja: '尻からおなら', + cn: '背后扇形', + ko: '후방 부채꼴 장판', + tc: '背後扇形', + }, + leftCleave: { + en: 'Left Cleave', + de: 'Linker Cleave', + fr: 'Cleave gauche', + ja: '左半面へ攻撃', + cn: '左刀', + ko: '왼쪽 공격', + tc: '左刀', + }, + rightCleave: { + en: 'Right Cleave', + de: 'Rechter Cleave', + fr: 'Cleave droit', + ja: '右半面へ攻撃', + cn: '右刀', + ko: '오른쪽 공격', + tc: '右刀', + }, + baitThenStack: { + en: 'Bait 4x Puddles => ${stack}', + }, + baitThenStackCleave: { + en: 'Bait 4x Puddles => ${stack} + ${cleave}', + }, + }, + }, + { + id: 'R12S Bursting Grotesquerie', + type: 'GainsEffect', + netRegex: { effectId: '1299', capture: true }, + condition: Conditions.targetIsYou(), + delaySeconds: 0.2, + durationSeconds: 17, + infoText: (data, _matches, output) => { + const cleave = data.grotesquerieCleave; + if (cleave === undefined) + return data.phase === 'doorboss' + ? output.baitThenSpread!() + : output.spreadCurtain!(); + return data.phase === 'doorboss' + ? output.baitThenSpreadCleave!({ cleave: output[cleave]!() }) + : output.spreadCurtain!(); + }, + outputStrings: { + frontCleave: { + en: 'Front Cleave', + de: 'Kegel Aoe nach Vorne', + fr: 'Cleave Avant', + ja: '口からおくび', + cn: '前方扇形', + ko: '전방 부채꼴 장판', + tc: '前方扇形', + }, + rearCleave: { + en: 'Rear Cleave', + de: 'Kegel Aoe nach Hinten', + fr: 'Cleave Arrière', + ja: '尻からおなら', + cn: '背后扇形', + ko: '후방 부채꼴 장판', + tc: '背後扇形', + }, + leftCleave: { + en: 'Left Cleave', + de: 'Linker Cleave', + fr: 'Cleave gauche', + ja: '左半面へ攻撃', + cn: '左刀', + ko: '왼쪽 공격', + tc: '左刀', + }, + rightCleave: { + en: 'Right Cleave', + de: 'Rechter Cleave', + fr: 'Cleave droit', + ja: '右半面へ攻撃', + cn: '右刀', + ko: '오른쪽 공격', + tc: '右刀', + }, + baitThenSpread: { + en: 'Bait 4x Puddles => Spread', + }, + baitThenSpreadCleave: { + en: 'Bait 4x Puddles => Spread + ${cleave}', + }, + spreadCurtain: { + en: 'Spread Debuff on YOU', + }, + }, + }, + { + id: 'R12S Ravenous Reach 1 Safe Side', + // These two syncs indicate the animation of where the head will go to cleave + // B49A => West Safe + // B49B => East Safe + type: 'Ability', + netRegex: { id: ['B49A', 'B49B'], source: 'Lindwurm', capture: true }, + condition: (data) => data.phase === 'doorboss', + infoText: (_data, matches, output) => { + if (matches.id === 'B49A') + return output.goWest!(); + return output.goEast!(); + }, + outputStrings: { + goEast: Outputs.east, + goWest: Outputs.west, + }, + }, + { + id: 'R12S Fourth-wall Fusion Stack', + type: 'HeadMarker', + netRegex: { id: headMarkerData['stack'], capture: true }, + condition: (data) => { + if (data.role === 'tank') + return false; + return true; + }, + durationSeconds: 5.1, + response: Responses.stackMarkerOn(), + }, + { + id: 'R12S Tankbuster', + type: 'HeadMarker', + netRegex: { id: headMarkerData['tankbuster'], capture: true }, + condition: Conditions.targetIsYou(), + durationSeconds: 5.1, + response: Responses.tankBuster(), + }, + { + id: 'R12S In Line Debuff Collector', + type: 'GainsEffect', + netRegex: { effectId: ['BBC', 'BBD', 'BBE', 'D7B'] }, + run: (data, matches) => { + const effectToNum: { [effectId: string]: number } = { + BBC: 1, + BBD: 2, + BBE: 3, + D7B: 4, + } as const; + const num = effectToNum[matches.effectId]; + if (num === undefined) + return; + data.inLine[matches.target] = num; + }, + }, + { + id: 'R12S Bonds of Flesh Flesh α/β Collect', + // Bonds of Flesh has the following timings: + // 1st - 26s + // 2nd - 31s + // 3rd - 36s + // 4rth - 41s + type: 'GainsEffect', + netRegex: { effectId: ['1290', '1292'], capture: true }, + condition: Conditions.targetIsYou(), + run: (data, matches) => { + data.myFleshBonds = matches.effectId === '1290' ? 'alpha' : 'beta'; + }, + }, + { + id: 'R12S In Line Debuff', + type: 'GainsEffect', + netRegex: { effectId: ['BBC', 'BBD', 'BBE', 'D7B'], capture: false }, + delaySeconds: 0.5, + durationSeconds: 10, + suppressSeconds: 1, + infoText: (data, _matches, output) => { + const myNum = data.inLine[data.me]; + if (myNum === undefined) + return; + const flesh = data.myFleshBonds; + if (flesh === undefined) + return output.order!({ + num: myNum + }); + if (flesh === 'alpha') { + switch (myNum) { + case 1: + return output.alpha1!(); + case 2: + return output.alpha2!(); + case 3: + return output.alpha3!(); + case 4: + return output.alpha4!(); + } + } + switch (myNum) { + case 1: + return output.beta1!(); + case 2: + return output.beta2!(); + case 3: + return output.beta3!(); + case 4: + return output.beta4!(); + } + }, + outputStrings: { + alpha1: { + en: '1α: Wait for Tether 1', + }, + alpha2: { + en: '2α: Wait for Tether 2', + }, + alpha3: { + en: '3α: Blob Tower 1', + }, + alpha4: { + en: '4α: Blob Tower 2', + }, + beta1: { + en: '1β: Wait for Tether 1', + }, + beta2: { + en: '2β: Wait for Tether 2', + }, + beta3: { + en: '3β: Chain Tower 1', + }, + beta4: { + en: '4β: Chain Tower 2', + }, + order: { + en: '${num}', + de: '${num}', + fr: '${num}', + ja: '${num}', + cn: '${num}', + ko: '${num}', + tc: '${num}', + }, + unknown: Outputs.unknown, + }, + }, + { + id: 'R12S Phagocyte Spotlight Blob Tower Location Collect', + // StartsUsing and StartsUsingExtra can have bad data, there is enough time that Ability is sufficient + // Pattern 1 + // Blob 1: (104, 104) SE Inner + // Blob 2: (96, 96) NW Inner + // Blob 3: (85, 110) SW Outer + // Blob 4: (115, 90) NE Outer + // Pattern 2 + // Blob 1: (104, 96) NE Inner + // Blob 2: (96, 104) SW Inner + // Blob 3: (85, 90) NW Outer + // Blob 4: (115, 110) SE Outer + // Pattern 3 + // Blob 1: (96, 96) NW Inner + // Blob 2: (104, 104) SE Inner + // Blob 3: (115, 90) NE Outer + // Blob 4: (85, 110) SW Outer + // Pattern 4 + // Blob 1: (96, 104) SW Inner + // Blob 2: (104, 96) NE Inner + // Blob 3: (115, 110) SE Outer + // Blob 4: (86, 90) NW Outer + type: 'Ability', + netRegex: { id: 'B4B6', capture: true }, + run: (data, matches) => { + const x = parseFloat(matches.x); + const y = parseFloat(matches.y); + data.blobTowerDirs.push(Directions.xyToIntercardDirOutput(x, y, center.x, center.y)); + }, + }, + { + id: 'R12S Phagocyte Spotlight Blob Tower Location (Early)', + // 23.8s until B4B7 Rolling Mass Blob Tower Hit + type: 'Ability', + netRegex: { id: 'B4B6', capture: true }, + condition: (data) => { + if (data.myFleshBonds === 'alpha') { + const myNum = data.inLine[data.me]; + if ( + (myNum === 1 && data.blobTowerDirs.length === 3) || + (myNum === 2 && data.blobTowerDirs.length === 4) || + (myNum === 3 && data.blobTowerDirs.length === 1) || + (myNum === 4 && data.blobTowerDirs.length === 2) + ) + return true; + } + return false; + }, + delaySeconds: 0.1, + durationSeconds: (data) => { + const myNum = data.inLine[data.me]; + // Timings based on next trigger + switch (myNum) { + case 1: + return 13; + case 2: + return 16; + case 3: + return 17; + case 4: + return 15; + } + }, + infoText: (data, _matches, output) => { + const myNum = data.inLine[data.me]; + if (myNum === undefined) + return; + + type index = { + [key: number]: number; + }; + const myNumToDirIndex: index = { + 1: 2, + 2: 3, + 3: 0, + 4: 1, + }; + const dirIndex = myNumToDirIndex[myNum]; + if (dirIndex === undefined) + return; + const towerNum = dirIndex + 1; + + const dir = data.blobTowerDirs[dirIndex]; + if (dir === undefined) + return; + + if (myNum > 2) + return output.innerBlobTower!({ + num: towerNum, + dir: output[dir]!(), + }); + return output.outerBlobTower!({ num: towerNum, dir: output[dir]!() }); + }, + outputStrings: { + ...Directions.outputStringsIntercardDir, + innerBlobTower: { + en: 'Blob Tower ${num} Inner ${dir} (later)', + }, + outerBlobTower: { + en: 'Blob Tower ${num} Outer ${dir} (later)', + }, + }, + }, + { + id: 'R12S Cell Chain Counter', + type: 'Tether', + netRegex: { id: headMarkerData['cellChainTether'], capture: false }, + condition: (data) => data.phase === 'doorboss', + run: (data) => data.cellChainCount = data.cellChainCount + 1, + }, + { + id: 'R12S Cell Chain Tether Number', + // Helpful for players to keep track of which chain tower is next + // Does not output when it is their turn to break the tether + type: 'Tether', + netRegex: { id: headMarkerData['cellChainTether'], capture: false }, + condition: (data) => { + if (data.phase === 'doorboss' && data.myFleshBonds === 'beta') + return true; + return false; + }, + infoText: (data, _matches, output) => { + const myNum = data.inLine[data.me]; + const num = data.cellChainCount; + if (myNum !== num) + return output.tether!({ num: num }); + if (myNum === undefined) + return output.tether!({ num: num }); + }, + outputStrings: { + tether: { + en: 'Tether ${num}', + de: 'Verbindung ${num}', + fr: 'Lien ${num}', + ja: '線 ${num}', + cn: '线 ${num}', + ko: '선 ${num}', + tc: '線 ${num}', + }, + }, + }, + { + id: 'R12S Chain Tower Number', + // Using B4B4 Dramatic Lysis to detect chain broken + type: 'Ability', + netRegex: { id: 'B4B4', capture: false }, + condition: (data) => { + if (data.phase === 'doorboss' && data.myFleshBonds === 'beta') + return true; + return false; + }, + suppressSeconds: 1, + alertText: (data, _matches, output) => { + const mechanicNum = data.cellChainCount; + const myNum = data.inLine[data.me]; + if (myNum === undefined) + return; + + type index = { + [key: number]: number; + }; + const myNumToOrder: index = { + 1: 3, + 2: 4, + 3: 1, + 4: 2, + }; + + const myOrder = myNumToOrder[myNum]; + if (myOrder === undefined) + return; + + if (myOrder === mechanicNum) + return output.tower!({ num: mechanicNum }); + }, + outputStrings: { + tower: { + en: 'Get Chain Tower ${num}', + }, + }, + }, + { + id: 'R12S Chain Tower Counter', + // Using B4B3 Roiling Mass to detect chain tower soak + // Also using B4B2 Unmitigated Explosion if missed tower + type: 'Ability', + netRegex: { id: ['B4B3', 'B4B2'], capture: false }, + suppressSeconds: 1, + run: (data) => data.cellTowerCount = data.cellTowerCount + 1, + }, + { + id: 'R12S Chain Tower Followup', + // Using B4B3 Roiling Mass to detect chain tower soak + // Beta player leaving early may get hit by alpha's chain break aoe + type: 'Ability', + netRegex: { id: 'B4B3', capture: true }, + condition: (data, matches) => { + if (data.myFleshBonds === 'beta' && data.me === matches.target) + return true; + return false; + }, + infoText: (data, _matches, output) => { + const mechanicNum = data.cellTowerCount; + const myNum = data.inLine[data.me]; + if (myNum === undefined) + return; + + type index = { + [key: number]: number; + }; + const myNumToOrder: index = { + 1: 3, + 2: 4, + 3: 1, + 4: 2, + }; + + const myOrder = myNumToOrder[myNum]; + if (myOrder === undefined) + return; + + if (myOrder === mechanicNum) { + if (mechanicNum < 4) + return output.goIntoMiddle!(); + return output.getOut!(); + } + }, + outputStrings: { + getOut: { + en: 'Get Out', + de: 'Raus da', + fr: 'Sortez', + ja: '外へ', + cn: '远离', + ko: '밖으로', + tc: '遠離', + }, + goIntoMiddle: Outputs.goIntoMiddle, + }, + }, + { + id: 'R12S Bonds of Flesh Flesh α First Two Towers', + // These are not dependent on player timings and so can be hard coded by duration + type: 'GainsEffect', + netRegex: { effectId: '1290', capture: true }, + condition: (data, matches) => { + if (matches.target === data.me) { + const duration = parseFloat(matches.duration); + if (duration < 35) + return false; + return true; + } + return false; + }, + delaySeconds: (_data, matches) => { + const duration = parseFloat(matches.duration); + if (duration > 35) + return 27; + return 32; + }, + infoText: (data, matches, output) => { + const duration = parseFloat(matches.duration); + const dir = data.blobTowerDirs[duration > 40 ? 1 : 0]; + if (duration > 40) { + if (dir !== undefined) + return output.alpha4Dir!({ dir: output[dir]!() }); + return output.alpha4!(); + } + if (dir !== undefined) + return output.alpha3Dir!({ dir: output[dir]!() }); + return output.alpha3!(); + }, + outputStrings: { + ...Directions.outputStringsIntercardDir, + alpha3: { + en: 'Get Blob Tower 1', + }, + alpha4: { + en: 'Get Blob Tower 2', + }, + alpha3Dir: { + en: 'Blob Tower 1 (Inner ${dir})', + }, + alpha4Dir: { + en: 'Blob Tower 2 (Inner ${dir})', + }, + }, + }, + { + id: 'R12S Unbreakable Flesh α/β Chains and Last Two Towers', + type: 'GainsEffect', + netRegex: { effectId: ['1291', '1293'], capture: true }, + condition: (data, matches) => { + if (matches.target === data.me && data.phase === 'doorboss') + return true; + return false; + }, + infoText: (data, matches, output) => { + const myNum = data.inLine[data.me]; + const flesh = matches.effectId === '1291' ? 'alpha' : 'beta'; + if (flesh === 'alpha') { + if (myNum === 1) { + const dir = data.blobTowerDirs[2]; + if (dir !== undefined) + return output.alpha1Dir!({ + chains: output.breakChains!(), + dir: output[dir]!(), + }); + } + if (myNum === 2) { + const dir = data.blobTowerDirs[3]; + if (dir !== undefined) + return output.alpha2Dir!({ + chains: output.breakChains!(), + dir: output[dir]!(), + }); + } + + // dir undefined or 3rd/4rth in line + switch (myNum) { + case 1: + return output.alpha1!({ chains: output.breakChains!() }); + case 2: + return output.alpha2!({ chains: output.breakChains!() }); + case 3: + return output.alpha3!({ chains: output.breakChains!() }); + case 4: + return output.alpha4!({ chains: output.breakChains!() }); + } + } + switch (myNum) { + case 1: + return output.beta1!({ chains: output.breakChains!() }); + case 2: + return output.beta2!({ chains: output.breakChains!() }); + case 3: + return output.beta3!({ chains: output.breakChains!() }); + case 4: + return output.beta4!({ chains: output.breakChains!() }); + } + return output.getTowers!(); + }, + outputStrings: { + ...Directions.outputStringsIntercardDir, + breakChains: Outputs.breakChains, + getTowers: Outputs.getTowers, + alpha1: { + en: '${chains} 1 + Blob Tower 3 (Outer)', + }, + alpha1Dir: { + en: '${chains} 1 + Blob Tower 3 (Outer ${dir})', + }, + alpha2: { + en: '${chains} 2 + Blob Tower 4 (Outer)', + }, + alpha2Dir: { + en: '${chains} 2 + Blob Tower 4 (Outer ${dir})', + }, + alpha3: { + en: '${chains} 3 + Get Out', + }, + alpha4: { + en: '${chains} 4 + Get Out', + }, + beta1: { + en: '${chains} 1 => Get Middle', + }, + beta2: { + en: '${chains} 2 => Get Middle', + }, + beta3: { + en: '${chains} 3 => Wait for last pair', + }, + beta4: { + en: '${chains} 4 + Get Out', + }, + }, + }, + { + id: 'R12S Splattershed', + type: 'StartsUsing', + netRegex: { id: ['B9C3', 'B9C4'], source: 'Lindwurm', capture: false }, + response: Responses.aoe(), + }, + { + id: 'R12S Mitotic Phase Direction Collect', + // Unknown_DE6 spell contains data in its count + type: 'GainsEffect', + netRegex: { effectId: 'DE6', capture: true }, + condition: Conditions.targetIsYou(), + durationSeconds: 10, + infoText: (data, matches, output) => { + data.myMitoticPhase = matches.count; + switch (matches.count) { + case '436': + return output.frontTower!(); + case '437': + return output.rightTower!(); + case '438': + return output.rearTower!(); + case '439': + return output.leftTower!(); + } + }, + outputStrings: { + frontTower: { + en: 'Tower (S/SW)', + }, + rearTower: { + en: 'Tower (N/NE)', + }, + leftTower: { + en: 'Tower (E/SE)', + }, + rightTower: { + en: 'Tower (W/NW)', + }, + }, + }, + { + id: 'R12S Grand Entrance Intercards/Cardinals', + // B4A1 is only cast when cardinals are safe + // B4A2 is only cast when intercardinals are safe + // These casts more than once, so just capture first event + type: 'StartsUsing', + netRegex: { id: ['B4A1', 'B4A2'], capture: false }, + suppressSeconds: 5, + infoText: (data, matches, output) => { + const count = data.myMitoticPhase; + if (count === undefined) + return; + if (matches.id === 'B4A1') { + switch (count) { + case '436': + return output.frontCardinals!(); + case '437': + return output.rightCardinals!(); + case '438': + return output.rearCardinals!(); + case '439': + return output.leftCardinals!(); + } + } + switch (count) { + case '436': + return output.frontIntercards!(); + case '437': + return output.rightIntercards!(); + case '438': + return output.rearIntercards!(); + case '439': + return output.leftIntercards!(); + } + }, + outputStrings: { + frontIntercards: Outputs.southwest, + rearIntercards: Outputs.northeast, + leftIntercards: Outputs.southeast, + rightIntercards: Outputs.northwest, + frontCardinals: Outputs.south, + rearCardinals: Outputs.north, + leftCardinals: Outputs.east, + rightCardinals: Outputs.west, + }, + }, + { + id: 'R12S Rotting Flesh', + type: 'GainsEffect', + netRegex: { effectId: '129B', capture: true }, + condition: Conditions.targetIsYou(), + durationSeconds: 10, + infoText: (_data, _matches, output) => output.text!(), + outputStrings: { + text: { + en: 'Rotting Flesh on YOU', + }, + }, + }, + { + id: 'R12S Rotting Flesh Collect', + type: 'GainsEffect', + netRegex: { effectId: '129B', capture: true }, + condition: Conditions.targetIsYou(), + run: (data) => data.hasRot === true, + }, + { + id: 'R12S Ravenous Reach 2', + // These two syncs indicate the animation of where the head will go to cleave + // B49A => West Safe + // B49B => East Safe + type: 'Ability', + netRegex: { id: ['B49A', 'B49B'], source: 'Lindwurm', capture: true }, + condition: (data) => data.phase === 'curtainCall', + alertText: (data, matches, output) => { + if (matches.id === 'B49A') { + return data.hasRot ? output.getHitEast!() : output.safeWest!(); + } + return data.hasRot ? output.getHitWest!() : output.safeEast!(); + }, + outputStrings: { + getHitWest: { + en: 'Spread in West Breadth', + }, + getHitEast: { + en: 'Spread in East Breadth', + }, + safeEast: { + en: 'Spread East', + }, + safeWest: { + en: 'Spread West', + }, + }, + }, + { + id: 'R12S Split Scourge and Venomous Scourge', + // B4AB Split Scourge and B4A8 Venomous Scourge are instant casts + // This actor control happens along with boss becoming targetable + type: 'ActorControl', + netRegex: { command: '8000000D', data0: '1E01', capture: false }, + durationSeconds: 9, + suppressSeconds: 1, + infoText: (data, _matches, output) => { + if (data.role === 'tank') + return output.tank!(); + return output.party!(); + }, + outputStrings: { + tank: { + en: 'Bait Line AoE from heads', + }, + party: { + en: 'Spread, Away from heads', + }, + }, + }, + { + id: 'R12S Grotesquerie: Curtain Call Spreads', + type: 'StartsUsing', + netRegex: { id: 'BEC0', source: 'Lindwurm', capture: false }, + infoText: (_data, _matches, output) => output.text!(), + outputStrings: { + text: 'Bait 5x Puddles', + }, + }, + { + id: 'R12S Curtain Call: Unbreakable Flesh α/β Chains', + type: 'GainsEffect', + netRegex: { effectId: ['1291', '1293'], capture: true }, + condition: (data, matches) => { + if (matches.target === data.me && data.phase === 'curtainCall') + return true; + return false; + }, + infoText: (_data, matches, output) => { + const flesh = matches.effectId === '1291' ? 'alpha' : 'beta'; + if (flesh === 'alpha') + return output.alphaChains!(); + return output.betaChains!(); + }, + outputStrings: { + alphaChains: Outputs.breakChains, + betaChains: Outputs.breakChains, + }, + }, + { + id: 'R12S Slaughtershed', + type: 'StartsUsing', + netRegex: { id: ['B4C6', 'B4C3'], source: 'Lindwurm', capture: false }, + response: Responses.bigAoe('alert'), + }, + { + id: 'R12S Slaughtershed Stack', + // TODO: Get Safe spot + type: 'HeadMarker', + netRegex: { id: headMarkerData['slaughterStack'], capture: true }, + condition: (data, matches) => { + const isDPS = data.party.isDPS(matches.target); + if (isDPS && data.role === 'dps') + return true; + if (!isDPS && data.role !== 'dps') + return true; + return false; + }, + durationSeconds: 5.1, + response: Responses.stackMarkerOn(), + }, + { + id: 'R12S Slaughtershed Spread', + // TODO: Get Safe spot + type: 'HeadMarker', + netRegex: { id: headMarkerData['slaughterSpread'], capture: true }, + condition: Conditions.targetIsYou(), + durationSeconds: 5.1, + suppressSeconds: 1, + response: Responses.spread(), + }, + { + id: 'R12S Serpintine Scourge Right Hand First', + // Left Hand first, then Right Hand + type: 'Ability', + netRegex: { id: 'B4CB', source: 'Lindwurm', capture: false }, + condition: (data) => data.phase === 'slaughtershed', + durationSeconds: 12, + infoText: (_data, _matches, output) => output.rightThenLeft!(), + outputStrings: { + rightThenLeft: Outputs.rightThenLeft, + }, + }, + { + id: 'R12S Serpintine Scourge Left Hand First', + // Right Hand first, then Left Hand + type: 'Ability', + netRegex: { id: 'B4CD', source: 'Lindwurm', capture: false }, + condition: (data) => data.phase === 'slaughtershed', + durationSeconds: 12, + infoText: (_data, _matches, output) => output.leftThenRight!(), + outputStrings: { + leftThenRight: Outputs.leftThenRight, + }, + }, + { + id: 'R12S Raptor Knuckles Right Hand First', + // Right Hand first, then Left Hand + type: 'Ability', + netRegex: { id: 'B4CC', source: 'Lindwurm', capture: false }, + condition: (data) => data.phase === 'slaughtershed', + durationSeconds: 12, + infoText: (_data, _matches, output) => output.text!(), + outputStrings: { + text: { + en: 'Northwest: Knockback to Northeast', + }, + }, + }, + { + id: 'R12S Raptor Knuckles Left Hand First', + // Left Hand first, then Right Hand + type: 'Ability', + netRegex: { id: 'B4CE', source: 'Lindwurm', capture: false }, + condition: (data) => data.phase === 'slaughtershed', + durationSeconds: 12, + infoText: (_data, _matches, output) => output.text!(), + outputStrings: { + text: { + en: 'Northeast: Knockback to Northwest', + }, + }, + }, + ], + timelineReplace: [ + { + 'locale': 'cn', + 'replaceSync': { + 'Lindwurm': '林德布鲁姆', + }, + 'replaceText': { + '\\(huge\\)': '(大)', + 'Bloodshed': '流血', + 'Bring Down the House': '震场', + '(? Date: Mon, 19 Jan 2026 08:24:37 -0500 Subject: [PATCH 002/139] lint --- ui/raidboss/data/07-dt/raid/r12s.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index beec50f1111..aae6d5e609f 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -216,8 +216,8 @@ const triggerSet: TriggerSet = { const cleave = data.grotesquerieCleave; if (cleave === undefined) return data.phase === 'doorboss' - ? output.baitThenSpread!() - : output.spreadCurtain!(); + ? output.baitThenSpread!() + : output.spreadCurtain!(); return data.phase === 'doorboss' ? output.baitThenSpreadCleave!({ cleave: output[cleave]!() }) : output.spreadCurtain!(); @@ -352,9 +352,7 @@ const triggerSet: TriggerSet = { return; const flesh = data.myFleshBonds; if (flesh === undefined) - return output.order!({ - num: myNum - }); + return output.order!({ num: myNum }); if (flesh === 'alpha') { switch (myNum) { case 1: From 4d181c4c2486aacbb5565ba6199b74d7f03fad16 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 19 Jan 2026 08:27:20 -0500 Subject: [PATCH 003/139] unused capture, remove timelinereplace copy from r12n --- ui/raidboss/data/07-dt/raid/r12s.ts | 35 ++--------------------------- 1 file changed, 2 insertions(+), 33 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index aae6d5e609f..961273b7470 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -448,7 +448,7 @@ const triggerSet: TriggerSet = { id: 'R12S Phagocyte Spotlight Blob Tower Location (Early)', // 23.8s until B4B7 Rolling Mass Blob Tower Hit type: 'Ability', - netRegex: { id: 'B4B6', capture: true }, + netRegex: { id: 'B4B6', capture: false }, condition: (data) => { if (data.myFleshBonds === 'alpha') { const myNum = data.inLine[data.me]; @@ -1064,38 +1064,7 @@ const triggerSet: TriggerSet = { }, }, ], - timelineReplace: [ - { - 'locale': 'cn', - 'replaceSync': { - 'Lindwurm': '林德布鲁姆', - }, - 'replaceText': { - '\\(huge\\)': '(大)', - 'Bloodshed': '流血', - 'Bring Down the House': '震场', - '(? Date: Mon, 19 Jan 2026 08:29:28 -0500 Subject: [PATCH 004/139] fix typo/paste error with labels --- ui/raidboss/data/07-dt/raid/r12s.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.txt b/ui/raidboss/data/07-dt/raid/r12s.txt index b3f0f22ee0f..e3186605a4e 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.txt +++ b/ui/raidboss/data/07-dt/raid/r12s.txt @@ -136,10 +136,10 @@ hideall "--sync--" 456.1 "Serpentine Scourge" Ability { id: "B9BC", source: "Lindwurm" } 459.8 "Serpentine Scourge Right" Ability { id: "B4D2", source: "Lindwurm" } 460.8 "Serpentine Scourge" Ability { id: "B9BC", source: "Lindwurm" } -464.4 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } forcejump "r12s-slaughtershed2" +464.4 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } forcejump "r12s-p1-slaughtershed2" # Serpentine Scourge Right First -540.6 label jump "r12s-p1-scourge-right-1" +540.6 label "r12s-p1-scourge-right-1" 547.6 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } 547.6 "Fourth-wall Fusion" Ability { id: "B4D5", source: "Lindwurm" } 548.0 "Burst" Ability { id: "B49F", source: "Lindwurm" } @@ -147,7 +147,7 @@ hideall "--sync--" 554.1 "Serpentine Scourge" Ability { id: "B9BC", source: "Lindwurm" } 557.7 "Serpentine Scourge Left" Ability { id: "B4D1", source: "Lindwurm" } 558.7 "Serpentine Scourge" Ability { id: "B9BC", source: "Lindwurm" } -562.4 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } forcejump "r12s-slaughtershed2" +562.4 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } forcejump "r12s-p1-slaughtershed2" # Raptor Knuckles Left First 641.1 label "r12s-p1-raptor-left-1" @@ -158,7 +158,7 @@ hideall "--sync--" 654.4 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } 658.3 "Raptor Knuckles Northwest" Ability { id: "B4CF", source: "Lindwurm" } 659.1 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } -662.9 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } forcejump "r12s-slaughtershed2" +662.9 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } forcejump "r12s-p1-slaughtershed2" # Raptor Knuckles Right First 742.7 label "r12s-p1-raptor-right-1" @@ -169,10 +169,10 @@ hideall "--sync--" 756.1 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } 759.9 "Raptor Knuckles Northeast" Ability { id: "B4D0", source: "Lindwurm" } 760.7 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } -764.5 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } forcejump "r12s-slaughtershed2" +764.5 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } forcejump "r12s-p1-slaughtershed2" # Slaughtershed 2 -864.4 label "r12s-slaughtershed2" +864.4 label "r12s-p1-slaughtershed2" 867.4 "Slaughtershed 2 (castbar)" Ability { id: ["B4C3", "B4C6"], source: "Lindwurm" } 869.8 "Slaughtershed 2" Ability { id: "ADC9", source: "Lindwurm" } From d0a69406bc9a60243f2309a5ce714ae9a77291a2 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 19 Jan 2026 08:35:12 -0500 Subject: [PATCH 005/139] add missing capture --- ui/raidboss/data/07-dt/raid/r12s.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 961273b7470..9394d7cf0ae 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -839,7 +839,7 @@ const triggerSet: TriggerSet = { // B4A2 is only cast when intercardinals are safe // These casts more than once, so just capture first event type: 'StartsUsing', - netRegex: { id: ['B4A1', 'B4A2'], capture: false }, + netRegex: { id: ['B4A1', 'B4A2'], capture: true }, suppressSeconds: 5, infoText: (data, matches, output) => { const count = data.myMitoticPhase; From e6bbdf107ad4bfc6f1962e6b334414deabd8dfa7 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 19 Jan 2026 08:35:40 -0500 Subject: [PATCH 006/139] remove erroneous 3 character --- ui/raidboss/data/07-dt/raid/r12s.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.txt b/ui/raidboss/data/07-dt/raid/r12s.txt index e3186605a4e..faa2d1a78b5 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.txt +++ b/ui/raidboss/data/07-dt/raid/r12s.txt @@ -245,7 +245,7 @@ hideall "--sync--" 1400.4 "--sync--" Ability { id: "B4CC", source: "Lindwurm" } jump "r12s-p1-raptor-right-3" 1407.4 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } 1407.4 "Fourth-wall Fusion" #Ability { id: "B4D5", source: "Lindwurm" } -1407.8 "Burst" 3Ability { id: "B49F", source: "Lindwurm" } +1407.8 "Burst" Ability { id: "B49F", source: "Lindwurm" } 1412.9 "Serpentine Scourge/Raptor Knuckles?" #Ability { id: ["B4D1", "B4D2", "B4CD", "B4CE"], source: "Lindwurm" } 1413.9 "Serpentine Scourge/--knockback--?" #Ability { id: ["B9BC", "B9C7"], source: "Lindwurm" } 1417.6 "Serpentine Scourge/Raptor Knuckles?" #Ability { id: ["B4D2", "B4D1", "B4CE", "B4CD"], source: "Lindwurm" } From 4226f7bd86b4bff0b6f2d36a0d5c812c6763e0b6 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 19 Jan 2026 08:39:26 -0500 Subject: [PATCH 007/139] paste error --- ui/raidboss/data/07-dt/raid/r12s.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.txt b/ui/raidboss/data/07-dt/raid/r12s.txt index faa2d1a78b5..9b6e5004459 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.txt +++ b/ui/raidboss/data/07-dt/raid/r12s.txt @@ -288,7 +288,7 @@ hideall "--sync--" 1636.8 "Refreshing Overkill (Enrage)?" #Ability { id: "B53A", source: "Lindwurm" } # Raptor Knuckles Left Third -1700.4 label "r12s-p1-raptor-right-3" +1700.4 label "r12s-p1-raptor-left-3" 1707.4 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } 1707.4 "Fourth-wall Fusion" Ability { id: "B4D5", source: "Lindwurm" } 1707.8 "Burst" Ability { id: "B49F", source: "Lindwurm" } From e62af8d9ec709631ddc6987c8588319b648d14ed Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 19 Jan 2026 16:08:59 -0500 Subject: [PATCH 008/139] fix ravenous reach 2 calls --- ui/raidboss/data/07-dt/raid/r12s.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 9394d7cf0ae..2f6cca8cdc1 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -672,7 +672,7 @@ const triggerSet: TriggerSet = { const duration = parseFloat(matches.duration); if (duration > 35) return 27; - return 32; + return 34; }, infoText: (data, matches, output) => { const duration = parseFloat(matches.duration); @@ -909,9 +909,9 @@ const triggerSet: TriggerSet = { condition: (data) => data.phase === 'curtainCall', alertText: (data, matches, output) => { if (matches.id === 'B49A') { - return data.hasRot ? output.getHitEast!() : output.safeWest!(); + return data.hasRot ? output.safeWest!() : output.getHitEast!(); } - return data.hasRot ? output.getHitWest!() : output.safeEast!(); + return data.hasRot ? output.safeEast!() : output.getHitWest!(); }, outputStrings: { getHitWest: { From 2ab3724d8fda030c607dc3ca1fe1331762e0c605 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 19 Jan 2026 17:15:16 -0500 Subject: [PATCH 009/139] update curtain call break chains TODO: Find safe spots. --- ui/raidboss/data/07-dt/raid/r12s.ts | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 2f6cca8cdc1..a8781ec4558 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -961,6 +961,7 @@ const triggerSet: TriggerSet = { }, { id: 'R12S Curtain Call: Unbreakable Flesh α/β Chains', + // TODO: Find safe spots type: 'GainsEffect', netRegex: { effectId: ['1291', '1293'], capture: true }, condition: (data, matches) => { @@ -970,13 +971,23 @@ const triggerSet: TriggerSet = { }, infoText: (_data, matches, output) => { const flesh = matches.effectId === '1291' ? 'alpha' : 'beta'; + const safeSpots = output.safeSpots!(); + const chains = output.breakChains!(); if (flesh === 'alpha') - return output.alphaChains!(); - return output.betaChains!(); + return output.alphaChains!( chains: chains, safe: safeSpots ); + return output.betaChains!( chains: chains, safeSpots ); }, outputStrings: { - alphaChains: Outputs.breakChains, - betaChains: Outputs.breakChains, + breakChains: Outputs.breakChains, + safeSpots: { + en: 'Avoid Blobs', + }, + alphaChains: { + en: '${chains} => ${safeSpots}', + }, + betaChains: { + en: '${chains} => ${safeSpots}', + }, }, }, { From 467ee5b0f913deb39c642058706bf5417ac0c9b1 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 19 Jan 2026 17:17:53 -0500 Subject: [PATCH 010/139] fix previous commit --- ui/raidboss/data/07-dt/raid/r12s.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index a8781ec4558..c7455fea270 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -974,8 +974,8 @@ const triggerSet: TriggerSet = { const safeSpots = output.safeSpots!(); const chains = output.breakChains!(); if (flesh === 'alpha') - return output.alphaChains!( chains: chains, safe: safeSpots ); - return output.betaChains!( chains: chains, safeSpots ); + return output.alphaChains!({ chains: chains, safe: safeSpots }); + return output.betaChains!({ chains: chains, safe: safeSpots }); }, outputStrings: { breakChains: Outputs.breakChains, From 3a166e7ea1ba75ded13edfd16a64d7e5d07b4e72 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 19 Jan 2026 18:00:59 -0500 Subject: [PATCH 011/139] blob towers called via pattern, add knockback for bind --- ui/raidboss/data/07-dt/raid/r12s.ts | 81 +++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 23 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index c7455fea270..81947c8f612 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -438,43 +438,56 @@ const triggerSet: TriggerSet = { // Blob 4: (86, 90) NW Outer type: 'Ability', netRegex: { id: 'B4B6', capture: true }, + suppressSeconds: 10, run: (data, matches) => { const x = parseFloat(matches.x); const y = parseFloat(matches.y); + const dir = Directions.xyToIntercardDirOutput(x, y, center.x, center.y); data.blobTowerDirs.push(Directions.xyToIntercardDirOutput(x, y, center.x, center.y)); + + if (dir === 'dirSE') { + data.blobTowerDirs.push('dirNW'); + data.blobTowerDirs.push('dirSW'); + data.blobTowerDirs.push('dirNE'); + } + else if (dir === 'dirNE') { + data.blobTowerDirs.push('dirSW'); + data.blobTowerDirs.push('dirNW'); + data.blobTowerDirs.push('dirSE'); + } + else if (dir === 'dirNW') { + data.blobTowerDirs.push('dirSE'); + data.blobTowerDirs.push('dirNE'); + data.blobTowerDirs.push('dirSW'); + } + else if (dir === 'dirSW') { + data.blobTowerDirs.push('dirNE'); + data.blobTowerDirs.push('dirSE'); + data.blobTowerDirs.push('dirNW'); + } }, }, { id: 'R12S Phagocyte Spotlight Blob Tower Location (Early)', // 23.8s until B4B7 Rolling Mass Blob Tower Hit + // Only need to know first blob location type: 'Ability', netRegex: { id: 'B4B6', capture: false }, - condition: (data) => { - if (data.myFleshBonds === 'alpha') { - const myNum = data.inLine[data.me]; - if ( - (myNum === 1 && data.blobTowerDirs.length === 3) || - (myNum === 2 && data.blobTowerDirs.length === 4) || - (myNum === 3 && data.blobTowerDirs.length === 1) || - (myNum === 4 && data.blobTowerDirs.length === 2) - ) - return true; - } - return false; - }, + condition: (data) => data.myFleshBonds === 'alpha', + suppressSeconds: 10, delaySeconds: 0.1, durationSeconds: (data) => { const myNum = data.inLine[data.me]; // Timings based on next trigger switch (myNum) { case 1: - return 13; + return 17; case 2: - return 16; + return 22; case 3: - return 17; + return 18; case 4: - return 15; + return 18; } }, infoText: (data, _matches, output) => { @@ -517,6 +530,16 @@ const triggerSet: TriggerSet = { }, }, }, + { + id: 'R12S Cursed Coil Bind Knocbkack', + // Using Phagocyte Spotlight, 1st one happens 7s before bind + // Delayed additionally to reduce overlap with alpha tower location calls + type: 'Ability', + netRegex: { id: 'B4B6', capture: false }, + suppressSeconds: 10, + delaySeconds: 4, // 4s warning + response: Responses.knockback(), + }, { id: 'R12S Cell Chain Counter', type: 'Tether', @@ -971,11 +994,20 @@ const triggerSet: TriggerSet = { }, infoText: (_data, matches, output) => { const flesh = matches.effectId === '1291' ? 'alpha' : 'beta'; - const safeSpots = output.safeSpots!(); - const chains = output.breakChains!(); if (flesh === 'alpha') - return output.alphaChains!({ chains: chains, safe: safeSpots }); - return output.betaChains!({ chains: chains, safe: safeSpots }); + return output.alphaChains!({ + chains: output.breakChains!(), + safe: output.safeSpots!(), + }); + if (flesh === 'beta') + return output.betaChains!({ + chains: output.breakChains!(), + safe: output.breakChains!(), + }); + return output.unknownChains!({ + chains: output.breakChains!(), + safe: output.breakChains!(), + }); }, outputStrings: { breakChains: Outputs.breakChains, @@ -983,10 +1015,13 @@ const triggerSet: TriggerSet = { en: 'Avoid Blobs', }, alphaChains: { - en: '${chains} => ${safeSpots}', + en: '${chains} => ${safe}', }, betaChains: { - en: '${chains} => ${safeSpots}', + en: '${chains} => ${safe}', + }, + unknownChains: { + en: '${chains} => ${safe}', }, }, }, From de6493fc1bd27c7608c99421a0983df82680ce6d Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 19 Jan 2026 18:03:42 -0500 Subject: [PATCH 012/139] lint --- ui/raidboss/data/07-dt/raid/r12s.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 81947c8f612..eebcd89f8b5 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -449,18 +449,15 @@ const triggerSet: TriggerSet = { data.blobTowerDirs.push('dirNW'); data.blobTowerDirs.push('dirSW'); data.blobTowerDirs.push('dirNE'); - } - else if (dir === 'dirNE') { + } else if (dir === 'dirNE') { data.blobTowerDirs.push('dirSW'); data.blobTowerDirs.push('dirNW'); data.blobTowerDirs.push('dirSE'); - } - else if (dir === 'dirNW') { + } else if (dir === 'dirNW') { data.blobTowerDirs.push('dirSE'); data.blobTowerDirs.push('dirNE'); data.blobTowerDirs.push('dirSW'); - } - else if (dir === 'dirSW') { + } else if (dir === 'dirSW') { data.blobTowerDirs.push('dirNE'); data.blobTowerDirs.push('dirSE'); data.blobTowerDirs.push('dirNW'); From f8651f991e14fd32dfd17b91a45a40390ba25a33 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 19 Jan 2026 18:06:05 -0500 Subject: [PATCH 013/139] replace function with previous dir const --- ui/raidboss/data/07-dt/raid/r12s.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index eebcd89f8b5..fa962293254 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -443,7 +443,7 @@ const triggerSet: TriggerSet = { const x = parseFloat(matches.x); const y = parseFloat(matches.y); const dir = Directions.xyToIntercardDirOutput(x, y, center.x, center.y); - data.blobTowerDirs.push(Directions.xyToIntercardDirOutput(x, y, center.x, center.y)); + data.blobTowerDirs.push(dir); if (dir === 'dirSE') { data.blobTowerDirs.push('dirNW'); From 996bab0dc754ccfebe4a593c2f63c050b0e25263 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 19 Jan 2026 18:07:28 -0500 Subject: [PATCH 014/139] fix suppressSeconds order --- ui/raidboss/data/07-dt/raid/r12s.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index fa962293254..60ef978f95d 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -471,7 +471,6 @@ const triggerSet: TriggerSet = { type: 'Ability', netRegex: { id: 'B4B6', capture: false }, condition: (data) => data.myFleshBonds === 'alpha', - suppressSeconds: 10, delaySeconds: 0.1, durationSeconds: (data) => { const myNum = data.inLine[data.me]; @@ -487,6 +486,7 @@ const triggerSet: TriggerSet = { return 18; } }, + suppressSeconds: 10, infoText: (data, _matches, output) => { const myNum = data.inLine[data.me]; if (myNum === undefined) @@ -533,8 +533,8 @@ const triggerSet: TriggerSet = { // Delayed additionally to reduce overlap with alpha tower location calls type: 'Ability', netRegex: { id: 'B4B6', capture: false }, - suppressSeconds: 10, delaySeconds: 4, // 4s warning + suppressSeconds: 10, response: Responses.knockback(), }, { From b81f1f649e3c9fc2b32126a649c92279f463ba8a Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 19 Jan 2026 18:27:04 -0500 Subject: [PATCH 015/139] add beta Tether => Tower outputs --- ui/raidboss/data/07-dt/raid/r12s.ts | 33 ++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 60ef978f95d..4417cd6f9db 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -558,8 +558,27 @@ const triggerSet: TriggerSet = { infoText: (data, _matches, output) => { const myNum = data.inLine[data.me]; const num = data.cellChainCount; - if (myNum !== num) + if (myNum !== num) { + if (myNum === 1 && num === 3) + return output.beta1Tower!({ + tether: output.tether!({ num: num }) + }); + if (myNum === 2 && num === 4) + return output.beta2Tower!({ + tether: output.tether!({ num: num }), + }); + if (myNum === 3 && num === 1) + return output.beta3Tower!({ + tether: output.tether!({num: num }), + }); + if (myNum === 4 && num === 2) + return output.beta4Tower!({ + tether: output.tether!({ num: num }), + }); + return output.tether!({ num: num }); + } + if (myNum === undefined) return output.tether!({ num: num }); }, @@ -573,6 +592,18 @@ const triggerSet: TriggerSet = { ko: '선 ${num}', tc: '線 ${num}', }, + beta1Tower: { + en: '${tether} => Chain Tower 3', + }, + beta2Tower: { + en: '${tether} => Chain Tower 4', + }, + beta3Tower: { + en: '${tether} => Chain Tower 1', + }, + beta4Tower: { + en: '${tether} => Chain Tower 2', + }, }, }, { From 55173924e6c5c2717a3fbe15b31166fe7a837d6b Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 19 Jan 2026 18:29:09 -0500 Subject: [PATCH 016/139] lint --- ui/raidboss/data/07-dt/raid/r12s.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 4417cd6f9db..8730fd692cd 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -561,7 +561,7 @@ const triggerSet: TriggerSet = { if (myNum !== num) { if (myNum === 1 && num === 3) return output.beta1Tower!({ - tether: output.tether!({ num: num }) + tether: output.tether!({ num: num }), }); if (myNum === 2 && num === 4) return output.beta2Tower!({ @@ -569,7 +569,7 @@ const triggerSet: TriggerSet = { }); if (myNum === 3 && num === 1) return output.beta3Tower!({ - tether: output.tether!({num: num }), + tether: output.tether!({ num: num }), }); if (myNum === 4 && num === 2) return output.beta4Tower!({ From b298c9552bb7d58bc782432516a83c1d4f8a877d Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 19 Jan 2026 19:06:02 -0500 Subject: [PATCH 017/139] fix delay for fist two blob towers --- ui/raidboss/data/07-dt/raid/r12s.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 8730fd692cd..df32bdf2c24 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -721,20 +721,21 @@ const triggerSet: TriggerSet = { }, delaySeconds: (_data, matches) => { const duration = parseFloat(matches.duration); - if (duration > 35) - return 27; - return 34; + // The following gives 5s warning to take tower + if (duration > 37) + return 31; // Alpha4 Time + return 26; // Alpha3 Time }, infoText: (data, matches, output) => { const duration = parseFloat(matches.duration); const dir = data.blobTowerDirs[duration > 40 ? 1 : 0]; if (duration > 40) { if (dir !== undefined) - return output.alpha4Dir!({ dir: output[dir]!() }); + return output.alpha4Dir!({ dir: output[dir]!(), dur: duration }); return output.alpha4!(); } if (dir !== undefined) - return output.alpha3Dir!({ dir: output[dir]!() }); + return output.alpha3Dir!({ dir: output[dir]!(), dur: duration }); return output.alpha3!(); }, outputStrings: { @@ -746,10 +747,10 @@ const triggerSet: TriggerSet = { en: 'Get Blob Tower 2', }, alpha3Dir: { - en: 'Blob Tower 1 (Inner ${dir})', + en: 'Blob Tower 1 (Inner ${dir}) ${dur}', }, alpha4Dir: { - en: 'Blob Tower 2 (Inner ${dir})', + en: 'Blob Tower 2 (Inner ${dir}) ${dur}', }, }, }, From e502f7030ff264eaeb0efb26607c86ee32b1672d Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 19 Jan 2026 19:10:45 -0500 Subject: [PATCH 018/139] add blob tower followup, reorder triggers --- ui/raidboss/data/07-dt/raid/r12s.ts | 149 ++++++++++++++++++---------- 1 file changed, 95 insertions(+), 54 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index df32bdf2c24..74bd2ae5451 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -21,6 +21,7 @@ export interface Data extends RaidbossData { inLine: { [name: string]: number }; blobTowerDirs: string[]; fleshBondsCount: number; + skinsplitterCount: number; cellChainCount: number; cellTowerCount: number; myMitoticPhase?: string; @@ -63,6 +64,7 @@ const triggerSet: TriggerSet = { // Phase 1 inLine: {}, blobTowerDirs: [], + skinsplitterCount: 0, fleshBondsCount: 0, cellChainCount: 0, cellTowerCount: 0, @@ -537,6 +539,21 @@ const triggerSet: TriggerSet = { suppressSeconds: 10, response: Responses.knockback(), }, + { + id: 'R12S Skinsplitter Counter', + // These occur every 5s + // Useful for blob tower tracking that happen 2s after + // 2: Tether 1 + // 3: Tether 2 + Blob Tower 1 + // 4: Tether 3 + Blob Tower 2 + // 5: Tether 4 + Blob Tower 3 + // 6: Blob Tower 4 + // 7: Last time to exit + type: 'Ability', + netRegex: { id: 'B4BC', capture: false }, + suppressSeconds: 1, + run: (data) => data.skinsplitterCount = data.skinsplitterCount + 1, + }, { id: 'R12S Cell Chain Counter', type: 'Tether', @@ -655,56 +672,6 @@ const triggerSet: TriggerSet = { suppressSeconds: 1, run: (data) => data.cellTowerCount = data.cellTowerCount + 1, }, - { - id: 'R12S Chain Tower Followup', - // Using B4B3 Roiling Mass to detect chain tower soak - // Beta player leaving early may get hit by alpha's chain break aoe - type: 'Ability', - netRegex: { id: 'B4B3', capture: true }, - condition: (data, matches) => { - if (data.myFleshBonds === 'beta' && data.me === matches.target) - return true; - return false; - }, - infoText: (data, _matches, output) => { - const mechanicNum = data.cellTowerCount; - const myNum = data.inLine[data.me]; - if (myNum === undefined) - return; - - type index = { - [key: number]: number; - }; - const myNumToOrder: index = { - 1: 3, - 2: 4, - 3: 1, - 4: 2, - }; - - const myOrder = myNumToOrder[myNum]; - if (myOrder === undefined) - return; - - if (myOrder === mechanicNum) { - if (mechanicNum < 4) - return output.goIntoMiddle!(); - return output.getOut!(); - } - }, - outputStrings: { - getOut: { - en: 'Get Out', - de: 'Raus da', - fr: 'Sortez', - ja: '外へ', - cn: '远离', - ko: '밖으로', - tc: '遠離', - }, - goIntoMiddle: Outputs.goIntoMiddle, - }, - }, { id: 'R12S Bonds of Flesh Flesh α First Two Towers', // These are not dependent on player timings and so can be hard coded by duration @@ -844,6 +811,80 @@ const triggerSet: TriggerSet = { }, }, }, + { + id: 'R12S Chain Tower Followup', + // Using B4B3 Roiling Mass to detect chain tower soak + // Beta player leaving early may get hit by alpha's chain break aoe + type: 'Ability', + netRegex: { id: 'B4B3', capture: true }, + condition: (data, matches) => { + if (data.myFleshBonds === 'beta' && data.me === matches.target) + return true; + return false; + }, + infoText: (data, _matches, output) => { + const mechanicNum = data.cellTowerCount; + const myNum = data.inLine[data.me]; + if (myNum === undefined) + return; + + type index = { + [key: number]: number; + }; + const myNumToOrder: index = { + 1: 3, + 2: 4, + 3: 1, + 4: 2, + }; + + const myOrder = myNumToOrder[myNum]; + if (myOrder === undefined) + return; + + if (myOrder === mechanicNum) { + if (mechanicNum < 4) + return output.goIntoMiddle!(); + return output.getOut!(); + } + }, + outputStrings: { + getOut: { + en: 'Get Out', + de: 'Raus da', + fr: 'Sortez', + ja: '外へ', + cn: '远离', + ko: '밖으로', + tc: '遠離', + }, + goIntoMiddle: Outputs.goIntoMiddle, + }, + }, + { + id: 'R12S Blob Tower Followup', + // Using B4B7 Roiling Mass to detect chain tower soak + // Alpha 3 and Alpha 4 get the inner towers before their chains + type: 'Ability', + netRegex: { id: 'B4B7', capture: true }, + condition: (data, matches) => { + if (data.myFleshBonds === 'alpha' && data.me === matches.target) + return true; + return false; + }, + infoText: (data, _matches, output) => { + const mechanicNum = data.skinsplitterCount; + const myNum = data.inLine[data.me]; + if (myNum === undefined) + return; + + if (myNum === mechanicNum) + return output.goIntoMiddle!(); + }, + outputStrings: { + goIntoMiddle: Outputs.goIntoMiddle, + }, + }, { id: 'R12S Splattershed', type: 'StartsUsing', @@ -967,10 +1008,10 @@ const triggerSet: TriggerSet = { }, outputStrings: { getHitWest: { - en: 'Spread in West Breadth', + en: 'Spread in West Cleave', }, getHitEast: { - en: 'Spread in East Breadth', + en: 'Spread in East Cleave', }, safeEast: { en: 'Spread East', @@ -1116,7 +1157,7 @@ const triggerSet: TriggerSet = { type: 'Ability', netRegex: { id: 'B4CC', source: 'Lindwurm', capture: false }, condition: (data) => data.phase === 'slaughtershed', - durationSeconds: 12, + durationSeconds: 15, infoText: (_data, _matches, output) => output.text!(), outputStrings: { text: { @@ -1130,7 +1171,7 @@ const triggerSet: TriggerSet = { type: 'Ability', netRegex: { id: 'B4CE', source: 'Lindwurm', capture: false }, condition: (data) => data.phase === 'slaughtershed', - durationSeconds: 12, + durationSeconds: 15, infoText: (_data, _matches, output) => output.text!(), outputStrings: { text: { From 310a608cfcf4f6027173b51f57b504264e382e2e Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 19 Jan 2026 20:25:14 -0500 Subject: [PATCH 019/139] rework chain tower to use skinsplitter count --- ui/raidboss/data/07-dt/raid/r12s.ts | 109 +++++++++++++++++++++++++--- 1 file changed, 98 insertions(+), 11 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 74bd2ae5451..ce6a537fb2e 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -23,7 +23,6 @@ export interface Data extends RaidbossData { fleshBondsCount: number; skinsplitterCount: number; cellChainCount: number; - cellTowerCount: number; myMitoticPhase?: string; hasRot: boolean; // Phase 2 @@ -67,7 +66,6 @@ const triggerSet: TriggerSet = { skinsplitterCount: 0, fleshBondsCount: 0, cellChainCount: 0, - cellTowerCount: 0, hasRot: false, // Phase 2 }), @@ -663,15 +661,6 @@ const triggerSet: TriggerSet = { }, }, }, - { - id: 'R12S Chain Tower Counter', - // Using B4B3 Roiling Mass to detect chain tower soak - // Also using B4B2 Unmitigated Explosion if missed tower - type: 'Ability', - netRegex: { id: ['B4B3', 'B4B2'], capture: false }, - suppressSeconds: 1, - run: (data) => data.cellTowerCount = data.cellTowerCount + 1, - }, { id: 'R12S Bonds of Flesh Flesh α First Two Towers', // These are not dependent on player timings and so can be hard coded by duration @@ -860,6 +849,104 @@ const triggerSet: TriggerSet = { }, goIntoMiddle: Outputs.goIntoMiddle, }, + }, { + id: 'R12S Chain Tower Followup', + // Using B4B3 Roiling Mass to detect chain tower soak + // Beta player leaving early may get hit by alpha's chain break aoe + type: 'Ability', + netRegex: { id: 'B4B3', capture: true }, + condition: (data, matches) => { + if (data.myFleshBonds === 'beta' && data.me === matches.target) + return true; + return false; + }, + infoText: (data, _matches, output) => { + // Possibly the count could be off if break late (giving damage and damage down) + // Ideal towers are soaked: + // Beta 1 at 5th Skinsplitter + // Beta 2 at 6th Skinsplitter + // Beta 3 at 3rd Skinsplitter + // Beta 4 at 4rth Skinsplitter + const mechanicNum = data.skinsplitterCount; + const myNum = data.inLine[data.me]; + if (myNum === undefined) { + // This can be corrected by the player later + if (mechanicNum < 5) + return output.goIntoMiddle!(); + return output.getOut!(); + } + + if (mechanicNum < 5) { + if (myNum === 1) + return output.beta1Middle!(); + if (myNum === 2) + return output.beta2Middle!(); + if (myNum === 3) + return output.beta3Middle!(); + if (myNum === 4) + return output.beta4Middle!(); + } + if (myNum === 1) + return output.beta1Out!(); + if (myNum === 2) + return output.beta2Out!(); + if (myNum === 3) + return output.beta3Out!(); + if (myNum === 4) + return output.beta4Out!(); + }, + outputStrings: { + getOut: { + en: 'Get Out', + de: 'Raus da', + fr: 'Sortez', + ja: '外へ', + cn: '远离', + ko: '밖으로', + tc: '遠離', + }, + goIntoMiddle: Outputs.goIntoMiddle, + beta1Middle: Outputs.goIntoMiddle, + beta2Middle: Outputs.goIntoMiddle, // Should not happen under ideal situation + beta3Middle: Outputs.goIntoMiddle, + beta4Middle: Outputs.goIntoMiddle, + beta1Out: { // Should not happen under ideal situation + en: 'Get Out', + de: 'Raus da', + fr: 'Sortez', + ja: '外へ', + cn: '远离', + ko: '밖으로', + tc: '遠離', + }, + beta2Out: { + en: 'Get Out', + de: 'Raus da', + fr: 'Sortez', + ja: '外へ', + cn: '远离', + ko: '밖으로', + tc: '遠離', + }, + beta3Out: { // Should not happen under ideal situation + en: 'Get Out', + de: 'Raus da', + fr: 'Sortez', + ja: '外へ', + cn: '远离', + ko: '밖으로', + tc: '遠離', + }, + beta4Out: { // Should not happen under ideal situation + en: 'Get Out', + de: 'Raus da', + fr: 'Sortez', + ja: '外へ', + cn: '远离', + ko: '밖으로', + tc: '遠離', + }, + }, }, { id: 'R12S Blob Tower Followup', From 52e9f7f859d3fa56f632ae0054f6a24ba3553050 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 19 Jan 2026 20:26:21 -0500 Subject: [PATCH 020/139] remove a duration from output --- ui/raidboss/data/07-dt/raid/r12s.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index ce6a537fb2e..4a309114f49 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -687,11 +687,11 @@ const triggerSet: TriggerSet = { const dir = data.blobTowerDirs[duration > 40 ? 1 : 0]; if (duration > 40) { if (dir !== undefined) - return output.alpha4Dir!({ dir: output[dir]!(), dur: duration }); + return output.alpha4Dir!({ dir: output[dir]!() }); return output.alpha4!(); } if (dir !== undefined) - return output.alpha3Dir!({ dir: output[dir]!(), dur: duration }); + return output.alpha3Dir!({ dir: output[dir]!() }); return output.alpha3!(); }, outputStrings: { @@ -703,10 +703,10 @@ const triggerSet: TriggerSet = { en: 'Get Blob Tower 2', }, alpha3Dir: { - en: 'Blob Tower 1 (Inner ${dir}) ${dur}', + en: 'Blob Tower 1 (Inner ${dir})', }, alpha4Dir: { - en: 'Blob Tower 2 (Inner ${dir}) ${dur}', + en: 'Blob Tower 2 (Inner ${dir})', }, }, }, From 29da224ebab32b97f532ea0c4f8b8d0b08e8060a Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 19 Jan 2026 20:29:43 -0500 Subject: [PATCH 021/139] fix paste error --- ui/raidboss/data/07-dt/raid/r12s.ts | 49 ----------------------------- 1 file changed, 49 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 4a309114f49..ed9c8f09d53 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -801,55 +801,6 @@ const triggerSet: TriggerSet = { }, }, { - id: 'R12S Chain Tower Followup', - // Using B4B3 Roiling Mass to detect chain tower soak - // Beta player leaving early may get hit by alpha's chain break aoe - type: 'Ability', - netRegex: { id: 'B4B3', capture: true }, - condition: (data, matches) => { - if (data.myFleshBonds === 'beta' && data.me === matches.target) - return true; - return false; - }, - infoText: (data, _matches, output) => { - const mechanicNum = data.cellTowerCount; - const myNum = data.inLine[data.me]; - if (myNum === undefined) - return; - - type index = { - [key: number]: number; - }; - const myNumToOrder: index = { - 1: 3, - 2: 4, - 3: 1, - 4: 2, - }; - - const myOrder = myNumToOrder[myNum]; - if (myOrder === undefined) - return; - - if (myOrder === mechanicNum) { - if (mechanicNum < 4) - return output.goIntoMiddle!(); - return output.getOut!(); - } - }, - outputStrings: { - getOut: { - en: 'Get Out', - de: 'Raus da', - fr: 'Sortez', - ja: '外へ', - cn: '远离', - ko: '밖으로', - tc: '遠離', - }, - goIntoMiddle: Outputs.goIntoMiddle, - }, - }, { id: 'R12S Chain Tower Followup', // Using B4B3 Roiling Mass to detect chain tower soak // Beta player leaving early may get hit by alpha's chain break aoe From 01b9d528850c6d52f1830ecc37e8a7fe51f012f8 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 19 Jan 2026 20:32:03 -0500 Subject: [PATCH 022/139] fix hasRot collect --- ui/raidboss/data/07-dt/raid/r12s.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index ed9c8f09d53..d391bcbab8f 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1028,7 +1028,7 @@ const triggerSet: TriggerSet = { type: 'GainsEffect', netRegex: { effectId: '129B', capture: true }, condition: Conditions.targetIsYou(), - run: (data) => data.hasRot === true, + run: (data) => data.hasRot = true, }, { id: 'R12S Ravenous Reach 2', @@ -1040,9 +1040,9 @@ const triggerSet: TriggerSet = { condition: (data) => data.phase === 'curtainCall', alertText: (data, matches, output) => { if (matches.id === 'B49A') { - return data.hasRot ? output.safeWest!() : output.getHitEast!(); + return data.hasRot ? output.getHitEast!(): output.safeWest!(); } - return data.hasRot ? output.safeEast!() : output.getHitWest!(); + return data.hasRot ? output.getHitWest!(): output.safeEast!(); }, outputStrings: { getHitWest: { From 6f4802c4b4d0f109904fc6bb83121224ba9c9e63 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 19 Jan 2026 20:41:29 -0500 Subject: [PATCH 023/139] missing space --- ui/raidboss/data/07-dt/raid/r12s.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index d391bcbab8f..cbf143dfae6 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1040,9 +1040,9 @@ const triggerSet: TriggerSet = { condition: (data) => data.phase === 'curtainCall', alertText: (data, matches, output) => { if (matches.id === 'B49A') { - return data.hasRot ? output.getHitEast!(): output.safeWest!(); + return data.hasRot ? output.getHitEast!() : output.safeWest!(); } - return data.hasRot ? output.getHitWest!(): output.safeEast!(); + return data.hasRot ? output.getHitWest!() : output.safeEast!(); }, outputStrings: { getHitWest: { From 17afe3c640065053007d604d9d83ea7253eea56d Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 19 Jan 2026 20:43:53 -0500 Subject: [PATCH 024/139] reduce 1s delay for knockback --- ui/raidboss/data/07-dt/raid/r12s.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index cbf143dfae6..de61e2d7131 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -533,7 +533,7 @@ const triggerSet: TriggerSet = { // Delayed additionally to reduce overlap with alpha tower location calls type: 'Ability', netRegex: { id: 'B4B6', capture: false }, - delaySeconds: 4, // 4s warning + delaySeconds: 3, // 5s warning suppressSeconds: 10, response: Responses.knockback(), }, From a355b827545ce50affecdfeb7af588e6dfa5a4d6 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 19 Jan 2026 20:48:02 -0500 Subject: [PATCH 025/139] increase break chains to alert and adjust output slightly --- ui/raidboss/data/07-dt/raid/r12s.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index de61e2d7131..703ab6cf7ef 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -682,7 +682,7 @@ const triggerSet: TriggerSet = { return 31; // Alpha4 Time return 26; // Alpha3 Time }, - infoText: (data, matches, output) => { + alertText: (data, matches, output) => { const duration = parseFloat(matches.duration); const dir = data.blobTowerDirs[duration > 40 ? 1 : 0]; if (duration > 40) { @@ -703,10 +703,10 @@ const triggerSet: TriggerSet = { en: 'Get Blob Tower 2', }, alpha3Dir: { - en: 'Blob Tower 1 (Inner ${dir})', + en: 'Get Blob Tower 1 (Inner ${dir})', }, alpha4Dir: { - en: 'Blob Tower 2 (Inner ${dir})', + en: 'Get Blob Tower 2 (Inner ${dir})', }, }, }, @@ -719,7 +719,7 @@ const triggerSet: TriggerSet = { return true; return false; }, - infoText: (data, matches, output) => { + alertText: (data, matches, output) => { const myNum = data.inLine[data.me]; const flesh = matches.effectId === '1291' ? 'alpha' : 'beta'; if (flesh === 'alpha') { @@ -796,7 +796,7 @@ const triggerSet: TriggerSet = { en: '${chains} 3 => Wait for last pair', }, beta4: { - en: '${chains} 4 + Get Out', + en: '${chains} 4 => Get Out', }, }, }, From 260cebde0ce1bb4bd2c9e05c27f606ca8d000e12 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Tue, 20 Jan 2026 20:18:14 -0500 Subject: [PATCH 026/139] add refreshing overkill, some initial p2 triggers --- ui/raidboss/data/07-dt/raid/r12s.ts | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 703ab6cf7ef..892679eb113 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1217,6 +1217,34 @@ const triggerSet: TriggerSet = { }, }, }, + { + id: 'R12S Refreshing Overkill', + // 10s castTime that could end with enrage or raidwide + type: 'StartsUsing', + netRegex: { id: 'B538', source: 'Lindwurm', capture: true }, + delaySeconds: (_data, matches) => parseFloat(matches.castTime) - 4, + durationSeconds: 4.7, + response: Responses.bigAoe('alert'), + }, + // Phase 2 + { + id: 'R12S Arcadia Aflame', + type: 'StartsUsing', + netRegex: { id: 'B528', source: 'Lindwurm', capture: false }, + response: Responses.bigAoe('alert'), + }, + { + id: 'R12S Snaking Kick', + type: 'StartsUsing', + netRegex: { id: 'B527', source: 'Lindwurm', capture: true }, + response: Responses.getBehind(), + }, + { + id: 'R12S Double Sobat', + type: 'StartsUsing', + netRegex: { id: 'B520', source: 'Lindwurm', capture: true }, + response: Responses.tankCleave(), + }, ], timelineReplace: [], }; From f6ddae7d29cbbe0d4d51ae24550091f47f9d5565 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Tue, 20 Jan 2026 20:22:29 -0500 Subject: [PATCH 027/139] fix enrage startsUsing, clean p2 output using addedcombatant sync, add transition --- ui/raidboss/data/07-dt/raid/r12s.txt | 43 +++++++++++++++++----------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.txt b/ui/raidboss/data/07-dt/raid/r12s.txt index 9b6e5004459..5fb6dbf9048 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.txt +++ b/ui/raidboss/data/07-dt/raid/r12s.txt @@ -11,7 +11,7 @@ hideall "--sync--" # -it "Lindwurm" 0.0 "--sync--" InCombat { inGameCombat: "1" } window 0,1 -#1.0 "--sync--" AddedCombatant { npcNameId: "", name: "Lindwurm", job: "00", level: "5A", ownerId: "0{4}", worldId: "00" } window 10,3 jump 3000.5 # Sync to P2 immediately through AddCombatant. +1.0 "--sync--" AddedCombatant { npcNameId: "14378", name: "Lindwurm", job: "00", level: "1", ownerId: "0{4}", worldId: "00" } window 10,3 jump r12s-p2-start # Sync to P2 immediately through AddCombatant. 15.6 "The Fixer" Ability { id: "B4D7", source: "Lindwurm" } window 20,10 25.8 "--sync--" Ability { id: "B7C4", source: "Lindwurm" } 40.9 "Mortal Slayer 1" Ability { id: ["B496", "B498"], source: "Lindwurm" } @@ -264,7 +264,7 @@ hideall "--sync--" 1516.9 "Serpentine Scourge" Ability { id: "B9BC", source: "Lindwurm" } 1526.5 "--sync--" StartsUsing { id: "B2C7", source: "Lindwurm" } jump "r12s-p1-enrage-alt" 1526.5 "--untargetable?--" -1527.7 "--sync--" StartsUsing { id: "B538", source: "Lindwurm" } jump "r12s-p1-enrage" +1526.7 "--sync--" StartsUsing { id: "B538", source: "Lindwurm" } jump "r12s-p1-enrage" 1531.5 "The Fixer (Enrage)?" #Ability { id: "B2C7", source: "Lindwurm" } 1536.7 "Refreshing Overkill (Enrage)?" #Ability { id: "B538", source: "Lindwurm" } 1536.8 "--untargetable?--" @@ -281,7 +281,7 @@ hideall "--sync--" 1618.6 "Serpentine Scourge" Ability { id: "B9BC", source: "Lindwurm" } 1626.5 "--sync--" StartsUsing { id: "B2C7", source: "Lindwurm" } jump "r12s-p1-enrage-alt" 1626.5 "--untargetable?--" -1627.7 "--sync--" StartsUsing { id: "B538", source: "Lindwurm" } jump "r12s-p1-enrage" +1626.7 "--sync--" StartsUsing { id: "B538", source: "Lindwurm" } jump "r12s-p1-enrage" 1631.5 "The Fixer (Enrage)?" #Ability { id: "B2C7", source: "Lindwurm" } 1636.7 "Refreshing Overkill (Enrage)?" #Ability { id: "B538", source: "Lindwurm" } 1636.8 "--untargetable?--" @@ -298,7 +298,7 @@ hideall "--sync--" 1718.3 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } 1726.5 "--sync--" StartsUsing { id: "B2C7", source: "Lindwurm" } jump "r12s-p1-enrage-alt" 1726.5 "--untargetable?--" -1727.7 "--sync--" StartsUsing { id: "B538", source: "Lindwurm" } jump "r12s-p1-enrage" +1726.7 "--sync--" StartsUsing { id: "B538", source: "Lindwurm" } jump "r12s-p1-enrage" 1731.5 "The Fixer (Enrage)?" #Ability { id: "B2C7", source: "Lindwurm" } 1736.7 "Refreshing Overkill (Enrage)?" #Ability { id: "B538", source: "Lindwurm" } 1736.8 "--untargetable?--" @@ -315,28 +315,39 @@ hideall "--sync--" 1818.3 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } 1826.5 "--sync--" StartsUsing { id: "B2C7", source: "Lindwurm" } jump "r12s-p1-enrage-alt" 1826.5 "--untargetable?--" -1827.7 "--sync--" StartsUsing { id: "B538", source: "Lindwurm" } jump "r12s-p1-enrage" +1826.7 "--sync--" StartsUsing { id: "B538", source: "Lindwurm" } jump "r12s-p1-enrage" 1831.5 "The Fixer (Enrage)?" #Ability { id: "B2C7", source: "Lindwurm" } 1836.7 "Refreshing Overkill (Enrage)?" #Ability { id: "B538", source: "Lindwurm" } 1836.8 "--untargetable?--" 1836.8 "Refreshing Overkill (Enrage)?" #Ability { id: "B53A", source: "Lindwurm" } -# Enrage sequence 1 -1927.7 label "r12s-p1-enrage" -1936.7 "Refreshing Overkill (Enrage)" Ability { id: "B538", source: "Lindwurm" } -1936.8 "--untargetable--" -1936.8 "Refreshing Overkill (Enrage)" Ability { id: "B53A", source: "Lindwurm" } +# Enrage sequence (Above 20%?) +1926.5 label "r12s-p1-enrage-alt" +1931.5 "The Fixer (Enrage)" Ability { id: "B2C7", source: "Lindwurm" } -# Enrage sequence 2 (Above 20%?) -2026.5 label "r12s-p1-enrage-alt" -2031.5 "The Fixer (Enrage)" Ability { id: "B2C7", source: "Lindwurm" } +# Enrage sequence +2027.7 label "r12s-p1-enrage" +2036.7 "Refreshing Overkill (Enrage)?" Ability { id: "B538", source: "Lindwurm" } +2036.8 "--untargetable--" +2036.8 "Refreshing Overkill (Enrage)?" Ability { id: "B53A", source: "Lindwurm" } + +# Kill/Transition sequence +2036.8 "Refreshing Overkill" Ability { id: "B539", source: "Lindwurm" } +2073.0 "--sync--" Ability { id: "BB9C", source: "Lindwurm" } +2073.5 "Down for the Count" duration 42 +2075.7 "--sync--" Ability { id: "B53B", source: "Lindwurm" } +2117.7 "--targetable--" ### Phase 2: Lindwurm II -# -p B528:3014.9 +# -p B528:3012.1 # -ii B51F -3010.2 "--sync--" StartsUsing { id: "B528", source: "Lindwurm" } window 3100,10 -3014.9 "Arcadia Aflame" Ability { id: "B528", source: "Lindwurm" } +# -it "Lindwurm" +3000.5 label "r12s-p2-start" +3007.1 "--sync--" StartsUsing { id: "B528", source: "Lindwurm" } window 3100,10 +3012.1 "Arcadia Aflame" Ability { id: "B528", source: "Lindwurm" } +3019.3 "--middle--" Ability { id: "B4D9", source: "Lindwurm" } +3024.4 "Replication" Ability { id: "B4D8", source: "Lindwurm" } # TBD # IGNORED ABILITIES From cbcc8635a71604ec37fa478f74f2591246adc1bf Mon Sep 17 00:00:00 2001 From: Legends0 Date: Tue, 20 Jan 2026 20:23:08 -0500 Subject: [PATCH 028/139] unused capture --- ui/raidboss/data/07-dt/raid/r12s.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 892679eb113..873a9b1e3b6 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1236,7 +1236,7 @@ const triggerSet: TriggerSet = { { id: 'R12S Snaking Kick', type: 'StartsUsing', - netRegex: { id: 'B527', source: 'Lindwurm', capture: true }, + netRegex: { id: 'B527', source: 'Lindwurm', capture: false }, response: Responses.getBehind(), }, { From 02615e765616000729a2e2d552f4cb36c8d11654 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Tue, 20 Jan 2026 20:23:53 -0500 Subject: [PATCH 029/139] lint --- ui/raidboss/data/07-dt/raid/r12s.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 873a9b1e3b6..58d50662189 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1222,7 +1222,7 @@ const triggerSet: TriggerSet = { // 10s castTime that could end with enrage or raidwide type: 'StartsUsing', netRegex: { id: 'B538', source: 'Lindwurm', capture: true }, - delaySeconds: (_data, matches) => parseFloat(matches.castTime) - 4, + delaySeconds: (_data, matches) => parseFloat(matches.castTime) - 4, durationSeconds: 4.7, response: Responses.bigAoe('alert'), }, From 87a0c92bb8fa5063126516bdf2a8c97ba7f1de42 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Tue, 20 Jan 2026 20:27:34 -0500 Subject: [PATCH 030/139] add quotes to jump --- ui/raidboss/data/07-dt/raid/r12s.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.txt b/ui/raidboss/data/07-dt/raid/r12s.txt index 5fb6dbf9048..e72d73a4dea 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.txt +++ b/ui/raidboss/data/07-dt/raid/r12s.txt @@ -11,7 +11,7 @@ hideall "--sync--" # -it "Lindwurm" 0.0 "--sync--" InCombat { inGameCombat: "1" } window 0,1 -1.0 "--sync--" AddedCombatant { npcNameId: "14378", name: "Lindwurm", job: "00", level: "1", ownerId: "0{4}", worldId: "00" } window 10,3 jump r12s-p2-start # Sync to P2 immediately through AddCombatant. +1.0 "--sync--" AddedCombatant { npcNameId: "14378", name: "Lindwurm", job: "00", level: "1", ownerId: "0{4}", worldId: "00" } window 10,3 jump "r12s-p2-start" # Sync to P2 immediately through AddCombatant. 15.6 "The Fixer" Ability { id: "B4D7", source: "Lindwurm" } window 20,10 25.8 "--sync--" Ability { id: "B7C4", source: "Lindwurm" } 40.9 "Mortal Slayer 1" Ability { id: ["B496", "B498"], source: "Lindwurm" } From 3016544fbce2ae33e561e9d87ec9f3f991da8810 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 22 Jan 2026 01:53:21 -0500 Subject: [PATCH 031/139] replace forcejumps with jumps + lookahead lines --- ui/raidboss/data/07-dt/raid/r12s.txt | 59 ++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 8 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.txt b/ui/raidboss/data/07-dt/raid/r12s.txt index e72d73a4dea..9d3b6c1d365 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.txt +++ b/ui/raidboss/data/07-dt/raid/r12s.txt @@ -136,7 +136,12 @@ hideall "--sync--" 456.1 "Serpentine Scourge" Ability { id: "B9BC", source: "Lindwurm" } 459.8 "Serpentine Scourge Right" Ability { id: "B4D2", source: "Lindwurm" } 460.8 "Serpentine Scourge" Ability { id: "B9BC", source: "Lindwurm" } -464.4 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } forcejump "r12s-p1-slaughtershed2" +464.4 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } jump "r12s-p1-slaughtershed2" +467.4 "Slaughtershed 2 (castbar)" #Ability { id: ["B4C3", "B4C6"], source: "Lindwurm" } +469.8 "Slaughtershed 2" #Ability { id: "ADC9", source: "Lindwurm" } +478.5 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } +478.5 "Fourth-wall Fusion" #Ability { id: "B4D5", source: "Lindwurm" } +478.9 "Burst" #Ability { id: "B49F", source: "Lindwurm" } # Serpentine Scourge Right First 540.6 label "r12s-p1-scourge-right-1" @@ -147,7 +152,12 @@ hideall "--sync--" 554.1 "Serpentine Scourge" Ability { id: "B9BC", source: "Lindwurm" } 557.7 "Serpentine Scourge Left" Ability { id: "B4D1", source: "Lindwurm" } 558.7 "Serpentine Scourge" Ability { id: "B9BC", source: "Lindwurm" } -562.4 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } forcejump "r12s-p1-slaughtershed2" +562.4 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } jump "r12s-p1-slaughtershed2" +567.4 "Slaughtershed 2 (castbar)" #Ability { id: ["B4C3", "B4C6"], source: "Lindwurm" } +569.8 "Slaughtershed 2" #Ability { id: "ADC9", source: "Lindwurm" } +578.5 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } +578.5 "Fourth-wall Fusion" #Ability { id: "B4D5", source: "Lindwurm" } +578.9 "Burst" #Ability { id: "B49F", source: "Lindwurm" } # Raptor Knuckles Left First 641.1 label "r12s-p1-raptor-left-1" @@ -158,7 +168,12 @@ hideall "--sync--" 654.4 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } 658.3 "Raptor Knuckles Northwest" Ability { id: "B4CF", source: "Lindwurm" } 659.1 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } -662.9 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } forcejump "r12s-p1-slaughtershed2" +662.9 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } jump "r12s-p1-slaughtershed2" +667.4 "Slaughtershed 2 (castbar)" #Ability { id: ["B4C3", "B4C6"], source: "Lindwurm" } +669.8 "Slaughtershed 2" #Ability { id: "ADC9", source: "Lindwurm" } +678.5 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } +678.5 "Fourth-wall Fusion" #Ability { id: "B4D5", source: "Lindwurm" } +678.9 "Burst" #Ability { id: "B49F", source: "Lindwurm" } # Raptor Knuckles Right First 742.7 label "r12s-p1-raptor-right-1" @@ -169,7 +184,12 @@ hideall "--sync--" 756.1 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } 759.9 "Raptor Knuckles Northeast" Ability { id: "B4D0", source: "Lindwurm" } 760.7 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } -764.5 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } forcejump "r12s-p1-slaughtershed2" +764.5 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } jump "r12s-p1-slaughtershed2" +767.4 "Slaughtershed 2 (castbar)" #Ability { id: ["B4C3", "B4C6"], source: "Lindwurm" } +769.8 "Slaughtershed 2" #Ability { id: "ADC9", source: "Lindwurm" } +778.5 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } +778.5 "Fourth-wall Fusion" #Ability { id: "B4D5", source: "Lindwurm" } +778.9 "Burst" #Ability { id: "B49F", source: "Lindwurm" } # Slaughtershed 2 864.4 label "r12s-p1-slaughtershed2" @@ -199,7 +219,13 @@ hideall "--sync--" 985.1 "Serpentine Scourge" Ability { id: "B9BC", source: "Lindwurm" } 988.7 "Serpentine Scourge Right" Ability { id: "B4D2", source: "Lindwurm" } 989.7 "Serpentine Scourge" Ability { id: "B9BC", source: "Lindwurm" } -993.3 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } forcejump "r12s-p1-slaughtershed3" +993.3 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } jump "r12s-p1-slaughtershed3" +996.3 "Slaughtershed 3 (castbar)" #Ability { id: ["B4C3", "B4C6"], source: "Lindwurm" } +998.7 "Slaughtershed 3" #Ability { id: "ADC9", source: "Lindwurm" } +1007.4 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } +1007.4 "Fourth-wall Fusion" #Ability { id: "B4D5", source: "Lindwurm" } +1007.8 "Burst" #Ability { id: "B49F", source: "Lindwurm" } + # Serpentine Scourge Right Second 1069.9 label "r12s-p1-scourge-right-2" @@ -210,7 +236,13 @@ hideall "--sync--" 1083.4 "Serpentine Scourge" Ability { id: "B9BC", source: "Lindwurm" } 1087.0 "Serpentine Scourge Left" Ability { id: "B4D1", source: "Lindwurm" } 1088.0 "Serpentine Scourge" Ability { id: "B9BC", source: "Lindwurm" } -1091.7 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } forcejump "r12s-p1-slaughtershed3" +1091.7 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } jump "r12s-p1-slaughtershed3" +1096.3 "Slaughtershed 3 (castbar)" #Ability { id: ["B4C3", "B4C6"], source: "Lindwurm" } +1098.7 "Slaughtershed 3" #Ability { id: "ADC9", source: "Lindwurm" } +1107.4 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } +1107.4 "Fourth-wall Fusion" #Ability { id: "B4D5", source: "Lindwurm" } +1107.8 "Burst" #Ability { id: "B49F", source: "Lindwurm" } + # Raptor Knuckles Left Second 1171.5 label "r12s-p1-raptor-left-2" @@ -221,7 +253,13 @@ hideall "--sync--" 1184.8 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } 1188.6 "Raptor Knuckles Northwest" Ability { id: "B4CF", source: "Lindwurm" } 1189.4 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } -1193.3 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } forcejump "r12s-p1-slaughtershed3" +1193.3 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } jump "r12s-p1-slaughtershed3" +1196.3 "Slaughtershed 3 (castbar)" #Ability { id: ["B4C3", "B4C6"], source: "Lindwurm" } +1198.7 "Slaughtershed 3" #Ability { id: "ADC9", source: "Lindwurm" } +1207.4 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } +1207.4 "Fourth-wall Fusion" #Ability { id: "B4D5", source: "Lindwurm" } +1207.8 "Burst" #Ability { id: "B49F", source: "Lindwurm" } + # Raptor Knuckles Right Second 1271.5 label "r12s-p1-raptor-right-2" @@ -232,7 +270,12 @@ hideall "--sync--" 1284.8 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } 1288.6 "Raptor Knuckles Left" Ability { id: "B4D0", source: "Lindwurm" } 1289.4 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } -1293.3 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } forcejump "r12s-p1-slaughtershed3" +1293.3 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } jump "r12s-p1-slaughtershed3" +1296.3 "Slaughtershed 3 (castbar)" #Ability { id: ["B4C3", "B4C6"], source: "Lindwurm" } +1298.7 "Slaughtershed 3" #Ability { id: "ADC9", source: "Lindwurm" } +1307.4 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } +1307.4 "Fourth-wall Fusion" #Ability { id: "B4D5", source: "Lindwurm" } +1307.8 "Burst" #Ability { id: "B49F", source: "Lindwurm" } # Slaughtershed 3 1393.3 label "r12s-p1-slaughtershed3" From e2b389751a58e5bddf67bc15b471140fc7c3aa0b Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 22 Jan 2026 21:02:27 -0500 Subject: [PATCH 032/139] convert to forcejump Co-authored-by: xiashtra <91220277+xiashtra@users.noreply.github.com> --- ui/raidboss/data/07-dt/raid/r12s.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.txt b/ui/raidboss/data/07-dt/raid/r12s.txt index 9d3b6c1d365..c4398934024 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.txt +++ b/ui/raidboss/data/07-dt/raid/r12s.txt @@ -136,7 +136,7 @@ hideall "--sync--" 456.1 "Serpentine Scourge" Ability { id: "B9BC", source: "Lindwurm" } 459.8 "Serpentine Scourge Right" Ability { id: "B4D2", source: "Lindwurm" } 460.8 "Serpentine Scourge" Ability { id: "B9BC", source: "Lindwurm" } -464.4 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } jump "r12s-p1-slaughtershed2" +464.4 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } forcejump "r12s-p1-slaughtershed2" 467.4 "Slaughtershed 2 (castbar)" #Ability { id: ["B4C3", "B4C6"], source: "Lindwurm" } 469.8 "Slaughtershed 2" #Ability { id: "ADC9", source: "Lindwurm" } 478.5 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } From 90d95901cfaff59abe1e810a303fd8d93a29eeb1 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 22 Jan 2026 21:02:35 -0500 Subject: [PATCH 033/139] convert to forcejump Co-authored-by: xiashtra <91220277+xiashtra@users.noreply.github.com> --- ui/raidboss/data/07-dt/raid/r12s.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.txt b/ui/raidboss/data/07-dt/raid/r12s.txt index c4398934024..63f5500fdaf 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.txt +++ b/ui/raidboss/data/07-dt/raid/r12s.txt @@ -152,7 +152,7 @@ hideall "--sync--" 554.1 "Serpentine Scourge" Ability { id: "B9BC", source: "Lindwurm" } 557.7 "Serpentine Scourge Left" Ability { id: "B4D1", source: "Lindwurm" } 558.7 "Serpentine Scourge" Ability { id: "B9BC", source: "Lindwurm" } -562.4 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } jump "r12s-p1-slaughtershed2" +562.4 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } forcejump "r12s-p1-slaughtershed2" 567.4 "Slaughtershed 2 (castbar)" #Ability { id: ["B4C3", "B4C6"], source: "Lindwurm" } 569.8 "Slaughtershed 2" #Ability { id: "ADC9", source: "Lindwurm" } 578.5 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } From f6e0782a78a84a54fc9d3e9b747fbe7a9cc35f16 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 22 Jan 2026 21:02:42 -0500 Subject: [PATCH 034/139] convert to forcejump Co-authored-by: xiashtra <91220277+xiashtra@users.noreply.github.com> --- ui/raidboss/data/07-dt/raid/r12s.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.txt b/ui/raidboss/data/07-dt/raid/r12s.txt index 63f5500fdaf..ab06c685674 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.txt +++ b/ui/raidboss/data/07-dt/raid/r12s.txt @@ -168,7 +168,7 @@ hideall "--sync--" 654.4 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } 658.3 "Raptor Knuckles Northwest" Ability { id: "B4CF", source: "Lindwurm" } 659.1 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } -662.9 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } jump "r12s-p1-slaughtershed2" +662.9 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } forcejump "r12s-p1-slaughtershed2" 667.4 "Slaughtershed 2 (castbar)" #Ability { id: ["B4C3", "B4C6"], source: "Lindwurm" } 669.8 "Slaughtershed 2" #Ability { id: "ADC9", source: "Lindwurm" } 678.5 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } From 3b5dfc088640432896bcb6e3e3d1aa382549997f Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 22 Jan 2026 21:02:49 -0500 Subject: [PATCH 035/139] convert to forcejump Co-authored-by: xiashtra <91220277+xiashtra@users.noreply.github.com> --- ui/raidboss/data/07-dt/raid/r12s.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.txt b/ui/raidboss/data/07-dt/raid/r12s.txt index ab06c685674..7fd55ed4525 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.txt +++ b/ui/raidboss/data/07-dt/raid/r12s.txt @@ -184,7 +184,7 @@ hideall "--sync--" 756.1 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } 759.9 "Raptor Knuckles Northeast" Ability { id: "B4D0", source: "Lindwurm" } 760.7 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } -764.5 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } jump "r12s-p1-slaughtershed2" +764.5 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } forcejump "r12s-p1-slaughtershed2" 767.4 "Slaughtershed 2 (castbar)" #Ability { id: ["B4C3", "B4C6"], source: "Lindwurm" } 769.8 "Slaughtershed 2" #Ability { id: "ADC9", source: "Lindwurm" } 778.5 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } From 2b8da9919a4d3135f6034ccdfc00f20e1a41f314 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 22 Jan 2026 21:02:56 -0500 Subject: [PATCH 036/139] convert to forcejump Co-authored-by: xiashtra <91220277+xiashtra@users.noreply.github.com> --- ui/raidboss/data/07-dt/raid/r12s.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.txt b/ui/raidboss/data/07-dt/raid/r12s.txt index 7fd55ed4525..6e478755838 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.txt +++ b/ui/raidboss/data/07-dt/raid/r12s.txt @@ -219,7 +219,7 @@ hideall "--sync--" 985.1 "Serpentine Scourge" Ability { id: "B9BC", source: "Lindwurm" } 988.7 "Serpentine Scourge Right" Ability { id: "B4D2", source: "Lindwurm" } 989.7 "Serpentine Scourge" Ability { id: "B9BC", source: "Lindwurm" } -993.3 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } jump "r12s-p1-slaughtershed3" +993.3 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } forcejump "r12s-p1-slaughtershed3" 996.3 "Slaughtershed 3 (castbar)" #Ability { id: ["B4C3", "B4C6"], source: "Lindwurm" } 998.7 "Slaughtershed 3" #Ability { id: "ADC9", source: "Lindwurm" } 1007.4 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } From a8cfcb285950bc6f357e2e72223c47c4aba1af56 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 22 Jan 2026 21:03:04 -0500 Subject: [PATCH 037/139] convert to forcejump Co-authored-by: xiashtra <91220277+xiashtra@users.noreply.github.com> --- ui/raidboss/data/07-dt/raid/r12s.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.txt b/ui/raidboss/data/07-dt/raid/r12s.txt index 6e478755838..cd28893ef0e 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.txt +++ b/ui/raidboss/data/07-dt/raid/r12s.txt @@ -236,7 +236,7 @@ hideall "--sync--" 1083.4 "Serpentine Scourge" Ability { id: "B9BC", source: "Lindwurm" } 1087.0 "Serpentine Scourge Left" Ability { id: "B4D1", source: "Lindwurm" } 1088.0 "Serpentine Scourge" Ability { id: "B9BC", source: "Lindwurm" } -1091.7 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } jump "r12s-p1-slaughtershed3" +1091.7 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } forcejump "r12s-p1-slaughtershed3" 1096.3 "Slaughtershed 3 (castbar)" #Ability { id: ["B4C3", "B4C6"], source: "Lindwurm" } 1098.7 "Slaughtershed 3" #Ability { id: "ADC9", source: "Lindwurm" } 1107.4 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } From 86fdb34168f715ec5509cf46005389b4e705735c Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 22 Jan 2026 21:03:12 -0500 Subject: [PATCH 038/139] convert to forcejump Co-authored-by: xiashtra <91220277+xiashtra@users.noreply.github.com> --- ui/raidboss/data/07-dt/raid/r12s.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.txt b/ui/raidboss/data/07-dt/raid/r12s.txt index cd28893ef0e..c55defecf21 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.txt +++ b/ui/raidboss/data/07-dt/raid/r12s.txt @@ -270,7 +270,7 @@ hideall "--sync--" 1284.8 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } 1288.6 "Raptor Knuckles Left" Ability { id: "B4D0", source: "Lindwurm" } 1289.4 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } -1293.3 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } jump "r12s-p1-slaughtershed3" +1293.3 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } forcejump "r12s-p1-slaughtershed3" 1296.3 "Slaughtershed 3 (castbar)" #Ability { id: ["B4C3", "B4C6"], source: "Lindwurm" } 1298.7 "Slaughtershed 3" #Ability { id: "ADC9", source: "Lindwurm" } 1307.4 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } From 72aabfd73e6d3b42ed3af050ef3fdfe46162911b Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 22 Jan 2026 21:03:18 -0500 Subject: [PATCH 039/139] convert to forcejump Co-authored-by: xiashtra <91220277+xiashtra@users.noreply.github.com> --- ui/raidboss/data/07-dt/raid/r12s.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.txt b/ui/raidboss/data/07-dt/raid/r12s.txt index c55defecf21..64c19ed1eb4 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.txt +++ b/ui/raidboss/data/07-dt/raid/r12s.txt @@ -307,7 +307,7 @@ hideall "--sync--" 1516.9 "Serpentine Scourge" Ability { id: "B9BC", source: "Lindwurm" } 1526.5 "--sync--" StartsUsing { id: "B2C7", source: "Lindwurm" } jump "r12s-p1-enrage-alt" 1526.5 "--untargetable?--" -1526.7 "--sync--" StartsUsing { id: "B538", source: "Lindwurm" } jump "r12s-p1-enrage" +1526.7 "--sync--" StartsUsing { id: "B538", source: "Lindwurm" } forcejump "r12s-p1-enrage" 1531.5 "The Fixer (Enrage)?" #Ability { id: "B2C7", source: "Lindwurm" } 1536.7 "Refreshing Overkill (Enrage)?" #Ability { id: "B538", source: "Lindwurm" } 1536.8 "--untargetable?--" From 370ae23627b4aca849b17f507d1b1d0a486ecbff Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 22 Jan 2026 21:03:24 -0500 Subject: [PATCH 040/139] convert to forcejump Co-authored-by: xiashtra <91220277+xiashtra@users.noreply.github.com> --- ui/raidboss/data/07-dt/raid/r12s.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.txt b/ui/raidboss/data/07-dt/raid/r12s.txt index 64c19ed1eb4..6f654e7dff3 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.txt +++ b/ui/raidboss/data/07-dt/raid/r12s.txt @@ -324,7 +324,7 @@ hideall "--sync--" 1618.6 "Serpentine Scourge" Ability { id: "B9BC", source: "Lindwurm" } 1626.5 "--sync--" StartsUsing { id: "B2C7", source: "Lindwurm" } jump "r12s-p1-enrage-alt" 1626.5 "--untargetable?--" -1626.7 "--sync--" StartsUsing { id: "B538", source: "Lindwurm" } jump "r12s-p1-enrage" +1626.7 "--sync--" StartsUsing { id: "B538", source: "Lindwurm" } forcejump "r12s-p1-enrage" 1631.5 "The Fixer (Enrage)?" #Ability { id: "B2C7", source: "Lindwurm" } 1636.7 "Refreshing Overkill (Enrage)?" #Ability { id: "B538", source: "Lindwurm" } 1636.8 "--untargetable?--" From ce82687de3bc08e761854aa58fda9626fe691e76 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 22 Jan 2026 21:03:30 -0500 Subject: [PATCH 041/139] convert to forcejump Co-authored-by: xiashtra <91220277+xiashtra@users.noreply.github.com> --- ui/raidboss/data/07-dt/raid/r12s.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.txt b/ui/raidboss/data/07-dt/raid/r12s.txt index 6f654e7dff3..68384da3b72 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.txt +++ b/ui/raidboss/data/07-dt/raid/r12s.txt @@ -341,7 +341,7 @@ hideall "--sync--" 1718.3 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } 1726.5 "--sync--" StartsUsing { id: "B2C7", source: "Lindwurm" } jump "r12s-p1-enrage-alt" 1726.5 "--untargetable?--" -1726.7 "--sync--" StartsUsing { id: "B538", source: "Lindwurm" } jump "r12s-p1-enrage" +1726.7 "--sync--" StartsUsing { id: "B538", source: "Lindwurm" } forcejump "r12s-p1-enrage" 1731.5 "The Fixer (Enrage)?" #Ability { id: "B2C7", source: "Lindwurm" } 1736.7 "Refreshing Overkill (Enrage)?" #Ability { id: "B538", source: "Lindwurm" } 1736.8 "--untargetable?--" From ee1274fcd8ff48fb5cd7125f2ff11f850f3e6000 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 22 Jan 2026 21:03:36 -0500 Subject: [PATCH 042/139] convert to forcejump Co-authored-by: xiashtra <91220277+xiashtra@users.noreply.github.com> --- ui/raidboss/data/07-dt/raid/r12s.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.txt b/ui/raidboss/data/07-dt/raid/r12s.txt index 68384da3b72..577e5626178 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.txt +++ b/ui/raidboss/data/07-dt/raid/r12s.txt @@ -358,7 +358,7 @@ hideall "--sync--" 1818.3 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } 1826.5 "--sync--" StartsUsing { id: "B2C7", source: "Lindwurm" } jump "r12s-p1-enrage-alt" 1826.5 "--untargetable?--" -1826.7 "--sync--" StartsUsing { id: "B538", source: "Lindwurm" } jump "r12s-p1-enrage" +1826.7 "--sync--" StartsUsing { id: "B538", source: "Lindwurm" } forcejump "r12s-p1-enrage" 1831.5 "The Fixer (Enrage)?" #Ability { id: "B2C7", source: "Lindwurm" } 1836.7 "Refreshing Overkill (Enrage)?" #Ability { id: "B538", source: "Lindwurm" } 1836.8 "--untargetable?--" From 5a1d50c78a0a71e8d342c8d6ebd5e9b00d7c053e Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 22 Jan 2026 21:13:35 -0500 Subject: [PATCH 043/139] move enrage forcejump and add window This should let the jump still have a lookahead without the forcejump incorrectly overwriting it. --- ui/raidboss/data/07-dt/raid/r12s.txt | 55 +++------------------------- 1 file changed, 6 insertions(+), 49 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.txt b/ui/raidboss/data/07-dt/raid/r12s.txt index 577e5626178..2ad45431375 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.txt +++ b/ui/raidboss/data/07-dt/raid/r12s.txt @@ -137,11 +137,6 @@ hideall "--sync--" 459.8 "Serpentine Scourge Right" Ability { id: "B4D2", source: "Lindwurm" } 460.8 "Serpentine Scourge" Ability { id: "B9BC", source: "Lindwurm" } 464.4 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } forcejump "r12s-p1-slaughtershed2" -467.4 "Slaughtershed 2 (castbar)" #Ability { id: ["B4C3", "B4C6"], source: "Lindwurm" } -469.8 "Slaughtershed 2" #Ability { id: "ADC9", source: "Lindwurm" } -478.5 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } -478.5 "Fourth-wall Fusion" #Ability { id: "B4D5", source: "Lindwurm" } -478.9 "Burst" #Ability { id: "B49F", source: "Lindwurm" } # Serpentine Scourge Right First 540.6 label "r12s-p1-scourge-right-1" @@ -153,11 +148,6 @@ hideall "--sync--" 557.7 "Serpentine Scourge Left" Ability { id: "B4D1", source: "Lindwurm" } 558.7 "Serpentine Scourge" Ability { id: "B9BC", source: "Lindwurm" } 562.4 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } forcejump "r12s-p1-slaughtershed2" -567.4 "Slaughtershed 2 (castbar)" #Ability { id: ["B4C3", "B4C6"], source: "Lindwurm" } -569.8 "Slaughtershed 2" #Ability { id: "ADC9", source: "Lindwurm" } -578.5 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } -578.5 "Fourth-wall Fusion" #Ability { id: "B4D5", source: "Lindwurm" } -578.9 "Burst" #Ability { id: "B49F", source: "Lindwurm" } # Raptor Knuckles Left First 641.1 label "r12s-p1-raptor-left-1" @@ -169,11 +159,6 @@ hideall "--sync--" 658.3 "Raptor Knuckles Northwest" Ability { id: "B4CF", source: "Lindwurm" } 659.1 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } 662.9 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } forcejump "r12s-p1-slaughtershed2" -667.4 "Slaughtershed 2 (castbar)" #Ability { id: ["B4C3", "B4C6"], source: "Lindwurm" } -669.8 "Slaughtershed 2" #Ability { id: "ADC9", source: "Lindwurm" } -678.5 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } -678.5 "Fourth-wall Fusion" #Ability { id: "B4D5", source: "Lindwurm" } -678.9 "Burst" #Ability { id: "B49F", source: "Lindwurm" } # Raptor Knuckles Right First 742.7 label "r12s-p1-raptor-right-1" @@ -185,11 +170,6 @@ hideall "--sync--" 759.9 "Raptor Knuckles Northeast" Ability { id: "B4D0", source: "Lindwurm" } 760.7 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } 764.5 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } forcejump "r12s-p1-slaughtershed2" -767.4 "Slaughtershed 2 (castbar)" #Ability { id: ["B4C3", "B4C6"], source: "Lindwurm" } -769.8 "Slaughtershed 2" #Ability { id: "ADC9", source: "Lindwurm" } -778.5 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } -778.5 "Fourth-wall Fusion" #Ability { id: "B4D5", source: "Lindwurm" } -778.9 "Burst" #Ability { id: "B49F", source: "Lindwurm" } # Slaughtershed 2 864.4 label "r12s-p1-slaughtershed2" @@ -220,12 +200,6 @@ hideall "--sync--" 988.7 "Serpentine Scourge Right" Ability { id: "B4D2", source: "Lindwurm" } 989.7 "Serpentine Scourge" Ability { id: "B9BC", source: "Lindwurm" } 993.3 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } forcejump "r12s-p1-slaughtershed3" -996.3 "Slaughtershed 3 (castbar)" #Ability { id: ["B4C3", "B4C6"], source: "Lindwurm" } -998.7 "Slaughtershed 3" #Ability { id: "ADC9", source: "Lindwurm" } -1007.4 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } -1007.4 "Fourth-wall Fusion" #Ability { id: "B4D5", source: "Lindwurm" } -1007.8 "Burst" #Ability { id: "B49F", source: "Lindwurm" } - # Serpentine Scourge Right Second 1069.9 label "r12s-p1-scourge-right-2" @@ -237,12 +211,6 @@ hideall "--sync--" 1087.0 "Serpentine Scourge Left" Ability { id: "B4D1", source: "Lindwurm" } 1088.0 "Serpentine Scourge" Ability { id: "B9BC", source: "Lindwurm" } 1091.7 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } forcejump "r12s-p1-slaughtershed3" -1096.3 "Slaughtershed 3 (castbar)" #Ability { id: ["B4C3", "B4C6"], source: "Lindwurm" } -1098.7 "Slaughtershed 3" #Ability { id: "ADC9", source: "Lindwurm" } -1107.4 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } -1107.4 "Fourth-wall Fusion" #Ability { id: "B4D5", source: "Lindwurm" } -1107.8 "Burst" #Ability { id: "B49F", source: "Lindwurm" } - # Raptor Knuckles Left Second 1171.5 label "r12s-p1-raptor-left-2" @@ -253,13 +221,7 @@ hideall "--sync--" 1184.8 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } 1188.6 "Raptor Knuckles Northwest" Ability { id: "B4CF", source: "Lindwurm" } 1189.4 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } -1193.3 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } jump "r12s-p1-slaughtershed3" -1196.3 "Slaughtershed 3 (castbar)" #Ability { id: ["B4C3", "B4C6"], source: "Lindwurm" } -1198.7 "Slaughtershed 3" #Ability { id: "ADC9", source: "Lindwurm" } -1207.4 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } -1207.4 "Fourth-wall Fusion" #Ability { id: "B4D5", source: "Lindwurm" } -1207.8 "Burst" #Ability { id: "B49F", source: "Lindwurm" } - +1193.3 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } forcejump "r12s-p1-slaughtershed3" # Raptor Knuckles Right Second 1271.5 label "r12s-p1-raptor-right-2" @@ -271,11 +233,6 @@ hideall "--sync--" 1288.6 "Raptor Knuckles Left" Ability { id: "B4D0", source: "Lindwurm" } 1289.4 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } 1293.3 "--sync--" StartsUsing { id: ["B4C3", "B4C6"], source: "Lindwurm" } forcejump "r12s-p1-slaughtershed3" -1296.3 "Slaughtershed 3 (castbar)" #Ability { id: ["B4C3", "B4C6"], source: "Lindwurm" } -1298.7 "Slaughtershed 3" #Ability { id: "ADC9", source: "Lindwurm" } -1307.4 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } -1307.4 "Fourth-wall Fusion" #Ability { id: "B4D5", source: "Lindwurm" } -1307.8 "Burst" #Ability { id: "B49F", source: "Lindwurm" } # Slaughtershed 3 1393.3 label "r12s-p1-slaughtershed3" @@ -307,11 +264,11 @@ hideall "--sync--" 1516.9 "Serpentine Scourge" Ability { id: "B9BC", source: "Lindwurm" } 1526.5 "--sync--" StartsUsing { id: "B2C7", source: "Lindwurm" } jump "r12s-p1-enrage-alt" 1526.5 "--untargetable?--" -1526.7 "--sync--" StartsUsing { id: "B538", source: "Lindwurm" } forcejump "r12s-p1-enrage" 1531.5 "The Fixer (Enrage)?" #Ability { id: "B2C7", source: "Lindwurm" } 1536.7 "Refreshing Overkill (Enrage)?" #Ability { id: "B538", source: "Lindwurm" } 1536.8 "--untargetable?--" 1536.8 "Refreshing Overkill (Enrage)?" #Ability { id: "B53A", source: "Lindwurm" } +1546.7 "--sync--" StartsUsing { id: "B538", source: "Lindwurm" } window 20,3 forcejump "r12s-p1-enrage" # Serpentine Scourge Right Third 1600.4 label "r12s-p1-scourge-right-3" @@ -324,11 +281,11 @@ hideall "--sync--" 1618.6 "Serpentine Scourge" Ability { id: "B9BC", source: "Lindwurm" } 1626.5 "--sync--" StartsUsing { id: "B2C7", source: "Lindwurm" } jump "r12s-p1-enrage-alt" 1626.5 "--untargetable?--" -1626.7 "--sync--" StartsUsing { id: "B538", source: "Lindwurm" } forcejump "r12s-p1-enrage" 1631.5 "The Fixer (Enrage)?" #Ability { id: "B2C7", source: "Lindwurm" } 1636.7 "Refreshing Overkill (Enrage)?" #Ability { id: "B538", source: "Lindwurm" } 1636.8 "--untargetable?--" 1636.8 "Refreshing Overkill (Enrage)?" #Ability { id: "B53A", source: "Lindwurm" } +1646.7 "--sync--" StartsUsing { id: "B538", source: "Lindwurm" } window 20,3 forcejump "r12s-p1-enrage" # Raptor Knuckles Left Third 1700.4 label "r12s-p1-raptor-left-3" @@ -341,11 +298,11 @@ hideall "--sync--" 1718.3 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } 1726.5 "--sync--" StartsUsing { id: "B2C7", source: "Lindwurm" } jump "r12s-p1-enrage-alt" 1726.5 "--untargetable?--" -1726.7 "--sync--" StartsUsing { id: "B538", source: "Lindwurm" } forcejump "r12s-p1-enrage" 1731.5 "The Fixer (Enrage)?" #Ability { id: "B2C7", source: "Lindwurm" } 1736.7 "Refreshing Overkill (Enrage)?" #Ability { id: "B538", source: "Lindwurm" } 1736.8 "--untargetable?--" 1736.8 "Refreshing Overkill (Enrage)?" #Ability { id: "B53A", source: "Lindwurm" } +1746.7 "--sync--" StartsUsing { id: "B538", source: "Lindwurm" } window 20,3 forcejump "r12s-p1-enrage" # Raptor Knuckles Right Third 1800.4 label "r12s-p1-raptor-right-3" @@ -358,11 +315,11 @@ hideall "--sync--" 1818.3 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } 1826.5 "--sync--" StartsUsing { id: "B2C7", source: "Lindwurm" } jump "r12s-p1-enrage-alt" 1826.5 "--untargetable?--" -1826.7 "--sync--" StartsUsing { id: "B538", source: "Lindwurm" } forcejump "r12s-p1-enrage" 1831.5 "The Fixer (Enrage)?" #Ability { id: "B2C7", source: "Lindwurm" } 1836.7 "Refreshing Overkill (Enrage)?" #Ability { id: "B538", source: "Lindwurm" } 1836.8 "--untargetable?--" 1836.8 "Refreshing Overkill (Enrage)?" #Ability { id: "B53A", source: "Lindwurm" } +1846.7 "--sync--" StartsUsing { id: "B538", source: "Lindwurm" } window 20,3 forcejump "r12s-p1-enrage" # Enrage sequence (Above 20%?) 1926.5 label "r12s-p1-enrage-alt" @@ -375,7 +332,7 @@ hideall "--sync--" 2036.8 "Refreshing Overkill (Enrage)?" Ability { id: "B53A", source: "Lindwurm" } # Kill/Transition sequence -2036.8 "Refreshing Overkill" Ability { id: "B539", source: "Lindwurm" } +2036.8 "Refreshing Overkill" Ability { id: "B539", source: "Lindwurm" } window 510,5 2073.0 "--sync--" Ability { id: "BB9C", source: "Lindwurm" } 2073.5 "Down for the Count" duration 42 2075.7 "--sync--" Ability { id: "B53B", source: "Lindwurm" } From dab93fd3f68e405c70b69a484769c19ec32b7c6c Mon Sep 17 00:00:00 2001 From: Legends0 Date: Fri, 23 Jan 2026 21:14:04 -0500 Subject: [PATCH 044/139] change p2 tankbuster to use headmarker --- ui/raidboss/data/07-dt/raid/r12s.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 58d50662189..e94b790470b 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -42,6 +42,8 @@ const headMarkerData = { 'slaughterSpread': '0177', 'cellChainTether': '016E', // Phase 2 + // VFX: sharelaser2tank5sec_c0k1, used by Double Sobat (B520) + 'sharedTankbuster': '0256', } as const; const center = { @@ -1241,9 +1243,11 @@ const triggerSet: TriggerSet = { }, { id: 'R12S Double Sobat', - type: 'StartsUsing', - netRegex: { id: 'B520', source: 'Lindwurm', capture: true }, - response: Responses.tankCleave(), + // Two half-room cleaves + // First hit targets highest emnity target, second targets second highest + type: 'HeadMarker', + netRegex: { id: headMarkerData['sharedTankbuster'], capture: true }, + response: Responses.sharedTankBuster(), }, ], timelineReplace: [], From 9a16fd09853a473afc9b77ac3a42487717cbcf91 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sat, 24 Jan 2026 20:01:49 -0500 Subject: [PATCH 045/139] add p2 replication 1 triggers --- ui/raidboss/data/07-dt/raid/r12s.ts | 225 +++++++++++++++++++++++++++- 1 file changed, 224 insertions(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index e94b790470b..9c7cebc908e 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -7,7 +7,13 @@ import ZoneId from '../../../../../resources/zone_id'; import { RaidbossData } from '../../../../../types/data'; import { TriggerSet } from '../../../../../types/trigger'; -export type Phase = 'doorboss' | 'curtainCall' | 'slaughtershed' | 'two'; +export type Phase = + | 'doorboss' + | 'curtainCall' + | 'slaughtershed' + | 'replication1' + | 'replication2' + | 'idyllic'; export interface Data extends RaidbossData { phase: Phase; @@ -26,6 +32,10 @@ export interface Data extends RaidbossData { myMitoticPhase?: string; hasRot: boolean; // Phase 2 + actorPositions: { [id: string]: { x: number; y: number; heading: number } }; + replication1Debuff?: 'fire' | 'dark'; + replication1FireActor?: string; + replication1FollowUp: boolean; } const headMarkerData = { @@ -54,6 +64,7 @@ const center = { const phaseMap: { [id: string]: Phase } = { 'BEC0': 'curtainCall', 'B4C6': 'slaughtershed', + 'B509': 'idyllic', }; const triggerSet: TriggerSet = { @@ -70,6 +81,8 @@ const triggerSet: TriggerSet = { cellChainCount: 0, hasRot: false, // Phase 2 + actorPositions: {}, + replication1FollowUp: false, }), triggers: [ { @@ -85,6 +98,63 @@ const triggerSet: TriggerSet = { data.phase = phase; }, }, + { + id: 'R12S Phase Two Replication Tracker', + // B4D8 Replication happens more than once, only track the first one + type: 'StartsUsing', + netRegex: { id: 'B4D8', source: 'Lindwurm', capture: false }, + suppressSeconds: 9999, + run: (data) => data.phase = 'replication1', + }, + { + id: 'R12S Phase Two Staging Tracker', + // B4E1 Staging happens more than once, only track the first one + type: 'StartsUsing', + netRegex: { id: 'B4E1', source: 'Lindwurm', capture: false }, + condition: (data) => data.phase === 'replication1', + suppressSeconds: 9999, + run: (data) => data.phase = 'replication2', + }, + { + id: 'R12S Phase Two ActorSetPos Tracker', + type: 'ActorSetPos', + netRegex: { id: '4[0-9A-Fa-f]{7}', capture: true }, + condition: (data) => { + if ( + data.phase === 'replication1' || + data.phase === 'replication2' || + data.phase === 'idyllic' + ) + return true; + return false; + }, + run: (data, matches) => + data.actorPositions[matches.id] = { + x: parseFloat(matches.x), + y: parseFloat(matches.y), + heading: parseFloat(matches.heading), + }, + }, + { + id: 'R12S Phase Two ActorMove Tracker', + type: 'ActorMove', + netRegex: { id: '4[0-9A-Fa-f]{7}', capture: true }, + condition: (data) => { + if ( + data.phase === 'replication1' || + data.phase === 'replication2' || + data.phase === 'idyllic' + ) + return true; + return false; + }, + run: (data, matches) => + data.actorPositions[matches.id] = { + x: parseFloat(matches.x), + y: parseFloat(matches.y), + heading: parseFloat(matches.heading), + }, + }, { id: 'R12S The Fixer', type: 'StartsUsing', @@ -1235,11 +1305,164 @@ const triggerSet: TriggerSet = { netRegex: { id: 'B528', source: 'Lindwurm', capture: false }, response: Responses.bigAoe('alert'), }, + { + id: 'R12S Fire and Dark Resistance Down II Collector', + // CFB Dark Resistance Down II + // B79 Fire Resistance Down II + type: 'GainsEffect', + netRegex: { effectId: ['CFB', 'B79'], capture: true }, + condition: Conditions.targetIsYou(), + suppressSeconds: 9999, + run: (data, matches) => { + data.replication1Debuff = matches.effectId === 'CFB' ? 'dark' : 'fire'; + }, + }, + { + id: 'R12S Fire and Dark Resistance Down II', + // CFB Dark Resistance Down II + // B79 Fire Resistance Down II + type: 'GainsEffect', + netRegex: { effectId: ['CFB', 'B79'], capture: true }, + condition: (data, matches) => { + if (data.me === matches.target) + return !data.replication1FollowUp; + return false; + }, + suppressSeconds: 9999, + infoText: (_data, matches, output) => { + return matches.effectId === 'CFB' ? output.dark!() : output.fire!(); + }, + outputStrings: { + fire: { + en: 'Fire Debuff: Spread near Dark Clone (later)', + }, + dark: { + en: 'Dark Debuff: Stack near Fire Clone (later)', + }, + }, + }, + { + id: 'R12S Fake Fire Resistance Down II', + // Two players will not receive a debuff, they will need to act as if they had + type: 'GainsEffect', + netRegex: { effectId: ['CFB', 'B79'], capture: false }, + condition: (data) => !data.replication1FollowUp, + delaySeconds: 0.3, // Delay for debuff/damage propagation + suppressSeconds: 9999, + infoText: (data, _matches, output) => { + if (data.replication1Debuff === undefined) + return output.noDebuff!(); + }, + outputStrings: { + noDebuff: { + en: 'No Debuff: Spread near Dark Clone (later)', + }, + }, + }, { id: 'R12S Snaking Kick', type: 'StartsUsing', netRegex: { id: 'B527', source: 'Lindwurm', capture: false }, response: Responses.getBehind(), + run: (data) => data.replication1FollowUp = true, + }, + { + id: 'R12S Replication 1 Follow-up Tracker', + // Tracking from B527 Snaking Kick + type: 'StartsUsing', + netRegex: { id: 'B527', source: 'Lindwurm', capture: false }, + suppressSeconds: 9999, + run: (data) => data.replication1FollowUp = true, + }, + { + id: 'R12S Top-Tier Slam Actor Collect', + // Fire NPCs always move in the first Set + // Locations are static + // Fire => Dark => Fire => Dark + // Dark => Fire => Dark => Fire + // The other 4 cleave in a line + // (90, 90) (110, 90) + // (95, 95) (105, 95) + // Boss + // (95, 100) (105, 105) + // (90, 110) (110, 110) + // ActorMove ~0.3s later will have the data + // ActorSet from the clones splitting we can infer the fire entities since their positions and headings are not perfect + type: 'Ability', + netRegex: { id: 'B4D9', source: 'Lindschrat', capture: true }, + condition: (data, matches) => { + if (data.replication1FollowUp) { + const pos = data.actorPositions[matches.sourceId]; + if (pos === undefined) + return false; + // These values should be 0 if coords are x.0000 + const xFilter = pos.x % 1; + const yFilter = pos.y % 1; + if (xFilter === 0 && yFilter === 0 && pos.heading === -0.0001) + return false; + return true; + } + return false; + }, + suppressSeconds: 9999, // Only need one of the two + run: (data, matches) => data.replication1FireActor = matches.sourceId, + }, + { + id: 'R12S Top-Tier Slam/Mighty Magic Locations', + type: 'Ability', + netRegex: { id: 'B4D9', source: 'Lindschrat', capture: false }, + condition: (data) => { + if (data.replication1FollowUp && data.replication1FireActor !== undefined) + return true; + return false; + }, + delaySeconds: 1, // Data is sometimes not available right away + suppressSeconds: 9999, + infoText: (data, _matches, output) => { + const fireId = data.replication1FireActor; + if (fireId === undefined) + return; + + const actor = data.actorPositions[fireId]; + if (actor === undefined) + return; + + const x = actor.x; + const dirNum = Directions.xyTo8DirNum(x, actor.y, center.x, center.y); + const dir1 = Directions.output8Dir[dirNum] ?? 'unknown'; + const dirNum2 = (dirNum + 4) % 8; + const dir2 = Directions.output8Dir[dirNum2] ?? 'unknown'; + + // Check if combatant moved to inner or outer + const isIn = (x > 94 && x < 106); + const fireIn = isIn ? dir1 : dir2; + const fireOut = isIn ? dir2 : dir1; + + if (data.replication1Debuff === 'dark') + return output.fire!({ + dir1: output[fireIn]!(), + dir2: output[fireOut]!(), + }); + + // Dark will be opposite pattern of Fire + const darkIn = isIn ? dir2 : dir1; + const darkOut = isIn ? dir1 : dir2; + + // Fire debuff players and unmarked bait Dark + return output.dark!({ + dir1: output[darkIn]!(), + dir2: output[darkOut]!(), + }); + }, + outputStrings: { + ...Directions.outputStringsIntercardDir, // Cardinals should result in '???' + fire: { + en: 'Bait Fire near In ${dir1}/Out ${dir2} (Partners)', + }, + dark: { + en: 'Bait Dark near In ${dir1}/Out ${dir2} (Solo)', + }, + }, }, { id: 'R12S Double Sobat', From 1bb17bb86372bd2181d27918ec55425e1dc55609 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sat, 24 Jan 2026 20:27:06 -0500 Subject: [PATCH 046/139] add p2 Winged Scourge cleaves --- ui/raidboss/data/07-dt/raid/r12s.ts | 32 +++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 9c7cebc908e..dc329d58116 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1305,6 +1305,38 @@ const triggerSet: TriggerSet = { netRegex: { id: 'B528', source: 'Lindwurm', capture: false }, response: Responses.bigAoe('alert'), }, + { + id: 'R12S Winged Scourge', + // B4DA E/W clones Facing S, Cleaving Front/Back (North/South) + // B4DB N/S clones Facing W, Cleaving Front/Back (East/West) + type: 'StartsUsing', + netRegex: { id: ['B4DA', 'B4DB'], source: 'Lindschrat', capture: true }, + suppressSeconds: 1, + infoText: (data, matches, output) => { + if (matches.id === 'B4DA') { + if (data.replication1FollowUp) + return output.northSouthCleaves2!(); + return output.northSouthCleaves!(); + } + if (data.replication1FollowUp) + return output.eastWestCleaves2!(); + return output.eastWestCleaves!(); + }, + outputStrings: { + northSouthCleaves: { + en: 'North/South Cleaves', + }, + eastWestCleaves: { + en: 'East/West Cleaves', + }, + northSouthCleaves2: { + en: 'North/South Cleaves', + }, + eastWestCleaves2: { + en: 'East/West Cleaves', + }, + }, + }, { id: 'R12S Fire and Dark Resistance Down II Collector', // CFB Dark Resistance Down II From 24e01ec5d0303acace0420d5c697f8ca199d73bc Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sat, 24 Jan 2026 20:33:58 -0500 Subject: [PATCH 047/139] p2 timeline up to Replication 2 --- ui/raidboss/data/07-dt/raid/r12s.txt | 120 +++++++++++++++++++++++++-- 1 file changed, 114 insertions(+), 6 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.txt b/ui/raidboss/data/07-dt/raid/r12s.txt index 2ad45431375..2ee7c5ea3fb 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.txt +++ b/ui/raidboss/data/07-dt/raid/r12s.txt @@ -340,14 +340,31 @@ hideall "--sync--" ### Phase 2: Lindwurm II -# -p B528:3012.1 -# -ii B51F +# -p B528:3015.7 +# -ii B51F B4DA B4DB B4DD B4DF # -it "Lindwurm" 3000.5 label "r12s-p2-start" -3007.1 "--sync--" StartsUsing { id: "B528", source: "Lindwurm" } window 3100,10 -3012.1 "Arcadia Aflame" Ability { id: "B528", source: "Lindwurm" } -3019.3 "--middle--" Ability { id: "B4D9", source: "Lindwurm" } -3024.4 "Replication" Ability { id: "B4D8", source: "Lindwurm" } +3010.7 "--sync--" StartsUsing { id: "B528", source: "Lindwurm" } window 3100,10 +3015.7 "Arcadia Aflame" Ability { id: "B528", source: "Lindwurm" } +3022.9 "--middle--" Ability { id: "B4D9", source: "Lindwurm" } +3028.0 "Replication 1" Ability { id: "B4D8", source: "Lindwurm" } +3039.6 "Top-tier Slam x2" #Ability { id: "B4DE", source: "Lindschrat" } +3040.4 "Winged Scourge x4" Ability { id: "B4DC", source: "Lindwurm" } +3040.6 "Mighty Magic x4" #Ability { id: "B4E0", source: "Lindwurm" } +3045.2 "Snaking Kick" Ability { id: "B527", source: "Lindwurm" } +3053.8 "--clones move 1--" #Ability { id: "B4D9", source: "Lindschrat" } +3054.8 "--clones move 2--" #Ability { id: "B4D9", source: "Lindschrat" } +3061.0 "Top-tier Slam x2" #Ability { id: "B4DE", source: "Lindschrat" } +3061.8 "Winged Scourge x4" Ability { id: "B4DC", source: "Lindwurm" } +3062.1 "Mighty Magic x4" #Ability { id: "B4E0", source: "Lindwurm" } +3069.3 "Double Sobat (castbar)" Ability { id: "B520", source: "Lindwurm" } +3070.0 "Double Sobat 1" Ability { id: "B522", source: "Lindwurm" } +3074.6 "Double Sobat 2" Ability { id: "B525", source: "Lindwurm" } +3077.0 "Esoteric Finisher" Ability { id: "B526", source: "Lindwurm" } + +3091.2 "Staging" Ability { id: "B4E1", source: "Lindwurm" } +3102.2 "--sync--" Ability { id: "B4E2", source: "Lindwurm" } +3105.4 "Replication 2" Ability { id: "B4D8", source: "Lindwurm" } # TBD # IGNORED ABILITIES @@ -373,6 +390,11 @@ hideall "--sync--" # Phase 2 # B51F --sync--: Attack autos +# B51F --sync--: Attack autos +# B4DA Winged Scourge: VFX E/W clones Facing S, Cleaving Front/Back (North/South) +# B4DB Winged Scourge: VFX N/S clones Facing W, Cleaving Front/Back (East/West) +# B4DD Top-tier Slam: VFX (cast that gives Fire Debuff) +# B4DF Mighty Magic: VFX (cast that gives Dark Debuff) # ALL ENCOUNTER ABILITIES # Phase 1 @@ -463,3 +485,89 @@ hideall "--sync--" # BEC0 Grotesquerie: Curtain Call # Phase 2 +# B46C Replication +# B4D8 Replication +# B4D9 --sync-- +# B4DA Winged Scourge +# B4DB Winged Scourge +# B4DC Winged Scourge +# B4DD Top-tier Slam +# B4DE Top-tier Slam +# B4DF Mighty Magic +# B4E0 Mighty Magic +# B4E1 Staging +# B4E2 --sync-- +# B4E3 Firefall Splash +# B4E4 Firefall Splash +# B4E5 Scalding Waves +# B4E6 Mana Burst +# B4E7 Mana Burst +# B51F --sync-- +# B4E8 Heavy Slam +# B4E9 Grotesquerie +# B4EA Grotesquerie +# B4EB Hemorrhagic Projection +# B4EC Reenactment +# B4ED Firefall Splash +# B4EE Mana Burst +# B4EF Heavy Slam +# B4F1 Grotesquerie +# B4F2 Lindwurm's Meteor +# B4F3 Downfall +# B4F4 Cosmic Kiss +# B4F6 Lindwurm's Dark II +# B4F7 Lindwurm's Stone III +# B4F8 Lindwurm's Glare +# B4FA Lindwurm's Thunder II +# B4FB Blood Mana +# B4FE Bloody Burst +# B500 Blood Wakening +# B501 Lindwurm's Water III +# B502 Lindwurm's Aero III +# B503 Straightforward Thunder II +# B504 Sideways Fire II +# B505 Mutating Cells +# B506 --sync-- +# B507 Dramatic Lysis +# B509 Idyllic Dream +# B50F Power Gusher +# B510 Power Gusher +# B511 Snaking Kick +# B512 Power Gusher +# B513 Power Gusher +# B514 Power Gusher +# B515 Snaking Kick +# B516 Power Gusher +# B517 Mana Burst +# B518 Mana Burst +# B519 Heavy Slam +# B51A Power Gusher +# B51C Temporal Curtain +# B51D --sync-- +# B51E --sync-- +# B51F Attack +# B520 Double Sobat +# B522 Double Sobat +# B524 Double Sobat +# B525 Double Sobat +# B526 Esoteric Finisher +# B527 Snaking Kick +# B528 Arcadia Aflame +# B529 Arcadian Arcanum +# B52B Netherworld Near +# B52D Wailing Wave +# B52E Netherwrath Near +# B530 Timeless Spite +# B533 Arcadian Hell +# B534 Arcadian Hell +# B535 Arcadian Hell +# B537 Arcadian Hell +# B8E1 Scalding Waves +# B922 Hemorrhagic Projection +# B9D9 Arcadian Arcanum +# BBE2 Twisted Vision +# BBE3 Mana Burst +# BCAF Snaking Kick +# BE5D Heavy Slam +# BE95 Snaking Kick +# BEC1 Arcadian Hell From 0c49083a618fac41c549e5851ae8d2d63ef50f9d Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 25 Jan 2026 00:57:59 -0500 Subject: [PATCH 048/139] p2 timeline up to reenactment 1 --- ui/raidboss/data/07-dt/raid/r12s.txt | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.txt b/ui/raidboss/data/07-dt/raid/r12s.txt index 2ee7c5ea3fb..0d58311133e 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.txt +++ b/ui/raidboss/data/07-dt/raid/r12s.txt @@ -341,7 +341,7 @@ hideall "--sync--" ### Phase 2: Lindwurm II # -p B528:3015.7 -# -ii B51F B4DA B4DB B4DD B4DF +# -ii B51F B4DA B4DB B4DD B4DF B4E3 B4E6 B4F0 B4E9 B4F1 B4EE # -it "Lindwurm" 3000.5 label "r12s-p2-start" 3010.7 "--sync--" StartsUsing { id: "B528", source: "Lindwurm" } window 3100,10 @@ -365,6 +365,21 @@ hideall "--sync--" 3091.2 "Staging" Ability { id: "B4E1", source: "Lindwurm" } 3102.2 "--sync--" Ability { id: "B4E2", source: "Lindwurm" } 3105.4 "Replication 2" Ability { id: "B4D8", source: "Lindwurm" } +3128.0 "Firefall Splash" Ability { id: "B4E4", source: "Lindwurm" } +3128.7 "Scalding Waves x4" Ability { id: "B4E5", source: "Lindwurm" } +3130.1 "Mana Burst x3" Ability { id: "B4E7", source: "Lindwurm" } +3135.5 "Heavy Slam x2" Ability { id: "B4E8", source: "Lindschrat" } +3136.7 "Grotesquerie x2" Ability { id: "B4EA", source: "Lindwurm" } +3137.3 "Hemorrhagic Projection x2" Ability { id: "B4EB", source: "Lindwurm" } +3141.1 "Snaking Kick" Ability { id: "B527", source: "Lindwurm" } +3151.3 "Reenactment 1" Ability { id: "B4EC", source: "Lindwurm" } +3159.4 "Firefall Splash" Ability { id: "B4ED", source: "Lindschrat" } +3159.4 "Netherwrath Near/Netherwrath Far" Ability { id: ["B52E", "B52F"], source: "Lindwurm" } +3160.6 "Timeless Spite" Ability { id: "B530", source: "Lindwurm" } +3160.8 "Scalding Waves x4" Ability { id: "B8E1", source: "Lindwurm" } +3164.4 "Grotesquerie x2" Ability { id: "B4EA", source: "Lindwurm" } +3164.4 "Mana Burst x3" Ability { id: "BBE3", source: "Lindwurm" } +3165.0 "Hemorrhagic Projection x2" Ability { id: "B922", source: "Lindwurm" } # TBD # IGNORED ABILITIES @@ -390,11 +405,16 @@ hideall "--sync--" # Phase 2 # B51F --sync--: Attack autos -# B51F --sync--: Attack autos # B4DA Winged Scourge: VFX E/W clones Facing S, Cleaving Front/Back (North/South) # B4DB Winged Scourge: VFX N/S clones Facing W, Cleaving Front/Back (East/West) # B4DD Top-tier Slam: VFX (cast that gives Fire Debuff) # B4DF Mighty Magic: VFX (cast that gives Dark Debuff) +# B4E3 Firefall Splash: VFX +# B4E6 Mana Burst: VFX +# B4F0 Unmitigated Impact: No one stacked in a Heavy Slam, causes 1035 Sustained Damage DoT +# B4E9 Grotesquerie: VFX +# B4F1 Grotesquerie: VFX used in Reenactment +# B4EE Mana Burst: VFX used in Reenactment # ALL ENCOUNTER ABILITIES # Phase 1 @@ -510,6 +530,7 @@ hideall "--sync--" # B4EC Reenactment # B4ED Firefall Splash # B4EE Mana Burst +# B4F0 Unmitigated Impact # B4EF Heavy Slam # B4F1 Grotesquerie # B4F2 Lindwurm's Meteor @@ -547,6 +568,7 @@ hideall "--sync--" # B51E --sync-- # B51F Attack # B520 Double Sobat +# B521 Double Sobat # B522 Double Sobat # B524 Double Sobat # B525 Double Sobat @@ -555,8 +577,10 @@ hideall "--sync--" # B528 Arcadia Aflame # B529 Arcadian Arcanum # B52B Netherworld Near +# B52C Netherworld Far # B52D Wailing Wave # B52E Netherwrath Near +# B52F Netherwrath Far # B530 Timeless Spite # B533 Arcadian Hell # B534 Arcadian Hell From e7db5d637313367dc834d520744897f993565dc0 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 25 Jan 2026 01:06:46 -0500 Subject: [PATCH 049/139] add replication 2 triggers up to reenactment --- ui/raidboss/data/07-dt/raid/r12s.ts | 386 +++++++++++++++++++++++++++- 1 file changed, 378 insertions(+), 8 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index dc329d58116..b6a8af4fe49 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -13,7 +13,9 @@ export type Phase = | 'slaughtershed' | 'replication1' | 'replication2' - | 'idyllic'; + | 'reenactment1' + | 'idyllic' + | 'reenactment2'; export interface Data extends RaidbossData { phase: Phase; @@ -33,9 +35,13 @@ export interface Data extends RaidbossData { hasRot: boolean; // Phase 2 actorPositions: { [id: string]: { x: number; y: number; heading: number } }; + replicationCounter: number; replication1Debuff?: 'fire' | 'dark'; replication1FireActor?: string; replication1FollowUp: boolean; + replication2TetherMap: { [dirNum: string]: string }; + replication2BossId?: string; + myReplication2Tether?: string; } const headMarkerData = { @@ -54,6 +60,12 @@ const headMarkerData = { // Phase 2 // VFX: sharelaser2tank5sec_c0k1, used by Double Sobat (B520) 'sharedTankbuster': '0256', + // Replication 2 Tethers + 'lockedTether': '0175', // Clone tethers + 'projectionTether': '016F', // Comes from Lindschrat, B4EA Grotesquerie + B4EB Hemorrhagic Projection cleave based on player facing + 'manaBurstTether': '0170', // Comes from Lindschrat, B4E7 Mana Burst defamation + 'heavySlamTether': '0171', // Comes from Lindschrat, B4E8 Heavy Slam stack with projection followup + 'fireballSplashTether': '0176', // Comes from the boss, B4E4 Fireball Splash baited jump } as const; const center = { @@ -82,7 +94,9 @@ const triggerSet: TriggerSet = { hasRot: false, // Phase 2 actorPositions: {}, + replicationCounter: 0, replication1FollowUp: false, + replication2TetherMap: {}, }), triggers: [ { @@ -100,20 +114,38 @@ const triggerSet: TriggerSet = { }, { id: 'R12S Phase Two Replication Tracker', - // B4D8 Replication happens more than once, only track the first one type: 'StartsUsing', netRegex: { id: 'B4D8', source: 'Lindwurm', capture: false }, - suppressSeconds: 9999, - run: (data) => data.phase = 'replication1', + run: (data) => { + if (data.replicationCounter === 0) + data.phase = 'replication1'; + data.replicationCounter = data.replicationCounter + 1; + }, }, { id: 'R12S Phase Two Staging Tracker', // B4E1 Staging happens more than once, only track the first one type: 'StartsUsing', - netRegex: { id: 'B4E1', source: 'Lindwurm', capture: false }, + netRegex: { id: 'B4E1', source: 'Lindwurm', capture: true }, condition: (data) => data.phase === 'replication1', suppressSeconds: 9999, - run: (data) => data.phase = 'replication2', + run: (data, matches) => { + data.phase = 'replication2'; + // Store the boss' id later for checking against tether + data.replication2BossId = matches.sourceId; + }, + }, + { + id: 'R12S Phase Two Reenactment Tracker', + type: 'StartsUsing', + netRegex: { id: 'B4EC', source: 'Lindwurm', capture: false }, + run: (data) => { + if (data.phase === 'replication1') { + data.phase = 'reenactment1'; + return; + } + data.phase = 'reenactment2'; + }, }, { id: 'R12S Phase Two ActorSetPos Tracker', @@ -155,6 +187,26 @@ const triggerSet: TriggerSet = { heading: parseFloat(matches.heading), }, }, + { + id: 'R12S Phase Two AddedCombatant Tracker', + type: 'AddedCombatant', + netRegex: { id: '4[0-9A-Fa-f]{7}', capture: true }, + condition: (data) => { + if ( + data.phase === 'replication1' || + data.phase === 'replication2' || + data.phase === 'idyllic' + ) + return true; + return false; + }, + run: (data, matches) => + data.actorPositions[matches.id] = { + x: parseFloat(matches.x), + y: parseFloat(matches.y), + heading: parseFloat(matches.heading), + }, + }, { id: 'R12S The Fixer', type: 'StartsUsing', @@ -1395,8 +1447,14 @@ const triggerSet: TriggerSet = { id: 'R12S Snaking Kick', type: 'StartsUsing', netRegex: { id: 'B527', source: 'Lindwurm', capture: false }, + condition: (data) => { + // Use Grotesquerie trigger for projection tethered players + const ability = data.myReplication2Tether; + if (ability === headMarkerData['projectionTether']) + return false; + return true; + }, response: Responses.getBehind(), - run: (data) => data.replication1FollowUp = true, }, { id: 'R12S Replication 1 Follow-up Tracker', @@ -1504,8 +1562,320 @@ const triggerSet: TriggerSet = { netRegex: { id: headMarkerData['sharedTankbuster'], capture: true }, response: Responses.sharedTankBuster(), }, + { + id: 'R12S Replication 2 Tethered Clone', + // Combatants are added ~4s before Staging starts casting + // Same tether ID is used for "locked" ability tethers + type: 'Tether', + netRegex: { id: headMarkerData['lockedTether'], capture: true }, + condition: Conditions.targetIsYou(), + suppressSeconds: 9999, + infoText: (data, matches, output) => { + const actor = data.actorPositions[matches.sourceId]; + if (actor === undefined) + return output.cloneTether!(); + + const dirNum = Directions.xyTo8DirNum(actor.x, actor.y, center.x, center.y); + const dir = Directions.output8Dir[dirNum] ?? 'unknown'; + return output.cloneTetherDir!({ dir: output[dir]!() }); + }, + outputStrings: { + ...Directions.outputStrings8Dir, + cloneTether: { + en: 'Tethered to Clone', + }, + cloneTetherDir: { + en: 'Tethered to ${dir} Clone', + }, + }, + }, + { + id: 'R12S Replication 2 Ability Tethers Collect', + // Record and store a map of where the tethers come from and what they do for later + // Boss tether handled separately since boss can move around + type: 'Tether', + netRegex: { + id: [ + headMarkerData['projectionTether'], + headMarkerData['manaBurstTether'], + headMarkerData['heavySlamTether'], + ], + capture: true, + }, + condition: (data) => data.phase === 'replication2', + run: (data, matches) => { + const actor = data.actorPositions[matches.sourceId]; + if (actor === undefined) + return; + const dirNum = Directions.xyTo8DirNum(actor.x, actor.y, center.x, center.y); + data.replication2TetherMap[dirNum] = matches.id; + }, + }, + { + id: 'R12S Replication 2 Ability Tethers Initial Call', + // Occur ~8s after end of Replication 2 cast + type: 'Tether', + netRegex: { + id: [ + headMarkerData['projectionTether'], + headMarkerData['manaBurstTether'], + headMarkerData['heavySlamTether'], + headMarkerData['fireballSplashTether'], + ], + capture: true, + }, + condition: Conditions.targetIsYou(), + suppressSeconds: 9999, // Can get spammy if players have more than 1 tether or swap a lot + infoText: (_data, matches, output) => { + switch (matches.id) { + case headMarkerData['projectionTether']: + return output.projectionTether!(); + case headMarkerData['manaBurstTether']: + return output.manaBurstTether!(); + case headMarkerData['heavySlamTether']: + return output.heavySlamTether!(); + } + return output.fireballSplashTether!(); + }, + outputStrings: { + ...Directions.outputStrings8Dir, + projectionTether: { + en: 'Projection Tether on YOU', + }, + manaBurstTether: { + en: 'Defamation Tether on YOU', + }, + heavySlamTether: { + en: 'Stack Tether on YOU', + }, + fireballSplashTether: { + en: 'Boss Tether on YOU', + }, + }, + }, + { + id: 'R12S Replication 2 Locked Tether 2 Collect', + type: 'Tether', + netRegex: { id: headMarkerData['lockedTether'], capture: true }, + condition: (data, matches) => { + return data.replicationCounter === 2 && data.me === matches.target; + }, + run: (data, matches) => { + // Check if boss tether + if (data.replication2BossId === matches.sourceId) { + data.myReplication2Tether = headMarkerData['fireballSplashTether']; + return; + } + + const actor = data.actorPositions[matches.sourceId]; + if (actor === undefined) { + // Setting to use that we know we have a tether but couldn't determine what ability it is + data.myReplication2Tether = 'unknown'; + return; + } + + const dirNum = Directions.xyTo8DirNum( + actor.x, + actor.y, + center.x, + center.y, + ); + + // Lookup what the tether was at the same location + const ability = data.replication2TetherMap[dirNum]; + if (ability === undefined) { + // Setting to use that we know we have a tether but couldn't determine what ability it is + data.myReplication2Tether = 'unknown'; + return; + } + data.myReplication2Tether = ability; + } + }, + { + id: 'R12S Replication 2 Locked Tether 2', + type: 'Tether', + netRegex: { id: headMarkerData['lockedTether'], capture: true }, + condition: (data, matches) => { + return data.replicationCounter === 2 && data.me === matches.target; + }, + delaySeconds: 0.1, + infoText: (data, matches, output) => { + // Check if it's the boss + if (data.replication2BossId === matches.sourceId) + return output.fireballSplashTether!({ + mech1: output.baitJump!(), + mech2: output.stackGroups!(), + }); + + switch (data.myReplication2Tether) { + case headMarkerData['projectionTether']: + return output.projectionTether!({ + mech1: output.baitProtean!(), + mech2: output.stackGroups!(), + }); + case headMarkerData['manaBurstTether']: + return output.manaBurstTether!({ + mech1: output.defamationOnYou!(), + mech2: output.stackGroups!(), + }); + case headMarkerData['heavySlamTether']: + return output.heavySlamTether!({ + mech1: output.baitProtean!(), + mech2: output.stackGroups!(), + }); + } + }, + outputStrings: { + defamationOnYou: Outputs.defamationOnYou, + stackGroups: { + en: 'Stack Groups', + de: 'Gruppen-Sammeln', + fr: 'Package en groupes', + ja: '組み分け頭割り', + cn: '分组分摊', + ko: '그룹별 쉐어', + tc: '分組分攤', + }, + baitProtean: { + en: 'Bait Protean from Boss', + }, + baitJump: { + en: 'Bait Boss Jump', + }, + projectionTether: { + en: '${mech1} => ${mech2}', + }, + manaBurstTether: { + en: '${mech1} => ${mech2}', + }, + heavySlamTether: { + en: '${mech1} => ${mech2}', + }, + fireballSplashTether: { + en: '${mech1} => ${mech2}', + }, + }, + }, + { + id: 'R12S Replication 2 Mana Burst Target', + // A player without a tether will be target for defamation + type: 'Tether', + netRegex: { id: headMarkerData['lockedTether'], capture: false }, + condition: (data) => { + return data.replicationCounter === 2; + }, + delaySeconds: 0.1, + suppressSeconds: 1, + infoText: (data, _matches, output) => { + if (data.myReplication2Tether !== undefined) + return; + return output.noTether!({ + mech1: output.defamationOnYou!(), + mech2: output.stackGroups!(), + }); + }, + outputStrings: { + defamationOnYou: Outputs.defamationOnYou, + stackGroups: { + en: 'Stack Groups', + de: 'Gruppen-Sammeln', + fr: 'Package en groupes', + ja: '組み分け頭割り', + cn: '分组分摊', + ko: '그룹별 쉐어', + tc: '分組分攤', + }, + noTether: { + en: '${mech1} => ${mech2}', + }, + }, + }, + { + id: 'R12S Heavy Slam', + // After B4E7 Mana Burst, Groups must stack up on the heavy slam targetted players + type: 'Ability', + netRegex: { id: 'B4E7', source: 'Lindwurm', capture: false }, + suppressSeconds: 1, + alertText: (data, _matches, output) => { + const ability = data.myReplication2Tether; + switch (ability) { + case headMarkerData['projectionTether']: + return output.projectionTether!({ + mech1: output.stackGroups!(), + mech2: output.lookAway!(), + mech3: output.getBehind!(), + }); + case headMarkerData['manaBurstTether']: + return output.manaBurstTether!({ + mech1: output.stackGroups!(), + mech2: output.getBehind!(), + }); + case headMarkerData['heavySlamTether']: + return output.heavySlamTether!({ + mech1: output.stackGroups!(), + mech2: output.getBehind!(), + }); + case headMarkerData['fireballSplashTether']: + return output.fireballSplashTether!({ + mech1: output.stackGroups!(), + mech2: output.getBehind!(), + }); + } + return output.noTether!({ + mech1: output.stackGroups!(), + mech2: output.getBehind!(), + }); + }, + outputStrings: { + getBehind: Outputs.getBehind, + lookAway: Outputs.lookAway, + stackGroups: { + en: 'Stack Groups', + de: 'Gruppen-Sammeln', + fr: 'Package en groupes', + ja: '組み分け頭割り', + cn: '分组分摊', + ko: '그룹별 쉐어', + tc: '分組分攤', + }, + stackOnYou: Outputs.stackOnYou, + projectionTether: { + en: '${mech1} + ${mech2} => ${mech3}', + }, + manaBurstTether: { + en: '${mech1} => ${mech2}', + }, + heavySlamTether: { + en: '${mech1} => ${mech2}', + }, + fireballSplashTether: { + en: '${mech1} => ${mech2}', + }, + noTether: { + en: '${mech1} => ${mech2}', + }, + }, + }, + { + id: 'R12S Grotesquerie', + // This seems to be the point at which the look for the Snaking Kick is snapshot + // The VFX B4E9 happens ~0.6s before Snaking Kick + // B4EA has the targetted player in it + // B4EB Hemorrhagic Projection conal aoe goes off ~0.5s after in the direction the player was facing + type: 'Ability', + netRegex: { id: 'B4EA', source: 'Lindwurm', capture: true }, + condition: Conditions.targetIsYou(), + response: Responses.getBehind(), + }, + ], + timelineReplace: [ + { + 'locale': 'en', + 'replaceText': { + 'Netherwrath Near/Netherwrath Far': 'Netherwrath Near/Far', + }, + }, ], - timelineReplace: [], }; export default triggerSet; From 6cab7ead8ba6203299e72d93c0a610c1d05f4669 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 25 Jan 2026 01:09:54 -0500 Subject: [PATCH 050/139] lint --- ui/raidboss/data/07-dt/raid/r12s.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index b6a8af4fe49..482b066639a 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1689,7 +1689,7 @@ const triggerSet: TriggerSet = { return; } data.myReplication2Tether = ability; - } + }, }, { id: 'R12S Replication 2 Locked Tether 2', From 2b265f71850288a40fccdf9305b2f64fb47654bd Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 25 Jan 2026 02:00:17 -0500 Subject: [PATCH 051/139] add Netherwrath Near/Far This mechanic could probably be done several ways, but for consistency I think basing on the tether the player had is a start. Otherwise a default would probably just say to everyone Stacks out, Proteans in or vice versa. --- ui/raidboss/data/07-dt/raid/r12s.ts | 83 +++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 482b066639a..65a71219c8c 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1867,6 +1867,89 @@ const triggerSet: TriggerSet = { condition: Conditions.targetIsYou(), response: Responses.getBehind(), }, + { + id: 'R12S Netherwrath Near/Far', + type: 'StartsUsing', + netRegex: { id: ['B52E', 'B52F'], source: 'Lindwurm', capture: true }, + infoText: (data, matches, output) => { + const ability = data.myReplication2Tether; + // For telling player in relation to their bait + const spiteBaits = matches.id === 'B52E' ? 'near' : 'far'; + const proteanBaits = matches.id === 'B52E' ? 'far' : 'near'; + + // For telling player in relation to what others will bait + const avoidProtean = matches.id === 'B52E' ? 'out' : 'in'; + const avoidSpite = matches.id === 'B52E' ? 'in' : 'out'; + + switch (ability) { + case headMarkerData['projectionTether']: + return output.projectionTether!({ + proteanBaits: output[proteanBaits]!(), + mech1: output.scaldingWave!(), + mech2: output.stacks!(), + spiteBaits: output[avoidSpite]!(), + }); + case headMarkerData['manaBurstTether']: + return output.manaBurstTether!({ + spiteBaits: output[spiteBaits]!(), + mech1: output.timelessSpite!(), + mech2: output.proteans!(), + proteanBaits: output[avoidProtean]!(), + }); + case headMarkerData['heavySlamTether']: + return output.heavySlamTether!({ + proteanBaits: output[proteanBaits]!(), + mech1: output.scaldingWave!(), + mech2: output.stacks!(), + spiteBaits: output[avoidSpite]!(), + }); + case headMarkerData['fireballSplashTether']: + return output.fireballSplashTether!({ + spiteBaits: output[spiteBaits]!(), + mech1: output.timelessSpite!(), + mech2: output.proteans!(), + proteanBaits: output[avoidProtean]!(), + }); + } + return output.noTether!({ + spiteBaits: output[spiteBaits]!(), + mech1: output.timelessSpite!(), + mech2: output.proteans!(), + proteanBaits: output[avoidProtean]!(), + }); + }, + outputStrings: { + scaldingWave: Outputs.protean, + timelessSpite: Outputs.stackPartner, + stacks: Outputs.stacks, + proteans: { + en: 'Proteans', + }, + near: { + en: 'Be In', + }, + in: Outputs.in, + far: { + en: 'Be Out', + }, + out: Outputs.out, + projectionTether: { + en: '${proteanBaits} + ${mech1} (${mech2} ${spiteBaits})', + }, + manaBurstTether: { + en: '${spiteBaits} + ${mech1} (${mech2} ${proteanBaits})', + }, + heavySlamTether: { + en: '${proteanBaits} + ${mech1} (${mech2} ${spiteBaits})', + }, + fireballSplashTether: { + en: '${spiteBaits} + ${mech1} (${mech2} ${proteanBaits})', + }, + noTether: { + en: '${spiteBaits} + ${mech1} (${mech2} ${proteanBaits})', + }, + }, + }, ], timelineReplace: [ { From a78d8279e60f5cb5d904fea2d80f8e165737a1b9 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 25 Jan 2026 02:22:41 -0500 Subject: [PATCH 052/139] adjust wording for netherwrath and jump bait --- ui/raidboss/data/07-dt/raid/r12s.ts | 32 +++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 65a71219c8c..5f5da5f84e6 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1740,7 +1740,8 @@ const triggerSet: TriggerSet = { en: 'Bait Protean from Boss', }, baitJump: { - en: 'Bait Boss Jump', + // This must be north to resolve later Netherwrath mechanic + en: 'Bait Jump North', }, projectionTether: { en: '${mech1} => ${mech2}', @@ -1869,17 +1870,18 @@ const triggerSet: TriggerSet = { }, { id: 'R12S Netherwrath Near/Far', + // Boss jumps onto north clone (Firefall Splash mechanic), aoe around the clone + proteans type: 'StartsUsing', netRegex: { id: ['B52E', 'B52F'], source: 'Lindwurm', capture: true }, infoText: (data, matches, output) => { const ability = data.myReplication2Tether; // For telling player in relation to their bait - const spiteBaits = matches.id === 'B52E' ? 'near' : 'far'; - const proteanBaits = matches.id === 'B52E' ? 'far' : 'near'; + const spiteBaits = matches.id === 'B52E' ? 'beNear' : 'beFar'; + const proteanBaits = matches.id === 'B52E' ? 'beFar' : 'beNear'; // For telling player in relation to what others will bait - const avoidProtean = matches.id === 'B52E' ? 'out' : 'in'; - const avoidSpite = matches.id === 'B52E' ? 'in' : 'out'; + const avoidProtean = matches.id === 'B52E' ? 'far' : 'near'; + const avoidSpite = matches.id === 'B52E' ? 'near' : 'far'; switch (ability) { case headMarkerData['projectionTether']: @@ -1925,14 +1927,26 @@ const triggerSet: TriggerSet = { proteans: { en: 'Proteans', }, + beNear: { + en: 'Be Near', + }, + beFar: { + en: 'Be Far', + }, near: { - en: 'Be In', + en: 'Near', + de: 'Nah', + fr: 'Proche', + cn: '近', + ko: '가까이', }, - in: Outputs.in, far: { - en: 'Be Out', + en: 'Far', + de: 'Fern', + fr: 'Loin', + cn: '远', + ko: '멀리', }, - out: Outputs.out, projectionTether: { en: '${proteanBaits} + ${mech1} (${mech2} ${spiteBaits})', }, From 90128e7c64bbff04162708b22d346665262b33d1 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 25 Jan 2026 03:14:21 -0500 Subject: [PATCH 053/139] use drawIn insterad of knockback Co-authored-by: xiashtra <91220277+xiashtra@users.noreply.github.com> --- ui/raidboss/data/07-dt/raid/r12s.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 5f5da5f84e6..614444eb2d8 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -652,14 +652,14 @@ const triggerSet: TriggerSet = { }, }, { - id: 'R12S Cursed Coil Bind Knocbkack', + id: 'R12S Cursed Coil Bind Draw-in', // Using Phagocyte Spotlight, 1st one happens 7s before bind // Delayed additionally to reduce overlap with alpha tower location calls type: 'Ability', netRegex: { id: 'B4B6', capture: false }, delaySeconds: 3, // 5s warning suppressSeconds: 10, - response: Responses.knockback(), + response: Responses.drawIn(), }, { id: 'R12S Skinsplitter Counter', From 9a050138833e5f5e5126d6b17c85100ede6e8c37 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 25 Jan 2026 03:29:25 -0500 Subject: [PATCH 054/139] fix p2 jump --- ui/raidboss/data/07-dt/raid/r12s.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.txt b/ui/raidboss/data/07-dt/raid/r12s.txt index 0d58311133e..ae9bd9eae76 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.txt +++ b/ui/raidboss/data/07-dt/raid/r12s.txt @@ -11,7 +11,7 @@ hideall "--sync--" # -it "Lindwurm" 0.0 "--sync--" InCombat { inGameCombat: "1" } window 0,1 -1.0 "--sync--" AddedCombatant { npcNameId: "14378", name: "Lindwurm", job: "00", level: "1", ownerId: "0{4}", worldId: "00" } window 10,3 jump "r12s-p2-start" # Sync to P2 immediately through AddCombatant. +1.0 "--sync--" AddedCombatant { npcNameId: "14380", name: "Lindschrat", job: "00", level: "64", ownerId: "0{4}", worldId: "00" } window 10,3 jump "r12s-p2-start" # Sync to P2 immediately through AddCombatant. 15.6 "The Fixer" Ability { id: "B4D7", source: "Lindwurm" } window 20,10 25.8 "--sync--" Ability { id: "B7C4", source: "Lindwurm" } 40.9 "Mortal Slayer 1" Ability { id: ["B496", "B498"], source: "Lindwurm" } From 936de78c2f7dcc3770f6901ba1829a0fb2c25906 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 25 Jan 2026 03:34:16 -0500 Subject: [PATCH 055/139] remove comment about north --- ui/raidboss/data/07-dt/raid/r12s.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 614444eb2d8..5b4121726b6 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1740,8 +1740,7 @@ const triggerSet: TriggerSet = { en: 'Bait Protean from Boss', }, baitJump: { - // This must be north to resolve later Netherwrath mechanic - en: 'Bait Jump North', + en: 'Bait Jump', }, projectionTether: { en: '${mech1} => ${mech2}', @@ -1870,7 +1869,7 @@ const triggerSet: TriggerSet = { }, { id: 'R12S Netherwrath Near/Far', - // Boss jumps onto north clone (Firefall Splash mechanic), aoe around the clone + proteans + // Boss jumps onto clone of player that took Firefall Splash, there is an aoe around the clone + proteans type: 'StartsUsing', netRegex: { id: ['B52E', 'B52F'], source: 'Lindwurm', capture: true }, infoText: (data, matches, output) => { From a23a49a183c3a202467c22268cc1f9a94af14c92 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 25 Jan 2026 04:29:26 -0500 Subject: [PATCH 056/139] add basic Blood Mana/Blood Awakening triggers --- ui/raidboss/data/07-dt/raid/r12s.ts | 103 ++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 5b4121726b6..d157e3fc744 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -42,6 +42,7 @@ export interface Data extends RaidbossData { replication2TetherMap: { [dirNum: string]: string }; replication2BossId?: string; myReplication2Tether?: string; + myMutation?: 'alpha' | 'beta'; } const headMarkerData = { @@ -1963,6 +1964,108 @@ const triggerSet: TriggerSet = { }, }, }, + { + id: 'R12S Mutation α/β Collect', + // Used in Blood Mana / Blood Awakening Mechanics + // 12A1 Mutation α: Don't get hit + // 12A3 Mutation β: Get Hit + // Players will get opposite debuff after Blood Mana + type: 'GainsEffect', + netRegex: { effectId: ['12A1', '12A3'], capture: true }, + condition: Conditions.targetIsYou(), + run: (data, matches) => { + data.myMutation = matches.effectId === '12A1' ? 'alpha' : 'beta'; + }, + }, + { + id: 'R12S Mutation α/β', + type: 'GainsEffect', + netRegex: { effectId: ['12A1', '12A3'], capture: true }, + condition: Conditions.targetIsYou(), + infoText: (_data, matches, output) => { + if (matches.effectId === '12A1') + return output.alpha!(); + return output.beta!(); + }, + outputStrings: { + alpha: { + en: 'Mutation α on YOU', + }, + beta: { + en: 'Mutation β on YOU', + }, + }, + }, + { + id: 'R12S Blood Mana', + // Black Holes and shapes + // TODO: Tell what shape to pop + which Black Hole mechanics and side? + type: 'Ability', + netRegex: { id: 'B4FB', source: 'Lindwurm', capture: false }, + infoText: (data, _matches, output) => { + if (data.myMutation === 'alpha') + return output.alpha!(); + return output.beta!(); + }, + outputStrings: { + alpha: { + en: 'Avoid Shape AoEs, Wait by Black Hole', + }, + beta: { + en: 'Shared Shape Soak => Get by Black Hole', + }, + }, + }, + { + id: 'R12S Blood Wakening Followup', + // Run to the other Black Hole after abilities go off + // B501 Lindwurm's Water III + // B502 Lindwurm's Aero III + // B503 Straightforward Thunder II + // B504 Sideways Fire II + // TODO: Tell which Black Hole and its mechanics? + type: 'StartsUsing', + netRegex: { id: ['B501', 'B502', 'B503', 'B504'], source: 'Lindwurm', capture: false }, + suppressSeconds: 9999, + alertText: (_data, _matches, output) => output.move!(), + outputStrings: { + move: { + en: 'Move to other Black Hole', + }, + }, + }, + { + id: 'R12S Netherworld Near/Far', + type: 'StartsUsing', + netRegex: { id: ['B52B', 'B52C'], source: 'Lindwurm', capture: true }, + alertText: (data, matches, output) => { + if (matches.id === 'B52B') + return data.myMutation === 'beta' + ? output.betaNear!({ mech: output.getUnder!() }) + : output.alphaNear!({ mech: output.maxMelee!() }); + return data.myMutation === 'beta' + ? output.betaFar!({ mech: output.maxMelee!() }) + : output.alphaFar!({ mech: output.getUnder!() }); + }, + outputStrings: { + getUnder: Outputs.getUnder, + maxMelee: { + en: 'Max Melee', + }, + alphaNear: { + en: '${mech} (Avoid Near Stack)', + }, + alphaFar: { + en: '${mech} (Avoid Far Stack)', + }, + betaNear: { + en: 'Near β Stack: ${mech}', + }, + betaFar: { + en: 'Far β Stack: ${mech}', + }, + }, + }, ], timelineReplace: [ { From d4a93979d316525edffa231c7ade2905f3b5bd09 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 25 Jan 2026 04:42:57 -0500 Subject: [PATCH 057/139] breakout netherwrath into near/far outputs May make it for easier to add configs later. --- ui/raidboss/data/07-dt/raid/r12s.ts | 101 +++++++++++++++++++++------- 1 file changed, 75 insertions(+), 26 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index d157e3fc744..61b3ba3ae55 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1875,49 +1875,83 @@ const triggerSet: TriggerSet = { netRegex: { id: ['B52E', 'B52F'], source: 'Lindwurm', capture: true }, infoText: (data, matches, output) => { const ability = data.myReplication2Tether; - // For telling player in relation to their bait - const spiteBaits = matches.id === 'B52E' ? 'beNear' : 'beFar'; - const proteanBaits = matches.id === 'B52E' ? 'beFar' : 'beNear'; + const isNear = matches.id === 'B52E'; - // For telling player in relation to what others will bait - const avoidProtean = matches.id === 'B52E' ? 'far' : 'near'; - const avoidSpite = matches.id === 'B52E' ? 'near' : 'far'; + if (isNear) { + switch (ability) { + case headMarkerData['projectionTether']: + return output.projectionTetherNear!({ + proteanBaits: output.beFar!(), + mech1: output.scaldingWave!(), + mech2: output.stacks!(), + spiteBaits: output.near!(), + }); + case headMarkerData['manaBurstTether']: + return output.manaBurstTetherNear!({ + spiteBaits: output.beNear!(), + mech1: output.timelessSpite!(), + mech2: output.proteans!(), + proteanBaits: output.far!(), + }); + case headMarkerData['heavySlamTether']: + return output.heavySlamTetherNear!({ + proteanBaits: output.beFar!(), + mech1: output.scaldingWave!(), + mech2: output.stacks!(), + spiteBaits: output.near!(), + }); + case headMarkerData['fireballSplashTether']: + return output.fireballSplashTetherNear!({ + spiteBaits: output.beNear!(), + mech1: output.timelessSpite!(), + mech2: output.proteans!(), + proteanBaits: output.far!(), + }); + } + return output.noTetherNear!({ + spiteBaits: output.beNear!(), + mech1: output.timelessSpite!(), + mech2: output.proteans!(), + proteanBaits: output.far!(), + }); + } + // Netherwrath Far switch (ability) { case headMarkerData['projectionTether']: - return output.projectionTether!({ - proteanBaits: output[proteanBaits]!(), + return output.projectionTetherFar!({ + proteanBaits: output.beNear!(), mech1: output.scaldingWave!(), mech2: output.stacks!(), - spiteBaits: output[avoidSpite]!(), + spiteBaits: output.far!(), }); case headMarkerData['manaBurstTether']: - return output.manaBurstTether!({ - spiteBaits: output[spiteBaits]!(), + return output.manaBurstTetherFar!({ + spiteBaits: output.beFar!(), mech1: output.timelessSpite!(), mech2: output.proteans!(), - proteanBaits: output[avoidProtean]!(), + proteanBaits: output.near!(), }); case headMarkerData['heavySlamTether']: - return output.heavySlamTether!({ - proteanBaits: output[proteanBaits]!(), + return output.heavySlamTetherFar!({ + proteanBaits: output.beNear!(), mech1: output.scaldingWave!(), mech2: output.stacks!(), - spiteBaits: output[avoidSpite]!(), + spiteBaits: output.far!(), }); case headMarkerData['fireballSplashTether']: - return output.fireballSplashTether!({ - spiteBaits: output[spiteBaits]!(), + return output.fireballSplashTetherFar!({ + spiteBaits: output.beFar!(), mech1: output.timelessSpite!(), mech2: output.proteans!(), - proteanBaits: output[avoidProtean]!(), + proteanBaits: output.near!(), }); } - return output.noTether!({ - spiteBaits: output[spiteBaits]!(), + return output.noTetherFar!({ + spiteBaits: output.beFar!(), mech1: output.timelessSpite!(), mech2: output.proteans!(), - proteanBaits: output[avoidProtean]!(), + proteanBaits: output.near!(), }); }, outputStrings: { @@ -1947,19 +1981,34 @@ const triggerSet: TriggerSet = { cn: '远', ko: '멀리', }, - projectionTether: { + projectionTetherFar: { en: '${proteanBaits} + ${mech1} (${mech2} ${spiteBaits})', }, - manaBurstTether: { + manaBurstTetherFar: { en: '${spiteBaits} + ${mech1} (${mech2} ${proteanBaits})', }, - heavySlamTether: { + heavySlamTetherFar: { en: '${proteanBaits} + ${mech1} (${mech2} ${spiteBaits})', }, - fireballSplashTether: { + fireballSplashTetherFar: { en: '${spiteBaits} + ${mech1} (${mech2} ${proteanBaits})', }, - noTether: { + noTetherFar: { + en: '${spiteBaits} + ${mech1} (${mech2} ${proteanBaits})', + }, + projectionTetherNear: { + en: '${proteanBaits} + ${mech1} (${mech2} ${spiteBaits})', + }, + manaBurstTetherNear: { + en: '${spiteBaits} + ${mech1} (${mech2} ${proteanBaits})', + }, + heavySlamTetherNear: { + en: '${proteanBaits} + ${mech1} (${mech2} ${spiteBaits})', + }, + fireballSplashTetherNear: { + en: '${spiteBaits} + ${mech1} (${mech2} ${proteanBaits})', + }, + noTetherNear: { en: '${spiteBaits} + ${mech1} (${mech2} ${proteanBaits})', }, }, From 9b9ac7fd8a9bd57634d11af3017b26aa970c1405 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 25 Jan 2026 04:48:20 -0500 Subject: [PATCH 058/139] change wording for initial tether calls and locked tethers --- ui/raidboss/data/07-dt/raid/r12s.ts | 71 ++++++++++------------------- 1 file changed, 24 insertions(+), 47 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 61b3ba3ae55..a4d9c3cd73e 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1641,7 +1641,7 @@ const triggerSet: TriggerSet = { outputStrings: { ...Directions.outputStrings8Dir, projectionTether: { - en: 'Projection Tether on YOU', + en: 'Cone Tether on YOU', }, manaBurstTether: { en: 'Defamation Tether on YOU', @@ -1693,67 +1693,44 @@ const triggerSet: TriggerSet = { }, }, { - id: 'R12S Replication 2 Locked Tether 2', + id: 'R12S Replication 2 Ability Tethers Initial Call', + // Occur ~8s after end of Replication 2 cast type: 'Tether', - netRegex: { id: headMarkerData['lockedTether'], capture: true }, - condition: (data, matches) => { - return data.replicationCounter === 2 && data.me === matches.target; + netRegex: { + id: [ + headMarkerData['projectionTether'], + headMarkerData['manaBurstTether'], + headMarkerData['heavySlamTether'], + headMarkerData['fireballSplashTether'], + ], + capture: true, }, - delaySeconds: 0.1, - infoText: (data, matches, output) => { - // Check if it's the boss - if (data.replication2BossId === matches.sourceId) - return output.fireballSplashTether!({ - mech1: output.baitJump!(), - mech2: output.stackGroups!(), - }); - - switch (data.myReplication2Tether) { + condition: Conditions.targetIsYou(), + suppressSeconds: 9999, // Can get spammy if players have more than 1 tether or swap a lot + infoText: (_data, matches, output) => { + switch (matches.id) { case headMarkerData['projectionTether']: - return output.projectionTether!({ - mech1: output.baitProtean!(), - mech2: output.stackGroups!(), - }); + return output.projectionTether!(); case headMarkerData['manaBurstTether']: - return output.manaBurstTether!({ - mech1: output.defamationOnYou!(), - mech2: output.stackGroups!(), - }); + return output.manaBurstTether!(); case headMarkerData['heavySlamTether']: - return output.heavySlamTether!({ - mech1: output.baitProtean!(), - mech2: output.stackGroups!(), - }); + return output.heavySlamTether!(); } + return output.fireballSplashTether!(); }, outputStrings: { - defamationOnYou: Outputs.defamationOnYou, - stackGroups: { - en: 'Stack Groups', - de: 'Gruppen-Sammeln', - fr: 'Package en groupes', - ja: '組み分け頭割り', - cn: '分组分摊', - ko: '그룹별 쉐어', - tc: '分組分攤', - }, - baitProtean: { - en: 'Bait Protean from Boss', - }, - baitJump: { - en: 'Bait Jump', - }, + ...Directions.outputStrings8Dir, projectionTether: { - en: '${mech1} => ${mech2}', + en: 'Cone Tether on YOU', }, manaBurstTether: { - en: '${mech1} => ${mech2}', + en: 'Defamation Tether on YOU', }, heavySlamTether: { - en: '${mech1} => ${mech2}', + en: 'Stack Tether on YOU', }, fireballSplashTether: { - en: '${mech1} => ${mech2}', + en: 'Boss Tether on YOU', }, }, }, From 058d45696669feeb6d0f01aa7691adbf80863e80 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 25 Jan 2026 04:51:34 -0500 Subject: [PATCH 059/139] fix copy/paste error --- ui/raidboss/data/07-dt/raid/r12s.ts | 65 +++++++++++++++++++---------- 1 file changed, 42 insertions(+), 23 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index a4d9c3cd73e..18c3d11b1ec 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1693,44 +1693,63 @@ const triggerSet: TriggerSet = { }, }, { - id: 'R12S Replication 2 Ability Tethers Initial Call', - // Occur ~8s after end of Replication 2 cast + id: 'R12S Replication 2 Locked Tether 2', type: 'Tether', - netRegex: { - id: [ - headMarkerData['projectionTether'], - headMarkerData['manaBurstTether'], - headMarkerData['heavySlamTether'], - headMarkerData['fireballSplashTether'], - ], - capture: true, + netRegex: { id: headMarkerData['lockedTether'], capture: true }, + condition: (data, matches) => { + return data.replicationCounter === 2 && data.me === matches.target; }, - condition: Conditions.targetIsYou(), - suppressSeconds: 9999, // Can get spammy if players have more than 1 tether or swap a lot - infoText: (_data, matches, output) => { - switch (matches.id) { + delaySeconds: 0.1, + infoText: (data, matches, output) => { + // Check if it's the boss + if (data.replication2BossId === matches.sourceId) + return output.fireballSplashTether!({ + mech1: output.baitJump!(), + }); + + switch (data.myReplication2Tether) { case headMarkerData['projectionTether']: - return output.projectionTether!(); + return output.projectionTether!({ + mech1: output.baitProtean!(), + }); case headMarkerData['manaBurstTether']: - return output.manaBurstTether!(); + return output.manaBurstTether!({ + mech1: output.defamationOnYou!(), + }); case headMarkerData['heavySlamTether']: - return output.heavySlamTether!(); + return output.heavySlamTether!({ + mech1: output.baitProtean!(), + }); } - return output.fireballSplashTether!(); }, outputStrings: { - ...Directions.outputStrings8Dir, + defamationOnYou: Outputs.defamationOnYou, + stackGroups: { + en: 'Stack Groups', + de: 'Gruppen-Sammeln', + fr: 'Package en groupes', + ja: '組み分け頭割り', + cn: '分组分摊', + ko: '그룹별 쉐어', + tc: '分組分攤', + }, + baitProtean: { + en: 'Bait Protean from Boss', + }, + baitJump: { + en: 'Bait Jump', + }, projectionTether: { - en: 'Cone Tether on YOU', + en: 'Cone Tether: ${mech1}', }, manaBurstTether: { - en: 'Defamation Tether on YOU', + en: 'Defamation Tether: ${mech1}', }, heavySlamTether: { - en: 'Stack Tether on YOU', + en: 'Stack Tether: ${mech1}', }, fireballSplashTether: { - en: 'Boss Tether on YOU', + en: 'Boss Tether: ${mech1}', }, }, }, From 565f3c941864ebc46981bf12039238f38332b623 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 25 Jan 2026 04:56:18 -0500 Subject: [PATCH 060/139] add p2 idyllic dream bigAoe --- ui/raidboss/data/07-dt/raid/r12s.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 18c3d11b1ec..ce39f0cd7fc 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -2111,6 +2111,13 @@ const triggerSet: TriggerSet = { }, }, }, + { + id: 'R12S Idyllic Dream', + type: 'StartsUsing', + netRegex: { id: 'B509', source: 'Lindwurm', capture: false }, + durationSeconds: 4.7, + response: Responses.bigAoe('alert'), + }, ], timelineReplace: [ { From 60aa9d2232811b11a021d8ba04c3f547721dbbf6 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 25 Jan 2026 06:05:55 -0500 Subject: [PATCH 061/139] add direction to initial ability tethers and locked tethers --- ui/raidboss/data/07-dt/raid/r12s.ts | 103 +++++++++++++++++++++++----- 1 file changed, 86 insertions(+), 17 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index ce39f0cd7fc..cf51bb7e1a5 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -113,6 +113,14 @@ const triggerSet: TriggerSet = { data.phase = phase; }, }, + { + id: 'R12S Phase Two Staging Tracker', + // Due to the way the combatants are added in prior to the cast of Staging, this is used to set the phase + type: 'AddedCombatant', + netRegex: { name: 'Understudy', capture: false }, + condition: (data) => data.phase === 'replication1', + run: (data) => data.phase = 'replication2', + }, { id: 'R12S Phase Two Replication Tracker', type: 'StartsUsing', @@ -124,17 +132,14 @@ const triggerSet: TriggerSet = { }, }, { - id: 'R12S Phase Two Staging Tracker', - // B4E1 Staging happens more than once, only track the first one + id: 'R12S Phase Two Boss ID Collect', + // Store the boss' id later for checking against tether + // Using first B4E1 Staging type: 'StartsUsing', netRegex: { id: 'B4E1', source: 'Lindwurm', capture: true }, - condition: (data) => data.phase === 'replication1', + condition: (data) => data.phase === 'replication2', suppressSeconds: 9999, - run: (data, matches) => { - data.phase = 'replication2'; - // Store the boss' id later for checking against tether - data.replication2BossId = matches.sourceId; - }, + run: (data, matches) => data.replication2BossId = matches.sourceId, }, { id: 'R12S Phase Two Reenactment Tracker', @@ -1627,28 +1632,56 @@ const triggerSet: TriggerSet = { }, condition: Conditions.targetIsYou(), suppressSeconds: 9999, // Can get spammy if players have more than 1 tether or swap a lot - infoText: (_data, matches, output) => { + infoText: (data, matches, output) => { + if (matches.id === headMarkerData['fireballSplashTether']) + return output.fireballSplashTether!(); + + // Get direction of the tether + const actor = data.actorPositions[matches.sourceId]; + if (actor === undefined) { + switch (matches.id) { + case headMarkerData['projectionTether']: + return output.projectionTether!(); + case headMarkerData['manaBurstTether']: + return output.manaBurstTether!(); + case headMarkerData['heavySlamTether']: + return output.heavySlamTether!(); + } + return; + } + + const dirNum = Directions.xyTo8DirNum(actor.x, actor.y, center.x, center.y); + const dir = Directions.output8Dir[dirNum] ?? 'unknown'; + switch (matches.id) { case headMarkerData['projectionTether']: - return output.projectionTether!(); + return output.projectionTetherDir!({ dir: output[dir]!() }); case headMarkerData['manaBurstTether']: - return output.manaBurstTether!(); + return output.manaBurstTetherDir!({ dir: output[dir]!() }); case headMarkerData['heavySlamTether']: - return output.heavySlamTether!(); + return output.heavySlamTetherDir!({ dir: output[dir]!() }); } - return output.fireballSplashTether!(); }, outputStrings: { ...Directions.outputStrings8Dir, projectionTether: { en: 'Cone Tether on YOU', }, + projectionTetherDir: { + en: '${dir} Cone Tether on YOU', + }, manaBurstTether: { en: 'Defamation Tether on YOU', }, + manaBurstTetherDir: { + en: '${dir} Defamation Tether on YOU', + }, heavySlamTether: { en: 'Stack Tether on YOU', }, + heavySlamTetherDir: { + en: '${dir} Stack Tether on YOU', + }, fireballSplashTether: { en: 'Boss Tether on YOU', }, @@ -1707,22 +1740,49 @@ const triggerSet: TriggerSet = { mech1: output.baitJump!(), }); + // Get direction of the tether + const actor = data.actorPositions[matches.sourceId]; + if (actor === undefined) { + switch (data.myReplication2Tether) { + case headMarkerData['projectionTether']: + return output.projectionTether!({ + mech1: output.baitProtean!(), + }); + case headMarkerData['manaBurstTether']: + return output.manaBurstTether!({ + mech1: output.defamationOnYou!(), + }); + case headMarkerData['heavySlamTether']: + return output.heavySlamTether!({ + mech1: output.baitProtean!(), + }); + } + return; + } + + const dirNum = Directions.xyTo8DirNum(actor.x, actor.y, center.x, center.y); + const dir = Directions.output8Dir[dirNum] ?? 'unknown'; + switch (data.myReplication2Tether) { case headMarkerData['projectionTether']: - return output.projectionTether!({ + return output.projectionTetherDir!({ + dir: output[dir]!(), mech1: output.baitProtean!(), }); case headMarkerData['manaBurstTether']: - return output.manaBurstTether!({ + return output.manaBurstTetherDir!({ + dir: output[dir]!(), mech1: output.defamationOnYou!(), }); case headMarkerData['heavySlamTether']: - return output.heavySlamTether!({ + return output.heavySlamTetherDir!({ + dir: output[dir]!(), mech1: output.baitProtean!(), }); } }, outputStrings: { + ...Directions.outputStrings8Dir, defamationOnYou: Outputs.defamationOnYou, stackGroups: { en: 'Stack Groups', @@ -1739,12 +1799,21 @@ const triggerSet: TriggerSet = { baitJump: { en: 'Bait Jump', }, + projectionTetherDir: { + en: '${dir} Cone Tether: ${mech1}', + }, projectionTether: { en: 'Cone Tether: ${mech1}', }, + manaBurstTetherDir: { + en: '${dir} Defamation Tether: ${mech1}', + }, manaBurstTether: { en: 'Defamation Tether: ${mech1}', }, + heavySlamTetherDir: { + en: '${dir} Stack Tether: ${mech1}', + }, heavySlamTether: { en: 'Stack Tether: ${mech1}', }, @@ -1783,7 +1852,7 @@ const triggerSet: TriggerSet = { tc: '分組分攤', }, noTether: { - en: '${mech1} => ${mech2}', + en: 'No Tether: ${mech1} => ${mech2}', }, }, }, From 384b8ed9abaf051d4f9018ff4f67960ca81d50de Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 25 Jan 2026 06:46:42 -0500 Subject: [PATCH 062/139] add some idyllic clone triggers I do not have a log to test these, but they work for replication 2 so maybe they are the same for idyllic. I noticed the tethers in idyllic will come out before the replication cast so would need an additional phase filter for replication 2 on those tethers. --- ui/raidboss/data/07-dt/raid/r12s.ts | 121 +++++++++++++++++++++++++++- 1 file changed, 117 insertions(+), 4 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index cf51bb7e1a5..94a0a57c650 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -2,7 +2,12 @@ import Conditions from '../../../../../resources/conditions'; import { UnreachableCode } from '../../../../../resources/not_reached'; import Outputs from '../../../../../resources/outputs'; import { Responses } from '../../../../../resources/responses'; -import { Directions } from '../../../../../resources/util'; +import { + DirectionOutput8, + DirectionOutputCardinal, + DirectionOutputIntercard, + Directions, +} from '../../../../../resources/util'; import ZoneId from '../../../../../resources/zone_id'; import { RaidbossData } from '../../../../../types/data'; import { TriggerSet } from '../../../../../types/trigger'; @@ -43,6 +48,7 @@ export interface Data extends RaidbossData { replication2BossId?: string; myReplication2Tether?: string; myMutation?: 'alpha' | 'beta'; + replication3CloneOrder: number[]; } const headMarkerData = { @@ -98,6 +104,7 @@ const triggerSet: TriggerSet = { replicationCounter: 0, replication1FollowUp: false, replication2TetherMap: {}, + replication3CloneOrder: [], }), triggers: [ { @@ -1692,7 +1699,13 @@ const triggerSet: TriggerSet = { type: 'Tether', netRegex: { id: headMarkerData['lockedTether'], capture: true }, condition: (data, matches) => { - return data.replicationCounter === 2 && data.me === matches.target; + if ( + data.phase === 'replication2' && + data.replicationCounter === 2 && + data.me === matches.target + ) + return true; + return false; }, run: (data, matches) => { // Check if boss tether @@ -1730,7 +1743,13 @@ const triggerSet: TriggerSet = { type: 'Tether', netRegex: { id: headMarkerData['lockedTether'], capture: true }, condition: (data, matches) => { - return data.replicationCounter === 2 && data.me === matches.target; + if ( + data.phase === 'replication2' && + data.replicationCounter === 2 && + data.me === matches.target + ) + return true; + return false; }, delaySeconds: 0.1, infoText: (data, matches, output) => { @@ -1828,7 +1847,9 @@ const triggerSet: TriggerSet = { type: 'Tether', netRegex: { id: headMarkerData['lockedTether'], capture: false }, condition: (data) => { - return data.replicationCounter === 2; + if (data.phase === 'replication2' && data.replicationCounter === 2) + return true; + return false; }, delaySeconds: 0.1, suppressSeconds: 1, @@ -2187,6 +2208,98 @@ const triggerSet: TriggerSet = { durationSeconds: 4.7, response: Responses.bigAoe('alert'), }, + { + id: 'R12S Idyllic Dream Replication Clone Order Collect', + type: 'ActorControlExtra', + netRegex: { category: '0197', param1: '11D2', capture: true }, + condition: (data) => { + if (data.phase === 'idyllic' && data.replicationCounter === 2) + return true; + return false; + }, + run: (data, matches) => { + const actor = data.actorPositions[matches.id]; + if (actor === undefined) + return; + const dirNum = Directions.xyTo8DirNum(actor.x, actor.y, center.x, center.y); + data.replication3CloneOrder.push(dirNum); + }, + }, + { + id: 'R12S Idyllic Dream Replication First Clone Cardinal/Intercardinal', + type: 'ActorControlExtra', + netRegex: { category: '0197', param1: '11D2', capture: true }, + condition: (data) => { + if (data.phase === 'idyllic' && data.replicationCounter === 2) + return true; + return false; + }, + suppressSeconds: 9999, + infoText: (data, matches, output) => { + const actor = data.actorPositions[matches.id]; + if (actor === undefined) + return; + + type DirectionCardinal = Exclude; + type DirectionIntercard = Exclude; + const isCardinalDir = (dir: DirectionOutput8): dir is DirectionCardinal => { + return (Directions.outputCardinalDir as string[]).includes(dir); + }; + const isIntercardDir = (dir: DirectionOutput8): dir is DirectionIntercard => { + return (Directions.outputIntercardDir as string[]).includes(dir); + }; + + const dirNum = Directions.xyTo8DirNum(actor.x, actor.y, center.x, center.y); + const dir = Directions.output8Dir[dirNum] ?? 'unknown'; + + if (isCardinalDir(dir)) + return output.firstClone!({ cards: output.cardinals!() }); + if (isIntercardDir(dir)) + return output.firstClone!({ cards: output.intercards!() }); + return output.firstClone!({ cards: output.unknown!() }); + }, + outputStrings: { + unknown: Outputs.unknown, + cardinals: Outputs.cardinals, + intercards: Outputs.intercards, + firstClone: { + en: 'First Clone: ${cards}', + }, + }, + }, + { + id: 'R12S Idyllic Dream Replication Tethered Clone', + type: 'Tether', + netRegex: { id: headMarkerData['lockedTether'], capture: true }, + condition: (data, matches) => { + if ( + data.phase === 'idyllic' && + data.replicationCounter === 2 && + data.me === matches.target + ) + return true; + return false; + }, + suppressSeconds: 9999, + infoText: (data, matches, output) => { + const actor = data.actorPositions[matches.sourceId]; + if (actor === undefined) + return output.cloneTether!(); + + const dirNum = Directions.xyTo8DirNum(actor.x, actor.y, center.x, center.y); + const dir = Directions.output8Dir[dirNum] ?? 'unknown'; + return output.cloneTetherDir!({ dir: output[dir]!() }); + }, + outputStrings: { + ...Directions.outputStrings8Dir, + cloneTether: { + en: 'Tethered to Clone', + }, + cloneTetherDir: { + en: 'Tethered to ${dir} Clone', + }, + }, + }, ], timelineReplace: [ { From 1a3ec2eaba3ce2b1d51e9bc4ad95cb59fdce1040 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 25 Jan 2026 06:51:57 -0500 Subject: [PATCH 063/139] lint --- ui/raidboss/data/07-dt/raid/r12s.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 94a0a57c650..2f8cb683661 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -2239,7 +2239,7 @@ const triggerSet: TriggerSet = { const actor = data.actorPositions[matches.id]; if (actor === undefined) return; - + type DirectionCardinal = Exclude; type DirectionIntercard = Exclude; const isCardinalDir = (dir: DirectionOutput8): dir is DirectionCardinal => { @@ -2248,7 +2248,7 @@ const triggerSet: TriggerSet = { const isIntercardDir = (dir: DirectionOutput8): dir is DirectionIntercard => { return (Directions.outputIntercardDir as string[]).includes(dir); }; - + const dirNum = Directions.xyTo8DirNum(actor.x, actor.y, center.x, center.y); const dir = Directions.output8Dir[dirNum] ?? 'unknown'; From c93971ee21c0f1c0fc6674db667dd5e8eabf8d6c Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 25 Jan 2026 08:05:53 -0500 Subject: [PATCH 064/139] add some Lindwurm's Meteor triggers --- ui/raidboss/data/07-dt/raid/r12s.ts | 152 ++++++++++++++++++++++++++-- 1 file changed, 143 insertions(+), 9 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 2f8cb683661..e50d39cd847 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -22,6 +22,9 @@ export type Phase = | 'idyllic' | 'reenactment2'; +type DirectionCardinal = Exclude; +type DirectionIntercard = Exclude; + export interface Data extends RaidbossData { phase: Phase; // Phase 1 @@ -49,6 +52,8 @@ export interface Data extends RaidbossData { myReplication2Tether?: string; myMutation?: 'alpha' | 'beta'; replication3CloneOrder: number[]; + hasLightResistanceDown: boolean; + doomPlayers: string[]; } const headMarkerData = { @@ -86,6 +91,14 @@ const phaseMap: { [id: string]: Phase } = { 'B509': 'idyllic', }; +const isCardinalDir = (dir: DirectionOutput8): dir is DirectionCardinal => { + return (Directions.outputCardinalDir as string[]).includes(dir); +}; + +const isIntercardDir = (dir: DirectionOutput8): dir is DirectionIntercard => { + return (Directions.outputIntercardDir as string[]).includes(dir); +}; + const triggerSet: TriggerSet = { id: 'AacHeavyweightM4Savage', zoneId: ZoneId.AacHeavyweightM4Savage, @@ -105,6 +118,8 @@ const triggerSet: TriggerSet = { replication1FollowUp: false, replication2TetherMap: {}, replication3CloneOrder: [], + hasLightResistanceDown: false, + doomPlayers: [], }), triggers: [ { @@ -2240,15 +2255,6 @@ const triggerSet: TriggerSet = { if (actor === undefined) return; - type DirectionCardinal = Exclude; - type DirectionIntercard = Exclude; - const isCardinalDir = (dir: DirectionOutput8): dir is DirectionCardinal => { - return (Directions.outputCardinalDir as string[]).includes(dir); - }; - const isIntercardDir = (dir: DirectionOutput8): dir is DirectionIntercard => { - return (Directions.outputIntercardDir as string[]).includes(dir); - }; - const dirNum = Directions.xyTo8DirNum(actor.x, actor.y, center.x, center.y); const dir = Directions.output8Dir[dirNum] ?? 'unknown'; @@ -2300,6 +2306,134 @@ const triggerSet: TriggerSet = { }, }, }, + { + id: 'R12S Lindwurm\'s Meteor', + type: 'StartsUsing', + netRegex: { id: 'B4F2', source: 'Lindwurm', capture: false }, + infoText: (_data, _matches, output) => output.healerGroups!(), + outputStrings: { + healerGroups: Outputs.healerGroups, + }, + }, + { + id: 'R12S Light Resistance Down II Collect', + type: 'GainsEffect', + netRegex: { effectId: '1044', capture: true }, + condition: Conditions.targetIsYou(), + run: (data) => data.hasLightResistanceDown = true, + }, + { + id: 'R12S Light Resistance Down II', + type: 'GainsEffect', + netRegex: { effectId: '1044', capture: true }, + condition: Conditions.targetIsYou(), + infoText: (_data, _matches, output) => output.text!(), + outputStrings: { + text: { + en: 'Soak Fire/Earth Meteor', + }, + }, + }, + { + id: 'R12S No Light Resistance Down II', + type: 'GainsEffect', + netRegex: { effectId: '1044', capture: false }, + delaySeconds: 0.1, + suppressSeconds: 9999, + infoText: (data, _matches, output) => { + if (!data.hasLightResistanceDown) + return output.text!(); + }, + outputStrings: { + text: { + en: 'Soak a White/Star Meteor', + }, + }, + }, + { + id: 'R12S Doom Collect', + type: 'GainsEffect', + netRegex: { effectId: 'D24', capture: true }, + run: (data, matches) => data.doomPlayers.push(matches.target), + }, + { + id: 'R12S Doom Collect', + type: 'GainsEffect', + netRegex: { effectId: 'D24', capture: true }, + condition: (data) => data.CanCleanse(), + delaySeconds: 0.1, + suppressSeconds: 1, + infoText: (data, _matches, output) => { + const players = data.doomPlayers; + if (players.length === 2) { + const target1 = data.party.member(data.doomPlayers[0]); + const target2 = data.party.member(data.doomPlayers[1]); + return output.cleanseDoom2!({ target1: target1, target2: target2 }); + } + if (players.length === 1) { + const target1 = data.party.member(data.doomPlayers[0]); + return output.cleanseDoom!({ target: target1 }); + } + }, + outputStrings: { + cleanseDoom: { + en: 'Cleanse ${target}', + de: 'Reinige ${target}', + fr: 'Guérison sur ${target}', + cn: '康复 ${target}', + ko: '${target} 에스나', + tc: '康復 ${target}', + }, + cleanseDoom2: { + en: 'Cleanse ${target1}/${target2}', + }, + }, + }, + { + id: 'R12S Doom Cleanup', + type: 'LosesEffect', + netRegex: { effectId: 'D24', capture: true }, + run: (data, matches) => { + data.doomPlayers = data.doomPlayers.filter( + (player) => player === matches.target + ); + }, + }, + { + id: 'R12S Hot-blooded', + // Player can still cast, but shouldn't move for 5s duration + type: 'GainsEffect', + netRegex: { effectId: '12A0', capture: true }, + condition: Conditions.targetIsYou(), + durationSeconds: (_data, matches) => parseFloat(matches.duration), + response: Responses.stopMoving(), + }, + { + id: 'R12S Idyllic Dream Replication Clone Cardinal/Intercardinal Reminder', + // Using Temporal Curtain + type: 'StartsUsing', + netRegex: { id: 'B51C', source: 'Lindwurm', capture: false }, + infoText: (data, _matches, output) => { + const firstClone = data.replication3CloneOrder[0]; + if (firstClone === undefined) + return; + const actor = data.actorPositions[firstClone]; + if (actor === undefined) + return; + + const dirNum = Directions.xyTo8DirNum(actor.x, actor.y, center.x, center.y); + const dir = Directions.output8Dir[dirNum] ?? 'unknown'; + + if (isCardinalDir(dir)) + return output.cardinals!(); + if (isIntercardDir(dir)) + return output.intercards!(); + }, + outputStrings: { + cardinals: Outputs.cardinals, + intercards: Outputs.intercards, + }, + }, ], timelineReplace: [ { From c2dee83860574ed45fe1c850c7c3f566c9b0ce7c Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 25 Jan 2026 08:07:52 -0500 Subject: [PATCH 065/139] lint --- ui/raidboss/data/07-dt/raid/r12s.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index e50d39cd847..cd1da7f3e5b 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -2395,7 +2395,7 @@ const triggerSet: TriggerSet = { netRegex: { effectId: 'D24', capture: true }, run: (data, matches) => { data.doomPlayers = data.doomPlayers.filter( - (player) => player === matches.target + (player) => player === matches.target, ); }, }, From 5ad055166dc21293b51a534664ed7fd22d33e334 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 25 Jan 2026 08:10:12 -0500 Subject: [PATCH 066/139] fix doom cleanse id --- ui/raidboss/data/07-dt/raid/r12s.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index cd1da7f3e5b..7404fa5cca0 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -2357,9 +2357,9 @@ const triggerSet: TriggerSet = { run: (data, matches) => data.doomPlayers.push(matches.target), }, { - id: 'R12S Doom Collect', + id: 'R12S Doom Cleanse', type: 'GainsEffect', - netRegex: { effectId: 'D24', capture: true }, + netRegex: { effectId: 'D24', capture: false }, condition: (data) => data.CanCleanse(), delaySeconds: 0.1, suppressSeconds: 1, From 25bcc985fd14bd1b5f562ed5b50e9cce691c671b Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 25 Jan 2026 18:32:49 -0500 Subject: [PATCH 067/139] increase rep1 and rep2 delays for nodebuffs/notethers --- ui/raidboss/data/07-dt/raid/r12s.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 7404fa5cca0..ed4c2f36569 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1456,10 +1456,11 @@ const triggerSet: TriggerSet = { { id: 'R12S Fake Fire Resistance Down II', // Two players will not receive a debuff, they will need to act as if they had + // Mechanics happen across 1.1s type: 'GainsEffect', netRegex: { effectId: ['CFB', 'B79'], capture: false }, condition: (data) => !data.replication1FollowUp, - delaySeconds: 0.3, // Delay for debuff/damage propagation + delaySeconds: 1.2, // +0.1s Delay for debuff/damage propagation suppressSeconds: 9999, infoText: (data, _matches, output) => { if (data.replication1Debuff === undefined) @@ -1866,7 +1867,7 @@ const triggerSet: TriggerSet = { return true; return false; }, - delaySeconds: 0.1, + delaySeconds: 0.2, suppressSeconds: 1, infoText: (data, _matches, output) => { if (data.myReplication2Tether !== undefined) From 144ab874fe0b0bed2e3d456e85bb59aefc3e8a8d Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 25 Jan 2026 20:01:15 -0500 Subject: [PATCH 068/139] add Double Sobat followup triggers --- ui/raidboss/data/07-dt/raid/r12s.ts | 54 +++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index ed4c2f36569..0b7429614f9 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1585,12 +1585,62 @@ const triggerSet: TriggerSet = { }, { id: 'R12S Double Sobat', - // Two half-room cleaves - // First hit targets highest emnity target, second targets second highest + // Shared half-room cleave on tank => random turn half-room cleave => + // Esoteric Finisher big circle aoes that hits two highest emnity targets type: 'HeadMarker', netRegex: { id: headMarkerData['sharedTankbuster'], capture: true }, response: Responses.sharedTankBuster(), }, + { + id: 'R12S Double Sobat 2', + // Followup half-room cleave: + // - No turn + // - 90 degree turn + // - 180 degree turn + // - 270 degree turn + type: 'StartsUsing', + netRegex: { id: 'B525', source: 'Lindwurm', capture: true }, + alertText: (data, matches, output) => { + const actor = data.actorPositions[matches.sourceId]; + if (actor === undefined) + return output.getBehind!(); + + const dirNum = (Directions.hdgTo16DirNum(actor.heading) + 8) % 16; + const dir = Directions.output16Dir[dirNum] ?? 'unknown'; + return output.getBehindDir!({ + dir: output[dir]!(), + mech: output.getBehind!(), + }); + }, + outputStrings: { + ...Directions.outputStrings16Dir, + getBehind: Outputs.getBehind, + getBehindDir: { + en: '${dir}: ${mech}', + }, + }, + }, + { + id: 'R12S Esoteric Finisher', + // After Double Sobat 2, boss hits targets highest emnity target, second targets second highest + type: 'StartsUsing', + netRegex: { id: 'B525', source: 'Lindwurm', capture: true }, + delaySeconds: (_data, matches) => parseFloat(matches.castTime), + response: (data, _matches, output) => { + // cactbot-builtin-response + output.responseOutputStrings = { + tankBusterCleaves: Outputs.tankBusterCleaves, + avoidTankCleaves: Outputs.avoidTankCleaves, + }; + + if (data.role === 'tank' || data.role === 'healer') { + if (data.role === 'healer') + return { infoText: output.tankBusterCleaves!() }; + return { alertText: output.tankBusterCleaves!() }; + } + return { infoText: output.avoidTankCleaves!() }; + }, + }, { id: 'R12S Replication 2 Tethered Clone', // Combatants are added ~4s before Staging starts casting From 35423f2a26689d258aec7338d5327b26116479e3 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 25 Jan 2026 20:06:53 -0500 Subject: [PATCH 069/139] add direction to Snaking Kick and Grotesquerie --- ui/raidboss/data/07-dt/raid/r12s.ts | 57 +++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 0b7429614f9..d99840dcacc 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1474,8 +1474,9 @@ const triggerSet: TriggerSet = { }, { id: 'R12S Snaking Kick', + // Targets random player type: 'StartsUsing', - netRegex: { id: 'B527', source: 'Lindwurm', capture: false }, + netRegex: { id: 'B527', source: 'Lindwurm', capture: true }, condition: (data) => { // Use Grotesquerie trigger for projection tethered players const ability = data.myReplication2Tether; @@ -1483,7 +1484,25 @@ const triggerSet: TriggerSet = { return false; return true; }, - response: Responses.getBehind(), + alertText: (data, matches, output) => { + const actor = data.actorPositions[matches.sourceId]; + if (actor === undefined) + return output.getBehind!(); + + const dirNum = (Directions.hdgTo16DirNum(actor.heading) + 8) % 16; + const dir = Directions.output16Dir[dirNum] ?? 'unknown'; + return output.getBehindDir!({ + dir: output[dir]!(), + mech: output.getBehind!(), + }); + }, + outputStrings: { + ...Directions.outputStrings16Dir, + getBehind: Outputs.getBehind, + getBehindDir: { + en: '${dir}: ${mech}', + }, + }, }, { id: 'R12S Replication 1 Follow-up Tracker', @@ -2018,7 +2037,39 @@ const triggerSet: TriggerSet = { type: 'Ability', netRegex: { id: 'B4EA', source: 'Lindwurm', capture: true }, condition: Conditions.targetIsYou(), - response: Responses.getBehind(), + { + id: 'R12S Grotesquerie', + // This seems to be the point at which the look for the Snaking Kick is snapshot + // The VFX B4E9 happens ~0.6s before Snaking Kick + // B4EA has the targetted player in it + // B4EB Hemorrhagic Projection conal aoe goes off ~0.5s after in the direction the player was facing + type: 'Ability', + netRegex: { id: 'B4EA', source: 'Lindwurm', capture: true }, + condition: Conditions.targetIsYou(), + alertText: (data, matches, output) => { + // Get Boss facing + const bossId = data.replication2BossId; + if (bossId === undefined) + return output.getBehind!(); + + const actor = data.actorPositions[bossId]; + if (actor === undefined) + return output.getBehind!(); + + const dirNum = (Directions.hdgTo16DirNum(actor.heading) + 8) % 16; + const dir = Directions.output16Dir[dirNum] ?? 'unknown'; + return output.getBehindDir!({ + dir: output[dir]!(), + mech: output.getBehind!(), + }); + }, + outputStrings: { + ...Directions.outputStrings16Dir, + getBehind: Outputs.getBehind, + getBehindDir: { + en: '${dir}: ${mech}', + }, + }, }, { id: 'R12S Netherwrath Near/Far', From 6b47d89ae4a8889e0a4dd38bd17ee0a0d481173a Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 25 Jan 2026 20:10:20 -0500 Subject: [PATCH 070/139] fix paste error --- ui/raidboss/data/07-dt/raid/r12s.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index d99840dcacc..cc0f504bca4 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -2028,15 +2028,6 @@ const triggerSet: TriggerSet = { }, }, }, - { - id: 'R12S Grotesquerie', - // This seems to be the point at which the look for the Snaking Kick is snapshot - // The VFX B4E9 happens ~0.6s before Snaking Kick - // B4EA has the targetted player in it - // B4EB Hemorrhagic Projection conal aoe goes off ~0.5s after in the direction the player was facing - type: 'Ability', - netRegex: { id: 'B4EA', source: 'Lindwurm', capture: true }, - condition: Conditions.targetIsYou(), { id: 'R12S Grotesquerie', // This seems to be the point at which the look for the Snaking Kick is snapshot From 009332af92f9e2cfff9296f9b92c21c9b6744c38 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 25 Jan 2026 20:24:35 -0500 Subject: [PATCH 071/139] fix missing debuff calls in rep1 snaking startsCasting happens prior to half the debuffs going out (which has its own issue with the calls overlapping). Switch to ability. --- ui/raidboss/data/07-dt/raid/r12s.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index cc0f504bca4..64057e0e888 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1507,7 +1507,7 @@ const triggerSet: TriggerSet = { { id: 'R12S Replication 1 Follow-up Tracker', // Tracking from B527 Snaking Kick - type: 'StartsUsing', + type: 'Ability', netRegex: { id: 'B527', source: 'Lindwurm', capture: false }, suppressSeconds: 9999, run: (data) => data.replication1FollowUp = true, From 169a2f84879c9320a2e5abdcf2466e2544f7a288 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 25 Jan 2026 20:25:33 -0500 Subject: [PATCH 072/139] unused matches --- ui/raidboss/data/07-dt/raid/r12s.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 64057e0e888..9f47660de2a 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -2037,7 +2037,7 @@ const triggerSet: TriggerSet = { type: 'Ability', netRegex: { id: 'B4EA', source: 'Lindwurm', capture: true }, condition: Conditions.targetIsYou(), - alertText: (data, matches, output) => { + alertText: (data, _matches, output) => { // Get Boss facing const bossId = data.replication2BossId; if (bossId === undefined) From 7b03634b350dc387e411813da19c341ea735b765 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 25 Jan 2026 21:20:19 -0500 Subject: [PATCH 073/139] shorten wording for rep1 baits/clones --- ui/raidboss/data/07-dt/raid/r12s.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 9f47660de2a..75fb766ab72 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1446,10 +1446,10 @@ const triggerSet: TriggerSet = { }, outputStrings: { fire: { - en: 'Fire Debuff: Spread near Dark Clone (later)', + en: 'Fire Debuff: Spread near Dark (later)', }, dark: { - en: 'Dark Debuff: Stack near Fire Clone (later)', + en: 'Dark Debuff: Stack near Fire (later)', }, }, }, @@ -1468,7 +1468,7 @@ const triggerSet: TriggerSet = { }, outputStrings: { noDebuff: { - en: 'No Debuff: Spread near Dark Clone (later)', + en: 'No Debuff: Spread near Dark (later)', }, }, }, @@ -1595,10 +1595,10 @@ const triggerSet: TriggerSet = { outputStrings: { ...Directions.outputStringsIntercardDir, // Cardinals should result in '???' fire: { - en: 'Bait Fire near In ${dir1}/Out ${dir2} (Partners)', + en: 'Bait Fire In ${dir1}/Out ${dir2} (Partners)', }, dark: { - en: 'Bait Dark near In ${dir1}/Out ${dir2} (Solo)', + en: 'Bait Dark In ${dir1}/Out ${dir2} (Solo)', }, }, }, From c6da383a3528dd631ebe90beeb1d12dd986216ca Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 25 Jan 2026 21:44:39 -0500 Subject: [PATCH 074/139] timeline to end of shapes --- ui/raidboss/data/07-dt/raid/r12s.txt | 121 +++++++++++++++++++-------- 1 file changed, 87 insertions(+), 34 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.txt b/ui/raidboss/data/07-dt/raid/r12s.txt index ae9bd9eae76..581b760d922 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.txt +++ b/ui/raidboss/data/07-dt/raid/r12s.txt @@ -343,43 +343,89 @@ hideall "--sync--" # -p B528:3015.7 # -ii B51F B4DA B4DB B4DD B4DF B4E3 B4E6 B4F0 B4E9 B4F1 B4EE # -it "Lindwurm" +# -p B528:3012.1 +# -ii B51F B4DA B4DB B4DD B4DF B4E3 B4E6 B4F0 B4E9 B4F1 B4EE B4EF B508 +# -it "Lindwurm" 3000.5 label "r12s-p2-start" 3010.7 "--sync--" StartsUsing { id: "B528", source: "Lindwurm" } window 3100,10 3015.7 "Arcadia Aflame" Ability { id: "B528", source: "Lindwurm" } 3022.9 "--middle--" Ability { id: "B4D9", source: "Lindwurm" } -3028.0 "Replication 1" Ability { id: "B4D8", source: "Lindwurm" } -3039.6 "Top-tier Slam x2" #Ability { id: "B4DE", source: "Lindschrat" } -3040.4 "Winged Scourge x4" Ability { id: "B4DC", source: "Lindwurm" } -3040.6 "Mighty Magic x4" #Ability { id: "B4E0", source: "Lindwurm" } -3045.2 "Snaking Kick" Ability { id: "B527", source: "Lindwurm" } -3053.8 "--clones move 1--" #Ability { id: "B4D9", source: "Lindschrat" } -3054.8 "--clones move 2--" #Ability { id: "B4D9", source: "Lindschrat" } -3061.0 "Top-tier Slam x2" #Ability { id: "B4DE", source: "Lindschrat" } -3061.8 "Winged Scourge x4" Ability { id: "B4DC", source: "Lindwurm" } -3062.1 "Mighty Magic x4" #Ability { id: "B4E0", source: "Lindwurm" } -3069.3 "Double Sobat (castbar)" Ability { id: "B520", source: "Lindwurm" } -3070.0 "Double Sobat 1" Ability { id: "B522", source: "Lindwurm" } -3074.6 "Double Sobat 2" Ability { id: "B525", source: "Lindwurm" } -3077.0 "Esoteric Finisher" Ability { id: "B526", source: "Lindwurm" } - -3091.2 "Staging" Ability { id: "B4E1", source: "Lindwurm" } -3102.2 "--sync--" Ability { id: "B4E2", source: "Lindwurm" } -3105.4 "Replication 2" Ability { id: "B4D8", source: "Lindwurm" } -3128.0 "Firefall Splash" Ability { id: "B4E4", source: "Lindwurm" } -3128.7 "Scalding Waves x4" Ability { id: "B4E5", source: "Lindwurm" } -3130.1 "Mana Burst x3" Ability { id: "B4E7", source: "Lindwurm" } -3135.5 "Heavy Slam x2" Ability { id: "B4E8", source: "Lindschrat" } -3136.7 "Grotesquerie x2" Ability { id: "B4EA", source: "Lindwurm" } -3137.3 "Hemorrhagic Projection x2" Ability { id: "B4EB", source: "Lindwurm" } -3141.1 "Snaking Kick" Ability { id: "B527", source: "Lindwurm" } -3151.3 "Reenactment 1" Ability { id: "B4EC", source: "Lindwurm" } -3159.4 "Firefall Splash" Ability { id: "B4ED", source: "Lindschrat" } -3159.4 "Netherwrath Near/Netherwrath Far" Ability { id: ["B52E", "B52F"], source: "Lindwurm" } -3160.6 "Timeless Spite" Ability { id: "B530", source: "Lindwurm" } -3160.8 "Scalding Waves x4" Ability { id: "B8E1", source: "Lindwurm" } -3164.4 "Grotesquerie x2" Ability { id: "B4EA", source: "Lindwurm" } -3164.4 "Mana Burst x3" Ability { id: "BBE3", source: "Lindwurm" } -3165.0 "Hemorrhagic Projection x2" Ability { id: "B922", source: "Lindwurm" } +3028.2 "Replication 1" Ability { id: "B4D8", source: "Lindwurm" } +3040.0 "Top-tier Slam x2" Ability { id: "B4DE", source: "Lindschrat" } +3040.8 "Winged Scourge x4" Ability { id: "B4DC", source: "Lindwurm" } +3041.0 "Mighty Magic x4" Ability { id: "B4E0", source: "Lindwurm" } +3045.7 "Snaking Kick" Ability { id: "B527", source: "Lindwurm" } +3054.1 "--clones move 1--" #Ability { id: "B4D9", source: "Lindschrat" } +3055.1 "--clones move 2--" #Ability { id: "B4D9", source: "Lindschrat" } +3061.4 "Top-tier Slam x2" Ability { id: "B4DE", source: "Lindschrat" } +3062.2 "Winged Scourge x4" #Ability { id: "B4DC", source: "Lindwurm" } +3062.5 "Mighty Magic x4" Ability { id: "B4E0", source: "Lindwurm" } +3069.8 "Double Sobat (castbar)" Ability { id: "B520", source: "Lindwurm" } +3070.4 "Double Sobat 1" Ability { id: "B522", source: "Lindwurm" } +3075.0 "Double Sobat 2" Ability { id: "B525", source: "Lindwurm" } +3077.4 "Esoteric Finisher" Ability { id: "B526", source: "Lindwurm" } + +# NOTE: Depending on clone order and tethers player takes, spells at each instance will differ +# TBD: Generalize these? +3091.6 "Staging" Ability { id: "B4E1", source: "Lindwurm" } +3102.6 "--sync--" Ability { id: "B4E2", source: "Lindwurm" } +3105.8 "Replication 2" Ability { id: "B4D8", source: "Lindwurm" } +3128.2 "Firefall Splash" Ability { id: "B4E4", source: "Lindwurm" } +3128.9 "Scalding Waves x4" Ability { id: "B4E5", source: "Lindwurm" } +3130.3 "Mana Burst x3" Ability { id: "B4E7", source: "Lindwurm" } +3135.7 "Heavy Slam x2" Ability { id: "B4E8", source: "Lindschrat" } +3136.9 "Grotesquerie x2" Ability { id: "B4EA", source: "Lindwurm" } +3137.5 "Hemorrhagic Projection x2" Ability { id: "B4EB", source: "Lindwurm" } +3141.4 "Snaking Kick" Ability { id: "B527", source: "Lindwurm" } + +3151.6 "Reenactment 1" Ability { id: "B4EC", source: "Lindwurm" } +3159.7 "Firefall Splash" Ability { id: "B4ED", source: "Lindschrat" } +3159.7 "Netherwrath Near/Netherwrath Far" Ability { id: ["B52E", "B52F"], source: "Lindwurm" } +3160.9 "Mana Burst 1" #Ability { id: "BBE3", source: "Lindwurm" } +3160.9 "Timeless Spite x2" Ability { id: "B530", source: "Lindwurm" } +3161.0 "Scalding Waves x4" Ability { id: "B8E1", source: "Lindwurm" } +3164.8 "Mana Burst 2" Ability { id: "BBE3", source: "Lindwurm" } +3164.8 "Grotesquerie 1" Ability { id: "B4EA", source: "Lindwurm" } +3165.4 "Hemorrhagic Projection 1" Ability { id: "B922", source: "Lindwurm" } +3169.1 "Heavy Slam" Ability { id: "BE5D", source: "Lindwurm" } +3172.9 "Grotesquerie 2" Ability { id: "B4EA", source: "Lindwurm" } +3172.9 "Mana Burst 3" Ability { id: "BBE3", source: "Lindwurm" } +3173.5 "Hemorrhagic Projection 2" Ability { id: "B922", source: "Lindwurm" } +3178.9 "--middle--" Ability { id: "B4D9", source: "Lindwurm" } + +# Blood Mana / Blood Wakening Phase (Superchain) +3184.1 "Mutating Cells" Ability { id: "B505", source: "Lindwurm" } +3185.3 "--sync--" #Ability { id: "B506", source: "Lindwurm" } +3185.3 "--sync--" #Ability { id: "B506", source: "Lindwurm" } +3190.3 "Blood Mana" Ability { id: "B4FB", source: "Lindwurm" } +3193.6 "--sync--" Ability { id: "BCB0", source: "Mana Sphere" } +3194.0 "--sync--" Ability { id: "B4FD", source: "Mana Sphere" } +3202.8 "Dramatic Lysis x8" #Ability { id: "B507", source: "Lindwurm" } +3203.0 "Bloody Burst 1" #Ability { id: "B4FE", source: "Lindwurm" } # Goes off when soaked by player +3203.4 "Bloody Burst 2" #Ability { id: "B4FE", source: "Lindwurm" } # Goes off when soaked by player +3206.7 "--sync--" Ability { id: "B4FF", source: "Mana Sphere" } +3207.6 "--sync--" Ability { id: "BCB0", source: "Mana Sphere" } +3209.8 "--sync--" Ability { id: "B4FF", source: "Mana Sphere" } +3210.7 "--sync--" Ability { id: "BCB0", source: "Mana Sphere" } + +# TBD Clean-up/Generalize this part as its dependent on what was soaked and what spawned +3217.4 "Blood Wakening" Ability { id: "B500", source: "Lindwurm" } +3218.6 "--sync--" Ability { id: "B4FC", source: "Mana Sphere" } +3219.0 "Straightforward Thunder II" #Ability { id: "B503", source: "Lindwurm" } +3219.0 "Lindwurm's Aero III" #Ability { id: "B502", source: "Lindwurm" } +3219.0 "Sideways Fire II" #Ability { id: "B504", source: "Lindwurm" } +3219.0 "Lindwurm's Water III" #Ability { id: "B501", source: "Lindwurm" } +3219.0 "Lindwurm's Aero III" #Ability { id: "B502", source: "Lindwurm" } +3219.0 "Sideways Fire II" #Ability { id: "B504", source: "Lindwurm" } +3223.6 "--sync--" Ability { id: "B4FC", source: "Mana Sphere" } +3224.0 "Sideways Fire II" #Ability { id: "B504", source: "Lindwurm" } +3224.0 "Straightforward Thunder II" #Ability { id: "B503", source: "Lindwurm" } +3224.0 "Sideways Fire II" #Ability { id: "B504", source: "Lindwurm" } +3224.0 "Lindwurm's Water III" #Ability { id: "B501", source: "Lindwurm" } +3224.0 "Straightforward Thunder II" #Ability { id: "B503", source: "Lindwurm" } +3228.3 "Netherworld Near/Netherworld Far" Ability { id: ["B52B", "B52C"], source: "Lindwurm" } +3229.5 "Wailing Wave" Ability { id: "B52D", source: "Lindwurm" } +3236.4 "Arcadia Aflame" Ability { id: "B528", source: "Lindwurm" } # TBD # IGNORED ABILITIES @@ -415,6 +461,8 @@ hideall "--sync--" # B4E9 Grotesquerie: VFX # B4F1 Grotesquerie: VFX used in Reenactment # B4EE Mana Burst: VFX used in Reenactment +# B4EF Heavy Slam: VFX used in Reenactment +# B508 Unmitigated Explosion: Getting hit when you have Mitigation α or failing to get hit with Mitigation β, applies 30s damage down # ALL ENCOUNTER ABILITIES # Phase 1 @@ -480,6 +528,7 @@ hideall "--sync--" # B4D6 Visceral Burst # B4D7 The Fixer # B538 Refreshing Overkill: Enrage castbar +# B539 Refreshing Overkill: Non-enrage # B53A Refreshing Overkill: Enrage # B53E Skinsplitter: Damage from running into snake during Cruel Coil # B56F --sync-- @@ -541,7 +590,10 @@ hideall "--sync--" # B4F8 Lindwurm's Glare # B4FA Lindwurm's Thunder II # B4FB Blood Mana +# B4FC --sync-- +# B4FD --sync-- # B4FE Bloody Burst +# B4FF --sync-- # B500 Blood Wakening # B501 Lindwurm's Water III # B502 Lindwurm's Aero III @@ -550,6 +602,7 @@ hideall "--sync--" # B505 Mutating Cells # B506 --sync-- # B507 Dramatic Lysis +# B508 Unmitigated Explosion # B509 Idyllic Dream # B50F Power Gusher # B510 Power Gusher @@ -566,7 +619,7 @@ hideall "--sync--" # B51C Temporal Curtain # B51D --sync-- # B51E --sync-- -# B51F Attack +# B51F --sync--: Attack # B520 Double Sobat # B521 Double Sobat # B522 Double Sobat From 872d2c5a6ebfe960a7f252570d5186a255f1b433 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 26 Jan 2026 00:56:16 -0500 Subject: [PATCH 075/139] add some delay to double sobat 2 and snaking kick --- ui/raidboss/data/07-dt/raid/r12s.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 75fb766ab72..04ee5362c32 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1484,6 +1484,7 @@ const triggerSet: TriggerSet = { return false; return true; }, + delaySeconds: 0.1, // Need to delay for actor position update alertText: (data, matches, output) => { const actor = data.actorPositions[matches.sourceId]; if (actor === undefined) @@ -1619,6 +1620,7 @@ const triggerSet: TriggerSet = { // - 270 degree turn type: 'StartsUsing', netRegex: { id: 'B525', source: 'Lindwurm', capture: true }, + delaySeconds: 0.1, // Need to delay for actor position update alertText: (data, matches, output) => { const actor = data.actorPositions[matches.sourceId]; if (actor === undefined) From 6e14e7d858a143380fa797d85a30a38a15d070e3 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 26 Jan 2026 06:14:16 -0500 Subject: [PATCH 076/139] additional control data0 for split scourge venomous --- ui/raidboss/data/07-dt/raid/r12s.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 04ee5362c32..1dc0db5bd35 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1215,10 +1215,13 @@ const triggerSet: TriggerSet = { id: 'R12S Split Scourge and Venomous Scourge', // B4AB Split Scourge and B4A8 Venomous Scourge are instant casts // This actor control happens along with boss becoming targetable + // Seems there are two different data0 values possible: + // 1E01: Coming back from Cardinal platforms + // 1E001: Coming back from Intercardinal platforms type: 'ActorControl', - netRegex: { command: '8000000D', data0: '1E01', capture: false }, + netRegex: { command: '8000000D', data0: ['1E01', '1E001'], capture: false }, durationSeconds: 9, - suppressSeconds: 1, + suppressSeconds: 9999, infoText: (data, _matches, output) => { if (data.role === 'tank') return output.tank!(); From c8395804f9b5ee80eb9d464e27791c3e69738f45 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 26 Jan 2026 07:32:24 -0500 Subject: [PATCH 077/139] more p2 updates, some idyllic + extended rest with fflogs --- ui/raidboss/data/07-dt/raid/r12s.txt | 233 ++++++++++++++++++--------- 1 file changed, 153 insertions(+), 80 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.txt b/ui/raidboss/data/07-dt/raid/r12s.txt index 581b760d922..fa2c8f03f78 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.txt +++ b/ui/raidboss/data/07-dt/raid/r12s.txt @@ -340,93 +340,163 @@ hideall "--sync--" ### Phase 2: Lindwurm II -# -p B528:3015.7 -# -ii B51F B4DA B4DB B4DD B4DF B4E3 B4E6 B4F0 B4E9 B4F1 B4EE -# -it "Lindwurm" # -p B528:3012.1 -# -ii B51F B4DA B4DB B4DD B4DF B4E3 B4E6 B4F0 B4E9 B4F1 B4EE B4EF B508 +# -ii B51F B4DA B4DB B4DD B4DF B4E3 B4E6 B4F0 B4E9 B4F1 B4EE B4EF B508 B4FF BCB0 # -it "Lindwurm" 3000.5 label "r12s-p2-start" 3010.7 "--sync--" StartsUsing { id: "B528", source: "Lindwurm" } window 3100,10 3015.7 "Arcadia Aflame" Ability { id: "B528", source: "Lindwurm" } 3022.9 "--middle--" Ability { id: "B4D9", source: "Lindwurm" } -3028.2 "Replication 1" Ability { id: "B4D8", source: "Lindwurm" } -3040.0 "Top-tier Slam x2" Ability { id: "B4DE", source: "Lindschrat" } -3040.8 "Winged Scourge x4" Ability { id: "B4DC", source: "Lindwurm" } -3041.0 "Mighty Magic x4" Ability { id: "B4E0", source: "Lindwurm" } -3045.7 "Snaking Kick" Ability { id: "B527", source: "Lindwurm" } -3054.1 "--clones move 1--" #Ability { id: "B4D9", source: "Lindschrat" } -3055.1 "--clones move 2--" #Ability { id: "B4D9", source: "Lindschrat" } -3061.4 "Top-tier Slam x2" Ability { id: "B4DE", source: "Lindschrat" } -3062.2 "Winged Scourge x4" #Ability { id: "B4DC", source: "Lindwurm" } -3062.5 "Mighty Magic x4" Ability { id: "B4E0", source: "Lindwurm" } -3069.8 "Double Sobat (castbar)" Ability { id: "B520", source: "Lindwurm" } -3070.4 "Double Sobat 1" Ability { id: "B522", source: "Lindwurm" } -3075.0 "Double Sobat 2" Ability { id: "B525", source: "Lindwurm" } -3077.4 "Esoteric Finisher" Ability { id: "B526", source: "Lindwurm" } +3028.0 "Replication 1" Ability { id: "B4D8", source: "Lindwurm" } +3039.7 "Top-tier Slam x2" Ability { id: "B4DE", source: "Lindschrat" } +3040.5 "Winged Scourge x4" Ability { id: "B4DC", source: "Lindwurm" } +3040.7 "Mighty Magic x4" #Ability { id: "B4E0", source: "Lindwurm" } +3045.3 "Snaking Kick" Ability { id: "B527", source: "Lindwurm" } +3053.7 "--clones move 1--" #Ability { id: "B4D9", source: "Lindschrat" } +3054.7 "--clones move 2--" #Ability { id: "B4D9", source: "Lindschrat" } +3061.0 "Top-tier Slam x2" Ability { id: "B4DE", source: "Lindschrat" } +3061.8 "Winged Scourge x4" Ability { id: "B4DC", source: "Lindwurm" } +3062.1 "Mighty Magic x4" #Ability { id: "B4E0", source: "Lindwurm" } +3069.4 "Double Sobat (castbar)" Ability { id: "B520", source: "Lindwurm" } +3070.0 "Double Sobat 1" Ability { id: ["B521", "B522", "B523", "B524"], source: "Lindwurm" } +3074.6 "Double Sobat 2" Ability { id: "B525", source: "Lindwurm" } +3077.0 "Esoteric Finisher" Ability { id: "B526", source: "Lindwurm" } # NOTE: Depending on clone order and tethers player takes, spells at each instance will differ # TBD: Generalize these? -3091.6 "Staging" Ability { id: "B4E1", source: "Lindwurm" } -3102.6 "--sync--" Ability { id: "B4E2", source: "Lindwurm" } -3105.8 "Replication 2" Ability { id: "B4D8", source: "Lindwurm" } -3128.2 "Firefall Splash" Ability { id: "B4E4", source: "Lindwurm" } -3128.9 "Scalding Waves x4" Ability { id: "B4E5", source: "Lindwurm" } -3130.3 "Mana Burst x3" Ability { id: "B4E7", source: "Lindwurm" } -3135.7 "Heavy Slam x2" Ability { id: "B4E8", source: "Lindschrat" } -3136.9 "Grotesquerie x2" Ability { id: "B4EA", source: "Lindwurm" } -3137.5 "Hemorrhagic Projection x2" Ability { id: "B4EB", source: "Lindwurm" } -3141.4 "Snaking Kick" Ability { id: "B527", source: "Lindwurm" } - -3151.6 "Reenactment 1" Ability { id: "B4EC", source: "Lindwurm" } -3159.7 "Firefall Splash" Ability { id: "B4ED", source: "Lindschrat" } -3159.7 "Netherwrath Near/Netherwrath Far" Ability { id: ["B52E", "B52F"], source: "Lindwurm" } -3160.9 "Mana Burst 1" #Ability { id: "BBE3", source: "Lindwurm" } -3160.9 "Timeless Spite x2" Ability { id: "B530", source: "Lindwurm" } -3161.0 "Scalding Waves x4" Ability { id: "B8E1", source: "Lindwurm" } -3164.8 "Mana Burst 2" Ability { id: "BBE3", source: "Lindwurm" } -3164.8 "Grotesquerie 1" Ability { id: "B4EA", source: "Lindwurm" } -3165.4 "Hemorrhagic Projection 1" Ability { id: "B922", source: "Lindwurm" } -3169.1 "Heavy Slam" Ability { id: "BE5D", source: "Lindwurm" } -3172.9 "Grotesquerie 2" Ability { id: "B4EA", source: "Lindwurm" } -3172.9 "Mana Burst 3" Ability { id: "BBE3", source: "Lindwurm" } -3173.5 "Hemorrhagic Projection 2" Ability { id: "B922", source: "Lindwurm" } -3178.9 "--middle--" Ability { id: "B4D9", source: "Lindwurm" } +3091.2 "Staging" Ability { id: "B4E1", source: "Lindwurm" } +3102.2 "--sync--" Ability { id: "B4E2", source: "Lindwurm" } +3105.4 "Replication 2" Ability { id: "B4D8", source: "Lindwurm" } +3127.8 "Firefall Splash" Ability { id: "B4E4", source: "Lindwurm" } +3128.5 "Scalding Waves x4" Ability { id: "B4E5", source: "Lindwurm" } +3129.9 "Mana Burst x3" Ability { id: "B4E7", source: "Lindwurm" } +3135.5 "Heavy Slam x2" Ability { id: "B4E8", source: "Lindschrat" } +3136.7 "Grotesquerie x2" Ability { id: "B4EA", source: "Lindwurm" } +3137.3 "Hemorrhagic Projection x2" Ability { id: "B4EB", source: "Lindwurm" } +3141.1 "Snaking Kick" Ability { id: "B527", source: "Lindwurm" } + +3151.3 "Reenactment 1" Ability { id: "B4EC", source: "Lindwurm" } +3159.4 "Firefall Splash" #Ability { id: "B4ED", source: "Lindschrat" } +3159.4 "Netherwrath Near/Netherwrath Far" Ability { id: ["B52E", "B52F"], source: "Lindwurm" } +3160.6 "Mana Burst 1" Ability { id: "BBE3", source: "Lindwurm" } +3160.6 "Timeless Spite x2" Ability { id: "B530", source: "Lindwurm" } +3160.7 "Scalding Waves x4" #Ability { id: "B8E1", source: "Lindwurm" } +3164.5 "Grotesquerie 1" #Ability { id: "B4EA", source: "Lindwurm" } +3164.5 "Mana Burst 2" Ability { id: "BBE3", source: "Lindwurm" } +3165.1 "Hemorrhagic Projection 1" Ability { id: "B922", source: "Lindwurm" } +3168.8 "Heavy Slam x2" Ability { id: "BE5D", source: "Lindwurm" } +3172.6 "Mana Burst 3" Ability { id: "BBE3", source: "Lindwurm" } +3172.6 "Grotesquerie 2" #Ability { id: "B4EA", source: "Lindwurm" } +3173.2 "Hemorrhagic Projection 2" Ability { id: "B922", source: "Lindwurm" } +3178.6 "--middle--" Ability { id: "B4D9", source: "Lindwurm" } # Blood Mana / Blood Wakening Phase (Superchain) -3184.1 "Mutating Cells" Ability { id: "B505", source: "Lindwurm" } -3185.3 "--sync--" #Ability { id: "B506", source: "Lindwurm" } -3185.3 "--sync--" #Ability { id: "B506", source: "Lindwurm" } -3190.3 "Blood Mana" Ability { id: "B4FB", source: "Lindwurm" } -3193.6 "--sync--" Ability { id: "BCB0", source: "Mana Sphere" } -3194.0 "--sync--" Ability { id: "B4FD", source: "Mana Sphere" } -3202.8 "Dramatic Lysis x8" #Ability { id: "B507", source: "Lindwurm" } -3203.0 "Bloody Burst 1" #Ability { id: "B4FE", source: "Lindwurm" } # Goes off when soaked by player -3203.4 "Bloody Burst 2" #Ability { id: "B4FE", source: "Lindwurm" } # Goes off when soaked by player -3206.7 "--sync--" Ability { id: "B4FF", source: "Mana Sphere" } -3207.6 "--sync--" Ability { id: "BCB0", source: "Mana Sphere" } -3209.8 "--sync--" Ability { id: "B4FF", source: "Mana Sphere" } -3210.7 "--sync--" Ability { id: "BCB0", source: "Mana Sphere" } +3183.8 "Mutating Cells" Ability { id: "B505", source: "Lindwurm" } +3185.0 "--sync--" Ability { id: "B506", source: "Lindwurm" } +3190.0 "Blood Mana" Ability { id: "B4FB", source: "Lindwurm" } +3193.3 "--black holes--" Ability { id: "BCB0", source: "Mana Sphere" } +3193.7 "--shapes--" Ability { id: "B4FD", source: "Mana Sphere" } +3200.7 "Bloody Burst 1" #Ability { id: "B4FE", source: "Lindwurm" } # Goes off when soaked by player +3201.0 "Bloody Burst 2" #Ability { id: "B4FE", source: "Lindwurm" } # Goes off when soaked by player +3202.2 "Dramatic Lysis x8" Ability { id: "B507", source: "Lindwurm" } # TBD Clean-up/Generalize this part as its dependent on what was soaked and what spawned -3217.4 "Blood Wakening" Ability { id: "B500", source: "Lindwurm" } -3218.6 "--sync--" Ability { id: "B4FC", source: "Mana Sphere" } -3219.0 "Straightforward Thunder II" #Ability { id: "B503", source: "Lindwurm" } -3219.0 "Lindwurm's Aero III" #Ability { id: "B502", source: "Lindwurm" } -3219.0 "Sideways Fire II" #Ability { id: "B504", source: "Lindwurm" } -3219.0 "Lindwurm's Water III" #Ability { id: "B501", source: "Lindwurm" } -3219.0 "Lindwurm's Aero III" #Ability { id: "B502", source: "Lindwurm" } -3219.0 "Sideways Fire II" #Ability { id: "B504", source: "Lindwurm" } -3223.6 "--sync--" Ability { id: "B4FC", source: "Mana Sphere" } -3224.0 "Sideways Fire II" #Ability { id: "B504", source: "Lindwurm" } -3224.0 "Straightforward Thunder II" #Ability { id: "B503", source: "Lindwurm" } -3224.0 "Sideways Fire II" #Ability { id: "B504", source: "Lindwurm" } -3224.0 "Lindwurm's Water III" #Ability { id: "B501", source: "Lindwurm" } -3224.0 "Straightforward Thunder II" #Ability { id: "B503", source: "Lindwurm" } -3228.3 "Netherworld Near/Netherworld Far" Ability { id: ["B52B", "B52C"], source: "Lindwurm" } -3229.5 "Wailing Wave" Ability { id: "B52D", source: "Lindwurm" } -3236.4 "Arcadia Aflame" Ability { id: "B528", source: "Lindwurm" } -# TBD +3216.7 "Blood Wakening" Ability { id: "B500", source: "Lindwurm" } +3217.9 "--sync--" Ability { id: "B4FC", source: "Mana Sphere" } +3218.3 "Black Hole 1" Ability { id: ["B501", "B502", "B503", "B504"], source: "Lindwurm" } +3222.9 "--sync--" Ability { id: "B4FC", source: "Mana Sphere" } +3223.3 "Black Hole 2" Ability { id: ["B501", "B502", "B503", "B504"], source: "Lindwurm" } +3227.6 "Netherworld Near/Netherworld Far" Ability { id: ["B52B", "B52C"], source: "Lindwurm" } +3228.8 "Wailing Wave x3" Ability { id: "B52D", source: "Lindwurm" } +3231.8 "Dramatic Lysis x8" Ability { id: "B507", source: "Lindwurm" } +3235.8 "Arcadia Aflame" Ability { id: "B528", source: "Lindwurm" } +3245.0 "Double Sobat (castbar)" Ability { id: "B520", source: "Lindwurm" } +3245.6 "Double Sobat 1" Ability { id: ["B521", "B522", "B523", "B524"], source: "Lindwurm" } +3250.2 "Double Sobat 2" Ability { id: "B525", source: "Lindwurm" } +3252.6 "Esoteric Finisher" Ability { id: "B526", source: "Lindwurm" } +3260.7 "--middle--" Ability { id: "B4D9", source: "Lindwurm" } + +# TBD Clean-up/Generalize this as well since stacks/defamation order is tether dependent +3268.8 "Idyllic Dream" Ability { id: "B509", source: "Lindwurm" } +3275.0 "Staging" Ability { id: "B4E1", source: "Lindwurm" } +3283.4 "--sync--" Ability { id: "B4E2", source: "Lindwurm" } +3290.1 "Twisted Vision" Ability { id: "BBE2", source: "Lindwurm" } +3296.2 "Replication 3" Ability { id: "B4D8", source: "Lindwurm" } +3308.6 "Twisted Vision" Ability { id: "BBE2", source: "Lindwurm" } +3309.6 "Power Gusher" #Ability { id: "B512", source: "Lindwurm" } +3309.6 "Power Gusher" #Ability { id: "B50F", source: "Lindschrat" } +3309.6 "Snaking Kick" #Ability { id: "B511", source: "Lindschrat" } +3309.6 "Power Gusher" #Ability { id: "B510", source: "Lindschrat" } +3314.7 "Replication 4" Ability { id: "B4D8", source: "Lindwurm" } + +# Towers Preview +3334.3 "Twisted Vision" Ability { id: "BBE2", source: "Lindwurm" } +3337.7 "Power Gusher" #Ability { id: "B513", source: "Lindschrat" } +3337.7 "Snaking Kick" Ability { id: "B515", source: "Lindschrat" } +3337.7 "Power Gusher" #Ability { id: "B514", source: "Lindschrat" } +3338.7 "Snaking Kick" Ability { id: "BE95", source: "Lindwurm" } +3338.9 "Power Gusher" #Ability { id: "B516", source: "Lindwurm" } +3342.8 "Lindwurm's Meteor" Ability { id: "B4F2", source: "Lindwurm" } +3348.9 "Downfall" Ability { id: "B4F3", source: "Lindwurm" } +3355.0 "Arcadian Arcanum" Ability { id: "B529", source: "Lindwurm" } +3356.2 "Arcadian Arcanum" Ability { id: "B9D9", source: "Lindwurm" } + +# First Stacks/Defamations +3363.0 "Twisted Vision" Ability { id: "BBE2", source: "Lindwurm" } +3369.6 "Heavy Slam" Ability { id: "B519", source: "Lindschrat" } +3374.6 "Mana Burst" Ability { id: "B517", source: "Lindschrat" } +3375.8 "Mana Burst" Ability { id: "B518", source: "Lindwurm" } +3379.6 "Heavy Slam" Ability { id: "B519", source: "Lindschrat" } +3384.6 "Mana Burst" Ability { id: "B517", source: "Lindschrat" } + +# fflogs +3389.5 "Twisted Vision" Ability { id: "BBE2", source: "Lindwurm" } window 10,10 +3393.9 "Cosmic Kiss" Ability { id: "B4F4", source: "Lindwurm" } +3394.6 "Lindwurm's Dark II" Ability { id: "B4F6", source: "Lindwurm" } +3396.6 "Pyretic Wurm" Ability { id: "B4F9", source: "Lindwurm" } +3399.6 "Lindwurm's Stone III" #Ability { id: "B4F7", source: "Lindwurm" } +3404.6 "Lindwurm's Thunder II" #Ability { id: "B4FA", source: "Lindwurm" } +3404.6 "Lindwurm's Glare" #Ability { id: "B4F8", source: "Lindwurm" } +3404.6 "Lindwurm's Thunder II" #Ability { id: "B4FA", source: "Lindwurm" } +3404.6 "Lindwurm's Glare" #Ability { id: "B4F8", source: "Lindwurm" } +3412.9 "Temporal Curtain" Ability { id: "B51C", source: "Lindwurm" } + +3416.0 "--sync--" Ability { id: "B51D", source: "Lindschrat" } +3419.1 "--sync--" Ability { id: "B4D9", source: "Lindschrat" } +3425.4 "Twisted Vision" Ability { id: "BBE2", source: "Lindwurm" } +3426.4 "Power Gusher" #Ability { id: "B50F", source: "Lindschrat" } +3426.4 "Snaking Kick" Ability { id: "BCAF", source: "Lindschrat" } +3431.5 "Reenactment 2" Ability { id: "B4EC", source: "Lindwurm" } +3433.5 "Heavy Slam" Ability { id: "B4EF", source: "Lindschrat" } +3434.7 "Mana Burst" Ability { id: "BBE3", source: "Lindwurm" } +3434.9 "Heavy Slam" #Ability { id: "BE5D", source: "Lindwurm" } +3440.5 "Twisted Vision" Ability { id: "BBE2", source: "Lindwurm" } +3444.1 "Power Gusher" #Ability { id: "B513", source: "Lindschrat" } +3444.1 "Snaking Kick" Ability { id: "B515", source: "Lindschrat" } +3445.1 "Snaking Kick" Ability { id: "BE95", source: "Lindwurm" } +3445.3 "Power Gusher" Ability { id: "B516", source: "Lindwurm" } +3448.2 "Twisted Vision" Ability { id: "BBE2", source: "Lindwurm" } +3454.5 "Heavy Slam" #Ability { id: "B4EF", source: "Lindschrat" } +3454.5 "--sync--" Ability { id: "B51E", source: "Lindschrat" } +3455.7 "Mana Burst" Ability { id: "BBE3", source: "Lindwurm" } +3455.9 "Heavy Slam" Ability { id: "BE5D", source: "Lindwurm" } +3459.5 "Power Gusher" Ability { id: "B51B", source: "Lindschrat" } +3460.6 "Power Gusher" Ability { id: "B516", source: "Lindwurm" } +3466.5 "Idyllic Dream" Ability { id: "B509", source: "Lindwurm" } +3474.6 "Double Sobat (castbar)" Ability { id: "B520", source: "Lindwurm" } +3475.2 "Double Sobat 1" Ability { id: ["B521", "B522", "B523", "B524"], source: "Lindwurm" } +3479.8 "Double Sobat 2" Ability { id: "B525", source: "Lindwurm" } +3482.2 "Esoteric Finisher" Ability { id: "B526", source: "Lindwurm" } + +# Enrage Sequence +3496.0 "Replication 5" Ability { id: "B46C", source: "Lindwurm" } +3509.4 "Arcadian Hell 1" Ability { id: "B533", source: "Lindwurm" } +3509.4 "Arcadian Hell" #Ability { id: "B534", source: "Lindschrat" } +3525.6 "Arcadian Hell 2" Ability { id: "B533", source: "Lindwurm" } +3525.6 "Arcadian Hell" #Ability { id: "B535", source: "Lindschrat" } +3538.4 "--sync--" StartsUsing { id: "B537", source: "Lindwurm" } +3548.4 "Arcadian Hell (Enrage)" Ability { id: "B537", source: "Lindwurm" } +3548.4 "Arcadian Hell (Enrage)" #Ability { id: "BEC1", source: "Lindschrat" } # IGNORED ABILITIES # Phase 1 @@ -463,6 +533,8 @@ hideall "--sync--" # B4EE Mana Burst: VFX used in Reenactment # B4EF Heavy Slam: VFX used in Reenactment # B508 Unmitigated Explosion: Getting hit when you have Mitigation α or failing to get hit with Mitigation β, applies 30s damage down +# B4FF --sync--: VFX related to Mana Spheres being eaten by Black Hole? +# BCB0 --sync--: VFX related to Mana Spheres being eaten by Black Hole? # ALL ENCOUNTER ABILITIES # Phase 1 @@ -579,8 +651,8 @@ hideall "--sync--" # B4EC Reenactment # B4ED Firefall Splash # B4EE Mana Burst -# B4F0 Unmitigated Impact # B4EF Heavy Slam +# B4F0 Unmitigated Impact # B4F1 Grotesquerie # B4F2 Lindwurm's Meteor # B4F3 Downfall @@ -620,11 +692,12 @@ hideall "--sync--" # B51D --sync-- # B51E --sync-- # B51F --sync--: Attack -# B520 Double Sobat -# B521 Double Sobat -# B522 Double Sobat -# B524 Double Sobat -# B525 Double Sobat +# B520 Double Sobat: Castbar +# B521 Double Sobat: 0 degree left turn then B525 +# B522 Double Sobat: 90 degree left turn then B525 +# B523 Double Sobat: 180 degree left turn then B525 +# B524 Double Sobat: 270 degree left turn (turns to the right) +# B525 Double Sobat: Followup cleave # B526 Esoteric Finisher # B527 Snaking Kick # B528 Arcadia Aflame From c1e61330a52d77012065c6236ca179ff041c1cda Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 26 Jan 2026 07:37:09 -0500 Subject: [PATCH 078/139] add Blood Mana/Blood Wakening solution triggers --- ui/raidboss/data/07-dt/raid/r12s.ts | 246 +++++++++++++++++++++++++++- 1 file changed, 238 insertions(+), 8 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 1dc0db5bd35..025dd857e58 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -50,7 +50,16 @@ export interface Data extends RaidbossData { replication2TetherMap: { [dirNum: string]: string }; replication2BossId?: string; myReplication2Tether?: string; + netherwrathFollowup: boolean; myMutation?: 'alpha' | 'beta'; + manaSpheres: { + [id: string]: 'lightning' | 'fire' | 'water' | 'wind' | 'blackHole'; + }; + westManaSpheres: { [id: string]: { x: number; y: number } }; + eastManaSpheres: { [id: string]: { x: number; y: number } }; + closeManaSphereIds: string[]; + firstBlackHole?: 'east' | 'west'; + manaSpherePopSide?: 'east' | 'west'; replication3CloneOrder: number[]; hasLightResistanceDown: boolean; doomPlayers: string[]; @@ -117,6 +126,11 @@ const triggerSet: TriggerSet = { replicationCounter: 0, replication1FollowUp: false, replication2TetherMap: {}, + netherwrathFollowup: false, + manaSpheres: {}, + westManaSpheres: {}, + eastManaSpheres: {}, + closeManaSphereIds: [], replication3CloneOrder: [], hasLightResistanceDown: false, doomPlayers: [], @@ -2212,6 +2226,80 @@ const triggerSet: TriggerSet = { }, }, }, + { + id: 'R12S Reenactment 1 Scalding Waves Collect', + // Players need to wait for BBE3 Mana Burst Defamations on the clones to complete before next mechanic + // NOTE: This is used with DN Strategy + type: 'Ability', + netRegex: { id: 'B8E1', source: 'Lindwurm', capture: false }, + condition: (data) => data.phase === 'reenactment1', + suppressSeconds: 9999, + run: (data) => data.netherwrathFollowup = true, + }, + { + id: 'R12S Reenactment 1 Clone Stacks', + // Players need to wait for BBE3 Mana Burst defamations on clones to complete + // This happens three times during reenactment and the third one (which is after the proteans) is the trigger + // NOTE: This is used with DN Strategy + type: 'Ability', + netRegex: { id: 'BBE3', source: 'Lindwurm', capture: false }, + condition: (data) => data.netherwrathFollowup, + suppressSeconds: 9999, + alertText: (_data, _matches, output) => output.text!(), + outputStrings: { + text: { + en: 'East/West Clone Stacks', + }, + }, + }, + { + id: 'R12S Reenactment 1 Clone Stacks', + // Players need to run back to north after clone stacks (BE5D Heavy Slam) + // The clone stacks become a defamation and the other a cleave going East or West through the room + // NOTE: This is used with DN Strategy + type: 'Ability', + netRegex: { id: 'BE5D', source: 'Lindwurm', capture: false }, + condition: (data) => data.netherwrathFollowup, + suppressSeconds: 9999, + alertText: (_data, _matches, output) => output.north!(), + outputStrings: { + north: Outputs.north, + }, + }, + { + id: 'R12S Mana Sphere Collect and Label', + // Combatants Spawn ~3s before B505 Mutating Cells startsUsing + // Their positions are available at B4FD in the 264 AbilityExtra lines and updated periodically after with 270 lines + // 19208 => Lightning Bowtie (N/S Cleave) + // 19209 => Fire Bowtie (E/W Cleave) + // 19205 => Black Hole + // 19206 => Water Sphere/Chariot + // 19207 => Wind Donut + // Position at add is center, so not useful here yet + type: 'AddedCombatant', + netRegex: { name: 'Mana Sphere', capture: true }, + run: (data, matches) => { + const id = matches.id; + const npcBaseId = parseInt(matches.npcBaseId); + switch (npcBaseId) { + case 19205: + data.manaSpheres[id] = 'blackHole'; + return; + case 19206: + data.manaSpheres[id] = 'water'; + return; + case 19207: + data.manaSpheres[id] = 'wind'; + return; + case 19208: + data.manaSpheres[id] = 'lightning'; + return; + case 19209: + data.manaSpheres[id] = 'fire'; + return; + } + }, + }, { id: 'R12S Mutation α/β Collect', // Used in Blood Mana / Blood Awakening Mechanics @@ -2245,23 +2333,143 @@ const triggerSet: TriggerSet = { }, }, { - id: 'R12S Blood Mana', + id: 'R12S Mana Sphere Position Collect', + // BCB0 Black Holes: + // These are (90, 100) and (110, 100) + // B4FD Shapes + // Side that needs to be exploded will have pairs with 2 of the same x or y coords + // Side to get the shapes to explode will be closest distance to black hole + type: 'AbilityExtra', + netRegex: { id: 'B4FD', capture: true }, + run: (data, matches) => { + // Calculate Distance to Black Hole + const getDistance = ( + x: number, + y: number, + ): number => { + const blackHoleX = x < 100 ? 90 : 110; + const dx = x - blackHoleX; + const dy = y - 100; + return Math.round(Math.sqrt(dx * dx + dy * dy)); + }; + const x = parseFloat(matches.x); + const y = parseFloat(matches.y); + const d = getDistance(x, y); + const id = matches.sourceId; + + // Put into different objects for easier lookup + if (x < 100) { + data.westManaSpheres[id] = { x: x, y: y }; + } + data.eastManaSpheres[id] = { x: x, y: y }; + + // Shapes with 6 distance are close, Shapes with 12 are far + if (d < 7) { + data.closeManaSphereIds.push(id); + + // Have enough data to solve at this point + if (data.closeManaSphereIds.length === 2) { + const popSide = x < 100 ? 'east' : 'west'; + data.manaSpherePopSide = popSide; + + const sphereId1 = data.closeManaSphereIds[0]; + const sphereId2 = id; + if (sphereId1 === undefined) + return; + + const sphereType1 = data.manaSpheres[sphereId1]; + const sphereType2 = data.manaSpheres[sphereId2]; + if (sphereType1 === undefined || sphereType2 === undefined) + return; + + // If you see Water, pop side first + // If you see Wind, non-pop side + // Can't be Lightning + Wind because Fire hits the donut + // Fire + Lightning would hit whole room + // Water + Wind would hit whole room + const nonPopSide = popSide === 'east' ? 'west' : 'east'; + const first = [sphereType1, sphereType2]; + const dir2 = first.includes('water') ? popSide : nonPopSide; + data.firstBlackHole = dir2; + } + } + }, + }, + { + id: 'R12S Black Hole and Shapes', // Black Holes and shapes - // TODO: Tell what shape to pop + which Black Hole mechanics and side? type: 'Ability', - netRegex: { id: 'B4FB', source: 'Lindwurm', capture: false }, + netRegex: { id: 'B4FD', source: 'Mana Sphere', capture: false }, + delaySeconds: 0.1, + suppressSeconds: 9999, infoText: (data, _matches, output) => { + const popSide = data.manaSpherePopSide; + const blackHole = data.firstBlackHole; + const sphereId1 = data.closeManaSphereIds[0]; + const sphereId2 = data.closeManaSphereIds[1]; + if ( + popSide === undefined || + blackHole === undefined || + sphereId1 === undefined || + sphereId2 === undefined + ) + return data.myMutation === 'alpha' ? output.alpha!() : output.beta!(); + + const sphereType1 = data.manaSpheres[sphereId1]; + const sphereType2 = data.manaSpheres[sphereId2]; + if (sphereType1 === undefined || sphereType2 === undefined) + return data.myMutation === 'alpha' ? output.alpha!() : output.beta!(); + if (data.myMutation === 'alpha') - return output.alpha!(); - return output.beta!(); + return output.alphaDir!({ + dir1: output[popSide]!(), + northSouth: output.northSouth!(), + dir2: output[blackHole]!(), + }); + return output.betaDir!({ + dir1: output[popSide]!(), + shape1: output[sphereType1]!(), + shape2: output[sphereType2]!(), + northSouth: output.northSouth!(), + dir2: output[blackHole]!(), + }); }, outputStrings: { + east: Outputs.east, + west: Outputs.west, + northSouth: { + en: 'N/S', + de: 'N/S', + fr: 'N/S', + ja: '南/北', + cn: '上/下', + ko: '남/북', + tc: '上/下', + }, + water: { + en: 'Orb', + }, + lightning: { + en: 'Lightning', + }, + fire: { + en: 'Fire', + }, + wind: { + en: 'Donut', + }, alpha: { en: 'Avoid Shape AoEs, Wait by Black Hole', }, beta: { en: 'Shared Shape Soak => Get by Black Hole', }, + alphaDir: { + en: 'Avoid ${dir1} Shape AoEs => ${dir2} Black Hole + ${northSouth}', + }, + betaDir: { + en: 'Share ${dir1} ${shape1}/${shape2} => ${dir2} Black Hole + ${northSouth}', + }, }, }, { @@ -2271,15 +2479,37 @@ const triggerSet: TriggerSet = { // B502 Lindwurm's Aero III // B503 Straightforward Thunder II // B504 Sideways Fire II - // TODO: Tell which Black Hole and its mechanics? - type: 'StartsUsing', + type: 'Ability', netRegex: { id: ['B501', 'B502', 'B503', 'B504'], source: 'Lindwurm', capture: false }, suppressSeconds: 9999, - alertText: (_data, _matches, output) => output.move!(), + alertText: (data, _matches, output) => { + const blackHole = data.firstBlackHole; + if (blackHole === undefined) + return output.move!(); + const next = blackHole === 'east' ? 'west' : 'east'; + return output.moveDir!({ + northSouth: output.northSouth!(), + dir: output[next]!(), + }); + }, outputStrings: { + east: Outputs.east, + west: Outputs.west, + northSouth: { + en: 'N/S', + de: 'N/S', + fr: 'N/S', + ja: '南/北', + cn: '上/下', + ko: '남/북', + tc: '上/下', + }, move: { en: 'Move to other Black Hole', }, + moveDir: { + en: '${dir} Black Hole + ${northSouth}', + }, }, }, { From 58925a914bdfbd7e314a1060aec040a8ae06a6a2 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 26 Jan 2026 07:41:31 -0500 Subject: [PATCH 079/139] fixes + timelineReplace for netherworld --- ui/raidboss/data/07-dt/raid/r12s.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 025dd857e58..2eb0b8ca383 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -2253,7 +2253,7 @@ const triggerSet: TriggerSet = { }, }, { - id: 'R12S Reenactment 1 Clone Stacks', + id: 'R12S Reenactment 1 Final Defamation Dodge Reminder', // Players need to run back to north after clone stacks (BE5D Heavy Slam) // The clone stacks become a defamation and the other a cleave going East or West through the room // NOTE: This is used with DN Strategy @@ -2487,7 +2487,7 @@ const triggerSet: TriggerSet = { if (blackHole === undefined) return output.move!(); const next = blackHole === 'east' ? 'west' : 'east'; - return output.moveDir!({ + return output.moveDir!({ northSouth: output.northSouth!(), dir: output[next]!(), }); @@ -2768,6 +2768,7 @@ const triggerSet: TriggerSet = { 'locale': 'en', 'replaceText': { 'Netherwrath Near/Netherwrath Far': 'Netherwrath Near/Far', + 'Netherworld Near/Netherwworld Far': 'Netherworld Near/Far', }, }, ], From 3c991caeab655faa5c4c8b51f1854c672ac792d9 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 26 Jan 2026 08:32:37 -0500 Subject: [PATCH 080/139] refactor double sobat 2 This appears to be more accurate and not require delay by just predicting what the boss is going to turn to. --- ui/raidboss/data/07-dt/raid/r12s.ts | 41 ++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 2eb0b8ca383..6721da8ca54 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1631,20 +1631,35 @@ const triggerSet: TriggerSet = { { id: 'R12S Double Sobat 2', // Followup half-room cleave: - // - No turn - // - 90 degree turn - // - 180 degree turn - // - 270 degree turn - type: 'StartsUsing', - netRegex: { id: 'B525', source: 'Lindwurm', capture: true }, - delaySeconds: 0.1, // Need to delay for actor position update - alertText: (data, matches, output) => { - const actor = data.actorPositions[matches.sourceId]; - if (actor === undefined) - return output.getBehind!(); + // B521 Double Sobat: 0 degree left turn then B525 + // B522 Double Sobat: 90 degree left turn then B525 + // B523 Double Sobat: 180 degree left turn then B525 + // B524 Double Sobat: 270 degree left turn (this ends up 90 degrees to the right) + type: 'Ability', + suppressSeconds: 1, + netRegex: { id: ['B521', 'B522', 'B523', 'B524'], source: 'Lindwurm', capture: true }, + alertText: (_data, matches, output) => { + const hdg = parseFloat(matches.heading); + const dirNum = Directions.hdgTo16DirNum(hdg); + const getNewDirNum = ( + dirNum: number, + id: string, + ): number => { + switch (id) { + case 'B521': + return dirNum; + case 'B522': + return dirNum - 4; + case 'B523': + return dirNum - 8; + case 'B524': + return dirNum - 12; + } + throw new UnreachableCode(); + }; - const dirNum = (Directions.hdgTo16DirNum(actor.heading) + 8) % 16; - const dir = Directions.output16Dir[dirNum] ?? 'unknown'; + const newDirNum = (getNewDirNum(dirNum, matches.id) + 8) % 16; + const dir = Directions.output16Dir[newDirNum] ?? 'unknown'; return output.getBehindDir!({ dir: output[dir]!(), mech: output.getBehind!(), From 112f32496b4a865eedc69270fb4b7b8c6dc0508d Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 26 Jan 2026 08:35:46 -0500 Subject: [PATCH 081/139] lint --- ui/raidboss/data/07-dt/raid/r12s.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 6721da8ca54..9b06f6c2359 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1636,8 +1636,8 @@ const triggerSet: TriggerSet = { // B523 Double Sobat: 180 degree left turn then B525 // B524 Double Sobat: 270 degree left turn (this ends up 90 degrees to the right) type: 'Ability', - suppressSeconds: 1, netRegex: { id: ['B521', 'B522', 'B523', 'B524'], source: 'Lindwurm', capture: true }, + suppressSeconds: 1, alertText: (_data, matches, output) => { const hdg = parseFloat(matches.heading); const dirNum = Directions.hdgTo16DirNum(hdg); From bddcba6462ed8ad462877b3bcbc290e3b34ca780 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 26 Jan 2026 08:50:17 -0500 Subject: [PATCH 082/139] lindwurm's meteor is a bigAoe and healer group split There's also cleaves to avoid at this time, which possibly will be called a second or two before this. --- ui/raidboss/data/07-dt/raid/r12s.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 9b06f6c2359..b9ede17a702 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -2653,9 +2653,18 @@ const triggerSet: TriggerSet = { id: 'R12S Lindwurm\'s Meteor', type: 'StartsUsing', netRegex: { id: 'B4F2', source: 'Lindwurm', capture: false }, - infoText: (_data, _matches, output) => output.healerGroups!(), + alertText: (_data, _matches, output) => { + return output.text!({ + mech1: output.bigAoe!(), + mech2: output.healerGroups!() + }); + }, outputStrings: { healerGroups: Outputs.healerGroups, + bigAoe: Outputs.bigAoe, + text: { + en: '${mech1} => ${mech2}', + }, }, }, { From a81c4554a7a171f6d557137616e46e478e364bea Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 26 Jan 2026 08:51:50 -0500 Subject: [PATCH 083/139] wording Both events happen with the ability. --- ui/raidboss/data/07-dt/raid/r12s.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index b9ede17a702..481242662c6 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -2663,7 +2663,7 @@ const triggerSet: TriggerSet = { healerGroups: Outputs.healerGroups, bigAoe: Outputs.bigAoe, text: { - en: '${mech1} => ${mech2}', + en: '${mech1} + ${mech2}', }, }, }, From be619209487191b14316b433b6aed9ee5369af59 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 26 Jan 2026 08:52:37 -0500 Subject: [PATCH 084/139] lint --- ui/raidboss/data/07-dt/raid/r12s.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 481242662c6..64cf884c0ba 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -2656,7 +2656,7 @@ const triggerSet: TriggerSet = { alertText: (_data, _matches, output) => { return output.text!({ mech1: output.bigAoe!(), - mech2: output.healerGroups!() + mech2: output.healerGroups!(), }); }, outputStrings: { From bef2f65b312a66d2569bc2a4c008131e3372095e Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 26 Jan 2026 09:58:53 -0500 Subject: [PATCH 085/139] remove unused output --- ui/raidboss/data/07-dt/raid/r12s.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 64cf884c0ba..7d2af0f1ec1 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1922,15 +1922,6 @@ const triggerSet: TriggerSet = { outputStrings: { ...Directions.outputStrings8Dir, defamationOnYou: Outputs.defamationOnYou, - stackGroups: { - en: 'Stack Groups', - de: 'Gruppen-Sammeln', - fr: 'Package en groupes', - ja: '組み分け頭割り', - cn: '分组分摊', - ko: '그룹별 쉐어', - tc: '分組分攤', - }, baitProtean: { en: 'Bait Protean from Boss', }, From c2487f8b7ba692344e313eeba2a1124d77b31374 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 26 Jan 2026 11:28:42 -0500 Subject: [PATCH 086/139] add idyllic dream triggers up to start of 4th vision Replication 4 Locked Tether 2 is replacing the Lindwurm Meteor trigger, however there are a lot of things going on that need to be resolved in a quick order. The duration has been set to 8, which is when the cast ends. Unsure on the limit to the room split, it looks like it's halfway through the Downfall cast, so maybe 3.1 to 4.6s extra after the meteor hit? I imagine there is some MapEffect or control for this. Immediately on tether snap, you have a few seconds to dodge clone cleaves. Technically you could try to position prior to this with risk that your tether hasn't yet locked in. Simultaneously with the cleaves is the big Aoe and room split. Also, there is a chance the tether you got was messed up and you end up with a different tether than you picked up but right now it's still reminding which tether you locked with which. The tether itself will probably get a trigger to address the 4rth vision so that detail could be dropped, the right clone order still needs to be checked and matched to the tether the player chose. --- ui/raidboss/data/07-dt/raid/r12s.ts | 229 ++++++++++++++++++++++++++-- 1 file changed, 217 insertions(+), 12 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 7d2af0f1ec1..7640479d3a9 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -60,7 +60,11 @@ export interface Data extends RaidbossData { closeManaSphereIds: string[]; firstBlackHole?: 'east' | 'west'; manaSpherePopSide?: 'east' | 'west'; + twistedVisionCounter: number; replication3CloneOrder: number[]; + idyllicVision2NorthSouthCleaveSpot?: 'north' | 'south'; + replication4TetherMap: { [dirNum: string]: string }; + myReplication4Tether?: string; hasLightResistanceDown: boolean; doomPlayers: string[]; } @@ -131,7 +135,9 @@ const triggerSet: TriggerSet = { westManaSpheres: {}, eastManaSpheres: {}, closeManaSphereIds: [], + twistedVisionCounter: 0, replication3CloneOrder: [], + replication4TetherMap: {}, hasLightResistanceDown: false, doomPlayers: [], }), @@ -189,6 +195,15 @@ const triggerSet: TriggerSet = { data.phase = 'reenactment2'; }, }, + { + id: 'R12S Phase Two Twisted Vision Tracker', + // Used for keeping track of phases in idyllic + type: 'StartsUsing', + netRegex: { id: 'BBE2', source: 'Lindwurm', capture: false }, + run: (data) => { + data.twistedVisionCounter = data.twistedVisionCounter + 1; + }, + }, { id: 'R12S Phase Two ActorSetPos Tracker', type: 'ActorSetPos', @@ -1722,7 +1737,7 @@ const triggerSet: TriggerSet = { }, }, { - id: 'R12S Replication 2 Ability Tethers Collect', + id: 'R12S Replication 2 and Replication 4 Ability Tethers Collect', // Record and store a map of where the tethers come from and what they do for later // Boss tether handled separately since boss can move around type: 'Tether', @@ -1734,13 +1749,20 @@ const triggerSet: TriggerSet = { ], capture: true, }, - condition: (data) => data.phase === 'replication2', + condition: (data) => { + if (data.phase === 'replication2' || data.phase === 'idyllic') + return true; + return false; + }, run: (data, matches) => { const actor = data.actorPositions[matches.sourceId]; if (actor === undefined) return; const dirNum = Directions.xyTo8DirNum(actor.x, actor.y, center.x, center.y); - data.replication2TetherMap[dirNum] = matches.id; + if (data.phase === 'replication2') + data.replication2TetherMap[dirNum] = matches.id; + if (data.phase === 'idyllic') + data.replication4TetherMap[dirNum] = matches.id; }, }, { @@ -2641,25 +2663,208 @@ const triggerSet: TriggerSet = { }, }, { - id: 'R12S Lindwurm\'s Meteor', + id: 'R12S Idyllic Dream Power Gusher Collect', + // Need to know this for later + // B511 Snaking Kick + // B512 from boss is the VFX and has headings that show directions for B50F and B510 + // B50F Power Gusher is the East/West caster + // B510 Power Gusher is the North/South caster + // Right now just the B510 caster is needed to resolve type: 'StartsUsing', - netRegex: { id: 'B4F2', source: 'Lindwurm', capture: false }, - alertText: (_data, _matches, output) => { - return output.text!({ - mech1: output.bigAoe!(), - mech2: output.healerGroups!(), + netRegex: { id: 'B510', source: 'Lindschrat', capture: true }, + run: (data, matches) => { + const y = parseFloat(matches.y); + data.idyllicVision2NorthSouthCleaveSpot = y < center.y ? 'north' : 'south'; + }, + }, + { + id: 'R12S Replication 4 Ability Tethers Initial Call', + type: 'Tether', + netRegex: { + id: [ + headMarkerData['manaBurstTether'], + headMarkerData['heavySlamTether'], + ], + capture: true, + }, + condition: (data, matches) => { + if (data.me === matches.target && data.phase === 'idyllic') + return true; + return false; + }, + suppressSeconds: 9999, // Can get spammy if players have more than 1 tether or swap a lot + infoText: (data, matches, output) => { + // Get direction of the tether + const actor = data.actorPositions[matches.sourceId]; + if (actor === undefined) { + switch (matches.id) { + case headMarkerData['manaBurstTether']: + return output.manaBurstTether!(); + case headMarkerData['heavySlamTether']: + return output.heavySlamTether!(); + } + return; + } + + const dirNum = Directions.xyTo8DirNum(actor.x, actor.y, center.x, center.y); + const dir = Directions.output8Dir[dirNum] ?? 'unknown'; + + switch (matches.id) { + case headMarkerData['manaBurstTether']: + return output.manaBurstTetherDir!({ dir: output[dir]!() }); + case headMarkerData['heavySlamTether']: + return output.heavySlamTetherDir!({ dir: output[dir]!() }); + } + }, + outputStrings: { + ...Directions.outputStrings8Dir, + manaBurstTether: { + en: 'Defamation Tether on YOU', + }, + manaBurstTetherDir: { + en: '${dir} Defamation Tether on YOU', + }, + heavySlamTether: { + en: 'Stack Tether on YOU', + }, + heavySlamTetherDir: { + en: '${dir} Stack Tether on YOU', + }, + }, + }, + { + id: 'R12S Replication 4 Locked Tether 2 Collect', + type: 'Tether', + netRegex: { id: headMarkerData['lockedTether'], capture: true }, + condition: (data, matches) => { + if ( + data.phase === 'idyllic' && + data.replicationCounter === 4 && + data.me === matches.target + ) + return true; + return false; + }, + run: (data, matches) => { + const actor = data.actorPositions[matches.sourceId]; + if (actor === undefined) { + // Setting to use that we know we have a tether but couldn't determine what ability it is + data.myReplication4Tether = 'unknown'; + return; + } + + const dirNum = Directions.xyTo8DirNum( + actor.x, + actor.y, + center.x, + center.y, + ); + + // Lookup what the tether was at the same location + const ability = data.replication4TetherMap[dirNum]; + if (ability === undefined) { + // Setting to use that we know we have a tether but couldn't determine what ability it is + data.myReplication4Tether = 'unknown'; + return; + } + data.myReplication4Tether = ability; + }, + }, + { + id: 'R12S Replication 4 Locked Tether 2', + // At this point the player needs to dodge the north/south cleaves + chariot + // Simultaneously there will be a B4F2 Lindwurm's Meteor bigAoe that ends with room split + type: 'Tether', + netRegex: { id: headMarkerData['lockedTether'], capture: true }, + condition: (data, matches) => { + if ( + data.phase === 'idyllic' && + data.twistedVisionCounter === 3 && + data.me === matches.target + ) + return true; + return false; + }, + delaySeconds: 0.1, + durationSeconds: 8, + alertText: (data, matches, output) => { + const meteorAoe = output.meteorAoe!({ + bigAoe: output.bigAoe!(), + groups: output.healerGroups!(), + }); + const cleaveOrigin = data.idyllicVision2NorthSouthCleaveSpot; + // Get direction of the tether + const actor = data.actorPositions[matches.sourceId]; + if (actor === undefined || cleaveOrigin === undefined) { + switch (data.myReplication4Tether) { + case headMarkerData['manaBurstTether']: + return output.manaBurstTether!({ meteorAoe: meteorAoe }); + case headMarkerData['heavySlamTether']: + return output.heavySlamTether!({ meteorAoe: meteorAoe }); + } + return; + } + + const dirNum = Directions.xyTo8DirNum(actor.x, actor.y, center.x, center.y); + const dir = Directions.output8Dir[dirNum] ?? 'unknown'; + + const dodge = output.dodgeCleaves!({ + dir: output[cleaveOrigin]!(), + sides: output.sides!(), }); + + switch (data.myReplication4Tether) { + case headMarkerData['manaBurstTether']: + return output.manaBurstTetherDir!({ + dir: output[dir]!(), + dodgeCleaves: dodge, + meteorAoe: meteorAoe, + }); + case headMarkerData['heavySlamTether']: + return output.heavySlamTetherDir!({ + dir: output[dir]!(), + dodgeCleaves: dodge, + meteorAoe: meteorAoe, + }); + } }, outputStrings: { - healerGroups: Outputs.healerGroups, + ...Directions.outputStrings8Dir, + north: Outputs.north, + south: Outputs.south, + sides: Outputs.sides, bigAoe: Outputs.bigAoe, - text: { - en: '${mech1} + ${mech2}', + healerGroups: Outputs.healerGroups, + meteorAoe: { + en: '${bigAoe} + ${groups}', + }, + dodgeCleaves: { + en: '${dir} + ${sides}', + }, + manaBurstTetherDir: { + en: '${dodgeCleaves} (${dir} Defamation Tether) => ${meteorAoe}', + }, + manaBurstTether: { + en: ' N/S Clone (Defamation Tether) => ${meteorAoe}', + }, + heavySlamTetherDir: { + en: '${dodgeCleaves} (${dir} Stack Tether) => ${meteorAoe}', + }, + heavySlamTether: { + en: ' N/S Clone (Stack Tether) => ${meteorAoe}', }, }, }, + { + id: 'R12S Arcadian Arcanum', + // Players hit will receive 1044 Light Resistance Down II debuff + type: 'StartsUsing', + netRegex: { id: 'B529', source: 'Lindwurm', capture: false }, + response: Responses.spread(), + }, { id: 'R12S Light Resistance Down II Collect', + // Players cannot soak a tower that has holy (triple element towers) type: 'GainsEffect', netRegex: { effectId: '1044', capture: true }, condition: Conditions.targetIsYou(), From 5794f8df3cf5fbedfadf331e264f1aa93105e5c7 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 26 Jan 2026 17:43:43 -0500 Subject: [PATCH 087/139] add avoid cleave for non-rotting flesh players Co-authored-by: xiashtra <91220277+xiashtra@users.noreply.github.com> --- ui/raidboss/data/07-dt/raid/r12s.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 7640479d3a9..a292b8ccbb3 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1233,10 +1233,10 @@ const triggerSet: TriggerSet = { en: 'Spread in East Cleave', }, safeEast: { - en: 'Spread East', + en: 'Spread East + Avoid Cleave', }, safeWest: { - en: 'Spread West', + en: 'Spread West + Avoid Cleave', }, }, }, From 1370f03f456799fdef702e8a6ea67b061f6635dc Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 26 Jan 2026 17:48:26 -0500 Subject: [PATCH 088/139] remove unnecessary beta/unknown chains for curtain call --- ui/raidboss/data/07-dt/raid/r12s.ts | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index a292b8ccbb3..42be0d6e8ef 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1275,30 +1275,20 @@ const triggerSet: TriggerSet = { }, }, { - id: 'R12S Curtain Call: Unbreakable Flesh α/β Chains', + id: 'R12S Curtain Call: Unbreakable Flesh α Chains', + // All players, including dead, receive α debuffs // TODO: Find safe spots type: 'GainsEffect', - netRegex: { effectId: ['1291', '1293'], capture: true }, + netRegex: { effectId: '1291', capture: true }, condition: (data, matches) => { if (matches.target === data.me && data.phase === 'curtainCall') return true; return false; }, - infoText: (_data, matches, output) => { - const flesh = matches.effectId === '1291' ? 'alpha' : 'beta'; - if (flesh === 'alpha') - return output.alphaChains!({ - chains: output.breakChains!(), - safe: output.safeSpots!(), - }); - if (flesh === 'beta') - return output.betaChains!({ - chains: output.breakChains!(), - safe: output.breakChains!(), - }); - return output.unknownChains!({ + infoText: (_data, _matches, output) => { + return output.alphaChains!({ chains: output.breakChains!(), - safe: output.breakChains!(), + safe: output.safeSpots!(), }); }, outputStrings: { @@ -1309,12 +1299,6 @@ const triggerSet: TriggerSet = { alphaChains: { en: '${chains} => ${safe}', }, - betaChains: { - en: '${chains} => ${safe}', - }, - unknownChains: { - en: '${chains} => ${safe}', - }, }, }, { From 0fac8e7baf2ed76b9100899bb48d0bd887c46fce Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 26 Jan 2026 17:50:59 -0500 Subject: [PATCH 089/139] increase Raptor Knuckles Right details Co-authored-by: xiashtra <91220277+xiashtra@users.noreply.github.com> --- ui/raidboss/data/07-dt/raid/r12s.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 42be0d6e8ef..34fb39f6228 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1367,7 +1367,7 @@ const triggerSet: TriggerSet = { infoText: (_data, _matches, output) => output.text!(), outputStrings: { text: { - en: 'Northwest: Knockback to Northeast', + en: 'Knockback from Northwest => Knockback from Northeast', }, }, }, From 52fd452a5d95cca3a8758ec84e48f7ca165cfd0e Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 26 Jan 2026 17:51:26 -0500 Subject: [PATCH 090/139] increase Raptor Knuckles Right output Co-authored-by: xiashtra <91220277+xiashtra@users.noreply.github.com> --- ui/raidboss/data/07-dt/raid/r12s.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 34fb39f6228..ba123166e9b 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1381,7 +1381,7 @@ const triggerSet: TriggerSet = { infoText: (_data, _matches, output) => output.text!(), outputStrings: { text: { - en: 'Northeast: Knockback to Northwest', + en: 'Knockback from Northeast => Knockback from Northwest', }, }, }, From ae8a11c63ddd52970f25578722f6d78059372c95 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 26 Jan 2026 18:22:43 -0500 Subject: [PATCH 091/139] add config for uptime knockback It's back and with the same timing from e8s. --- ui/raidboss/data/07-dt/raid/r12s.ts | 41 +++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index ba123166e9b..0ace58f3f70 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -26,6 +26,9 @@ type DirectionCardinal = Exclude; type DirectionIntercard = Exclude; export interface Data extends RaidbossData { + readonly triggerSetConfig: { + uptimeKnockbackStrat: true | false; + }; phase: Phase; // Phase 1 grotesquerieCleave?: @@ -115,6 +118,28 @@ const isIntercardDir = (dir: DirectionOutput8): dir is DirectionIntercard => { const triggerSet: TriggerSet = { id: 'AacHeavyweightM4Savage', zoneId: ZoneId.AacHeavyweightM4Savage, + config: [ + { + id: 'uptimeKnockbackStrat', + name: { + en: 'Enable uptime knockback strat', + de: 'Aktiviere Uptime Rückstoß Strategie', + fr: 'Activer la strat Poussée-Uptime', + ja: 'エデン零式共鳴編4層:cactbot「ヘヴンリーストライク (ノックバック)」ギミック', // FIXME + cn: '启用击退镜 uptime 策略', + ko: '정확한 타이밍 넉백방지 공략 사용', + tc: '啟用擊退鏡 uptime 策略', + }, + comment: { + en: `If you want cactbot to callout Raptor Knuckles double knockback, enable this option. + Callout happens during/after first animation and requires <1.4s reaction time + to avoid both Northwest and Northeast knockbacks. + NOTE: This will call for each set.`, + }, + type: 'checkbox', + default: false, + }, + ], timelineFile: 'r12s.txt', initData: () => ({ phase: 'doorboss', @@ -1385,6 +1410,22 @@ const triggerSet: TriggerSet = { }, }, }, + { + id: 'R12S Raptor Knuckles Uptime Knockback', + // First knockback is at 7.3s + // Second knockback is at 11.9s + // Use knockback at 5.9s to hit both with ~1.4s leniency + type: 'Ability', + netRegex: { id: ['B4CC', 'B4CD'], source: 'Lindwurm', capture: false }, + condition: (data) => { + if (data.phase === 'slaughtershed' && data.triggerSetConfig.uptimeKnockbackStrat) + return true; + return false; + }, + delaySeconds: 5.9, + durationSeconds: 1.4, + response: Responses.knockback(), + }, { id: 'R12S Refreshing Overkill', // 10s castTime that could end with enrage or raidwide From 1ef1552d270e68e76297f1e1269b8bba40ab4175 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 26 Jan 2026 18:25:23 -0500 Subject: [PATCH 092/139] lint --- ui/raidboss/data/07-dt/raid/r12s.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 0ace58f3f70..e9b4d9b955e 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -27,7 +27,7 @@ type DirectionIntercard = Exclude; export interface Data extends RaidbossData { readonly triggerSetConfig: { - uptimeKnockbackStrat: true | false; + uptimeKnockbackStrat: true | false; }; phase: Phase; // Phase 1 From d5ea00466573edf3df44666e1cdf88325d4fce4f Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 26 Jan 2026 21:17:37 -0500 Subject: [PATCH 093/139] update kb timing, add shapes reminder, add idyllic gush later --- ui/raidboss/data/07-dt/raid/r12s.ts | 74 +++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 4 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index e9b4d9b955e..824365d56f5 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1412,9 +1412,9 @@ const triggerSet: TriggerSet = { }, { id: 'R12S Raptor Knuckles Uptime Knockback', - // First knockback is at 7.3s - // Second knockback is at 11.9s - // Use knockback at 5.9s to hit both with ~1.4s leniency + // First knockback is at ~13.2s + // Second knockback is at ~18s + // Use knockback at 11.9s to hit both with ~1.4s leniency type: 'Ability', netRegex: { id: ['B4CC', 'B4CD'], source: 'Lindwurm', capture: false }, condition: (data) => { @@ -1422,7 +1422,7 @@ const triggerSet: TriggerSet = { return true; return false; }, - delaySeconds: 5.9, + delaySeconds: 11.8, durationSeconds: 1.4, response: Responses.knockback(), }, @@ -2454,6 +2454,7 @@ const triggerSet: TriggerSet = { type: 'Ability', netRegex: { id: 'B4FD', source: 'Mana Sphere', capture: false }, delaySeconds: 0.1, + durationSeconds: 8.3, suppressSeconds: 9999, infoText: (data, _matches, output) => { const popSide = data.manaSpherePopSide; @@ -2525,6 +2526,52 @@ const triggerSet: TriggerSet = { }, }, }, + { + id: 'R12S Dramatic Lysis Black Hole 1 Reminder', + // This may not happen if all shapes are failed + type: 'Ability', + netRegex: { id: ['B507'], source: 'Lindwurm', capture: false }, + suppressSeconds: 9999, + alertText: (data, _matches, output) => { + const blackHole = data.firstBlackHole; + if (blackHole === undefined) + return data.myMutation === 'alpha' ? output.alpha!() : output.beta!(); + return data.myMutation === 'alpha' + ? output.alphaDir!({ + northSouth: output.northSouth!(), + dir2: output[blackHole]!(), + }) + : output.betaDir!({ + northSouth: output.northSouth!(), + dir2: output[blackHole]!(), + }); + }, + outputStrings: { + east: Outputs.east, + west: Outputs.west, + northSouth: { + en: 'N/S', + de: 'N/S', + fr: 'N/S', + ja: '南/北', + cn: '上/下', + ko: '남/북', + tc: '上/下', + }, + alpha: { + en: 'Get by Black Hole', + }, + beta: { + en: 'Get by Black Hole', + }, + alphaDir: { + en: '${dir2} Black Hole + ${northSouth}', + }, + betaDir: { + en: '${dir2} Black Hole + ${northSouth}', + }, + }, + }, { id: 'R12S Blood Wakening Followup', // Run to the other Black Hole after abilities go off @@ -2702,6 +2749,25 @@ const triggerSet: TriggerSet = { data.idyllicVision2NorthSouthCleaveSpot = y < center.y ? 'north' : 'south'; }, }, + { + id: 'R12S Idyllic Dream Power Gusher Vision', + // Call where the E/W safe spots will be later + type: 'StartsUsing', + netRegex: { id: 'B510', source: 'Lindschrat', capture: true }, + infoText: (_data, matches, output) => { + const y = parseFloat(matches.y); + const dir = y < center.y ? 'north' : 'south'; + return output.text!({ dir: output[dir]!(), sides: output.sides!() }); + }, + outputStrings: { + north: Outputs.north, + south: Outputs.south, + sides: Outputs.sides, + text: { + en: '${dir} + ${sides} (later)', + }, + }, + }, { id: 'R12S Replication 4 Ability Tethers Initial Call', type: 'Tether', From e64f9f853c63a2748d98b0094c380fd181dcd1fb Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 26 Jan 2026 21:21:55 -0500 Subject: [PATCH 094/139] comment --- ui/raidboss/data/07-dt/raid/r12s.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 824365d56f5..7eebb5f1f4e 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1414,7 +1414,7 @@ const triggerSet: TriggerSet = { id: 'R12S Raptor Knuckles Uptime Knockback', // First knockback is at ~13.2s // Second knockback is at ~18s - // Use knockback at 11.9s to hit both with ~1.4s leniency + // Use knockback at ~11.8s to hit both with ~1.4s leniency type: 'Ability', netRegex: { id: ['B4CC', 'B4CD'], source: 'Lindwurm', capture: false }, condition: (data) => { From fa0a141d818c60e9294997b5049360eedb7c0964 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 26 Jan 2026 21:55:49 -0500 Subject: [PATCH 095/139] fix wrong id used --- ui/raidboss/data/07-dt/raid/r12s.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 7eebb5f1f4e..529e45df823 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1416,7 +1416,7 @@ const triggerSet: TriggerSet = { // Second knockback is at ~18s // Use knockback at ~11.8s to hit both with ~1.4s leniency type: 'Ability', - netRegex: { id: ['B4CC', 'B4CD'], source: 'Lindwurm', capture: false }, + netRegex: { id: ['B4CC', 'B4CE'], source: 'Lindwurm', capture: false }, condition: (data) => { if (data.phase === 'slaughtershed' && data.triggerSetConfig.uptimeKnockbackStrat) return true; From 8ca40621af82112d5697100ec7c16d86e1354b91 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 26 Jan 2026 23:16:35 -0500 Subject: [PATCH 096/139] fix for -dirNum in sobat call --- ui/raidboss/data/07-dt/raid/r12s.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 529e45df823..cefb7ebc342 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1698,8 +1698,14 @@ const triggerSet: TriggerSet = { throw new UnreachableCode(); }; - const newDirNum = (getNewDirNum(dirNum, matches.id) + 8) % 16; - const dir = Directions.output16Dir[newDirNum] ?? 'unknown'; + const newDirNum = getNewDirNum(dirNum, matches.id); + + // Handle case where we have negative value + const positiveDirNum = ( + newDirNum + 8 < 0 ? Math.abs(newDirNum) : newDirNum + 8 + ) % 16; + + const dir = Directions.output16Dir[positiveDirNum] ?? 'unknown'; return output.getBehindDir!({ dir: output[dir]!(), mech: output.getBehind!(), From 5a75ca5ed4477b728debb4a6cd9a30694370c30c Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 26 Jan 2026 23:43:01 -0500 Subject: [PATCH 097/139] adjust timing compared to some more logs --- ui/raidboss/data/07-dt/raid/r12s.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index cefb7ebc342..097b5e27ef8 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -132,7 +132,7 @@ const triggerSet: TriggerSet = { }, comment: { en: `If you want cactbot to callout Raptor Knuckles double knockback, enable this option. - Callout happens during/after first animation and requires <1.4s reaction time + Callout happens during/after first animation and requires <1.8s reaction time to avoid both Northwest and Northeast knockbacks. NOTE: This will call for each set.`, }, @@ -1412,9 +1412,11 @@ const triggerSet: TriggerSet = { }, { id: 'R12S Raptor Knuckles Uptime Knockback', - // First knockback is at ~13.2s - // Second knockback is at ~18s - // Use knockback at ~11.8s to hit both with ~1.4s leniency + // First knockback is at ~13.374s + // Second knockback is at ~17.964s + // Use knockback at ~11.5s to hit both with ~1.8s leniency + // ~11.457s before is too late as it comes off the same time as hit + // ~11.554s before works (~0.134 before hit) type: 'Ability', netRegex: { id: ['B4CC', 'B4CE'], source: 'Lindwurm', capture: false }, condition: (data) => { @@ -1422,8 +1424,8 @@ const triggerSet: TriggerSet = { return true; return false; }, - delaySeconds: 11.8, - durationSeconds: 1.4, + delaySeconds: 11.5, + durationSeconds: 1.8, response: Responses.knockback(), }, { From 283fb08a60c274ec52d2aa8a3698ae7467dd8533 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 26 Jan 2026 23:45:06 -0500 Subject: [PATCH 098/139] clarify timing --- ui/raidboss/data/07-dt/raid/r12s.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 097b5e27ef8..a4ddcdd5cb4 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1416,7 +1416,7 @@ const triggerSet: TriggerSet = { // Second knockback is at ~17.964s // Use knockback at ~11.5s to hit both with ~1.8s leniency // ~11.457s before is too late as it comes off the same time as hit - // ~11.554s before works (~0.134 before hit) + // ~11.554s before works (surecast ends ~0.134 after hit) type: 'Ability', netRegex: { id: ['B4CC', 'B4CE'], source: 'Lindwurm', capture: false }, condition: (data) => { From 69ad3bde6a2c7d12ab5d5b8d91b3aba501438f52 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Tue, 27 Jan 2026 00:00:18 -0500 Subject: [PATCH 099/139] cleanup negative value handling this actually is the same issue report in p8s which should be fixed --- ui/raidboss/data/07-dt/raid/r12s.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index a4ddcdd5cb4..bf86940d480 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1702,12 +1702,10 @@ const triggerSet: TriggerSet = { const newDirNum = getNewDirNum(dirNum, matches.id); - // Handle case where we have negative value - const positiveDirNum = ( - newDirNum + 8 < 0 ? Math.abs(newDirNum) : newDirNum + 8 - ) % 16; + // Adding 16 incase of negative values + const newDirNum = (getNewDirNum(dirNum, matches.id) + 16 + 8) % 16; - const dir = Directions.output16Dir[positiveDirNum] ?? 'unknown'; + const dir = Directions.output16Dir[newDirNum] ?? 'unknown'; return output.getBehindDir!({ dir: output[dir]!(), mech: output.getBehind!(), From cb4066021461d1df753a69891f41ce89ef4af66f Mon Sep 17 00:00:00 2001 From: Legends0 Date: Tue, 27 Jan 2026 00:07:33 -0500 Subject: [PATCH 100/139] fix paste error --- ui/raidboss/data/07-dt/raid/r12s.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index bf86940d480..e35b0e3caef 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1700,8 +1700,6 @@ const triggerSet: TriggerSet = { throw new UnreachableCode(); }; - const newDirNum = getNewDirNum(dirNum, matches.id); - // Adding 16 incase of negative values const newDirNum = (getNewDirNum(dirNum, matches.id) + 16 + 8) % 16; From 105ccea245db02bcf99c0a02e2015010081efdb8 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 29 Jan 2026 02:21:22 -0500 Subject: [PATCH 101/139] replication 4 first set This could work with any strategy... may go back to rep2 later to better track the clone pair + ability pair + player order. It's essentially the same code needed. Sorry for the massive amount of conditionals, the way it seems right now is complex and this was the simplest I came up with. --- ui/raidboss/data/07-dt/raid/r12s.ts | 259 ++++++++++++++++++++++++++-- 1 file changed, 246 insertions(+), 13 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index e35b0e3caef..dca7fc1e889 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -65,8 +65,12 @@ export interface Data extends RaidbossData { manaSpherePopSide?: 'east' | 'west'; twistedVisionCounter: number; replication3CloneOrder: number[]; + replication3CloneDirNumPlayers: { [dirNum: number]: string }; idyllicVision2NorthSouthCleaveSpot?: 'north' | 'south'; - replication4TetherMap: { [dirNum: string]: string }; + replication4DirNumAbility: { [dirNum: number]: string }; + replication4PlayerAbilities: { [player: string]: string }; + replication4PlayerOrder: string[]; + replication4AbilityOrder: string[]; myReplication4Tether?: string; hasLightResistanceDown: boolean; doomPlayers: string[]; @@ -162,7 +166,11 @@ const triggerSet: TriggerSet = { closeManaSphereIds: [], twistedVisionCounter: 0, replication3CloneOrder: [], - replication4TetherMap: {}, + replication3CloneDirNumPlayers: {}, + replication4DirNumAbility: {}, + replication4PlayerAbilities: {}, + replication4PlayerOrder: [], + replication4AbilityOrder: [], hasLightResistanceDown: false, doomPlayers: [], }), @@ -2706,7 +2714,29 @@ const triggerSet: TriggerSet = { }, }, { - id: 'R12S Idyllic Dream Replication Tethered Clone', + id: 'R12S Idyllic Dream Staging 2 Tethered Clone Collect', + // Map the locations to a player name + type: 'Tether', + netRegex: { id: headMarkerData['lockedTether'], capture: true }, + condition: (data) => { + if ( + data.phase === 'idyllic' && + data.replicationCounter === 2 + ) + return true; + return false; + }, + run: (data, matches) => { + const actor = data.actorPositions[matches.sourceId]; + if (actor === undefined) + return; + + const dirNum = Directions.xyTo8DirNum(actor.x, actor.y, center.x, center.y); + data.replication3CloneDirNumPlayers[dirNum] = matches.target; + }, + }, + { + id: 'R12S Idyllic Dream Staging 2 Tethered Clone', type: 'Tether', netRegex: { id: headMarkerData['lockedTether'], capture: true }, condition: (data, matches) => { @@ -2831,20 +2861,21 @@ const triggerSet: TriggerSet = { id: 'R12S Replication 4 Locked Tether 2 Collect', type: 'Tether', netRegex: { id: headMarkerData['lockedTether'], capture: true }, - condition: (data, matches) => { + condition: (data) => { if ( data.phase === 'idyllic' && - data.replicationCounter === 4 && - data.me === matches.target + data.replicationCounter === 4 ) return true; return false; }, run: (data, matches) => { const actor = data.actorPositions[matches.sourceId]; + const target = matches.target; if (actor === undefined) { // Setting to use that we know we have a tether but couldn't determine what ability it is - data.myReplication4Tether = 'unknown'; + if (data.me === target) + data.replication4PlayerAbilities[target] = 'unknown'; return; } @@ -2856,13 +2887,33 @@ const triggerSet: TriggerSet = { ); // Lookup what the tether was at the same location - const ability = data.replication4TetherMap[dirNum]; + const ability = data.replication4DirNumAbility[dirNum]; if (ability === undefined) { // Setting to use that we know we have a tether but couldn't determine what ability it is - data.myReplication4Tether = 'unknown'; + data.replication4PlayerAbilities[target] = 'unknown'; return; } - data.myReplication4Tether = ability; + data.replication4PlayerAbilities[target] = ability; + + // Create ability order once we have all 8 players + // If players had more than one tether previously, the extra tethers are randomly assigned + if (Object.keys(data.replication4PlayerAbilities).length === 8) { + const abilities = data.replication4PlayerAbilities; + const order = data.replication3CloneOrder; // Order in which clones spawned + const players = data.replication3CloneDirNumPlayers; // Direction of player's clone + + // Mechanics are resolved clockwise, get create order based on cards/inters + const first = order[0]; + if (first === undefined) + return; + const dirNumOrder = first % 2 === 0 ? [0, 2, 4, 6, 1, 3, 5, 7] : [1, 3, 5, 7, 0, 2, 4, 6]; + for (const dirNum of dirNumOrder) { + const player = players[dirNum] ?? 'unknown'; + const ability = abilities[player] ?? 'unknown'; + data.replication4PlayerOrder.push(player); + data.replication4AbilityOrder.push(ability); + } + } }, }, { @@ -2888,10 +2939,11 @@ const triggerSet: TriggerSet = { groups: output.healerGroups!(), }); const cleaveOrigin = data.idyllicVision2NorthSouthCleaveSpot; + const myAbility = data.replication4PlayerAbilities[data.me]; // Get direction of the tether const actor = data.actorPositions[matches.sourceId]; if (actor === undefined || cleaveOrigin === undefined) { - switch (data.myReplication4Tether) { + switch (myAbility) { case headMarkerData['manaBurstTether']: return output.manaBurstTether!({ meteorAoe: meteorAoe }); case headMarkerData['heavySlamTether']: @@ -2908,7 +2960,7 @@ const triggerSet: TriggerSet = { sides: output.sides!(), }); - switch (data.myReplication4Tether) { + switch (myAbility) { case headMarkerData['manaBurstTether']: return output.manaBurstTetherDir!({ dir: output[dir]!(), @@ -2989,10 +3041,191 @@ const triggerSet: TriggerSet = { }, outputStrings: { text: { - en: 'Soak a White/Star Meteor', + en: 'Soak a White/Star Meteor (later)', }, }, }, + { + id: 'R12S Twisted Vision 4 Stack/Defamation 1', + // Used for keeping track of phases in idyllic + type: 'StartsUsing', + netRegex: { id: 'BBE2', source: 'Lindwurm', capture: false }, + condition: (data) => data.twistedVisionCounter === 4, + response: (data, _matches, output) => { + // cactbot-builtin-response + output.responseOutputStrings = { + ...Directions.outputStrings8Dir, + stacks: Outputs.stacks, + avoidDefamation: { + en: 'Avoid Defamation', + }, + avoidStack: { + en: 'Avoid Stack', + de: 'Vermeide Sammeln', + fr: 'Évitez le package', + cn: '远离分摊', + ko: '쉐어징 피하기', + tc: '遠離分攤', + }, + defamationOnYou: Outputs.defamationOnYou, + stackOnYou: Outputs.stackOnYou, + stackOnPlayer: Outputs.stackOnPlayer, + defamations: { + en: 'Defamations', + de: 'Große AoE auf dir', + fr: 'Grosse AoE sur vous', + ja: '自分に巨大な爆発', + cn: '大圈点名', + ko: '광역 대상자', + tc: '大圈點名', + }, + oneMechThenOne: { + en: '${mech1} => ${mech2}' + }, + oneMechThenTwo: { + en: '${mech1} => ${mech2} + ${mech3}', + }, + twoMechsThenOne: { + en: '${mech1} + ${mech2} => ${mech3}', + }, + twoMechsThenTwo: { + en: '${mech1} + ${mech2} => ${mech3} + ${mech4}', + }, + }; + const abilityOrder = data.replication4AbilityOrder; + const playerOrder = data.replication4PlayerOrder; + if ( + abilityOrder === undefined || + playerOrder === undefined + ) + return; + + const ability1 = abilityOrder[0]; + const ability2 = abilityOrder[1]; + const player1 = playerOrder[0]; + const player2 = playerOrder[1]; + + // Get Stack/Defamation #2 details + const ability3 = abilityOrder[2]; + const ability4 = abilityOrder[3]; + const player3 = playerOrder[2]; + const player4 = playerOrder[3]; + + // Handle some obscure strategies or mistakes + const isThisSame = ability1 === ability2; + const isNextSame = ability3 === ability4; + const defamation = headMarkerData['manaBurstTether']; + let this1; + let this2; + let next1; + let next2; + // Handle This Set + if (player1 === data.me) { + this1 = ability1 === defamation ? 'defamationOnYou' : 'stackOnYou'; + if (!isThisSame) + this2 = ability2 === defamation ? 'avoidDefamation' : 'avoidStack'; + } else if (player2 === data.me) { + if (!isThisSame) { + this1 = ability1 === defamation ? 'avoidDefamation' : 'avoidStack'; + this2 = ability2 === defamation ? 'defamationOnYou' : 'stackOnYou'; + } else { + this1 = ability1 === defamation ? 'defamationOnYou' : 'stackOnYou'; + } + } else if (isThisSame) { + this1 = ability1 === defamation ? 'defamations' : 'stacks'; + } else if (!isThisSame) { + this1 = ability1 === defamation ? 'avoidDefamation' : 'stack'; + this2 = ability2 === defamation ? 'avoidDefamation' : 'stack'; + } + + // Handle Next Set + if (player3 === data.me) { + next1 = ability3 === defamation ? 'defamationOnYou' : 'stackOnYou'; + if (!isThisSame) + next2 = ability4 === defamation ? 'avoidDefamation' : 'avoidStack'; + } else if (player4 === data.me) { + if (!isThisSame) { + next1 = ability4 === defamation ? 'avoidDefamation' : 'avoidStack'; + next2 = ability4 === defamation ? 'defamationOnYou' : 'stackOnYou'; + } else { + next1 = ability4 === defamation ? 'defamationOnYou' : 'stackOnYou'; + } + } else if (isNextSame) { + next1 = ability3 === defamation ? 'defamations' : 'stacks'; + } else if (!isNextSame) { + next1 = ability3 === defamation ? 'avoidDefamation' : 'stack'; + next2 = ability4 === defamation ? 'avoidDefamation' : 'stack'; + } + + // Build output + if (this1 === undefined || next1 === undefined) + return; + const text = (player1 === data.me || player2 === data.me) ? 'alertText' : 'infoText'; + if (isThisSame && isNextSame) { + return { + [text]: output.oneMechThenOne!({ + mech1: output[this1]!(), + mech2: output[next1]!(), + }), + }; + } + + const shortPlayer3 = data.party.member(player3); + const shortPlayer4 = data.party.member(player4); + if (isThisSame && !isNextSame) { + if (next2 === undefined) + return; + return { + [text]: output.oneMechThenTwo!({ + mech1: output[this1]!(), + mech2: next1 === 'stack' + ? output.stackOnPlayer!({ player: shortPlayer3 }) + : output[next1]!(), + mech3: next2 === 'stack' + ? output.stackOnPlayer!({ player: shortPlayer4 }) + : output[next2]!(), + }) + }; + } + + const shortPlayer1 = data.party.member(player1); + const shortPlayer2 = data.party.member(player2); + if (!isThisSame && isNextSame) { + if (this2 === undefined) + return; + return { + [text]: output.twoMechsThenOne!({ + mech1: this1 === 'stack' + ? output.stackOnPlayer!({ player: shortPlayer1 }) + : output[this1]!(), + mech2: this2 === 'stack' + ? output.stackOnPlayer!({ player: shortPlayer2 }) + : output[this2]!(), + mech3: output[next1]!(), + }) + }; + } + + if (this2 === undefined || next2 === undefined) + return; + return { + [text]: output.twoMechsThenTwo!({ + mech1: this1 === 'stack' + ? output.stackOnPlayer!({ player: shortPlayer1 }) + : output[this1]!(), + mech2: this2 === 'stack' + ? output.stackOnPlayer!({ player: shortPlayer2 }) + : output[this2]!(), + mech3: next1 === 'stack' + ? output.stackOnPlayer!({ player: shortPlayer3 }) + : output[next1]!(), + mech4: next2 === 'stack' + ? output.stackOnPlayer!({ player: shortPlayer4 }) + : output[next2]!(), + }) + }; + }, + }, { id: 'R12S Doom Collect', type: 'GainsEffect', From 3f10baf000c4c99f61959f56d81c841a03b4924e Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 29 Jan 2026 02:23:14 -0500 Subject: [PATCH 102/139] missed a line --- ui/raidboss/data/07-dt/raid/r12s.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index dca7fc1e889..a84ae7af433 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1799,7 +1799,7 @@ const triggerSet: TriggerSet = { if (data.phase === 'replication2') data.replication2TetherMap[dirNum] = matches.id; if (data.phase === 'idyllic') - data.replication4TetherMap[dirNum] = matches.id; + data.replication4DirNumAbility[dirNum] = matches.id; }, }, { From 9203a9e4c944556da4b9cbbadbf881654a70b0d2 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 29 Jan 2026 02:27:53 -0500 Subject: [PATCH 103/139] lint --- ui/raidboss/data/07-dt/raid/r12s.ts | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index a84ae7af433..2f2e05ed050 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -3025,7 +3025,7 @@ const triggerSet: TriggerSet = { infoText: (_data, _matches, output) => output.text!(), outputStrings: { text: { - en: 'Soak Fire/Earth Meteor', + en: 'Soak Fire/Earth Meteor (later)', }, }, }, @@ -3080,7 +3080,7 @@ const triggerSet: TriggerSet = { tc: '大圈點名', }, oneMechThenOne: { - en: '${mech1} => ${mech2}' + en: '${mech1} => ${mech2}', }, oneMechThenTwo: { en: '${mech1} => ${mech2} + ${mech3}', @@ -3184,7 +3184,7 @@ const triggerSet: TriggerSet = { mech3: next2 === 'stack' ? output.stackOnPlayer!({ player: shortPlayer4 }) : output[next2]!(), - }) + }), }; } @@ -3202,7 +3202,7 @@ const triggerSet: TriggerSet = { ? output.stackOnPlayer!({ player: shortPlayer2 }) : output[this2]!(), mech3: output[next1]!(), - }) + }), }; } @@ -3211,18 +3211,18 @@ const triggerSet: TriggerSet = { return { [text]: output.twoMechsThenTwo!({ mech1: this1 === 'stack' - ? output.stackOnPlayer!({ player: shortPlayer1 }) - : output[this1]!(), + ? output.stackOnPlayer!({ player: shortPlayer1 }) + : output[this1]!(), mech2: this2 === 'stack' - ? output.stackOnPlayer!({ player: shortPlayer2 }) - : output[this2]!(), + ? output.stackOnPlayer!({ player: shortPlayer2 }) + : output[this2]!(), mech3: next1 === 'stack' - ? output.stackOnPlayer!({ player: shortPlayer3 }) - : output[next1]!(), + ? output.stackOnPlayer!({ player: shortPlayer3 }) + : output[next1]!(), mech4: next2 === 'stack' - ? output.stackOnPlayer!({ player: shortPlayer4 }) - : output[next2]!(), - }) + ? output.stackOnPlayer!({ player: shortPlayer4 }) + : output[next2]!(), + }), }; }, }, From 581d18cc1cd5bc2b2b762c282f0abe0ee3facc5b Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 29 Jan 2026 21:12:18 -0500 Subject: [PATCH 104/139] some cleanup to p2, add tethers/clones lines --- ui/raidboss/data/07-dt/raid/r12s.txt | 116 +++++++++++++++++---------- 1 file changed, 73 insertions(+), 43 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.txt b/ui/raidboss/data/07-dt/raid/r12s.txt index fa2c8f03f78..a6010b3aa0b 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.txt +++ b/ui/raidboss/data/07-dt/raid/r12s.txt @@ -285,6 +285,7 @@ hideall "--sync--" 1636.7 "Refreshing Overkill (Enrage)?" #Ability { id: "B538", source: "Lindwurm" } 1636.8 "--untargetable?--" 1636.8 "Refreshing Overkill (Enrage)?" #Ability { id: "B53A", source: "Lindwurm" } + 1646.7 "--sync--" StartsUsing { id: "B538", source: "Lindwurm" } window 20,3 forcejump "r12s-p1-enrage" # Raptor Knuckles Left Third @@ -327,12 +328,12 @@ hideall "--sync--" # Enrage sequence 2027.7 label "r12s-p1-enrage" -2036.7 "Refreshing Overkill (Enrage)?" Ability { id: "B538", source: "Lindwurm" } +2036.7 "Refreshing Overkill (Enrage)?" Ability { id: "B538", source: "Lindwurm" } window 510,5 2036.8 "--untargetable--" 2036.8 "Refreshing Overkill (Enrage)?" Ability { id: "B53A", source: "Lindwurm" } # Kill/Transition sequence -2036.8 "Refreshing Overkill" Ability { id: "B539", source: "Lindwurm" } window 510,5 +2036.8 "Refreshing Overkill" Ability { id: "B539", source: "Lindwurm" } 2073.0 "--sync--" Ability { id: "BB9C", source: "Lindwurm" } 2073.5 "Down for the Count" duration 42 2075.7 "--sync--" Ability { id: "B53B", source: "Lindwurm" } @@ -340,8 +341,8 @@ hideall "--sync--" ### Phase 2: Lindwurm II -# -p B528:3012.1 -# -ii B51F B4DA B4DB B4DD B4DF B4E3 B4E6 B4F0 B4E9 B4F1 B4EE B4EF B508 B4FF BCB0 +# -p B528:3015.7 +# -ii B51F B4DA B4DB B4DD B4DF B4E3 B4E6 B4F0 B4E9 B4F1 B4EE B4EF B508 B4F5 B512 B513 B514 B515 # -it "Lindwurm" 3000.5 label "r12s-p2-start" 3010.7 "--sync--" StartsUsing { id: "B528", source: "Lindwurm" } window 3100,10 @@ -365,8 +366,16 @@ hideall "--sync--" # NOTE: Depending on clone order and tethers player takes, spells at each instance will differ # TBD: Generalize these? 3091.2 "Staging" Ability { id: "B4E1", source: "Lindwurm" } +3092.5 "--clones x2 1--" ActorControlExtra { category: "0197", param1: "11D2" } +3094.0 "--clones x2 2--" ActorControlExtra { category: "0197", param1: "11D2" } +3095.5 "--clones x2 3--" ActorControlExtra { category: "0197", param1: "11D2" } +3097.0 "--clones x2 4--" ActorControlExtra { category: "0197", param1: "11D2" } +3099.1 "--locked tethers--" Tether { id: "0175", source: "Understudy" } 3102.2 "--sync--" Ability { id: "B4E2", source: "Lindwurm" } 3105.4 "Replication 2" Ability { id: "B4D8", source: "Lindwurm" } +3108.6 "--boss clones x6--" ActorControlExtra { category: "0197", param1: "11D5" } +3113.8 "--tethers--" #Tether { id: ["016F", "0170", "0171", "0176"] } # These could change hands causing sync issue +3121.8 "--locked tethers--" Tether { id: "0175", source: ["Lindschrat", "Lindwurm"] } 3127.8 "Firefall Splash" Ability { id: "B4E4", source: "Lindwurm" } 3128.5 "Scalding Waves x4" Ability { id: "B4E5", source: "Lindwurm" } 3129.9 "Mana Burst x3" Ability { id: "B4E7", source: "Lindwurm" } @@ -396,11 +405,15 @@ hideall "--sync--" 3190.0 "Blood Mana" Ability { id: "B4FB", source: "Lindwurm" } 3193.3 "--black holes--" Ability { id: "BCB0", source: "Mana Sphere" } 3193.7 "--shapes--" Ability { id: "B4FD", source: "Mana Sphere" } -3200.7 "Bloody Burst 1" #Ability { id: "B4FE", source: "Lindwurm" } # Goes off when soaked by player -3201.0 "Bloody Burst 2" #Ability { id: "B4FE", source: "Lindwurm" } # Goes off when soaked by player +3200.7 "Bloody Burst x2" #Ability { id: "B4FE", source: "Lindwurm" } # Goes off when soaked by player 3202.2 "Dramatic Lysis x8" Ability { id: "B507", source: "Lindwurm" } +3203.2 "--close shapes eaten--" Ability { id: "B4FF", source: "Mana Sphere" } +3203.1 "--sync--" Ability { id: "BCB0", source: "Mana Sphere" } # Blackhole 1 +3204.2 "--far shapes eaten--" Ability { id: "B4FF", source: "Mana Sphere" } +3206.0 "--sync--" Ability { id: "BCB0", source: "Mana Sphere" } # Blackhole 2 +3207.2 "--soaked shapes eaten--" Ability { id: "B4FF", source: "Mana Sphere" } +3209.1 "--sync--" Ability { id: "BCB0", source: "Mana Sphere" } # blackhole 1 -# TBD Clean-up/Generalize this part as its dependent on what was soaked and what spawned 3216.7 "Blood Wakening" Ability { id: "B500", source: "Lindwurm" } 3217.9 "--sync--" Ability { id: "B4FC", source: "Mana Sphere" } 3218.3 "Black Hole 1" Ability { id: ["B501", "B502", "B503", "B504"], source: "Lindwurm" } @@ -419,63 +432,73 @@ hideall "--sync--" # TBD Clean-up/Generalize this as well since stacks/defamation order is tether dependent 3268.8 "Idyllic Dream" Ability { id: "B509", source: "Lindwurm" } 3275.0 "Staging" Ability { id: "B4E1", source: "Lindwurm" } +3276.3 "--clones x4 1--" ActorControlExtra { category: "0197", param1: "11D2" } +3278.3 "--clones x4 2--" ActorControlExtra { category: "0197", param1: "11D2" } +3280.4 "--locked tethers--" Tether { id: "0175", source: "Understudy" } 3283.4 "--sync--" Ability { id: "B4E2", source: "Lindwurm" } -3290.1 "Twisted Vision" Ability { id: "BBE2", source: "Lindwurm" } +3290.1 "Twisted Vision 1" Ability { id: "BBE2", source: "Lindwurm" } 3296.2 "Replication 3" Ability { id: "B4D8", source: "Lindwurm" } -3308.6 "Twisted Vision" Ability { id: "BBE2", source: "Lindwurm" } -3309.6 "Power Gusher" #Ability { id: "B512", source: "Lindwurm" } -3309.6 "Power Gusher" #Ability { id: "B50F", source: "Lindschrat" } +3299.4 "--boss clones x3--" ActorControlExtra { category: "0197", param1: "11D5" } +3308.6 "Twisted Vision 2" Ability { id: "BBE2", source: "Lindwurm" } +3309.6 "Power Gusher x2" #Ability { id: ["B50F", "B510"], source: "Lindschrat" } 3309.6 "Snaking Kick" #Ability { id: "B511", source: "Lindschrat" } -3309.6 "Power Gusher" #Ability { id: "B510", source: "Lindschrat" } 3314.7 "Replication 4" Ability { id: "B4D8", source: "Lindwurm" } +3317.9 "--boss clones x2 1--" ActorControlExtra { category: "0197", param1: "11D5" } +3318.9 "--boss clones x2 2--" ActorControlExtra { category: "0197", param1: "11D5" } +3319.9 "--boss clones x2 3--" ActorControlExtra { category: "0197", param1: "11D5" } +3320.9 "--boss clones x2 4--" ActorControlExtra { category: "0197", param1: "11D5" } +3326.1 "--tethers--" #Tether { id: ["0170", "0171"], source: "Lindschrat" } # These could change hands causing sync issue +3334.1 "--locked tethers--" Tether { id: ["0170", "0171"], source: "Lindschrat" } # Towers Preview -3334.3 "Twisted Vision" Ability { id: "BBE2", source: "Lindwurm" } -3337.7 "Power Gusher" #Ability { id: "B513", source: "Lindschrat" } -3337.7 "Snaking Kick" Ability { id: "B515", source: "Lindschrat" } -3337.7 "Power Gusher" #Ability { id: "B514", source: "Lindschrat" } +3334.3 "Twisted Vision 3" Ability { id: "BBE2", source: "Lindwurm" } 3338.7 "Snaking Kick" Ability { id: "BE95", source: "Lindwurm" } -3338.9 "Power Gusher" #Ability { id: "B516", source: "Lindwurm" } +3338.9 "Power Gusher x4" #Ability { id: "B516", source: "Lindwurm" } # Front/Back are apparently counting as their own casts 3342.8 "Lindwurm's Meteor" Ability { id: "B4F2", source: "Lindwurm" } 3348.9 "Downfall" Ability { id: "B4F3", source: "Lindwurm" } -3355.0 "Arcadian Arcanum" Ability { id: "B529", source: "Lindwurm" } +3355.0 "Arcadian Arcanum (castbar)" Ability { id: "B529", source: "Lindwurm" } 3356.2 "Arcadian Arcanum" Ability { id: "B9D9", source: "Lindwurm" } # First Stacks/Defamations -3363.0 "Twisted Vision" Ability { id: "BBE2", source: "Lindwurm" } -3369.6 "Heavy Slam" Ability { id: "B519", source: "Lindschrat" } -3374.6 "Mana Burst" Ability { id: "B517", source: "Lindschrat" } -3375.8 "Mana Burst" Ability { id: "B518", source: "Lindwurm" } -3379.6 "Heavy Slam" Ability { id: "B519", source: "Lindschrat" } -3384.6 "Mana Burst" Ability { id: "B517", source: "Lindschrat" } +# Orders could be different, but we can detect B517 abiltiy to know if Mana Burst is coming 1.2s before B518 +3363.0 "Twisted Vision 4" Ability { id: "BBE2", source: "Lindwurm" } +3369.6 "Clone 1 Heavy Slam?" Ability { id: "B519", source: "Lindschrat" } # Or B517 Mana Burst (Lindschrat) happens here +3370.8 "Clone 1 Mana Burst?" Ability { id: "B518", source: "Lindwurm" } +3374.6 "Clone 2 Heavy Slam?" Ability { id: "B519", source: "Lindschrat" } # Or B517 Mana Burst (Lindschrat) happens here +3375.8 "Clone 2 Mana Burst?" Ability { id: "B518", source: "Lindwurm" } +3379.6 "Clone 3 Heavy Slam?" Ability { id: "B519", source: "Lindschrat" } # Or B517 Mana Burst (Lindschrat) happens here +3380.8 "Clone 3 Mana Burst?" Ability { id: "B518", source: "Lindwurm" } +3384.6 "Clone 4 Heavy Slam?" Ability { id: "B519", source: "Lindschrat" } # Or B517 Mana Burst (Lindschrat) happens here +3385.8 "Clone 4 Mana Burst?" Ability { id: "B518", source: "Lindwurm" } + +# Twisted Vision 5: Towers +3393.6 "Twisted Vision 5" Ability { id: "BBE2", source: "Lindwurm" } +3398.0 "Cosmic Kiss" Ability { id: "B4F4", source: "Lindwurm" } + +# fflogs adjusted by 4.1s +3398.7 "Lindwurm's Dark II" Ability { id: "B4F6", source: "Lindwurm" } +3400.7 "Pyretic Wurm" Ability { id: "B4F9", source: "Lindwurm" } +3403.7 "Lindwurm's Stone III" #Ability { id: "B4F7", source: "Lindwurm" } +3408.7 "Lindwurm's Thunder II" #Ability { id: "B4FA", source: "Lindwurm" } +3408.7 "Lindwurm's Glare" #Ability { id: "B4F8", source: "Lindwurm" } +3408.7 "Lindwurm's Thunder II" #Ability { id: "B4FA", source: "Lindwurm" } +3408.7 "Lindwurm's Glare" #Ability { id: "B4F8", source: "Lindwurm" } +3412.0 "Temporal Curtain" Ability { id: "B51C", source: "Lindwurm" } window 10,10 # fflogs -3389.5 "Twisted Vision" Ability { id: "BBE2", source: "Lindwurm" } window 10,10 -3393.9 "Cosmic Kiss" Ability { id: "B4F4", source: "Lindwurm" } -3394.6 "Lindwurm's Dark II" Ability { id: "B4F6", source: "Lindwurm" } -3396.6 "Pyretic Wurm" Ability { id: "B4F9", source: "Lindwurm" } -3399.6 "Lindwurm's Stone III" #Ability { id: "B4F7", source: "Lindwurm" } -3404.6 "Lindwurm's Thunder II" #Ability { id: "B4FA", source: "Lindwurm" } -3404.6 "Lindwurm's Glare" #Ability { id: "B4F8", source: "Lindwurm" } -3404.6 "Lindwurm's Thunder II" #Ability { id: "B4FA", source: "Lindwurm" } -3404.6 "Lindwurm's Glare" #Ability { id: "B4F8", source: "Lindwurm" } -3412.9 "Temporal Curtain" Ability { id: "B51C", source: "Lindwurm" } - 3416.0 "--sync--" Ability { id: "B51D", source: "Lindschrat" } 3419.1 "--sync--" Ability { id: "B4D9", source: "Lindschrat" } -3425.4 "Twisted Vision" Ability { id: "BBE2", source: "Lindwurm" } +3425.4 "Twisted Vision 6" Ability { id: "BBE2", source: "Lindwurm" } 3426.4 "Power Gusher" #Ability { id: "B50F", source: "Lindschrat" } 3426.4 "Snaking Kick" Ability { id: "BCAF", source: "Lindschrat" } 3431.5 "Reenactment 2" Ability { id: "B4EC", source: "Lindwurm" } 3433.5 "Heavy Slam" Ability { id: "B4EF", source: "Lindschrat" } 3434.7 "Mana Burst" Ability { id: "BBE3", source: "Lindwurm" } 3434.9 "Heavy Slam" #Ability { id: "BE5D", source: "Lindwurm" } -3440.5 "Twisted Vision" Ability { id: "BBE2", source: "Lindwurm" } -3444.1 "Power Gusher" #Ability { id: "B513", source: "Lindschrat" } -3444.1 "Snaking Kick" Ability { id: "B515", source: "Lindschrat" } +3440.5 "Twisted Vision 7" Ability { id: "BBE2", source: "Lindwurm" } 3445.1 "Snaking Kick" Ability { id: "BE95", source: "Lindwurm" } 3445.3 "Power Gusher" Ability { id: "B516", source: "Lindwurm" } -3448.2 "Twisted Vision" Ability { id: "BBE2", source: "Lindwurm" } +3448.2 "Twisted Vision 8" Ability { id: "BBE2", source: "Lindwurm" } 3454.5 "Heavy Slam" #Ability { id: "B4EF", source: "Lindschrat" } 3454.5 "--sync--" Ability { id: "B51E", source: "Lindschrat" } 3455.7 "Mana Burst" Ability { id: "BBE3", source: "Lindwurm" } @@ -533,8 +556,13 @@ hideall "--sync--" # B4EE Mana Burst: VFX used in Reenactment # B4EF Heavy Slam: VFX used in Reenactment # B508 Unmitigated Explosion: Getting hit when you have Mitigation α or failing to get hit with Mitigation β, applies 30s damage down -# B4FF --sync--: VFX related to Mana Spheres being eaten by Black Hole? -# BCB0 --sync--: VFX related to Mana Spheres being eaten by Black Hole? +# B4FF --sync--: VFX Mana Spheres eaten by Black Hole +# BCB0 --sync--: VFX Black Hole light eruption +# B4F5 Unmitigated Explosion: Missing Cosmic Kiss Tower (Twisted Vision 5) +# B512 Power Gusher: VFX during Twisted Vision 2 with B50F and B510 +# B513 Power Gusher: VFX during Twisted Vision 3, related to the B50F +# B514 Power Gusher: VFX during Twisted Vision 3, related to the B510 +# B515 Snaking Kick: VFX during Twisted Vision 3 # ALL ENCOUNTER ABILITIES # Phase 1 @@ -599,6 +627,7 @@ hideall "--sync--" # B4D5 Fourth-wall Fusion # B4D6 Visceral Burst # B4D7 The Fixer +# B4F5 Unmitigated Explosion # B538 Refreshing Overkill: Enrage castbar # B539 Refreshing Overkill: Non-enrage # B53A Refreshing Overkill: Enrage @@ -684,7 +713,7 @@ hideall "--sync--" # B514 Power Gusher # B515 Snaking Kick # B516 Power Gusher -# B517 Mana Burst +# B517 Mana Burst: VFX during Twisted Vision 4, happens 1.2s before B518, useful for sync branch # B518 Mana Burst # B519 Heavy Slam # B51A Power Gusher @@ -718,6 +747,7 @@ hideall "--sync--" # BBE2 Twisted Vision # BBE3 Mana Burst # BCAF Snaking Kick +# BCB0 --sync--: Blackhole spawn # BE5D Heavy Slam # BE95 Snaking Kick # BEC1 Arcadian Hell From 20d259f6ae51fe0415e6de2c7cec17199b1d282b Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 29 Jan 2026 21:22:58 -0500 Subject: [PATCH 105/139] fixes from test_timeline Apparently the ActorControlExtra matches just backsync with the ~1.5s matches, so commented them out. --- ui/raidboss/data/07-dt/raid/r12s.txt | 32 ++++++++++++++-------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.txt b/ui/raidboss/data/07-dt/raid/r12s.txt index a6010b3aa0b..5079b3eb9f8 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.txt +++ b/ui/raidboss/data/07-dt/raid/r12s.txt @@ -366,10 +366,10 @@ hideall "--sync--" # NOTE: Depending on clone order and tethers player takes, spells at each instance will differ # TBD: Generalize these? 3091.2 "Staging" Ability { id: "B4E1", source: "Lindwurm" } -3092.5 "--clones x2 1--" ActorControlExtra { category: "0197", param1: "11D2" } -3094.0 "--clones x2 2--" ActorControlExtra { category: "0197", param1: "11D2" } -3095.5 "--clones x2 3--" ActorControlExtra { category: "0197", param1: "11D2" } -3097.0 "--clones x2 4--" ActorControlExtra { category: "0197", param1: "11D2" } +3092.5 "--clones x2 1--" #ActorControlExtra { category: "0197", param1: "11D2" } +3094.0 "--clones x2 2--" #ActorControlExtra { category: "0197", param1: "11D2" } +3095.5 "--clones x2 3--" #ActorControlExtra { category: "0197", param1: "11D2" } +3097.0 "--clones x2 4--" #ActorControlExtra { category: "0197", param1: "11D2" } 3099.1 "--locked tethers--" Tether { id: "0175", source: "Understudy" } 3102.2 "--sync--" Ability { id: "B4E2", source: "Lindwurm" } 3105.4 "Replication 2" Ability { id: "B4D8", source: "Lindwurm" } @@ -408,11 +408,11 @@ hideall "--sync--" 3200.7 "Bloody Burst x2" #Ability { id: "B4FE", source: "Lindwurm" } # Goes off when soaked by player 3202.2 "Dramatic Lysis x8" Ability { id: "B507", source: "Lindwurm" } 3203.2 "--close shapes eaten--" Ability { id: "B4FF", source: "Mana Sphere" } -3203.1 "--sync--" Ability { id: "BCB0", source: "Mana Sphere" } # Blackhole 1 -3204.2 "--far shapes eaten--" Ability { id: "B4FF", source: "Mana Sphere" } -3206.0 "--sync--" Ability { id: "BCB0", source: "Mana Sphere" } # Blackhole 2 -3207.2 "--soaked shapes eaten--" Ability { id: "B4FF", source: "Mana Sphere" } -3209.1 "--sync--" Ability { id: "BCB0", source: "Mana Sphere" } # blackhole 1 +3204.1 "--sync--" Ability { id: "BCB0", source: "Mana Sphere" } # Blackhole 1 +3206.2 "--far shapes eaten--" Ability { id: "B4FF", source: "Mana Sphere" } +3207.0 "--sync--" Ability { id: "BCB0", source: "Mana Sphere" } # Blackhole 2 +3209.2 "--soaked shapes eaten--" Ability { id: "B4FF", source: "Mana Sphere" } +3210.1 "--sync--" Ability { id: "BCB0", source: "Mana Sphere" } # blackhole 1 3216.7 "Blood Wakening" Ability { id: "B500", source: "Lindwurm" } 3217.9 "--sync--" Ability { id: "B4FC", source: "Mana Sphere" } @@ -432,8 +432,8 @@ hideall "--sync--" # TBD Clean-up/Generalize this as well since stacks/defamation order is tether dependent 3268.8 "Idyllic Dream" Ability { id: "B509", source: "Lindwurm" } 3275.0 "Staging" Ability { id: "B4E1", source: "Lindwurm" } -3276.3 "--clones x4 1--" ActorControlExtra { category: "0197", param1: "11D2" } -3278.3 "--clones x4 2--" ActorControlExtra { category: "0197", param1: "11D2" } +3276.3 "--clones x4 1--" #ActorControlExtra { category: "0197", param1: "11D2" } +3278.3 "--clones x4 2--" #ActorControlExtra { category: "0197", param1: "11D2" } 3280.4 "--locked tethers--" Tether { id: "0175", source: "Understudy" } 3283.4 "--sync--" Ability { id: "B4E2", source: "Lindwurm" } 3290.1 "Twisted Vision 1" Ability { id: "BBE2", source: "Lindwurm" } @@ -443,12 +443,12 @@ hideall "--sync--" 3309.6 "Power Gusher x2" #Ability { id: ["B50F", "B510"], source: "Lindschrat" } 3309.6 "Snaking Kick" #Ability { id: "B511", source: "Lindschrat" } 3314.7 "Replication 4" Ability { id: "B4D8", source: "Lindwurm" } -3317.9 "--boss clones x2 1--" ActorControlExtra { category: "0197", param1: "11D5" } -3318.9 "--boss clones x2 2--" ActorControlExtra { category: "0197", param1: "11D5" } -3319.9 "--boss clones x2 3--" ActorControlExtra { category: "0197", param1: "11D5" } -3320.9 "--boss clones x2 4--" ActorControlExtra { category: "0197", param1: "11D5" } +3317.9 "--boss clones x2 1--" #ActorControlExtra { category: "0197", param1: "11D5" } +3318.9 "--boss clones x2 2--" #ActorControlExtra { category: "0197", param1: "11D5" } +3319.9 "--boss clones x2 3--" #ActorControlExtra { category: "0197", param1: "11D5" } +3320.9 "--boss clones x2 4--" #ActorControlExtra { category: "0197", param1: "11D5" } 3326.1 "--tethers--" #Tether { id: ["0170", "0171"], source: "Lindschrat" } # These could change hands causing sync issue -3334.1 "--locked tethers--" Tether { id: ["0170", "0171"], source: "Lindschrat" } +3334.1 "--locked tethers--" Tether { id: "0175", source: "Lindschrat" } # Towers Preview 3334.3 "Twisted Vision 3" Ability { id: "BBE2", source: "Lindwurm" } From 7acc873fb02674e0380d2447117ee452e83f9729 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 29 Jan 2026 21:26:42 -0500 Subject: [PATCH 106/139] remove double space --- ui/raidboss/data/07-dt/raid/r12s.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.txt b/ui/raidboss/data/07-dt/raid/r12s.txt index 5079b3eb9f8..c52d6bf5117 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.txt +++ b/ui/raidboss/data/07-dt/raid/r12s.txt @@ -483,7 +483,7 @@ hideall "--sync--" 3408.7 "Lindwurm's Glare" #Ability { id: "B4F8", source: "Lindwurm" } 3408.7 "Lindwurm's Thunder II" #Ability { id: "B4FA", source: "Lindwurm" } 3408.7 "Lindwurm's Glare" #Ability { id: "B4F8", source: "Lindwurm" } -3412.0 "Temporal Curtain" Ability { id: "B51C", source: "Lindwurm" } window 10,10 +3412.0 "Temporal Curtain" Ability { id: "B51C", source: "Lindwurm" } window 10,10 # fflogs 3416.0 "--sync--" Ability { id: "B51D", source: "Lindschrat" } From 60aa6b483adfee7f408d0e3eb43c5bfcbea33af5 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 29 Jan 2026 22:52:16 -0500 Subject: [PATCH 107/139] add trigger for remaining stacks/defamations in vision 4 Also pre-calls a tower positions output. --- ui/raidboss/data/07-dt/raid/r12s.ts | 236 ++++++++++++++++++++++++++++ 1 file changed, 236 insertions(+) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 2f2e05ed050..1b886e90991 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -73,6 +73,7 @@ export interface Data extends RaidbossData { replication4AbilityOrder: string[]; myReplication4Tether?: string; hasLightResistanceDown: boolean; + twistedVision4MechCounter: number; doomPlayers: string[]; } @@ -172,6 +173,7 @@ const triggerSet: TriggerSet = { replication4PlayerOrder: [], replication4AbilityOrder: [], hasLightResistanceDown: false, + twistedVision4MechCounter: 0, doomPlayers: [], }), triggers: [ @@ -3226,6 +3228,240 @@ const triggerSet: TriggerSet = { }; }, }, + { + id: 'R12S Twisted Vision 4 Stack/Defamation 2-4', + // Used for keeping of which Twisted Vision 4 mechanic we are on + // Note: B519 Heavy Slam and B517 Mana Burst cast regardless of players alive + // A B4F0 Unmitigated Impact will occur should the stack be missed + // Note2: B518 Mana Burst seems to not cast if the target is dead, and there doesn't seem to be repercussions + type: 'Ability', + netRegex: { id: ['B519', 'B517'], source: 'Lindschrat', capture: false }, + condition: (data) => data.twistedVisionCounter === 4 && data.twistedVision4MechCounter < 6, + suppressSeconds: 1, + response: (data, _matches, output) => { + // cactbot-builtin-response + output.responseOutputStrings = { + ...Directions.outputStrings8Dir, + stacks: Outputs.stacks, + towers: { + en: 'Tower Positions', + de: 'Turm Positionen', + fr: 'Position tour', + ja: '塔の位置へ', + cn: '八人塔站位', + ko: '기둥 자리잡기', + tc: '八人塔站位', + }, + avoidDefamation: { + en: 'Avoid Defamation', + }, + avoidStack: { + en: 'Avoid Stack', + de: 'Vermeide Sammeln', + fr: 'Évitez le package', + cn: '远离分摊', + ko: '쉐어징 피하기', + tc: '遠離分攤', + }, + defamationOnYou: Outputs.defamationOnYou, + stackOnYou: Outputs.stackOnYou, + stackOnPlayer: Outputs.stackOnPlayer, + defamations: { + en: 'Defamations', + de: 'Große AoE auf dir', + fr: 'Grosse AoE sur vous', + ja: '自分に巨大な爆発', + cn: '大圈点名', + ko: '광역 대상자', + tc: '大圈點名', + }, + oneMechThenOne: { + en: '${mech1} => ${mech2}', + }, + oneMechThenTwo: { + en: '${mech1} => ${mech2} + ${mech3}', + }, + twoMechsThenOne: { + en: '${mech1} + ${mech2} => ${mech3}', + }, + twoMechsThenTwo: { + en: '${mech1} + ${mech2} => ${mech3} + ${mech4}', + }, + oneMechThenTowers: { + en: '${mech1} => ${towers}', + }, + twoMechsThenTowers: { + en: '${mech1} + ${mech2} => ${towers}', + }, + }; + data.twistedVision4MechCounter = data.twistedVision4MechCounter + 2; // Mechanic is done in pairs + // Don't output for first one as it was called 1s prior to this trigger + if (data.twistedVision4MechCounter < 2) + return; + const count = data.twistedVision4MechCounter; + const abilityOrder = data.replication4AbilityOrder; + const playerOrder = data.replication4PlayerOrder; + if ( + abilityOrder === undefined || + playerOrder === undefined + ) + return; + + const ability1 = abilityOrder[0 + count]; + const ability2 = abilityOrder[1 + count]; + const player1 = playerOrder[0 + count]; + const player2 = playerOrder[1 + count]; + let this1; + let this2; + const defamation = headMarkerData['manaBurstTether']; + const isThisSame = ability1 === ability2; + const shortPlayer1 = data.party.member(player1); + const shortPlayer2 = data.party.member(player2); + const text = (player1 === data.me || player2 === data.me) ? 'alertText' : 'infoText'; + + // Handle This Set + if (player1 === data.me) { + this1 = ability1 === defamation ? 'defamationOnYou' : 'stackOnYou'; + if (!isThisSame) + this2 = ability2 === defamation ? 'avoidDefamation' : 'avoidStack'; + } else if (player2 === data.me) { + if (!isThisSame) { + this1 = ability1 === defamation ? 'avoidDefamation' : 'avoidStack'; + this2 = ability2 === defamation ? 'defamationOnYou' : 'stackOnYou'; + } else { + this1 = ability1 === defamation ? 'defamationOnYou' : 'stackOnYou'; + } + } else if (isThisSame) { + this1 = ability1 === defamation ? 'defamations' : 'stacks'; + } else if (!isThisSame) { + this1 = ability1 === defamation ? 'avoidDefamation' : 'stack'; + this2 = ability2 === defamation ? 'avoidDefamation' : 'stack'; + } + + if (count < 6) { + // Handle next set + // Get Stack/Defamation #2 details + const ability3 = abilityOrder[2 + count]; + const ability4 = abilityOrder[3 + count]; + const player3 = playerOrder[2 + count]; + const player4 = playerOrder[3 + count]; + + // Handle some obscure strategies or mistakes + const isNextSame = ability3 === ability4; + let next1; + let next2; + + if (player3 === data.me) { + next1 = ability3 === defamation ? 'defamationOnYou' : 'stackOnYou'; + if (!isThisSame) + next2 = ability4 === defamation ? 'avoidDefamation' : 'avoidStack'; + } else if (player4 === data.me) { + if (!isThisSame) { + next1 = ability4 === defamation ? 'avoidDefamation' : 'avoidStack'; + next2 = ability4 === defamation ? 'defamationOnYou' : 'stackOnYou'; + } else { + next1 = ability4 === defamation ? 'defamationOnYou' : 'stackOnYou'; + } + } else if (isNextSame) { + next1 = ability3 === defamation ? 'defamations' : 'stacks'; + } else if (!isNextSame) { + next1 = ability3 === defamation ? 'avoidDefamation' : 'stack'; + next2 = ability4 === defamation ? 'avoidDefamation' : 'stack'; + } + + // Build output + if (this1 === undefined || next1 === undefined) + return; + if (isThisSame && isNextSame) { + return { + [text]: output.oneMechThenOne!({ + mech1: output[this1]!(), + mech2: output[next1]!(), + }), + }; + } + + const shortPlayer3 = data.party.member(player3); + const shortPlayer4 = data.party.member(player4); + if (isThisSame && !isNextSame) { + if (next2 === undefined) + return; + return { + [text]: output.oneMechThenTwo!({ + mech1: output[this1]!(), + mech2: next1 === 'stack' + ? output.stackOnPlayer!({ player: shortPlayer3 }) + : output[next1]!(), + mech3: next2 === 'stack' + ? output.stackOnPlayer!({ player: shortPlayer4 }) + : output[next2]!(), + }), + }; + } + + if (!isThisSame && isNextSame) { + if (this2 === undefined) + return; + return { + [text]: output.twoMechsThenOne!({ + mech1: this1 === 'stack' + ? output.stackOnPlayer!({ player: shortPlayer1 }) + : output[this1]!(), + mech2: this2 === 'stack' + ? output.stackOnPlayer!({ player: shortPlayer2 }) + : output[this2]!(), + mech3: output[next1]!(), + }), + }; + } + + if (this2 === undefined || next2 === undefined) + return; + return { + [text]: output.twoMechsThenTwo!({ + mech1: this1 === 'stack' + ? output.stackOnPlayer!({ player: shortPlayer1 }) + : output[this1]!(), + mech2: this2 === 'stack' + ? output.stackOnPlayer!({ player: shortPlayer2 }) + : output[this2]!(), + mech3: next1 === 'stack' + ? output.stackOnPlayer!({ player: shortPlayer3 }) + : output[next1]!(), + mech4: next2 === 'stack' + ? output.stackOnPlayer!({ player: shortPlayer4 }) + : output[next2]!(), + }), + }; + } + // Build output for last mechanic set to warn of towers + if (this1 === undefined) + return; + if (isThisSame) { + return { + [text]: output.oneMechThenTowers!({ + mech1: output[this1]!(), + towers: output.towers!(), + }), + }; + } + if (!isThisSame) { + if (this2 === undefined) + return; + return { + [text]: output.twoMechsThenTowers!({ + mech1: this1 === 'stack' + ? output.stackOnPlayer!({ player: shortPlayer1 }) + : output[this1]!(), + mech2: this2 === 'stack' + ? output.stackOnPlayer!({ player: shortPlayer2 }) + : output[this2]!(), + towers: output.towers!(), + }), + }; + } + }, + }, { id: 'R12S Doom Collect', type: 'GainsEffect', From f4f5f0c198c3456d76c1ea46f947a4aab6940a26 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 29 Jan 2026 22:57:40 -0500 Subject: [PATCH 108/139] filter light resistance early call to just vision 3 --- ui/raidboss/data/07-dt/raid/r12s.ts | 31 ++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 1b886e90991..2ccd6deea3c 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -3011,19 +3011,15 @@ const triggerSet: TriggerSet = { netRegex: { id: 'B529', source: 'Lindwurm', capture: false }, response: Responses.spread(), }, - { - id: 'R12S Light Resistance Down II Collect', - // Players cannot soak a tower that has holy (triple element towers) - type: 'GainsEffect', - netRegex: { effectId: '1044', capture: true }, - condition: Conditions.targetIsYou(), - run: (data) => data.hasLightResistanceDown = true, - }, { id: 'R12S Light Resistance Down II', type: 'GainsEffect', netRegex: { effectId: '1044', capture: true }, - condition: Conditions.targetIsYou(), + condition: (data, matches) => { + if (data.twistedVisionCounter === 3 && data.me === matches.target) + return true; + return false; + }, infoText: (_data, _matches, output) => output.text!(), outputStrings: { text: { @@ -3031,6 +3027,23 @@ const triggerSet: TriggerSet = { }, }, }, + { + id: 'R12S No Light Resistance Down II', + type: 'GainsEffect', + netRegex: { effectId: '1044', capture: false }, + condition: (data) => data.twistedVisionCounter === 3, + delaySeconds: 0.1, + suppressSeconds: 9999, + infoText: (data, _matches, output) => { + if (!data.hasLightResistanceDown) + return output.text!(); + }, + outputStrings: { + text: { + en: 'Soak a White/Star Meteor (later)', + }, + }, + }, { id: 'R12S No Light Resistance Down II', type: 'GainsEffect', From cbaad3062e671f839b973d70f25e3f7e97c4de9f Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 29 Jan 2026 23:11:25 -0500 Subject: [PATCH 109/139] twisted vision 5 tower call Will likely want to look into which towers were where previously, record that, but I'm not sure if a play position check prior to the end of the cast is adequate since the player could not yet be on the side they need to be at that time. Could add a delayed notification at end of cast of which of the two towers to take and you'd have ~4.4s to adjust. --- ui/raidboss/data/07-dt/raid/r12s.ts | 40 +++++++++++++++++------------ 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 2ccd6deea3c..a5b851f5c8f 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -3044,25 +3044,8 @@ const triggerSet: TriggerSet = { }, }, }, - { - id: 'R12S No Light Resistance Down II', - type: 'GainsEffect', - netRegex: { effectId: '1044', capture: false }, - delaySeconds: 0.1, - suppressSeconds: 9999, - infoText: (data, _matches, output) => { - if (!data.hasLightResistanceDown) - return output.text!(); - }, - outputStrings: { - text: { - en: 'Soak a White/Star Meteor (later)', - }, - }, - }, { id: 'R12S Twisted Vision 4 Stack/Defamation 1', - // Used for keeping track of phases in idyllic type: 'StartsUsing', netRegex: { id: 'BBE2', source: 'Lindwurm', capture: false }, condition: (data) => data.twistedVisionCounter === 4, @@ -3475,6 +3458,7 @@ const triggerSet: TriggerSet = { } }, }, + { id: 'R12S Doom Collect', type: 'GainsEffect', @@ -3514,6 +3498,28 @@ const triggerSet: TriggerSet = { }, }, }, + { + id: 'R12S Twisted Vision 5', + // TODO: Get Position of the towers and player side and state the front/left back/right + // Towers aren't visible until after cast, but you would have 4.4s to adjust if the trigger was delayed + type: 'StartsUsing', + netRegex: { id: 'BBE2', source: 'Lindwurm', capture: true }, + condition: (data) => data.twistedVisionCounter === 5, + durationSeconds: (_data, matches) => parseFloat(matches.castTime) + 4.1, + alertText: (data, _matches, output) => { + if (data.hasLightResistanceDown) + return output.fireEarthTower!(); + return output.holyTower!(); + }, + outputStrings: { + fireEarthTower: { + en: 'Soak Fire/Earth Meteor', + }, + holyTower: { + en: 'Soak a White/Star Meteor', + }, + }, + }, { id: 'R12S Doom Cleanup', type: 'LosesEffect', From 5b42c756068c47e1a676a9bdfea69ab071e85b39 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 29 Jan 2026 23:13:45 -0500 Subject: [PATCH 110/139] remove extra line --- ui/raidboss/data/07-dt/raid/r12s.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index a5b851f5c8f..0bd98886398 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -3458,7 +3458,6 @@ const triggerSet: TriggerSet = { } }, }, - { id: 'R12S Doom Collect', type: 'GainsEffect', From 107b1a50bf4017f722bf0815a9ba016ae5389e08 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Thu, 29 Jan 2026 23:21:55 -0500 Subject: [PATCH 111/139] add Lindwurm's Stone III trigger --- ui/raidboss/data/07-dt/raid/r12s.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 0bd98886398..79c71a512bd 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -3498,9 +3498,10 @@ const triggerSet: TriggerSet = { }, }, { - id: 'R12S Twisted Vision 5', + id: 'R12S Twisted Vision 5 Towers', // TODO: Get Position of the towers and player side and state the front/left back/right // Towers aren't visible until after cast, but you would have 4.4s to adjust if the trigger was delayed + // 4s castTime type: 'StartsUsing', netRegex: { id: 'BBE2', source: 'Lindwurm', capture: true }, condition: (data) => data.twistedVisionCounter === 5, @@ -3538,6 +3539,21 @@ const triggerSet: TriggerSet = { durationSeconds: (_data, matches) => parseFloat(matches.duration), response: Responses.stopMoving(), }, + { + id: 'R12S Idyllic Dream Lindwurm\'s Stone III', + // TODO: Get their target locations and output avoid + // 5s castTime + type: 'StartsUsing', + netRegex: { id: 'B4F7', source: 'Lindwurm', capture: true }, + durationSeconds: (_data, matches) => parseFloat(matches.castTime), + suppressSeconds: 1, + infoText: (_data, _matches, output) => output.avoidEarthTower!(), + outputStrings: { + avoidEarthTower: { + en: 'Avoid Earth Tower', + }, + }, + }, { id: 'R12S Idyllic Dream Replication Clone Cardinal/Intercardinal Reminder', // Using Temporal Curtain From f6bc69509d52dacb00ce3f3b352660480de5ac75 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Fri, 30 Jan 2026 00:09:09 -0500 Subject: [PATCH 112/139] add nearby and faraway portent + baits Delayed until 4.7s as that is about when the Earth will have gone off and it will be safe to move. --- ui/raidboss/data/07-dt/raid/r12s.ts | 121 ++++++++++++++++++---------- 1 file changed, 78 insertions(+), 43 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 79c71a512bd..7986606d3fb 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -3458,8 +3458,56 @@ const triggerSet: TriggerSet = { } }, }, + { + id: 'R12S Twisted Vision 5 Towers', + // TODO: Get Position of the towers and player side and state the front/left back/right + // Towers aren't visible until after cast, but you would have 4.4s to adjust if the trigger was delayed + // 4s castTime + type: 'StartsUsing', + netRegex: { id: 'BBE2', source: 'Lindwurm', capture: true }, + condition: (data) => data.twistedVisionCounter === 5, + durationSeconds: (_data, matches) => parseFloat(matches.castTime) + 4.1, + alertText: (data, _matches, output) => { + if (data.hasLightResistanceDown) + return output.fireEarthTower!(); + return output.holyTower!(); + }, + outputStrings: { + fireEarthTower: { + en: 'Soak Fire/Earth Meteor', + }, + holyTower: { + en: 'Soak a White/Star Meteor', + }, + }, + }, + { + id: 'R12S Hot-blooded', + // Player can still cast, but shouldn't move for 5s duration + type: 'GainsEffect', + netRegex: { effectId: '12A0', capture: true }, + condition: Conditions.targetIsYou(), + durationSeconds: (_data, matches) => parseFloat(matches.duration), + response: Responses.stopMoving(), + }, + { + id: 'R12S Idyllic Dream Lindwurm\'s Stone III', + // TODO: Get their target locations and output avoid + // 5s castTime + type: 'StartsUsing', + netRegex: { id: 'B4F7', source: 'Lindwurm', capture: true }, + durationSeconds: (_data, matches) => parseFloat(matches.castTime), + suppressSeconds: 1, + infoText: (_data, _matches, output) => output.avoidEarthTower!(), + outputStrings: { + avoidEarthTower: { + en: 'Avoid Earth Tower', + }, + }, + }, { id: 'R12S Doom Collect', + // Happens about 1.3s after Dark Tower when it casts B4F6 Lindwurm's Dark II type: 'GainsEffect', netRegex: { effectId: 'D24', capture: true }, run: (data, matches) => data.doomPlayers.push(matches.target), @@ -3498,59 +3546,46 @@ const triggerSet: TriggerSet = { }, }, { - id: 'R12S Twisted Vision 5 Towers', - // TODO: Get Position of the towers and player side and state the front/left back/right - // Towers aren't visible until after cast, but you would have 4.4s to adjust if the trigger was delayed - // 4s castTime - type: 'StartsUsing', - netRegex: { id: 'BBE2', source: 'Lindwurm', capture: true }, - condition: (data) => data.twistedVisionCounter === 5, - durationSeconds: (_data, matches) => parseFloat(matches.castTime) + 4.1, - alertText: (data, _matches, output) => { - if (data.hasLightResistanceDown) - return output.fireEarthTower!(); - return output.holyTower!(); + id: 'R12S Nearby and Faraway Portent', + // 129D Lindwurm's Portent prevents stacking the portents + // 129E Farwaway Portent + // 129F Nearby Portent + // 10s duration, need to delay to avoid earth + doom trigger overlap + // TODO: Configure for element tower they soaked + type: 'GainsEffect', + netRegex: { effectId: ['129E', '129F'], capture: true }, + condition: Conditions.targetIsYou(), + delaySeconds: (_data, matches) => parseFloat(matches.duration) - 5.3, + infoText: (_data, matches, output) => { + if (matches.id === '129E') + return output.farOnYou!(); + return output.nearOnYou!(); }, outputStrings: { - fireEarthTower: { - en: 'Soak Fire/Earth Meteor', + nearOnYou: { + en: 'Near on YOU: Be on Middle Hitbox', }, - holyTower: { - en: 'Soak a White/Star Meteor', + farOnYou: { + en: 'Far on YOU: Be on N/S Hitbox', // Most parties probably put this North? }, }, }, { - id: 'R12S Doom Cleanup', - type: 'LosesEffect', - netRegex: { effectId: 'D24', capture: true }, - run: (data, matches) => { - data.doomPlayers = data.doomPlayers.filter( - (player) => player === matches.target, - ); - }, - }, - { - id: 'R12S Hot-blooded', - // Player can still cast, but shouldn't move for 5s duration + id: 'R12S Nearby and Faraway Portent Baits', + // TODO: Configure for element tower they soaked type: 'GainsEffect', - netRegex: { effectId: '12A0', capture: true }, - condition: Conditions.targetIsYou(), - durationSeconds: (_data, matches) => parseFloat(matches.duration), - response: Responses.stopMoving(), - }, - { - id: 'R12S Idyllic Dream Lindwurm\'s Stone III', - // TODO: Get their target locations and output avoid - // 5s castTime - type: 'StartsUsing', - netRegex: { id: 'B4F7', source: 'Lindwurm', capture: true }, - durationSeconds: (_data, matches) => parseFloat(matches.castTime), + netRegex: { effectId: ['129E', '129F'], capture: true }, + condition: (data) => data.hasLightResistanceDown, + delaySeconds: (_data, matches) => parseFloat(matches.duration) - 5.3, suppressSeconds: 1, - infoText: (_data, _matches, output) => output.avoidEarthTower!(), + infoText: (_data, _matches, output) => output.bait!(), outputStrings: { - avoidEarthTower: { - en: 'Avoid Earth Tower', + bait: { + en: 'Bait Cone', + de: 'Köder Kegel-AoE', + cn: '诱导扇形', + ko: '부채꼴 유도', + tc: '誘導扇形', }, }, }, From 52a35e8954bb48c3c0631c93b2f6ec7b28e0fca1 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Fri, 30 Jan 2026 01:05:29 -0500 Subject: [PATCH 113/139] fix twisted vision 6 early call, add twisted vision 8 I have yet to test these. --- ui/raidboss/data/07-dt/raid/r12s.ts | 92 ++++++++++++++++++++++++----- 1 file changed, 77 insertions(+), 15 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 7986606d3fb..35e6b8f004c 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -3590,29 +3590,91 @@ const triggerSet: TriggerSet = { }, }, { - id: 'R12S Idyllic Dream Replication Clone Cardinal/Intercardinal Reminder', - // Using Temporal Curtain - type: 'StartsUsing', - netRegex: { id: 'B51C', source: 'Lindwurm', capture: false }, + id: 'R12S Twisted Vision 6 Light Party Stacks', + // At end of cast it's cardinal or intercard + type: 'Ability', + netRegex: { id: 'BBE2', source: 'Lindwurm', capture: false }, + condition: (data) => data.twistedVisionCounter === 6, infoText: (data, _matches, output) => { - const firstClone = data.replication3CloneOrder[0]; - if (firstClone === undefined) - return; - const actor = data.actorPositions[firstClone]; - if (actor === undefined) + const first = data.replication3CloneOrder[0]; + if (first === undefined) return; + const dirNumOrder = first % 2 === 0 ? [0, 2, 4, 6] : [1, 3, 5, 7]; + + // Need to lookup what ability is at each dir, only need cards or intercard dirs + const abilities = data.replication4AbilityOrder.splice(0,4); + const stackDirs = []; + let i = 0; + + // Find first all stacks in cards or intercards + // Incorrect amount means players made an unsolvable? run + for (const dirNum of dirNumOrder) { + if (abilities[i++] === headMarkerData['heavySlamTether']) + stackDirs.push(dirNum); + } + // Only grabbing first two + const dirNum1 = stackDirs[0]; + const dirNum2 = stackDirs[1]; - const dirNum = Directions.xyTo8DirNum(actor.x, actor.y, center.x, center.y); - const dir = Directions.output8Dir[dirNum] ?? 'unknown'; + // If we failed to get two stacks, just output generic cards/intercards reminder + if (dirNum1 === undefined || dirNum2 === undefined) { + return first % 2 === 0 ? output.cardinals!() : output.intercards!(); + } + const dir1 = Directions.output8Dir[dirNum1]; + const dir2 = Directions.output8Dir[dirNum2]; + return output.stack!({ dir1: dir1, dir2: dir2 }); + }, + outputStrings: { + ...Directions.outputStrings8Dir, + cardinals: Outputs.cardinals, + intercards: Outputs.intercards, + stack: { + en: 'Stack ${dir1}/${dir2} + Lean Middle Out', + }, + }, + }, + { + id: 'R12S Twisted Vision 8 Light Party Stacks', + // At end of cast it's cardinal or intercard + type: 'Ability', + netRegex: { id: 'BBE2', source: 'Lindwurm', capture: false }, + condition: (data) => data.twistedVisionCounter === 8, + infoText: (data, _matches, output) => { + const first = data.replication3CloneOrder[0]; + if (first === undefined) + return; + const dirNumOrder = first % 2 !== 0 ? [0, 2, 4, 6] : [1, 3, 5, 7]; + + // Need to lookup what ability is at each dir, only need cards or intercard dirs + const abilities = data.replication4AbilityOrder.slice(4,8); + const stackDirs = []; + let i = 0; + + // Find first all stacks in cards or intercards + // Incorrect amount means players made an unsolvable? run + for (const dirNum of dirNumOrder) { + if (abilities[i++] === headMarkerData['heavySlamTether']) + stackDirs.push(dirNum); + } + // Only grabbing first two + const dirNum1 = stackDirs[0]; + const dirNum2 = stackDirs[1]; - if (isCardinalDir(dir)) - return output.cardinals!(); - if (isIntercardDir(dir)) - return output.intercards!(); + // If we failed to get two stacks, just output generic cards/intercards reminder + if (dirNum1 === undefined || dirNum2 === undefined) { + return first % 2 !== 0 ? output.cardinals!() : output.intercards!(); + } + const dir1 = Directions.output8Dir[dirNum1]; + const dir2 = Directions.output8Dir[dirNum2]; + return output.stack!({ dir1: dir1, dir2: dir2 }); }, outputStrings: { + ...Directions.outputStrings8Dir, cardinals: Outputs.cardinals, intercards: Outputs.intercards, + stack: { + en: 'Stack ${dir1}/${dir2} + Lean Middle Out', + }, }, }, ], From de1643c23f7a277e77e3e2eb404fe5396cc28cd7 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Fri, 30 Jan 2026 01:19:40 -0500 Subject: [PATCH 114/139] add twisted vision 7 template Will be able to update it with direction later, needed to be able to call the vision 8 stuff earlier because of quickness of mechanics. --- ui/raidboss/data/07-dt/raid/r12s.ts | 62 ++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 35e6b8f004c..7d8b39bff7b 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -3633,13 +3633,71 @@ const triggerSet: TriggerSet = { }, }, }, + { + id: 'R12S Twisted Vision 7 Safe Platform', + // TODO: Get direction of the safe platform + N/S or E/W? + type: 'StartsUsing', + netRegex: { id: 'BBE2', source: 'Lindwurm', capture: true }, + condition: (data) => data.twistedVisionCounter === 7, + durationSeconds: (_data, matches) => parseFloat(matches.castTime) + 3, + infoText: (data, _matches, output) => { + const first = data.replication3CloneOrder[0]; + if (first === undefined) + return; + const dirNumOrder = first % 2 !== 0 ? [0, 2, 4, 6] : [1, 3, 5, 7]; + + // Need to lookup what ability is at each dir, only need cards or intercard dirs + const abilities = data.replication4AbilityOrder.slice(4,8); + const stackDirs = []; + let i = 0; + + // Find first all stacks in cards or intercards + // Incorrect amount means players made an unsolvable? run + for (const dirNum of dirNumOrder) { + if (abilities[i++] === headMarkerData['heavySlamTether']) + stackDirs.push(dirNum); + } + // Only grabbing first two + const dirNum1 = stackDirs[0]; + const dirNum2 = stackDirs[1]; + + // If we failed to get two stacks, just output generic cards/intercards reminder + if (dirNum1 === undefined || dirNum2 === undefined) { + const card = first % 2 !== 0 ? 'cardinals' : 'intercards'; + return output.platformThenStack!({ + platform: output.safePlatform!(), + stack: output[card]!(), + }); + } + const dir1 = Directions.output8Dir[dirNum1]; + const dir2 = Directions.output8Dir[dirNum2]; + return output.platformThenStack!({ + platform: output.safePlatform!(), + stack: output.stack!({ dir1: dir1, dir2: dir2 }), + }); + }, + outputStrings: { + ...Directions.outputStrings8Dir, + cardinals: Outputs.cardinals, + intercards: Outputs.intercards, + safePlatform: { + en: 'Safe Platform', + }, + stack: { + en: 'Stack ${dir1}/${dir2}', + }, + platformThenStack: { + en: '${platform} => ${stack}', + }, + }, + }, { id: 'R12S Twisted Vision 8 Light Party Stacks', // At end of cast it's cardinal or intercard - type: 'Ability', + type: 'StartsUsing', netRegex: { id: 'BBE2', source: 'Lindwurm', capture: false }, condition: (data) => data.twistedVisionCounter === 8, - infoText: (data, _matches, output) => { + alertText: (data, _matches, output) => { const first = data.replication3CloneOrder[0]; if (first === undefined) return; From 5cbdc6654fe26e5a71a07368d3abe9f6c551d3df Mon Sep 17 00:00:00 2001 From: Legends0 Date: Fri, 30 Jan 2026 01:21:37 -0500 Subject: [PATCH 115/139] lint --- ui/raidboss/data/07-dt/raid/r12s.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 7d8b39bff7b..87d5fb9d66f 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -3602,7 +3602,7 @@ const triggerSet: TriggerSet = { const dirNumOrder = first % 2 === 0 ? [0, 2, 4, 6] : [1, 3, 5, 7]; // Need to lookup what ability is at each dir, only need cards or intercard dirs - const abilities = data.replication4AbilityOrder.splice(0,4); + const abilities = data.replication4AbilityOrder.splice(0, 4); const stackDirs = []; let i = 0; @@ -3620,8 +3620,8 @@ const triggerSet: TriggerSet = { if (dirNum1 === undefined || dirNum2 === undefined) { return first % 2 === 0 ? output.cardinals!() : output.intercards!(); } - const dir1 = Directions.output8Dir[dirNum1]; - const dir2 = Directions.output8Dir[dirNum2]; + const dir1 = Directions.output8Dir[dirNum1]; + const dir2 = Directions.output8Dir[dirNum2]; return output.stack!({ dir1: dir1, dir2: dir2 }); }, outputStrings: { @@ -3647,7 +3647,7 @@ const triggerSet: TriggerSet = { const dirNumOrder = first % 2 !== 0 ? [0, 2, 4, 6] : [1, 3, 5, 7]; // Need to lookup what ability is at each dir, only need cards or intercard dirs - const abilities = data.replication4AbilityOrder.slice(4,8); + const abilities = data.replication4AbilityOrder.slice(4, 8); const stackDirs = []; let i = 0; @@ -3669,8 +3669,8 @@ const triggerSet: TriggerSet = { stack: output[card]!(), }); } - const dir1 = Directions.output8Dir[dirNum1]; - const dir2 = Directions.output8Dir[dirNum2]; + const dir1 = Directions.output8Dir[dirNum1]; + const dir2 = Directions.output8Dir[dirNum2]; return output.platformThenStack!({ platform: output.safePlatform!(), stack: output.stack!({ dir1: dir1, dir2: dir2 }), @@ -3704,7 +3704,7 @@ const triggerSet: TriggerSet = { const dirNumOrder = first % 2 !== 0 ? [0, 2, 4, 6] : [1, 3, 5, 7]; // Need to lookup what ability is at each dir, only need cards or intercard dirs - const abilities = data.replication4AbilityOrder.slice(4,8); + const abilities = data.replication4AbilityOrder.slice(4, 8); const stackDirs = []; let i = 0; @@ -3722,8 +3722,8 @@ const triggerSet: TriggerSet = { if (dirNum1 === undefined || dirNum2 === undefined) { return first % 2 !== 0 ? output.cardinals!() : output.intercards!(); } - const dir1 = Directions.output8Dir[dirNum1]; - const dir2 = Directions.output8Dir[dirNum2]; + const dir1 = Directions.output8Dir[dirNum1]; + const dir2 = Directions.output8Dir[dirNum2]; return output.stack!({ dir1: dir1, dir2: dir2 }); }, outputStrings: { From 5cd2fe2181e5cc4d045a4f9352e5d16b443409f4 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sat, 31 Jan 2026 14:32:19 -0500 Subject: [PATCH 116/139] fix for dn Clone Stacks + final defamation These can likely be cleared up later when replication 2 is refactored to be like replication 3 and we can determine the ability/location order... --- ui/raidboss/data/07-dt/raid/r12s.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 87d5fb9d66f..afc84d38d3b 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -223,7 +223,7 @@ const triggerSet: TriggerSet = { type: 'StartsUsing', netRegex: { id: 'B4EC', source: 'Lindwurm', capture: false }, run: (data) => { - if (data.phase === 'replication1') { + if (data.phase === 'replication2') { data.phase = 'reenactment1'; return; } From 9db5136fe9ccde50ff4731f962e279724d5aca3b Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 1 Feb 2026 04:18:04 -0500 Subject: [PATCH 117/139] fix twisted vision 4 calls --- ui/raidboss/data/07-dt/raid/r12s.ts | 514 ++++++++++------------------ 1 file changed, 188 insertions(+), 326 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index afc84d38d3b..5c08e8f4e9c 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -69,9 +69,9 @@ export interface Data extends RaidbossData { idyllicVision2NorthSouthCleaveSpot?: 'north' | 'south'; replication4DirNumAbility: { [dirNum: number]: string }; replication4PlayerAbilities: { [player: string]: string }; + replication4BossCloneDirNumPlayers: { [dirNum: number]: string }; replication4PlayerOrder: string[]; replication4AbilityOrder: string[]; - myReplication4Tether?: string; hasLightResistanceDown: boolean; twistedVision4MechCounter: number; doomPlayers: string[]; @@ -170,6 +170,7 @@ const triggerSet: TriggerSet = { replication3CloneDirNumPlayers: {}, replication4DirNumAbility: {}, replication4PlayerAbilities: {}, + replication4BossCloneDirNumPlayers: {}, replication4PlayerOrder: [], replication4AbilityOrder: [], hasLightResistanceDown: false, @@ -2888,6 +2889,9 @@ const triggerSet: TriggerSet = { center.y, ); + // Store the player at each dirNum + data.replication4BossCloneDirNumPlayers[dirNum] = target; + // Lookup what the tether was at the same location const ability = data.replication4DirNumAbility[dirNum]; if (ability === undefined) { @@ -2900,11 +2904,12 @@ const triggerSet: TriggerSet = { // Create ability order once we have all 8 players // If players had more than one tether previously, the extra tethers are randomly assigned if (Object.keys(data.replication4PlayerAbilities).length === 8) { + // Used for Twisted Vision 7 and 8 mechanics const abilities = data.replication4PlayerAbilities; const order = data.replication3CloneOrder; // Order in which clones spawned const players = data.replication3CloneDirNumPlayers; // Direction of player's clone - // Mechanics are resolved clockwise, get create order based on cards/inters + // Mechanics are resolved clockwise, create order based on cards/inters const first = order[0]; if (first === undefined) return; @@ -3052,176 +3057,99 @@ const triggerSet: TriggerSet = { response: (data, _matches, output) => { // cactbot-builtin-response output.responseOutputStrings = { - ...Directions.outputStrings8Dir, stacks: Outputs.stacks, - avoidDefamation: { - en: 'Avoid Defamation', - }, - avoidStack: { - en: 'Avoid Stack', - de: 'Vermeide Sammeln', - fr: 'Évitez le package', - cn: '远离分摊', - ko: '쉐어징 피하기', - tc: '遠離分攤', - }, - defamationOnYou: Outputs.defamationOnYou, stackOnYou: Outputs.stackOnYou, - stackOnPlayer: Outputs.stackOnPlayer, defamations: { - en: 'Defamations', - de: 'Große AoE auf dir', - fr: 'Grosse AoE sur vous', - ja: '自分に巨大な爆発', - cn: '大圈点名', - ko: '광역 대상자', - tc: '大圈點名', + en: 'Avoid Defamations', + }, + defamationOnYou: Outputs.defamationOnYou, + stacksThenDefamations: { + en: '${mech1} => ${mech2}', + }, + defamationsThenStacks: { + en: '${mech1} => ${mech2}', }, - oneMechThenOne: { + stacksThenDefamationOnYou: { en: '${mech1} => ${mech2}', }, - oneMechThenTwo: { - en: '${mech1} => ${mech2} + ${mech3}', + defamationsThenStackOnYou: { + en: '${mech1} => ${mech2}', }, - twoMechsThenOne: { - en: '${mech1} + ${mech2} => ${mech3}', + stackOnYouThenDefamations: { + en: '${mech1} => ${mech2}', }, - twoMechsThenTwo: { - en: '${mech1} + ${mech2} => ${mech3} + ${mech4}', + defamationOnYouThenStack: { + en: '${mech1} => ${mech2}', }, }; - const abilityOrder = data.replication4AbilityOrder; - const playerOrder = data.replication4PlayerOrder; + const player1 = data.replication4BossCloneDirNumPlayers[0]; + const player2 = data.replication4BossCloneDirNumPlayers[4]; + const player3 = data.replication4BossCloneDirNumPlayers[1]; + const player4 = data.replication4BossCloneDirNumPlayers[5]; + const abilityId = data.replication4DirNumAbility[0]; // Only need to know one + if ( - abilityOrder === undefined || - playerOrder === undefined + abilityId === undefined || player1 === undefined || + player2 === undefined || player3 === undefined || + player4 === undefined ) return; - const ability1 = abilityOrder[0]; - const ability2 = abilityOrder[1]; - const player1 = playerOrder[0]; - const player2 = playerOrder[1]; - - // Get Stack/Defamation #2 details - const ability3 = abilityOrder[2]; - const ability4 = abilityOrder[3]; - const player3 = playerOrder[2]; - const player4 = playerOrder[3]; - - // Handle some obscure strategies or mistakes - const isThisSame = ability1 === ability2; - const isNextSame = ability3 === ability4; - const defamation = headMarkerData['manaBurstTether']; - let this1; - let this2; - let next1; - let next2; - // Handle This Set - if (player1 === data.me) { - this1 = ability1 === defamation ? 'defamationOnYou' : 'stackOnYou'; - if (!isThisSame) - this2 = ability2 === defamation ? 'avoidDefamation' : 'avoidStack'; - } else if (player2 === data.me) { - if (!isThisSame) { - this1 = ability1 === defamation ? 'avoidDefamation' : 'avoidStack'; - this2 = ability2 === defamation ? 'defamationOnYou' : 'stackOnYou'; - } else { - this1 = ability1 === defamation ? 'defamationOnYou' : 'stackOnYou'; - } - } else if (isThisSame) { - this1 = ability1 === defamation ? 'defamations' : 'stacks'; - } else if (!isThisSame) { - this1 = ability1 === defamation ? 'avoidDefamation' : 'stack'; - this2 = ability2 === defamation ? 'avoidDefamation' : 'stack'; - } + const ability1 = abilityId === headMarkerData['manaBurstTether'] + ? 'defamations' + : abilityId === headMarkerData['heavySlamTether'] + ? 'stacks' + : 'unknown'; - // Handle Next Set - if (player3 === data.me) { - next1 = ability3 === defamation ? 'defamationOnYou' : 'stackOnYou'; - if (!isThisSame) - next2 = ability4 === defamation ? 'avoidDefamation' : 'avoidStack'; - } else if (player4 === data.me) { - if (!isThisSame) { - next1 = ability4 === defamation ? 'avoidDefamation' : 'avoidStack'; - next2 = ability4 === defamation ? 'defamationOnYou' : 'stackOnYou'; - } else { - next1 = ability4 === defamation ? 'defamationOnYou' : 'stackOnYou'; - } - } else if (isNextSame) { - next1 = ability3 === defamation ? 'defamations' : 'stacks'; - } else if (!isNextSame) { - next1 = ability3 === defamation ? 'avoidDefamation' : 'stack'; - next2 = ability4 === defamation ? 'avoidDefamation' : 'stack'; - } + if (ability1 === 'stacks') { + if (data.me === player1 || data.me === player2) + return { + alertText: output.stackOnYouThenDefamations!({ + mech1: output.stackOnYou!(), + mech2: output.defamations!(), + }) + }; - // Build output - if (this1 === undefined || next1 === undefined) - return; - const text = (player1 === data.me || player2 === data.me) ? 'alertText' : 'infoText'; - if (isThisSame && isNextSame) { - return { - [text]: output.oneMechThenOne!({ - mech1: output[this1]!(), - mech2: output[next1]!(), - }), - }; - } + if (data.me === player3 || data.me === player4) + return { + infoText: output.stacksThenDefamationOnYou!({ + mech1: output.stacks!(), + mech2: output.defamationOnYou!(), + }) + }; - const shortPlayer3 = data.party.member(player3); - const shortPlayer4 = data.party.member(player4); - if (isThisSame && !isNextSame) { - if (next2 === undefined) - return; return { - [text]: output.oneMechThenTwo!({ - mech1: output[this1]!(), - mech2: next1 === 'stack' - ? output.stackOnPlayer!({ player: shortPlayer3 }) - : output[next1]!(), - mech3: next2 === 'stack' - ? output.stackOnPlayer!({ player: shortPlayer4 }) - : output[next2]!(), - }), + infoText: output.stacksThenDefamations!({ + mech1: output.stacks!(), + mech2: output.defamations!(), + }) }; } - const shortPlayer1 = data.party.member(player1); - const shortPlayer2 = data.party.member(player2); - if (!isThisSame && isNextSame) { - if (this2 === undefined) - return; + if (ability1 === 'defamations') { + if (data.me === player1 || data.me === player2) + return { + alertText: output.defamationOnYouThenStack!({ + mech1: output.defamationOnYou!(), + mech2: output.stacks!(), + }) + }; + + if (data.me === player3 || data.me === player4) + return { + infoText: output.defamationsThenStackOnYou!({ + mech1: output.defamations!(), + mech2: output.stackOnYou!(), + }) + }; + return { - [text]: output.twoMechsThenOne!({ - mech1: this1 === 'stack' - ? output.stackOnPlayer!({ player: shortPlayer1 }) - : output[this1]!(), - mech2: this2 === 'stack' - ? output.stackOnPlayer!({ player: shortPlayer2 }) - : output[this2]!(), - mech3: output[next1]!(), - }), + infoText: output.defamationsThenStacks!({ + mech1: output.defamations!(), + mech2: output.stacks!(), + }) }; } - - if (this2 === undefined || next2 === undefined) - return; - return { - [text]: output.twoMechsThenTwo!({ - mech1: this1 === 'stack' - ? output.stackOnPlayer!({ player: shortPlayer1 }) - : output[this1]!(), - mech2: this2 === 'stack' - ? output.stackOnPlayer!({ player: shortPlayer2 }) - : output[this2]!(), - mech3: next1 === 'stack' - ? output.stackOnPlayer!({ player: shortPlayer3 }) - : output[next1]!(), - mech4: next2 === 'stack' - ? output.stackOnPlayer!({ player: shortPlayer4 }) - : output[next2]!(), - }), - }; }, }, { @@ -3237,57 +3165,38 @@ const triggerSet: TriggerSet = { response: (data, _matches, output) => { // cactbot-builtin-response output.responseOutputStrings = { - ...Directions.outputStrings8Dir, stacks: Outputs.stacks, - towers: { - en: 'Tower Positions', - de: 'Turm Positionen', - fr: 'Position tour', - ja: '塔の位置へ', - cn: '八人塔站位', - ko: '기둥 자리잡기', - tc: '八人塔站位', - }, - avoidDefamation: { - en: 'Avoid Defamation', - }, - avoidStack: { - en: 'Avoid Stack', - de: 'Vermeide Sammeln', - fr: 'Évitez le package', - cn: '远离分摊', - ko: '쉐어징 피하기', - tc: '遠離分攤', - }, - defamationOnYou: Outputs.defamationOnYou, stackOnYou: Outputs.stackOnYou, - stackOnPlayer: Outputs.stackOnPlayer, defamations: { - en: 'Defamations', - de: 'Große AoE auf dir', - fr: 'Grosse AoE sur vous', - ja: '自分に巨大な爆発', - cn: '大圈点名', - ko: '광역 대상자', - tc: '大圈點名', + en: 'Avoid Defamations', }, - oneMechThenOne: { + defamationOnYou: Outputs.defamationOnYou, + stacksThenDefamations: { en: '${mech1} => ${mech2}', }, - oneMechThenTwo: { - en: '${mech1} => ${mech2} + ${mech3}', + defamationsThenStacks: { + en: '${mech1} => ${mech2}', }, - twoMechsThenOne: { - en: '${mech1} + ${mech2} => ${mech3}', + stacksThenDefamationOnYou: { + en: '${mech1} => ${mech2}', }, - twoMechsThenTwo: { - en: '${mech1} + ${mech2} => ${mech3} + ${mech4}', + defamationsThenStackOnYou: { + en: '${mech1} => ${mech2}', }, - oneMechThenTowers: { - en: '${mech1} => ${towers}', + stackOnYouThenDefamations: { + en: '${mech1} => ${mech2}', }, - twoMechsThenTowers: { - en: '${mech1} + ${mech2} => ${towers}', + defamationOnYouThenStack: { + en: '${mech1} => ${mech2}', + }, + towers: { + en: 'Tower Positions', + de: 'Turm Positionen', + fr: 'Position tour', + ja: '塔の位置へ', + cn: '八人塔站位', + ko: '기둥 자리잡기', + tc: '八人塔站位', }, }; data.twistedVision4MechCounter = data.twistedVision4MechCounter + 2; // Mechanic is done in pairs @@ -3295,165 +3204,118 @@ const triggerSet: TriggerSet = { if (data.twistedVision4MechCounter < 2) return; const count = data.twistedVision4MechCounter; - const abilityOrder = data.replication4AbilityOrder; - const playerOrder = data.replication4PlayerOrder; + const players = data.replication4BossCloneDirNumPlayers; + const abilityIds = data.replication4DirNumAbility; + const player1 = count === 2 + ? players[1] + : count === 4 ? players[2] : players[3]; + const player2 = count === 2 + ? players[5] + : count === 4 ? players[6] : players[7]; + const abilityId = count === 2 + ? abilityIds[1] + : count === 4 ? abilityIds[2] : abilityIds[3]; + if ( - abilityOrder === undefined || - playerOrder === undefined + abilityId === undefined || player1 === undefined || + player2 === undefined ) return; - const ability1 = abilityOrder[0 + count]; - const ability2 = abilityOrder[1 + count]; - const player1 = playerOrder[0 + count]; - const player2 = playerOrder[1 + count]; - let this1; - let this2; - const defamation = headMarkerData['manaBurstTether']; - const isThisSame = ability1 === ability2; - const shortPlayer1 = data.party.member(player1); - const shortPlayer2 = data.party.member(player2); - const text = (player1 === data.me || player2 === data.me) ? 'alertText' : 'infoText'; - - // Handle This Set - if (player1 === data.me) { - this1 = ability1 === defamation ? 'defamationOnYou' : 'stackOnYou'; - if (!isThisSame) - this2 = ability2 === defamation ? 'avoidDefamation' : 'avoidStack'; - } else if (player2 === data.me) { - if (!isThisSame) { - this1 = ability1 === defamation ? 'avoidDefamation' : 'avoidStack'; - this2 = ability2 === defamation ? 'defamationOnYou' : 'stackOnYou'; - } else { - this1 = ability1 === defamation ? 'defamationOnYou' : 'stackOnYou'; - } - } else if (isThisSame) { - this1 = ability1 === defamation ? 'defamations' : 'stacks'; - } else if (!isThisSame) { - this1 = ability1 === defamation ? 'avoidDefamation' : 'stack'; - this2 = ability2 === defamation ? 'avoidDefamation' : 'stack'; - } + const ability1 = abilityId === headMarkerData['manaBurstTether'] + ? 'defamations' + : abilityId === headMarkerData['heavySlamTether'] + ? 'stacks' + : 'unknown'; if (count < 6) { - // Handle next set - // Get Stack/Defamation #2 details - const ability3 = abilityOrder[2 + count]; - const ability4 = abilityOrder[3 + count]; - const player3 = playerOrder[2 + count]; - const player4 = playerOrder[3 + count]; - - // Handle some obscure strategies or mistakes - const isNextSame = ability3 === ability4; - let next1; - let next2; - - if (player3 === data.me) { - next1 = ability3 === defamation ? 'defamationOnYou' : 'stackOnYou'; - if (!isThisSame) - next2 = ability4 === defamation ? 'avoidDefamation' : 'avoidStack'; - } else if (player4 === data.me) { - if (!isThisSame) { - next1 = ability4 === defamation ? 'avoidDefamation' : 'avoidStack'; - next2 = ability4 === defamation ? 'defamationOnYou' : 'stackOnYou'; - } else { - next1 = ability4 === defamation ? 'defamationOnYou' : 'stackOnYou'; - } - } else if (isNextSame) { - next1 = ability3 === defamation ? 'defamations' : 'stacks'; - } else if (!isNextSame) { - next1 = ability3 === defamation ? 'avoidDefamation' : 'stack'; - next2 = ability4 === defamation ? 'avoidDefamation' : 'stack'; - } - - // Build output - if (this1 === undefined || next1 === undefined) + const player3 = count === 2 ? players[2] : players[3]; + const player4 = count === 2 ? players[6] : players[7]; + if (player3 === undefined || player4 === undefined) return; - if (isThisSame && isNextSame) { + + if (ability1 === 'stacks') { + if (data.me === player1 || data.me === player2) + return { + alertText: output.stackOnYouThenDefamations!({ + mech1: output.stackOnYou!(), + mech2: output.defamations!(), + }) + }; + + if (data.me === player3 || data.me === player4) + return { + infoText: output.stacksThenDefamationOnYou!({ + mech1: output.stacks!(), + mech2: output.defamationOnYou!(), + }) + }; + return { - [text]: output.oneMechThenOne!({ - mech1: output[this1]!(), - mech2: output[next1]!(), - }), + infoText: output.stacksThenDefamations!({ + mech1: output.stacks!(), + mech2: output.defamations!(), + }) }; } - const shortPlayer3 = data.party.member(player3); - const shortPlayer4 = data.party.member(player4); - if (isThisSame && !isNextSame) { - if (next2 === undefined) - return; + if (ability1 === 'defamations') { + if (data.me === player1 || data.me === player2) + return { + alertText: output.defamationOnYouThenStack!({ + mech1: output.defamationOnYou!(), + mech2: output.stacks!(), + }) + }; + if (data.me === player3 || data.me === player4) + return { + infoText: output.defamationsThenStackOnYou!({ + mech1: output.defamations!(), + mech2: output.stackOnYou!(), + }) + }; + return { - [text]: output.oneMechThenTwo!({ - mech1: output[this1]!(), - mech2: next1 === 'stack' - ? output.stackOnPlayer!({ player: shortPlayer3 }) - : output[next1]!(), - mech3: next2 === 'stack' - ? output.stackOnPlayer!({ player: shortPlayer4 }) - : output[next2]!(), - }), + infoText: output.defamationsThenStacks!({ + mech1: output.defamations!(), + mech2: output.stacks!(), + }) }; } + } - if (!isThisSame && isNextSame) { - if (this2 === undefined) - return; + // Last set followed up with tower positions + if (ability1 === 'stacks') { + if (data.me === player1 || data.me === player2) return { - [text]: output.twoMechsThenOne!({ - mech1: this1 === 'stack' - ? output.stackOnPlayer!({ player: shortPlayer1 }) - : output[this1]!(), - mech2: this2 === 'stack' - ? output.stackOnPlayer!({ player: shortPlayer2 }) - : output[this2]!(), - mech3: output[next1]!(), - }), + alertText: output.stackOnYouThenDefamations!({ + mech1: output.stackOnYou!(), + mech2: output.towers!(), + }) }; - } - if (this2 === undefined || next2 === undefined) - return; - return { - [text]: output.twoMechsThenTwo!({ - mech1: this1 === 'stack' - ? output.stackOnPlayer!({ player: shortPlayer1 }) - : output[this1]!(), - mech2: this2 === 'stack' - ? output.stackOnPlayer!({ player: shortPlayer2 }) - : output[this2]!(), - mech3: next1 === 'stack' - ? output.stackOnPlayer!({ player: shortPlayer3 }) - : output[next1]!(), - mech4: next2 === 'stack' - ? output.stackOnPlayer!({ player: shortPlayer4 }) - : output[next2]!(), - }), - }; - } - // Build output for last mechanic set to warn of towers - if (this1 === undefined) - return; - if (isThisSame) { return { - [text]: output.oneMechThenTowers!({ - mech1: output[this1]!(), - towers: output.towers!(), - }), + infoText: output.stacksThenDefamations!({ + mech1: output.stacks!(), + mech2: output.towers!(), + }) }; } - if (!isThisSame) { - if (this2 === undefined) - return; + + if (ability1 === 'defamations') { + if (data.me === player1 || data.me === player2) + return { + alertText: output.defamationOnYouThenStack!({ + mech1: output.defamationOnYou!(), + mech2: output.towers!(), + }) + }; + return { - [text]: output.twoMechsThenTowers!({ - mech1: this1 === 'stack' - ? output.stackOnPlayer!({ player: shortPlayer1 }) - : output[this1]!(), - mech2: this2 === 'stack' - ? output.stackOnPlayer!({ player: shortPlayer2 }) - : output[this2]!(), - towers: output.towers!(), - }), + infoText: output.defamationsThenStacks!({ + mech1: output.defamations!(), + mech2: output.towers!(), + }) }; } }, From b12bf208203d8733bc19ed95d08ec589ed16a887 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 1 Feb 2026 04:21:45 -0500 Subject: [PATCH 118/139] lint --- ui/raidboss/data/07-dt/raid/r12s.ts | 52 ++++++++++++++++------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 5c08e8f4e9c..54693cdaf61 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -3098,8 +3098,8 @@ const triggerSet: TriggerSet = { const ability1 = abilityId === headMarkerData['manaBurstTether'] ? 'defamations' : abilityId === headMarkerData['heavySlamTether'] - ? 'stacks' - : 'unknown'; + ? 'stacks' + : 'unknown'; if (ability1 === 'stacks') { if (data.me === player1 || data.me === player2) @@ -3107,7 +3107,7 @@ const triggerSet: TriggerSet = { alertText: output.stackOnYouThenDefamations!({ mech1: output.stackOnYou!(), mech2: output.defamations!(), - }) + }), }; if (data.me === player3 || data.me === player4) @@ -3115,14 +3115,14 @@ const triggerSet: TriggerSet = { infoText: output.stacksThenDefamationOnYou!({ mech1: output.stacks!(), mech2: output.defamationOnYou!(), - }) + }), }; return { infoText: output.stacksThenDefamations!({ mech1: output.stacks!(), mech2: output.defamations!(), - }) + }), }; } @@ -3132,7 +3132,7 @@ const triggerSet: TriggerSet = { alertText: output.defamationOnYouThenStack!({ mech1: output.defamationOnYou!(), mech2: output.stacks!(), - }) + }), }; if (data.me === player3 || data.me === player4) @@ -3140,14 +3140,14 @@ const triggerSet: TriggerSet = { infoText: output.defamationsThenStackOnYou!({ mech1: output.defamations!(), mech2: output.stackOnYou!(), - }) + }), }; return { infoText: output.defamationsThenStacks!({ mech1: output.defamations!(), mech2: output.stacks!(), - }) + }), }; } }, @@ -3208,13 +3208,19 @@ const triggerSet: TriggerSet = { const abilityIds = data.replication4DirNumAbility; const player1 = count === 2 ? players[1] - : count === 4 ? players[2] : players[3]; + : count === 4 + ? players[2] + : players[3]; const player2 = count === 2 ? players[5] - : count === 4 ? players[6] : players[7]; + : count === 4 + ? players[6] + : players[7]; const abilityId = count === 2 ? abilityIds[1] - : count === 4 ? abilityIds[2] : abilityIds[3]; + : count === 4 + ? abilityIds[2] + : abilityIds[3]; if ( abilityId === undefined || player1 === undefined || @@ -3225,8 +3231,8 @@ const triggerSet: TriggerSet = { const ability1 = abilityId === headMarkerData['manaBurstTether'] ? 'defamations' : abilityId === headMarkerData['heavySlamTether'] - ? 'stacks' - : 'unknown'; + ? 'stacks' + : 'unknown'; if (count < 6) { const player3 = count === 2 ? players[2] : players[3]; @@ -3240,7 +3246,7 @@ const triggerSet: TriggerSet = { alertText: output.stackOnYouThenDefamations!({ mech1: output.stackOnYou!(), mech2: output.defamations!(), - }) + }), }; if (data.me === player3 || data.me === player4) @@ -3248,14 +3254,14 @@ const triggerSet: TriggerSet = { infoText: output.stacksThenDefamationOnYou!({ mech1: output.stacks!(), mech2: output.defamationOnYou!(), - }) + }), }; return { infoText: output.stacksThenDefamations!({ mech1: output.stacks!(), mech2: output.defamations!(), - }) + }), }; } @@ -3265,21 +3271,21 @@ const triggerSet: TriggerSet = { alertText: output.defamationOnYouThenStack!({ mech1: output.defamationOnYou!(), mech2: output.stacks!(), - }) + }), }; if (data.me === player3 || data.me === player4) return { infoText: output.defamationsThenStackOnYou!({ mech1: output.defamations!(), mech2: output.stackOnYou!(), - }) + }), }; return { infoText: output.defamationsThenStacks!({ mech1: output.defamations!(), mech2: output.stacks!(), - }) + }), }; } } @@ -3291,14 +3297,14 @@ const triggerSet: TriggerSet = { alertText: output.stackOnYouThenDefamations!({ mech1: output.stackOnYou!(), mech2: output.towers!(), - }) + }), }; return { infoText: output.stacksThenDefamations!({ mech1: output.stacks!(), mech2: output.towers!(), - }) + }), }; } @@ -3308,14 +3314,14 @@ const triggerSet: TriggerSet = { alertText: output.defamationOnYouThenStack!({ mech1: output.defamationOnYou!(), mech2: output.towers!(), - }) + }), }; return { infoText: output.defamationsThenStacks!({ mech1: output.defamations!(), mech2: output.towers!(), - }) + }), }; } }, From e114f2f91356c1a9c8497e99c7584f54e997069f Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 1 Feb 2026 05:56:53 -0500 Subject: [PATCH 119/139] twisted vision 7 plaform and twisted vision 8 cleave --- ui/raidboss/data/07-dt/raid/r12s.ts | 263 +++++++++++++++++++--------- 1 file changed, 184 insertions(+), 79 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 54693cdaf61..3a449510ed2 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -66,7 +66,10 @@ export interface Data extends RaidbossData { twistedVisionCounter: number; replication3CloneOrder: number[]; replication3CloneDirNumPlayers: { [dirNum: number]: string }; - idyllicVision2NorthSouthCleaveSpot?: 'north' | 'south'; + idyllicVision2NorthSouthCleaveSpot?: 'north' | 'south';\ + idyllicDreamActorEW?: string; + idyllicDreamActorNS?: string; + idyllicDreamActorSnaking?: string; replication4DirNumAbility: { [dirNum: number]: string }; replication4PlayerAbilities: { [player: string]: string }; replication4BossCloneDirNumPlayers: { [dirNum: number]: string }; @@ -2772,18 +2775,31 @@ const triggerSet: TriggerSet = { }, }, { - id: 'R12S Idyllic Dream Power Gusher Collect', - // Need to know this for later + id: 'R12S Idyllic Dream Power Gusher and Snaking Kick Collect', + // Need to know these for later // B511 Snaking Kick // B512 from boss is the VFX and has headings that show directions for B50F and B510 // B50F Power Gusher is the East/West caster // B510 Power Gusher is the North/South caster // Right now just the B510 caster is needed to resolve type: 'StartsUsing', - netRegex: { id: 'B510', source: 'Lindschrat', capture: true }, + netRegex: { id: ['B50F', 'B510', 'B511'], source: 'Lindschrat', capture: true }, run: (data, matches) => { - const y = parseFloat(matches.y); - data.idyllicVision2NorthSouthCleaveSpot = y < center.y ? 'north' : 'south'; + // Temporal Curtain can have early calls based on matching the id for which add went where + switch (matches.id) { + case 'B510': { + const y = parseFloat(matches.y); + data.idyllicVision2NorthSouthCleaveSpot = y < center.y ? 'north' : 'south'; + data.idyllicDreamActorEW = matches.id; + return; + } + case 'B511': + data.idyllicDreamActorSnaking = matches.id; + return; + case 'B50F': + data.idyllicDreamActorNS = matches.id; + return; + } }, }, { @@ -2820,43 +2836,29 @@ const triggerSet: TriggerSet = { return true; return false; }, - suppressSeconds: 9999, // Can get spammy if players have more than 1 tether or swap a lot - infoText: (data, matches, output) => { - // Get direction of the tether - const actor = data.actorPositions[matches.sourceId]; - if (actor === undefined) { - switch (matches.id) { - case headMarkerData['manaBurstTether']: - return output.manaBurstTether!(); - case headMarkerData['heavySlamTether']: - return output.heavySlamTether!(); - } - return; + delaySeconds: 0.1, + suppressSeconds: 9999, + infoText: (data, _matches, output) => { + const first = data.replication4DirNumAbility[0]; + if (first === undefined) { + return output.getTether!(); } - const dirNum = Directions.xyTo8DirNum(actor.x, actor.y, center.x, center.y); - const dir = Directions.output8Dir[dirNum] ?? 'unknown'; - - switch (matches.id) { - case headMarkerData['manaBurstTether']: - return output.manaBurstTetherDir!({ dir: output[dir]!() }); - case headMarkerData['heavySlamTether']: - return output.heavySlamTetherDir!({ dir: output[dir]!() }); - } + if (first === headMarkerData['manaBurstTether']) + return output.stacksFirst!(); + if (first === headMarkerData['heavySlamTether']) + return output.defamationsFirst!(); + return output.getTether!(); }, outputStrings: { - ...Directions.outputStrings8Dir, - manaBurstTether: { - en: 'Defamation Tether on YOU', + getTether: { + en: 'Get Tether', }, - manaBurstTetherDir: { - en: '${dir} Defamation Tether on YOU', + defamationsFirst: { + en: 'Defamations First (later); Get Tether', }, - heavySlamTether: { - en: 'Stack Tether on YOU', - }, - heavySlamTetherDir: { - en: '${dir} Stack Tether on YOU', + stacksFirst: { + en: 'Stacks First (later); Get Tether', }, }, }, @@ -3016,6 +3018,14 @@ const triggerSet: TriggerSet = { netRegex: { id: 'B529', source: 'Lindwurm', capture: false }, response: Responses.spread(), }, + { + id: 'R12S Light Resistance Down II Collect', + // Players cannot soak a tower that has holy (triple element towers) + type: 'GainsEffect', + netRegex: { effectId: '1044', capture: true }, + condition: Conditions.targetIsYou(), + run: (data) => data.hasLightResistanceDown = true, + }, { id: 'R12S Light Resistance Down II', type: 'GainsEffect', @@ -3457,6 +3467,99 @@ const triggerSet: TriggerSet = { }, }, }, + { + id: 'R12S Temporal Curtain Part 1 Collect', + // Describe actor going into portal + type: 'Ability', + netRegex: { id: 'B51D', source: 'Lindschrat', capture: true }, + run: (data, matches) => { + switch (matches.id) { + case data.idyllicDreamActorEW: + data.idyllicVision8SafeSides = 'frontBack'; + return; + case data.idyllicDreamActorNS: + data.idyllicVision8SafeSides = 'sides'; + } + }, + }, + { + id: 'R12S Temporal Curtain Part 1', + // Describe actor going into portal + type: 'Ability', + netRegex: { id: 'B51D', source: 'Lindschrat', capture: true }, + infoText: (data, matches, output) => { + switch (matches.id) { + case data.idyllicDreamActorEW: + return output.frontBackLater!(); + case data.idyllicDreamActorNS: + return output.sidesLater!(); + } + }, + outputStrings: { + frontBackLater: { + en: 'Portal + Front/Back Clone (later)', + }, + sidesLater: { + en: 'Portal + Sides Clone (later)', + }, + }, + }, + { + id: 'R12S Temporal Curtain Part 2 Collect', + // Describe actor going into portal + type: 'AbilityExtra', + netRegex: { id: 'B4D9', capture: true }, + run: (data, matches) => { + switch (matches.id) { + case data.idyllicDreamActorEW: + data.idyllicVision7SafeSides = 'frontBack'; + return; + case data.idyllicDreamActorNS: + data.idyllicVision7SafeSides = 'sides'; + return; + case data.idyllicDreamActorSnaking: { + const x = parseFloat(matches.x); + data.idyllicVision7SafePlatform = x < 100 ? 'west' : 'east'; + } + } + }, + }, + { + id: 'R12S Temporal Curtain Part 2', + // Describe actor going into portal + type: 'AbilityExtra', + netRegex: { id: 'B4D9', capture: false }, + delaySeconds: 0.1, + suppressSeconds: 9999, + infoText: (data, _matches, output) => { + if (data.idyllicVision7SafeSides === 'frontBack') { + if (data.idyllicVision7SafePlatform === 'east') + return output.frontBackEastLater!(); + if (data.idyllicVision7SafePlatform === 'west') + return output.frontBackWestLater!(); + } + if (data.idyllicVision7SafeSides === 'sides') { + if (data.idyllicVision7SafePlatform === 'east') + return output.sidesEastLater!(); + if (data.idyllicVision7SafePlatform === 'west') + return output.sidesWestLater!(); + } + }, + outputStrings: { + frontBackWestLater: { + en: 'West Platform => Front/Back Clone (later)', + }, + sidesWestLater: { + en: 'West Platform => Sides Clone (later)', + }, + frontBackEastLater: { + en: 'East Platform => Front/Back Clone (later)', + }, + sidesEastLater: { + en: 'East Platform => Sides Clone (later)', + }, + }, + }, { id: 'R12S Twisted Vision 6 Light Party Stacks', // At end of cast it's cardinal or intercard @@ -3503,59 +3606,40 @@ const triggerSet: TriggerSet = { }, { id: 'R12S Twisted Vision 7 Safe Platform', - // TODO: Get direction of the safe platform + N/S or E/W? type: 'StartsUsing', netRegex: { id: 'BBE2', source: 'Lindwurm', capture: true }, condition: (data) => data.twistedVisionCounter === 7, - durationSeconds: (_data, matches) => parseFloat(matches.castTime) + 3, + durationSeconds: (_data, matches) => parseFloat(matches.castTime), infoText: (data, _matches, output) => { - const first = data.replication3CloneOrder[0]; - if (first === undefined) - return; - const dirNumOrder = first % 2 !== 0 ? [0, 2, 4, 6] : [1, 3, 5, 7]; - - // Need to lookup what ability is at each dir, only need cards or intercard dirs - const abilities = data.replication4AbilityOrder.slice(4, 8); - const stackDirs = []; - let i = 0; - - // Find first all stacks in cards or intercards - // Incorrect amount means players made an unsolvable? run - for (const dirNum of dirNumOrder) { - if (abilities[i++] === headMarkerData['heavySlamTether']) - stackDirs.push(dirNum); + if (data.idyllicVision7SafeSides === 'frontBack') { + if (data.idyllicVision7SafePlatform === 'east') + return output.northSouthEastPlaform!(); + if (data.idyllicVision7SafePlatform === 'west') + return output.northSouthWestPlaform!(); } - // Only grabbing first two - const dirNum1 = stackDirs[0]; - const dirNum2 = stackDirs[1]; - - // If we failed to get two stacks, just output generic cards/intercards reminder - if (dirNum1 === undefined || dirNum2 === undefined) { - const card = first % 2 !== 0 ? 'cardinals' : 'intercards'; - return output.platformThenStack!({ - platform: output.safePlatform!(), - stack: output[card]!(), - }); + if (data.idyllicVision7SafeSides === 'sides') { + if (data.idyllicVision7SafePlatform === 'east') + return output.eastWestEastPlaform!(); + if (data.idyllicVision7SafePlatform === 'west') + return output.eastWestWestPlaform!(); } - const dir1 = Directions.output8Dir[dirNum1]; - const dir2 = Directions.output8Dir[dirNum2]; - return output.platformThenStack!({ - platform: output.safePlatform!(), - stack: output.stack!({ dir1: dir1, dir2: dir2 }), - }); + return output.safePlatform!(); }, outputStrings: { - ...Directions.outputStrings8Dir, - cardinals: Outputs.cardinals, - intercards: Outputs.intercards, safePlatform: { - en: 'Safe Platform', + en: 'Move to Safe Platform Side => Dodge Cleaves', }, - stack: { - en: 'Stack ${dir1}/${dir2}', + sidesWestPlatform: { + en: 'West Platform => Sides of Clone', + }, + sidesEastPlatform: { + en: 'East Platform => Sides of Clone', }, - platformThenStack: { - en: '${platform} => ${stack}', + frontBackEastPlatform: { + en: 'East Platform => Front/Back of Clone', + }, + frontBackWestPlatform: { + en: 'West Platform => Front/Back of Clone', }, }, }, @@ -3603,6 +3687,27 @@ const triggerSet: TriggerSet = { }, }, }, + { + id: 'R12S Twisted Vision 8 Dodge Cleaves', + // Trigger on Clone's BE5D Heavy Slam + type: 'Ability', + netRegex: { id: 'BE5D', source: 'Lindwurm', capture: false }, + suppressSeconds: 9999, + alertText: (data, _matches, output) => { + if (data.idyllicVision8SafeSides === 'sides') + return output.sides!(); + if (data.idyllicVision8SafeSides === 'frontBack') + return output.frontBack!(); + }, + outputStrings: { + sides: { + en: 'Sides of Clone', + }, + frontBack: { + en: 'Front/Back of Clone', + }, + }, + }, ], timelineReplace: [ { From 1c8b68c1e0e1e88dbbb7d3eca157383e34c2b04b Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 1 Feb 2026 05:58:04 -0500 Subject: [PATCH 120/139] minor timing updates --- ui/raidboss/data/07-dt/raid/r12s.txt | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.txt b/ui/raidboss/data/07-dt/raid/r12s.txt index c52d6bf5117..c873e1e7b24 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.txt +++ b/ui/raidboss/data/07-dt/raid/r12s.txt @@ -473,22 +473,18 @@ hideall "--sync--" # Twisted Vision 5: Towers 3393.6 "Twisted Vision 5" Ability { id: "BBE2", source: "Lindwurm" } -3398.0 "Cosmic Kiss" Ability { id: "B4F4", source: "Lindwurm" } - -# fflogs adjusted by 4.1s -3398.7 "Lindwurm's Dark II" Ability { id: "B4F6", source: "Lindwurm" } -3400.7 "Pyretic Wurm" Ability { id: "B4F9", source: "Lindwurm" } -3403.7 "Lindwurm's Stone III" #Ability { id: "B4F7", source: "Lindwurm" } -3408.7 "Lindwurm's Thunder II" #Ability { id: "B4FA", source: "Lindwurm" } -3408.7 "Lindwurm's Glare" #Ability { id: "B4F8", source: "Lindwurm" } -3408.7 "Lindwurm's Thunder II" #Ability { id: "B4FA", source: "Lindwurm" } -3408.7 "Lindwurm's Glare" #Ability { id: "B4F8", source: "Lindwurm" } -3412.0 "Temporal Curtain" Ability { id: "B51C", source: "Lindwurm" } window 10,10 +3398.0 "Cosmic Kiss x8" Ability { id: "B4F4", source: "Lindwurm" } +3398.7 "Lindwurm's Dark II x2" Ability { id: "B4F6", source: "Lindwurm" } +3400.7 "Pyretic Wurm x2" Ability { id: "B4F9", source: "Lindwurm" } +3403.7 "Lindwurm's Stone III x2" #Ability { id: "B4F7", source: "Lindwurm" } +3408.7 "Lindwurm's Thunder II x4" #Ability { id: "B4FA", source: "Lindwurm" } +3408.7 "Lindwurm's Glare x4" #Ability { id: "B4F8", source: "Lindwurm" } +3417.1 "Temporal Curtain" Ability { id: "B51C", source: "Lindwurm" } +3420.2 "--clone takes portal--" Ability { id: "B51D", source: "Lindschrat" } +3423.3 "--clones on platform--" Ability { id: "B4D9", source: "Lindschrat" } # fflogs -3416.0 "--sync--" Ability { id: "B51D", source: "Lindschrat" } -3419.1 "--sync--" Ability { id: "B4D9", source: "Lindschrat" } -3425.4 "Twisted Vision 6" Ability { id: "BBE2", source: "Lindwurm" } +3425.4 "Twisted Vision 6" Ability { id: "BBE2", source: "Lindwurm" } window 10,10 3426.4 "Power Gusher" #Ability { id: "B50F", source: "Lindschrat" } 3426.4 "Snaking Kick" Ability { id: "BCAF", source: "Lindschrat" } 3431.5 "Reenactment 2" Ability { id: "B4EC", source: "Lindwurm" } From e4bf519a88e8b25399fc143b77abfae21e36f1be Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 1 Feb 2026 05:59:17 -0500 Subject: [PATCH 121/139] remove invalid character --- ui/raidboss/data/07-dt/raid/r12s.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 3a449510ed2..fdcd1536f4e 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -66,7 +66,7 @@ export interface Data extends RaidbossData { twistedVisionCounter: number; replication3CloneOrder: number[]; replication3CloneDirNumPlayers: { [dirNum: number]: string }; - idyllicVision2NorthSouthCleaveSpot?: 'north' | 'south';\ + idyllicVision2NorthSouthCleaveSpot?: 'north' | 'south'; idyllicDreamActorEW?: string; idyllicDreamActorNS?: string; idyllicDreamActorSnaking?: string; From 3e0a73104f883df0c58abf062fb0ccd6df96ace9 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 1 Feb 2026 06:01:30 -0500 Subject: [PATCH 122/139] add missing variables --- ui/raidboss/data/07-dt/raid/r12s.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index fdcd1536f4e..b3487d6decf 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -78,6 +78,9 @@ export interface Data extends RaidbossData { hasLightResistanceDown: boolean; twistedVision4MechCounter: number; doomPlayers: string[]; + idyllicVision8SafeSides?: 'frontBack' | 'sides'; + idyllicVision7SafeSides?: 'frontBack' | 'sides'; + idyllicVision7SafePlatform?: 'east' | 'west'; } const headMarkerData = { From 84c8b87dd377e12b9b6778784de12837c2a82f8a Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 1 Feb 2026 06:40:03 -0500 Subject: [PATCH 123/139] fix missing output --- ui/raidboss/data/07-dt/raid/r12s.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index b3487d6decf..923911de741 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -3616,15 +3616,15 @@ const triggerSet: TriggerSet = { infoText: (data, _matches, output) => { if (data.idyllicVision7SafeSides === 'frontBack') { if (data.idyllicVision7SafePlatform === 'east') - return output.northSouthEastPlaform!(); + return output.frontBackEastPlaform!(); if (data.idyllicVision7SafePlatform === 'west') - return output.northSouthWestPlaform!(); + return output.frontBackWestPlaform!(); } if (data.idyllicVision7SafeSides === 'sides') { if (data.idyllicVision7SafePlatform === 'east') - return output.eastWestEastPlaform!(); + return output.sidesEastPlaform!(); if (data.idyllicVision7SafePlatform === 'west') - return output.eastWestWestPlaform!(); + return output.sidesWestPlaform!(); } return output.safePlatform!(); }, From 74efb5a47e42c9451981409a5a550374e26d3769 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 1 Feb 2026 15:24:01 -0500 Subject: [PATCH 124/139] typo --- ui/raidboss/data/07-dt/raid/r12s.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 923911de741..443f9a825ba 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -3616,15 +3616,15 @@ const triggerSet: TriggerSet = { infoText: (data, _matches, output) => { if (data.idyllicVision7SafeSides === 'frontBack') { if (data.idyllicVision7SafePlatform === 'east') - return output.frontBackEastPlaform!(); + return output.frontBackEastPlatform!(); if (data.idyllicVision7SafePlatform === 'west') - return output.frontBackWestPlaform!(); + return output.frontBackWestPlatform!(); } if (data.idyllicVision7SafeSides === 'sides') { if (data.idyllicVision7SafePlatform === 'east') - return output.sidesEastPlaform!(); + return output.sidesEastPlatform!(); if (data.idyllicVision7SafePlatform === 'west') - return output.sidesWestPlaform!(); + return output.sidesWestPlatform!(); } return output.safePlatform!(); }, From 76b12082bb068b75d0fec3d7db90539ddff22ccc Mon Sep 17 00:00:00 2001 From: Legends0 Date: Sun, 1 Feb 2026 20:44:47 -0500 Subject: [PATCH 125/139] fix dir not output --- ui/raidboss/data/07-dt/raid/r12s.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 443f9a825ba..867faf9a1a0 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -3594,9 +3594,9 @@ const triggerSet: TriggerSet = { if (dirNum1 === undefined || dirNum2 === undefined) { return first % 2 === 0 ? output.cardinals!() : output.intercards!(); } - const dir1 = Directions.output8Dir[dirNum1]; - const dir2 = Directions.output8Dir[dirNum2]; - return output.stack!({ dir1: dir1, dir2: dir2 }); + const dir1 = Directions.output8Dir[dirNum1] ?? 'unknown'; + const dir2 = Directions.output8Dir[dirNum2] ?? 'unknown'; + return output.stack!({ dir1: output[dir1]!(), dir2: output[dir2]!() }); }, outputStrings: { ...Directions.outputStrings8Dir, @@ -3677,9 +3677,9 @@ const triggerSet: TriggerSet = { if (dirNum1 === undefined || dirNum2 === undefined) { return first % 2 !== 0 ? output.cardinals!() : output.intercards!(); } - const dir1 = Directions.output8Dir[dirNum1]; - const dir2 = Directions.output8Dir[dirNum2]; - return output.stack!({ dir1: dir1, dir2: dir2 }); + const dir1 = Directions.output8Dir[dirNum1] ?? 'unknown'; + const dir2 = Directions.output8Dir[dirNum2] ?? 'unknown'; + return output.stack!({ dir1: output[dir1]!(), dir2: output[dir2]!() }); }, outputStrings: { ...Directions.outputStrings8Dir, From cc58a5c805c9d043d817c5972fbc2aeebd68fa6c Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 2 Feb 2026 01:09:34 -0500 Subject: [PATCH 126/139] add additional portent bait calls output at the moment is just default dn --- ui/raidboss/data/07-dt/raid/r12s.ts | 77 +++++++++++++++++++++-------- 1 file changed, 57 insertions(+), 20 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 867faf9a1a0..e10b61aec78 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -78,6 +78,8 @@ export interface Data extends RaidbossData { hasLightResistanceDown: boolean; twistedVision4MechCounter: number; doomPlayers: string[]; + hasDoom: boolean; + hasPyretic: boolean; idyllicVision8SafeSides?: 'frontBack' | 'sides'; idyllicVision7SafeSides?: 'frontBack' | 'sides'; idyllicVision7SafePlatform?: 'east' | 'west'; @@ -182,6 +184,8 @@ const triggerSet: TriggerSet = { hasLightResistanceDown: false, twistedVision4MechCounter: 0, doomPlayers: [], + hasDoom: false, + hasPyretic: false, }), triggers: [ { @@ -3362,6 +3366,14 @@ const triggerSet: TriggerSet = { }, }, }, + { + id: 'R12S Hot-blooded Collect', + // Player can still cast, but shouldn't move for 5s duration + type: 'GainsEffect', + netRegex: { effectId: '12A0', capture: true }, + condition: Conditions.targetIsYou(), + run: (data, _matches) => data.hasPyretic = true, + }, { id: 'R12S Hot-blooded', // Player can still cast, but shouldn't move for 5s duration @@ -3391,7 +3403,11 @@ const triggerSet: TriggerSet = { // Happens about 1.3s after Dark Tower when it casts B4F6 Lindwurm's Dark II type: 'GainsEffect', netRegex: { effectId: 'D24', capture: true }, - run: (data, matches) => data.doomPlayers.push(matches.target), + run: (data, matches) => { + data.doomPlayers.push(matches.target); + if (data.me === matches.target) + data.hasDoom = true; + }, }, { id: 'R12S Doom Cleanse', @@ -3432,41 +3448,55 @@ const triggerSet: TriggerSet = { // 129E Farwaway Portent // 129F Nearby Portent // 10s duration, need to delay to avoid earth + doom trigger overlap - // TODO: Configure for element tower they soaked + // This would go out to players that soaked white/holy meteors type: 'GainsEffect', netRegex: { effectId: ['129E', '129F'], capture: true }, condition: Conditions.targetIsYou(), delaySeconds: (_data, matches) => parseFloat(matches.duration) - 5.3, - infoText: (_data, matches, output) => { - if (matches.id === '129E') - return output.farOnYou!(); - return output.nearOnYou!(); + infoText: (data, matches, output) => { + if (matches.id === '129E') { + if (data.hasDoom) + return output.farOnYouDark!(); + return output.farOnYouWind!(); + } + if (data.hasDoom) + return output.nearOnYouDark!(); + return output.nearOnYouWind!(); }, outputStrings: { - nearOnYou: { + nearOnYouWind: { en: 'Near on YOU: Be on Middle Hitbox', }, - farOnYou: { - en: 'Far on YOU: Be on N/S Hitbox', // Most parties probably put this North? + nearOnYouDark: { + en: 'Near on YOU: Be on Hitbox N', + }, + farOnYouWind: { + en: 'Far on YOU: Be on Middle Hitbox', + }, + farOnYouDark: { + en: 'Far on YOU: Be on Hitbox N', }, }, }, { id: 'R12S Nearby and Faraway Portent Baits', - // TODO: Configure for element tower they soaked + // This would go out on players that soaked fire/earth meteors type: 'GainsEffect', netRegex: { effectId: ['129E', '129F'], capture: true }, condition: (data) => data.hasLightResistanceDown, delaySeconds: (_data, matches) => parseFloat(matches.duration) - 5.3, suppressSeconds: 1, - infoText: (_data, _matches, output) => output.bait!(), + infoText: (data, _matches, output) => { + if (data.hasPyretic) + return output.baitFire!(); + return output.baitEarth!(); + }, outputStrings: { - bait: { - en: 'Bait Cone', - de: 'Köder Kegel-AoE', - cn: '诱导扇形', - ko: '부채꼴 유도', - tc: '誘導扇形', + baitFire: { + en: 'Bait Cone N/S', + }, + baitEarth: { + en: 'Bait Cone N/S', }, }, }, @@ -3476,7 +3506,7 @@ const triggerSet: TriggerSet = { type: 'Ability', netRegex: { id: 'B51D', source: 'Lindschrat', capture: true }, run: (data, matches) => { - switch (matches.id) { + switch (matches.sourceId) { case data.idyllicDreamActorEW: data.idyllicVision8SafeSides = 'frontBack'; return; @@ -3491,7 +3521,7 @@ const triggerSet: TriggerSet = { type: 'Ability', netRegex: { id: 'B51D', source: 'Lindschrat', capture: true }, infoText: (data, matches, output) => { - switch (matches.id) { + switch (matches.sourceId) { case data.idyllicDreamActorEW: return output.frontBackLater!(); case data.idyllicDreamActorNS: @@ -3513,7 +3543,7 @@ const triggerSet: TriggerSet = { type: 'AbilityExtra', netRegex: { id: 'B4D9', capture: true }, run: (data, matches) => { - switch (matches.id) { + switch (matches.sourceId) { case data.idyllicDreamActorEW: data.idyllicVision7SafeSides = 'frontBack'; return; @@ -3711,6 +3741,13 @@ const triggerSet: TriggerSet = { }, }, }, + { + id: 'R12S Arcadian Hell', + type: 'StartsUsing', + netRegex: { id: 'B533', source: 'Lindwurm', capture: false }, + durationSeconds: 4.7, + response: Responses.bigAoe('alert'), + }, ], timelineReplace: [ { From e4b1b8f9a3d8d7866655dcfa26bfc5bd88f42206 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 2 Feb 2026 18:27:40 -0500 Subject: [PATCH 127/139] fix Replication 4 Ability Tethers Initial Call --- ui/raidboss/data/07-dt/raid/r12s.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index e10b61aec78..8956891dc6f 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -2851,9 +2851,9 @@ const triggerSet: TriggerSet = { return output.getTether!(); } - if (first === headMarkerData['manaBurstTether']) - return output.stacksFirst!(); if (first === headMarkerData['heavySlamTether']) + return output.stacksFirst!(); + if (first === headMarkerData['manaBurstTether']) return output.defamationsFirst!(); return output.getTether!(); }, From 1614f23d12defb38f85d9e1449a92277c6e273bd Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 2 Feb 2026 19:04:09 -0500 Subject: [PATCH 128/139] fix idyllicVision7SafePlatform it was set to where the add was, woops --- ui/raidboss/data/07-dt/raid/r12s.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 8956891dc6f..322ea259ee1 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -3552,7 +3552,7 @@ const triggerSet: TriggerSet = { return; case data.idyllicDreamActorSnaking: { const x = parseFloat(matches.x); - data.idyllicVision7SafePlatform = x < 100 ? 'west' : 'east'; + data.idyllicVision7SafePlatform = x < 100 ? 'east' : 'west'; } } }, From 67cd28f18a500d183e6bc34687066fe1e5154542 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 2 Feb 2026 19:18:53 -0500 Subject: [PATCH 129/139] fix for no output Order of trigger here should mean this always fires after the collect and the collect will have stored the data before this fires. --- ui/raidboss/data/07-dt/raid/r12s.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 322ea259ee1..7a46736334a 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -3562,8 +3562,6 @@ const triggerSet: TriggerSet = { // Describe actor going into portal type: 'AbilityExtra', netRegex: { id: 'B4D9', capture: false }, - delaySeconds: 0.1, - suppressSeconds: 9999, infoText: (data, _matches, output) => { if (data.idyllicVision7SafeSides === 'frontBack') { if (data.idyllicVision7SafePlatform === 'east') From 7704ddea87379195a727d3705b7b2e83ff0cd595 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 2 Feb 2026 19:25:28 -0500 Subject: [PATCH 130/139] fix for Twisted Vision 8 cleave call not working --- ui/raidboss/data/07-dt/raid/r12s.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 7a46736334a..7851bf1c20e 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -3723,13 +3723,16 @@ const triggerSet: TriggerSet = { // Trigger on Clone's BE5D Heavy Slam type: 'Ability', netRegex: { id: 'BE5D', source: 'Lindwurm', capture: false }, - suppressSeconds: 9999, alertText: (data, _matches, output) => { if (data.idyllicVision8SafeSides === 'sides') return output.sides!(); if (data.idyllicVision8SafeSides === 'frontBack') return output.frontBack!(); }, + run: (data) => { + // Prevent re-execution of output + delete data.idyllicVision8SafeSides; + }, outputStrings: { sides: { en: 'Sides of Clone', From 5a90be0ceefd7da021822ac9e799b83e718d35f0 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 2 Feb 2026 19:35:47 -0500 Subject: [PATCH 131/139] remaining p2 timeline network log --- ui/raidboss/data/07-dt/raid/r12s.txt | 87 ++++++++++++++++------------ 1 file changed, 50 insertions(+), 37 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.txt b/ui/raidboss/data/07-dt/raid/r12s.txt index c873e1e7b24..a96f6b5a2d9 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.txt +++ b/ui/raidboss/data/07-dt/raid/r12s.txt @@ -342,7 +342,7 @@ hideall "--sync--" ### Phase 2: Lindwurm II # -p B528:3015.7 -# -ii B51F B4DA B4DB B4DD B4DF B4E3 B4E6 B4F0 B4E9 B4F1 B4EE B4EF B508 B4F5 B512 B513 B514 B515 +# -ii B51F B4DA B4DB B4DD B4DF B4E3 B4E6 B4F0 B4E9 B4F1 B4EE B4EF B508 B4F5 B512 B513 B514 B515 B4F9 B51B # -it "Lindwurm" 3000.5 label "r12s-p2-start" 3010.7 "--sync--" StartsUsing { id: "B528", source: "Lindwurm" } window 3100,10 @@ -429,7 +429,7 @@ hideall "--sync--" 3252.6 "Esoteric Finisher" Ability { id: "B526", source: "Lindwurm" } 3260.7 "--middle--" Ability { id: "B4D9", source: "Lindwurm" } -# TBD Clean-up/Generalize this as well since stacks/defamation order is tether dependent +# Idyllic Dream 3268.8 "Idyllic Dream" Ability { id: "B509", source: "Lindwurm" } 3275.0 "Staging" Ability { id: "B4E1", source: "Lindwurm" } 3276.3 "--clones x4 1--" #ActorControlExtra { category: "0197", param1: "11D2" } @@ -450,7 +450,7 @@ hideall "--sync--" 3326.1 "--tethers--" #Tether { id: ["0170", "0171"], source: "Lindschrat" } # These could change hands causing sync issue 3334.1 "--locked tethers--" Tether { id: "0175", source: "Lindschrat" } -# Towers Preview +# Twisted Vision 3: Towers Preview 3334.3 "Twisted Vision 3" Ability { id: "BBE2", source: "Lindwurm" } 3338.7 "Snaking Kick" Ability { id: "BE95", source: "Lindwurm" } 3338.9 "Power Gusher x4" #Ability { id: "B516", source: "Lindwurm" } # Front/Back are apparently counting as their own casts @@ -459,7 +459,7 @@ hideall "--sync--" 3355.0 "Arcadian Arcanum (castbar)" Ability { id: "B529", source: "Lindwurm" } 3356.2 "Arcadian Arcanum" Ability { id: "B9D9", source: "Lindwurm" } -# First Stacks/Defamations +# Twisted Vision 4: First Stacks/Defamations # Orders could be different, but we can detect B517 abiltiy to know if Mana Burst is coming 1.2s before B518 3363.0 "Twisted Vision 4" Ability { id: "BBE2", source: "Lindwurm" } 3369.6 "Clone 1 Heavy Slam?" Ability { id: "B519", source: "Lindschrat" } # Or B517 Mana Burst (Lindschrat) happens here @@ -474,8 +474,9 @@ hideall "--sync--" # Twisted Vision 5: Towers 3393.6 "Twisted Vision 5" Ability { id: "BBE2", source: "Lindwurm" } 3398.0 "Cosmic Kiss x8" Ability { id: "B4F4", source: "Lindwurm" } +3398.6 "--Hot-blooded x2--" duration 5 3398.7 "Lindwurm's Dark II x2" Ability { id: "B4F6", source: "Lindwurm" } -3400.7 "Pyretic Wurm x2" Ability { id: "B4F9", source: "Lindwurm" } +3398.7 "--Doom x2--" duration 8 3403.7 "Lindwurm's Stone III x2" #Ability { id: "B4F7", source: "Lindwurm" } 3408.7 "Lindwurm's Thunder II x4" #Ability { id: "B4FA", source: "Lindwurm" } 3408.7 "Lindwurm's Glare x4" #Ability { id: "B4F8", source: "Lindwurm" } @@ -483,39 +484,46 @@ hideall "--sync--" 3420.2 "--clone takes portal--" Ability { id: "B51D", source: "Lindschrat" } 3423.3 "--clones on platform--" Ability { id: "B4D9", source: "Lindschrat" } -# fflogs -3425.4 "Twisted Vision 6" Ability { id: "BBE2", source: "Lindwurm" } window 10,10 -3426.4 "Power Gusher" #Ability { id: "B50F", source: "Lindschrat" } -3426.4 "Snaking Kick" Ability { id: "BCAF", source: "Lindschrat" } -3431.5 "Reenactment 2" Ability { id: "B4EC", source: "Lindwurm" } -3433.5 "Heavy Slam" Ability { id: "B4EF", source: "Lindschrat" } -3434.7 "Mana Burst" Ability { id: "BBE3", source: "Lindwurm" } -3434.9 "Heavy Slam" #Ability { id: "BE5D", source: "Lindwurm" } -3440.5 "Twisted Vision 7" Ability { id: "BBE2", source: "Lindwurm" } -3445.1 "Snaking Kick" Ability { id: "BE95", source: "Lindwurm" } -3445.3 "Power Gusher" Ability { id: "B516", source: "Lindwurm" } -3448.2 "Twisted Vision 8" Ability { id: "BBE2", source: "Lindwurm" } -3454.5 "Heavy Slam" #Ability { id: "B4EF", source: "Lindschrat" } -3454.5 "--sync--" Ability { id: "B51E", source: "Lindschrat" } -3455.7 "Mana Burst" Ability { id: "BBE3", source: "Lindwurm" } -3455.9 "Heavy Slam" Ability { id: "BE5D", source: "Lindwurm" } -3459.5 "Power Gusher" Ability { id: "B51B", source: "Lindschrat" } -3460.6 "Power Gusher" Ability { id: "B516", source: "Lindwurm" } -3466.5 "Idyllic Dream" Ability { id: "B509", source: "Lindwurm" } -3474.6 "Double Sobat (castbar)" Ability { id: "B520", source: "Lindwurm" } -3475.2 "Double Sobat 1" Ability { id: ["B521", "B522", "B523", "B524"], source: "Lindwurm" } -3479.8 "Double Sobat 2" Ability { id: "B525", source: "Lindwurm" } -3482.2 "Esoteric Finisher" Ability { id: "B526", source: "Lindwurm" } +# Twisted Vision 6: Reenactment 2 Part 1 +# NOTE: Practical solution seems to be that you have x2 mana burts + x2 heavy slams +# This is because mana burts hits will knockback and heavy slams give Magic Vulns +# In theory you could do somehow get here and survive with: +# 3x Mana Burst + 1 Heavy Slam => 3x Heavy Slam + 1 Mana Burst or +# 3x Heavy Slam + 1 Mana Burst => 3x Mana Burst + 1 Heavy Slam +# But those would probably require lots of mit and tank immune probably on 2/3 of the heavy slams in the 3 set +3429.6 "Twisted Vision 6" Ability { id: "BBE2", source: "Lindwurm" } +3430.6 "Power Gusher" Ability { id: "B50F", source: "Lindschrat" } +3430.6 "Snaking Kick" Ability { id: "BCAF", source: "Lindschrat" } +3435.7 "Reenactment 2" Ability { id: "B4EC", source: "Lindwurm" } +3439.0 "Clone Mana Burst x2" Ability { id: "BBE3", source: "Lindwurm" } +3439.2 "Clone Heavy Slam x2" Ability { id: "BE5D", source: "Lindwurm" } + +# Twisted Vision 7: Safe Platform + Front/Back or Sides Platform +3444.8 "Twisted Vision 7" Ability { id: "BBE2", source: "Lindwurm" } +3449.2 "Snaking Kick" Ability { id: "BE95", source: "Lindwurm" } +3449.4 "Power Gusher" Ability { id: "B516", source: "Lindwurm" } + +# Twisted Vision 8: Reenactment 2 Part 2 +3452.3 "Twisted Vision 8" Ability { id: "BBE2", source: "Lindwurm" } +3458.6 "--sync--" Ability { id: "B51E", source: "Lindschrat" } +3459.8 "Clone Mana Burst x2" Ability { id: "BBE3", source: "Lindwurm" } +3460.0 "Clone Heavy Slam x2" Ability { id: "BE5D", source: "Lindwurm" } +3464.8 "Power Gusher" Ability { id: "B516", source: "Lindwurm" } +3470.7 "Idyllic Dream" Ability { id: "B509", source: "Lindwurm" } +3478.8 "Double Sobat (castbar)" Ability { id: "B520", source: "Lindwurm" } +3479.6 "Double Sobat 1" Ability { id: ["B521", "B522", "B523", "B524"], source: "Lindwurm" } +3484.2 "Double Sobat 2" Ability { id: "B525", source: "Lindwurm" } +3486.6 "Esoteric Finisher" Ability { id: "B526", source: "Lindwurm" } # Enrage Sequence -3496.0 "Replication 5" Ability { id: "B46C", source: "Lindwurm" } -3509.4 "Arcadian Hell 1" Ability { id: "B533", source: "Lindwurm" } -3509.4 "Arcadian Hell" #Ability { id: "B534", source: "Lindschrat" } -3525.6 "Arcadian Hell 2" Ability { id: "B533", source: "Lindwurm" } -3525.6 "Arcadian Hell" #Ability { id: "B535", source: "Lindschrat" } -3538.4 "--sync--" StartsUsing { id: "B537", source: "Lindwurm" } -3548.4 "Arcadian Hell (Enrage)" Ability { id: "B537", source: "Lindwurm" } -3548.4 "Arcadian Hell (Enrage)" #Ability { id: "BEC1", source: "Lindschrat" } +3499.8 "Replication 5" Ability { id: "B46C", source: "Lindwurm" } +3513.2 "Arcadian Hell 1 (boss) " Ability { id: "B533", source: "Lindwurm" } +3513.2 "Arcadian Hell 1 x4 (clones)" #Ability { id: "B534", source: "Lindschrat" } +3529.4 "Arcadian Hell 2 (boss)" Ability { id: "B533", source: "Lindwurm" } +3529.4 "Arcadian Hell 2 x8 (clones)" #Ability { id: "B535", source: "Lindschrat" } +3542.3 "--sync--" StartsUsing { id: "B537", source: "Lindwurm" } +3552.3 "Arcadian Hell (Boss Enrage)" Ability { id: "B537", source: "Lindwurm" } +3552.3 "Arcadian Hell x16 (Clones Enrage)" #Ability { id: "BEC1", source: "Lindschrat" } # IGNORED ABILITIES # Phase 1 @@ -559,6 +567,8 @@ hideall "--sync--" # B513 Power Gusher: VFX during Twisted Vision 3, related to the B50F # B514 Power Gusher: VFX during Twisted Vision 3, related to the B510 # B515 Snaking Kick: VFX during Twisted Vision 3 +# B4F9 Pyretic Wurm: Damage suffered when player moves while under affect of 12A0 Hot-blooded +# B51B Power Gusher: VFX during Twisted Vision 8, related to B516 # ALL ENCOUNTER ABILITIES # Phase 1 @@ -624,6 +634,7 @@ hideall "--sync--" # B4D6 Visceral Burst # B4D7 The Fixer # B4F5 Unmitigated Explosion +# B4F9 Pyretic Wurm # B538 Refreshing Overkill: Enrage castbar # B539 Refreshing Overkill: Non-enrage # B53A Refreshing Overkill: Enrage @@ -682,6 +693,7 @@ hideall "--sync--" # B4F2 Lindwurm's Meteor # B4F3 Downfall # B4F4 Cosmic Kiss +# B4F5 Unmitigated Explosion # B4F6 Lindwurm's Dark II # B4F7 Lindwurm's Stone III # B4F8 Lindwurm's Glare @@ -708,11 +720,12 @@ hideall "--sync--" # B513 Power Gusher # B514 Power Gusher # B515 Snaking Kick -# B516 Power Gusher +# B516 Power Gusher: Cast during Twisted Vision 7 and 8 # B517 Mana Burst: VFX during Twisted Vision 4, happens 1.2s before B518, useful for sync branch # B518 Mana Burst # B519 Heavy Slam # B51A Power Gusher +# B51B Power Gusher # B51C Temporal Curtain # B51D --sync-- # B51E --sync-- From 8af3e69aa2432d61179529b7bb1efe76aa447698 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 2 Feb 2026 20:01:35 -0500 Subject: [PATCH 132/139] remove extra space --- ui/raidboss/data/07-dt/raid/r12s.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.txt b/ui/raidboss/data/07-dt/raid/r12s.txt index a96f6b5a2d9..abc1d59339a 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.txt +++ b/ui/raidboss/data/07-dt/raid/r12s.txt @@ -511,7 +511,7 @@ hideall "--sync--" 3464.8 "Power Gusher" Ability { id: "B516", source: "Lindwurm" } 3470.7 "Idyllic Dream" Ability { id: "B509", source: "Lindwurm" } 3478.8 "Double Sobat (castbar)" Ability { id: "B520", source: "Lindwurm" } -3479.6 "Double Sobat 1" Ability { id: ["B521", "B522", "B523", "B524"], source: "Lindwurm" } +3479.6 "Double Sobat 1" Ability { id: ["B521", "B522", "B523", "B524"], source: "Lindwurm" } 3484.2 "Double Sobat 2" Ability { id: "B525", source: "Lindwurm" } 3486.6 "Esoteric Finisher" Ability { id: "B526", source: "Lindwurm" } From 90f4eb7d48d0ff62fc81f8ebe59cde29a7868115 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 2 Feb 2026 20:55:13 -0500 Subject: [PATCH 133/139] add more notes on replication 1 patterns --- ui/raidboss/data/07-dt/raid/r12s.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 7851bf1c20e..940bf437c4d 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1607,6 +1607,15 @@ const triggerSet: TriggerSet = { // (90, 110) (110, 110) // ActorMove ~0.3s later will have the data // ActorSet from the clones splitting we can infer the fire entities since their positions and headings are not perfect + // For first set there are two patterns that use these coordinates: + // (100, 86) + // (86, 100) (114, 100) + // (100, 114) + // Either N/S are clones casting Winged Scourge, or the E/W clones cast Winged Scourge + // Each pattern has its own pattern for IDs of the clones, in order + // N/S will have Fire -5 and -6 of its original + // E/W will have Fire -6 and -7 of its original + // Could use -6 to cover both cases, but that doesn't determine which add jumps first type: 'Ability', netRegex: { id: 'B4D9', source: 'Lindschrat', capture: true }, condition: (data, matches) => { From 24418ee245743783189f4fcdb4e1c088e2dc5315 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 2 Feb 2026 21:02:18 -0500 Subject: [PATCH 134/139] add clarity on modulus and heading check --- ui/raidboss/data/07-dt/raid/r12s.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 940bf437c4d..eaa7c973d22 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1623,7 +1623,8 @@ const triggerSet: TriggerSet = { const pos = data.actorPositions[matches.sourceId]; if (pos === undefined) return false; - // These values should be 0 if coords are x.0000 + // These values should be 0 when x or y coord has non-zero decimal values + // Heading is also checked as the non fire clones all face a perfect heading const xFilter = pos.x % 1; const yFilter = pos.y % 1; if (xFilter === 0 && yFilter === 0 && pos.heading === -0.0001) From 9bc5978c10004d6a1cd496077e1c6c4c414b8d85 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 2 Feb 2026 22:35:32 -0500 Subject: [PATCH 135/139] refactor replication2 Instead of a config, this just looks at the setup the players created and matches it to what the player needs to do. Additional triggers can now be added without config by checking the order in which the clones are setting off abilities. --- ui/raidboss/data/07-dt/raid/r12s.ts | 177 ++++++++++++++++++++-------- 1 file changed, 130 insertions(+), 47 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index eaa7c973d22..f8209acfc55 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -49,10 +49,14 @@ export interface Data extends RaidbossData { replicationCounter: number; replication1Debuff?: 'fire' | 'dark'; replication1FireActor?: string; + replication1FireActor2?: string; replication1FollowUp: boolean; - replication2TetherMap: { [dirNum: string]: string }; + replication2CloneDirNumPlayers: { [dirNum: number]: string }; + replication2DirNumAbility: { [dirNum: string]: string }; + replication2PlayerAbilities: { [player: string]: string }; replication2BossId?: string; - myReplication2Tether?: string; + replication2PlayerOrder: string[]; + replication2AbilityOrder: string[]; netherwrathFollowup: boolean; myMutation?: 'alpha' | 'beta'; manaSpheres: { @@ -167,7 +171,11 @@ const triggerSet: TriggerSet = { actorPositions: {}, replicationCounter: 0, replication1FollowUp: false, - replication2TetherMap: {}, + replication2CloneDirNumPlayers: {}, + replication2DirNumAbility: {}, + replication2PlayerAbilities: {}, + replication2PlayerOrder: [], + replication2AbilityOrder: [], netherwrathFollowup: false, manaSpheres: {}, westManaSpheres: {}, @@ -1770,7 +1778,22 @@ const triggerSet: TriggerSet = { }, }, { - id: 'R12S Replication 2 Tethered Clone', + id: 'R12S Staging 1 Tethered Clone Collect', + // Map the locations to a player name + type: 'Tether', + netRegex: { id: headMarkerData['lockedTether'], capture: true }, + condition: (data) => data.replicationCounter === 1, + run: (data, matches) => { + const actor = data.actorPositions[matches.sourceId]; + if (actor === undefined) + return; + + const dirNum = Directions.xyTo8DirNum(actor.x, actor.y, center.x, center.y); + data.replication2CloneDirNumPlayers[dirNum] = matches.target; + }, + }, + { + id: 'R12S Staging 1 Tethered Clone', // Combatants are added ~4s before Staging starts casting // Same tether ID is used for "locked" ability tethers type: 'Tether', @@ -1820,7 +1843,7 @@ const triggerSet: TriggerSet = { return; const dirNum = Directions.xyTo8DirNum(actor.x, actor.y, center.x, center.y); if (data.phase === 'replication2') - data.replication2TetherMap[dirNum] = matches.id; + data.replication2DirNumAbility[dirNum] = matches.id; if (data.phase === 'idyllic') data.replication4DirNumAbility[dirNum] = matches.id; }, @@ -1896,51 +1919,72 @@ const triggerSet: TriggerSet = { }, }, { - id: 'R12S Replication 2 Locked Tether 2 Collect', + id: 'R12S Replication 2 Locked Tether Collect', type: 'Tether', netRegex: { id: headMarkerData['lockedTether'], capture: true }, condition: (data, matches) => { if ( data.phase === 'replication2' && - data.replicationCounter === 2 && - data.me === matches.target + data.replicationCounter === 2 ) return true; return false; }, run: (data, matches) => { + const target = matches.target; + const sourceId = matches.sourceId; // Check if boss tether - if (data.replication2BossId === matches.sourceId) { - data.myReplication2Tether = headMarkerData['fireballSplashTether']; - return; - } + if (data.replication2BossId === sourceId) + data.replication2PlayerAbilities[target] = headMarkerData['fireballSplashTether']; + else if (data.replication2BossId !== sourceId) { + const actor = data.actorPositions[sourceId]; + if (actor === undefined) { + // Setting to use that we know we have a tether but couldn't determine what ability it is + data.replication2PlayerAbilities[target] = 'unknown'; + return; + } - const actor = data.actorPositions[matches.sourceId]; - if (actor === undefined) { - // Setting to use that we know we have a tether but couldn't determine what ability it is - data.myReplication2Tether = 'unknown'; - return; + const dirNum = Directions.xyTo8DirNum( + actor.x, + actor.y, + center.x, + center.y, + ); + + // Lookup what the tether was at the same location + const ability = data.replication2DirNumAbility[dirNum]; + if (ability === undefined) { + // Setting to use that we know we have a tether but couldn't determine what ability it is + data.replication2PlayerAbilities[target] = 'unknown'; + return; + } + data.replication2PlayerAbilities[target] = ability; } - const dirNum = Directions.xyTo8DirNum( - actor.x, - actor.y, - center.x, - center.y, - ); + // Create ability order once we have all 8 players + // If players had more than one tether previously, the extra tethers are randomly assigned + if (Object.keys(data.replication2PlayerAbilities).length === 7) { + // Fill in for player that had no tether, they are going to be boss' defamation + if (data.replication2PlayerAbilities[data.me] === undefined) + data.replication2PlayerAbilities[data.me] = 'none'; - // Lookup what the tether was at the same location - const ability = data.replication2TetherMap[dirNum]; - if (ability === undefined) { - // Setting to use that we know we have a tether but couldn't determine what ability it is - data.myReplication2Tether = 'unknown'; - return; + // Used for Twisted Vision 7 and 8 mechanics + const abilities = data.replication2PlayerAbilities; + const order = [0, 4, 1, 5, 2, 6, 3, 7]; // Order in which clones spawned, this is static + const players = data.replication2CloneDirNumPlayers; // Direction of player's clone + + // Mechanics are resolved clockwise + for (const dirNum of order) { + const player = players[dirNum] ?? 'unknown'; + const ability = abilities[player] ?? 'unknown'; + data.replication2PlayerOrder.push(player); + data.replication2AbilityOrder.push(ability); + } } - data.myReplication2Tether = ability; }, }, { - id: 'R12S Replication 2 Locked Tether 2', + id: 'R12S Replication 2 Locked Tether', type: 'Tether', netRegex: { id: headMarkerData['lockedTether'], capture: true }, condition: (data, matches) => { @@ -1954,16 +1998,18 @@ const triggerSet: TriggerSet = { }, delaySeconds: 0.1, infoText: (data, matches, output) => { + const sourceId = matches.sourceId; // Check if it's the boss - if (data.replication2BossId === matches.sourceId) + if (data.replication2BossId === sourceId) return output.fireballSplashTether!({ mech1: output.baitJump!(), }); // Get direction of the tether - const actor = data.actorPositions[matches.sourceId]; + const actor = data.actorPositions[sourceId]; + const ability = data.replication2PlayerAbilities[data.me]; if (actor === undefined) { - switch (data.myReplication2Tether) { + switch (ability) { case headMarkerData['projectionTether']: return output.projectionTether!({ mech1: output.baitProtean!(), @@ -1983,7 +2029,7 @@ const triggerSet: TriggerSet = { const dirNum = Directions.xyTo8DirNum(actor.x, actor.y, center.x, center.y); const dir = Directions.output8Dir[dirNum] ?? 'unknown'; - switch (data.myReplication2Tether) { + switch (ability) { case headMarkerData['projectionTether']: return output.projectionTetherDir!({ dir: output[dir]!(), @@ -2046,7 +2092,10 @@ const triggerSet: TriggerSet = { delaySeconds: 0.2, suppressSeconds: 1, infoText: (data, _matches, output) => { - if (data.myReplication2Tether !== undefined) + if ( + data.replication2PlayerAbilities[data.me] !== 'none' || + data.replication2PlayerAbilities[data.me] === undefined + ) return; return output.noTether!({ mech1: output.defamationOnYou!(), @@ -2076,7 +2125,7 @@ const triggerSet: TriggerSet = { netRegex: { id: 'B4E7', source: 'Lindwurm', capture: false }, suppressSeconds: 1, alertText: (data, _matches, output) => { - const ability = data.myReplication2Tether; + const ability = data.replication2PlayerAbilities[data.me]; switch (ability) { case headMarkerData['projectionTether']: return output.projectionTether!({ @@ -2175,7 +2224,7 @@ const triggerSet: TriggerSet = { type: 'StartsUsing', netRegex: { id: ['B52E', 'B52F'], source: 'Lindwurm', capture: true }, infoText: (data, matches, output) => { - const ability = data.myReplication2Tether; + const ability = data.replication2PlayerAbilities[data.me]; const isNear = matches.id === 'B52E'; if (isNear) { @@ -2331,7 +2380,24 @@ const triggerSet: TriggerSet = { // NOTE: This is used with DN Strategy type: 'Ability', netRegex: { id: 'BBE3', source: 'Lindwurm', capture: false }, - condition: (data) => data.netherwrathFollowup, + condition: (data) => { + if (data.netherwrathFollowup) { + const order = data.replication2AbilityOrder; + const stack = headMarkerData['heavySlamTether']; + const defamation = headMarkerData['manaBurstTether']; + const projection = headMarkerData['projectionTether']; + // Defined as east/west clones with stacks and SW/NE with defamation + projection + if ( + order[4] === stack && order[5] === stack && + ( + (order[2] === defamation && order[3] === projection) || + (order[2] === projection && order[3] === defamation) + ) + ) + return true; + } + return false; + }, suppressSeconds: 9999, alertText: (_data, _matches, output) => output.text!(), outputStrings: { @@ -2347,7 +2413,24 @@ const triggerSet: TriggerSet = { // NOTE: This is used with DN Strategy type: 'Ability', netRegex: { id: 'BE5D', source: 'Lindwurm', capture: false }, - condition: (data) => data.netherwrathFollowup, + condition: (data) => { + if (data.netherwrathFollowup) { + const order = data.replication2AbilityOrder; + const stack = headMarkerData['heavySlamTether']; + const defamation = headMarkerData['manaBurstTether']; + const projection = headMarkerData['projectionTether']; + // Defined as east/west clones with stacks and NW/SE with defamation + projection + if ( + order[4] === stack && order[5] === stack && + ( + (order[6] === defamation && order[7] === projection) || + (order[7] === projection && order[6] === defamation) + ) + ) + return true; + } + return false; + }, suppressSeconds: 9999, alertText: (_data, _matches, output) => output.north!(), outputStrings: { @@ -2687,7 +2770,7 @@ const triggerSet: TriggerSet = { response: Responses.bigAoe('alert'), }, { - id: 'R12S Idyllic Dream Replication Clone Order Collect', + id: 'R12S Idyllic Dream Staging 2 Clone Order Collect', type: 'ActorControlExtra', netRegex: { category: '0197', param1: '11D2', capture: true }, condition: (data) => { @@ -2704,7 +2787,7 @@ const triggerSet: TriggerSet = { }, }, { - id: 'R12S Idyllic Dream Replication First Clone Cardinal/Intercardinal', + id: 'R12S Idyllic Dream Staging 2 First Clone Cardinal/Intercardinal', type: 'ActorControlExtra', netRegex: { category: '0197', param1: '11D2', capture: true }, condition: (data) => { @@ -2807,14 +2890,14 @@ const triggerSet: TriggerSet = { case 'B510': { const y = parseFloat(matches.y); data.idyllicVision2NorthSouthCleaveSpot = y < center.y ? 'north' : 'south'; - data.idyllicDreamActorEW = matches.id; + data.idyllicDreamActorEW = matches.sourceId; return; } case 'B511': - data.idyllicDreamActorSnaking = matches.id; + data.idyllicDreamActorSnaking = matches.sourceId; return; case 'B50F': - data.idyllicDreamActorNS = matches.id; + data.idyllicDreamActorNS = matches.sourceId; return; } }, @@ -2880,7 +2963,7 @@ const triggerSet: TriggerSet = { }, }, { - id: 'R12S Replication 4 Locked Tether 2 Collect', + id: 'R12S Replication 4 Locked Tether Collect', type: 'Tether', netRegex: { id: headMarkerData['lockedTether'], capture: true }, condition: (data) => { @@ -2943,7 +3026,7 @@ const triggerSet: TriggerSet = { }, }, { - id: 'R12S Replication 4 Locked Tether 2', + id: 'R12S Replication 4 Locked Tether', // At this point the player needs to dodge the north/south cleaves + chariot // Simultaneously there will be a B4F2 Lindwurm's Meteor bigAoe that ends with room split type: 'Tether', From 8643e49b934148b492243731a21eee2769d54acb Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 2 Feb 2026 22:43:09 -0500 Subject: [PATCH 136/139] adjust second snaking kick, use grotesquerie timing --- ui/raidboss/data/07-dt/raid/r12s.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index f8209acfc55..bad1d73191c 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1563,16 +1563,11 @@ const triggerSet: TriggerSet = { { id: 'R12S Snaking Kick', // Targets random player + // Second cast of this happens before Grotesquerie, delay until Grotesquerie to reduce chance of none projection players running into it type: 'StartsUsing', netRegex: { id: 'B527', source: 'Lindwurm', capture: true }, - condition: (data) => { - // Use Grotesquerie trigger for projection tethered players - const ability = data.myReplication2Tether; - if (ability === headMarkerData['projectionTether']) - return false; - return true; - }, delaySeconds: 0.1, // Need to delay for actor position update + suppressSeconds: 9999, alertText: (data, matches, output) => { const actor = data.actorPositions[matches.sourceId]; if (actor === undefined) @@ -2192,7 +2187,7 @@ const triggerSet: TriggerSet = { // B4EB Hemorrhagic Projection conal aoe goes off ~0.5s after in the direction the player was facing type: 'Ability', netRegex: { id: 'B4EA', source: 'Lindwurm', capture: true }, - condition: Conditions.targetIsYou(), + suppressSeconds: 9999, alertText: (data, _matches, output) => { // Get Boss facing const bossId = data.replication2BossId; From cf24c133c38eddfa8966ed727bc035937e5c71bc Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 2 Feb 2026 23:13:06 -0500 Subject: [PATCH 137/139] fixes and add config for Towers portent resolution --- ui/raidboss/data/07-dt/raid/r12s.ts | 115 +++++++++++++++++++++++++--- 1 file changed, 103 insertions(+), 12 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index bad1d73191c..5abc09e34f1 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -28,6 +28,7 @@ type DirectionIntercard = Exclude; export interface Data extends RaidbossData { readonly triggerSetConfig: { uptimeKnockbackStrat: true | false; + portentStrategy: 'dn' | 'zenith' | 'none'; }; phase: Phase; // Phase 1 @@ -156,6 +157,21 @@ const triggerSet: TriggerSet = { type: 'checkbox', default: false, }, + { + id: 'portentStrategy', + name: { + en: 'Phase 2 Tower Portent Resolution Strategy', + }, + type: 'select', + options: { + en: { + 'DN Strategy: Dark N Hitbox, Wind Middle Hitbox, Earth/Fire N/S Max Melee': 'dn', + 'Zenith Strategy: Wind N Max Melee, Earth/Dark Middle (Lean North), Fire S Max Melee': 'zenith', + 'No strategy: call element and debuff': 'none', + }, + }, + default: 'none', + }, ], timelineFile: 'r12s.txt', initData: () => ({ @@ -2186,7 +2202,7 @@ const triggerSet: TriggerSet = { // B4EA has the targetted player in it // B4EB Hemorrhagic Projection conal aoe goes off ~0.5s after in the direction the player was facing type: 'Ability', - netRegex: { id: 'B4EA', source: 'Lindwurm', capture: true }, + netRegex: { id: 'B4EA', source: 'Lindwurm', capture: false }, suppressSeconds: 9999, alertText: (data, _matches, output) => { // Get Boss facing @@ -2389,7 +2405,7 @@ const triggerSet: TriggerSet = { (order[2] === projection && order[3] === defamation) ) ) - return true; + return true; } return false; }, @@ -2422,7 +2438,7 @@ const triggerSet: TriggerSet = { (order[7] === projection && order[6] === defamation) ) ) - return true; + return true; } return false; }, @@ -3543,27 +3559,77 @@ const triggerSet: TriggerSet = { delaySeconds: (_data, matches) => parseFloat(matches.duration) - 5.3, infoText: (data, matches, output) => { if (matches.id === '129E') { - if (data.hasDoom) + if (data.hasDoom) { + switch (data.triggerSetConfig.portentStrategy) { + case 'dn': + return output.farOnYouDarkDN!(); + case 'zenith': + return output.farOnYouDarkZenith!(); + } return output.farOnYouDark!(); + } + switch (data.triggerSetConfig.portentStrategy) { + case 'dn': + return output.farOnYouWindDN!(); + case 'zenith': + return output.farOnYouWindZenith!(); + } return output.farOnYouWind!(); } - if (data.hasDoom) + if (data.hasDoom) { + switch (data.triggerSetConfig.portentStrategy) { + case 'dn': + return output.nearOnYouDarkDN!(); + case 'zenith': + return output.nearOnYouDarkZenith!(); + } return output.nearOnYouDark!(); + } + switch (data.triggerSetConfig.portentStrategy) { + case 'dn': + return output.nearOnYouWindDN!(); + case 'zenith': + return output.nearOnYouWindZenith!(); + } return output.nearOnYouWind!(); }, outputStrings: { - nearOnYouWind: { + nearOnYouWindDN: { en: 'Near on YOU: Be on Middle Hitbox', }, - nearOnYouDark: { + nearOnYouDarkDN: { en: 'Near on YOU: Be on Hitbox N', }, - farOnYouWind: { + farOnYouWindDN: { en: 'Far on YOU: Be on Middle Hitbox', }, - farOnYouDark: { + farOnYouDarkDN: { en: 'Far on YOU: Be on Hitbox N', }, + nearOnYouWindZenith: { + en: 'Near on YOU: Max Melee N', + }, + nearOnYouDarkZenith: { + en: 'Near on YOU: Be on Middle Hitbox (Lean North)', + }, + farOnYouWindZenith: { + en: 'Far on YOU: Max Melee N', + }, + farOnYouDarkZenith: { + en: 'Far on YOU: Be on Middle Hitbox (Lean North)', + }, + nearOnYouWind: { + en: 'Wind: Near on YOU', + }, + nearOnYouDark: { + en: 'Dark: Near on YOU', + }, + farOnYouWind: { + en: 'Wind: Far on YOU', + }, + farOnYouDark: { + en: 'Dark: Far on YOU', + }, }, }, { @@ -3575,16 +3641,41 @@ const triggerSet: TriggerSet = { delaySeconds: (_data, matches) => parseFloat(matches.duration) - 5.3, suppressSeconds: 1, infoText: (data, _matches, output) => { - if (data.hasPyretic) + if (data.hasPyretic) { + switch (data.triggerSetConfig.portentStrategy) { + case 'dn': + return output.baitFireDN!(); + case 'zenith': + return output.baitFireZenith!(); + } return output.baitFire!(); + } + switch (data.triggerSetConfig.portentStrategy) { + case 'dn': + return output.baitEarthDN!(); + case 'zenith': + return output.baitEarthZenith!(); + } return output.baitEarth!(); }, outputStrings: { + baitFireDN: { + en: 'Bait Cone N/S Max Melee', + }, + baitEarthDN: { + en: 'Bait Cone N/S Max Melee', + }, + baitFireZenith: { + en: 'Bait Cone S, Max Melee', + }, + baitEarthZenith: { + en: 'Bait Cone Middle, Max Melee (Lean North)', + }, baitFire: { - en: 'Bait Cone N/S', + en: 'Fire: Bait Cone', }, baitEarth: { - en: 'Bait Cone N/S', + en: 'Earth: Bait Cone', }, }, }, From b07e8600ae1a14af763dc708cc6d8c4b90e29d9a Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 2 Feb 2026 23:16:40 -0500 Subject: [PATCH 138/139] lint --- ui/raidboss/data/07-dt/raid/r12s.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 5abc09e34f1..105151052d2 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -166,7 +166,8 @@ const triggerSet: TriggerSet = { options: { en: { 'DN Strategy: Dark N Hitbox, Wind Middle Hitbox, Earth/Fire N/S Max Melee': 'dn', - 'Zenith Strategy: Wind N Max Melee, Earth/Dark Middle (Lean North), Fire S Max Melee': 'zenith', + 'Zenith Strategy: Wind N Max Melee, Earth/Dark Middle (Lean North), Fire S Max Melee': + 'zenith', 'No strategy: call element and debuff': 'none', }, }, @@ -3577,7 +3578,7 @@ const triggerSet: TriggerSet = { return output.farOnYouWind!(); } if (data.hasDoom) { - switch (data.triggerSetConfig.portentStrategy) { + switch (data.triggerSetConfig.portentStrategy) { case 'dn': return output.nearOnYouDarkDN!(); case 'zenith': From eda9360a47889847e5bd008d53ab90327dab9b86 Mon Sep 17 00:00:00 2001 From: Legends0 Date: Mon, 2 Feb 2026 23:24:11 -0500 Subject: [PATCH 139/139] remove unused matches --- ui/raidboss/data/07-dt/raid/r12s.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/raidboss/data/07-dt/raid/r12s.ts b/ui/raidboss/data/07-dt/raid/r12s.ts index 105151052d2..84b26c61271 100644 --- a/ui/raidboss/data/07-dt/raid/r12s.ts +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -1934,7 +1934,7 @@ const triggerSet: TriggerSet = { id: 'R12S Replication 2 Locked Tether Collect', type: 'Tether', netRegex: { id: headMarkerData['lockedTether'], capture: true }, - condition: (data, matches) => { + condition: (data) => { if ( data.phase === 'replication2' && data.replicationCounter === 2