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
452 lines
34 KiB
JavaScript
452 lines
34 KiB
JavaScript
'use strict';
|
|
/* ============================================================
|
|
DATA: dialogues + data validation. All original fiction.
|
|
Node: { t: string|fn(G), opts: fn(G)->[opt] | array }
|
|
opt: { l: label, cond?: fn, act?(G), next?: nodeId,
|
|
close?, shop?: npcId }
|
|
============================================================ */
|
|
|
|
const DIALOGUES = {};
|
|
function defDlg(id, o) { o.id = id; DIALOGUES[id] = o; }
|
|
|
|
/* ---- Elder Chen ---- */
|
|
defDlg('d_elder', { name: 'Elder Chen', nodes: {
|
|
start: { t: (G) => {
|
|
const q = G.quests['q_main_1'];
|
|
if (!q) return 'Ah, awake at last. Trouble, I\u2019m afraid \u2014 wolves have come down off the ridge bold as tax collectors. Three flocks torn this week.';
|
|
if (!q.done && q.stage === 1) return `The ridge, child. The wolves won\u2019t cull themselves. (${Math.min(G.flags.wolfKills || 0, 3)}/3)`;
|
|
if (!q.done && q.stage === 2) return 'Back already? And the ridge?';
|
|
if (q.done) return 'You carry the look of the wide roads now. The village is proud of you.';
|
|
return 'Speak, child.';
|
|
},
|
|
opts: (G) => {
|
|
const o = []; const q = G.quests['q_main_1'];
|
|
if (!q) {
|
|
o.push({ l: 'I\u2019ll deal with the wolves.', act: () => Game.startQuest('q_main_1'), next: 'accept' });
|
|
o.push({ l: 'Why me?', next: 'why' });
|
|
} else if (!q.done && q.stage === 2) {
|
|
o.push({ l: 'The wolves are dealt with.', act: () => { Game.completeQuest('q_main_1', { exp: 45, silver: 60, fame: 5 }); G.flags.unlockedCity = true; G.flags.mainChapter = 2; }, next: 'reward' });
|
|
} else if (q.done && !G.flags.missingAsked) {
|
|
o.push({ l: 'Anything else troubling the village?', next: 'missing' });
|
|
}
|
|
if (G.flags.unlockedCity && !G.flags.toldAboutYan) {
|
|
o.push({ l: 'What lies beyond the village?', next: 'beyond' });
|
|
}
|
|
o.push({ l: 'Leave.', close: true });
|
|
return o;
|
|
}},
|
|
why: { t: () => 'Because you\u2019re young, armed, and the militia is twelve farmers with pitchforks. Also \u2014 your late father cleared wolves from this same ridge. Habit runs in blood.',
|
|
opts: [{ l: 'Then it runs in mine. I\u2019ll go.', act: () => Game.startQuest('q_main_1'), next: 'accept' }, { l: 'Find someone else.', close: true }] },
|
|
accept: { t: () => 'Good. Take the north path to Cloudmist Ridge. Three kills will teach the pack manners. And \u2014 here, bread for the road.',
|
|
opts: [{ l: '(Take the bread)', act: () => Game.addItem('c_bun', 2), close: true }] },
|
|
reward: { t: () => 'The flocks thank you, though they don\u2019t know it. Sixty silver from the village fund \u2014 and listen: the road east to Qingyun City is open to you. A person of your hands shouldn\u2019t stay cooped in a village.',
|
|
opts: [{ l: 'Thank you, Elder.', close: true }] },
|
|
missing: { t: () => 'One thing gnaws at me. Old Uncle Ping went to cut bamboo three days back and never returned. His grandniece cries at the gate every evening. If you pass the Bamboo Sea\u2026 ask Hunter Ren. Please.',
|
|
opts: [{ l: 'I\u2019ll look for him.', act: () => { G.flags.missingAsked = true; Game.startQuest('q_missing'); }, close: true }, { l: 'I can\u2019t, not now.', close: true }] },
|
|
beyond: { t: () => 'Qingyun City \u2014 money, schools, knives with opinions. The Garrison Liaison there keeps watch on the mountain roads. If those wolves were pushed onto us by something worse, tell Captain Yan. Tell her Elder Chen\u2019s beard stood white before its time.',
|
|
opts: [{ l: 'I\u2019ll remember it.', act: () => { G.flags.toldAboutYan = true; }, close: true }] }
|
|
}});
|
|
|
|
/* ---- Xiaoli ---- */
|
|
defDlg('d_kid', { name: 'Xiaoli', nodes: {
|
|
start: { t: () => Util.pick([
|
|
'They say the Ghost Market only opens after dark, down by the docks! I\u2019m NOT supposed to know that.',
|
|
'Uncle Wang in Ember Town makes swords better if you bring him ore!',
|
|
'Miss Lin from the mountain sect beats up ALL the senior disciples. Even the big ones!',
|
|
'If you meet a drunk beggar in the city\u2026 lose to him on purpose. Trust me!',
|
|
'Alchemist Bai pays GOOD for snake guts. Gross, but good.',
|
|
'The tournament happens every week when the drum tower hits five!'
|
|
]),
|
|
opts: (G) => {
|
|
const o = [];
|
|
if ((G.aff['n_kid_xiaoli'] || 0) >= 20 && Game.hasItem('gift_cakes')) {
|
|
o.push({ l: '(Give her the Osmanthus Cakes)', act: () => Game.giveGift('n_kid_xiaoli', 'gift_cakes'), next: 'candy' });
|
|
}
|
|
o.push({ l: 'See you around, Xiaoli.', close: true });
|
|
return o;
|
|
}},
|
|
candy: { t: () => 'OSMANTHUS! You\u2019re my favorite hero ever!', opts: [{ l: '(Warm feeling acquired)', close: true }] }
|
|
}});
|
|
|
|
/* ---- innkeepers ---- */
|
|
defDlg('d_inn_ma', { name: 'Innkeeper Ma', nodes: { start: { t: () => 'Rooms are eight coppers, congee\u2019s free, gossip\u2019s extra. Rest up, dear.',
|
|
opts: [
|
|
{ l: 'Rent a room (-8 silver)', act: () => Game.restInn(8), close: true },
|
|
{ l: 'Just passing through.', close: true }
|
|
]}}});
|
|
defDlg('d_inn_zhou', { name: 'Innkeeper Zhou', nodes: { start: { t: () => 'Welcome to the Golden Carp! Best beds in Qingyun \u2014 the pillows alone are famous.',
|
|
opts: [
|
|
{ l: 'Rent a room (-14 silver)', act: () => Game.restInn(14), close: true },
|
|
{ l: 'Another time.', close: true }
|
|
]}}});
|
|
defDlg('d_inn_lu', { name: 'Innkeeper Lu', nodes: { start: { t: () => 'Miners snore, smiths snore louder. You look quieter. Room\u2019s ten.',
|
|
opts: [
|
|
{ l: 'Rent a room (-10 silver)', act: () => Game.restInn(10), close: true },
|
|
{ l: 'Later.', close: true }
|
|
]}}});
|
|
|
|
/* ---- Merchant Su ---- */
|
|
defDlg('d_su', { name: 'Merchant Su', nodes: {
|
|
start: { t: (G) => (G.aff['n_merchant_su'] || 0) > 40
|
|
? 'My favorite customer! Fresh stock from the river barges \u2014 look, don\u2019t touch unless buying. Touching counts as buying.'
|
|
: 'Welcome, welcome! Su & Sons \u2014 everything a traveler needs and several things they don\u2019t.',
|
|
opts: [
|
|
{ l: 'Browse wares', shop: 'n_merchant_su' },
|
|
{ l: 'Any news?', next: 'news' },
|
|
{ l: 'Leave.', close: true }
|
|
]},
|
|
news: { t: () => Util.pick([
|
|
'Word is the Iron Fist Guild recruits at the garrison. Big Guo breaks bricks AND egos.',
|
|
'A silver-umbrella gentleman has been asking after tournament fighters. Rich type. Wrong smile.',
|
|
'Pelts fetch fair silver at the Bamboo Sea cabin. Hunter Ren doesn\u2019t haggle, bless him.',
|
|
'The Pure Lotus Temple trains anyone who sweeps the courtyard without complaining. Nobody manages it.'
|
|
]), opts: [{ l: 'Interesting.', next: '_back' }] },
|
|
_back: { t: () => 'Anything else?',
|
|
opts: [ { l: 'Browse wares', shop: 'n_merchant_su' }, { l: 'Leave.', close: true } ]}
|
|
}});
|
|
|
|
/* ---- Captain Yan ---- */
|
|
defDlg('d_yan', { name: 'Captain Yan', nodes: {
|
|
start: { t: (G) => {
|
|
const s = G.quests['q_main_2'] ? (G.quests['q_main_2'].done ? 99 : G.quests['q_main_2'].stage) : -1;
|
|
if (s === -1 && G.flags.unlockedCity) return 'State your business. Quickly \u2014 the paperwork breeds when unwatched.';
|
|
if (s === 1) return 'The bandit camp squats in the old shrine hollow on Cloudmist Ridge. Their leader calls himself "One-Eye" Qiang. Clean it out.';
|
|
if (s === 2) return 'Qiang\u2019s camp is on the Ridge. Come back when he\u2019s retired from brigandage.';
|
|
if (s === 3) return 'Searched the camp yet? Evidence, wanderer. Wars are fought with paper more than swords.';
|
|
if (s === 99 && G.quests['q_main_3'] && !G.quests['q_main_3'].done && G.quests['q_main_3'].stage <= 1) return 'The Ghost Market, under the docks, after dusk. Umbrellas hate daylight and so do their secrets.';
|
|
if (G.quests['q_main_4'] && !G.quests['q_main_4'].done && G.quests['q_main_4'].stage < 2) return 'Allies and standing. No one faces the Iron Umbrella alone \u2014 not even you.';
|
|
if (G.quests['q_main_4'] && !G.quests['q_main_4'].done && G.quests['q_main_4'].stage === 2) return 'So. Companions gathered, a school behind you. Now the hard part.';
|
|
if (G.quests['q_main_5'] && !G.quests['q_main_5'].done) return 'The tomb past the Ridge. The Ledger. You know what rides on it.';
|
|
return 'The Garrison watches the roads. Lately the roads need watching \u2014 umbrella-men in the mists.';
|
|
},
|
|
opts: (G) => {
|
|
const o = [];
|
|
const q2 = G.quests['q_main_2']; const s2 = q2 && !q2.done ? q2.stage : -1;
|
|
if (!q2 && G.flags.unlockedCity) o.push({ l: 'Elder Chen sent me \u2014 wolves driven onto Willow Creek.', next: 'brief' });
|
|
if (s2 === 1) o.push({ l: 'I\u2019ll handle the camp.', close: true });
|
|
if ((s2 === 3 || s2 === 4) && Game.hasItem('mat_token')) o.push({ l: 'This lead token was hidden under Qiang\u2019s cot.', next: 'token' });
|
|
const q4 = G.quests['q_main_4'];
|
|
if (q4 && !q4.done && q4.stage === 1 && (G.rep.garrison || 0) >= 30) o.push({ l: `The garrison trusts me now. (${G.rep.garrison}/30)`, act: () => Game.setStage('q_main_4', 2), next: 'trustok' });
|
|
if (q4 && !q4.done && q4.stage === 2) o.push({ l: 'I\u2019ve gathered my strength. What\u2019s the hard part?', act: () => { Game.completeQuest('q_main_4', { exp: 300, silver: 250, fame: 20 }); G.flags.mainChapter = 4; Game.startQuest('q_main_5'); }, next: 'ledger' });
|
|
if (G.quests['q_main_6'] && !G.quests['q_main_6'].done) o.push({ l: 'Report the Ledger secured.', close: true });
|
|
o.push({ l: 'Leave.', close: true });
|
|
return o;
|
|
}},
|
|
brief: { t: () => 'Wolves, wasps, and now bandits \u2014 the Ridge crawls. There\u2019s a standing bounty: the camp in the shrine hollow, chief "One-Eye" Qiang especially. Do it, and the Garrison remembers its friends.',
|
|
opts: [{ l: 'I\u2019ll take the bounty.', act: () => Game.startQuest('q_main_2'), close: true },
|
|
{ l: 'Who are these bandits?', next: 'whobandits' }] },
|
|
whobandits: { t: () => 'Deserters, smugglers, men who owed money to worse men. But their discipline is wrong for rabble \u2014 someone drills them. Find out who, and the payment doubles in gratitude.',
|
|
opts: [{ l: 'Understood.', act: () => Game.startQuest('q_main_2'), close: true }] },
|
|
token: { t: () => 'An umbrella stamped in lead\u2026 So the rumors have claws after all. The Iron Umbrella Society \u2014 money-lenders, information brokers\u2026 and increasingly, an army without a flag. They move behind festivals and tournaments.',
|
|
opts: [{ l: '(Hand over the token)', act: () => { Game.removeItem('mat_token', 1); Game.completeQuest('q_main_2', { exp: 160, silver: 200, fame: 15 }); G.flags.iuKnown = true; Game.startQuest('q_main_3'); }, next: 'umbrella' }] },
|
|
umbrella: { t: () => 'The Qingyun tournament runs every fifth day. Their agents walk straight into such rings. Get into the finals. Win. Watch who comes to collect the losers \u2014 then bring me proof, not corpses. The Garrison cannot move against ghosts\u2026 but between us? Corpses that deserve it tend to become ghosts anyway. Go.',
|
|
opts: [{ l: 'Consider it done.', close: true }] },
|
|
trustok: { t: () => 'Thirty points of garrison trust doesn\u2019t grow on trees. Good. You\u2019ll need every friend when the storm comes.',
|
|
opts: [{ l: 'What storm?', act: () => Game.setStage('q_main_4', 2), close: true }] },
|
|
ledger: { t: () => 'There is a ledger. The Heaven\u2019s Ledger \u2014 every bribe, every bought magistrate, every umbrella-order signed by hand. It sleeps in the Ancient Tomb past the Ridge, behind doors that have killed better people than me. The Iron Umbrella wants it burned; we want it read. Whoever holds it owns the truth. Move before they do.',
|
|
opts: [{ l: 'I\u2019ll reach it first.', close: true }] }
|
|
}});
|
|
|
|
/* ---- Host Qian / tournament ---- */
|
|
defDlg('d_qian', { name: 'Host Qian', nodes: {
|
|
start: { t: (G) => {
|
|
if (G.quests['q_main_3'] && !G.quests['q_main_3'].done && G.quests['q_main_3'].stage === 3) return 'You\u2019re in the finals, champ! But between us \u2014 that silver-umbrella gentleman asked me to pass a message. He wants to meet. Alone. Behind the arena.';
|
|
if (Game.isTournamentDay() && G.tourney.lastDay !== G.time.day) return 'Fighters! Spectators! Betters of silver! The weekly Martial Tournament is OPEN! Shall I ink your name?';
|
|
if (G.tourney.lastDay === G.time.day) return 'Bracket\u2019s closed today, hero \u2014 come back when the drum tower next calls the fifth day!';
|
|
return 'Tournament runs each fifth day. Champions get silver, glory, and my professional envy.';
|
|
},
|
|
opts: (G) => {
|
|
const o = [];
|
|
if (Game.isTournamentDay() && G.tourney.lastDay !== G.time.day) o.push({ l: 'Enter the tournament!', act: () => { Dialogue.close(); Game.enterTournament(); } });
|
|
if (G.quests['q_main_3'] && !G.quests['q_main_3'].done && G.quests['q_main_3'].stage === 3) o.push({ l: 'A private meeting, hm\u2026', act: () => { Dialogue.close(); Game.dukangAmbush(); } });
|
|
o.push({ l: 'Leave.', close: true });
|
|
return o;
|
|
}}
|
|
}});
|
|
|
|
/* ---- Smith Wang ---- */
|
|
defDlg('d_wang', { name: 'Smith Wang', nodes: {
|
|
start: { t: (G) => (G.aff['n_smith_wang'] || 0) > 40 ? 'Back again? Ha! Bring ore, leave silver, both of us leave happy.' : 'Forge is hot, prices are fair, complaints go in the quench bucket. Browse or upgrade?',
|
|
opts: [
|
|
{ l: 'Browse wares', shop: 'n_smith_wang' },
|
|
{ l: 'Upgrade equipment', act: () => UI.openSmith(), close: true },
|
|
{ l: 'Leave.', close: true }
|
|
]}}});
|
|
|
|
/* ---- Alchemist Bai ---- */
|
|
defDlg('d_bai', { name: 'Alchemist Bai', nodes: {
|
|
start: { t: (G) => {
|
|
const q = G.quests['q_cure'];
|
|
if (q && !q.done && q.stage === 1) return 'Snake gall and green herbs \u2014 got them?';
|
|
if (!q) return 'Fresh remedies, honest prices. Hmm \u2014 actually, since you look capable: I need snake gallbladder and green herbs for a fever cure. Help me, and I\u2019ll share my real recipes.';
|
|
return 'The kettle never lies and neither do I. Remedies? Brews?';
|
|
},
|
|
opts: (G) => {
|
|
const o = []; const q = G.quests['q_cure'];
|
|
if (!q) o.push({ l: 'What do you need exactly?', act: () => Game.startQuest('q_cure'), next: 'need' });
|
|
if (q && !q.done && q.stage === 1 && Game.hasItem('mat_gall', 2) && Game.hasItem('c_herb_green', 3)) {
|
|
o.push({ l: 'Deliver ingredients (2 gall, 3 herbs)', act: () => { Game.removeItem('mat_gall', 2); Game.removeItem('c_herb_green', 3); Game.completeQuest('q_cure', { exp: 90, silver: 80, items: [['c_pill_hp', 2]], aff: [['n_alchemist_bai', 18]] }); G.flags.knowsAlchemy = true; }, next: 'thanks' });
|
|
}
|
|
o.push({ l: 'Browse remedies', shop: 'n_alchemist_bai' });
|
|
if (G.flags.knowsAlchemy) o.push({ l: 'Brew together', act: () => { Dialogue.close(); UI.openAlchemy(); } });
|
|
o.push({ l: 'Leave.', close: true });
|
|
return o;
|
|
}},
|
|
need: { t: () => 'Two snake gallbladders \u2014 the vipers on the Bamboo trails are perfect \u2014 and three handfuls of green herb. The fever cure won\u2019t brew itself, and children won\u2019t stop being small and fragile.',
|
|
opts: [{ l: 'I\u2019ll get them.', close: true }] },
|
|
thanks: { t: () => 'Wonderful! The little ones will sleep tonight. As promised \u2014 my real recipes. Come brew anytime, and take these pills for your troubles.',
|
|
opts: [{ l: 'Glad to help.', close: true }] }
|
|
}});
|
|
|
|
/* ---- Hunter Ren ---- */
|
|
defDlg('d_ren', { name: 'Hunter Ren', nodes: {
|
|
start: { t: (G) => {
|
|
const q = G.quests['q_missing'];
|
|
if (q && !q.done && q.stage === 1) return 'Ping? Aye, he came through two days back, headed deep \u2014 past the third bend, where the bamboo grows black-tight. Fool\u2019s route this season.';
|
|
return 'City folk. Well \u2014 you\u2019ve woodsmoke in your jacket at least. Pelts, fangs, or silence: choose.';
|
|
},
|
|
opts: (G) => {
|
|
const o = []; const q = G.quests['q_missing'];
|
|
if (Game.hasItem('mat_pelt', 3)) o.push({ l: 'Trade 3 Wolf Pelts for 60 silver', act: () => { Game.removeItem('mat_pelt', 3); Game.addSilver(60); Log.add('Traded pelts for 60 silver.', 'good'); } });
|
|
if (Game.hasItem('mat_fang', 4)) o.push({ l: 'Trade 4 Wolf Fangs for 50 silver', act: () => { Game.removeItem('mat_fang', 4); Game.addSilver(50); Log.add('Traded fangs for 50 silver.', 'good'); } });
|
|
if (q && !q.done && q.stage === 1) o.push({ l: 'Which way past the third bend?', act: () => Game.setStage('q_missing', 2), next: 'deep' });
|
|
if ((G.aff['n_hunter_ren'] || 0) >= 25 && !G.player.techs['t_blade_1']) o.push({ l: 'Teach me your saber forms (free, friends only)', act: () => { Dialogue.close(); Game.learnTechnique('t_blade_1'); } });
|
|
if (Game.canSpar('n_hunter_ren')) o.push({ l: 'Spar with Ren', act: () => { Dialogue.close(); Game.spar('n_hunter_ren'); } });
|
|
o.push({ l: 'Leave.', close: true });
|
|
return o;
|
|
}},
|
|
deep: { t: () => 'Keep the moss on your LEFT. Birds go quiet, draw steel first and apologize after. And if you find him\u2026 bring words back, not just bones.',
|
|
opts: [{ l: 'Understood.', act: () => { Dialogue.close(); Game.searchDeepBamboo(); } }] }
|
|
}});
|
|
|
|
/* ---- Zhao Tiehu ---- */
|
|
defDlg('d_tiehu', { name: 'Zhao Tiehu', nodes: {
|
|
start: { t: (G) => {
|
|
if (!G.allies.includes('n_tiehu') && (FVsafe('mainChapter') >= 3) && (G.aff['n_tiehu'] || 0) >= 40) return 'Wanderer! They say you cracked a conspiracy in front of the whole city. My arms are strong but my curiosity is stronger \u2014 take me along, will you?';
|
|
return 'Arm-wrestling champion of Peach Blossom Dock, three years running! Care to test the legend? Or drink? Or both, in strict order?';
|
|
},
|
|
opts: (G) => {
|
|
const o = [];
|
|
if (!G.allies.includes('n_tiehu') && FVsafe('mainChapter') >= 3 && (G.aff['n_tiehu'] || 0) >= 40) o.push({ l: 'Join me, Tiehu!', act: () => Game.recruitCompanion('n_tiehu'), next: 'joined' });
|
|
if (Game.canSpar('n_tiehu')) o.push({ l: 'Arm-wrestle / Spar (friendly)', act: () => { Dialogue.close(); Game.spar('n_tiehu'); } });
|
|
o.push({ l: 'Drinking contest (Constitution test, 20 silver stake)', act: () => { Dialogue.close(); Game.drinkingContest(); } });
|
|
o.push({ l: 'Leave.', close: true });
|
|
return o;
|
|
}},
|
|
joined: { t: () => 'HA! Adventure, conspiracy, beautiful scenery \u2014 count me in! Just don\u2019t ask me to read anything longer than a menu.',
|
|
opts: [{ l: 'Welcome aboard.', close: true }] }
|
|
}});
|
|
function FVsafe(k){ return G.flags[k] || 0; }
|
|
|
|
/* ---- Ferryman ---- */
|
|
defDlg('d_ferry', { name: 'Old Gao the Ferryman', nodes: {
|
|
start: { t: () => TimeSys.isNight()
|
|
? 'Night tide\u2019s right. Two silvers rows you to the shore without lanterns. You didn\u2019t hear it from me.'
|
|
: 'Come back when the sun\u2019s drowned, friend. Daylight passengers ride ordinary boats to ordinary places.',
|
|
opts: (G) => {
|
|
const o = [];
|
|
if (TimeSys.isNight()) o.push({ l: 'Row me over (-2 silver)', cond: () => G.player.silver >= 2, act: () => { if (Game.spendSilver(2)) { Dialogue.close(); Game.travelTo('l_market'); } } });
|
|
o.push({ l: 'Wave goodbye.', close: true });
|
|
return o;
|
|
}}}});
|
|
|
|
/* ---- Abbot Lian ---- */
|
|
defDlg('d_abbot', { name: 'Abbot Lian', nodes: {
|
|
start: { t: (G) => (G.aff['n_abbot_lian'] || 0) > 50 ? 'Ah, the wandering bell returns. Sit, breathe. The leaves fell badly today \u2014 very suspicious of the wind.' : 'The courtyard welcomes those whose steps disturb neither dust nor dew. How may the temple serve?',
|
|
opts: (G) => {
|
|
const o = [];
|
|
const qf = G.quests['q_trial_fl'];
|
|
if (!G.flags.templeMember && !qf && !G.flags.azureMember) o.push({ l: 'Ask to study at Pure Lotus Temple', act: () => Game.startQuest('q_trial_fl'), next: 'trialfl' });
|
|
if (qf && !qf.done && qf.stage === 1) o.push({ l: 'Report: courtyard swept, breath stilled, monk sparred', act: () => { Game.completeQuest('q_trial_fl', { exp: 100, rep: [['temple', 25]], aff: [['n_abbot_lian', 12]] }); G.flags.templeMember = true; Game.addRep('temple', 10); }, next: 'memberfl' });
|
|
for (const t of NPCS.n_abbot_lian.teach) {
|
|
if (Game.alreadyKnows(t)) continue;
|
|
const ok = Game.canLearnFrom('n_abbot_lian', t);
|
|
const lbl = t.internal ? ('Internal art: ' + INTERNALS[t.internal].name) : ('Technique: ' + TECHNIQUES[t.tech].name);
|
|
o.push({ l: lbl + (ok.ok ? ` (${t.cost} silver)` : ` (${ok.why})`), act: () => { Dialogue.close(); Game.learnFrom('n_abbot_lian', t); } });
|
|
}
|
|
if (G.flags.sunHint && !G.player.knownInternals.includes('i_sun') && !Game.teachListHas(NPCS.n_abbot_lian, 'internal', 'i_sun')) {
|
|
o.push({ l: 'Ask of the lost Sun Manual.', next: 'sunhint2' });
|
|
}
|
|
o.push({ l: 'Leave.', close: true });
|
|
return o;
|
|
}},
|
|
trialfl: { t: () => '\u201cThe temple turns away no one willing to be bored properly.\u201d He hands you a broom and a cushion. \u201cOne honest sitting in the hall, one friendly round with Brother Jing. Then we talk about sutras.\u201d',
|
|
opts: [{ l: '(Accept broom and cushion)', close: true }] },
|
|
memberfl: { t: () => '\u201cBoredom, survived.\u201d The Abbot almost smiles. \u201cWelcome into the lotus gate, disciple. Mind the third step on your way out \u2014 it bites.\u201d\u2026 no wait, that\u2019s the valley.',
|
|
opts: [{ l: 'Thank you, Abbot.', close: true }] },
|
|
sunhint2: { t: () => 'So the beggar told you. Yes \u2014 the Blazing Sun Manual was carried into the Ancient Tomb three hundred years ago by its last master. If the tomb opens to you, look where the light pools at noon. Such things guard themselves.',
|
|
opts: [{ l: 'Thank you, Abbot.', close: true }] }
|
|
}});
|
|
// expose i_sun teaching after hint (append dynamically)
|
|
NPCS.n_abbot_lian.teach.push({ internal: 'i_sun', aff: 90, cost: 2000, reqFlag: 'sunHint' });
|
|
|
|
/* unified namespace referenced across modules */
|
|
const DATA = { ITEMS, TECHNIQUES, INTERNALS, LIGHTNESS, ENEMIES, NPCS, LOCS, QUESTS, ORIGINS, DIALOGUES };
|
|
|
|
/* ---- Monk Jing ---- */
|
|
defDlg('d_jing', { name: 'Monk Jing', nodes: {
|
|
start: { t: () => 'The training posts are free; my company is cheaper. Spar with me, friend \u2014 gently. Or not. The bruises are instructive either way.',
|
|
opts: (G) => {
|
|
const o = [];
|
|
if (Game.canSpar('n_monk_jing')) o.push({ l: 'Spar with Monk Jing', act: () => { Dialogue.close(); Game.spar('n_monk_jing'); } });
|
|
o.push({ l: 'Peace upon you.', close: true });
|
|
return o;
|
|
}}}});
|
|
|
|
/* ---- Master Yun ---- */
|
|
defDlg('d_yun', { name: 'Master Yun', nodes: {
|
|
start: { t: (G) => {
|
|
if (!G.flags.azureMember && !G.quests['q_trial_az']) return 'A new face on the peak. The Azure Cloud Sect accepts students of decent heart \u2014 and tests them first. Interested?';
|
|
const q = G.quests['q_trial_az'];
|
|
if (q && !q.done && q.stage === 1) return 'The Lotus Herbs grow where the Bamboo Sea thins. Two, unbruised. Patience, not strength.';
|
|
if (G.flags.azureMember) return 'Disciple. The forms await \u2014 and so does the mission hall, if your blade is restless.';
|
|
return 'The peak keeps its own counsel. Speak.';
|
|
},
|
|
opts: (G) => {
|
|
const o = []; const q = G.quests['q_trial_az'];
|
|
if (!G.flags.azureMember && !q) o.push({ l: 'I wish to study under Azure Cloud.', act: () => Game.startQuest('q_trial_az'), next: 'trial' });
|
|
if (q && !q.done && q.stage === 1 && Game.hasItem('c_lotus', 2)) o.push({ l: 'Deliver 2 Lotus Herbs', act: () => { Game.removeItem('c_lotus', 2); Game.completeQuest('q_trial_az', { exp: 120, rep: [['azure', 25]], aff: [['n_master_yun', 15]] }); G.flags.azureMember = true; Game.addRep('azure', 10); }, next: 'member' });
|
|
for (const t of NPCS.n_master_yun.teach) {
|
|
if (Game.alreadyKnows(t)) continue;
|
|
const ok = Game.canLearnFrom('n_master_yun', t);
|
|
const lbl = t.internal ? ('Internal art: ' + INTERNALS[t.internal].name) : ('Technique: ' + TECHNIQUES[t.tech].name);
|
|
o.push({ l: lbl + (ok.ok ? ` (${t.cost} silver)` : ` (${ok.why})`), act: () => { Dialogue.close(); Game.learnFrom('n_master_yun', t); } });
|
|
}
|
|
o.push({ l: 'Leave.', close: true });
|
|
return o;
|
|
}},
|
|
trial: { t: () => 'Good. The trial is humility with leaves: bring me two Lotus Herbs from the Bamboo Sea below. They bruise if you stare at them wrongly. Go.',
|
|
opts: [{ l: 'As you say, Master.', close: true }] },
|
|
member: { t: () => 'Unbruised. Excellent. From this morning you walk beneath our clouds. Welcome to Azure Cloud, disciple.',
|
|
opts: [{ l: 'Thank you, Master.', close: true }] }
|
|
}});
|
|
|
|
/* ---- Lin Wan'er ---- */
|
|
defDlg('d_lin', { name: 'Lin Wan\u2019er', nodes: {
|
|
start: { t: (G) => {
|
|
if (G.partner === 'n_lin') return 'There you are. I saved you the sunny side of the terrace. Also I beat three senior brothers while waiting. A productive morning.';
|
|
if ((G.aff['n_lin'] || 0) >= 80 && FVsafe('mainChapter') >= 3 && !G.partner) return 'You\u2019re leaving again soon, aren\u2019t you. Somewhere with conspiracies in it. \u2026Idiot. Take me with you. Not as a disciple-babysitter. As\u2014 as yours. If you want.';
|
|
if (!G.allies.includes('n_lin') && FVsafe('mainChapter') >= 3 && (G.aff['n_lin'] || 0) >= 50) return 'So the famous umbrella-hunter wants ME at their side? Beat me first. Properly. Then we\u2019ll talk about roads.';
|
|
return 'Oh \u2014 the wanderer. Heard you fight umbrellas. I collect sword styles; show me yours sometime.';
|
|
},
|
|
opts: (G) => {
|
|
const o = [];
|
|
if ((G.aff['n_lin'] || 0) >= 80 && FVsafe('mainChapter') >= 3 && !G.partner) o.push({ l: 'Then come with me \u2014 as yours, and mine.', act: () => Game.confess('n_lin'), next: 'confessed' });
|
|
else if (!G.allies.includes('n_lin') && FVsafe('mainChapter') >= 3 && (G.aff['n_lin'] || 0) >= 50) o.push({ l: 'Then let\u2019s duel for it.', act: () => { Dialogue.close(); Game.linDuel(); } });
|
|
if (Game.canSpar('n_lin')) o.push({ l: 'Spar with Wan\u2019er', act: () => { Dialogue.close(); Game.spar('n_lin'); } });
|
|
o.push({ l: 'Leave.', close: true });
|
|
return o;
|
|
}},
|
|
confessed: { t: () => '\u2026You\u2019re supposed to hesitate dramatically at least ONCE. \u2014Fine. FINE. Yes. Obviously yes. If you die on some stupid road I\u2019ll resurrect you and kill you myself.',
|
|
opts: [{ l: 'Deal.', close: true }] }
|
|
}});
|
|
|
|
/* ---- Lady Miao ---- */
|
|
defDlg('d_miao', { name: 'Lady Miao', nodes: {
|
|
start: { t: (G) => G.flags.serpentMember ? 'Little serpent returns. Mind the third step, it bites.' : 'A guest who walks between the poison patches uninvited. Either lucky or destined \u2014 both taste the same in my tea.',
|
|
opts: (G) => {
|
|
const o = [];
|
|
if (!G.flags.serpentMember) o.push({ l: 'Ask to study in Serpent Veil Valley', act: () => { G.flags.serpentMember = true; Game.addRep('serpent', 15); Log.add('You are now a sworn friend of Serpent Veil Valley.', 'sys'); }, next: 'join' });
|
|
for (const t of NPCS.n_lady_miao.teach) {
|
|
if (Game.alreadyKnows(t)) continue;
|
|
const ok = Game.canLearnFrom('n_lady_miao', t);
|
|
const lbl = t.internal ? ('Internal art: ' + INTERNALS[t.internal].name) : ('Technique: ' + TECHNIQUES[t.tech].name);
|
|
o.push({ l: lbl + (ok.ok ? ` (${t.cost} silver)` : ` (${ok.why})`), act: () => { Dialogue.close(); Game.learnFrom('n_lady_miao', t); } });
|
|
}
|
|
o.push({ l: 'Leave.', close: true });
|
|
return o;
|
|
}},
|
|
join: { t: () => 'Amusing. Very well \u2014 the Valley marks you friend. Touch nothing shiny, drink nothing offered, and visit often. We\u2019re terribly welcoming.',
|
|
opts: [{ l: '\u2026Noted.', close: true }] }
|
|
}});
|
|
|
|
/* ---- Su Qingluo ---- */
|
|
defDlg('d_suqing', { name: 'Su Qingluo', nodes: {
|
|
start: { t: (G) => {
|
|
if (G.partner === 'n_suqing') return 'Back so soon? Hold still \u2014 there\u2019s bamboo dust in your collar. Honestly, adventures are just dirt with plot.';
|
|
if (!G.allies.includes('n_suqing') && FVsafe('mainChapter') >= 2 && (G.aff['n_suqing'] || 0) >= 45) return 'Miao says I should see the world before it sees me. You seem\u2027 structurally sound. Take me traveling, physician\u2019s orders.';
|
|
if (!G.flags.qingluoMet) return 'New face. Don\u2019t touch the purple ones. Or breathe too deep. Welcome to the valley!';
|
|
return 'Need something brewed, or just admiring the poison garden?';
|
|
},
|
|
opts: (G) => {
|
|
const o = [];
|
|
if (!G.flags.qingluoMet) o.push({ l: 'Just passing through.', act: () => { Game.addAffinity('n_suqing', 5); G.flags.qingluoMet = true; }, close: true });
|
|
if (!G.allies.includes('n_suqing') && FVsafe('mainChapter') >= 2 && (G.aff['n_suqing'] || 0) >= 45) o.push({ l: 'Travel with me, Qingluo.', act: () => Game.recruitCompanion('n_suqing'), next: 'joined' });
|
|
o.push({ l: 'Ask her about herbs.', next: 'herbs' });
|
|
o.push({ l: 'Leave.', close: true });
|
|
return o;
|
|
}},
|
|
joined: { t: () => 'Excellent. I\u2019m bringing the leeches. Kidding! \u2026Mostly. Don\u2019t die stupidly, that\u2019s all I ask.', opts: [{ l: 'Welcome aboard.', close: true }] },
|
|
herbs: { t: () => 'Lotus herb only grows in mist \u2014 the Bamboo Sea, or right here if Lady Miao isn\u2019t watching. Snake gall keeps best fresh, so hunt vipers last. And if something numbles your tongue, spit and run.', opts: [{ l: 'Useful. Grim, but useful.', close: true }] }
|
|
}});
|
|
|
|
/* ---- Master Guo ---- */
|
|
defDlg('d_guo', { name: 'Master Guo', nodes: {
|
|
start: { t: (G) => G.flags.fistMember ? 'Guild sibling! Bricks don\u2019t break themselves.' : 'Iron Fist Guild. We punch things professionally. You look like you might have two functional fists. Interested?',
|
|
opts: (G) => {
|
|
const o = [];
|
|
if (!G.flags.fistMember) o.push({ l: 'Join the Iron Fist Guild (100 silver dues)', cond: () => G.player.silver >= 100, act: () => { if (Game.spendSilver(100)) { G.flags.fistMember = true; Game.addRep('fist', 15); Log.add('Welcome to the Iron Fist Guild!', 'sys'); } }, next: 'joinedg' });
|
|
for (const t of NPCS.n_master_guo.teach) {
|
|
if (Game.alreadyKnows(t)) continue;
|
|
const ok = Game.canLearnFrom('n_master_guo', t);
|
|
o.push({ l: 'Technique: ' + TECHNIQUES[t.tech].name + (ok.ok ? ` (${t.cost} silver)` : ` (${ok.why})`), act: () => { Dialogue.close(); Game.learnFrom('n_master_guo', t); } });
|
|
}
|
|
if (Game.canSpar('n_master_guo')) o.push({ l: 'Spar with Master Guo', act: () => { Dialogue.close(); Game.spar('n_master_guo'); } });
|
|
o.push({ l: 'Leave.', close: true });
|
|
return o;
|
|
}},
|
|
joinedg: { t: () => 'Dues collected, hand shaken \u2014 possibly cracked. Welcome to the guild, sibling. Now go hit something responsibly.', opts: [{ l: 'Yes sir.', close: true }] }
|
|
}});
|
|
|
|
/* ---- Drill-Captain Pei ---- */
|
|
defDlg('d_pei', { name: 'Drill-Captain Pei', nodes: {
|
|
start: { t: () => 'The bounty board pays for problems. The Garrison pays double when the problem wears an umbrella. Read the board, take a contract, keep your receipts.',
|
|
opts: [
|
|
{ l: 'Check the military bounty board', act: () => { Dialogue.close(); UI.openBoard(true); } },
|
|
{ l: 'Leave.', close: true }
|
|
]}}});
|
|
|
|
/* ---- Broker Qiu ---- */
|
|
defDlg('d_broker', { name: 'Broker Qiu', nodes: {
|
|
start: { t: (G) => {
|
|
if (G.quests['q_main_3'] && !G.quests['q_main_3'].done && G.quests['q_main_3'].stage === 1) return 'Ah, the person everyone\u2019s whispering about. I know why the umbrellas flock to Qingyun\u2019s tournament ring. That knowledge costs eighty silver. Knowledge always costs; that\u2019s how you know it\u2019s good.';
|
|
return 'Secrets, maps, introductions. Everything has a price; everything has a previous owner.';
|
|
},
|
|
opts: (G) => {
|
|
const o = [];
|
|
const q3 = G.quests['q_main_3'];
|
|
if (q3 && !q3.done && q3.stage === 1) o.push({ l: 'Buy the intel (-80 silver)', cond: () => G.player.silver >= 80, act: () => { if (Game.spendSilver(80)) { Game.setStage('q_main_3', 2); Game.addRep('underground', 5); } }, next: 'intel' });
|
|
o.push({ l: 'Buy map rumor: reveal hidden places (-50 silver)', cond: () => !G.discovered['l_valley'] || !G.discovered['l_market'], act: () => { if (Game.spendSilver(50)) { G.discovered['l_valley'] = true; G.discovered['l_market'] = true; Toast.show('Map knowledge gained.'); } } });
|
|
o.push({ l: 'Leave.', close: true });
|
|
return o;
|
|
}},
|
|
intel: { t: () => 'Wise purchase. Listen: the Society plants a ringer in the tournament finals \u2014 a duelist called Du Kang, silver umbrella, silk smile. He collects promising losers for their cause, and buries stubborn ones. Win to the finals, unmask him publicly, and the umbrella\u2019s grip on this city slips. The rest of the intel\u2019s free: don\u2019t drink anything he pours.',
|
|
opts: [{ l: 'Silver well spent.', close: true }] }
|
|
}});
|
|
|
|
/* ---- Fence Wu ---- */
|
|
defDlg('d_fence', { name: 'Fence Wu', nodes: {
|
|
start: { t: () => 'Everything here fell off a wagon. Some wagons were on fire. Browse anyway.',
|
|
opts: [
|
|
{ l: 'Browse acquisitions', shop: 'n_fence_wu' },
|
|
{ l: 'Leave.', close: true }
|
|
]}}});
|
|
|
|
/* ---- Hong the Beggar ---- */
|
|
defDlg('d_hong', { name: 'Hong the Beggar', nodes: {
|
|
start: { t: (G) => {
|
|
if ((G.flags.hongBeats || 0) >= 2 && !G.player.techs['t_fist_4']) return 'Twice. Twice you\u2019ve put grandpa Hong in the dirt! HAHAHA! Fine, fine \u2014 come closer. No, closer. \u2026These old palms have drunk ten thousand rivers. Watch closely now.';
|
|
if (G.flags.hongBeats === 1) return 'One win apiece, eh? Again! Grandpa hasn\u2019t been knocked down since the Emperor\u2019s grandmother was a girl. It felt NOSTALGIC.';
|
|
return '*hic* The wine here\u2019s watered, the streets are crooked, and yet \u2014 young people keep stepping over me with fists like unopened letters. Spar with an old man, hm?';
|
|
},
|
|
opts: (G) => {
|
|
const o = [];
|
|
if ((G.flags.hongBeats || 0) >= 2 && !G.player.techs['t_fist_4']) o.push({ l: 'Learn the Ten Thousand Rivers Palms', act: () => { Dialogue.close(); Game.learnHongArt(); } });
|
|
if (Game.canSpar('n_beggar_hong')) o.push({ l: 'Spar with Hong', act: () => { Dialogue.close(); Game.spar('n_beggar_hong'); } });
|
|
if (Game.hasItem('c_wine')) o.push({ l: 'Share your Mulled Wine', act: () => Game.giveGift('n_beggar_hong', 'c_wine'), next: 'wine' });
|
|
o.push({ l: 'Step over him politely.', close: true });
|
|
return o;
|
|
}},
|
|
wine: { t: () => 'AH! A person of culture and poor judgment! Sit, sit. Did you know the Pure Lotus Temple keeps a certain manual locked where noon-light lands? Neither did I. *hic* Probably nothing.', opts: [{ l: 'Probably nothing\u2026', act: () => { G.flags.sunHint = true; Toast.show('Rumor learned: the lost Sun Manual sleeps in the Ancient Tomb.'); }, close: true }] }
|
|
}});
|