Skip to content

Commit

Permalink
speculative spring crystals 1 + 2
Browse files Browse the repository at this point in the history
  • Loading branch information
quisquous committed Nov 5, 2023
1 parent ee2237c commit ee0d3e5
Show file tree
Hide file tree
Showing 2 changed files with 192 additions and 3 deletions.
27 changes: 25 additions & 2 deletions ui/oopsyraidsy/data/06-ew/dungeon/another_aloalo_island.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,34 @@
import NetRegexes from '../../../../../resources/netregexes';
import ZoneId from '../../../../../resources/zone_id';
import { OopsyData } from '../../../../../types/data';
import { OopsyTriggerSet } from '../../../../../types/oopsy';
import { OopsyMistakeType, OopsyTrigger, OopsyTriggerSet } from '../../../../../types/oopsy';

// TODO: people who missed their 8AC2 Burst tower

export type Data = OopsyData;

// TODO: we could probably move this helper to some oopsy util.
const nonzeroDamageMistake = (
triggerId: string,
abilityId: string | string[],
type: OopsyMistakeType,
): OopsyTrigger<OopsyData> => {
return {
id: triggerId,
type: 'Ability',
netRegex: NetRegexes.ability({ id: abilityId }),
condition: (data, matches) => data.DamageFromMatches(matches) > 0,
mistake: (_data, matches) => {
return {
type: type,
blame: matches.target,
reportId: matches.targetId,
text: matches.ability,
};
},
};
};

const triggerSet: OopsyTriggerSet<Data> = {
zoneId: ZoneId.AnotherAloaloIsland,
damageWarn: {
Expand Down Expand Up @@ -36,8 +58,8 @@ const triggerSet: OopsyTriggerSet<Data> = {
'AAI Hydrobullet': '8ABA', // spread debuffs
},
soloWarn: {
'AAI Hydrofall': '8AB7', // partner stack debuffs
'AAI Snipper Water III': '8C64', // Snipper stack marker
'AAI Hydrofall': '8AB7', // partner stack debuffs
},
triggers: [
{
Expand All @@ -60,6 +82,7 @@ const triggerSet: OopsyTriggerSet<Data> = {
};
},
},
nonzeroDamageMistake('AAI Hundred Lashings', ['8AC9', '8ACB'], 'warn'),
],
};

Expand Down
168 changes: 167 additions & 1 deletion ui/raidboss/data/06-ew/dungeon/another_aloalo_island.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,29 @@ import { Responses } from '../../../../../resources/responses';
import ZoneId from '../../../../../resources/zone_id';
import { RaidbossData } from '../../../../../types/data';
import { PluginCombatantState } from '../../../../../types/event';
import { NetMatches } from '../../../../../types/net_matches';
import { TriggerSet } from '../../../../../types/trigger';

// TODO: use code from AMR to handle cases of "role stacks" when somebody is dead
// TODO: switch crystals 1 to always tell you what fetters do

export interface Data extends RaidbossData {
readonly triggerSetConfig: {
stackOrder: 'meleeRolesPartners' | 'rolesPartners';
};
combatantData: PluginCombatantState[];
ketuSpringCrystalCount: number;
ketuCrystalAdd: NetMatches['AddedCombatant'][];
ketuHydroBuffCount: number;
ketuBuff?: 'bubble' | 'fetters';
}

// Horizontal crystals have a heading of 0, vertical crystals are -pi/2.
const isHorizontalCrystal = (line: NetMatches['AddedCombatant']) => {
const epsilon = 0.1;
return Math.abs(parseFloat(line.heading)) < epsilon;
};

