forked from xziino/ff14-mitigator
Merge remote-tracking branch 'origin/master' into akus_schabernack3
# Conflicts: # js/planner.js
This commit is contained in:
commit
15754fefda
@ -228,6 +228,7 @@
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mechanic-no-assign {
|
.mechanic-no-assign {
|
||||||
@ -417,6 +418,39 @@
|
|||||||
|
|
||||||
.ability-chip--other-job { opacity: 0.45; }
|
.ability-chip--other-job { opacity: 0.45; }
|
||||||
|
|
||||||
|
/* ── Equivalents hint ────────────────────────────────────────────────────────── */
|
||||||
|
.badge-with-hint {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-equiv-hint {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--green);
|
||||||
|
white-space: nowrap;
|
||||||
|
padding: 0 2px;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-no-equiv-hint {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--red);
|
||||||
|
white-space: nowrap;
|
||||||
|
padding: 0 2px;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-job-hint {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--t3);
|
||||||
|
white-space: nowrap;
|
||||||
|
padding: 0 2px;
|
||||||
|
cursor: default;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Folder Sidebar ──────────────────────────────────────────────────────────── */
|
/* ── Folder Sidebar ──────────────────────────────────────────────────────────── */
|
||||||
.folder-section { margin-bottom: 2px; }
|
.folder-section { margin-bottom: 2px; }
|
||||||
|
|
||||||
|
|||||||
@ -375,11 +375,22 @@ function renderMechanicListHtml(plan) {
|
|||||||
const ability = assignmentAbilityName(a, plan);
|
const ability = assignmentAbilityName(a, plan);
|
||||||
const label = a.job ? `${escHtml(a.job)} · ${escHtml(ability)}` : escHtml(ability);
|
const label = a.job ? `${escHtml(a.job)} · ${escHtml(ability)}` : escHtml(ability);
|
||||||
const title = isMissing ? `${escHtml(a.job)} nicht in Jobaufstellung` : '';
|
const title = isMissing ? `${escHtml(a.job)} nicht in Jobaufstellung` : '';
|
||||||
return `<span class="badge badge-assign ${cls}${isMissing ? ' badge-assign--missing-job' : ''}"${title ? ` title="${title}"` : ''}>
|
const suggestions = isMissing ? findEquivSuggestions(a.ability, activeJobSet) : [];
|
||||||
|
const badgeHtml = `<span class="badge badge-assign ${cls}${isMissing ? ' badge-assign--missing-job' : ''}"${title ? ` title="${title}"` : ''}>
|
||||||
${icon ? `<img class="badge-icon" src="${escHtml(icon)}" alt="">` : ''}
|
${icon ? `<img class="badge-icon" src="${escHtml(icon)}" alt="">` : ''}
|
||||||
${label}
|
${label}
|
||||||
<button class="badge-remove" data-mechanic-id="${escHtml(m.id)}" data-ability="${escHtml(a.ability)}" title="Entfernen">×</button>
|
<button class="badge-remove" data-mechanic-id="${escHtml(m.id)}" data-ability="${escHtml(a.ability)}" title="Entfernen">×</button>
|
||||||
</span>`;
|
</span>`;
|
||||||
|
const hintHtml = suggestions.map(s =>
|
||||||
|
`<span class="badge-equiv-hint">→ ${escHtml(s.ability)} (${escHtml(s.job)})?</span>`
|
||||||
|
).join('');
|
||||||
|
const noEquivHint = isMissing && suggestions.length === 0
|
||||||
|
? `<span class="badge-no-equiv-hint">→ Kein Äquivalent!</span>` : '';
|
||||||
|
const jobHint = !a.job ? `<span class="badge-job-hint">→ Job zuordnen</span>` : '';
|
||||||
|
const needsWrap = suggestions.length > 0 || !!noEquivHint || !!jobHint;
|
||||||
|
return needsWrap
|
||||||
|
? `<span class="badge-with-hint">${badgeHtml}${hintHtml}${noEquivHint}${jobHint}</span>`
|
||||||
|
: badgeHtml;
|
||||||
}).join('');
|
}).join('');
|
||||||
|
|
||||||
return `
|
return `
|
||||||
@ -843,6 +854,29 @@ const MITIG_ICONS = {
|
|||||||
'Improvised Finish': 'assets/icons/mitigation/improvised-finish.png',
|
'Improvised Finish': 'assets/icons/mitigation/improvised-finish.png',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Groups of abilities that are functionally equivalent across different jobs.
|
||||||
|
// Used to suggest replacements when a job is missing from the composition.
|
||||||
|
const ABILITY_EQUIVALENTS = [
|
||||||
|
['Troubadour', 'Tactician', 'Shield Samba'],
|
||||||
|
['Sacred Soil', 'Kerachole'],
|
||||||
|
];
|
||||||
|
|
||||||
|
function findEquivSuggestions(abilityName, activeJobSet) {
|
||||||
|
const group = ABILITY_EQUIVALENTS.find(g => g.includes(abilityName));
|
||||||
|
if (!group) return [];
|
||||||
|
const suggestions = [];
|
||||||
|
for (const equiv of group) {
|
||||||
|
if (equiv === abilityName) continue;
|
||||||
|
for (const job of activeJobSet) {
|
||||||
|
if ((JOB_ABILITIES[job] ?? []).some(a => a.name === equiv)) {
|
||||||
|
suggestions.push({ ability: equiv, job });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return suggestions;
|
||||||
|
}
|
||||||
|
|
||||||
const ASSIGN_ORDER = { debuff: 0, buff: 1, shield: 2 };
|
const ASSIGN_ORDER = { debuff: 0, buff: 1, shield: 2 };
|
||||||
|
|
||||||
function sortedAssignments(assignments) {
|
function sortedAssignments(assignments) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user