fighting-konoha/passives.js

88 lines
6.4 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Helper pasif, kelemahan, dan gaya bertarung (RENCANA 3.2 dan 3.3). Data: content/passives.js (window.PASSIVES, window.STYLES).
Berkas ini tidak menyentuh mesin: game.js memanggil Passives.merge(id, gaya) saat pertandingan dimulai lalu membaca angkanya lewat passiveMod(actor, key, base).
Di sini juga teks angka yang tampil ke pemain (menu Karakter, layar VS, daftar gerakan), disusun otomatis dari `mods` agar selalu sama dengan yang dipakai mesin. */
(() => {
'use strict';
// Kunci yang dikenal. up = 'good' bila angka naik menguntungkan pemilik (damage keluar, kecepatan, ...), 'bad' bila naik merugikan (damage masuk). add = kunci penambah.
const KEYS = {
dmgOut: { label: 'Damage', short: 'Damage', up: 'good' },
dmgBasic: { label: 'Damage pukulan dasar', short: 'Pukulan dasar', up: 'good' },
dmgSkill: { label: 'Damage skill dan ultimate', short: 'Skill dan ultimate', up: 'good' },
dmgIn: { label: 'Damage yang diterima', short: 'Damage diterima', up: 'bad' },
walk: { label: 'Kecepatan jalan', short: 'Jalan', up: 'good' },
cd: { label: 'Kecepatan pulih cooldown', short: 'Pulih cooldown', up: 'good' },
cdSkill: { label: 'Kecepatan pulih skill', short: 'Pulih skill', up: 'good' },
cdUlt: { label: 'Kecepatan pulih ultimate', short: 'Pulih ultimate', up: 'good' },
parry: { label: 'Jendela parry', short: 'Parry', up: 'good' },
modeDur: { label: 'Durasi mode', short: 'Durasi mode', up: 'good' },
regen: { label: 'Pemulihan HP', short: 'Pulih HP', up: 'good', add: true, unit: ' HP/dtk' }
};
const STYLE_ORDER = ['agresif', 'seimbang', 'bertahan'], DEFAULT_STYLE = 'seimbang';
const esc = t => String(t ?? '').replace(/[&<>"']/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]));
const styles = () => window.STYLES || {};
const styleKey = k => (styles()[k] ? k : DEFAULT_STYLE);
const info = id => { const p = window.PASSIVES?.[id]; return p ? { id, name: p.name, desc: p.desc, flaw: p.flaw || { name: '', desc: '' }, mods: p.mods || {} } : null; };
const style = k => { const key = styleKey(k), s = styles()[key] || { name: 'SEIMBANG', desc: '', mods: {} }; return { key, name: s.name, desc: s.desc, mods: s.mods || {} }; };
// Gabungan angka perk + kelemahan karakter dan gaya yang dipilih: pengali dikalikan, kunci penambah dijumlahkan. Hanya kunci yang dikenal.
function merge(id, styleName) {
const out = {};
for (const src of [window.PASSIVES?.[id]?.mods, styles()[styleKey(styleName)]?.mods]) {
if (!src) continue;
for (const [k, v] of Object.entries(src)) { const def = KEYS[k]; if (!def || !Number.isFinite(v)) continue; out[k] = def.add ? (out[k] || 0) + v : (out[k] ?? 1) * v; }
}
return out;
}
// Satu butir efek sebagai teks: "Damage yang diterima -8%", "Pemulihan HP +0,5 HP/dtk". Koma desimal sesuai bahasa Indonesia.
function effectText(k, v, short) {
const def = KEYS[k]; if (!def) return '';
const label = short ? def.short || def.label : def.label;
if (def.add) return `${label} +${String(Math.round(v * 10) / 10).replace('.', ',')}${def.unit || ''}`;
const pct = Math.round((v - 1) * 100);
return pct ? `${label} ${pct > 0 ? '+' : '−'}${Math.abs(pct)}%` : '';
}
// Pisahkan efek menguntungkan (good) dan merugikan (bad) dari arah angkanya.
function effects(mods, short) {
const good = [], bad = [];
for (const [k, v] of Object.entries(mods || {})) {
const def = KEYS[k]; if (!def) continue;
const delta = def.add ? v : v - 1, text = effectText(k, v, short); if (!text || !delta) continue;
((delta > 0) === (def.up === 'good') ? good : bad).push(text);
}
return { good, bad };
}
// Perk dan kelemahan untuk ditampilkan: nama, cerita, dan angka otomatis.
function describe(id, short) {
const p = info(id); if (!p) return null;
const fx = effects(p.mods, short);
return { id, perk: { name: p.name, desc: p.desc, fx: fx.good.join(', ') }, flaw: { name: p.flaw.name, desc: p.flaw.desc, fx: fx.bad.join(', ') } };
}
function styleView(k, short) { const s = style(k), fx = effects(s.mods, short); return { key: s.key, name: s.name, desc: s.desc, good: fx.good, bad: fx.bad, fx: [...fx.good, ...fx.bad].join(', ') }; }
// Gaya acak untuk CPU. `rand` (0..1) dari pengacak kosmetik penelepon; bukan random() mesin.
const randomStyle = rand => STYLE_ORDER[Math.min(STYLE_ORDER.length - 1, Math.floor((typeof rand === 'function' ? rand() : Math.random()) * STYLE_ORDER.length))];
// ---- layar VS: satu baris kecil per sisi (perk, kelemahan, dan gaya bila bukan Seimbang) ----
function vsHtml(id, styleName) {
const d = describe(id); if (!d) return '';
const sk = styleKey(styleName), tag = sk === DEFAULT_STYLE ? '' : `<em>GAYA ${esc(style(sk).name)}</em>`;
return `<p class="vs-perk" aria-label="Pasif ${esc(d.perk.name)}. Kelemahan ${esc(d.flaw.name)}."><span class="up">▲ ${esc(d.perk.name)}</span><span class="dn">▼ ${esc(d.flaw.name)}</span>${tag}</p>`;
}
function decorateVS(el, vs = {}) {
try {
const sides = [['.vs-side.l .vs-info', vs.player, vs.styles?.player], ['.vs-side.r .vs-info', vs.enemy, vs.styles?.enemy]];
for (const [sel, id, st] of sides) { const box = el.querySelector(sel); if (box && !box.querySelector('.vs-perk')) box.insertAdjacentHTML('beforeend', vsHtml(id, st)); }
} catch (_) { /* hiasan saja; layar VS tetap tampil */ }
}
// ---- menu Karakter (meta.js renderLocker): blok Pasif dan blok Gaya bertarung ----
function lockerHtml(id, styleName) {
const d = describe(id); if (!d) return '';
const sk = styleKey(styleName), sv = styleView(sk);
const row = (cls, mark, name, desc, fx) => `<div class="pas-row ${cls}"><b>${mark} ${esc(name)}</b><span>${esc(desc)}</span>${fx ? `<small>${esc(fx)}</small>` : ''}</div>`;
const pills = STYLE_ORDER.filter(k => styles()[k]).map(k => `<button class="pill ${k === sk ? 'on' : ''}" data-style="${k}">${esc(styles()[k].name)}</button>`).join('');
return `<h5>Pasif dan kelemahan</h5><div class="pas">${row('up', '▲', d.perk.name, d.perk.desc, d.perk.fx)}${row('dn', '▼', d.flaw.name, d.flaw.desc, d.flaw.fx)}</div>`
+ `<h5>Gaya bertarung</h5><div class="pills">${pills}</div><p class="meta-hint pas-style"><b>${esc(sv.name)}</b> — ${esc(sv.desc)}${sv.fx ? `<small>${esc(sv.fx)}</small>` : ''}</p>`;
}
window.Passives = { KEYS, STYLE_ORDER, DEFAULT_STYLE, info, style, styleKey, merge, effects, describe, styleView, randomStyle, vsHtml, decorateVS, lockerHtml };
})();