import{l as Z,h as s,c as N}from"./schema-B1NKxfZc.js";import{A as Q,b as Y,d as q,e as G,S as K}from"./sprite-view-Br3916N3.js";import{s as W}from"./spell-icons-DyCY_CiT.js";const X=["skill_id","name","skill_level","skill_number","mpConsume","hpConsume","itemConsumeId","itemConsumeCount","reuseDelay","buffDuration","target","target_to","damage_value","damage_dice","damage_dice_count","probability_value","probability_dice","attr","type","lawful","ranged","area","through","id","nameid","action_id","castgfx","sysmsgID_happen","sysmsgID_stop","sysmsgID_fail"];function ee(t){const i=[];let n="",e=!1;for(let l=0;l<t.length;l++){const f=t[l];if(e){if(f==="\\"){n+=t[++l]??"";continue}if(f==="'"){e=!1;continue}n+=f}else f==="'"?e=!0:f===","?(i.push(n.trim()),n=""):n+=f}return i.push(n.trim()),i}function te(t){const i=/CREATE TABLE\s+`?skills`?\s*\(([\s\S]*?)\n\)/i.exec(t);if(!i)return null;const n=[...i[1].matchAll(/^\s*`([A-Za-z_][A-Za-z0-9_]*)`\s+[a-z]/gim)].map(e=>e[1]);return n.length?n:null}function se(t){const i={};t=t.replace(/^[ \t]*--.*$/gm,"");const n=te(t),e=/INSERT\s+INTO\s+`?skills`?\s*(\(([^)]*)\))?\s*VALUES\s*([\s\S]*?);\s*(?:\r?\n|$)/gi;for(const l of t.matchAll(e)){const d=(l[2]?l[2].split(",").map(u=>u.trim().replace(/`/g,"")):null)??n??X,m=l[3];for(const u of m.split(/\)\s*,\s*\(/)){const g=ee(u.replace(/^\s*\(/,"").replace(/\)\s*$/,""));if(g.length!==d.length)continue;const v=$=>{const p=d.indexOf($);return p<0?void 0:g[p]},c=$=>Number(v($))||0,k=v("skill_id");if(k===void 0)continue;const w=(v("name")??"").replace(/([a-z])([A-Z])/g,"$1 $2");if(!w||w==="none")continue;const h={name:w,lvl:c("skill_level"),num:c("skill_number"),mp:c("mpConsume"),hp:c("hpConsume"),reuse:c("reuseDelay"),buff:c("buffDuration"),target:v("target")??"",attr:c("attr"),type:c("type"),nameid:v("nameid")??"",act:c("action_id"),gfx:c("castgfx"),msgOn:c("sysmsgID_happen"),msgOff:c("sysmsgID_stop"),msgFail:c("sysmsgID_fail")};v("castgfx2")!==void 0&&c("castgfx2")&&(h.gfx2=c("castgfx2")),v("buff_iconid")!==void 0&&c("buff_iconid")&&(h.buffIcon=c("buff_iconid")),v("buff_stringid")!==void 0&&c("buff_stringid")&&(h.buffString=c("buff_stringid")),i[k]=h}}return i}const C="l15.spelllab",ne=t=>[[1,"earth"],[2,"fire"],[4,"water"],[8,"wind"],[16,"holy"],[32,"dark"]].filter(([i])=>t&i).map(([,i])=>i).join("+")||"none",o={skills:{},spells:[],arts:[],desc:[],strings:[],spellDesc:[],spellExDesc:[],over:{},haveIcon:new Set,dupIcon:{},imported:0,selected:0,filter:"",cls:"",show:"all"},ie=()=>{try{o.over=JSON.parse(localStorage.getItem(C)||"{}")}catch{o.over={}}},T=()=>{try{localStorage.setItem(C,JSON.stringify(o.over))}catch{}},oe=t=>o.over[t]??={},I=(t,i)=>{Object.assign(oe(t),i);for(const n of Object.keys(o.over[t])){const e=o.over[t][n];(e===void 0||e===""||e===!1)&&delete o.over[t][n]}Object.keys(o.over[t]).length||delete o.over[t],T()};let S=[];function R(){const t=new Map;for(const i of o.spells){const n=W(i);t.set(n,{skillId:n,cli:i})}for(const[i,n]of Object.entries(o.skills)){const e=Number(i);if(e>=1e4&&e<2e4)continue;const l=t.get(e)??{skillId:e};l.srv=n,t.set(e,l)}for(const i of o.arts)t.set(i.id,{skillId:i.id,art:i,srv:o.skills[i.id]});S=[...t.values()].sort((i,n)=>i.skillId-n.skillId)}function z(t){const i=t.srv?.nameid?.match(/^\$(\d+)$/);if(i){const e=o.desc[Number(i[1])];if(e)return e.replace(/^\(|\)$/g,"")}if(t.srv?.name&&t.srv.name!=="none")return t.srv.name;if(t.art?.desc){const e=t.art.desc.match(/^\$(\d+)$/),l=e?o.desc[Number(e[1])]:"";if(l)return l.replace(/^\(|\)$/g,"")}const n=o.spellDesc[t.skillId]?.replace(/^\d+\s*/,"");return n||t.cli?.name||t.art?.name||`Skill ${t.skillId}`}const L=t=>o.over[t.skillId]?.icon??t.cli?.xicon??t.cli?.icon??t.art?.icon??t.srv?.buffIcon??null,F=t=>o.over[t.skillId]?.gfx??t.srv?.gfx??0;function U(t){const i=[];return!t.srv&&!t.art&&i.push("no server skills row — the loaded dump does not cover this spell; import your live table"),!t.cli&&!t.art&&i.push("no client SpellList row — nothing in the paks describes this spell"),t.srv&&t.cli&&t.cli.mp!==void 0&&t.srv.mp!==t.cli.mp&&i.push(`MP differs: server ${t.srv.mp}, client ${t.cli.mp}`),V(t)&&i.push("icon and xicon are DIFFERENT PICTURES — pick the one the desktop client shows"),t.srv&&!t.srv.gfx&&i.push("no castgfx — the server plays no effect sprite for this spell"),t.srv?.buffIcon&&i.push(`the server's own buff_iconid is ${t.srv.buffIcon} — no need to choose a buff icon here`),i}let b=null,j=null;async function le(t,i){const n=new Y;await n.init({width:260,height:220,background:723983,antialias:!1}),t.append(n.canvas);const e=new q(i,64*1024*1024);await e.loadIndex();const l=new G;l.position.set(130,180),l.scale.set(.6),n.stage.addChild(l),b=new K(e),b.position.set(-48,0),l.addChild(b),j=n,n.ticker.add(f=>b?.update(f.deltaMS)),window.__fx={app:n,view:b,loader:e}}async function ce(t){if(!b||(b.visible=!!t,!t))return;await b.setSprite(t);const i=b.meta;if(!i)return;const n=i.actions[0]?0:Number(Object.keys(i.actions)[0]??-1);n<0||(await b.prefetch(n),b.setHeading(0),b.play(n,{loop:!0,restart:!0}))}const A=(t,i)=>t.url(`icons/${i}.png`),H=t=>t!=null&&t>0&&(!o.haveIcon.size||o.haveIcon.has(t)),D=t=>o.dupIcon[t]??t,V=t=>t.cli?.icon!==void 0&&t.cli.xicon!==void 0&&D(t.cli.icon)!==D(t.cli.xicon);function ae(t){const i=[],n=(l,f)=>{if(l===void 0||l<=0||!H(l))return;const d=i.find(m=>m.id===l||D(m.id)===D(l));if(d){d.label.includes(f)||(d.label+=`/${f}`);return}i.push({id:l,label:f})};n(t.cli?.xicon,"xicon"),n(t.cli?.icon,"icon"),n(t.art?.icon,"art"),n(t.art?.icon2,"art2"),n(o.over[t.skillId]?.icon,"picked"),n(o.over[t.skillId]?.buffIcon,"buff"),n(t.srv?.buffIcon,"server buff");const e=t.cli?.xicon??t.cli?.icon??t.art?.icon;if(e)for(const l of[-2,-1,1,2,3])n(e+l,`${l>0?"+":""}${l}`);return i}function M(t,i,n,e){const l=[...o.haveIcon].sort((c,k)=>c-k),f=o.over[i.skillId]?.[n]??L(i)??0;let d=Math.max(0,l.findIndex(c=>c>=f));const m=s("div.sl-strip"),u=()=>{N(m);for(const c of l.slice(d,d+120))m.append(s(`div.sl-cand${c===(o.over[i.skillId]?.[n]??-1)?".on":""}`,{title:`icon ${c}`,onclick:()=>{I(i.skillId,{[n]:c}),e()}},s("img",{src:A(t,c),loading:"lazy"}),s("span",{},String(c))))},g=s("div",{style:{display:"none"}},s("div.sl-pick",{},s("button.sl-btn",{onclick:()=>{d=Math.max(0,d-120),u()}},"← earlier"),s("button.sl-btn",{onclick:()=>{d=Math.min(l.length-1,d+120),u()}},"later →"),"jump to id ",s("input",{type:"number",onchange:c=>{d=Math.max(0,l.findIndex(k=>k>=Number(c.target.value))),u()}})),m),v=s("button.sl-btn",{onclick:()=>{const c=g.style.display==="none";g.style.display=c?"":"none",c&&u()}},"Browse all icons…");return s("div",{},v,g)}function J(t,i,n,e){const l=o.over[i.skillId]?.[n]??(n==="icon"?L(i):void 0),f=s("div.sl-strip");for(const d of ae(i)){const m=s(`div.sl-cand${d.id===l?".on":""}`,{title:`icon ${d.id}`,onclick:()=>{I(i.skillId,{[n]:d.id}),e()}},s("img",{src:A(t,d.id),loading:"lazy",onerror:u=>{u.target.style.opacity="0.15"}}),s("span",{},s("b",{},String(d.id)),` ${d.label}`));f.append(m)}return f}function P(t){const i=s("table.sl-kv");for(const[n,e,l]of t)e===void 0||e===""||i.append(s("tr",{},s("td",{},n),s("td",l?{class:"sl-diff"}:{},String(e))));return i}function re(t,i,n){N(t);const e=S.find(p=>p.skillId===o.selected);if(!e){t.append(s("p",{},"Pick a spell on the left."));return}const l=o.over[e.skillId]??{},f=U(e);if(t.append(s("h2.sl-h1",{},`${z(e)}`)),t.append(s("p.sl-h2",{},`skill ${e.skillId} · client id ${e.cli?.id??e.skillId-1}`+(e.cli?` · ${e.cli.cls} grade ${e.cli.grade}`:"")+(e.art?` · ${e.art.className} arts sheet ${e.art.sheet??"?"} @ ${e.art.sheetLoc??"?"}`:"")+(e.srv?` · level ${e.srv.lvl} slot ${e.srv.num}`:""))),f.length){const p=s("div.sl-sec",{},s("h3",{},"Needs a look")),x=s("div");for(const O of f)x.append(s("div.sl-warnline",{},`• ${O}`));p.append(x),t.append(p)}const d=s("div.sl-sec",{},s("h3",{},"Spell icon (the magic window)")),m=s("div",{},s("div.sl-pick",{},"chosen: ",s("input",{type:"number",value:String(o.over[e.skillId]?.icon??L(e)??""),onchange:p=>{I(e.skillId,{icon:Number(p.target.value)||void 0}),n()}}),s("button.sl-btn",{onclick:()=>{I(e.skillId,{icon:void 0}),n()}},"clear"),l.icon?s("span",{}," (overridden)"):s("span",{}," (from SpellList.xml)")),J(i,e,"icon",n),M(i,e,"icon",n));d.append(m),t.append(d);const u=s("div.sl-sec",{},s("h3",{},"Buff-bar icon (while the spell is active)"));u.append(s("div",{},s("div.sl-pick",{},"chosen: ",s("input",{type:"number",value:String(l.buffIcon??""),onchange:p=>{I(e.skillId,{buffIcon:Number(p.target.value)||void 0}),n()}}),s("button.sl-btn",{onclick:()=>{I(e.skillId,{buffIcon:void 0}),n()}},"clear"),s("span",{},l.buffIcon?" (set)":" (unset — the spell icon is used)")),J(i,e,"buffIcon",n),M(i,e,"buffIcon",n))),t.append(u);const g=F(e),v=s("div.sl-sec",{},s("h3",{},"Cast effect (sprite played on the target)")),c=s("div.sl-fxbox",{id:"sl-fxbox"});v.append(s("div.sl-fx",{},c,s("div",{},s("div.sl-pick",{},"castgfx: ",s("input",{type:"number",value:String(g||""),onchange:p=>{I(e.skillId,{gfx:Number(p.target.value)||void 0}),n()}}),s("button.sl-btn",{onclick:()=>{I(e.skillId,{gfx:void 0}),n()}},"reset"),s("a",{href:`/sprite.html?id=${g}`,target:"_blank"},"open in sprite inspector")),P([["server castgfx",e.srv?.gfx],["second effect (castgfx2)",e.srv?.gfx2],["caster action",e.srv?`${e.srv.act}${e.srv.act===18?" (directional)":e.srv.act===19?" (non-directional)":""}`:void 0],["client category",e.cli?.category],["client attribute",e.cli?.attribute],["aimed",e.cli?.direction===!0?"yes (direction=true)":e.cli?.direction===!1?"no":void 0]])))),t.append(v);const k=s("div.sl-sec",{},s("h3",{},"Server row vs client row")),w=!!(e.srv&&e.cli?.mp!==void 0&&e.srv.mp!==e.cli.mp);k.append(s("div",{},P([["internal name",e.srv?.name],["nameid",e.srv?.nameid],["client name",e.cli?.name??e.art?.name],["desc key",e.cli?.desc],["MP",e.srv?`${e.srv.mp}${e.cli?.mp!==void 0?` (client ${e.cli.mp})`:""}`:e.cli?.mp,w],["HP",e.srv?.hp||e.cli?.hp],["target",e.srv?.target],["reuse",e.srv?.reuse?`${e.srv.reuse}`:void 0],["buff duration",e.srv?.buff?`${e.srv.buff}s`:e.cli?.duration?`${e.cli.duration}s (client)`:void 0],["element",e.srv?ne(e.srv.attr):void 0],["type bits",e.srv?.type],["range",e.cli?.range],["delay",e.cli?.delay??e.art?.delay],["align",e.cli?.align],["needs",[e.cli?.equip,e.cli?.weapon].filter(Boolean).join(" / ")||void 0],["catalyst",e.cli?.catalyst?.map(p=>`type ${p.type} ×${p.count}`).join(", ")],["passive slot",e.cli?.passive??e.art?.passive],["element affinity",e.cli?.major],["art MP by level",e.art?.mpCost?.join("/")],["art HP by level",e.art?.hpCost?.join("/")],["server buff_iconid",e.srv?.buffIcon],["server buff_stringid",e.srv?.buffString?`${e.srv.buffString}: ${o.strings[e.srv.buffString]??"?"}`:void 0],["msg on",e.srv?.msgOn?`${e.srv.msgOn}: ${o.strings[e.srv.msgOn]??"?"}`:void 0],["msg off",e.srv?.msgOff?`${e.srv.msgOff}: ${o.strings[e.srv.msgOff]??"?"}`:void 0],["msg fail",e.srv?.msgFail?`${e.srv.msgFail}: ${o.strings[e.srv.msgFail]??"?"}`:void 0]]))),t.append(k);const h=o.spellExDesc[e.skillId]?.replace(/^\d+\s*/,"").replace(/\^/g,`
`);h&&t.append(s("div.sl-sec",{},s("h3",{},"In-game description"),s("div.sl-desc",{},h)));const $=s("div.sl-sec",{},s("h3",{},"Sign-off"));$.append(s("div",{},s("label",{},s("input",{type:"checkbox",checked:!!l.ok,onchange:p=>{I(e.skillId,{ok:p.target.checked}),n()}})," checked against the desktop client"),s("div.sl-pick",{style:{marginTop:"6px"}},"note: ",s("input",{type:"text",style:{width:"420px"},value:l.note??"",onchange:p=>{I(e.skillId,{note:p.target.value||void 0})}})))),t.append($),ce(g)}const de=se;async function fe(){const t=await Z(),i=new Q(t.assetBase);ie();const n=async(r,a)=>{try{const y=await fetch(i.url(r));return y.ok?await y.json():a}catch{return a}};[o.skills,o.spells,o.arts,o.desc,o.strings,o.spellDesc,o.spellExDesc]=await Promise.all([n("data/skills.json",{}),n("data/spelllist.json",[]),n("data/magicarts.json",[]),n("data/desc-e.json",[]),n("data/string-e.json",[]),n("data/spelldesc-e.json",[]),n("data/spellexdesc-e.json",[])]),o.haveIcon=new Set(await n("icons/index.json",[])),o.dupIcon=await n("icons/dupes.json",{});try{const r=JSON.parse(localStorage.getItem(`${C}.skills`)||"{}");o.imported=Object.keys(r).length,Object.assign(o.skills,r)}catch{}R();const e=s("div.sl-list"),l=s("div.sl-detail"),f=s("span.sl-count"),d=()=>{u(),re(l,i,d),m()},m=()=>{const r=S.filter(y=>o.over[y.skillId]?.ok).length,a=Object.keys(o.over).length;N(f),f.append(s("b",{},String(r)),` / ${S.length} signed off · ${a} edited`,o.imported?` · ${o.imported} skills imported`:"")},u=()=>{N(e);const r=o.filter.toLowerCase();for(const a of S){if(o.cls&&(a.cli?.cls??a.art?.className??"")!==o.cls)continue;const y=U(a),_=o.over[a.skillId];if(o.show==="todo"&&_?.ok||o.show==="done"&&!_?.ok||o.show==="noserver"&&(a.srv||a.art)||o.show==="noclient"&&(a.cli||a.art)||o.show==="twoicons"&&!V(a))continue;const B=z(a);if(r&&!B.toLowerCase().includes(r)&&!String(a.skillId).includes(r)&&!(a.cli?.desc??"").toLowerCase().includes(r))continue;const E=L(a);e.append(s(`div.sl-row${a.skillId===o.selected?".sel":""}${_?.ok?".ok":""}${y.length&&!_?.ok?".warn":""}`,{onclick:()=>{o.selected=a.skillId,d()}},H(E)?s("img.sl-ico",{src:A(i,E),loading:"lazy"}):s("div.sl-ico"),s("div.sl-rmain",{},s("span.sl-rname",{},`${a.skillId}. ${B}`),s("span.sl-rsub",{},`${a.cli?.cls??a.art?.className??"—"}${a.cli?` g${a.cli.grade}`:""} · icon ${E??"—"} · gfx ${F(a)||"—"}`))))}},g=s("input",{type:"search",placeholder:"name or id",oninput:r=>{o.filter=r.target.value,u()}}),v=s("select",{onchange:r=>{o.cls=r.target.value,u()}},s("option",{value:""},"all classes"),...[...new Set(S.map(r=>r.cli?.cls??r.art?.className??"").filter(Boolean))].sort().map(r=>s("option",{value:r},r))),c=s("select",{onchange:r=>{o.show=r.target.value,u()}},s("option",{value:"all"},"everything"),s("option",{value:"todo"},"not signed off"),s("option",{value:"done"},"signed off"),s("option",{value:"noserver"},"missing from the server table"),s("option",{value:"noclient"},"missing from the client table"),s("option",{value:"twoicons"},"two icons to choose between")),k=s("button.sl-btn",{onclick:()=>{const r=new Blob([JSON.stringify(o.over,null,2)],{type:"application/json"}),a=s("a",{href:URL.createObjectURL(r),download:"spell-overrides.json"});a.click(),setTimeout(()=>URL.revokeObjectURL(a.href),5e3)}},"Export overrides"),w=s("button.sl-btn",{onclick:()=>{navigator.clipboard.writeText(JSON.stringify(o.over,null,2)).then(()=>{w.textContent="Copied",setTimeout(()=>{w.textContent="Copy JSON"},1200)})}},"Copy JSON"),h=s("input",{type:"file",accept:".json,.sql,.txt",style:{display:"none"},onchange:async()=>{const r=h.files?.[0];if(!r)return;const a=await r.text();if(/INSERT\s+INTO\s+`?skills`?/i.test(a)){const y=de(a);o.imported=Object.keys(y).length;try{localStorage.setItem(`${C}.skills`,JSON.stringify(y))}catch{}Object.assign(o.skills,y),R()}else try{o.over=JSON.parse(a),T()}catch{alert("Not a spell-overrides JSON or a skills SQL dump.")}h.value="",d()}}),$=s("button.sl-btn",{title:"A spell-overrides.json to carry on from, or a dump of your live `skills` table to fill in the spells the repo SQL predates",onclick:()=>h.click()},"Import…"),p=s("button.sl-btn",{onclick:()=>{confirm("Throw away every override and sign-off?")&&(o.over={},T(),d())}},"Reset");document.body.append(s("div.sl-top",{},s("b",{},"Spell lab"),g,v,c,h,$,k,w,p,f),s("div.sl-main",{},e,l)),o.selected=S[0]?.skillId??0,d();const x=document.createElement("div");await le(x,i);const O=()=>{const r=document.getElementById("sl-fxbox");r&&j&&r.firstChild!==j.canvas&&r.append(j.canvas)};new MutationObserver(O).observe(l,{childList:!0,subtree:!0}),O()}fe().catch(t=>{console.error(t),document.body.insertAdjacentText("beforeend",`fatal: ${t.message}`)});
