Compare commits

...

4 Commits

Author SHA1 Message Date
Akurosia Kamo
15754fefda Merge remote-tracking branch 'origin/master' into akus_schabernack3
# Conflicts:
#	js/planner.js
2026-05-22 12:48:22 +02:00
xziino
86d2a106bb Planner: Badge-Alignment fix bei Äquivalenz-Hinweisen
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 12:46:52 +02:00
xziino
1c0918a4a3 Planner: Äquivalenz-Hinweise verfeinert
- Grüner Hinweis wenn Äquivalent verfügbar (z.B. → Kerachole (SGE)?)
- Roter Hinweis "→ Kein Äquivalent!" wenn kein Ersatz vorhanden
- Grau-kursiv "→ Job zuordnen" bei Abilities ohne Job-Zuordnung

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 12:43:27 +02:00
xziino
d585f3be5a Planner: Äquivalenz-Hinweise für fehlende Job-Abilities
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 12:36:16 +02:00
2 changed files with 78 additions and 10 deletions

View File

@ -228,6 +228,7 @@
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; }

View File

@ -375,11 +375,22 @@ function renderMechanicListHtml(plan) {
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 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) {