From b420f3f10aba9bcba197692aabce09f7716b7603 Mon Sep 17 00:00:00 2001 From: ksh Date: Fri, 10 Oct 2025 17:57:53 +0900 Subject: [PATCH 1/6] Add triggers and translations for Pilgrim's Traverse and The Final Verse --- build/@.txt | 4 + .../the_palace_of_the_dead_general.ts | 40 +-- .../deepdungeon/heaven-on-high_general.ts | 47 +-- .../deepdungeon/eureka_orthos_floors_41-50.ts | 2 +- .../deepdungeon/eureka_orthos_floors_81-90.ts | 3 +- .../eureka_orthos_floors_91-100.ts | 10 +- .../deepdungeon/eureka_orthos_general.ts | 19 +- .../deepdungeon/pilgrims_traverse_general.ts | 308 ++++++++++++++++++ .../pilgrims_traverse_the_final_verse.ts | 189 +++++++++++ 9 files changed, 541 insertions(+), 81 deletions(-) create mode 100644 build/@.txt create mode 100644 ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_general.ts create mode 100644 ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_the_final_verse.ts diff --git a/build/@.txt b/build/@.txt new file mode 100644 index 00000000..92de1cf8 --- /dev/null +++ b/build/@.txt @@ -0,0 +1,4 @@ +🔜 +⊕︎⊗︎⌾︎⏺︎▢︎◉︎◎︎●︎◯︎☀︎☯︎☹︎☺︎☻︎☼︎🞅🞆🞇🞈🞉 +🔴🟠🟡🟢🔵🟣🟤⚪⚫ +🟥🟧🟨🟩🟦🟪🟫◻️◼️▪️▫️⬛⬜ diff --git a/ui/raidboss/data/03-hw/deepdungeon/the_palace_of_the_dead_general.ts b/ui/raidboss/data/03-hw/deepdungeon/the_palace_of_the_dead_general.ts index ff9a9f10..4011e5ad 100644 --- a/ui/raidboss/data/03-hw/deepdungeon/the_palace_of_the_dead_general.ts +++ b/ui/raidboss/data/03-hw/deepdungeon/the_palace_of_the_dead_general.ts @@ -1,3 +1,4 @@ +import Outputs from '../../../../../resources/outputs'; import { Responses } from '../../../../../resources/responses'; import ZoneId from '../../../../../resources/zone_id'; import { RaidbossData } from '../../../../../types/data'; @@ -73,7 +74,7 @@ const triggerSet: TriggerSet = { // ---------------- Pomanders ---------------- { id: 'PotD General Pomander Duplicate', - // duplicate item message: https://xivapi.com/LogMessage/7222?pretty=true + // duplicate pomander message: https://v2.xivapi.com/api/sheet/LogMessage/7222 // en: You return the pomander of ${pomander} to the coffer. You cannot carry any more of that item. type: 'SystemLogMessage', netRegex: { id: '1C36' }, @@ -111,12 +112,8 @@ const triggerSet: TriggerSet = { return output.duplicate!({ pomander: output.raising!() }); case 16: return output.duplicate!({ pomander: output.resolution!() }); - case 17: - return output.duplicate!({ pomander: output.frailty!() }); - case 18: - return output.duplicate!({ pomander: output.concealment!() }); - case 19: - return output.duplicate!({ pomander: output.petrification!() }); + default: + return output.duplicate!({ pomander: output.unknown!() }); } }, outputStrings: { @@ -128,7 +125,7 @@ const triggerSet: TriggerSet = { cn: '${pomander} 重复', ko: '${pomander} 중복', }, - // pomanders: https://xivapi.com/deepdungeonItem?pretty=true + // pomanders: https://v2.xivapi.com/api/sheet/DeepDungeonItem safety: { en: 'Safety', de: 'Siegelbruchs', @@ -257,37 +254,14 @@ const triggerSet: TriggerSet = { cn: '基路伯化', ko: '쿠리부 변신', }, - frailty: { - en: 'Frailty', - de: 'Feindschwächung', - fr: 'Incapacité', - ja: '敵弱体', - cn: '弱化敌人', - ko: '적 약화', - }, - concealment: { - en: 'Concealment', - de: 'Verschwindens', - fr: 'Invisibilité', - ja: 'バニシュ', - cn: '隐形', - ko: '배니시', - }, - petrification: { - en: 'Petrification', - de: 'Feindversteinerung', - fr: 'Pétrification', - ja: '敵石化', - cn: '石化敌人', - ko: '적 석화', - }, + unknown: Outputs.unknown, }, }, // ---------------- Floor Notifications ---------------- { id: 'PotD General Cairn of Passage', // portal to transfer between floors - // Cairn of Passage activation message: https://xivapi.com/LogMessage/7245?pretty=true + // Cairn of Passage activation message: https://v2.xivapi.com/api/sheet/LogMessage/7245 // en: The Cairn of Passage is activated! type: 'SystemLogMessage', netRegex: { id: '1C4D', capture: false }, diff --git a/ui/raidboss/data/04-sb/deepdungeon/heaven-on-high_general.ts b/ui/raidboss/data/04-sb/deepdungeon/heaven-on-high_general.ts index e7236964..3a90d4f4 100644 --- a/ui/raidboss/data/04-sb/deepdungeon/heaven-on-high_general.ts +++ b/ui/raidboss/data/04-sb/deepdungeon/heaven-on-high_general.ts @@ -1,3 +1,4 @@ +import Outputs from '../../../../../resources/outputs'; import { Responses } from '../../../../../resources/responses'; import ZoneId from '../../../../../resources/zone_id'; import { RaidbossData } from '../../../../../types/data'; @@ -63,7 +64,7 @@ const triggerSet: TriggerSet = { // ---------------- Pomanders and Magicite ---------------- { id: 'HoH General Pomander Duplicate', - // duplicate item message: https://xivapi.com/LogMessage/7222?pretty=true + // duplicate pomander message: https://v2.xivapi.com/api/sheet/LogMessage/7222 // en: You return the pomander of ${pomander} to the coffer. You cannot carry any more of that item. type: 'SystemLogMessage', netRegex: { id: '1C36' }, @@ -91,22 +92,18 @@ const triggerSet: TriggerSet = { return output.duplicate!({ pomander: output.witching!() }); case 11: return output.duplicate!({ pomander: output.serenity!() }); - case 12: - return output.duplicate!({ pomander: output.rage!() }); - case 13: - return output.duplicate!({ pomander: output.lust!() }); case 14: return output.duplicate!({ pomander: output.intuition!() }); case 15: return output.duplicate!({ pomander: output.raising!() }); - case 16: - return output.duplicate!({ pomander: output.resolution!() }); case 17: return output.duplicate!({ pomander: output.frailty!() }); case 18: return output.duplicate!({ pomander: output.concealment!() }); case 19: return output.duplicate!({ pomander: output.petrification!() }); + default: + return output.duplicate!({ pomander: output.unknown!() }); } }, outputStrings: { @@ -118,7 +115,7 @@ const triggerSet: TriggerSet = { cn: '${pomander} 重复', ko: '${pomander} 중복', }, - // pomanders: https://xivapi.com/deepdungeonItem?pretty=true + // pomanders: https://v2.xivapi.com/api/sheet/DeepDungeonItem safety: { en: 'Safety', de: 'Siegelbruchs', @@ -207,22 +204,6 @@ const triggerSet: TriggerSet = { cn: '魔法效果解除', ko: '마법 효과 해제', }, - rage: { - en: 'Rage', - de: 'Manticoren', - fr: 'Manticore', - ja: 'マンティコア化', - cn: '曼提克化', - ko: '만티코어 변신', - }, - lust: { - en: 'Lust', - de: 'Sukkuben', - fr: 'Succube', - ja: 'サキュバス化', - cn: '梦魔化', - ko: '서큐버스 변신', - }, intuition: { en: 'Intuition', de: 'Finders', @@ -239,14 +220,6 @@ const triggerSet: TriggerSet = { cn: '重生', ko: '리레이즈', }, - resolution: { - en: 'Resolution', - de: 'Kuribu', - fr: 'Kuribu', - ja: 'クリブ化', - cn: '基路伯化', - ko: '쿠리부 변신', - }, frailty: { en: 'Frailty', de: 'Feindschwächung', @@ -271,11 +244,12 @@ const triggerSet: TriggerSet = { cn: '石化敌人', ko: '적 석화', }, + unknown: Outputs.unknown, }, }, { id: 'HoH General Magicite Duplicate', - // duplicate item message: https://xivapi.com/LogMessage/9208?pretty=true + // duplicate magicite message: https://v2.xivapi.com/api/sheet/LogMessage/9208 // en: You return the splinter of ${magicite} magicite to the coffer. You cannot carry any more of that item. type: 'SystemLogMessage', netRegex: { id: '23F8' }, @@ -289,6 +263,8 @@ const triggerSet: TriggerSet = { return output.duplicate!({ magicite: output.vortex!() }); case 4: return output.duplicate!({ magicite: output.elder!() }); + default: + return output.duplicate!({ magicite: output.unknown!() }); } }, outputStrings: { @@ -300,7 +276,7 @@ const triggerSet: TriggerSet = { cn: '${magicite} 重复', ko: '${magicite} 중복', }, - // magicite: https://xivapi.com/DeepDungeonMagicStone?pretty=true + // magicite: https://v2.xivapi.com/api/sheet/DeepDungeonMagicStone inferno: { en: 'Inferno', de: 'Ifrit', @@ -333,13 +309,14 @@ const triggerSet: TriggerSet = { cn: '奥丁', ko: '오딘', }, + unknown: Outputs.unknown, }, }, // ---------------- Floor Notifications ---------------- { id: 'HoH General Beacon of Passage', // portal to transfer between floors - // Beacon of Passage activation message: https://xivapi.com/LogMessage/7245?pretty=true + // Beacon of Passage activation message: https://v2.xivapi.com/api/sheet/LogMessage/7245 // en: The Beacon of Passage is activated! type: 'SystemLogMessage', netRegex: { id: '1C4D', capture: false }, diff --git a/ui/raidboss/data/06-ew/deepdungeon/eureka_orthos_floors_41-50.ts b/ui/raidboss/data/06-ew/deepdungeon/eureka_orthos_floors_41-50.ts index b28ae27b..f710448a 100644 --- a/ui/raidboss/data/06-ew/deepdungeon/eureka_orthos_floors_41-50.ts +++ b/ui/raidboss/data/06-ew/deepdungeon/eureka_orthos_floors_41-50.ts @@ -37,7 +37,7 @@ const triggerSet: TriggerSet = { de: 'Raus oder unterbreche ${name}', fr: 'Extérieur ou interrompez ${name}', ja: '沈黙: ${name}', - cn: '出去或打断 ${name}', + cn: '远离或打断 ${name}', ko: '밖으로 또는 인터럽트: ${name}', }, }, diff --git a/ui/raidboss/data/06-ew/deepdungeon/eureka_orthos_floors_81-90.ts b/ui/raidboss/data/06-ew/deepdungeon/eureka_orthos_floors_81-90.ts index da3d7a4f..a8dbf12a 100644 --- a/ui/raidboss/data/06-ew/deepdungeon/eureka_orthos_floors_81-90.ts +++ b/ui/raidboss/data/06-ew/deepdungeon/eureka_orthos_floors_81-90.ts @@ -155,7 +155,7 @@ const triggerSet: TriggerSet = { de: 'Geh in eine Intercardinale Himmelsrichtung vom Boss', fr: 'Allez en intercardinal du boss', ja: 'ボスの斜めへ', - cn: '去Boss的对角线方向', + cn: '去Boss的斜角方向', ko: '보스 비스듬히 피해요', }, }, @@ -348,6 +348,7 @@ const triggerSet: TriggerSet = { between: { en: '${safe}, between eggs', de: '${safe}, zwichen den Eiern', + fr: '${safe}, entre les œufs', ja: '${safe}, たまごの間', cn: '${safe}, 蛋中间', ko: '${safe}, 알 사이', diff --git a/ui/raidboss/data/06-ew/deepdungeon/eureka_orthos_floors_91-100.ts b/ui/raidboss/data/06-ew/deepdungeon/eureka_orthos_floors_91-100.ts index d170ae2c..91cce448 100644 --- a/ui/raidboss/data/06-ew/deepdungeon/eureka_orthos_floors_91-100.ts +++ b/ui/raidboss/data/06-ew/deepdungeon/eureka_orthos_floors_91-100.ts @@ -40,7 +40,7 @@ const triggerSet: TriggerSet = { de: 'Raus oder unterbreche ${name}', fr: 'Extérieur ou interrompez ${name}', ja: '沈黙: ${name}', - cn: '出去或打断 ${name}', + cn: '远离或打断 ${name}', ko: '밖으로 또는 인터럽트: ${name}', }, }, @@ -219,7 +219,7 @@ const triggerSet: TriggerSet = { de: 'Lass dich von Eis treffen', fr: 'Faites-vous toucher par la glace', ja: '氷へ', - cn: '被冰打', + cn: '吃冰', ko: '얼음에 부디쳐요', }, fire: { @@ -227,7 +227,7 @@ const triggerSet: TriggerSet = { de: 'Lass dich von Feuer treffen', fr: 'Faites-vous toucher par le feu', ja: '炎へ', - cn: '被火打', + cn: '吃火', ko: '불에 부디쳐요', }, }, @@ -251,7 +251,7 @@ const triggerSet: TriggerSet = { de: 'Lass dich von Eis treffen', fr: 'Faites-vous toucher par la glace', ja: '氷へ', - cn: '被冰打', + cn: '吃冰', ko: '얼음에 부디쳐요', }, avoid: { @@ -283,7 +283,7 @@ const triggerSet: TriggerSet = { de: 'Lass dich von Feuer treffen', fr: 'Faites-vous toucher par le feu', ja: '炎へ', - cn: '被火打', + cn: '吃火', ko: '불에 부디쳐요', }, avoid: { diff --git a/ui/raidboss/data/06-ew/deepdungeon/eureka_orthos_general.ts b/ui/raidboss/data/06-ew/deepdungeon/eureka_orthos_general.ts index acd3c0b7..a883bf1f 100644 --- a/ui/raidboss/data/06-ew/deepdungeon/eureka_orthos_general.ts +++ b/ui/raidboss/data/06-ew/deepdungeon/eureka_orthos_general.ts @@ -1,3 +1,4 @@ +import Outputs from '../../../../../resources/outputs'; import { Responses } from '../../../../../resources/responses'; import ZoneId from '../../../../../resources/zone_id'; import { RaidbossData } from '../../../../../types/data'; @@ -63,7 +64,7 @@ const triggerSet: TriggerSet = { // ---------------- Protomanders and Demiclones ---------------- { id: 'EO General Protomander Duplicate', - // duplicate item message: https://xivapi.com/LogMessage/7222?pretty=true + // duplicate protomander message: https://v2.xivapi.com/api/sheet/LogMessage/7222 // en: You return the protomander of ${protomander} to the coffer. You cannot carry any more of that item. type: 'SystemLogMessage', netRegex: { id: '1C36' }, @@ -101,6 +102,8 @@ const triggerSet: TriggerSet = { return output.duplicate!({ protomander: output.intuition!() }); case 35: return output.duplicate!({ protomander: output.raising!() }); + default: + return output.duplicate!({ protomander: output.unknown!() }); } }, outputStrings: { @@ -108,11 +111,11 @@ const triggerSet: TriggerSet = { en: '${protomander} duplicate', de: 'Doppelter ${protomander}', fr: '${protomander} dupliqué(e)', - ja: 'もっています: ${protomander}', + ja: '${protomander} 被り', cn: '${protomander} 重复', ko: '${protomander} 또 나옴', }, - // protomanders: https://xivapi.com/deepdungeonItem?pretty=true + // protomanders: https://v2.xivapi.com/api/sheet/DeepDungeonItem lethargy: { en: 'Lethargy', de: 'Trägheit', @@ -241,11 +244,12 @@ const triggerSet: TriggerSet = { cn: '重生', ko: '리레이즈(Raising)', }, + unknown: Outputs.unknown, }, }, { id: 'EO General Demiclone Duplicate', - // duplicate item message: https://xivapi.com/LogMessage/10287?pretty=true + // duplicate demiclone message: https://v2.xivapi.com/api/sheet/LogMessage/10287 // en: You return the ${demiclone} demiclone to the coffer. You cannot carry any more of that item. type: 'SystemLogMessage', netRegex: { id: '282F' }, @@ -257,6 +261,8 @@ const triggerSet: TriggerSet = { return output.duplicate!({ demiclone: output.doga!() }); case 3: return output.duplicate!({ demiclone: output.onion!() }); + default: + return output.duplicate!({ demiclone: output.unknown!() }); } }, outputStrings: { @@ -268,7 +274,7 @@ const triggerSet: TriggerSet = { cn: '${demiclone} 重复', ko: '${demiclone} 또 나옴', }, - // demiclones: https://xivapi.com/DeepDungeonDemiclone?pretty=true + // demiclones: https://v2.xivapi.com/api/sheet/DeepDungeonDemiclone unei: { en: 'Unei', de: 'Unei', @@ -293,13 +299,14 @@ const triggerSet: TriggerSet = { cn: '洋葱剑士', ko: '양파 기사', }, + unknown: Outputs.unknown, }, }, // ---------------- Floor Notifications ---------------- { id: 'EO General Pylon of Passage', // portal to transfer between floors - // Pylon of Passage activation message: https://xivapi.com/LogMessage/7245?pretty=true + // Pylon of Passage activation message: https://v2.xivapi.com/api/sheet/LogMessage/7245 // en: The Pylon of Passage is activated! type: 'SystemLogMessage', netRegex: { id: '1C4D', capture: false }, diff --git a/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_general.ts b/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_general.ts new file mode 100644 index 00000000..d10bf2c6 --- /dev/null +++ b/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_general.ts @@ -0,0 +1,308 @@ +import Outputs from '../../../../../resources/outputs'; +import { Responses } from '../../../../../resources/responses'; +import ZoneId from '../../../../../resources/zone_id'; +import { RaidbossData } from '../../../../../types/data'; +import { TriggerSet } from '../../../../../types/trigger'; + +// Triggers applicable to all Pilgrim's Traverse floors. + +export type Data = RaidbossData; + +const triggerSet: TriggerSet = { + id: 'PilgrimsTraverseGeneral', + zoneId: [ + ZoneId.PilgrimsTraverseStones1_10, + ZoneId.PilgrimsTraverseStones11_20, + ZoneId.PilgrimsTraverseStones21_30, + ZoneId.PilgrimsTraverseStones31_40, + ZoneId.PilgrimsTraverseStones41_50, + ZoneId.PilgrimsTraverseStones51_60, + ZoneId.PilgrimsTraverseStones61_70, + ZoneId.PilgrimsTraverseStones71_80, + ZoneId.PilgrimsTraverseStones81_90, + ZoneId.PilgrimsTraverseStones91_100, + ], + zoneLabel: { + en: 'Pilgrim\'s Traverse (All Stones)', + ko: '필그림 트래버스 (모든 층)', + }, + + triggers: [ + // ---------------- Mimics ---------------- + { + id: 'PT General Mimic Spawn', + // 14264 = Mimic (floor 1-30 bronze chests, can stun or interrupt) + // 14265 = Mimic (floor 31-60 silver chests, can stun or interrupt) + // 14266 = Mimic (floor 61+ gold chests, can interrupt, immune to stun) + // TODO: some Mimics may spawn after transference between floors and get called early before being found + type: 'AddedCombatant', + netRegex: { npcNameId: ['14264', '14265', '142646'], capture: false }, + infoText: (_data, _matches, output) => output.text!(), + outputStrings: { + text: { + en: 'Mimic spawned!', + de: 'Mimik ist erschienen!', + fr: 'Un mimic apparait !', + ja: 'ミミック!', + cn: '已生成 拟态怪!', + ko: '미믹이 나왔어요!', + }, + }, + }, + { + id: 'PT General Mimic Malice', + // inflicts Accursed Pox (43F) if not interrupted + type: 'StartsUsing', + netRegex: { id: 'AF34', source: 'Mimic' }, + response: Responses.interruptIfPossible(), + }, + // ---------------- Pomanders and Juniper Incense ---------------- + { + id: 'PT General Pomander Duplicate', + // duplicate item message: https://v2.xivapi.com/api/sheet/LogMessage/7222 + // en: You return the pomander of ${pomander} to the coffer. You cannot carry any more of that item. + type: 'SystemLogMessage', + netRegex: { id: '1C36' }, + infoText: (_data, matches, output) => { + switch (parseInt(matches.param1, 16)) { + case 1: + return output.duplicate!({ pomander: output.safety!() }); + case 2: + return output.duplicate!({ pomander: output.sight!() }); + case 3: + return output.duplicate!({ pomander: output.strength!() }); + case 4: + return output.duplicate!({ pomander: output.steel!() }); + case 5: + return output.duplicate!({ pomander: output.affluence!() }); + case 6: + return output.duplicate!({ pomander: output.flight!() }); + case 7: + return output.duplicate!({ pomander: output.alteration!() }); + case 8: + return output.duplicate!({ pomander: output.purity!() }); + case 9: + return output.duplicate!({ pomander: output.fortune!() }); + case 10: + return output.duplicate!({ pomander: output.witching!() }); + case 11: + return output.duplicate!({ pomander: output.serenity!() }); + case 14: + return output.duplicate!({ pomander: output.intuition!() }); + case 15: + return output.duplicate!({ pomander: output.raising!() }); + case 36: + return output.duplicate!({ pomander: output.haste!() }); + case 37: + return output.duplicate!({ pomander: output.purification!() }); + case 38: + return output.duplicate!({ pomander: output.devotion!() }); + default: + return output.duplicate!({ pomander: output.unknown!() }); + } + }, + outputStrings: { + duplicate: { + en: '${pomander} duplicate', + de: 'Doppelter ${pomander}', + fr: '${pomander} dupliqué(e)', + ja: '${pomander} 被り', + cn: '${pomander} 重复', + ko: '${pomander} 또 나옴', + }, + // pomanders: https://v2.xivapi.com/api/sheet/DeepDungeonItem + safety: { + en: 'Safety', + de: 'Siegelbruchs', + fr: 'Désamorçage', + ja: '呪印解除', + cn: '咒印解除', + ko: '함정 해제(Safety)', + }, + sight: { + en: 'Sight', + de: 'Sicht', + fr: 'Localisation', + ja: 'サイトロ', + cn: '全景', + ko: '사이트로(Sight)', + }, + strength: { + en: 'Strength', + de: 'Stärkung', + fr: 'Puissance', + ja: '自己強化', + cn: '强化自身', + ko: '자기 강화(Strength)', + }, + steel: { + en: 'Steel', + de: 'Abwehr', + fr: 'Protection', + ja: '防御強化', + cn: '强化防御', + ko: '방어 강화(Steel)', + }, + affluence: { + en: 'Affluence', + de: 'Schätze', + fr: 'Décèlement', + ja: '宝箱増加', + cn: '宝箱增加', + ko: '보물상자 증가(Affluence)', + }, + flight: { + en: 'Flight', + de: 'Feindtods', + fr: 'Sécurisation', + ja: '敵排除', + cn: '减少敌人', + ko: '적 감소(Flight)', + }, + alteration: { + en: 'Alteration', + de: 'Feindwandlung', + fr: 'Affaiblissement', + ja: '敵変化', + cn: '改变敌人', + ko: '적 변화(Alteration)', + }, + purity: { + en: 'Purity', + de: 'Entzauberung', + fr: 'Anti-maléfice', + ja: '解呪', + cn: '解咒', + ko: '저주 해제(Purity)', + }, + fortune: { + en: 'Fortune', + de: 'Glücks', + fr: 'Chance', + ja: '運気上昇', + cn: '运气上升', + ko: '운 상승(Fortune)', + }, + witching: { + en: 'Witching', + de: 'Wandlung', + fr: 'Mutation', + ja: '形態変化', + cn: '形态变化', + ko: '적 변형(Witching)', + }, + serenity: { + en: 'Serenity', + de: 'Enthexung', + fr: 'Dissipation', + ja: '魔法効果解除', + cn: '魔法效果解除', + ko: '마법 효과 해제(Serenity)', + }, + intuition: { + en: 'Intuition', + de: 'Finders', + fr: 'Intuition', + ja: '財宝感知', + cn: '感知宝藏', + ko: '보물 탐지(Intuition)', + }, + raising: { + en: 'Raising', + de: 'Lebens', + fr: 'Résurrection', + ja: 'リレイズ', + cn: '重生', + ko: '리레이즈(Raising)', + }, + haste: { + en: 'Haste', + ko: '헤이스트(Haste)', + }, + purification: { + en: 'Purification', + ko: '이상 현상 바리어(Purification)', + }, + devotion: { + en: 'Devotion', + ko: '헌신(Devotion)', + }, + unknown: Outputs.unknown, + }, + }, + { + id: 'PT General Incense Duplicate', + // two different SystemLogMessage depending on which incense + // duplicate incense message 1: https://v2.xivapi.com/api/sheet/LogMessage/9208 + // duplicate incense message 2: https://v2.xivapi.com/api/sheet/LogMessage/10287 + // en: You return the piece of ${incense} incense to the coffer. You cannot carry any more of that item. + type: 'SystemLogMessage', + netRegex: { id: ['23F8', '282F'] }, + infoText: (_data, matches, output) => { + const id = matches.id; + const param1 = parseInt(matches.param1, 16); + + // incense items are in two different tables: DeepDungeonDemiclone and DeepDungeonMagicStone + // https://v2.xivapi.com/api/sheet/DeepDungeonDemiclone + // https://v2.xivapi.com/api/sheet/DeepDungeonMagicStone + if (id === '23F8' && param1 === 5) { + // incense is from DeepDungeonMagicStone + return output.duplicate!({ incense: output.poisonfruit!() }); + } + + if (id === '282F') { + // incense is from DeepDungeonDemiclone + switch (param1) { + case 4: + return output.duplicate!({ incense: output.mazeroot!() }); + case 5: + return output.duplicate!({ incense: output.barkbalm!() }); + } + } + + return output.duplicate!({ incense: output.unknown!() }); + }, + outputStrings: { + duplicate: { + en: '${incense} duplicate', + ko: '${incense} 또 나옴', + }, + mazeroot: { + en: 'Mazeroot', + ko: '미로뿌리(Mazeroot)', + }, + barkbalm: { + en: 'Barkbalm', + ko: '나무껍질연고(Barkbalm)', + }, + poisonfruit: { + en: 'Poisonfruit', + ko: '독과일(Poisonfruit)', + }, + unknown: Outputs.unknown, + }, + }, + // ---------------- Floor Notifications ---------------- + { + id: 'PT General Pylon of Passage', + // portal to transfer between floors + // Pylon of Passage activation message: https://v2.xivapi.com/api/sheet/LogMessage/7245 + // en: The Pylon of Passage is activated! + type: 'SystemLogMessage', + netRegex: { id: '1C4D', capture: false }, + infoText: (_data, _matches, output) => output.text!(), + outputStrings: { + text: { + en: 'Pylon of Passage activated', + de: 'Translokator aktiviert', + fr: 'Pylone de téléportation activé', + ja: '転移が出来ます', + cn: '传送装置已启动', + ko: '다음 층으로 갈 수 있어요', + }, + }, + }, + ], +}; + +export default triggerSet; diff --git a/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_the_final_verse.ts b/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_the_final_verse.ts new file mode 100644 index 00000000..1719ac2f --- /dev/null +++ b/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_the_final_verse.ts @@ -0,0 +1,189 @@ +import Conditions from '../../../../../resources/conditions'; +import Outputs from '../../../../../resources/outputs'; +import { Responses } from '../../../../../resources/responses'; +import ZoneId from '../../../../../resources/zone_id'; +import { RaidbossData } from '../../../../../types/data'; +import { TriggerSet } from '../../../../../types/trigger'; + +// Pilgrim's Traverse Stone 99/The Final Verse +// TODO: Abysal Blaze left/right safe spots +// TODO: timeline + +export type Data = RaidbossData; + +const triggerSet: TriggerSet = { + id: 'TheFinalVerse', + zoneId: [ + ZoneId.PilgrimsTraverseStones91_100, + ZoneId.TheFinalVerse, + ], + zoneLabel: { + en: 'Pilgrim\'s Traverse Stone 99/The Final Verse', + }, + + triggers: [ + // ---------------- Stone 99/The Final Verse Boss: Eminent Grief/Devoured Eater ---------------- + { + id: 'PT 99 Devoured Eater Blade of First Light', + type: 'StartsUsing', + netRegex: { id: ['AC21', 'AC22', 'AC27', 'AC28'], source: 'Devoured Eater', capture: true }, + alertText: (_data, matches, outputs) => { + const id = matches.id; + if (id === 'AC21' || id === 'AC27') + return outputs.sides!(); + return outputs.middle!(); + }, + outputStrings: { + sides: Outputs.sides, + middle: Outputs.goIntoMiddle, + }, + }, + { + id: 'PT 99 Eminent Grief Ball of Fire', + type: 'Ability', + netRegex: { id: ['AC1D', 'AC24'], source: 'Eminent Grief', capture: false }, + response: Responses.moveAway('alert'), + }, + { + id: 'PT 99 Eminent Grief Chains of Condemnation', + // raidwide + applies 11D2 Chains of Condemnation for 3s; heavy damage if moving + type: 'StartsUsing', + netRegex: { id: ['AC20', 'AC26'], source: 'Eminent Grief', capture: true }, + delaySeconds: (_data, matches) => parseFloat(matches.castTime) - 3, + durationSeconds: 6, + alarmText: (_data, _matches, outputs) => outputs.text!(), + outputStrings: { + text: { + en: 'AoE + Stop Moving!', + ko: '전체 공격 + 움직이면 안되요!', + }, + }, + }, + { + id: 'PT 99 Devoured Eater Bounds of Sin', + // applies 119E Bind for 3s + type: 'Ability', + netRegex: { id: 'AC32', source: 'Devoured Eater', capture: false }, + delaySeconds: 3, + response: Responses.moveAway('alert'), + }, + { + id: 'PT 99 Eminent Grief Spinelash Bait', + // laser will break glass + summon add if it hits a window + type: 'HeadMarker', + netRegex: { id: '00EA', capture: true }, + condition: Conditions.targetIsYou(), + alertText: (_data, _matches, outputs) => outputs.text!(), + outputStrings: { + text: { + en: 'Laser on YOU', + ko: '내게 레이저!', + }, + }, + }, + { + id: 'PT 99 Eminent Grief Spinelash', + type: 'StartsUsing', + netRegex: { id: 'B03E', source: 'Eminent Grief', capture: false }, + infoText: (_data, _matches, outputs) => outputs.text!(), + outputStrings: { + text: { + en: 'Avoid laser', + ko: '레이저 피해요', + }, + }, + }, + { + id: 'PT 99 Vodoriga Minion Spawn', + // 14039 = Vodoriga Minion + type: 'AddedCombatant', + netRegex: { npcNameId: '14039', capture: false }, + response: Responses.killExtraAdd(), + }, + { + id: 'PT 99 Eminent Grief Drain Aether', + // AC3[BD] = failstate casts? + type: 'StartsUsing', + netRegex: { id: ['AC38', 'AC39'], source: 'Eminent Grief', capture: true }, + delaySeconds: (_data, matches) => parseFloat(matches.castTime) - 3, + alertText: (_data, _matches, outputs) => outputs.text!(), + outputStrings: { + text: { + en: 'Get Light debuff', + ko: '빛🟡 디버프 받아요', + }, + }, + }, + { + id: 'PT 99 Devoured Eater Drain Aether', + type: 'StartsUsing', + netRegex: { id: ['AC3A', 'AC3C'], source: 'Devoured Eater', capture: true }, + delaySeconds: (_data, matches) => parseFloat(matches.castTime) - 3, + alertText: (_data, _matches, outputs) => outputs.text!(), + outputStrings: { + text: { + en: 'Get Dark debuff', + ko: '어둠⚫️ 디버프 받아요', + }, + }, + }, + { + id: 'PT 99 Eminent Grief Abyssal Blaze Safe Spots', + // AC2A = first cast, horizontal exaflares, front safe + // AC2B = first cast, vertical exaflares, left or right safe + // AC2C = second instant cast, horizontal exaflares, back safe + // AC2D = second instant cast, vertical exaflares, left or right safe + // AC2F = diamonds glow, exaflares start at end of cast + // AC30 = instant, exaflare explosion/damage + type: 'Ability', + netRegex: { id: ['AC2A', 'AC2B', 'AC2C', 'AC2D'], source: 'Eminent Grief', capture: true }, + durationSeconds: 10, + infoText: (_data, matches, outputs) => { + const id = matches.id; + switch (id) { + case 'AC2A': + return outputs.text!({ safe: outputs.frontSafe!() }); + case 'AC2B': + return outputs.text!({ safe: outputs.leftRightSafe!() }); + case 'AC2C': + return outputs.text!({ safe: outputs.backSafe!() }); + case 'AC2D': + return outputs.text!({ safe: outputs.leftRightSafe!() }); + } + }, + outputStrings: { + text: { + en: '${safe}, for later', + ko: '(${safe})', + }, + frontSafe: { + en: 'Front safe', + ko: '앞쪽이 안전', + }, + backSafe: { + en: 'Back safe', + ko: '뒤쪽이 안전', + }, + leftRightSafe: { + en: 'Check safe side', + ko: '안전한 옆 확인해요', + }, + }, + }, + { + id: 'PT 99 Eminent Grief Abyssal Blaze', + type: 'StartsUsing', + netRegex: { id: 'AC2F', source: 'Eminent Grief', capture: false }, + suppressSeconds: 1, + alarmText: (_data, _matches, outputs) => outputs.text!(), + outputStrings: { + text: { + en: 'Avoid Exaflares', + ko: '엑사플레어 피해요', + }, + }, + }, + ], +}; + +export default triggerSet; From 2ad253b2e6f0c7b6883e685d51bdd9ab0d05fc90 Mon Sep 17 00:00:00 2001 From: ksh Date: Fri, 10 Oct 2025 21:17:15 +0900 Subject: [PATCH 2/6] =?UTF-8?q?=EB=B2=84=EC=A0=84=2099.167.0=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20=EB=B0=8F=20?= =?UTF-8?q?ArkveldEx=20=EC=A0=84=ED=88=AC=20=ED=8A=B8=EB=A6=AC=EA=B1=B0=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build/@.txt | 7 +- package.json | 2 +- .../Properties/AssemblyInfo.cs | 4 +- .../CactbotOverlay/Properties/AssemblyInfo.cs | 4 +- ui/raidboss/data/07-dt/trial/arkveld-ex.ts | 145 ++++++++++++++---- 5 files changed, 128 insertions(+), 34 deletions(-) diff --git a/build/@.txt b/build/@.txt index 92de1cf8..761c6a7e 100644 --- a/build/@.txt +++ b/build/@.txt @@ -1,4 +1,9 @@ -🔜 +🔙🔚🔛🔜🔝 ⊕︎⊗︎⌾︎⏺︎▢︎◉︎◎︎●︎◯︎☀︎☯︎☹︎☺︎☻︎☼︎🞅🞆🞇🞈🞉 🔴🟠🟡🟢🔵🟣🟤⚪⚫ 🟥🟧🟨🟩🟦🟪🟫◻️◼️▪️▫️⬛⬜ +🠜🠝🠞🠟 +🡄🡅🡆🡇 +🡐🡑🡒🡓 +🡸🡹🡺🡻🡼🡽🡾🡿 +🢀🢁🢂🢃🢄🢅🢆🢇 diff --git a/package.json b/package.json index f27472f7..020fe99d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cactbot-build", - "version": "99.166.0", + "version": "99.167.0", "license": "Apache-2.0", "type": "module", "types": "./types", diff --git a/plugin/CactbotEventSource/Properties/AssemblyInfo.cs b/plugin/CactbotEventSource/Properties/AssemblyInfo.cs index b88079e9..0de54bd7 100644 --- a/plugin/CactbotEventSource/Properties/AssemblyInfo.cs +++ b/plugin/CactbotEventSource/Properties/AssemblyInfo.cs @@ -21,5 +21,5 @@ // - Revision // GitHub has only 3 version components, so Revision should always be 0. // CactbotOverlay and CactbotEventSource version should match. -[assembly: AssemblyVersion("99.166.0.0")] -[assembly: AssemblyFileVersion("99.166.0.0")] +[assembly: AssemblyVersion("99.167.0.0")] +[assembly: AssemblyFileVersion("99.167.0.0")] diff --git a/plugin/CactbotOverlay/Properties/AssemblyInfo.cs b/plugin/CactbotOverlay/Properties/AssemblyInfo.cs index 5b68b41e..2eecc0c6 100644 --- a/plugin/CactbotOverlay/Properties/AssemblyInfo.cs +++ b/plugin/CactbotOverlay/Properties/AssemblyInfo.cs @@ -20,5 +20,5 @@ // - Build Number // - Revision // GitHub has only 3 version components, so Revision should always be 0. -[assembly: AssemblyVersion("99.166.0.0")] -[assembly: AssemblyFileVersion("99.166.0.0")] +[assembly: AssemblyVersion("99.167.0.0")] +[assembly: AssemblyFileVersion("99.167.0.0")] diff --git a/ui/raidboss/data/07-dt/trial/arkveld-ex.ts b/ui/raidboss/data/07-dt/trial/arkveld-ex.ts index f894f342..1a6802e7 100644 --- a/ui/raidboss/data/07-dt/trial/arkveld-ex.ts +++ b/ui/raidboss/data/07-dt/trial/arkveld-ex.ts @@ -10,6 +10,7 @@ export interface Data extends RaidbossData { dice?: number; chaseDir?: 'cw' | 'ccw'; chases: number; + beams: number; } const diceMap: { [id: string]: number } = { @@ -33,6 +34,7 @@ const triggerSet: TriggerSet = { initData: () => ({ resonance: 0, chases: 0, + beams: 0, }), triggers: [ { @@ -44,12 +46,16 @@ const triggerSet: TriggerSet = { { id: 'ArkveldEx 날개짓', type: 'StartsUsing', - // 43891 왼쪽 - // 43892 오른쪽 - netRegex: { id: ['AB73', 'AB74'] }, + // AB73 왼쪽 + // AB74 오른쪽 + // B019 왼쪽 + // B020 오른쪽 + netRegex: { id: ['AB73', 'AB74', 'B019', 'B020'] }, durationSeconds: 3, infoText: (_data, matches, output) => { - const dir = matches.id === 'AB74' ? output.left!() : output.right!(); + const dir = matches.id === 'AB74' || matches.id === 'B020' + ? output.left!() + : output.right!(); return output.text!({ direction: dir }); }, outputStrings: { @@ -61,6 +67,20 @@ const triggerSet: TriggerSet = { right: Outputs.right, }, }, + { + id: 'ArkveldEx 날개짓 이동', + type: 'StartsUsing', + netRegex: { id: ['AB73', 'AB74', 'B019', 'B020'] }, + delaySeconds: (_data, matches) => parseFloat(matches.castTime) - 0.2, + durationSeconds: 2, + alertText: (_data, _matches, output) => output.move!(), + outputStrings: { + move: { + en: 'Move', + ko: '움직여요!', + }, + }, + }, { id: 'ArkveldEx 빨간 돌진', type: 'StartsUsing', @@ -89,8 +109,7 @@ const triggerSet: TriggerSet = { }, }, }, - /* - { + /* { id: 'ArkveldEx White Flash', type: 'StartsUsing', netRegex: { id: 'AB82', capture: false }, @@ -99,8 +118,7 @@ const triggerSet: TriggerSet = { outputStrings: { text: Outputs.healerGroups, }, - }, - */ + }, */ { id: 'ArkveldEx Rush', type: 'StartsUsing', @@ -129,7 +147,8 @@ const triggerSet: TriggerSet = { type: 'StartsUsing', // 43916 왼쪽 // 43918 오른쪽 - netRegex: { id: ['AB8C', 'AB8E'] }, + // B034 왼쪽 + netRegex: { id: ['AB8C', 'AB8E', 'B032', 'B034'] }, durationSeconds: 4, response: (data, matches, output) => { // cactbot-builtin-response @@ -145,7 +164,9 @@ const triggerSet: TriggerSet = { left: Outputs.left, right: Outputs.right, }; - const dir = matches.id === 'AB8E' ? output.left!() : output.right!(); + const dir = matches.id === 'AB8E' || matches.id === 'B034' + ? output.left!() + : output.right!(); if (data.wildEnergy) return { alertText: output.spread!({ direction: dir }) }; return { infoText: output.text!({ direction: dir }) }; @@ -155,7 +176,7 @@ const triggerSet: TriggerSet = { { id: 'ArkveldEx Steeltail Thrust', type: 'StartsUsing', - netRegex: { id: 'ABAD', capture: false }, + netRegex: { id: ['ABAD', 'B035'], capture: false }, infoText: (_data, _matches, output) => output.text!(), outputStrings: { text: { @@ -244,14 +265,14 @@ const triggerSet: TriggerSet = { response: (data, matches, output) => { // cactbot-builtin-response output.responseOutputStrings = { - cw1: { en: 'East', ko: '오른쪽으로' }, - cw2: { en: 'South', ko: '남쪽으로' }, - cw3: { en: 'West', ko: '왼쪽으로' }, - cw4: { en: 'North', ko: '북쪽으로' }, - ccw1: { en: 'West', ko: '왼쪽으로' }, - ccw2: { en: 'South', ko: '남쪽으로' }, - ccw3: { en: 'East', ko: '오른쪽으로' }, - ccw4: { en: 'North', ko: '북쪽으로' }, + cw1: { en: 'East', ko: '오른쪽🡆으로' }, + cw2: { en: 'South', ko: '남쪽🡇으로' }, + cw3: { en: 'West', ko: '왼쪽🡄으로' }, + cw4: { en: 'North', ko: '북쪽🡅으로' }, + ccw1: { en: 'West', ko: '왼쪽🡄으로' }, + ccw2: { en: 'South', ko: '남쪽🡇으로' }, + ccw3: { en: 'East', ko: '오른쪽🡆으로' }, + ccw4: { en: 'North', ko: '북쪽🡅으로' }, others: { en: 'Go center', ko: '한가운데서 대기' }, }; data.chaseDir = matches.id === 'ABB3' ? 'cw' : 'ccw'; @@ -272,17 +293,17 @@ const triggerSet: TriggerSet = { response: (data, _matches, output) => { // cactbot-builtin-response output.responseOutputStrings = { - cw5: { en: 'East', ko: '오른쪽으로' }, - cw6: { en: 'South', ko: '남쪽으로' }, - cw7: { en: 'West', ko: '왼쪽으로' }, - cw8: { en: 'North', ko: '북쪽으로' }, - ccw5: { en: 'West', ko: '왼쪽으로' }, - ccw6: { en: 'South', ko: '남쪽으로' }, - ccw7: { en: 'East', ko: '오른쪽으로' }, - ccw8: { en: 'North', ko: '북쪽으로' }, + cw5: { en: 'East', ko: '오른쪽🡆으로' }, + cw6: { en: 'South', ko: '남쪽🡇으로' }, + cw7: { en: 'West', ko: '왼쪽🡄으로' }, + cw8: { en: 'North', ko: '북쪽🡅으로' }, + ccw5: { en: 'West', ko: '왼쪽🡄으로' }, + ccw6: { en: 'South', ko: '남쪽🡇으로' }, + ccw7: { en: 'East', ko: '오른쪽🡆으로' }, + ccw8: { en: 'North', ko: '북쪽🡅으로' }, avoid: { en: 'Avoid!', - ko: '피해요!', + ko: '한가운데로 피해욧!', }, }; data.chases = (data.chases ?? 0) + 1; @@ -294,6 +315,74 @@ const triggerSet: TriggerSet = { } }, }, + /* { + id: 'ArkveldEx Wyvern\'s Weal', + type: 'StartsUsing', + netRegex: { id: 'ABA0', capture: false }, + infoText: (_data, _matches, output) => output.text!(), + run: (data) => data.beams = 0, + outputStrings: { + text: { + en: 'Bait beams', + ko: '3단 빔 유도', + }, + }, + }, */ + { + id: 'ArkveldEx Weal Beam', + type: 'HeadMarker', + netRegex: { id: '01D6' }, + durationSeconds: 5, + response: (data, matches, output) => { + // cactbot-builtin-response + output.responseOutputStrings = { + unknown: Outputs.unknown, + west: Outputs.west, + east: Outputs.east, + beamMe: { + en: 'Beams on YOU, go ${direction}!', + ko: '나에게 빔! ${direction}으로!', + }, + partyMove: { + en: 'Go ${direction}, later', + ko: '(${direction}으로)', + }, + }; + const beamer: string[] = ['east', 'west', 'east']; + const party: string[] = ['west', 'east', 'west']; + const cur = data.beams++; + if (data.me === matches.target) { + const dir = output[beamer[cur] ?? 'unknown']!(); + return { alertText: output[dir]!() }; + } + const dir = output[party[cur] ?? 'unknown']!(); + return { infoText: output.partyMove!({ direction: dir }) }; + }, + }, + { + id: 'ArkveldEx Wrathful Rattle', + type: 'StartsUsing', + netRegex: { id: 'ABA7', capture: false }, + infoText: (_data, _matches, output) => output.text!(), + outputStrings: { + text: { + en: 'Waves', + ko: '연속 물결 장판', + }, + }, + }, + { + id: 'ArkveldEx Roar + Exaflare', + type: 'StartsUsing', + netRegex: { id: 'ABAF', capture: false }, + infoText: (_data, _matches, output) => output.text!(), + outputStrings: { + text: { + en: 'AoE + Exaflare', + ko: '전체 공격 🔜 엑사플레어', + }, + }, + }, ], }; From 2e318bba91b0590122c946215cad5835e0b96e8e Mon Sep 17 00:00:00 2001 From: ksh Date: Sun, 12 Oct 2025 13:25:02 +0900 Subject: [PATCH 3/6] =?UTF-8?q?Pilgrim's=20Traverse=20=EB=B0=8F=20ArkveldE?= =?UTF-8?q?x=20=ED=8A=B8=EB=A6=AC=EA=B1=B0=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EB=B0=8F=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pilgrims_traverse_the_final_verse.ts | 30 +++++++-- ui/raidboss/data/07-dt/trial/arkveld-ex.ts | 65 +++++++++---------- 2 files changed, 52 insertions(+), 43 deletions(-) diff --git a/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_the_final_verse.ts b/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_the_final_verse.ts index 1719ac2f..e88990de 100644 --- a/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_the_final_verse.ts +++ b/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_the_final_verse.ts @@ -42,7 +42,7 @@ const triggerSet: TriggerSet = { id: 'PT 99 Eminent Grief Ball of Fire', type: 'Ability', netRegex: { id: ['AC1D', 'AC24'], source: 'Eminent Grief', capture: false }, - response: Responses.moveAway('alert'), + response: Responses.moveAway('alarm'), }, { id: 'PT 99 Eminent Grief Chains of Condemnation', @@ -51,11 +51,11 @@ const triggerSet: TriggerSet = { netRegex: { id: ['AC20', 'AC26'], source: 'Eminent Grief', capture: true }, delaySeconds: (_data, matches) => parseFloat(matches.castTime) - 3, durationSeconds: 6, - alarmText: (_data, _matches, outputs) => outputs.text!(), + alertText: (_data, _matches, outputs) => outputs.text!(), outputStrings: { text: { en: 'AoE + Stop Moving!', - ko: '전체 공격 + 움직이면 안되요!', + ko: '전체 공격 + 동작 그만!', }, }, }, @@ -65,6 +65,7 @@ const triggerSet: TriggerSet = { type: 'Ability', netRegex: { id: 'AC32', source: 'Devoured Eater', capture: false }, delaySeconds: 3, + suppressSeconds: 1, response: Responses.moveAway('alert'), }, { @@ -110,7 +111,7 @@ const triggerSet: TriggerSet = { outputStrings: { text: { en: 'Get Light debuff', - ko: '빛🟡 디버프 받아요', + ko: '빛🟡 받아요', }, }, }, @@ -123,7 +124,7 @@ const triggerSet: TriggerSet = { outputStrings: { text: { en: 'Get Dark debuff', - ko: '어둠⚫️ 디버프 받아요', + ko: '어둠⚫️ 받아요', }, }, }, @@ -166,7 +167,7 @@ const triggerSet: TriggerSet = { }, leftRightSafe: { en: 'Check safe side', - ko: '안전한 옆 확인해요', + ko: '좌우 안전한 곳 확인해요', }, }, }, @@ -175,7 +176,7 @@ const triggerSet: TriggerSet = { type: 'StartsUsing', netRegex: { id: 'AC2F', source: 'Eminent Grief', capture: false }, suppressSeconds: 1, - alarmText: (_data, _matches, outputs) => outputs.text!(), + alertText: (_data, _matches, outputs) => outputs.text!(), outputStrings: { text: { en: 'Avoid Exaflares', @@ -183,6 +184,21 @@ const triggerSet: TriggerSet = { }, }, }, + { + id: 'PT 99 Eminent Grief 불의 알', + type: 'StartsUsing', + netRegex: { id: 'AC2F', source: 'Eminent Grief', capture: false }, + delaySeconds: 20, + durationSeconds: 4, + suppressSeconds: 1, + alertText: (_data, _matches, outputs) => outputs.text!(), + outputStrings: { + text: { + en: '(Spread, later)', + ko: '(구슬 대비, 흩어져요)', + }, + }, + }, ], }; diff --git a/ui/raidboss/data/07-dt/trial/arkveld-ex.ts b/ui/raidboss/data/07-dt/trial/arkveld-ex.ts index 1a6802e7..e46772c3 100644 --- a/ui/raidboss/data/07-dt/trial/arkveld-ex.ts +++ b/ui/raidboss/data/07-dt/trial/arkveld-ex.ts @@ -10,7 +10,6 @@ export interface Data extends RaidbossData { dice?: number; chaseDir?: 'cw' | 'ccw'; chases: number; - beams: number; } const diceMap: { [id: string]: number } = { @@ -34,7 +33,6 @@ const triggerSet: TriggerSet = { initData: () => ({ resonance: 0, chases: 0, - beams: 0, }), triggers: [ { @@ -68,10 +66,24 @@ const triggerSet: TriggerSet = { }, }, { - id: 'ArkveldEx 날개짓 이동', + id: 'ArkveldEx 날개짓 이동 전반', type: 'StartsUsing', - netRegex: { id: ['AB73', 'AB74', 'B019', 'B020'] }, - delaySeconds: (_data, matches) => parseFloat(matches.castTime) - 0.2, + netRegex: { id: ['AB73', 'AB74'], capture: false }, + delaySeconds: 6.5, + durationSeconds: 2, + alertText: (_data, _matches, output) => output.move!(), + outputStrings: { + move: { + en: 'Move', + ko: '움직여요!', + }, + }, + }, + { + id: 'ArkveldEx 날개짓 이동 후반', + type: 'StartsUsing', + netRegex: { id: ['B019', 'B020'], capture: false }, + delaySeconds: 7.5, durationSeconds: 2, alertText: (_data, _matches, output) => output.move!(), outputStrings: { @@ -84,7 +96,7 @@ const triggerSet: TriggerSet = { { id: 'ArkveldEx 빨간 돌진', type: 'StartsUsing', - netRegex: { id: 'AB81', capture: false }, + netRegex: { id: ['B030', 'B037'], capture: false }, durationSeconds: 5, suppressSeconds: 1, infoText: (_data, _matches, output) => output.text!(), @@ -98,7 +110,7 @@ const triggerSet: TriggerSet = { { id: 'ArkveldEx 하얀 돌진', type: 'StartsUsing', - netRegex: { id: 'AB82', capture: false }, + netRegex: { id: ['B02B', 'B045'], capture: false }, durationSeconds: 5, suppressSeconds: 1, infoText: (_data, _matches, output) => output.text!(), @@ -275,9 +287,9 @@ const triggerSet: TriggerSet = { ccw4: { en: 'North', ko: '북쪽🡅으로' }, others: { en: 'Go center', ko: '한가운데서 대기' }, }; - data.chaseDir = matches.id === 'ABB3' ? 'cw' : 'ccw'; if (data.dice === undefined) return; + data.chaseDir = matches.id === 'ABB3' ? 'cw' : 'ccw'; if (data.dice > 4) return { infoText: output.others!() }; const res = `${data.chaseDir}${data.dice}` as const; @@ -306,7 +318,9 @@ const triggerSet: TriggerSet = { ko: '한가운데로 피해욧!', }, }; - data.chases = (data.chases ?? 0) + 1; + if (data.dice === undefined) + return; + data.chases++; if (data.chases === data.dice) return { infoText: output.avoid!() }; if (data.chases < 5 && (data.chases + 4) === data.dice) { @@ -315,19 +329,6 @@ const triggerSet: TriggerSet = { } }, }, - /* { - id: 'ArkveldEx Wyvern\'s Weal', - type: 'StartsUsing', - netRegex: { id: 'ABA0', capture: false }, - infoText: (_data, _matches, output) => output.text!(), - run: (data) => data.beams = 0, - outputStrings: { - text: { - en: 'Bait beams', - ko: '3단 빔 유도', - }, - }, - }, */ { id: 'ArkveldEx Weal Beam', type: 'HeadMarker', @@ -336,27 +337,19 @@ const triggerSet: TriggerSet = { response: (data, matches, output) => { // cactbot-builtin-response output.responseOutputStrings = { - unknown: Outputs.unknown, - west: Outputs.west, - east: Outputs.east, beamMe: { - en: 'Beams on YOU, go ${direction}!', - ko: '나에게 빔! ${direction}으로!', + en: 'Beams on YOU', + ko: '나에게 빔!', }, partyMove: { - en: 'Go ${direction}, later', - ko: '(${direction}으로)', + en: '(Avoid beams)', + ko: '(빔 피해요)', }, }; - const beamer: string[] = ['east', 'west', 'east']; - const party: string[] = ['west', 'east', 'west']; - const cur = data.beams++; if (data.me === matches.target) { - const dir = output[beamer[cur] ?? 'unknown']!(); - return { alertText: output[dir]!() }; + return { alertText: output.beamMe!() }; } - const dir = output[party[cur] ?? 'unknown']!(); - return { infoText: output.partyMove!({ direction: dir }) }; + return { infoText: output.partyMove!() }; }, }, { From 5015b93340c8a8eb2994c68f889ce7232051a148 Mon Sep 17 00:00:00 2001 From: ksh Date: Sun, 12 Oct 2025 13:55:35 +0900 Subject: [PATCH 4/6] =?UTF-8?q?ArkveldEx=20=ED=8A=B8=EB=A6=AC=EA=B1=B0=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/raidboss/data/07-dt/trial/arkveld-ex.ts | 47 ++++++---------------- 1 file changed, 13 insertions(+), 34 deletions(-) diff --git a/ui/raidboss/data/07-dt/trial/arkveld-ex.ts b/ui/raidboss/data/07-dt/trial/arkveld-ex.ts index e46772c3..a69d0d4f 100644 --- a/ui/raidboss/data/07-dt/trial/arkveld-ex.ts +++ b/ui/raidboss/data/07-dt/trial/arkveld-ex.ts @@ -23,8 +23,6 @@ const diceMap: { [id: string]: number } = { '019B': 8, } as const; -// const diceDuration: number[] = [8, 11, 14, 17, 20, 23, 26, 29] as const; - // the Windward Wilds (Extreme) const triggerSet: TriggerSet = { id: 'ArkveldEx', @@ -44,10 +42,6 @@ const triggerSet: TriggerSet = { { id: 'ArkveldEx 날개짓', type: 'StartsUsing', - // AB73 왼쪽 - // AB74 오른쪽 - // B019 왼쪽 - // B020 오른쪽 netRegex: { id: ['AB73', 'AB74', 'B019', 'B020'] }, durationSeconds: 3, infoText: (_data, matches, output) => { @@ -65,25 +59,11 @@ const triggerSet: TriggerSet = { right: Outputs.right, }, }, - { - id: 'ArkveldEx 날개짓 이동 전반', - type: 'StartsUsing', - netRegex: { id: ['AB73', 'AB74'], capture: false }, - delaySeconds: 6.5, - durationSeconds: 2, - alertText: (_data, _matches, output) => output.move!(), - outputStrings: { - move: { - en: 'Move', - ko: '움직여요!', - }, - }, - }, - { - id: 'ArkveldEx 날개짓 이동 후반', + /* { + id: 'ArkveldEx 날개짓 이동', type: 'StartsUsing', - netRegex: { id: ['B019', 'B020'], capture: false }, - delaySeconds: 7.5, + netRegex: { id: ['AB73', 'AB74', 'B019', 'B020'] }, + delaySeconds: (_data, matches) => parseFloat(matches.castTime) - 0.2, durationSeconds: 2, alertText: (_data, _matches, output) => output.move!(), outputStrings: { @@ -92,7 +72,7 @@ const triggerSet: TriggerSet = { ko: '움직여요!', }, }, - }, + }, */ { id: 'ArkveldEx 빨간 돌진', type: 'StartsUsing', @@ -157,10 +137,7 @@ const triggerSet: TriggerSet = { { id: 'ArkveldEx Wyvern\'s Ouroblade', type: 'StartsUsing', - // 43916 왼쪽 - // 43918 오른쪽 - // B034 왼쪽 - netRegex: { id: ['AB8C', 'AB8E', 'B032', 'B034'] }, + netRegex: { id: ['AB8C', 'AB8E', 'B031', 'B032'] }, durationSeconds: 4, response: (data, matches, output) => { // cactbot-builtin-response @@ -176,7 +153,7 @@ const triggerSet: TriggerSet = { left: Outputs.left, right: Outputs.right, }; - const dir = matches.id === 'AB8E' || matches.id === 'B034' + const dir = matches.id === 'AB8E' || matches.id === 'B032' ? output.left!() : output.right!(); if (data.wildEnergy) @@ -300,7 +277,7 @@ const triggerSet: TriggerSet = { { id: 'ArkveldEx Chase Dice', type: 'StartsUsing', - netRegex: { id: 'ABB8', capture: false }, + netRegex: { id: ['ABB5', 'ABB8'], capture: false }, durationSeconds: 4, response: (data, _matches, output) => { // cactbot-builtin-response @@ -323,7 +300,7 @@ const triggerSet: TriggerSet = { data.chases++; if (data.chases === data.dice) return { infoText: output.avoid!() }; - if (data.chases < 5 && (data.chases + 4) === data.dice) { + if ((data.chases + 4) === data.dice) { const res = `${data.chaseDir}${data.dice}` as const; return { alertText: output[res]!() }; } @@ -338,11 +315,11 @@ const triggerSet: TriggerSet = { // cactbot-builtin-response output.responseOutputStrings = { beamMe: { - en: 'Beams on YOU', + en: 'Beam on YOU', ko: '나에게 빔!', }, partyMove: { - en: '(Avoid beams)', + en: '(Avoid beam)', ko: '(빔 피해요)', }, }; @@ -380,3 +357,5 @@ const triggerSet: TriggerSet = { }; export default triggerSet; + +// 추출: StartsCasting 14:([^:]*):Guardian Arkveld:([^:]*) From 0da9527be09fe31a340ce3cb11877472f4cff196 Mon Sep 17 00:00:00 2001 From: ksh Date: Tue, 21 Oct 2025 09:50:59 +0900 Subject: [PATCH 5/6] =?UTF-8?q?=EB=B2=84=EC=A0=84=2099.168.0=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20=EB=B0=8F=20?= =?UTF-8?q?Pilgrim's=20Traverse=20=EC=8A=A4=ED=86=A4=2041-90=EC=97=90=20?= =?UTF-8?q?=EB=8C=80=ED=95=9C=20=ED=8A=B8=EB=A6=AC=EA=B1=B0=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- .../Properties/AssemblyInfo.cs | 4 +- .../CactbotOverlay/Properties/AssemblyInfo.cs | 4 +- .../deepdungeon/eureka_orthos_floors_21-30.ts | 2 +- .../deepdungeon/pilgrims_traverse_general.ts | 3 +- .../pilgrims_traverse_stones_41-50.ts | 50 +++++ .../pilgrims_traverse_stones_51-60.ts | 66 ++++++ .../pilgrims_traverse_stones_61-70.ts | 195 ++++++++++++++++++ .../pilgrims_traverse_stones_71-80.ts | 115 +++++++++++ .../pilgrims_traverse_stones_81-90.ts | 47 +++++ .../pilgrims_traverse_the_final_verse.ts | 64 ++++-- util/gen_coverage_report.ts | 2 + util/zone_overrides.ts | 7 +- 13 files changed, 536 insertions(+), 25 deletions(-) create mode 100644 ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_stones_41-50.ts create mode 100644 ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_stones_51-60.ts create mode 100644 ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_stones_61-70.ts create mode 100644 ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_stones_71-80.ts create mode 100644 ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_stones_81-90.ts diff --git a/package.json b/package.json index 020fe99d..68530e28 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cactbot-build", - "version": "99.167.0", + "version": "99.168.0", "license": "Apache-2.0", "type": "module", "types": "./types", diff --git a/plugin/CactbotEventSource/Properties/AssemblyInfo.cs b/plugin/CactbotEventSource/Properties/AssemblyInfo.cs index 0de54bd7..449e5782 100644 --- a/plugin/CactbotEventSource/Properties/AssemblyInfo.cs +++ b/plugin/CactbotEventSource/Properties/AssemblyInfo.cs @@ -21,5 +21,5 @@ // - Revision // GitHub has only 3 version components, so Revision should always be 0. // CactbotOverlay and CactbotEventSource version should match. -[assembly: AssemblyVersion("99.167.0.0")] -[assembly: AssemblyFileVersion("99.167.0.0")] +[assembly: AssemblyVersion("99.168.0.0")] +[assembly: AssemblyFileVersion("99.168.0.0")] diff --git a/plugin/CactbotOverlay/Properties/AssemblyInfo.cs b/plugin/CactbotOverlay/Properties/AssemblyInfo.cs index 2eecc0c6..8378e11c 100644 --- a/plugin/CactbotOverlay/Properties/AssemblyInfo.cs +++ b/plugin/CactbotOverlay/Properties/AssemblyInfo.cs @@ -20,5 +20,5 @@ // - Build Number // - Revision // GitHub has only 3 version components, so Revision should always be 0. -[assembly: AssemblyVersion("99.167.0.0")] -[assembly: AssemblyFileVersion("99.167.0.0")] +[assembly: AssemblyVersion("99.168.0.0")] +[assembly: AssemblyFileVersion("99.168.0.0")] diff --git a/ui/raidboss/data/06-ew/deepdungeon/eureka_orthos_floors_21-30.ts b/ui/raidboss/data/06-ew/deepdungeon/eureka_orthos_floors_21-30.ts index 3158ae9d..ee6f867b 100644 --- a/ui/raidboss/data/06-ew/deepdungeon/eureka_orthos_floors_21-30.ts +++ b/ui/raidboss/data/06-ew/deepdungeon/eureka_orthos_floors_21-30.ts @@ -62,7 +62,7 @@ const triggerSet: TriggerSet = { type: 'HeadMarker', netRegex: { id: '00C5' }, condition: Conditions.targetIsYou(), - alertText: (_data, _matches, outputs) => outputs.text!(), + alertText: (_data, _matches, output) => output.text!(), outputStrings: { text: { en: '5x chasing puddles on you!', diff --git a/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_general.ts b/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_general.ts index d10bf2c6..18179fe4 100644 --- a/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_general.ts +++ b/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_general.ts @@ -36,7 +36,8 @@ const triggerSet: TriggerSet = { // 14266 = Mimic (floor 61+ gold chests, can interrupt, immune to stun) // TODO: some Mimics may spawn after transference between floors and get called early before being found type: 'AddedCombatant', - netRegex: { npcNameId: ['14264', '14265', '142646'], capture: false }, + netRegex: { npcNameId: ['14264', '14265', '14266'], capture: false }, + suppressSeconds: 1, infoText: (_data, _matches, output) => output.text!(), outputStrings: { text: { diff --git a/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_stones_41-50.ts b/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_stones_41-50.ts new file mode 100644 index 00000000..e04787f3 --- /dev/null +++ b/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_stones_41-50.ts @@ -0,0 +1,50 @@ +import Conditions from '../../../../../resources/conditions'; +import { Responses } from '../../../../../resources/responses'; +import ZoneId from '../../../../../resources/zone_id'; +import { RaidbossData } from '../../../../../types/data'; +import { TriggerSet } from '../../../../../types/trigger'; + +export type Data = RaidbossData; + +const triggerSet: TriggerSet = { + id: 'PilgrimsTraverseStones41_50', + zoneId: ZoneId.PilgrimsTraverseStones41_50, + triggers: [ + // ---------------- Stone 50 Boss: Ogbunabali ---------------- + { + id: 'PT 41-50 Ogbunabali Liquefaction', + type: 'StartsUsing', + netRegex: { id: 'AA0B', source: 'Ogbunabali', capture: false }, + alertText: (_data, _matches, output) => output.text!(), + outputStrings: { + text: { + en: 'Stand on a rock', + ko: '바위 위로 올라서요', + }, + }, + }, + { + id: 'PT 41-50 Ogbunabali Sandpit', + type: 'HeadMarker', + netRegex: { id: '0280', capture: true }, + condition: Conditions.targetIsYou(), + alertText: (_data, _matches, output) => output.text!(), + outputStrings: { + text: { + en: '4x Chasing AoE on YOU', + ko: '따라오는 장판 x4', + }, + }, + }, + { + id: 'PT 41-50 Ogbunabali Biting Wind', + type: 'StartsUsing', + netRegex: { id: 'AA12', source: 'Ogbunabali', capture: true }, + delaySeconds: (_data, matches) => parseFloat(matches.castTime) - 5, + countdownSeconds: 5, + response: Responses.knockback(), + }, + ], +}; + +export default triggerSet; diff --git a/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_stones_51-60.ts b/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_stones_51-60.ts new file mode 100644 index 00000000..49ebd313 --- /dev/null +++ b/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_stones_51-60.ts @@ -0,0 +1,66 @@ +import Outputs from '../../../../../resources/outputs'; +import { Responses } from '../../../../../resources/responses'; +import ZoneId from '../../../../../resources/zone_id'; +import { RaidbossData } from '../../../../../types/data'; +import { TriggerSet } from '../../../../../types/trigger'; + +export type Data = RaidbossData; + +const triggerSet: TriggerSet = { + id: 'PilgrimsTraverseStones51_60', + zoneId: ZoneId.PilgrimsTraverseStones51_60, + triggers: [ + // ---------------- Stone 60 Boss: Ancestral Maliktender ---------------- + { + id: 'PT 51-60 Ancestral Maliktender Spineshot', + type: 'StartsUsing', + netRegex: { id: 'AF42', source: 'Ancestral Maliktender', capture: false }, + response: Responses.goSides(), + }, + { + id: 'PT 51-60 Ancestral Maliktender Spinning Needles', + type: 'StartsUsing', + netRegex: { id: ['AF43', 'AF44'], source: 'Ancestral Maliktender', capture: true }, + durationSeconds: 16, + alertText: (_data, matches, output) => { + const rotation = matches.id === 'AF43' ? output.counterclockwise!() : output.clockwise!(); + return output.text!({ rotation: rotation }); + }, + outputStrings: { + text: { + en: 'Sides + Rotate ${rotation}', + ko: '옆쪽에서 + ${rotation}으로 돌아요', + }, + clockwise: Outputs.clockwise, + counterclockwise: Outputs.counterclockwise, + }, + }, + { + id: 'PT 51-60 Ancestral Maliktender One/Two-stone March', + type: 'StartsUsing', + netRegex: { id: ['AF3E', 'AF3F'], source: 'Ancestral Maliktender', capture: true }, + infoText: (_data, matches, output) => { + const id = matches.id; + if (id === 'AF3E') + return output.text!({ count: output.once!() }); + return output.text!({ count: output.twice!() }); + }, + outputStrings: { + text: { + en: 'Cactuar move ${count}', + ko: '선인장이 ${count} 이동', + }, + once: { + en: 'Once', + ko: '한 번', + }, + twice: { + en: 'Twice', + ko: '두 번', + }, + }, + }, + ], +}; + +export default triggerSet; diff --git a/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_stones_61-70.ts b/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_stones_61-70.ts new file mode 100644 index 00000000..2c5c9107 --- /dev/null +++ b/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_stones_61-70.ts @@ -0,0 +1,195 @@ +import Outputs from '../../../../../resources/outputs'; +import { Responses } from '../../../../../resources/responses'; +import { Directions } from '../../../../../resources/util'; +import ZoneId from '../../../../../resources/zone_id'; +import { RaidbossData } from '../../../../../types/data'; +import { TriggerSet } from '../../../../../types/trigger'; + +type DirectionOutputCardinalRelative = + | 'front' + | 'right' + | 'back' + | 'left' + | 'unknown'; + +const outputCardinalRelativeDir: DirectionOutputCardinalRelative[] = [ + 'front', + 'right', + 'back', + 'left', +]; + +const outputRelativeFrom4DirNum = (dirNum: number): DirectionOutputCardinalRelative => { + return outputCardinalRelativeDir[dirNum] ?? 'unknown'; +}; + +export interface Data extends RaidbossData { + octupleSwipes?: number[]; + calledOctupleSwipes?: boolean; +} + +const triggerSet: TriggerSet = { + id: 'PilgrimsTraverseStones61_70', + zoneId: ZoneId.PilgrimsTraverseStones61_70, + triggers: [ + // ---------------- Stone 70 Boss: Forgiven Zeal ---------------- + // A993 = Zealous Glower dummy self-cast, back-to-front line + // A98E = Zealous Glower dummy self-cast, front-to-back line + // A99A = Ardorous Eye dummy self-cast, clockwise ring + // A99F = Ardorous Eye dummy self-cast, counterclockwise ring + // A9A5 = 2000-mina Swing damage cast + // A9A7 = Disorienting Groan damage cast + // --- Octuple Swipe --- + // A9A8 = dummy self-cast for castbar + // A9A9 = instant, final damage cast + // A9AA = instant, damage cast + // A9AB = instant, damage cast + // A9AC = instant, damage cast + // A9AD = dummy cast showing telegraph + { + id: 'PT 61-70 Forgiven Zeal Zealous Glower', + type: 'StartsUsing', + netRegex: { id: ['A993', 'A98E'], source: 'Forgiven Zeal', capture: false }, + response: Responses.goSides(), + }, + { + id: 'PT 61-70 Forgiven Zeal Zealous Glower Dodge Direction', + type: 'Ability', + netRegex: { id: ['A993', 'A98E'], source: 'Forgiven Zeal', capture: true }, + durationSeconds: 15, + infoText: (_data, matches, output) => { + const dir = matches.id === 'A993' ? output.backFront!() : output.frontBack!(); + return output.text!({ dir: dir }); + }, + outputStrings: { + text: { + en: 'Dodge ${dir}', + ko: '${dir} 피해요!', + }, + backFront: { + en: 'Back-to-front', + ko: '앞에서 뒤로', + }, + frontBack: { + en: 'Front-to-back', + ko: '뒤에서 앞으로', + }, + }, + }, + { + id: 'PT 61-70 Forgiven Zeal Ardorous Eye', + type: 'StartsUsing', + netRegex: { id: ['A99A', 'A99F'], source: 'Forgiven Zeal', capture: false }, + response: Responses.getIn(), + }, + { + id: 'PT 61-70 Forgiven Zeal Ardorous Eye Dodge Direction', + type: 'Ability', + netRegex: { id: ['A99A', 'A99F'], source: 'Forgiven Zeal', capture: true }, + durationSeconds: 20, + infoText: (_data, matches, output) => { + const dir = matches.id === 'A99A' ? output.clockwise!() : output.counterclockwise!(); + return output.text!({ dir: dir }); + }, + outputStrings: { + text: { + en: 'Dodge ${dir}', + ko: '${dir}으로 피해요!', + }, + clockwise: Outputs.clockwise, + counterclockwise: Outputs.counterclockwise, + }, + }, + { + id: 'PT 61-70 Forgiven Zeal Disorienting Groan', + type: 'StartsUsing', + netRegex: { id: 'A9A7', source: 'Forgiven Zeal', capture: false }, + response: Responses.knockback(), + }, + { + id: 'PT 61-70 Forgiven Zeal 2000-mina Swing', + type: 'StartsUsing', + netRegex: { id: 'A9A5', source: 'Forgiven Zeal', capture: false }, + response: Responses.getOut(), + }, + { + id: 'PT 61-70 Forgiven Zeal Octuple Swipe Cleanup', + type: 'Ability', + netRegex: { id: 'A9A9', source: 'Forgiven Zeal', capture: false }, + run: (data) => { + delete data.octupleSwipes; + delete data.calledOctupleSwipes; + }, + }, + { + id: 'PT 61-70 Forgiven Zeal Octuple Swipe', + type: 'StartsUsingExtra', + netRegex: { id: 'A9AD', capture: true }, + condition: (data) => !data.calledOctupleSwipes, + durationSeconds: 18, + alertText: (data, matches, output) => { + const heading = Directions.hdgTo4DirNum(parseFloat(matches.heading)); + data.octupleSwipes ??= []; + data.octupleSwipes.push(heading); + + if (data.octupleSwipes.length < 8) + return; + + data.calledOctupleSwipes = true; + const [swipe1, swipe4, swipe5, swipe8] = [ + data.octupleSwipes[0], + data.octupleSwipes[3], + data.octupleSwipes[4], + data.octupleSwipes[7], + ]; + + if ( + swipe1 === undefined || + swipe4 === undefined || + swipe5 === undefined || + swipe8 === undefined + ) + return output.avoid!(); + + const dir1 = outputRelativeFrom4DirNum(swipe1); + const dir4 = outputRelativeFrom4DirNum(swipe4); + const dir5 = outputRelativeFrom4DirNum(swipe5); + const dir8 = outputRelativeFrom4DirNum(swipe8); + + if (swipe1 === swipe8) + // swipe order is 1 > 2 > 3 > 4 > 5 > 6 > 7 > 1 + // dodge order is 4 > 1 > 1 > 1 > 1 > 1 > 1 > 4 + return output.rewind!({ dir4: output[dir4]!(), dir1: output[dir1]!() }); + + // swipe order is 1 > 2 > 3 > 4 > 5 > 6 > 7 > 8 + // dodge order is 4 > 1 > 1 > 1 > 8 > 5 > 5 > 5 + return output.repeat!({ + dir1: output[dir1]!(), + dir4: output[dir4]!(), + dir5: output[dir5]!(), + dir8: output[dir8]!(), + }); + }, + outputStrings: { + repeat: { + en: '${dir4} => ${dir1} x3 => ${dir8} => ${dir5} x3', + ko: '${dir4} 🔜 ${dir1}x3 🔜 ${dir8} 🔜 ${dir5}x3', + }, + rewind: { + en: '${dir4} => ${dir1} x6 => ${dir4}', + ko: '${dir4} 🔜 ${dir1}x6 🔜 ${dir4}', + }, + avoid: { + en: 'Avoid swipes x8', + ko: '스와이프x8 피해요!', + }, + left: Outputs.left, + right: Outputs.right, + front: Outputs.front, + back: Outputs.back, + }, + }, + ], +}; + +export default triggerSet; diff --git a/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_stones_71-80.ts b/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_stones_71-80.ts new file mode 100644 index 00000000..4cc16a2d --- /dev/null +++ b/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_stones_71-80.ts @@ -0,0 +1,115 @@ +import Conditions from '../../../../../resources/conditions'; +import Outputs from '../../../../../resources/outputs'; +import { Responses } from '../../../../../resources/responses'; +import ZoneId from '../../../../../resources/zone_id'; +import { RaidbossData } from '../../../../../types/data'; +import { TriggerSet } from '../../../../../types/trigger'; + +export interface Data extends RaidbossData { + prowlingDeath?: 'shadowOfDeath' | 'nowhereToRun'; +} + +const triggerSet: TriggerSet = { + id: 'PilgrimsTraverseStones71_80', + zoneId: ZoneId.PilgrimsTraverseStones71_80, + triggers: [ + // ---------------- Stone 80 Boss: Forgiven Profanity ---------------- + // A9C9 = Roaring Ring dummy self-cast, donut + front cleave + // A9CB = Roaring Ring dummy self-cast, donut + back cleave + // A9CC = Roaring Ring damage cast + // A9CD = Perilous Lair dummy self-cast, pbaoe + front cleave + // A9CF = Perilous Lair dummy self-cast, pbaoe + back cleave + // A9D0 = Perilous Lair damage cast + // A9D1 = Profane Waul whiskers cleave damage cast + // A9D3 = Prowling Death applies debuffs + // A9D4 = Stalking Static dummy self-cast + // AB13 = Stalking Static small line cleave + // AC1C = Static Shock final big AoE + { + id: 'PT 71-80 Forgiven Profanity Roaring Ring', + type: 'StartsUsing', + netRegex: { id: ['A9C9', 'A9CB'], source: 'Forgiven Profanity', capture: true }, + alertText: (data, matches, output) => { + const id = matches.id; + let backFront; + if (data.prowlingDeath === 'shadowOfDeath') + backFront = id === 'A9C9' ? output.front!() : output.back!(); + else + backFront = id === 'A9C9' ? output.back!() : output.front!(); + return output.text!({ in: output.in!(), backFront: backFront }); + }, + run: (data) => { + delete data.prowlingDeath; + }, + outputStrings: { + text: { + en: '${in} + ${backFront}', + ko: '${backFront} + ${in}', + }, + back: Outputs.back, + front: Outputs.front, + in: Outputs.in, + }, + }, + { + id: 'PT 71-80 Forgiven Profanity Perilous Lair', + type: 'StartsUsing', + netRegex: { id: ['A9CD', 'A9CF'], source: 'Forgiven Profanity', capture: true }, + alertText: (data, matches, output) => { + const id = matches.id; + let backFront; + if (data.prowlingDeath === 'shadowOfDeath') + backFront = id === 'A9CD' ? output.front!() : output.back!(); + else + backFront = id === 'A9CD' ? output.back!() : output.front!(); + return output.text!({ out: output.out!(), backFront: backFront }); + }, + run: (data) => { + delete data.prowlingDeath; + }, + outputStrings: { + text: { + en: '${out} + ${backFront}', + ko: '${backFront} + ${out}', + }, + back: Outputs.back, + front: Outputs.front, + out: Outputs.out, + }, + }, + { + id: 'PT 71-80 Forgiven Profanity Stalking Static', + type: 'StartsUsing', + netRegex: { id: 'A9D4', source: 'Forgiven Profanity', capture: false }, + durationSeconds: 12, + alertText: (_data, _matches, output) => output.text!(), + outputStrings: { + text: { + en: 'Avoid final AoE', + ko: '마지막 장판 피해요!', + }, + }, + }, + { + id: 'PT 71-80 Forgiven Profanity Prowling Death', + type: 'StartsUsing', + netRegex: { id: 'A9D3', source: 'Forgiven Profanity', capture: false }, + response: Responses.aoe(), + }, + { + id: 'PT 71-80 Forgiven Profanity Prowling Death Collect', + // 11A6 = Shadow of Death, lethal on expiration, cleanse by being hit by whiskers cleave + // en: All who deny Light are condemned to death... + // 11A7 = Nowhere to Run, gain stacks on movement, lethal at 8 stacks + // en: Death approaches all who dare tread forth... + type: 'GainsEffect', + netRegex: { effectId: ['11A6', '11A7'], capture: true }, + condition: Conditions.targetIsYou(), + run: (data, matches) => { + data.prowlingDeath = matches.effectId === '11A6' ? 'shadowOfDeath' : 'nowhereToRun'; + }, + }, + ], +}; + +export default triggerSet; diff --git a/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_stones_81-90.ts b/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_stones_81-90.ts new file mode 100644 index 00000000..8edcd8bf --- /dev/null +++ b/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_stones_81-90.ts @@ -0,0 +1,47 @@ +import { Responses } from '../../../../../resources/responses'; +import ZoneId from '../../../../../resources/zone_id'; +import { RaidbossData } from '../../../../../types/data'; +import { TriggerSet } from '../../../../../types/trigger'; + +export type Data = RaidbossData; + +const triggerSet: TriggerSet = { + id: 'PilgrimsTraverseStones81_90', + zoneId: ZoneId.PilgrimsTraverseStones81_90, + + triggers: [ + // ---------------- Stone 90 Boss: Malacoda ---------------- + { + id: 'PT 81-90 Malacoda Backhand Right', + type: 'StartsUsing', + netRegex: { id: 'ACDA', source: 'Malacoda', capture: false }, + response: Responses.goLeft(), + }, + { + id: 'PT 81-90 Malacoda Backhand Left', + type: 'StartsUsing', + netRegex: { id: 'ACDB', source: 'Malacoda', capture: false }, + response: Responses.goRight(), + }, + { + id: 'PT 81-90 Malacoda Fore-hind Folly', + type: 'StartsUsing', + netRegex: { id: 'ACE2', source: 'Malacoda', capture: false }, + response: Responses.goSides(), + }, + { + id: 'PT 81-90 Malacoda Twin-winged Treachery', + type: 'StartsUsing', + netRegex: { id: 'ACE3', source: 'Malacoda', capture: false }, + response: Responses.goFrontBack(), + }, + { + id: 'PT 81-90 Malacoda Skinflayer', + type: 'StartsUsing', + netRegex: { id: 'ACEA', source: 'Malacoda', capture: false }, + response: Responses.knockback(), + }, + ], +}; + +export default triggerSet; diff --git a/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_the_final_verse.ts b/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_the_final_verse.ts index e88990de..1e2d9181 100644 --- a/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_the_final_verse.ts +++ b/ui/raidboss/data/07-dt/deepdungeon/pilgrims_traverse_the_final_verse.ts @@ -6,6 +6,7 @@ import { RaidbossData } from '../../../../../types/data'; import { TriggerSet } from '../../../../../types/trigger'; // Pilgrim's Traverse Stone 99/The Final Verse +// TODO: Bounds of Sin dodge direction // TODO: Abysal Blaze left/right safe spots // TODO: timeline @@ -27,11 +28,11 @@ const triggerSet: TriggerSet = { id: 'PT 99 Devoured Eater Blade of First Light', type: 'StartsUsing', netRegex: { id: ['AC21', 'AC22', 'AC27', 'AC28'], source: 'Devoured Eater', capture: true }, - alertText: (_data, matches, outputs) => { + alertText: (_data, matches, output) => { const id = matches.id; if (id === 'AC21' || id === 'AC27') - return outputs.sides!(); - return outputs.middle!(); + return output.sides!(); + return output.middle!(); }, outputStrings: { sides: Outputs.sides, @@ -49,8 +50,9 @@ const triggerSet: TriggerSet = { // raidwide + applies 11D2 Chains of Condemnation for 3s; heavy damage if moving type: 'StartsUsing', netRegex: { id: ['AC20', 'AC26'], source: 'Eminent Grief', capture: true }, - delaySeconds: (_data, matches) => parseFloat(matches.castTime) - 3, - durationSeconds: 6, + delaySeconds: (_data, matches) => parseFloat(matches.castTime) - 5, + countdownSeconds: 5, + durationSeconds: 8, alertText: (_data, _matches, outputs) => outputs.text!(), outputStrings: { text: { @@ -62,10 +64,10 @@ const triggerSet: TriggerSet = { { id: 'PT 99 Devoured Eater Bounds of Sin', // applies 119E Bind for 3s + // AC33 = sequential damage cast, may have good position data for dodge direction type: 'Ability', netRegex: { id: 'AC32', source: 'Devoured Eater', capture: false }, delaySeconds: 3, - suppressSeconds: 1, response: Responses.moveAway('alert'), }, { @@ -74,7 +76,7 @@ const triggerSet: TriggerSet = { type: 'HeadMarker', netRegex: { id: '00EA', capture: true }, condition: Conditions.targetIsYou(), - alertText: (_data, _matches, outputs) => outputs.text!(), + alertText: (_data, _matches, output) => output.text!(), outputStrings: { text: { en: 'Laser on YOU', @@ -86,7 +88,7 @@ const triggerSet: TriggerSet = { id: 'PT 99 Eminent Grief Spinelash', type: 'StartsUsing', netRegex: { id: 'B03E', source: 'Eminent Grief', capture: false }, - infoText: (_data, _matches, outputs) => outputs.text!(), + infoText: (_data, _matches, output) => output.text!(), outputStrings: { text: { en: 'Avoid laser', @@ -103,11 +105,14 @@ const triggerSet: TriggerSet = { }, { id: 'PT 99 Eminent Grief Drain Aether', - // AC3[BD] = failstate casts? + // AC38 = short cast + // AC39 = long cast + // [AC3B, AC3D] = failstate casts? type: 'StartsUsing', netRegex: { id: ['AC38', 'AC39'], source: 'Eminent Grief', capture: true }, - delaySeconds: (_data, matches) => parseFloat(matches.castTime) - 3, - alertText: (_data, _matches, outputs) => outputs.text!(), + delaySeconds: (_data, matches) => + matches.id === 'AC38' ? 0 : parseFloat(matches.castTime) - 5, + alertText: (_data, _matches, output) => output.text!(), outputStrings: { text: { en: 'Get Light debuff', @@ -117,10 +122,13 @@ const triggerSet: TriggerSet = { }, { id: 'PT 99 Devoured Eater Drain Aether', + // AC3A = short cast + // AC3C = long cast type: 'StartsUsing', netRegex: { id: ['AC3A', 'AC3C'], source: 'Devoured Eater', capture: true }, - delaySeconds: (_data, matches) => parseFloat(matches.castTime) - 3, - alertText: (_data, _matches, outputs) => outputs.text!(), + delaySeconds: (_data, matches) => + matches.id === 'AC3A' ? 0 : parseFloat(matches.castTime) - 4, + alertText: (_data, _matches, output) => output.text!(), outputStrings: { text: { en: 'Get Dark debuff', @@ -134,6 +142,7 @@ const triggerSet: TriggerSet = { // AC2B = first cast, vertical exaflares, left or right safe // AC2C = second instant cast, horizontal exaflares, back safe // AC2D = second instant cast, vertical exaflares, left or right safe + // AC2E = used approximately 7s after each horizontal/vertical indicator, may have good data for starting positions // AC2F = diamonds glow, exaflares start at end of cast // AC30 = instant, exaflare explosion/damage type: 'Ability', @@ -155,7 +164,7 @@ const triggerSet: TriggerSet = { outputStrings: { text: { en: '${safe}, for later', - ko: '(${safe})', + ko: '(나중에 ${safe})', }, frontSafe: { en: 'Front safe', @@ -167,7 +176,7 @@ const triggerSet: TriggerSet = { }, leftRightSafe: { en: 'Check safe side', - ko: '좌우 안전한 곳 확인해요', + ko: '좌우 안전한 곳 찾아봐요', }, }, }, @@ -195,11 +204,34 @@ const triggerSet: TriggerSet = { outputStrings: { text: { en: '(Spread, later)', - ko: '(구슬 대비, 흩어져요)', + ko: '(곧 구슬, 흩어져요)', }, }, }, ], + timelineReplace: [ + { + 'locale': 'de', + 'replaceSync': { + 'Devoured Eater': 'erodiert(?:e|er|es|en) Sündenvertilger', + 'Eminent Grief': 'Eminent(?:e|er|es|en) Trauer', + }, + }, + { + 'locale': 'fr', + 'replaceSync': { + 'Devoured Eater': 'purgateur dévoré', + 'Eminent Grief': 'Pontife du Chagrin', + }, + }, + { + 'locale': 'ja', + 'replaceSync': { + 'Devoured Eater': '侵蝕された罪喰い', + 'Eminent Grief': 'エミネントグリーフ', + }, + }, + ], }; export default triggerSet; diff --git a/util/gen_coverage_report.ts b/util/gen_coverage_report.ts index e9ce4b49..3c4f79c5 100644 --- a/util/gen_coverage_report.ts +++ b/util/gen_coverage_report.ts @@ -358,6 +358,8 @@ const buildTotals = (coverage: Coverage, missingTranslations: MissingTranslation [ContentType.VCDungeonFinder]: ContentType.Dungeons, // lump chaotic alliance raids with raids for now. [ContentType.ChaoticAllianceRaid]: ContentType.Raids, + // lump Deep Dungeon bonus fights (e.g., The Final Verse) with Deep Dungeons. + [ContentType.DeepDungeonExtras]: ContentType.DeepDungeons, }; const contentType = contentTypeRemap[origContentType] ?? origContentType; diff --git a/util/zone_overrides.ts b/util/zone_overrides.ts index 6cd1f6e3..db06dda6 100644 --- a/util/zone_overrides.ts +++ b/util/zone_overrides.ts @@ -97,9 +97,12 @@ const _SYNTHETIC_IDS: NameKeyToTerritoryId = { 'CuttersCry72': 170, }; -// This will override the data-soourced ContentType with our own value. +// This will override the data-sourced ContentType with our own value. const _SYNTHETIC_CONTENT_TYPE: NameKeyToTerritoryId = { - // empty since 7.1 + 'HallOfTheNovice': 20, + 'SeasonalEvents': 22, + 'TheDiadem': 23, + 'DeepDungeonExtras': 39, }; // Certain collisions will always happen and we don't care because we're not From a1e0e969e1bf78f18e3f69d1ea6b83ef212ff654 Mon Sep 17 00:00:00 2001 From: ksh Date: Tue, 21 Oct 2025 09:51:17 +0900 Subject: [PATCH 6/6] content_type --- resources/content_type.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/resources/content_type.ts b/resources/content_type.ts index f089114c..4ef744a5 100644 --- a/resources/content_type.ts +++ b/resources/content_type.ts @@ -5,6 +5,7 @@ const data = { 'ChaoticAllianceRaid': 37, 'Companions': 12, 'CustomDeliveries': 25, + 'DeepDungeonExtras': 39, 'DeepDungeons': 21, 'DisciplesOfTheHand': 17, 'DisciplesOfTheLand': 16, @@ -17,6 +18,7 @@ const data = { 'GoldSaucer': 19, 'GrandCompany': 11, 'Guildhests': 3, + 'HallOfTheNovice': 20, 'IslandSanctuary': 36, 'Levequests': 10, 'OccultCrescent': 38, @@ -28,7 +30,9 @@ const data = { 'Raids': 5, 'RetainerVentures': 18, 'SaveTheQueen': 29, + 'SeasonalEvents': 22, 'SocietyQuests': 13, + 'TheDiadem': 23, 'TheHunt': 33, 'TheMaskedCarnivale': 27, 'TreasureHunt': 9,