Planer: DR-Simulation + Schild-Eingabe in Mechanik-Cards
- ABILITY_DR-Map mit DR-Werten pro Ability (Feint = 5% magic) - simulateDrMultiplier() berechnet multiplikativen DR-Faktor - Mechanik-Cards zeigen mitigierten Wert (DR-only) + optional mit Schild - SGE/SCH Schild-Feld (in k) im Info-Panel, wird pro Plan gespeichert - Farbige Anzeige: grün = unter ∅ HP, rot = darüber Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
8b00d1d2a8
commit
e358a4e709
@ -290,6 +290,11 @@
|
||||
color: var(--t3);
|
||||
margin-left: 6px;
|
||||
}
|
||||
.mechanic-mitig-row { margin-top: -2px; display: flex; align-items: baseline; flex-wrap: wrap; gap: 8px; }
|
||||
.mechanic-mitig-shield { font-size: 12px; color: var(--t3); }
|
||||
.mechanic-mitig-val { font-weight: 500; }
|
||||
.mechanic-mitig--ok { color: var(--green); }
|
||||
.mechanic-mitig--risk { color: var(--red); }
|
||||
|
||||
.mechanic-assignments {
|
||||
display: flex;
|
||||
@ -573,6 +578,38 @@
|
||||
.info-warning--job { color: var(--t2); }
|
||||
.info-warning--missing { color: var(--red); }
|
||||
|
||||
.info-section--extra { margin-top: 12px; }
|
||||
|
||||
.info-extra-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.info-extra-label {
|
||||
font-size: 12px;
|
||||
color: var(--t2);
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.info-extra-input-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.info-extra-input {
|
||||
width: 52px;
|
||||
font-size: 13px;
|
||||
padding: 3px 6px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.info-extra-unit {
|
||||
font-size: 12px;
|
||||
color: var(--t3);
|
||||
}
|
||||
|
||||
/* ── Folder Sidebar ──────────────────────────────────────────────────────────── */
|
||||
.folder-section { margin-bottom: 2px; }
|
||||
|
||||
|
||||
@ -435,6 +435,15 @@ function avgNonTankMaxHp(plan) {
|
||||
return Math.round(hps.reduce((s, v) => s + v, 0) / hps.length);
|
||||
}
|
||||
|
||||
function simulateDrMultiplier(mechanic) {
|
||||
let mult = 1;
|
||||
for (const a of mechanic.assignments ?? []) {
|
||||
if (a.buffType === 'shield') continue;
|
||||
mult *= (1 - (ABILITY_DR[a.ability] ?? 0));
|
||||
}
|
||||
return mult;
|
||||
}
|
||||
|
||||
function renderMechanicListHtml(plan) {
|
||||
const mechanics = visiblePlanMechanics(plan);
|
||||
if (mechanics.length === 0) {
|
||||
@ -483,6 +492,14 @@ function renderMechanicListHtml(plan) {
|
||||
: badgeHtml;
|
||||
}).join('');
|
||||
|
||||
const drOnly = m.unmitigatedDamage ? Math.round(m.unmitigatedDamage * simulateDrMultiplier(m)) : 0;
|
||||
const shieldVal = (plan.shieldK ?? 0) * 1000;
|
||||
const mitigFull = Math.max(0, drOnly - shieldVal);
|
||||
const hasDrAssign = (m.assignments ?? []).some(a => a.buffType !== 'shield' && (ABILITY_DR[a.ability] ?? 0) > 0);
|
||||
const hasShield = shieldVal > 0;
|
||||
const drOnlyCls = avgHp ? (drOnly <= avgHp ? 'mechanic-mitig--ok' : 'mechanic-mitig--risk') : '';
|
||||
const fullCls = avgHp ? (mitigFull <= avgHp ? 'mechanic-mitig--ok' : 'mechanic-mitig--risk') : '';
|
||||
|
||||
return `
|
||||
<div class="mechanic-card" data-mechanic-id="${escHtml(m.id)}">
|
||||
<div class="mechanic-time">${escHtml(fmtTimestamp(m.timestamp))}</div>
|
||||
@ -493,6 +510,10 @@ function renderMechanicListHtml(plan) {
|
||||
? `<div class="mechanic-dmg">${fmtNumber(m.unmitigatedDamage)} unmitigiert${avgHp ? ` <span class="mechanic-avg-hp">∅ ${fmtNumber(avgHp)} HP</span>` : ''}</div>`
|
||||
: ''
|
||||
}
|
||||
${hasDrAssign || hasShield ? `<div class="mechanic-dmg mechanic-mitig-row">
|
||||
${hasDrAssign ? `<span class="mechanic-mitig-val${drOnlyCls ? ' ' + drOnlyCls : ''}">→ ${fmtNumber(drOnly)}</span> mitigiert` : ''}
|
||||
${hasShield ? `<span class="mechanic-mitig-shield${fullCls ? ' ' + fullCls : ''}">Mitigation mit Schild ${fmtNumber(mitigFull)}</span>` : ''}
|
||||
</div>` : ''}
|
||||
<div class="mechanic-assignments">${assignHtml}</div>
|
||||
${m.notes ? `<div class="mechanic-notes">${escHtml(m.notes)}</div>` : ''}
|
||||
<div class="mechanic-edit-hint">Klicken zum Bearbeiten</div>
|
||||
@ -1072,7 +1093,29 @@ function renderInfoPanel(plan) {
|
||||
warningsHtml += '</div>';
|
||||
}
|
||||
|
||||
el.innerHTML = legendHtml + warningsHtml;
|
||||
const extraInfoHtml = `
|
||||
<div class="info-section info-section--extra">
|
||||
<div class="info-section-title">Zusätzliche Informationen</div>
|
||||
<div class="info-extra-row">
|
||||
<label class="info-extra-label">SGE/SCH Schild</label>
|
||||
<div class="info-extra-input-wrap">
|
||||
<input type="text" inputmode="numeric" id="plan-shield-k-input" class="info-extra-input"
|
||||
value="${plan.shieldK ?? ''}" placeholder="0">
|
||||
<span class="info-extra-unit">k</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
el.innerHTML = legendHtml + warningsHtml + extraInfoHtml;
|
||||
|
||||
const shieldInput = el.querySelector('#plan-shield-k-input');
|
||||
if (shieldInput) {
|
||||
shieldInput.addEventListener('input', () => {
|
||||
const val = parseFloat(shieldInput.value) || 0;
|
||||
updatePlan(plan.id, { shieldK: val > 0 ? val : null });
|
||||
refreshMechanicList(plan.id);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function removeAssignment(planId, mechanicId, abilityName) {
|
||||
@ -1468,6 +1511,27 @@ const MITIG_ICONS = {
|
||||
'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,
|
||||
};
|
||||
|
||||
// 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 = [
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user