forked from xziino/ff14-mitigator
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);
|
color: var(--t3);
|
||||||
margin-left: 6px;
|
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 {
|
.mechanic-assignments {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -573,6 +578,38 @@
|
|||||||
.info-warning--job { color: var(--t2); }
|
.info-warning--job { color: var(--t2); }
|
||||||
.info-warning--missing { color: var(--red); }
|
.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 Sidebar ──────────────────────────────────────────────────────────── */
|
||||||
.folder-section { margin-bottom: 2px; }
|
.folder-section { margin-bottom: 2px; }
|
||||||
|
|
||||||
|
|||||||
@ -435,6 +435,15 @@ function avgNonTankMaxHp(plan) {
|
|||||||
return Math.round(hps.reduce((s, v) => s + v, 0) / hps.length);
|
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) {
|
function renderMechanicListHtml(plan) {
|
||||||
const mechanics = visiblePlanMechanics(plan);
|
const mechanics = visiblePlanMechanics(plan);
|
||||||
if (mechanics.length === 0) {
|
if (mechanics.length === 0) {
|
||||||
@ -483,6 +492,14 @@ function renderMechanicListHtml(plan) {
|
|||||||
: badgeHtml;
|
: badgeHtml;
|
||||||
}).join('');
|
}).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 `
|
return `
|
||||||
<div class="mechanic-card" data-mechanic-id="${escHtml(m.id)}">
|
<div class="mechanic-card" data-mechanic-id="${escHtml(m.id)}">
|
||||||
<div class="mechanic-time">${escHtml(fmtTimestamp(m.timestamp))}</div>
|
<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>`
|
? `<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>
|
<div class="mechanic-assignments">${assignHtml}</div>
|
||||||
${m.notes ? `<div class="mechanic-notes">${escHtml(m.notes)}</div>` : ''}
|
${m.notes ? `<div class="mechanic-notes">${escHtml(m.notes)}</div>` : ''}
|
||||||
<div class="mechanic-edit-hint">Klicken zum Bearbeiten</div>
|
<div class="mechanic-edit-hint">Klicken zum Bearbeiten</div>
|
||||||
@ -1072,7 +1093,29 @@ function renderInfoPanel(plan) {
|
|||||||
warningsHtml += '</div>';
|
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) {
|
function removeAssignment(planId, mechanicId, abilityName) {
|
||||||
@ -1468,6 +1511,27 @@ const MITIG_ICONS = {
|
|||||||
'Improvised Finish': 'assets/icons/mitigation/improvised-finish.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,
|
||||||
|
};
|
||||||
|
|
||||||
// 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 = [
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user