From 24c0683bdbf22118c9e7b86291860073a2f1027d Mon Sep 17 00:00:00 2001 From: Dowon Date: Wed, 9 Sep 2026 13:56:30 +0900 Subject: [PATCH 1/2] resources: add Beastmaster --- plugin/CactbotEventSource/FFXIVProcess.cs | 1 + resources/party.ts | 8 +++++++ resources/util.ts | 24 +++++++++++++++----- test/unittests/util_test.ts | 5 ++++ types/job.d.ts | 3 ++- ui/jobs/components/index.ts | 1 + ui/raidboss/emulator/ui/EmulatedPartyInfo.ts | 1 + 7 files changed, 36 insertions(+), 7 deletions(-) diff --git a/plugin/CactbotEventSource/FFXIVProcess.cs b/plugin/CactbotEventSource/FFXIVProcess.cs index 6f592c3c7e6..6d3dd3827b1 100644 --- a/plugin/CactbotEventSource/FFXIVProcess.cs +++ b/plugin/CactbotEventSource/FFXIVProcess.cs @@ -109,6 +109,7 @@ public enum EntityJob : byte { SGE = 40, VPR = 41, PCT = 42, + BST = 43, }; static internal bool IsGatherer(EntityJob job) { diff --git a/resources/party.ts b/resources/party.ts index 1bb4c7d30e3..9de383ebaa5 100644 --- a/resources/party.ts +++ b/resources/party.ts @@ -461,6 +461,10 @@ const jobLocalizedAbbr: Record = { ko: '픽토', tc: '繪靈', }, + BST: { + en: 'BST', + ko: '마수', + }, }; const jobLocalizedFull: Record = { @@ -851,6 +855,10 @@ const jobLocalizedFull: Record = { ko: '픽토맨서', tc: '繪靈法師', }, + BST: { + en: 'Beastmaster', + ko: '마수조련사', + }, }; export default class PartyTracker { diff --git a/resources/util.ts b/resources/util.ts index 08928a63842..8e734506f01 100644 --- a/resources/util.ts +++ b/resources/util.ts @@ -56,6 +56,7 @@ const nameToJobEnum: Record = { SGE: 40, VPR: 41, PCT: 42, + BST: 43, }; export const allJobs = Object.keys(nameToJobEnum) as Job[]; @@ -63,20 +64,31 @@ const allRoles = ['tank', 'healer', 'dps', 'crafter', 'gatherer', 'none'] as Rol export const tankJobs: Job[] = ['GLA', 'PLD', 'MRD', 'WAR', 'DRK', 'GNB']; export const healerJobs: Job[] = ['CNJ', 'WHM', 'SCH', 'AST', 'SGE']; -export const meleeDpsJobs: Job[] = ['PGL', 'MNK', 'LNC', 'DRG', 'ROG', 'NIN', 'SAM', 'RPR', 'VPR']; +export const meleeDpsJobs: Job[] = [ + 'PGL', + 'MNK', + 'LNC', + 'DRG', + 'ROG', + 'NIN', + 'SAM', + 'RPR', + 'VPR', + 'BST', +]; export const rangedDpsJobs: Job[] = ['ARC', 'BRD', 'DNC', 'MCH']; export const casterDpsJobs: Job[] = ['BLU', 'RDM', 'BLM', 'SMN', 'ACN', 'THM', 'PCT']; export const dpsJobs: Job[] = [...meleeDpsJobs, ...rangedDpsJobs, ...casterDpsJobs]; export const craftingJobs: Job[] = ['CRP', 'BSM', 'ARM', 'GSM', 'LTW', 'WVR', 'ALC', 'CUL']; export const gatheringJobs: Job[] = ['MIN', 'BTN', 'FSH']; -export const limitedJobs: Job[] = ['BLU']; +export const limitedJobs: Job[] = ['BLU', 'BST']; const stunJobs: Job[] = ['BLU', ...tankJobs, ...meleeDpsJobs]; -const silenceJobs: Job[] = ['BLU', ...tankJobs, ...rangedDpsJobs]; -const sleepJobs: Job[] = [...casterDpsJobs, ...healerJobs]; -const feintJobs: Job[] = [...meleeDpsJobs]; +const silenceJobs: Job[] = ['BLU', 'BST', ...tankJobs, ...rangedDpsJobs]; +const sleepJobs: Job[] = ['BST', ...casterDpsJobs, ...healerJobs]; +const feintJobs: Job[] = meleeDpsJobs.filter((job) => job !== 'BST'); const addleJobs: Job[] = [...casterDpsJobs]; -const cleanseJobs: Job[] = ['BLU', 'BRD', ...healerJobs]; +const cleanseJobs: Job[] = ['BLU', 'BRD', 'BST', ...healerJobs]; const jobToRoleMap: Map = (() => { const addToMap = (map: Map, jobs: Job[], role: Role) => { diff --git a/test/unittests/util_test.ts b/test/unittests/util_test.ts index 816ca76b3aa..254ac21fd82 100644 --- a/test/unittests/util_test.ts +++ b/test/unittests/util_test.ts @@ -41,6 +41,11 @@ const jobs: JobInfo[] = ((): JobInfo[] => { 'Stun', ); + // Beastmaster has its own utility actions instead of role actions. + const beastmaster = jobs.find((job) => job.name === 'BST'); + if (beastmaster) + beastmaster.actions = ['Cleanse', 'Silence', 'Sleep', 'Stun']; + return jobs; })(); diff --git a/types/job.d.ts b/types/job.d.ts index 50d6fbd3192..5ffda5b8b1f 100644 --- a/types/job.d.ts +++ b/types/job.d.ts @@ -41,6 +41,7 @@ export type Job = | 'RPR' | 'SGE' | 'VPR' - | 'PCT'; + | 'PCT' + | 'BST'; export type Role = 'tank' | 'healer' | 'dps' | 'crafter' | 'gatherer' | 'none'; diff --git a/ui/jobs/components/index.ts b/ui/jobs/components/index.ts index 1c26a2bfeb0..44338372c40 100644 --- a/ui/jobs/components/index.ts +++ b/ui/jobs/components/index.ts @@ -59,6 +59,7 @@ const ComponentMap: Record = { SAM: SAMComponent, RPR: RPRComponent, VPR: VPRComponent, + BST: BaseComponent, // ranged dps ARC: BRDComponent, BRD: BRDComponent, diff --git a/ui/raidboss/emulator/ui/EmulatedPartyInfo.ts b/ui/raidboss/emulator/ui/EmulatedPartyInfo.ts index dc262740e7c..c7d9958c1be 100644 --- a/ui/raidboss/emulator/ui/EmulatedPartyInfo.ts +++ b/ui/raidboss/emulator/ui/EmulatedPartyInfo.ts @@ -24,6 +24,7 @@ const jobOrder = [ 'SAM', 'RPR', 'VPR', + 'BST', 'BRD', 'MCH', 'DNC', From 2114c4130fba71c4ce3d3bb07db2a8857c2ce833 Mon Sep 17 00:00:00 2001 From: Dowon Date: Wed, 9 Sep 2026 18:00:25 +0900 Subject: [PATCH 2/2] add Beastmaster translations --- resources/party.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/resources/party.ts b/resources/party.ts index 9de383ebaa5..671d6303cbc 100644 --- a/resources/party.ts +++ b/resources/party.ts @@ -463,6 +463,10 @@ const jobLocalizedAbbr: Record = { }, BST: { en: 'BST', + de: 'BST', + fr: 'DRE', + ja: 'BST', // FIXME + cn: 'BST', // FIXME ko: '마수', }, }; @@ -857,6 +861,10 @@ const jobLocalizedFull: Record = { }, BST: { en: 'Beastmaster', + de: 'Bestienbändiger', + fr: 'dresseur', + ja: '魔獣使い', + cn: '驯兽师', ko: '마수조련사', }, };