Original open-world wuxia browser RPG: - Graphical 48px tile world (12 locations) with walkable character, NPC interaction, action spots, roaming enemies, travel portals - Tactical 10x7 grid battles: shapes, statuses, internals, 30+ techniques incl. tier-V ultimates & support arts across 6 weapon types - Full equipment: weapon/head/body/feet/2 accessories, 5 tiers, smithing - 6-chapter main story + side jobs board + bounty hunts + tournament - Crafting (smith/alchemy), fishing minigame, gambling, pickpocketing - Companions with chemistry passives, affection, romance, sects - Achievements, monster codex, day/night cycle, endings - 25-test headless smoke suite; no-cache static server included
65 lines
2.9 KiB
JavaScript
65 lines
2.9 KiB
JavaScript
'use strict';
|
|
/* ============================================================
|
|
MAIN — boot, data validation
|
|
============================================================ */
|
|
|
|
function Data_validate() {
|
|
const problems = [];
|
|
const chkItem = (id, where) => { if (!ITEMS[id]) problems.push(`missing item ${id} (${where})`); };
|
|
// shops
|
|
for (const nid of Object.keys(NPCS)) {
|
|
const n = NPCS[nid];
|
|
if (n.shop) n.shop.stock.forEach(i => chkItem(i, 'shop ' + nid));
|
|
(n.teach || []).forEach(t => {
|
|
if (t.tech && !TECHNIQUES[t.tech]) problems.push(`npc ${nid} teaches missing tech ${t.tech}`);
|
|
if (t.internal && !INTERNALS[t.internal]) problems.push(`npc ${nid} teaches missing internal ${t.internal}`);
|
|
});
|
|
if (n.dlg && !DIALOGUES[n.dlg]) problems.push(`npc ${nid} dialogue missing: ${n.dlg}`);
|
|
}
|
|
// locations
|
|
for (const lid of Object.keys(LOCS)) {
|
|
LOCS[lid].npcs.forEach(nid => { if (!NPCS[nid]) problems.push(`loc ${lid} npc missing ${nid}`); });
|
|
}
|
|
TRAVEL.forEach(([a, b]) => {
|
|
if (!LOCS[a]) problems.push('travel missing loc ' + a);
|
|
if (!LOCS[b]) problems.push('travel missing loc ' + b);
|
|
});
|
|
// quests referenced in dialogues (rough scan)
|
|
const refs = JSON.stringify(DIALOGUES).match(/q_[a-z0-9_]+/g) || [];
|
|
[...new Set(refs)].forEach(q => { if (!QUESTS[q]) problems.push('dialogue references unknown quest ' + q); });
|
|
// techniques referenced as manuals
|
|
Object.values(ITEMS).filter(i => i.type === 'manual').forEach(m => {
|
|
if (!TECHNIQUES[m.teach] && !INTERNALS[m.teach] && !LIGHTNESS[m.teach]) problems.push(`manual ${m.id} teaches unknown ${m.teach}`);
|
|
});
|
|
// origins items/techs
|
|
Object.entries(ORIGINS).forEach(([oid, o]) => {
|
|
(o.items || []).forEach(i => chkItem(i, 'origin ' + oid));
|
|
(o.techs || []).forEach(t => { if (!TECHNIQUES[t]) problems.push(`origin ${oid} tech ${t} missing`); });
|
|
});
|
|
// upgrades / recipes
|
|
Object.entries(SMITH_UPGRADES).forEach(([k, up]) => { chkItem(k, 'smith'); chkItem(up.to, 'smith->'); Object.keys(up.mats).forEach(m => chkItem(m, 'smith mat')); });
|
|
ALCHEMY_RECIPES.forEach(r => { chkItem(r.out, 'alch'); Object.keys(r.mats).forEach(m => chkItem(m, 'alch mat')); });
|
|
// dialogues nodes integrity
|
|
for (const did of Object.keys(DIALOGUES)) {
|
|
const D = DIALOGUES[did];
|
|
for (const nid of Object.keys(D.nodes)) {
|
|
const opts = typeof D.nodes[nid].opts === 'function' ? [] : (D.nodes[nid].opts || []);
|
|
opts.forEach(o => { if (o.next && !D.nodes[o.next]) problems.push(`${did}.${nid} -> missing node ${o.next}`); });
|
|
}
|
|
}
|
|
if (problems.length) {
|
|
console.warn('[Data] issues found:');
|
|
problems.forEach(p => console.warn(' -', p));
|
|
} else {
|
|
console.log('[Data] validation passed ✔');
|
|
}
|
|
return problems;
|
|
}
|
|
|
|
window.addEventListener('load', () => {
|
|
Data_validate();
|
|
UI.init();
|
|
UI.renderTitleMenu();
|
|
Log.add('The jianghu stirs\u2026', 'sys');
|
|
});
|