diff --git a/fabula-ultima-sheet.js b/fabula-ultima-sheet.js index e5cdaa3..10b3f21 100644 --- a/fabula-ultima-sheet.js +++ b/fabula-ultima-sheet.js @@ -248,35 +248,54 @@ function addSpell() { spells.push({}); renderSpells(); } function removeSpell(i) { spells.splice(i, 1); renderSpells(); } // ── SAVE / LOAD ──────────────────────────────────── +// Short key legend (serialized form only; in-memory objects use full names): +// n=name, pn=pronouns, id=identity, th=theme, og=origin, tr=traits +// lv=level, xp=xp, z=zenit +// im=initMod, df=defense, md=magDef +// dxb=dexBase, dxc=dexCur, inb=insBase, inc=insCur +// mgb=migBase, mgc=migCur, wpb=wlpBase, wpc=wlpCur +// hx=hpMax, hc=hpCur, mx=mpMax, mc=mpCur, ix=ipMax, ic=ipCur +// fp=fp, bp=backpack +// acn=accName, acd=accDesc, amn=armName, amd=armDesc +// mhn=mhName, mhd=mhDesc, ohn=ohName, ohd=ohDesc +// hs=heroicSkills, rn=ritualsNotes +// sa=statusesActive, ma=martialChecked, da=disciplinesChecked +// bo=bonds, pc=primaryClasses, oc=otherClasses, sp=spells +// Nested bonds: n=name, f=feelings +// Nested classes: n=name, b=benefits, s=skills +// Nested spells: n=name, nt=notes, mp=mp, tg=targets, dr=duration function collectData() { const get = id => { const el = document.getElementById(id); return el ? el.value : ''; }; - const statusesActive = [...document.querySelectorAll('.status-item.active-status')].map(el => el.dataset.status); - const martialChecked = [...document.querySelectorAll('.martial-item.checked')].map(el => el.querySelector('span').textContent); - const disciplinesChecked = [...document.querySelectorAll('.disc-item.checked')].map(el => el.querySelector('span').textContent); + const sa = [...document.querySelectorAll('.status-item.active-status')].map(el => el.dataset.status); + const ma = [...document.querySelectorAll('.martial-item.checked')].map(el => el.querySelector('span').textContent); + const da = [...document.querySelectorAll('.disc-item.checked')].map(el => el.querySelector('span').textContent); return { - name: get('charName'), pronouns: get('charPronouns'), - identity: get('charIdentity'), theme: get('charTheme'), origin: get('charOrigin'), - traits: get('charTraits'), - level, xp: get('xpCurrent'), zenit: get('zenit'), - initMod: get('initMod'), defense: get('defense'), magDef: get('magDef'), - dexBase: get('dex-base'), dexCur: get('dex-cur'), - insBase: get('ins-base'), insCur: get('ins-cur'), - migBase: get('mig-base'), migCur: get('mig-cur'), - wlpBase: get('wlp-base'), wlpCur: get('wlp-cur'), - hpMax: get('hpMax'), hpCur: get('hpCur'), - mpMax: get('mpMax'), mpCur: get('mpCur'), - ipMax: get('ipMax'), ipCur: get('ipCur'), + n: get('charName'), pn: get('charPronouns'), + id: get('charIdentity'), th: get('charTheme'), og: get('charOrigin'), + tr: get('charTraits'), + lv: level, xp: get('xpCurrent'), z: get('zenit'), + im: get('initMod'), df: get('defense'), md: get('magDef'), + dxb: get('dex-base'), dxc: get('dex-cur'), + inb: get('ins-base'), inc: get('ins-cur'), + mgb: get('mig-base'), mgc: get('mig-cur'), + wpb: get('wlp-base'), wpc: get('wlp-cur'), + hx: get('hpMax'), hc: get('hpCur'), + mx: get('mpMax'), mc: get('mpCur'), + ix: get('ipMax'), ic: get('ipCur'), fp: get('fpCount'), - backpack: get('backpack'), - accName: get('acc-name'), accDesc: get('acc-desc'), - armName: get('arm-name'), armDesc: get('arm-desc'), - mhName: get('mh-name'), mhDesc: get('mh-desc'), - ohName: get('oh-name'), ohDesc: get('oh-desc'), - heroicSkills: get('heroicSkills'), - ritualsNotes: get('ritualsNotes'), - statusesActive, martialChecked, disciplinesChecked, - bonds, primaryClasses, otherClasses, spells + bp: get('backpack'), + acn: get('acc-name'), acd: get('acc-desc'), + amn: get('arm-name'), amd: get('arm-desc'), + mhn: get('mh-name'), mhd: get('mh-desc'), + ohn: get('oh-name'), ohd: get('oh-desc'), + hs: get('heroicSkills'), + rn: get('ritualsNotes'), + sa, ma, da, + bo: bonds.map(b => ({ n: b.name, f: b.feelings })), + pc: primaryClasses.map(c => ({ n: c.name, b: c.benefits, s: c.skills })), + oc: otherClasses.map(c => ({ n: c.name, b: c.benefits, s: c.skills })), + sp: spells.map(s => ({ n: s.name, nt: s.notes, mp: s.mp, tg: s.targets, dr: s.duration })), }; } @@ -302,54 +321,74 @@ function loadSheet() { function applyData(d) { const set = (id, val) => { const el = document.getElementById(id); if (el && val !== undefined) el.value = val; }; - set('charName', d.name); set('charPronouns', d.pronouns); - set('charIdentity', d.identity); set('charTheme', d.theme); set('charOrigin', d.origin); - set('charTraits', d.traits); - level = d.level || 1; + set('charName', d.n ?? d.name); set('charPronouns', d.pn ?? d.pronouns); + set('charIdentity',d.id ?? d.identity); set('charTheme', d.th ?? d.theme); + set('charOrigin', d.og ?? d.origin); set('charTraits', d.tr ?? d.traits); + level = d.lv ?? d.level ?? 1; document.getElementById('levelDisplay').textContent = level; - set('xpCurrent', d.xp); set('zenit', d.zenit); - set('initMod', d.initMod); set('defense', d.defense); set('magDef', d.magDef); - set('dex-base', d.dexBase); set('dex-cur', d.dexCur); - set('ins-base', d.insBase); set('ins-cur', d.insCur); - set('mig-base', d.migBase); set('mig-cur', d.migCur); - set('wlp-base', d.wlpBase); set('wlp-cur', d.wlpCur); - set('hpMax', d.hpMax); set('hpCur', d.hpCur); - set('mpMax', d.mpMax); set('mpCur', d.mpCur); - set('ipMax', d.ipMax); set('ipCur', d.ipCur); - set('fpCount', d.fp); - set('backpack', d.backpack); - set('acc-name', d.accName); set('acc-desc', d.accDesc); - set('arm-name', d.armName); set('arm-desc', d.armDesc); - set('mh-name', d.mhName); set('mh-desc', d.mhDesc); - set('oh-name', d.ohName); set('oh-desc', d.ohDesc); - set('heroicSkills', d.heroicSkills); - set('ritualsNotes', d.ritualsNotes); + set('xpCurrent', d.xp); set('zenit', d.z ?? d.zenit); + set('initMod', d.im ?? d.initMod); set('defense', d.df ?? d.defense); + set('magDef', d.md ?? d.magDef); + set('dex-base', d.dxb ?? d.dexBase); set('dex-cur', d.dxc ?? d.dexCur); + set('ins-base', d.inb ?? d.insBase); set('ins-cur', d.inc ?? d.insCur); + set('mig-base', d.mgb ?? d.migBase); set('mig-cur', d.mgc ?? d.migCur); + set('wlp-base', d.wpb ?? d.wlpBase); set('wlp-cur', d.wpc ?? d.wlpCur); + set('hpMax', d.hx ?? d.hpMax); set('hpCur', d.hc ?? d.hpCur); + set('mpMax', d.mx ?? d.mpMax); set('mpCur', d.mc ?? d.mpCur); + set('ipMax', d.ix ?? d.ipMax); set('ipCur', d.ic ?? d.ipCur); + set('fpCount', d.fp); + set('backpack', d.bp ?? d.backpack); + set('acc-name', d.acn ?? d.accName); set('acc-desc', d.acd ?? d.accDesc); + set('arm-name', d.amn ?? d.armName); set('arm-desc', d.amd ?? d.armDesc); + set('mh-name', d.mhn ?? d.mhName); set('mh-desc', d.mhd ?? d.mhDesc); + set('oh-name', d.ohn ?? d.ohName); set('oh-desc', d.ohd ?? d.ohDesc); + set('heroicSkills', d.hs ?? d.heroicSkills); + set('ritualsNotes', d.rn ?? d.ritualsNotes); // Statuses + const sa = d.sa ?? d.statusesActive ?? []; document.querySelectorAll('.status-item').forEach(el => { - const active = (d.statusesActive || []).includes(el.dataset.status); + const active = sa.includes(el.dataset.status); el.classList.toggle('active-status', active); el.querySelector('.status-check').textContent = active ? '✗' : ''; }); // Martial + const ma = d.ma ?? d.martialChecked ?? []; document.querySelectorAll('.martial-item').forEach(el => { - const checked = (d.martialChecked || []).includes(el.querySelector('span').textContent); + const checked = ma.includes(el.querySelector('span').textContent); el.classList.toggle('checked', checked); el.querySelector('.martial-box').textContent = checked ? '✓' : ''; }); // Disciplines + const da = d.da ?? d.disciplinesChecked ?? []; document.querySelectorAll('.disc-item').forEach(el => { - const checked = (d.disciplinesChecked || []).includes(el.querySelector('span').textContent); + const checked = da.includes(el.querySelector('span').textContent); el.classList.toggle('checked', checked); el.querySelector('.disc-box').textContent = checked ? '✓' : ''; }); - if (d.bonds) { bonds = d.bonds; renderBonds(); } - if (d.primaryClasses) { primaryClasses = d.primaryClasses; renderPrimaryClasses(); } - if (d.otherClasses) { otherClasses = d.otherClasses; renderOtherClasses(); } - if (d.spells) { spells = d.spells; renderSpells(); } + const rawBonds = d.bo ?? d.bonds; + if (rawBonds) { + bonds = rawBonds.map(b => ({ name: b.n ?? b.name, feelings: b.f ?? b.feelings ?? [] })); + renderBonds(); + } + const rawPrimary = d.pc ?? d.primaryClasses; + if (rawPrimary) { + primaryClasses = rawPrimary.map(c => ({ name: c.n ?? c.name, benefits: c.b ?? c.benefits, skills: c.s ?? c.skills })); + renderPrimaryClasses(); + } + const rawOther = d.oc ?? d.otherClasses; + if (rawOther) { + otherClasses = rawOther.map(c => ({ name: c.n ?? c.name, benefits: c.b ?? c.benefits, skills: c.s ?? c.skills })); + renderOtherClasses(); + } + const rawSpells = d.sp ?? d.spells; + if (rawSpells) { + spells = rawSpells.map(s => ({ name: s.n ?? s.name, notes: s.nt ?? s.notes, mp: s.mp, targets: s.tg ?? s.targets, duration: s.dr ?? s.duration })); + renderSpells(); + } renderFP(); updateXPBar(); @@ -363,7 +402,7 @@ function exportSheet() { const blob = new Blob([json], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); - const charName = (data.name || 'character').replace(/[^a-z0-9_\- ]/gi, '').trim() || 'character'; + const charName = (data.n || 'character').replace(/[^a-z0-9_\- ]/gi, '').trim() || 'character'; a.href = url; a.download = charName + '-fabula-ultima.json'; a.click();