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 0000000000..e35b0e3cae --- /dev/null +++ b/ui/raidboss/data/07-dt/raid/r12s.ts @@ -0,0 +1,3092 @@ +import Conditions from '../../../../../resources/conditions'; +import { UnreachableCode } from '../../../../../resources/not_reached'; +import Outputs from '../../../../../resources/outputs'; +import { Responses } from '../../../../../resources/responses'; +import { + DirectionOutput8, + DirectionOutputCardinal, + DirectionOutputIntercard, + 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' + | 'replication1' + | 'replication2' + | 'reenactment1' + | 'idyllic' + | 'reenactment2'; + +type DirectionCardinal = Exclude; +type DirectionIntercard = Exclude; + +export interface Data extends RaidbossData { + readonly triggerSetConfig: { + uptimeKnockbackStrat: true | false; + }; + phase: Phase; + // Phase 1 + grotesquerieCleave?: + | 'rightCleave' + | 'leftCleave' + | 'frontCleave' + | 'rearCleave'; + myFleshBonds?: 'alpha' | 'beta'; + inLine: { [name: string]: number }; + blobTowerDirs: string[]; + fleshBondsCount: number; + skinsplitterCount: number; + cellChainCount: number; + myMitoticPhase?: string; + 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; + 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'; + twistedVisionCounter: number; + replication3CloneOrder: number[]; + idyllicVision2NorthSouthCleaveSpot?: 'north' | 'south'; + replication4TetherMap: { [dirNum: string]: string }; + myReplication4Tether?: string; + hasLightResistanceDown: boolean; + doomPlayers: string[]; +} + +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 + // 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 = { + x: 100, + y: 100, +} as const; + +const phaseMap: { [id: string]: Phase } = { + 'BEC0': 'curtainCall', + 'B4C6': 'slaughtershed', + '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, + 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.8s 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', + // Phase 1 + inLine: {}, + blobTowerDirs: [], + skinsplitterCount: 0, + fleshBondsCount: 0, + cellChainCount: 0, + hasRot: false, + // Phase 2 + actorPositions: {}, + replicationCounter: 0, + replication1FollowUp: false, + replication2TetherMap: {}, + netherwrathFollowup: false, + manaSpheres: {}, + westManaSpheres: {}, + eastManaSpheres: {}, + closeManaSphereIds: [], + twistedVisionCounter: 0, + replication3CloneOrder: [], + replication4TetherMap: {}, + hasLightResistanceDown: false, + doomPlayers: [], + }), + 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 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', + netRegex: { id: 'B4D8', source: 'Lindwurm', capture: false }, + run: (data) => { + if (data.replicationCounter === 0) + data.phase = 'replication1'; + data.replicationCounter = data.replicationCounter + 1; + }, + }, + { + 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 === 'replication2', + suppressSeconds: 9999, + run: (data, matches) => 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 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', + 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 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', + 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 }, + 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(dir); + + 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) => data.myFleshBonds === 'alpha', + delaySeconds: 0.1, + durationSeconds: (data) => { + const myNum = data.inLine[data.me]; + // Timings based on next trigger + switch (myNum) { + case 1: + return 17; + case 2: + return 22; + case 3: + return 18; + case 4: + return 18; + } + }, + suppressSeconds: 10, + 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 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.drawIn(), + }, + { + 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', + 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) { + 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 }); + }, + outputStrings: { + tether: { + en: 'Tether ${num}', + de: 'Verbindung ${num}', + fr: 'Lien ${num}', + ja: '線 ${num}', + cn: '线 ${num}', + 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', + }, + }, + }, + { + 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 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); + // The following gives 5s warning to take tower + if (duration > 37) + return 31; // Alpha4 Time + return 26; // Alpha3 Time + }, + alertText: (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: 'Get Blob Tower 1 (Inner ${dir})', + }, + alpha4Dir: { + en: 'Get 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; + }, + alertText: (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 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', + // 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', + 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: true }, + 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 Cleave', + }, + getHitEast: { + en: 'Spread in East Cleave', + }, + safeEast: { + en: 'Spread East + Avoid Cleave', + }, + safeWest: { + en: 'Spread West + Avoid Cleave', + }, + }, + }, + { + 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', '1E001'], capture: false }, + durationSeconds: 9, + suppressSeconds: 9999, + 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', + // All players, including dead, receive α debuffs + // TODO: Find safe spots + type: 'GainsEffect', + netRegex: { effectId: '1291', capture: true }, + condition: (data, matches) => { + if (matches.target === data.me && data.phase === 'curtainCall') + return true; + return false; + }, + infoText: (_data, _matches, output) => { + return output.alphaChains!({ + chains: output.breakChains!(), + safe: output.safeSpots!(), + }); + }, + outputStrings: { + breakChains: Outputs.breakChains, + safeSpots: { + en: 'Avoid Blobs', + }, + alphaChains: { + en: '${chains} => ${safe}', + }, + }, + }, + { + 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: 15, + infoText: (_data, _matches, output) => output.text!(), + outputStrings: { + text: { + en: 'Knockback from Northwest => Knockback from 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: 15, + infoText: (_data, _matches, output) => output.text!(), + outputStrings: { + text: { + en: 'Knockback from Northeast => Knockback from Northwest', + }, + }, + }, + { + id: 'R12S Raptor Knuckles Uptime Knockback', + // 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 (surecast ends ~0.134 after hit) + type: 'Ability', + netRegex: { id: ['B4CC', 'B4CE'], source: 'Lindwurm', capture: false }, + condition: (data) => { + if (data.phase === 'slaughtershed' && data.triggerSetConfig.uptimeKnockbackStrat) + return true; + return false; + }, + delaySeconds: 11.5, + durationSeconds: 1.8, + response: Responses.knockback(), + }, + { + 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 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 + // 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 (later)', + }, + dark: { + en: 'Dark Debuff: Stack near Fire (later)', + }, + }, + }, + { + 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: 1.2, // +0.1s 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 (later)', + }, + }, + }, + { + id: 'R12S Snaking Kick', + // Targets random player + 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 + 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', + // Tracking from B527 Snaking Kick + type: 'Ability', + 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 In ${dir1}/Out ${dir2} (Partners)', + }, + dark: { + en: 'Bait Dark In ${dir1}/Out ${dir2} (Solo)', + }, + }, + }, + { + id: 'R12S Double Sobat', + // 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: + // 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', + 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); + 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(); + }; + + // Adding 16 incase of negative values + const newDirNum = (getNewDirNum(dirNum, matches.id) + 16 + 8) % 16; + + const dir = Directions.output16Dir[newDirNum] ?? '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 + // 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 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', + netRegex: { + id: [ + headMarkerData['projectionTether'], + headMarkerData['manaBurstTether'], + headMarkerData['heavySlamTether'], + ], + capture: true, + }, + 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); + if (data.phase === 'replication2') + data.replication2TetherMap[dirNum] = matches.id; + if (data.phase === 'idyllic') + data.replication4TetherMap[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) => { + 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.projectionTetherDir!({ dir: output[dir]!() }); + case headMarkerData['manaBurstTether']: + return output.manaBurstTetherDir!({ dir: output[dir]!() }); + case headMarkerData['heavySlamTether']: + return output.heavySlamTetherDir!({ dir: output[dir]!() }); + } + }, + 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', + }, + }, + }, + { + id: 'R12S Replication 2 Locked Tether 2 Collect', + type: 'Tether', + netRegex: { id: headMarkerData['lockedTether'], capture: true }, + condition: (data, matches) => { + if ( + data.phase === 'replication2' && + data.replicationCounter === 2 && + data.me === matches.target + ) + return true; + return false; + }, + 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) => { + if ( + data.phase === 'replication2' && + data.replicationCounter === 2 && + data.me === matches.target + ) + return true; + return false; + }, + delaySeconds: 0.1, + infoText: (data, matches, output) => { + // Check if it's the boss + if (data.replication2BossId === matches.sourceId) + return output.fireballSplashTether!({ + 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.projectionTetherDir!({ + dir: output[dir]!(), + mech1: output.baitProtean!(), + }); + case headMarkerData['manaBurstTether']: + return output.manaBurstTetherDir!({ + dir: output[dir]!(), + mech1: output.defamationOnYou!(), + }); + case headMarkerData['heavySlamTether']: + return output.heavySlamTetherDir!({ + dir: output[dir]!(), + mech1: output.baitProtean!(), + }); + } + }, + outputStrings: { + ...Directions.outputStrings8Dir, + defamationOnYou: Outputs.defamationOnYou, + baitProtean: { + en: 'Bait Protean from Boss', + }, + 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}', + }, + fireballSplashTether: { + en: 'Boss Tether: ${mech1}', + }, + }, + }, + { + 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) => { + if (data.phase === 'replication2' && data.replicationCounter === 2) + return true; + return false; + }, + delaySeconds: 0.2, + 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: 'No Tether: ${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(), + 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', + // 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) => { + const ability = data.myReplication2Tether; + const isNear = matches.id === 'B52E'; + + 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.projectionTetherFar!({ + proteanBaits: output.beNear!(), + mech1: output.scaldingWave!(), + mech2: output.stacks!(), + spiteBaits: output.far!(), + }); + case headMarkerData['manaBurstTether']: + return output.manaBurstTetherFar!({ + spiteBaits: output.beFar!(), + mech1: output.timelessSpite!(), + mech2: output.proteans!(), + proteanBaits: output.near!(), + }); + case headMarkerData['heavySlamTether']: + return output.heavySlamTetherFar!({ + proteanBaits: output.beNear!(), + mech1: output.scaldingWave!(), + mech2: output.stacks!(), + spiteBaits: output.far!(), + }); + case headMarkerData['fireballSplashTether']: + return output.fireballSplashTetherFar!({ + spiteBaits: output.beFar!(), + mech1: output.timelessSpite!(), + mech2: output.proteans!(), + proteanBaits: output.near!(), + }); + } + return output.noTetherFar!({ + spiteBaits: output.beFar!(), + mech1: output.timelessSpite!(), + mech2: output.proteans!(), + proteanBaits: output.near!(), + }); + }, + outputStrings: { + scaldingWave: Outputs.protean, + timelessSpite: Outputs.stackPartner, + stacks: Outputs.stacks, + proteans: { + en: 'Proteans', + }, + beNear: { + en: 'Be Near', + }, + beFar: { + en: 'Be Far', + }, + near: { + en: 'Near', + de: 'Nah', + fr: 'Proche', + cn: '近', + ko: '가까이', + }, + far: { + en: 'Far', + de: 'Fern', + fr: 'Loin', + cn: '远', + ko: '멀리', + }, + projectionTetherFar: { + en: '${proteanBaits} + ${mech1} (${mech2} ${spiteBaits})', + }, + manaBurstTetherFar: { + en: '${spiteBaits} + ${mech1} (${mech2} ${proteanBaits})', + }, + heavySlamTetherFar: { + en: '${proteanBaits} + ${mech1} (${mech2} ${spiteBaits})', + }, + fireballSplashTetherFar: { + en: '${spiteBaits} + ${mech1} (${mech2} ${proteanBaits})', + }, + 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})', + }, + }, + }, + { + 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 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 + 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 + // 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 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 + 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; + 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.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}', + }, + }, + }, + { + 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 + // B501 Lindwurm's Water III + // B502 Lindwurm's Aero III + // B503 Straightforward Thunder II + // B504 Sideways Fire II + type: 'Ability', + netRegex: { id: ['B501', 'B502', 'B503', 'B504'], source: 'Lindwurm', capture: false }, + suppressSeconds: 9999, + 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}', + }, + }, + }, + { + 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}', + }, + }, + }, + { + id: 'R12S Idyllic Dream', + type: 'StartsUsing', + netRegex: { id: 'B509', source: 'Lindwurm', capture: false }, + 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; + + 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', + }, + }, + }, + { + 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: 'B510', source: 'Lindschrat', capture: true }, + run: (data, matches) => { + const y = parseFloat(matches.y); + 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', + 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: { + ...Directions.outputStrings8Dir, + north: Outputs.north, + south: Outputs.south, + sides: Outputs.sides, + bigAoe: Outputs.bigAoe, + 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(), + 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 Cleanse', + type: 'GainsEffect', + netRegex: { effectId: 'D24', capture: false }, + 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: [ + { + 'locale': 'en', + 'replaceText': { + 'Netherwrath Near/Netherwrath Far': 'Netherwrath Near/Far', + 'Netherworld Near/Netherwworld Far': 'Netherworld Near/Far', + }, + }, + ], +}; + +export default triggerSet; diff --git a/ui/raidboss/data/07-dt/raid/r12s.txt b/ui/raidboss/data/07-dt/raid/r12s.txt new file mode 100644 index 0000000000..fa2c8f03f7 --- /dev/null +++ b/ui/raidboss/data/07-dt/raid/r12s.txt @@ -0,0 +1,723 @@ +### AAC HEAVYWEIGHT M4 (SAVAGE) +# ZoneId: 1327 + +hideall "--Reset--" +hideall "--sync--" + +0.0 "--Reset--" ActorControl { command: "4000000F" } window 0,100000 jump 0 + +### Phase 1: Lindwurm +# -ii B4D3 B495 B4B2 B4B4 B4B3 B4BD B4BE B53E B4B5 B4B1 BE0A B570 B56F B4AD B76A B469 B769 +# -it "Lindwurm" + +0.0 "--sync--" InCombat { inGameCombat: "1" } window 0,1 +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" } +43.9 "Mortal Slayer 2" Ability { id: ["B496", "B498"], source: "Lindwurm" } +46.9 "Mortal Slayer 3" Ability { id: ["B496", "B498"], source: "Lindwurm" } +49.9 "Mortal Slayer 4" Ability { id: ["B496", "B498"], source: "Lindwurm" } +52.9 "--sync--" Ability { id: "B7C5", source: "Lindwurm" } +61.0 "--sync--" Ability { id: "B9DB", source: "Lindwurm" } + +70.0 "Grotesquerie: Act 1" Ability { id: "BEBD", source: "Lindwurm" } window 70,5 +77.2 "--sync--" Ability { id: ["B49A", "B49B"], source: "Lindwurm" } +79.2 "Phagocyte Spotlight 1" #Ability { id: "B49E", source: "Lindwurm" } +81.2 "Phagocyte Spotlight 2" #Ability { id: "B49E", source: "Lindwurm" } +83.2 "Phagocyte Spotlight 3" #Ability { id: "B49E", source: "Lindwurm" } +85.2 "Phagocyte Spotlight 4" #Ability { id: "B49E", source: "Lindwurm" } +87.3 "--sync--" #Ability { id: "B46E", source: "Lindwurm" } +87.7 "Ravenous Reach" Ability { id: "B49D", source: "Lindwurm" } +88.0 "Hemorrhagic Projection x8" #Ability { id: "B4AF", source: "Lindwurm" } +88.0 "Dramatic Lysis x4" #Ability { id: "B4AA", source: "Lindwurm" } +88.0 "Fourth-wall Fusion" Ability { id: "B4AE", source: "Lindwurm" } +96.7 "Burst" Ability { id: "B49F", source: "Lindwurm" } +97.3 "Visceral Burst x2" #Ability { id: "B4D6", source: "Lindwurm" } +97.3 "Fourth-wall Fusion" Ability { id: "B9B9", source: "Lindwurm" } +107.3 "The Fixer" Ability { id: "B4D7", source: "Lindwurm" } + +119.5 "Grotesquerie: Act 2" Ability { id: "BEBE", source: "Lindwurm" } window 120,5 +128.7 "Phagocyte Spotlight 1" #Ability { id: "B4B6", source: "Lindwurm" } +130.7 "Phagocyte Spotlight 2" #Ability { id: "B4B6", source: "Lindwurm" } +132.7 "Phagocyte Spotlight 3" #Ability { id: "B4B6", source: "Lindwurm" } +134.7 "Cruel Coil" Ability { id: ["B4B9", "B4B8"], source: "Lindwurm" } +134.7 "Phagocyte Spotlight 4" #Ability { id: "B4B6", source: "Lindwurm" } +135.7 "--bind--" Ability { id: "B472", source: "Lindwurm" } duration 5 +140.7 "Skinsplitter 1" Ability { id: "B4BC", source: "Lindwurm" } +143.7 "--sync--" Ability { id: "B4C1", source: "Lindwurm" } +145.7 "Skinsplitter 2" Ability { id: "B4BC", source: "Lindwurm" } +145.7 "--tether 1--" +150.7 "Skinsplitter 3" Ability { id: "B4BC", source: "Lindwurm" } +150.7 "--tether 2--" +152.7 "Roiling Mass 1" Ability { id: "B4B7", source: "Blood Vessel" } +155.7 "Skinsplitter 4" Ability { id: "B4BC", source: "Lindwurm" } +155.7 "--tether 3--" +157.7 "Roiling Mass 2" Ability { id: "B4B7", source: "Blood Vessel" } +160.7 "Skinsplitter 5" Ability { id: "B4BC", source: "Lindwurm" } +160.7 "--tether 4--" +162.7 "Roiling Mass 3" Ability { id: "B4B7", source: "Blood Vessel" } +165.7 "Skinsplitter 6" Ability { id: "B4BC", source: "Lindwurm" } +167.7 "Roiling Mass 4" Ability { id: "B4B7", source: "Blood Vessel" } +170.7 "Skinsplitter 7" Ability { id: "B4BC", source: "Lindwurm" } +176.7 "Constrictor" Ability { id: "B4C2", source: "Lindwurm" } +185.8 "Splattershed (castbar)" Ability { id: "B9C4", source: "Lindwurm" } +188.2 "Splattershed" Ability { id: "B9C6", source: "Lindwurm" } +189.9 "--sync--" Ability { id: "B4CB", source: "Lindwurm" } + +205.9 "Grotesquerie: Act 3" Ability { id: "BEBF", source: "Lindwurm" } window 205,5 +209.0 "--untargetable--" +211.0 "Feral Fission" Ability { id: "BE09", source: "Lindwurm" } +212.0 "Grand Entrance" #Ability { id: ["B4A1", "B4A0"], source: "Lindwurm" } +212.3 "Grand Entrance" #Ability { id: ["B4A1", "B4A2"], source: "Lindwurm" } +212.4 "Grand Entrance" #Ability { id: "B4A3", source: "Lindwurm" } +212.5 "Grand Entrance" Ability { id: "B4A0", source: "Lindwurm" } +212.6 "Grand Entrance" #Ability { id: "B4A3", source: "Lindwurm" } +212.7 "Grand Entrance" #Ability { id: ["B4A1", "B4A2"], source: "Lindwurm" } +212.8 "Grand Entrance" #Ability { id: "B4A3", source: "Lindwurm" } +212.8 "Grand Entrance" #Ability { id: ["B4A1", "B4A2"], source: "Lindwurm" } +213.0 "Grand Entrance" #Ability { id: "B4A3", source: "Lindwurm" } +213.2 "Grand Entrance" #Ability { id: "B4A3", source: "Lindwurm" } +215.6 "Bring Down the House" Ability { id: ["B4A5", "B4A4"], source: "Lindwurm" } +218.0 "Dramatic Lysis x8" #Ability { id: "B4B0", source: "Lindwurm" } +218.2 "Metamitosis x8" Ability { id: "B923", source: "Lindwurm" } +220.6 "--targetable--" +226.9 "Split Scourge (cast)" Ability { id: "B4A7", source: "Lindwurm" } +228.0 "Split Scourge" Ability { id: "B4AB", source: "Lindwurm" } +230.4 "Venomous Scourge" Ability { id: "B4A8", source: "Lindwurm" } +231.5 "--sync--" Ability { id: "B479", source: "Lindwurm" } +239.6 "The Fixer" Ability { id: "B4D7", source: "Lindwurm" } + +251.7 "Grotesquerie: Curtain Call" Ability { id: "BEC0", source: "Lindwurm" } window 251,5 +255.7 "Phagocyte Spotlight 1" #Ability { id: "B49E", source: "Lindwurm" } +255.8 "--sync--" Ability { id: ["B49A", "B49B"], source: "Lindwurm" } +257.6 "Phagocyte Spotlight 2" #Ability { id: "B49E", source: "Lindwurm" } +259.6 "Phagocyte Spotlight 3" #Ability { id: "B49E", source: "Lindwurm" } +261.6 "Phagocyte Spotlight 4" #Ability { id: "B49E", source: "Lindwurm" } +263.6 "Phagocyte Spotlight 5" #Ability { id: "B49E", source: "Lindwurm" } +265.9 "--sync--" Ability { id: "B46E", source: "Lindwurm" } +266.5 "Ravenous Reach" Ability { id: "B49D", source: "Lindwurm" } +266.6 "Cell Shedding x4" #Ability { id: "B4AC", source: "Lindwurm" } +266.8 "Dramatic Lysis x4" #Ability { id: "B4AA", source: "Lindwurm" } +279.4 "Burst" Ability { id: "B49F", source: "Lindwurm" } +286.1 "Splattershed (castbar)" Ability { id: "B9C3", source: "Lindwurm" } +288.5 "Splattershed" Ability { id: "B9C6", source: "Lindwurm" } +290.2 "--sync--" Ability { id: "B4CC", source: "Lindwurm" } +299.3 "--sync--" Ability { id: "B7C4", source: "Lindwurm" } +314.4 "Mortal Slayer 1" Ability { id: ["B496", "B498"], source: "Lindwurm" } +317.4 "Mortal Slayer 2" Ability { id: ["B496", "B498"], source: "Lindwurm" } +320.4 "Mortal Slayer 3" Ability { id: ["B496", "B498"], source: "Lindwurm" } +323.4 "Mortal Slayer 4" Ability { id: ["B496", "B498"], source: "Lindwurm" } +326.4 "--sync--" Ability { id: "B7C5", source: "Lindwurm" } + +# Slaughershed 1 +338.5 "Slaughtershed 1 (castbar)" Ability { id: ["B4C3", "B4C6"], source: "Lindwurm" } window 338,5 +340.9 "Slaughtershed 1" Ability { id: "ADC9", source: "Lindwurm" } +342.6 "--sync--" Ability { id: "B4CB", source: "Lindwurm" } jump "r12s-p1-scourge-left-1" +342.6 "--sync--" Ability { id: "B4CD", source: "Lindwurm" } jump "r12s-p1-scourge-right-1" +342.6 "--sync--" Ability { id: "B4CE", source: "Lindwurm" } jump "r12s-p1-raptor-left-1" +342.6 "--sync--" Ability { id: "B4CC", source: "Lindwurm" } jump "r12s-p1-raptor-right-1" +349.6 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } +349.6 "Fourth-wall Fusion" #Ability { id: "B4D5", source: "Lindwurm" } +350.0 "Burst" #Ability { id: "B49F", source: "Lindwurm" } +355.1 "Serpentine Scourge/Raptor Knuckles?" #Ability { id: ["B4D1", "B4D2", "B4CD", "B4CE"], source: "Lindwurm" } +356.1 "Serpentine Scourge/--knockback--?" #Ability { id: ["B9BC", "B9C7"], source: "Lindwurm" } +359.8 "Serpentine Scourge/Raptor Knuckles?" #Ability { id: ["B4D2", "B4D1", "B4CE", "B4CD"], source: "Lindwurm" } +360.8 "Serpentine Scourge/--knockback--?" #Ability { id: ["B9BC", "B9C7"], source: "Lindwurm" } +367.4 "Slaughtershed 2 (castbar)" #Ability { id: ["B4C3", "B4C6"], source: "Lindwurm" } +369.8 "Slaughtershed 2" #Ability { id: "ADC9", source: "Lindwurm" } + +# Serpentine Scourge Left First +442.6 label "r12s-p1-scourge-left-1" +449.6 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } +449.6 "Fourth-wall Fusion" Ability { id: "B4D5", source: "Lindwurm" } +450.0 "Burst" Ability { id: "B49F", source: "Lindwurm" } +455.1 "Serpentine Scourge Left" Ability { id: "B4D1", source: "Lindwurm" } +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" + +# Serpentine Scourge Right First +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" } +553.1 "Serpentine Scourge Right" Ability { id: "B4D2", source: "Lindwurm" } +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" + +# Raptor Knuckles Left First +641.1 label "r12s-p1-raptor-left-1" +648.1 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } +648.1 "Fourth-wall Fusion" Ability { id: "B4D5", source: "Lindwurm" } +648.5 "Burst" Ability { id: "B49F", source: "Lindwurm" } +653.6 "Raptor Knuckles Northeast" Ability { id: "B4D0", source: "Lindwurm" } +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" + +# Raptor Knuckles Right First +742.7 label "r12s-p1-raptor-right-1" +749.7 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } +749.7 "Fourth-wall Fusion" Ability { id: "B4D5", source: "Lindwurm" } +750.1 "Burst" Ability { id: "B49F", source: "Lindwurm" } +755.2 "Raptor Knuckles Northwest" Ability { id: "B4CF", source: "Lindwurm" } +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" + +# Slaughtershed 2 +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" } + +871.5 "--sync--" Ability { id: "B4CB", source: "Lindwurm" } jump "r12s-p1-scourge-left-2" +871.5 "--sync--" Ability { id: "B4CD", source: "Lindwurm" } jump "r12s-p1-scourge-right-2" +871.5 "--sync--" Ability { id: "B4CE", source: "Lindwurm" } jump "r12s-p1-raptor-left-2" +871.5 "--sync--" Ability { id: "B4CC", source: "Lindwurm" } jump "r12s-p1-raptor-right-2" +878.5 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } +878.5 "Fourth-wall Fusion" Ability { id: "B4D5", source: "Lindwurm" } +878.9 "Burst" Ability { id: "B49F", source: "Lindwurm" } +884.0 "Serpentine Scourge/Raptor Knuckles?" #Ability { id: ["B4D1", "B4D2", "B4CD", "B4CE"], source: "Lindwurm" } +884.8 "Serpentine Scourge/--knockback--?" #Ability { id: ["B9BC", "B9C7"], source: "Lindwurm" } +888.6 "Serpentine Scourge/Raptor Knuckles?" #Ability { id: ["B4D2", "B4D1", "B4CE", "B4CD"], source: "Lindwurm" } +889.4 "Serpentine Scourge/--knockback--?" #Ability { id: ["B9BC", "B9C7"], source: "Lindwurm" } +896.3 "Slaughtershed 3 (castbar)" #Ability { id: ["B4C3", "B4C6"], source: "Lindwurm" } +898.7 "Slaughtershed 3" #Ability { id: "ADC9", source: "Lindwurm" } + +# Serpentine Scourge Left Second +971.6 label "r12s-p1-scourge-left-2" +978.6 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } +978.6 "Fourth-wall Fusion" Ability { id: "B4D5", source: "Lindwurm" } +979.0 "Burst" Ability { id: "B49F", source: "Lindwurm" } +984.1 "Serpentine Scourge Left" Ability { id: "B4D1", source: "Lindwurm" } +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" + +# Serpentine Scourge Right Second +1069.9 label "r12s-p1-scourge-right-2" +1076.9 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } +1076.9 "Fourth-wall Fusion" Ability { id: "B4D5", source: "Lindwurm" } +1077.3 "Burst" Ability { id: "B49F", source: "Lindwurm" } +1082.4 "Serpentine Scourge Right" Ability { id: "B4D2", source: "Lindwurm" } +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" + +# Raptor Knuckles Left Second +1171.5 label "r12s-p1-raptor-left-2" +1178.5 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } +1178.5 "Fourth-wall Fusion" Ability { id: "B4D5", source: "Lindwurm" } +1178.9 "Burst" Ability { id: "B49F", source: "Lindwurm" } +1184.0 "Raptor Knuckles Northeast" Ability { id: "B4D0", source: "Lindwurm" } +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" + +# Raptor Knuckles Right Second +1271.5 label "r12s-p1-raptor-right-2" +1278.5 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } +1278.5 "Fourth-wall Fusion" Ability { id: "B4D5", source: "Lindwurm" } +1278.9 "Burst" Ability { id: "B49F", source: "Lindwurm" } +1284.0 "Raptor Knuckles Right" Ability { id: "B4CF", source: "Lindwurm" } +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" + +# Slaughtershed 3 +1393.3 label "r12s-p1-slaughtershed3" +1396.3 "Slaughtershed 3 (castbar)" Ability { id: ["B4C3", "B4C6"], source: "Lindwurm" } +1398.7 "Slaughtershed 3" Ability { id: "ADC9", source: "Lindwurm" } + +1400.4 "--sync--" Ability { id: "B4CB", source: "Lindwurm" } jump "r12s-p1-scourge-left-3" +1400.4 "--sync--" Ability { id: "B4CD", source: "Lindwurm" } jump "r12s-p1-scourge-right-3" +1400.4 "--sync--" Ability { id: "B4CE", source: "Lindwurm" } jump "r12s-p1-raptor-left-3" +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" 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" } +1418.6 "Serpentine Scourge/--knockback--?" #Ability { id: ["B9BC", "B9C7"], source: "Lindwurm" } +1431.5 "The Fixer (Enrage)?" #Ability { id: "B2C7", source: "Lindwurm" } +1436.7 "Refreshing Overkill (Enrage)?" #Ability { id: "B538", source: "Lindwurm" } + +# Serpentine Scourge Left Third +1498.8 label "r12s-p1-scourge-left-3" +1505.8 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } +1505.8 "Fourth-wall Fusion" Ability { id: "B4D5", source: "Lindwurm" } +1506.2 "Burst" Ability { id: "B49F", source: "Lindwurm" } +1511.3 "Serpentine Scourge Left" Ability { id: "B4D1", source: "Lindwurm" } +1512.3 "Serpentine Scourge" Ability { id: "B9BC", source: "Lindwurm" } +1515.9 "Serpentine Scourge Right" Ability { id: "B4D2", source: "Lindwurm" } +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?--" +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" +1607.4 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } +1607.4 "Fourth-wall Fusion" Ability { id: "B4D5", source: "Lindwurm" } +1607.8 "Burst" Ability { id: "B49F", source: "Lindwurm" } +1612.9 "Serpentine Scourge Left/Right" Ability { id: "B4D2", source: "Lindwurm" } +1613.9 "Serpentine Scourge" Ability { id: "B9BC", source: "Lindwurm" } +1617.6 "Serpentine Scourge Right/Left" Ability { id: "B4D1", source: "Lindwurm" } +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?--" +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" +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" } +1712.9 "Raptor Knuckles Northeast" Ability { id: "B4D0", source: "Lindwurm" } +1713.7 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } +1717.5 "Raptor Knuckles Northwest" Ability { id: "B4CF", source: "Lindwurm" } +1718.3 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } +1726.5 "--sync--" StartsUsing { id: "B2C7", source: "Lindwurm" } jump "r12s-p1-enrage-alt" +1726.5 "--untargetable?--" +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" +1807.4 "Dramatic Lysis x4" #Ability { id: "B4D4", source: "Lindwurm" } +1807.4 "Fourth-wall Fusion" Ability { id: "B4D5", source: "Lindwurm" } +1807.8 "Burst" Ability { id: "B49F", source: "Lindwurm" } +1812.9 "Raptor Knuckles Northwest" Ability { id: "B4CF", source: "Lindwurm" } +1813.7 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } +1817.5 "Raptor Knuckles Northeast" Ability { id: "B4D0", source: "Lindwurm" } +1818.3 "--knockback--" Ability { id: "B9C7", source: "Lindwurm" } +1826.5 "--sync--" StartsUsing { id: "B2C7", source: "Lindwurm" } jump "r12s-p1-enrage-alt" +1826.5 "--untargetable?--" +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" +1931.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" } 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" } +2117.7 "--targetable--" + + +### Phase 2: Lindwurm II +# -p B528:3012.1 +# -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.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.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) +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 +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 +# B4D3 --sync--: Attack autos +# B495 Mortal Slayer: VFX +# B4B2 Unmitigated Explosion: Missed blob tower +# B4B4 Dramatic Lysis: Damage from breaking a tether; These can be broken at various times +# B4B5 Dramatic Lysis: 6s to break tether, else this applies every 1s, doing damage and giving a damage down +# B4B3 Roiling Mass: Damage from soaking a tether-created tower; The towers can be created at various times +# B4BE Constrictor: VFX Ending of Cruel Coil B4B8 +# B4BD Constrictor: VFX Ending of Cruel Coil B4B9 +# B4B1 Metamitosis: VFX +# B469 Ravenous Reach: VFX +# B769 Ravenous Reach: VFX +# B76A Ravenous Reach: VFX +# B56F --sync--: These happen same time as Bring Down the House BE0A +# B570 --sync--: These happen same time as Bring Down the House BE0A +# BE0A Bring Down the House: VFX +# B4CF Raptor Knuckles: VFX for knockback from NW +# B4D0 Raptor Knuckles: VFX for knockback from NE +# B4AD Cell Death: Failing Cell Shedding by not getting hit by Ravenous Reach + +# Phase 2 +# 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 +# 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 +# ADC9 Slaughtershed +# B2C7 The Fixer: Alternative enrage +# B469 Ravenous Reach: VFX +# B46E --sync-- +# B472 --sync-- Animation coinciding with boss jumping to middle and binding the players +# B479 --sync-- +# B495 Mortal Slayer +# B496 Mortal Slayer: Green (DPS/Healer) Orbs +# B498 Mortal Slayer: Purple (Tank) Orbs +# B49A --sync--: Animation for Wyrm moving West (Cleaving East) +# B49B --sync--: Animation for Wyrm moving East (Cleaving West) +# B49D Ravenous Reach +# B49E Phagocyte Spotlight +# B49F Burst +# B4A0 Grand Entrance: Small circle aoe of wyrm going into ground? Does a knockback and gives damage down +# B4A1 Grand Entrance: Only cast when caradinals are safe; Small circle aoe of wyrm coming out of ground? Does a knockback and gives damage down +# B4A2 Grand Entrance: Only cast when intercardinal are safe +# B4A3 Grand Entrance +# B4A4 Bring Down the House +# B4A5 Bring Down the House +# B4A6 Bring Down the House +# B4A7 Split Scourge +# B4A8 Venomous Scourge +# B4AA Dramatic Lysis: Spread AoE damage +# B4AB Split Scourge +# B4AC Cell Shedding +# B4AD Cell Death +# B4AE Fourth-wall Fusion +# B4AF Hemorrhagic Projection +# B4B0 Dramatic Lysis +# B4B1 Metamitosis +# B4B2 Unmitigated Explosion +# B4B3 Roiling Mass: Chain Tower soaks +# B4B4 Dramatic Lysis +# B4B5 Dramatic Lysis +# B4B6 Phagocyte Spotlight +# B4B7 Roiling Mass: Blob Tower Soaks +# B4B8 Cruel Coil: Boss starting one way? +# B4B9 Cruel Coil: Boss starting the other way? +# B4BA Cruel Coil +# B4BC Skinsplitter +# B4BE Constrictor: VFX Ending of Cruel Coil B4B8 +# B4BD Constrictor: VFX Ending of Cruel Coil B4B9 +# B4BF Constrictor +# B4C1 --sync-- +# B4C2 Constrictor: "soft enrage" damage for Cruel Coil +# B4C3 Slaughtershed +# B4C6 Slaughtershed +# B4CB --sync--: Animation that indicates B4D1 Serpintine Scourge (Left Hand/E) is first +# B4CC --sync--: Animation that indicates B4CF Raptor Knuckles (Right Hand/NW) is first +# B4CD --sync--: Animation that indicates B4D2 Serpintine Scourge (Right hand/W) is first +# B4CE --sync--: Animation that indicates B4D0 Raptor Knuckles (Left Hand/NE) is first +# B4CF Raptor Knuckles +# B4D0 Raptor Knuckles +# B4D1 Serpentine Scourge +# B4D2 Serpentine Scourge +# B4D3 --sync-- +# B4D4 Dramatic Lysis +# B4D5 Fourth-wall Fusion +# 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-- +# B570 --sync-- +# B571 --sync-- +# B769 Ravenous Reach: VFX +# B76A Ravenous Reach: VFX +# B7C4 --sync-- +# B7C5 --sync-- +# B923 Metamitosis +# B9B9 Fourth-wall Fusion +# B9BC Serpentine Scourge +# B9C3 Splattershed +# B9C4 Splattershed +# B9C6 Splattershed +# B9C7 Raptor Knuckles: Damage +# B9DB --sync-- +# BE09 Feral Fission +# BE0A Bring Down the House +# BEBD Grotesquerie: Act 1 +# BEBE Grotesquerie: Act 2 +# BEBF Grotesquerie: Act 3 +# 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 +# B4F0 Unmitigated Impact +# 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 +# B4FC --sync-- +# B4FD --sync-- +# B4FE Bloody Burst +# B4FF --sync-- +# 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 +# B508 Unmitigated Explosion +# 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 --sync--: Attack +# 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 +# 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 +# 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