forked from xziino/ff14-mitigator
move all skill/player things into a single file
This commit is contained in:
parent
8f00c22682
commit
0f8a90d1b4
108
js/analysis.js
108
js/analysis.js
@ -1,102 +1,5 @@
|
|||||||
(function () {
|
(function () {
|
||||||
const MITIG_ICONS = {
|
const { MITIG_ICONS, JOB_ABBR, ABILITY_JOBS, JOB_ROLE } = window.FF14_DATA;
|
||||||
// DR buffs
|
|
||||||
'Passage of Arms': 'assets/icons/mitigation/passage-of-arms.png',
|
|
||||||
'Dark Missionary': 'assets/icons/mitigation/dark-missionary.png',
|
|
||||||
'Heart of Light': 'assets/icons/mitigation/heart-of-light.png',
|
|
||||||
'Temperance': 'assets/icons/mitigation/temperance.png',
|
|
||||||
'Sacred Soil': 'assets/icons/mitigation/sacred-soil.png',
|
|
||||||
'Expedient': 'assets/icons/mitigation/expedient.png',
|
|
||||||
'Fey Illumination': 'assets/icons/mitigation/fey-illumination.png',
|
|
||||||
'Collective Unconscious': 'assets/icons/mitigation/collective-unconscious.png',
|
|
||||||
'Holos': 'assets/icons/mitigation/holos.png',
|
|
||||||
'Kerachole': 'assets/icons/mitigation/kerachole.png',
|
|
||||||
'Troubadour': 'assets/icons/mitigation/troubadour.png',
|
|
||||||
'Tactician': 'assets/icons/mitigation/tactician.png',
|
|
||||||
'Shield Samba': 'assets/icons/mitigation/shield-samba.png',
|
|
||||||
'Magick Barrier': 'assets/icons/mitigation/magick-barrier.png',
|
|
||||||
// Debuffs
|
|
||||||
'Reprisal': 'assets/icons/mitigation/reprisal.png',
|
|
||||||
'Feint': 'assets/icons/mitigation/feint.png',
|
|
||||||
'Addle': 'assets/icons/mitigation/addle.png',
|
|
||||||
// Shields
|
|
||||||
'Divine Veil': 'assets/icons/mitigation/divine-veil.png',
|
|
||||||
'Guardian': 'assets/icons/mitigation/guardian.png',
|
|
||||||
'Shake It Off': 'assets/icons/mitigation/shake-it-off.png',
|
|
||||||
'Bloodwhetting': 'assets/icons/mitigation/bloodwhetting.png',
|
|
||||||
'Divine Benison': 'assets/icons/mitigation/divine-benison.png',
|
|
||||||
'Divine Caress': 'assets/icons/mitigation/divine-caress.png',
|
|
||||||
'Intersection': 'assets/icons/mitigation/intersection.png',
|
|
||||||
'Neutral Sect': 'assets/icons/mitigation/neutral-sect.png',
|
|
||||||
'the Spire': 'assets/icons/mitigation/the-spire.png',
|
|
||||||
'Panhaima': 'assets/icons/mitigation/panhaima.png',
|
|
||||||
'Holosakos': 'assets/icons/mitigation/holos.png',
|
|
||||||
'Eukrasian Prognosis': 'assets/icons/mitigation/eukrasian-prognosis.png',
|
|
||||||
'Eukrasian Prognosis II': 'assets/icons/mitigation/eukrasian-prognosis-ii.png',
|
|
||||||
'Eukrasian Diagnosis': 'assets/icons/mitigation/eukrasian-diagnosis.png',
|
|
||||||
'Differential Diagnosis': 'assets/icons/mitigation/eukrasian-diagnosis.png',
|
|
||||||
'Haima': 'assets/icons/mitigation/haima.png',
|
|
||||||
'Galvanize': 'assets/icons/mitigation/galvanize.png',
|
|
||||||
'Seraphic Veil': 'assets/icons/mitigation/seraphic-veil.png',
|
|
||||||
'Radiant Aegis': 'assets/icons/mitigation/radiant-aegis.png',
|
|
||||||
'Tempera Coat': 'assets/icons/mitigation/tempera-coat.png',
|
|
||||||
'Tempera Grassa': 'assets/icons/mitigation/tempera-grassa.png',
|
|
||||||
'Improvised Finish': 'assets/icons/mitigation/improvised-finish.png',
|
|
||||||
};
|
|
||||||
|
|
||||||
const JOB_ABBR = {
|
|
||||||
'Paladin': 'PLD', 'Warrior': 'WAR', 'DarkKnight': 'DRK', 'Gunbreaker': 'GNB',
|
|
||||||
'WhiteMage': 'WHM', 'Scholar': 'SCH', 'Astrologian': 'AST', 'Sage': 'SGE',
|
|
||||||
'Monk': 'MNK', 'Dragoon': 'DRG', 'Ninja': 'NIN', 'Samurai': 'SAM',
|
|
||||||
'Reaper': 'RPR', 'Viper': 'VPR',
|
|
||||||
'Bard': 'BRD', 'Machinist': 'MCH', 'Dancer': 'DNC',
|
|
||||||
'BlackMage': 'BLM', 'Summoner': 'SMN', 'RedMage': 'RDM',
|
|
||||||
'Pictomancer': 'PCT', 'BlueMage': 'BLU',
|
|
||||||
};
|
|
||||||
|
|
||||||
// ability name → jobs that can provide it (for job-based ref comparison)
|
|
||||||
const ABILITY_JOBS = {
|
|
||||||
'Passage of Arms': ['PLD'],
|
|
||||||
'Divine Veil': ['PLD'],
|
|
||||||
'Guardian': ['PLD'],
|
|
||||||
'Reprisal': ['PLD', 'WAR', 'DRK', 'GNB'],
|
|
||||||
'Shake It Off': ['WAR'],
|
|
||||||
'Bloodwhetting': ['WAR'],
|
|
||||||
'Dark Missionary': ['DRK'],
|
|
||||||
'Heart of Light': ['GNB'],
|
|
||||||
'Temperance': ['WHM'],
|
|
||||||
'Divine Benison': ['WHM'],
|
|
||||||
'Divine Caress': ['WHM'],
|
|
||||||
'Sacred Soil': ['SCH'],
|
|
||||||
'Expedient': ['SCH'],
|
|
||||||
'Fey Illumination': ['SCH'],
|
|
||||||
'Galvanize': ['SCH'],
|
|
||||||
'Seraphic Veil': ['SCH'],
|
|
||||||
'Catalyze': ['SCH'],
|
|
||||||
'Collective Unconscious': ['AST'],
|
|
||||||
'Neutral Sect': ['AST'],
|
|
||||||
'Intersection': ['AST'],
|
|
||||||
'the Spire': ['AST'],
|
|
||||||
'Kerachole': ['SGE'],
|
|
||||||
'Holos': ['SGE'],
|
|
||||||
'Holosakos': ['SGE'],
|
|
||||||
'Panhaima': ['SGE'],
|
|
||||||
'Eukrasian Prognosis': ['SGE'],
|
|
||||||
'Eukrasian Prognosis II': ['SGE'],
|
|
||||||
'Eukrasian Diagnosis': ['SGE'],
|
|
||||||
'Differential Diagnosis': ['SGE'],
|
|
||||||
'Haima': ['SGE'],
|
|
||||||
'Troubadour': ['BRD'],
|
|
||||||
'Tactician': ['MCH'],
|
|
||||||
'Shield Samba': ['DNC'],
|
|
||||||
'Improvised Finish': ['DNC'],
|
|
||||||
'Feint': ['MNK', 'DRG', 'NIN', 'SAM', 'RPR', 'VPR'],
|
|
||||||
'Addle': ['BLM', 'SMN', 'RDM', 'PCT'],
|
|
||||||
'Radiant Aegis': ['SMN'],
|
|
||||||
'Magick Barrier': ['RDM'],
|
|
||||||
'Tempera Coat': ['PCT'],
|
|
||||||
'Tempera Grassa': ['PCT'],
|
|
||||||
};
|
|
||||||
|
|
||||||
// Deduplicated list of all mitigations across all targets of a ref event
|
// Deduplicated list of all mitigations across all targets of a ref event
|
||||||
function collectRefMitigs(refEvent) {
|
function collectRefMitigs(refEvent) {
|
||||||
@ -509,11 +412,6 @@
|
|||||||
const refPlanPanel = document.getElementById('ref-plan-panel');
|
const refPlanPanel = document.getElementById('ref-plan-panel');
|
||||||
const refPlanSelect = document.getElementById('ref-plan-select');
|
const refPlanSelect = document.getElementById('ref-plan-select');
|
||||||
|
|
||||||
const PLAN_JOB_ROLE = {
|
|
||||||
'PLD': 'tank', 'WAR': 'tank', 'DRK': 'tank', 'GNB': 'tank',
|
|
||||||
'WHM': 'healer', 'SCH': 'healer', 'AST': 'healer', 'SGE': 'healer',
|
|
||||||
};
|
|
||||||
|
|
||||||
function loadPlansForRef() {
|
function loadPlansForRef() {
|
||||||
try { return JSON.parse(localStorage.getItem('ff14-planner-plans') || '[]'); }
|
try { return JSON.parse(localStorage.getItem('ff14-planner-plans') || '[]'); }
|
||||||
catch { return []; }
|
catch { return []; }
|
||||||
@ -528,7 +426,7 @@
|
|||||||
const players = jobComp.map((job, i) => ({
|
const players = jobComp.map((job, i) => ({
|
||||||
job,
|
job,
|
||||||
name: roster[i]?.name ?? '',
|
name: roster[i]?.name ?? '',
|
||||||
role: PLAN_JOB_ROLE[job] ?? 'dps',
|
role: JOB_ROLE[job] ?? 'dps',
|
||||||
})).filter(p => p.name && p.job);
|
})).filter(p => p.name && p.job);
|
||||||
|
|
||||||
return plan.mechanics.map(m => {
|
return plan.mechanics.map(m => {
|
||||||
@ -610,7 +508,7 @@
|
|||||||
.map((job, i) => {
|
.map((job, i) => {
|
||||||
const name = plan.playerRoster?.[i]?.name ?? '';
|
const name = plan.playerRoster?.[i]?.name ?? '';
|
||||||
if (!name || !job) return null;
|
if (!name || !job) return null;
|
||||||
return { name, type: job, role: PLAN_JOB_ROLE[job] ?? 'dps' };
|
return { name, type: job, role: JOB_ROLE[job] ?? 'dps' };
|
||||||
})
|
})
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
|
|
||||||
|
|||||||
212
js/ffxiv-data.js
Normal file
212
js/ffxiv-data.js
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
(function () {
|
||||||
|
const JOB_FROM_TYPE = {
|
||||||
|
'Paladin': 'PLD', 'Warrior': 'WAR', 'DarkKnight': 'DRK', 'Gunbreaker': 'GNB',
|
||||||
|
'WhiteMage': 'WHM', 'Scholar': 'SCH', 'Astrologian': 'AST', 'Sage': 'SGE',
|
||||||
|
'Monk': 'MNK', 'Dragoon': 'DRG', 'Ninja': 'NIN', 'Samurai': 'SAM',
|
||||||
|
'Reaper': 'RPR', 'Viper': 'VPR', 'Bard': 'BRD', 'Machinist': 'MCH',
|
||||||
|
'Dancer': 'DNC', 'BlackMage': 'BLM', 'Summoner': 'SMN', 'RedMage': 'RDM',
|
||||||
|
'Pictomancer': 'PCT', 'BlueMage': 'BLU',
|
||||||
|
};
|
||||||
|
|
||||||
|
const JOB_ROLE = {
|
||||||
|
'PLD': 'tank', 'WAR': 'tank', 'DRK': 'tank', 'GNB': 'tank',
|
||||||
|
'WHM': 'healer', 'SCH': 'healer', 'AST': 'healer', 'SGE': 'healer',
|
||||||
|
'MNK': 'dps', 'DRG': 'dps', 'NIN': 'dps', 'SAM': 'dps',
|
||||||
|
'RPR': 'dps', 'VPR': 'dps', 'BRD': 'dps', 'MCH': 'dps',
|
||||||
|
'DNC': 'dps', 'BLM': 'dps', 'SMN': 'dps', 'RDM': 'dps', 'PCT': 'dps',
|
||||||
|
'BLU': 'dps',
|
||||||
|
};
|
||||||
|
|
||||||
|
const ALL_JOBS = [
|
||||||
|
{ group: 'Tank', jobs: ['PLD', 'WAR', 'DRK', 'GNB'] },
|
||||||
|
{ group: 'Healer', jobs: ['WHM', 'SCH', 'AST', 'SGE'] },
|
||||||
|
{ group: 'Melee', jobs: ['MNK', 'DRG', 'NIN', 'SAM', 'RPR', 'VPR'] },
|
||||||
|
{ group: 'Ranged', jobs: ['BRD', 'MCH', 'DNC'] },
|
||||||
|
{ group: 'Caster', jobs: ['BLM', 'SMN', 'RDM', 'PCT'] },
|
||||||
|
];
|
||||||
|
|
||||||
|
const JOB_ABILITIES = {
|
||||||
|
'PLD': [
|
||||||
|
{ name: 'Passage of Arms', buffType: 'buff' },
|
||||||
|
{ name: 'Divine Veil', buffType: 'shield' },
|
||||||
|
{ name: 'Guardian', buffType: 'shield' },
|
||||||
|
{ name: 'Reprisal', buffType: 'debuff' },
|
||||||
|
],
|
||||||
|
'WAR': [
|
||||||
|
{ name: 'Shake It Off', buffType: 'shield' },
|
||||||
|
{ name: 'Bloodwhetting', buffType: 'shield' },
|
||||||
|
{ name: 'Reprisal', buffType: 'debuff' },
|
||||||
|
],
|
||||||
|
'DRK': [
|
||||||
|
{ name: 'Dark Missionary', buffType: 'buff' },
|
||||||
|
{ name: 'Reprisal', buffType: 'debuff' },
|
||||||
|
],
|
||||||
|
'GNB': [
|
||||||
|
{ name: 'Heart of Light', buffType: 'buff' },
|
||||||
|
{ name: 'Reprisal', buffType: 'debuff' },
|
||||||
|
],
|
||||||
|
'WHM': [
|
||||||
|
{ name: 'Temperance', buffType: 'buff' },
|
||||||
|
{ name: 'Divine Benison', buffType: 'shield' },
|
||||||
|
{ name: 'Divine Caress', buffType: 'shield' },
|
||||||
|
],
|
||||||
|
'SCH': [
|
||||||
|
{ name: 'Sacred Soil', buffType: 'buff' },
|
||||||
|
{ name: 'Expedient', buffType: 'buff' },
|
||||||
|
{ name: 'Fey Illumination', buffType: 'buff' },
|
||||||
|
{ name: 'Galvanize', buffType: 'shield' },
|
||||||
|
{ name: 'Seraphic Veil', buffType: 'shield' },
|
||||||
|
{ name: 'Catalyze', buffType: 'shield' },
|
||||||
|
],
|
||||||
|
'AST': [
|
||||||
|
{ name: 'Collective Unconscious', buffType: 'buff' },
|
||||||
|
{ name: 'Neutral Sect', buffType: 'shield' },
|
||||||
|
{ name: 'Intersection', buffType: 'shield' },
|
||||||
|
{ name: 'the Spire', buffType: 'shield' },
|
||||||
|
],
|
||||||
|
'SGE': [
|
||||||
|
{ name: 'Kerachole', buffType: 'buff' },
|
||||||
|
{ name: 'Holos', buffType: 'buff' },
|
||||||
|
{ name: 'Holosakos', buffType: 'shield' },
|
||||||
|
{ name: 'Panhaima', buffType: 'shield' },
|
||||||
|
{ name: 'Eukrasian Prognosis', buffType: 'shield' },
|
||||||
|
{ name: 'Eukrasian Prognosis II', buffType: 'shield' },
|
||||||
|
{ name: 'Eukrasian Diagnosis', buffType: 'shield' },
|
||||||
|
{ name: 'Differential Diagnosis', buffType: 'shield' },
|
||||||
|
{ name: 'Haima', buffType: 'shield' },
|
||||||
|
],
|
||||||
|
'BRD': [{ name: 'Troubadour', buffType: 'buff' }],
|
||||||
|
'MCH': [{ name: 'Tactician', buffType: 'buff' }],
|
||||||
|
'DNC': [
|
||||||
|
{ name: 'Shield Samba', buffType: 'buff' },
|
||||||
|
{ name: 'Improvised Finish', buffType: 'shield' },
|
||||||
|
],
|
||||||
|
'MNK': [{ name: 'Feint', buffType: 'debuff' }],
|
||||||
|
'DRG': [{ name: 'Feint', buffType: 'debuff' }],
|
||||||
|
'NIN': [{ name: 'Feint', buffType: 'debuff' }],
|
||||||
|
'SAM': [{ name: 'Feint', buffType: 'debuff' }],
|
||||||
|
'RPR': [{ name: 'Feint', buffType: 'debuff' }],
|
||||||
|
'VPR': [{ name: 'Feint', buffType: 'debuff' }],
|
||||||
|
'BLM': [{ name: 'Addle', buffType: 'debuff' }],
|
||||||
|
'SMN': [
|
||||||
|
{ name: 'Addle', buffType: 'debuff' },
|
||||||
|
{ name: 'Radiant Aegis', buffType: 'shield' },
|
||||||
|
],
|
||||||
|
'RDM': [
|
||||||
|
{ name: 'Addle', buffType: 'debuff' },
|
||||||
|
{ name: 'Magick Barrier', buffType: 'buff' },
|
||||||
|
],
|
||||||
|
'PCT': [
|
||||||
|
{ name: 'Addle', buffType: 'debuff' },
|
||||||
|
{ name: 'Tempera Coat', buffType: 'shield' },
|
||||||
|
{ name: 'Tempera Grassa', buffType: 'shield' },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const ABILITY_JOB_MAP = {
|
||||||
|
'Passage of Arms': 'PLD', 'Divine Veil': 'PLD', 'Guardian': 'PLD',
|
||||||
|
'Shake It Off': 'WAR', 'Bloodwhetting': 'WAR',
|
||||||
|
'Dark Missionary': 'DRK',
|
||||||
|
'Heart of Light': 'GNB',
|
||||||
|
'Temperance': 'WHM', 'Divine Benison': 'WHM', 'Divine Caress': 'WHM',
|
||||||
|
'Sacred Soil': 'SCH', 'Expedient': 'SCH', 'Fey Illumination': 'SCH',
|
||||||
|
'Galvanize': 'SCH', 'Seraphic Veil': 'SCH', 'Catalyze': 'SCH',
|
||||||
|
'Collective Unconscious': 'AST', 'Neutral Sect': 'AST',
|
||||||
|
'Intersection': 'AST', 'the Spire': 'AST',
|
||||||
|
'Kerachole': 'SGE', 'Holos': 'SGE', 'Holosakos': 'SGE',
|
||||||
|
'Panhaima': 'SGE', 'Haima': 'SGE',
|
||||||
|
'Eukrasian Prognosis': 'SGE', 'Eukrasian Prognosis II': 'SGE',
|
||||||
|
'Eukrasian Diagnosis': 'SGE', 'Differential Diagnosis': 'SGE',
|
||||||
|
'Troubadour': 'BRD',
|
||||||
|
'Tactician': 'MCH',
|
||||||
|
'Shield Samba': 'DNC', 'Improvised Finish': 'DNC',
|
||||||
|
'Radiant Aegis': 'SMN',
|
||||||
|
'Magick Barrier': 'RDM',
|
||||||
|
'Tempera Coat': 'PCT', 'Tempera Grassa': 'PCT',
|
||||||
|
};
|
||||||
|
|
||||||
|
const MITIG_ICONS = {
|
||||||
|
'Passage of Arms': 'assets/icons/mitigation/passage-of-arms.png',
|
||||||
|
'Dark Missionary': 'assets/icons/mitigation/dark-missionary.png',
|
||||||
|
'Heart of Light': 'assets/icons/mitigation/heart-of-light.png',
|
||||||
|
'Temperance': 'assets/icons/mitigation/temperance.png',
|
||||||
|
'Sacred Soil': 'assets/icons/mitigation/sacred-soil.png',
|
||||||
|
'Expedient': 'assets/icons/mitigation/expedient.png',
|
||||||
|
'Fey Illumination': 'assets/icons/mitigation/fey-illumination.png',
|
||||||
|
'Collective Unconscious': 'assets/icons/mitigation/collective-unconscious.png',
|
||||||
|
'Holos': 'assets/icons/mitigation/holos.png',
|
||||||
|
'Kerachole': 'assets/icons/mitigation/kerachole.png',
|
||||||
|
'Troubadour': 'assets/icons/mitigation/troubadour.png',
|
||||||
|
'Tactician': 'assets/icons/mitigation/tactician.png',
|
||||||
|
'Shield Samba': 'assets/icons/mitigation/shield-samba.png',
|
||||||
|
'Magick Barrier': 'assets/icons/mitigation/magick-barrier.png',
|
||||||
|
'Reprisal': 'assets/icons/mitigation/reprisal.png',
|
||||||
|
'Feint': 'assets/icons/mitigation/feint.png',
|
||||||
|
'Addle': 'assets/icons/mitigation/addle.png',
|
||||||
|
'Divine Veil': 'assets/icons/mitigation/divine-veil.png',
|
||||||
|
'Guardian': 'assets/icons/mitigation/guardian.png',
|
||||||
|
'Shake It Off': 'assets/icons/mitigation/shake-it-off.png',
|
||||||
|
'Bloodwhetting': 'assets/icons/mitigation/bloodwhetting.png',
|
||||||
|
'Divine Benison': 'assets/icons/mitigation/divine-benison.png',
|
||||||
|
'Divine Caress': 'assets/icons/mitigation/divine-caress.png',
|
||||||
|
'Intersection': 'assets/icons/mitigation/intersection.png',
|
||||||
|
'Neutral Sect': 'assets/icons/mitigation/neutral-sect.png',
|
||||||
|
'the Spire': 'assets/icons/mitigation/the-spire.png',
|
||||||
|
'Panhaima': 'assets/icons/mitigation/panhaima.png',
|
||||||
|
'Holosakos': 'assets/icons/mitigation/holos.png',
|
||||||
|
'Eukrasian Prognosis': 'assets/icons/mitigation/eukrasian-prognosis.png',
|
||||||
|
'Eukrasian Prognosis II': 'assets/icons/mitigation/eukrasian-prognosis-ii.png',
|
||||||
|
'Eukrasian Diagnosis': 'assets/icons/mitigation/eukrasian-diagnosis.png',
|
||||||
|
'Differential Diagnosis': 'assets/icons/mitigation/eukrasian-diagnosis.png',
|
||||||
|
'Haima': 'assets/icons/mitigation/haima.png',
|
||||||
|
'Galvanize': 'assets/icons/mitigation/galvanize.png',
|
||||||
|
'Seraphic Veil': 'assets/icons/mitigation/seraphic-veil.png',
|
||||||
|
'Radiant Aegis': 'assets/icons/mitigation/radiant-aegis.png',
|
||||||
|
'Tempera Coat': 'assets/icons/mitigation/tempera-coat.png',
|
||||||
|
'Tempera Grassa': 'assets/icons/mitigation/tempera-grassa.png',
|
||||||
|
'Improvised Finish': 'assets/icons/mitigation/improvised-finish.png',
|
||||||
|
};
|
||||||
|
|
||||||
|
const ABILITY_DR = {
|
||||||
|
'Passage of Arms': 0.15,
|
||||||
|
'Troubadour': 0.15,
|
||||||
|
'Tactician': 0.15,
|
||||||
|
'Shield Samba': 0.15,
|
||||||
|
'Dark Missionary': 0.10,
|
||||||
|
'Heart of Light': 0.10,
|
||||||
|
'Temperance': 0.10,
|
||||||
|
'Sacred Soil': 0.10,
|
||||||
|
'Expedient': 0.10,
|
||||||
|
'Collective Unconscious': 0.10,
|
||||||
|
'Holos': 0.10,
|
||||||
|
'Kerachole': 0.10,
|
||||||
|
'Magick Barrier': 0.10,
|
||||||
|
'Fey Illumination': 0.05,
|
||||||
|
'Reprisal': 0.10,
|
||||||
|
'Feint': 0.05,
|
||||||
|
'Addle': 0.10,
|
||||||
|
};
|
||||||
|
|
||||||
|
const ABILITY_JOBS = {};
|
||||||
|
Object.entries(JOB_ABILITIES).forEach(([job, abilities]) => {
|
||||||
|
abilities.forEach(ability => {
|
||||||
|
if (!ABILITY_JOBS[ability.name]) ABILITY_JOBS[ability.name] = [];
|
||||||
|
ABILITY_JOBS[ability.name].push(job);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
window.FF14_DATA = {
|
||||||
|
JOB_ABBR: JOB_FROM_TYPE,
|
||||||
|
JOB_FROM_TYPE,
|
||||||
|
JOB_ROLE,
|
||||||
|
ALL_JOBS,
|
||||||
|
JOB_ABILITIES,
|
||||||
|
ABILITY_JOB_MAP,
|
||||||
|
ABILITY_JOBS,
|
||||||
|
ABILITY_DR,
|
||||||
|
MITIG_ICONS,
|
||||||
|
TANK_JOBS: new Set(['PLD', 'WAR', 'DRK', 'GNB']),
|
||||||
|
MELEE_JOBS: new Set(['MNK', 'DRG', 'NIN', 'SAM', 'RPR', 'VPR']),
|
||||||
|
CASTER_JOBS: new Set(['BLM', 'SMN', 'RDM', 'PCT']),
|
||||||
|
};
|
||||||
|
})();
|
||||||
229
js/planner.js
229
js/planner.js
@ -444,18 +444,18 @@ function simulateDrMultiplier(mechanic, assignments = mechanic.assignments ?? []
|
|||||||
return mult;
|
return mult;
|
||||||
}
|
}
|
||||||
|
|
||||||
function effectiveAssignmentsForMechanic(plan, targetMechanic) {
|
function plannedAssignmentsForMechanic(plan, targetMechanic) {
|
||||||
|
const mechanics = visiblePlanMechanics(plan);
|
||||||
const result = [];
|
const result = [];
|
||||||
const seen = new Set();
|
|
||||||
|
|
||||||
for (const entry of canonicalAssignmentActivations(plan, { dedupeKey: canonicalMechanicKey })) {
|
for (const entry of canonicalAssignmentActivations(plan, { dedupeKey: canonicalMechanicKey })) {
|
||||||
if (targetMechanic.timestamp < entry.start || targetMechanic.timestamp > entry.end) continue;
|
const displayMechanic = mechanics.reduce((best, mechanic) =>
|
||||||
const assignment = entry.assignment;
|
Math.abs(mechanic.timestamp - entry.start) < Math.abs(best.timestamp - entry.start) ? mechanic : best
|
||||||
const key = canonicalMechanicKey(entry);
|
, mechanics[0]);
|
||||||
if (seen.has(key)) continue;
|
if (displayMechanic?.id !== targetMechanic.id) continue;
|
||||||
seen.add(key);
|
|
||||||
result.push({
|
result.push({
|
||||||
...assignment,
|
...entry.assignment,
|
||||||
sourceMechanicId: entry.mechanic.id,
|
sourceMechanicId: entry.mechanic.id,
|
||||||
sourceStart: entry.start,
|
sourceStart: entry.start,
|
||||||
});
|
});
|
||||||
@ -482,8 +482,8 @@ function renderMechanicListHtml(plan) {
|
|||||||
const avgHp = avgNonTankMaxHp(plan);
|
const avgHp = avgNonTankMaxHp(plan);
|
||||||
|
|
||||||
return mechanics.map(m => {
|
return mechanics.map(m => {
|
||||||
const effective = effectiveAssignmentsForMechanic(plan, m);
|
const planned = plannedAssignmentsForMechanic(plan, m);
|
||||||
const sorted = sortedAssignments(effective);
|
const sorted = sortedAssignments(planned);
|
||||||
const assignHtml = sorted.length === 0
|
const assignHtml = sorted.length === 0
|
||||||
? '<span class="mechanic-no-assign">Keine Zuweisung</span>'
|
? '<span class="mechanic-no-assign">Keine Zuweisung</span>'
|
||||||
: sorted.map(a => {
|
: sorted.map(a => {
|
||||||
@ -513,10 +513,10 @@ function renderMechanicListHtml(plan) {
|
|||||||
: badgeHtml;
|
: badgeHtml;
|
||||||
}).join('');
|
}).join('');
|
||||||
|
|
||||||
const drOnly = m.unmitigatedDamage ? Math.round(m.unmitigatedDamage * simulateDrMultiplier(m, effective)) : 0;
|
const drOnly = m.unmitigatedDamage ? Math.round(m.unmitigatedDamage * simulateDrMultiplier(m, planned)) : 0;
|
||||||
const shieldVal = (plan.shieldK ?? 0) * 1000;
|
const shieldVal = (plan.shieldK ?? 0) * 1000;
|
||||||
const mitigFull = Math.max(0, drOnly - shieldVal);
|
const mitigFull = Math.max(0, drOnly - shieldVal);
|
||||||
const hasDrAssign = effective.some(a => a.buffType !== 'shield' && (ABILITY_DR[a.ability] ?? 0) > 0);
|
const hasDrAssign = planned.some(a => a.buffType !== 'shield' && (ABILITY_DR[a.ability] ?? 0) > 0);
|
||||||
const hasShield = shieldVal > 0;
|
const hasShield = shieldVal > 0;
|
||||||
const drOnlyCls = avgHp ? (drOnly <= avgHp ? 'mechanic-mitig--ok' : 'mechanic-mitig--risk') : '';
|
const drOnlyCls = avgHp ? (drOnly <= avgHp ? 'mechanic-mitig--ok' : 'mechanic-mitig--risk') : '';
|
||||||
const fullCls = avgHp ? (mitigFull <= avgHp ? 'mechanic-mitig--ok' : 'mechanic-mitig--risk') : '';
|
const fullCls = avgHp ? (mitigFull <= avgHp ? 'mechanic-mitig--ok' : 'mechanic-mitig--risk') : '';
|
||||||
@ -1589,199 +1589,20 @@ function initNewFolderForm() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Ability → Job mapping ─────────────────────────────────────────────────────
|
// ── Shared FFXIV metadata ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
const ABILITY_JOB_MAP = {
|
|
||||||
'Passage of Arms': 'PLD', 'Divine Veil': 'PLD', 'Guardian': 'PLD',
|
|
||||||
'Shake It Off': 'WAR', 'Bloodwhetting': 'WAR',
|
|
||||||
'Dark Missionary': 'DRK',
|
|
||||||
'Heart of Light': 'GNB',
|
|
||||||
'Temperance': 'WHM', 'Divine Benison': 'WHM', 'Divine Caress': 'WHM',
|
|
||||||
'Sacred Soil': 'SCH', 'Expedient': 'SCH', 'Fey Illumination': 'SCH',
|
|
||||||
'Galvanize': 'SCH', 'Seraphic Veil': 'SCH', 'Catalyze': 'SCH',
|
|
||||||
'Collective Unconscious': 'AST', 'Neutral Sect': 'AST',
|
|
||||||
'Intersection': 'AST', 'the Spire': 'AST',
|
|
||||||
'Kerachole': 'SGE', 'Holos': 'SGE', 'Holosakos': 'SGE',
|
|
||||||
'Panhaima': 'SGE', 'Haima': 'SGE',
|
|
||||||
'Eukrasian Prognosis': 'SGE',
|
|
||||||
'Eukrasian Diagnosis': 'SGE', 'Differential Diagnosis': 'SGE',
|
|
||||||
'Troubadour': 'BRD',
|
|
||||||
'Tactician': 'MCH',
|
|
||||||
'Shield Samba': 'DNC', 'Improvised Finish': 'DNC',
|
|
||||||
'Radiant Aegis': 'SMN',
|
|
||||||
'Magick Barrier': 'RDM',
|
|
||||||
'Tempera Coat': 'PCT', 'Tempera Grassa': 'PCT',
|
|
||||||
};
|
|
||||||
|
|
||||||
const JOB_FROM_TYPE = {
|
|
||||||
'Paladin': 'PLD', 'Warrior': 'WAR', 'DarkKnight': 'DRK', 'Gunbreaker': 'GNB',
|
|
||||||
'WhiteMage': 'WHM', 'Scholar': 'SCH', 'Astrologian': 'AST', 'Sage': 'SGE',
|
|
||||||
'Monk': 'MNK', 'Dragoon': 'DRG', 'Ninja': 'NIN', 'Samurai': 'SAM',
|
|
||||||
'Reaper': 'RPR', 'Viper': 'VPR', 'Bard': 'BRD', 'Machinist': 'MCH',
|
|
||||||
'Dancer': 'DNC', 'BlackMage': 'BLM', 'Summoner': 'SMN', 'RedMage': 'RDM',
|
|
||||||
'Pictomancer': 'PCT',
|
|
||||||
};
|
|
||||||
|
|
||||||
const TANK_JOBS = new Set(['PLD', 'WAR', 'DRK', 'GNB']);
|
|
||||||
const MELEE_JOBS = new Set(['MNK', 'DRG', 'NIN', 'SAM', 'RPR', 'VPR']);
|
|
||||||
const CASTER_JOBS = new Set(['BLM', 'SMN', 'RDM', 'PCT']);
|
|
||||||
|
|
||||||
const JOB_ROLE = {
|
|
||||||
'PLD': 'tank', 'WAR': 'tank', 'DRK': 'tank', 'GNB': 'tank',
|
|
||||||
'WHM': 'healer', 'SCH': 'healer', 'AST': 'healer', 'SGE': 'healer',
|
|
||||||
'MNK': 'dps', 'DRG': 'dps', 'NIN': 'dps', 'SAM': 'dps',
|
|
||||||
'RPR': 'dps', 'VPR': 'dps', 'BRD': 'dps', 'MCH': 'dps',
|
|
||||||
'DNC': 'dps', 'BLM': 'dps', 'SMN': 'dps', 'RDM': 'dps', 'PCT': 'dps',
|
|
||||||
};
|
|
||||||
|
|
||||||
const ALL_JOBS = [
|
|
||||||
{ group: 'Tank', jobs: ['PLD', 'WAR', 'DRK', 'GNB'] },
|
|
||||||
{ group: 'Healer', jobs: ['WHM', 'SCH', 'AST', 'SGE'] },
|
|
||||||
{ group: 'Melee', jobs: ['MNK', 'DRG', 'NIN', 'SAM', 'RPR', 'VPR'] },
|
|
||||||
{ group: 'Ranged', jobs: ['BRD', 'MCH', 'DNC'] },
|
|
||||||
{ group: 'Caster', jobs: ['BLM', 'SMN', 'RDM', 'PCT'] },
|
|
||||||
];
|
|
||||||
|
|
||||||
const JOB_ABILITIES = {
|
|
||||||
'PLD': [
|
|
||||||
{ name: 'Passage of Arms', buffType: 'buff' },
|
|
||||||
{ name: 'Divine Veil', buffType: 'shield' },
|
|
||||||
{ name: 'Guardian', buffType: 'shield' },
|
|
||||||
{ name: 'Reprisal', buffType: 'debuff' },
|
|
||||||
],
|
|
||||||
'WAR': [
|
|
||||||
{ name: 'Shake It Off', buffType: 'shield' },
|
|
||||||
{ name: 'Bloodwhetting', buffType: 'shield' },
|
|
||||||
{ name: 'Reprisal', buffType: 'debuff' },
|
|
||||||
],
|
|
||||||
'DRK': [
|
|
||||||
{ name: 'Dark Missionary', buffType: 'buff' },
|
|
||||||
{ name: 'Reprisal', buffType: 'debuff' },
|
|
||||||
],
|
|
||||||
'GNB': [
|
|
||||||
{ name: 'Heart of Light', buffType: 'buff' },
|
|
||||||
{ name: 'Reprisal', buffType: 'debuff' },
|
|
||||||
],
|
|
||||||
'WHM': [
|
|
||||||
{ name: 'Temperance', buffType: 'buff' },
|
|
||||||
{ name: 'Divine Benison', buffType: 'shield' },
|
|
||||||
{ name: 'Divine Caress', buffType: 'shield' },
|
|
||||||
],
|
|
||||||
'SCH': [
|
|
||||||
{ name: 'Sacred Soil', buffType: 'buff' },
|
|
||||||
{ name: 'Expedient', buffType: 'buff' },
|
|
||||||
{ name: 'Fey Illumination', buffType: 'buff' },
|
|
||||||
{ name: 'Galvanize', buffType: 'shield' },
|
|
||||||
{ name: 'Seraphic Veil', buffType: 'shield' },
|
|
||||||
{ name: 'Catalyze', buffType: 'shield' },
|
|
||||||
],
|
|
||||||
'AST': [
|
|
||||||
{ name: 'Collective Unconscious', buffType: 'buff' },
|
|
||||||
{ name: 'Neutral Sect', buffType: 'shield' },
|
|
||||||
{ name: 'Intersection', buffType: 'shield' },
|
|
||||||
{ name: 'the Spire', buffType: 'shield' },
|
|
||||||
],
|
|
||||||
'SGE': [
|
|
||||||
{ name: 'Kerachole', buffType: 'buff' },
|
|
||||||
{ name: 'Holos', buffType: 'buff' },
|
|
||||||
{ name: 'Holosakos', buffType: 'shield' },
|
|
||||||
{ name: 'Panhaima', buffType: 'shield' },
|
|
||||||
{ name: 'Eukrasian Prognosis', buffType: 'shield' },
|
|
||||||
{ name: 'Eukrasian Diagnosis', buffType: 'shield' },
|
|
||||||
{ name: 'Differential Diagnosis', buffType: 'shield' },
|
|
||||||
{ name: 'Haima', buffType: 'shield' },
|
|
||||||
],
|
|
||||||
'BRD': [{ name: 'Troubadour', buffType: 'buff' }],
|
|
||||||
'MCH': [{ name: 'Tactician', buffType: 'buff' }],
|
|
||||||
'DNC': [
|
|
||||||
{ name: 'Shield Samba', buffType: 'buff' },
|
|
||||||
{ name: 'Improvised Finish', buffType: 'shield' },
|
|
||||||
],
|
|
||||||
'MNK': [{ name: 'Feint', buffType: 'debuff' }],
|
|
||||||
'DRG': [{ name: 'Feint', buffType: 'debuff' }],
|
|
||||||
'NIN': [{ name: 'Feint', buffType: 'debuff' }],
|
|
||||||
'SAM': [{ name: 'Feint', buffType: 'debuff' }],
|
|
||||||
'RPR': [{ name: 'Feint', buffType: 'debuff' }],
|
|
||||||
'VPR': [{ name: 'Feint', buffType: 'debuff' }],
|
|
||||||
'BLM': [{ name: 'Addle', buffType: 'debuff' }],
|
|
||||||
'SMN': [
|
|
||||||
{ name: 'Addle', buffType: 'debuff' },
|
|
||||||
{ name: 'Radiant Aegis', buffType: 'shield' },
|
|
||||||
],
|
|
||||||
'RDM': [
|
|
||||||
{ name: 'Addle', buffType: 'debuff' },
|
|
||||||
{ name: 'Magick Barrier', buffType: 'buff' },
|
|
||||||
],
|
|
||||||
'PCT': [
|
|
||||||
{ name: 'Addle', buffType: 'debuff' },
|
|
||||||
{ name: 'Tempera Coat', buffType: 'shield' },
|
|
||||||
{ name: 'Tempera Grassa', buffType: 'shield' },
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const MITIG_ICONS = {
|
|
||||||
'Passage of Arms': 'assets/icons/mitigation/passage-of-arms.png',
|
|
||||||
'Dark Missionary': 'assets/icons/mitigation/dark-missionary.png',
|
|
||||||
'Heart of Light': 'assets/icons/mitigation/heart-of-light.png',
|
|
||||||
'Temperance': 'assets/icons/mitigation/temperance.png',
|
|
||||||
'Sacred Soil': 'assets/icons/mitigation/sacred-soil.png',
|
|
||||||
'Expedient': 'assets/icons/mitigation/expedient.png',
|
|
||||||
'Fey Illumination': 'assets/icons/mitigation/fey-illumination.png',
|
|
||||||
'Collective Unconscious': 'assets/icons/mitigation/collective-unconscious.png',
|
|
||||||
'Holos': 'assets/icons/mitigation/holos.png',
|
|
||||||
'Kerachole': 'assets/icons/mitigation/kerachole.png',
|
|
||||||
'Troubadour': 'assets/icons/mitigation/troubadour.png',
|
|
||||||
'Tactician': 'assets/icons/mitigation/tactician.png',
|
|
||||||
'Shield Samba': 'assets/icons/mitigation/shield-samba.png',
|
|
||||||
'Magick Barrier': 'assets/icons/mitigation/magick-barrier.png',
|
|
||||||
'Reprisal': 'assets/icons/mitigation/reprisal.png',
|
|
||||||
'Feint': 'assets/icons/mitigation/feint.png',
|
|
||||||
'Addle': 'assets/icons/mitigation/addle.png',
|
|
||||||
'Divine Veil': 'assets/icons/mitigation/divine-veil.png',
|
|
||||||
'Guardian': 'assets/icons/mitigation/guardian.png',
|
|
||||||
'Shake It Off': 'assets/icons/mitigation/shake-it-off.png',
|
|
||||||
'Bloodwhetting': 'assets/icons/mitigation/bloodwhetting.png',
|
|
||||||
'Divine Benison': 'assets/icons/mitigation/divine-benison.png',
|
|
||||||
'Divine Caress': 'assets/icons/mitigation/divine-caress.png',
|
|
||||||
'Intersection': 'assets/icons/mitigation/intersection.png',
|
|
||||||
'Neutral Sect': 'assets/icons/mitigation/neutral-sect.png',
|
|
||||||
'the Spire': 'assets/icons/mitigation/the-spire.png',
|
|
||||||
'Panhaima': 'assets/icons/mitigation/panhaima.png',
|
|
||||||
'Holosakos': 'assets/icons/mitigation/holos.png',
|
|
||||||
'Eukrasian Prognosis': 'assets/icons/mitigation/eukrasian-prognosis.png',
|
|
||||||
'Eukrasian Prognosis II': 'assets/icons/mitigation/eukrasian-prognosis-ii.png',
|
|
||||||
'Eukrasian Diagnosis': 'assets/icons/mitigation/eukrasian-diagnosis.png',
|
|
||||||
'Differential Diagnosis': 'assets/icons/mitigation/eukrasian-diagnosis.png',
|
|
||||||
'Haima': 'assets/icons/mitigation/haima.png',
|
|
||||||
'Galvanize': 'assets/icons/mitigation/galvanize.png',
|
|
||||||
'Seraphic Veil': 'assets/icons/mitigation/seraphic-veil.png',
|
|
||||||
'Radiant Aegis': 'assets/icons/mitigation/radiant-aegis.png',
|
|
||||||
'Tempera Coat': 'assets/icons/mitigation/tempera-coat.png',
|
|
||||||
'Tempera Grassa': 'assets/icons/mitigation/tempera-grassa.png',
|
|
||||||
'Improvised Finish': 'assets/icons/mitigation/improvised-finish.png',
|
|
||||||
};
|
|
||||||
|
|
||||||
// DR values (0–1) for buff/debuff mitigations — shields excluded (no reliable sim).
|
|
||||||
const ABILITY_DR = {
|
|
||||||
'Passage of Arms': 0.15,
|
|
||||||
'Troubadour': 0.15,
|
|
||||||
'Tactician': 0.15,
|
|
||||||
'Shield Samba': 0.15,
|
|
||||||
'Dark Missionary': 0.10,
|
|
||||||
'Heart of Light': 0.10,
|
|
||||||
'Temperance': 0.10,
|
|
||||||
'Sacred Soil': 0.10,
|
|
||||||
'Expedient': 0.10,
|
|
||||||
'Collective Unconscious': 0.10,
|
|
||||||
'Holos': 0.10,
|
|
||||||
'Kerachole': 0.10,
|
|
||||||
'Magick Barrier': 0.10,
|
|
||||||
'Fey Illumination': 0.05,
|
|
||||||
'Reprisal': 0.10,
|
|
||||||
'Feint': 0.05, // 10% phys / 5% magic — use magic (conservative)
|
|
||||||
'Addle': 0.10,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
const {
|
||||||
|
ABILITY_DR,
|
||||||
|
ABILITY_JOB_MAP,
|
||||||
|
ALL_JOBS,
|
||||||
|
CASTER_JOBS,
|
||||||
|
JOB_ABILITIES,
|
||||||
|
JOB_FROM_TYPE,
|
||||||
|
JOB_ROLE,
|
||||||
|
MELEE_JOBS,
|
||||||
|
MITIG_ICONS,
|
||||||
|
TANK_JOBS,
|
||||||
|
} = window.FF14_DATA;
|
||||||
// Groups of abilities that are functionally equivalent across different jobs.
|
// Groups of abilities that are functionally equivalent across different jobs.
|
||||||
// Used to suggest replacements when a job is missing from the composition.
|
// Used to suggest replacements when a job is missing from the composition.
|
||||||
const ABILITY_EQUIVALENTS = [
|
const ABILITY_EQUIVALENTS = [
|
||||||
|
|||||||
@ -122,6 +122,7 @@
|
|||||||
|
|
||||||
<script src="js/app.js"></script>
|
<script src="js/app.js"></script>
|
||||||
<script src="js/tabs.js"></script>
|
<script src="js/tabs.js"></script>
|
||||||
|
<script src="js/ffxiv-data.js"></script>
|
||||||
<script src="js/analysis.js"></script>
|
<script src="js/analysis.js"></script>
|
||||||
<script src="js/planner.js"></script>
|
<script src="js/planner.js"></script>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user