Skip to content

Commit

Permalink
raidboss: add The Forecaster (S-rank: Living Memory) (OverlayPlugin#401)
Browse files Browse the repository at this point in the history
  • Loading branch information
wexxlee authored Sep 3, 2024
1 parent 4530446 commit 1c56b9b
Showing 1 changed file with 103 additions and 2 deletions.
105 changes: 103 additions & 2 deletions ui/raidboss/data/07-dt/hunts/living_memory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import ZoneId from '../../../../../resources/zone_id';
import { RaidbossData } from '../../../../../types/data';
import { TriggerSet } from '../../../../../types/trigger';

// TODO: Add triggers for The Forecaster (S-Rank)
// TODO: Code Execution/Reverse Code: possibly add individual step callouts?
// TODO: Sally - Code Execution/Reverse Code: possibly add individual step callouts?

type ExecutionSafe = 'out' | 'in' | 'cardinals';
const executionIdToSafeMap: { [id: string]: ExecutionSafe } = {
Expand All @@ -22,15 +21,36 @@ const executionOutputStrings = {
next: Outputs.next,
} as const;

type ForecastSafe = 'under' | 'out' | 'behind' | 'intercards';
const forecastNpcYellMap: { [id: string]: ForecastSafe[] } = {
'425E': ['under', 'out', 'intercards'], // "Reacquiring data... Searing sunshine will give way to electric storms, followed by the coldest of cold snaps!"
'425F': ['out', 'behind', 'under'], // "Reacquiring data... Thunderclouds will give way to ferocious gales then sizzling heat!"
'4260': ['intercards', 'out', 'behind'], // "Reacquiring data... Blinding blizzards will turn to lightning storms, followed by tremendous gales!"
'4261': ['behind', 'under', 'out'], // "Reacquiring data... A howling tempest will stoke raging wildfires, followed by an almighty thunderstorm!"
};
const forecastNpcYellIds = Object.keys(forecastNpcYellMap);

const climateChangeNpcYellMap: { [id: string]: Partial<Record<ForecastSafe, ForecastSafe>> } = {
// id: { replacedEffect: newEffect }
'428A': { 'intercards': 'under' }, // "<bleep> ErRoR! Revising forecast... Expect wildfire conditions, not wintry woe!"
'428B': { 'behind': 'intercards' }, // "<bloop> eRrOr! Revising forecast... Expect blizzard conditions, not violent zephyrs!"
'428C': { 'under': 'out' }, // "<bleep> erRoR! Revising forecast... Expect hyperelectricity, not searing sunshine!"
'428D': { 'out': 'behind' }, // "<bloop> ErROr! Revising forecast... Expect gale-force winds, not shocking storms!"
};
const climateChangeNpcYellIds = Object.keys(climateChangeNpcYellMap);
const weatherChannelCastIds = ['967C', '967E', '9680', '9682'];

export interface Data extends RaidbossData {
executionSafe: ExecutionSafe[];
forecastSafe: ForecastSafe[];
}

const triggerSet: TriggerSet<Data> = {
id: 'LivingMemory',
zoneId: ZoneId.LivingMemory,
initData: () => ({
executionSafe: [],
forecastSafe: [],
}),
triggers: [
// ****** A-RANK: Cat's Eye ****** //
Expand Down Expand Up @@ -85,6 +105,7 @@ const triggerSet: TriggerSet<Data> = {
},
},
},

// ****** A-RANK: Sally the Sweeper ****** //
{
id: 'Hunt Sally Execution Model Collect',
Expand Down Expand Up @@ -127,7 +148,87 @@ const triggerSet: TriggerSet<Data> = {
run: (data) => data.executionSafe = [],
outputStrings: executionOutputStrings,
},

// ****** S-RANK: The Forecaster ****** //
{
id: 'Hunt The Forecaster Wildfire Conditions',
type: 'StartsUsing',
netRegex: { id: '9684', source: 'The Forecaster', capture: false },
response: Responses.getUnder('alert'),
},
{
id: 'Hunt The Forecaster Hyperelectricity',
type: 'StartsUsing',
netRegex: { id: '9685', source: 'The Forecaster', capture: false },
response: Responses.outOfMelee('alert'),
},
{
id: 'Hunt The Forecaster Gale-force Winds',
type: 'StartsUsing',
netRegex: { id: '9686', source: 'The Forecaster', capture: false },
response: Responses.getBehind(),
},
{
id: 'Hunt The Forecaster Blizzard Conditions',
type: 'StartsUsing',
netRegex: { id: '9687', source: 'The Forecaster', capture: false },
alertText: (_data, _matches, output) => output.intercards!(),
outputStrings: {
intercards: Outputs.intercards,
},
},
// Forecaster announces three weather effects via `NpcYell`, and then applies 3 buffs
// with 'Forecast'. (There are only 4 possible sequences based on `NpcYell` entries.)
// Forecaster may also follow with an `NpcYell` message indicating one of the 3 buffs
// will be swapped to a different effect, and will use 'Climate Change' to apply a new
// buff indicating the new effect (although the old buff remains active).
// Again, based on NpcYell entries, however, there are only 4 possible substitutions.
// So we can get all we need from the initial (and, if present, subsequent) NpcYell message.
{
id: 'Hunt The Forecaster Forecast Collect',
type: 'NpcYell',
netRegex: { npcNameId: '347D', npcYellId: forecastNpcYellIds },
run: (data, matches) => {
const safe = forecastNpcYellMap[matches.npcYellId];
if (safe === undefined)
throw new UnreachableCode();
data.forecastSafe = safe;
},
},
{
id: 'Hunt The Forecaster Climate Change Collect',
type: 'NpcYell',
netRegex: { npcNameId: '347D', npcYellId: climateChangeNpcYellIds },
run: (data, matches) => {
const swapMap = climateChangeNpcYellMap[matches.npcYellId];
if (swapMap === undefined)
throw new UnreachableCode();
data.forecastSafe = data.forecastSafe.map((effect) => swapMap[effect] || effect);
},
},
{
id: 'Hunt The Forecaster Weather Channel',
type: 'StartsUsing',
// There are 4 possible cast ids for Weather Channel, and each corresponds to
// the first attack in the sequence. But due to Climate Change, we can't reliably
// use the cast id to predict the final sequence, so just trigger on all of them.
netRegex: { id: weatherChannelCastIds, source: 'The Forecaster', capture: false },
durationSeconds: 11,
alertText: (data, _matches, output) => {
const safe = data.forecastSafe;
if (safe.length !== 3)
return;
return safe.map((spot) => output[spot]!()).join(output.next!());
},
run: (data) => data.forecastSafe = [],
outputStrings: {
under: Outputs.getUnder,
out: Outputs.out,
behind: Outputs.getBehind,
intercards: Outputs.intercards,
next: Outputs.next,
},
},
],
timelineReplace: [
{
Expand Down

0 comments on commit 1c56b9b

Please sign in to comment.