const triggerSet: TriggerSet<Data> = {
id: 'AnotherAloaloIsland',
zoneId: ZoneId.AnotherAloaloIsland,
Expand All @@ -23,6 +35,7 @@ const triggerSet: TriggerSet<Data> = {
return {
combatantData: [],
ketuSpringCrystalCount: 0,
ketuCrystalAdd: [],
ketuHydroBuffCount: 0,
};
},
Expand Down Expand Up @@ -113,12 +126,28 @@ const triggerSet: TriggerSet<Data> = {
netRegex: { id: '8AD4', source: 'Ketuduke', capture: false },
response: Responses.bleedAoe(),
},
{
id: 'AAI Ketuduke Spring Crystals',
type: 'StartsUsing',
netRegex: { id: '8AA8', source: 'Ketuduke', capture: false },
run: (data) => {
data.ketuSpringCrystalCount++;
data.ketuCrystalAdd = [];
},
},
{
id: 'AAI Ketuduke Spring Crystal Collect',
type: 'AddedCombatant',
netRegex: { npcNameId: '12607' },
run: (data, matches) => data.ketuCrystalAdd.push(matches),
},
{
id: 'AAI Ketuduke Foamy Fetters',
type: 'GainsEffect',
netRegex: { effectId: 'ECC' },
condition: Conditions.targetIsYou(),
alertText: (_data, _matches, output) => output.text!(),
run: (data) => data.ketuBuff = 'fetters',
outputStrings: {
text: {
en: 'Fetters',
Expand All @@ -131,6 +160,7 @@ const triggerSet: TriggerSet<Data> = {
netRegex: { effectId: 'E9F' },
condition: Conditions.targetIsYou(),
alertText: (_data, _matches, output) => output.text!(),
run: (data) => data.ketuBuff = 'bubble',
outputStrings: {
text: {
en: 'Bubble',
Expand All @@ -147,17 +177,112 @@ const triggerSet: TriggerSet<Data> = {
},
{
id: 'AAI Ketuduke Hydro Buff 1',
comment: {
en: `These directions assume that you always pick a square in the same
quadrant as the crystal specified.
For brevity, "next to" always means horizontal east/west of something.
The number in parentheses is the limit cut wind you should be on.`,
},
type: 'StartsUsing',
netRegex: { id: ['8AB8', '8AB4'], source: 'Ketuduke' },
condition: (data) => data.ketuHydroBuffCount === 1,
alertText: (_data, matches, output) => {
durationSeconds: 8,
alertText: (data, matches, output) => {
// If somebody died and missed a debuff, good luck.
if (data.ketuBuff === undefined)
return;

// Bubble always does the same thing.
if (data.ketuBuff === 'bubble')
return output.bubble!();

// Two layouts, one with each crystal in its own column ("split")
// and one with two columns that have an H and a V in that same column ("columns").
// Wind doesn't matter, as "1" will always be on the horizontal crystals.
//
// STACK FETTERS COLUMNS (kitty to horizontal)
// stack, counter_nw2
// 2 2
// + - - - - + + - - - - +
// | V f | 1 | H f | 1
// | H b | => | V |
// | H b | | V |
// 1 | f V | 1 | H f |
// + - - - - + + - - - - +
// 2 2
//
// STACK FETTERS SPLIT (on horizontal)
// stack, clock_nw1
// 2 2
// + - - - - + + - - - - +
// 1 | V | 1 | V |
// | H b | => | f H |
// | V | | V |
// | b H | 1 | H f | 1
// + - - - - + + - - - - +
// 2 2
//
// SPREAD FETTERS COLUMNS (adjacent to vertical)
// spread, counter_nw2
// 2 2
// + - - - - + + - - - - +
// | V f | 1 | f H b | 1
// | H b | => | V |
// | H b | | V |
// 1 | V f | 1 | H b f |
// + - - - - + + - - - - +
// 2 2
//
// SPREAD FETTERS SPLIT (kitty to vertical)
// spread, counter_nw2
// 2 2
// + - - - - + + - - - - +
// | V | 1 | V | 1
// | f b H | => | f H b |
// | V | | V |
// 1 | H b f | 1 | b H f |
// + - - - - + + - - - - +
// 2 2

const isSpread = matches.id === '8AB8';
const horizontal = data.ketuCrystalAdd.filter((x) => isHorizontalCrystal(x));
const vertical = data.ketuCrystalAdd.filter((x) => !isHorizontalCrystal(x));

const [firstHorizontal] = horizontal;
if (horizontal.length !== 2 || vertical.length !== 2 || firstHorizontal === undefined)
return;
const firstHorizX = parseFloat(firstHorizontal.x);
// It's split if no vertical is in the same column as either horizontal.
const isSplitLayout =
vertical.find((line) => Math.abs(parseFloat(line.x) - firstHorizX) < 1) === undefined;

if (isSpread)
return isSplitLayout ? output.fettersSpreadSplit!() : output.fettersSpreadColumn!();
return isSplitLayout ? output.fettersStackSplit!() : output.fettersStackColumn!();
},
infoText: (_data, matches, output) => {
return matches.id === '8AB8' ? output.spread!() : output.stacks!();
},
outputStrings: {
spread: Outputs.spread,
stacks: {
en: 'Stacks',
},
bubble: {
en: 'Next to Horizontal (1)',
},
fettersSpreadSplit: {
en: 'Diagonal of Vertical (2)',
},
fettersSpreadColumn: {
en: 'Next to Vertical (2)',
},
fettersStackSplit: {
en: 'On Horizontal (1)',
},
fettersStackColumn: {
en: 'Diagonal of Horizontal (1)',
},
},
},
{
Expand Down Expand Up @@ -189,6 +314,47 @@ const triggerSet: TriggerSet<Data> = {
netRegex: { id: '8ACE', source: 'Ketuduke', capture: false },
response: Responses.getInThenOut(),
},
{
id: 'AAI Ketuduke Spring Crystals 2',
type: 'AddedCombatant',
netRegex: { npcNameId: '12607', capture: false },
condition: (data) => data.ketuSpringCrystalCount === 2 && data.ketuCrystalAdd.length === 4,
alertText: (data, _matches, output) => {
const horizontal = data.ketuCrystalAdd.filter((x) => isHorizontalCrystal(x));
const vertical = data.ketuCrystalAdd.filter((x) => !isHorizontalCrystal(x));
if (horizontal.length !== 2 || vertical.length !== 2)
return;

// Crystal positions are always -15, -5, 5, 15.

// Check if any verticals are on the outer vertical edges.
for (const line of vertical) {
const y = parseFloat(line.y);
if (y < -10 || y > 10)
return output.eastWestSafe!();
}

// Check if any horizontals are on the outer horizontal edges.
for (const line of horizontal) {
const x = parseFloat(line.x);
if (x < -10 || x > 10)
return output.northSouthSafe!();
}

return output.cornersSafe!();
},
outputStrings: {
northSouthSafe: {
en: 'North/South',
},
eastWestSafe: {
en: 'East/West',
},
cornersSafe: {
en: 'Corners',
},
},
},
],
timelineReplace: [
{
Expand Down

0 comments on commit ee0d3e5

Please sign in to comment.