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
@ -224,10 +224,11 @@
|
||||
}
|
||||
|
||||
.mechanic-assignments {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
margin-top: 2px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
margin-top: 2px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.mechanic-no-assign {
|
||||
@ -417,6 +418,39 @@
|
||||
|
||||
.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-section { margin-bottom: 2px; }
|
||||
|
||||
|
||||
@ -370,16 +370,27 @@ function renderMechanicListHtml(plan) {
|
||||
const cls = a.buffType === 'debuff' ? 'badge-assign-debuff'
|
||||
: a.buffType === 'shield' ? 'badge-assign-shield'
|
||||
: 'badge-assign-buff';
|
||||
const isMissing = !!a.job && !activeJobSet.has(a.job);
|
||||
const icon = MITIG_ICONS[a.ability] ?? '';
|
||||
const ability = assignmentAbilityName(a, plan);
|
||||
const label = a.job ? `${escHtml(a.job)} · ${escHtml(ability)}` : escHtml(ability);
|
||||
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 isMissing = !!a.job && !activeJobSet.has(a.job);
|
||||
const icon = MITIG_ICONS[a.ability] ?? '';
|
||||
const ability = assignmentAbilityName(a, plan);
|
||||
const label = a.job ? `${escHtml(a.job)} · ${escHtml(ability)}` : escHtml(ability);
|
||||
const title = isMissing ? `${escHtml(a.job)} nicht in Jobaufstellung` : '';
|
||||
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="">` : ''}
|
||||
${label}
|
||||
<button class="badge-remove" data-mechanic-id="${escHtml(m.id)}" data-ability="${escHtml(a.ability)}" title="Entfernen">×</button>
|
||||
</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('');
|
||||
|
||||
return `
|
||||
@ -843,6 +854,29 @@ const MITIG_ICONS = {
|
||||
'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 };
|
||||
|
||||
function sortedAssignments(assignments) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user