Skip to content

Commit 540858b

Browse files
committed
Merge remote-tracking branch 'upstream/main'
2 parents 4b28fd1 + ca8fdb3 commit 540858b

1 file changed

Lines changed: 267 additions & 1 deletion

File tree

ui/raidboss/data/07-dt/eureka/occult_crescent_south_horn.ts

Lines changed: 267 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import { TriggerSet } from '../../../../../types/trigger';
1111

1212
export interface Data extends RaidbossData {
1313
ce?: string;
14+
phantomJob?: string;
15+
phantomJobLevel?: number;
1416
sisyphusResoundingMemoryWedge?: 'intercards' | 'cardinals' | 'unknown';
1517
demonTabletChiselTargets: string[];
1618
demonTabletRotationCounter: number;
@@ -413,6 +415,171 @@ const magitaurOutputStrings = {
413415
},
414416
};
415417

418+
// Used to filter the GainsEffect
419+
const phantomJobEffectIds = [
420+
'1092', // Freelancer
421+
'1106', // Knight
422+
'1107', // Berserker
423+
'1108', // Monk
424+
'1109', // Ranger
425+
'1110', // Oracle
426+
'1111', // Thief
427+
'110A', // Samurai
428+
'110B', // Bard
429+
'110C', // Geomancer
430+
'110D', // Time Mage
431+
'110E', // Cannonneer
432+
'110F', // Chemist
433+
'12C3', // Mystic Knight
434+
'12C4', // Gladiator
435+
'12C5', // Dancer
436+
];
437+
438+
// Useful for matching on job name in condition trigger
439+
const phantomJobData = {
440+
'freelancer': '1092',
441+
'knight': '1106',
442+
'berserker': '1107',
443+
'monk': '1108',
444+
'ranger': '1109',
445+
'oracle': '1110',
446+
'thief': '1111',
447+
'samurai': '110A',
448+
'bard': '110B',
449+
'geomancer': '110C',
450+
'timeMage': '110D',
451+
'cannoneer': '110E',
452+
'chemist': '110F',
453+
'mysticKnight': '12C3',
454+
'gladiator': '12C4',
455+
'dancer': '12C5',
456+
} as const;
457+
458+
// Return if the player has a phantom job that can dispel
459+
// Phantom Time Mage Lv 4: Dispel
460+
const phantomCanDispel = (
461+
phantomJob: string,
462+
phantomJobLevel: number,
463+
): boolean => {
464+
if (phantomJob === phantomJobData.timeMage && phantomJobLevel >= 4)
465+
return true;
466+
return false;
467+
};
468+
469+
// Return if the player has a phantom job that can slow
470+
// Phantom Time Mage Lv 1: Slowga
471+
/*
472+
const phantomCanSlow = (
473+
phantomJob: string,
474+
phantomJobLevel: number,
475+
): boolean => {
476+
if (phantomJob === phantomJobData.timeMage && phantomJobLevel >= 1)
477+
return true;
478+
return false;
479+
};
480+
*/
481+
482+
// Return if the player has a phantom job that can cleanse
483+
// Phantom Oracle Lv 2: Recuperation
484+
const phantomCanCleanse = (
485+
phantomJob: string,
486+
phantomJobLevel: number,
487+
): boolean => {
488+
if (phantomJob === phantomJobData.oracle && phantomJobLevel >= 2)
489+
return true;
490+
return false;
491+
};
492+
493+
// Return if the player has a phantom job that can freeze time
494+
// Phantom Bard Lv 2: Romeo's Ballad (aoe)
495+
// Phantom Dancer Lv 1 may be able to use Dance with Tempting Tango proc (single-target)
496+
const phantomCanFreeze = (
497+
phantomJob: string,
498+
phantomJobLevel: number,
499+
): boolean => {
500+
if (phantomJob === phantomJobData.bard && phantomJobLevel >= 2)
501+
return true;
502+
if (phantomJob === phantomJobData.dancer && phantomJobLevel >= 1)
503+
return true;
504+
return false;
505+
};
506+
507+
// Return if the player has a phantom job that can suspend
508+
// Phantom Geomancer Lv 4: Suspend
509+
/*
510+
const phantomCanSuspend = (
511+
phantomJob: string,
512+
phantomJobLevel: number,
513+
): boolean => {
514+
if (phantomJob === phantomJobData.geomancer && phantomJobLevel >= 4)
515+
return true;
516+
return false;
517+
};
518+
*/
519+
520+
// Return if the player has a phantom job that can reduce tankbuster
521+
// Phantom Knight Lv 4: Phantom Guard + Enhanced Phantom Guard (90%)
522+
// Phantom Knight Lv 6: Pledge
523+
// Phantom Oracle Lv 6: Invulnerability
524+
// Phantom Dancer Lv 3: Steadfast Dance (10% MaxHP Barrier)
525+
// Phantom Dancer Lv 4: Mesmerize (40%)
526+
// Phantom Mystic Knight Lv 2: Magic Shell (20% MaxHP Barrier of caster)
527+
// Phantom Gladiator Lv 2: Defend (50%)
528+
/*
529+
const phantomCaresAboutTankbuster = (
530+
phantomJob: string,
531+
phantomJobLevel: number,
532+
): boolean => {
533+
if (phantomJob === phantomJobData.knight && phantomJobLevel >= 4)
534+
return true;
535+
if (phantomJob === phantomJobData.oracle && phantomJobLevel >= 6)
536+
return true;
537+
if (phantomJob === phantomJobData.dancer && phantomJobLevel >= 3)
538+
return true;
539+
if (phantomJob === phantomJobData.mysticKnight && phantomJobLevel >= 2)
540+
return true;
541+
if (phantomJob === phantomJobData.gladiator && phantomJobLevel >= 2)
542+
return true;
543+
return false;
544+
};
545+
*/
546+
547+
// Return if the player has a phantom job that can block physical damage
548+
// Phantom Samurai Lv 2: Shirahadori
549+
// Phantom Oracle Lv 6: Invulnerability
550+
/*
551+
const phantomCanBlockPhysical = (
552+
phantomJob: string,
553+
phantomJobLevel: number,
554+
): boolean => {
555+
if (phantomJob === phantomJobData.samurai && phantomJobLevel >= 2)
556+
return true;
557+
if (phantomJob === phantomJobData.oracle && phantomJobLevel >= 6)
558+
return true;
559+
return false;
560+
};
561+
*/
562+
563+
// Return if the player has a phantom job that helps with enemy aoes
564+
// Phantom Bard Lv 3: Mighty March (+20% MaxHP)
565+
// Phantom Ranger Lv 6: Occult Unicorn (40k AoE Shield)
566+
// Phantom Dancer Lv 4: Mesmerize (Require's target, 4s 40% damage reduction then 100s 10% damage reduction)
567+
// Phantom Geomance Lv 2 may be able to use Weather with Blessed Rain, Misty Mirage, Sunbath, or Cloudy Caress effects
568+
/*
569+
const phantomCaresAboutAOE = (
570+
phantomJob: string,
571+
phantomJobLevel: number,
572+
): boolean => {
573+
if (phantomJob === phantomJobData.bard && phantomJobLevel >= 3)
574+
return true;
575+
if (phantomJob === phantomJobData.ranger && phantomJobLevel >= 6)
576+
return true;
577+
if (phantomJob === phantomJobData.dancer && phantomJobLevel >= 4)
578+
return true;
579+
return false;
580+
};
581+
*/
582+
416583
const triggerSet: TriggerSet<Data> = {
417584
id: 'TheOccultCrescentSouthHorn',
418585
zoneId: ZoneId.TheOccultCrescentSouthHorn,
@@ -693,6 +860,42 @@ const triggerSet: TriggerSet<Data> = {
693860
console.log(`Start CE: ??? (${ceId})`);
694861
},
695862
},
863+
{
864+
id: 'Occult Crescent Phantom Job Tracker',
865+
// count also contains a Phantom Job id and level, it's supposed to be two bytes but has weird padding in logs
866+
// Expecting first two characters to be part of Phantom Job id, and the later two to be the level
867+
// First digit is the job:
868+
// Dancer = F
869+
// Gladiator = E
870+
// Mystic Knight = D
871+
// Thief = C
872+
// Oracle = B
873+
// Chemist = A
874+
// Cannoneer = 9
875+
// Time Mage = 8
876+
// Geomancer = 7
877+
// Bard = 6
878+
// Samurai = 5
879+
// Ranger = 4
880+
// Monk = 3
881+
// Berserker = 2
882+
// Knight = 1
883+
// Freelancer = null
884+
// Freelancer level is accumulation of maxed jobs +1, can also be inferred from stacks of Phantom Mastery (1082)
885+
type: 'GainsEffect',
886+
netRegex: { effectId: [...phantomJobEffectIds], capture: true },
887+
condition: Conditions.targetIsYou(),
888+
run: (data, matches) => {
889+
data.phantomJob = matches.effectId;
890+
const jobData = matches.count?.padStart(4, '0');
891+
892+
// Assuming this isn't possible given the filter on statuses
893+
if (jobData === undefined)
894+
return;
895+
896+
data.phantomJobLevel = parseInt(jobData.slice(2), 16);
897+
},
898+
},
696899
{
697900
id: 'Occult Crescent Forked Tower: Blood Clear Data',
698901
type: 'SystemLogMessage',
@@ -762,6 +965,29 @@ const triggerSet: TriggerSet<Data> = {
762965
netRegex: { source: 'Crescent Berserker', id: 'A6C3', capture: false },
763966
response: Responses.getBehind(),
764967
},
968+
{
969+
id: 'Occult Crescent Crescent Berserker Damage Up',
970+
// Crescent Berserker gains Damage Up (20s) from Channeled Rage (7846)
971+
// Crescent Berserker gains Damage Up (40s) from Heightened Rage (93B1)
972+
type: 'GainsEffect',
973+
netRegex: { effectId: '3D', target: 'Crescent Berserker', capture: true },
974+
condition: (data) => {
975+
if (data.phantomJob === undefined || data.phantomJobLevel === undefined)
976+
return false;
977+
return phantomCanDispel(data.phantomJob, data.phantomJobLevel);
978+
},
979+
infoText: (_data, matches, output) => output.dispel!({ name: matches.target }),
980+
outputStrings: {
981+
dispel: {
982+
en: 'Dispel ${name}',
983+
de: 'Entferne ${name}',
984+
fr: 'Dissipez ${name}',
985+
ja: '${name}にバフ解除',
986+
cn: '驱散 ${name} 的BUFF',
987+
ko: '${name} 버프 해제',
988+
},
989+
},
990+
},
765991
{
766992
id: 'Occult Crescent Hinkypunk Dread Dive',
767993
type: 'StartsUsing',
@@ -811,6 +1037,28 @@ const triggerSet: TriggerSet<Data> = {
8111037
netRegex: { source: 'Neo Garula', id: 'A0E5', capture: true },
8121038
response: Responses.tankBuster(),
8131039
},
1040+
{
1041+
id: 'Occult Crescent Neo Garula Damage Up',
1042+
// Neo Garula gains Damage Up (60s) after casting Agitated Groan
1043+
type: 'GainsEffect',
1044+
netRegex: { effectId: '489', target: 'Neo Garula', capture: true },
1045+
condition: (data) => {
1046+
if (data.phantomJob === undefined || data.phantomJobLevel === undefined)
1047+
return false;
1048+
return phantomCanDispel(data.phantomJob, data.phantomJobLevel);
1049+
},
1050+
infoText: (_data, matches, output) => output.dispel!({ name: matches.target }),
1051+
outputStrings: {
1052+
dispel: {
1053+
en: 'Dispel ${name}',
1054+
de: 'Entferne ${name}',
1055+
fr: 'Dissipez ${name}',
1056+
ja: '${name}にバフ解除',
1057+
cn: '驱散 ${name} 的BUFF',
1058+
ko: '${name} 버프 해제',
1059+
},
1060+
},
1061+
},
8141062
{
8151063
id: 'Occult Crescent Lion Rampant Fearsome Glint',
8161064
type: 'StartsUsing',
@@ -3307,6 +3555,15 @@ const triggerSet: TriggerSet<Data> = {
33073555
// This will count until all 12 have started casting
33083556
type: 'StartsUsing',
33093557
netRegex: { source: 'Tower Idol', id: 'A61F', capture: true },
3558+
condition: (data) => {
3559+
if (
3560+
data.phantomJob === undefined ||
3561+
data.phantomJobLevel === undefined ||
3562+
phantomCanFreeze(data.phantomJob, data.phantomJobLevel)
3563+
)
3564+
return true;
3565+
return false;
3566+
},
33103567
promise: async (data, matches) => {
33113568
const combatants = (await callOverlayHandler({
33123569
call: 'getCombatants',
@@ -4885,7 +5142,16 @@ const triggerSet: TriggerSet<Data> = {
48855142
// TODO: Cleanse call for Doom, but it is not yet logged, it's probably 11CE?
48865143
type: 'GainsEffect',
48875144
netRegex: { effectId: '115C', capture: true },
4888-
condition: Conditions.targetIsYou(),
5145+
condition: (data, matches) => {
5146+
if (
5147+
(data.me === matches.target) &&
5148+
(data.phantomJob === undefined ||
5149+
data.phantomJobLevel === undefined ||
5150+
phantomCanCleanse(data.phantomJob, data.phantomJobLevel))
5151+
)
5152+
return true;
5153+
return false;
5154+
},
48895155
// 25s - 20s, plus some delay for buff/debuff propagation
48905156
delaySeconds: (_data, matches) => parseFloat(matches.duration) - 20 + 0.5,
48915157
suppressSeconds: 1,

0 commit comments

Comments
 (0)