const Story = { 'start': async () => { const settingsPopup = UI.createPopup(); settingsPopup.querySelector('[data-template-slot="content"]').append(UI.createSettingsMenu()); UI.drawPopup(settingsPopup); await new Promise((resolve) => { settingsPopup.addEventListener('close', UI.wrapCallback(async () => { resolve(); })); }); return await Story.immediateProgress('start', 'introduction'); }, 'introduction': async () => { const story = await fetchStory('introduction'); const characterCeo = story.characters.ceo; const characterShopKeeper = story.characters.shopKeeper; const npcCeo = await fetchNpc(characterCeo.slug); const npcShopKeeper = await fetchNpc(characterShopKeeper.slug); await UI.buildAndShowStoryPopup({ speaker: npcCeo, text: translate(characterCeo.text[0]) }); await UI.buildAndShowStoryPopup({ speaker: npcCeo, text: translate(characterCeo.text[1]) }); await UI.buildAndShowStoryPopup({ speaker: npcShopKeeper, text: translate(characterShopKeeper.text[0]) }); const possibleStarterMonsters = await Promise.all( story.monsters.map(async (monsterData) => { const monster = await fetchMonster(monsterData.slug); monster.level = monsterData.level; return monster; }) ); const monsterSelection = UI.openStarterMonsterSelection(possibleStarterMonsters, { title: translate('story:introduction:monster_selection:title') }); await new Promise((resolve) => { monsterSelection.addEventListener('starter:monster:selected', UI.wrapCallback(async (event) => { if (!confirm(`Select ${event.detail.monster.name}?`)) { return; } event.detail.popup.remove(); await UI.buildAndShowStoryPopup({ speaker: npcShopKeeper, text: translate(characterShopKeeper.text[1]) }); // set rival monster Memory.state.rivalMonster = event.detail.monster.slug; // set initial values Memory.state.money = story.money; // go to starting area await Game.goToArea(story.area); resolve(); })); }); return true; }, 'select-starter-monster': async () => { const story = await fetchStory('select-starter-monster'); const characterDante = story.characters.dante; const npcDante = await fetchNpc(characterDante.slug); await UI.buildAndShowStoryPopup({ speaker: npcDante, text: translate(characterDante.text[0]) }); await UI.buildAndShowStoryPopup({ speaker: npcDante, text: translate(characterDante.text[1]) }); await UI.buildAndShowStoryPopup({ speaker: npcDante, text: translate(characterDante.text[2]) }); const possibleStarterMonsters = await Promise.all( story.monsters.map(async (monsterData) => { const monster = await fetchMonster(monsterData.slug); monster.level = monsterData.level; return monster; }) ); const monsterSelection = UI.openStarterMonsterSelection(possibleStarterMonsters, { title: translate('story:select_starter_monster:monster_selection:title') }); await new Promise((resolve) => { monsterSelection.addEventListener('starter:monster:selected', UI.wrapCallback(async (event) => { if (!confirm(`Select ${event.detail.monster.name}?`)) { return; } Memory.state.player.monsters.push(event.detail.monster); Game.setActivePlayerMonster(event.detail.monster); UI.drawActiveMonster(); UI.drawActiveTechniques(); event.detail.popup.remove(); resolve(); })); }); return await Story.immediateProgress('select-starter-monster', 'battle-rival-one'); }, 'battle-rival-one': async () => { const story = await fetchStory('battle-rival-one'); const characterRival = story.characters.rival; const npcRival = await fetchNpc(story.characters.rival.slug); await UI.buildAndShowStoryPopup({ speaker: npcRival, text: translate(characterRival.text[0]) }); await Story.setupBattle(npcRival, characterRival.monsters); const didWin = await Story.battle(); if (didWin) { await UI.buildAndShowStoryPopup({ speaker: npcRival, text: translate(characterRival.text[1]) }); } else { await UI.buildAndShowStoryPopup({ speaker: npcRival, text: translate(characterRival.text[2]) }); } await UI.buildAndShowStoryPopup({ speaker: npcRival, text: translate(characterRival.text[3]) }); Game.healParty(); return true; }, 'battle-rival-two': async () => { const story = await fetchStory('battle-rival-two'); const characterRival = story.characters.rival; const npcRival = await fetchNpc(story.characters.rival.slug); await UI.buildAndShowStoryPopup({ speaker: npcRival, text: translate(characterRival.text[0]) }); await Story.setupBattle(npcRival, characterRival.monsters); const didWin = await Story.battle(); if (didWin) { await UI.buildAndShowStoryPopup({ speaker: npcRival, text: translate(characterRival.text[1]) }); } return didWin; }, 'hospital-floor-1': async () => { const story = await fetchStory('hospital-floor-1'); const characterAurora = story.characters.aurora; const npcAurora = await fetchNpc(characterAurora.slug); await UI.buildAndShowStoryPopup({ speaker: npcAurora, text: translate(characterAurora.text[0]) }); await Story.setupBattle(npcAurora, characterAurora.monsters); didWin = await Story.battle(); if (didWin) { await UI.buildAndShowStoryPopup({ speaker: npcAurora, text: translate(characterAurora.text[1]) }); } return didWin; }, 'hospital-floor-2': async () => { const story = await fetchStory('hospital-floor-2'); const characterLuzia = story.characters.luzia; const npcLuzia = await fetchNpc(characterLuzia.slug); const characterRhizome = story.characters.rhizome; const npcRhizome = await fetchNpc(characterRhizome.slug); await UI.buildAndShowStoryPopup({ speaker: npcLuzia, text: translate(characterLuzia.text[0]) }); await Story.setupBattle(npcLuzia, characterLuzia.monsters); let didWin = await Story.battle(); if (didWin) { await UI.buildAndShowStoryPopup({ speaker: npcLuzia, text: translate(characterLuzia.text[1]) }); } else { return false; } await UI.buildAndShowStoryPopup({ speaker: npcRhizome, text: translate(characterRhizome.text[0]) }); await Story.setupBattle(npcRhizome, characterRhizome.monsters); didWin = await Story.battle(); if (didWin) { await UI.buildAndShowStoryPopup({ speaker: npcRhizome, text: translate(characterRhizome.text[1]) }); } return didWin; }, 'hospital-floor-3': async () => { const story = await fetchStory('hospital-floor-3'); const characterRival = story.characters.rival; const npcRival = await fetchNpc(characterRival.slug); await UI.buildAndShowStoryPopup({ speaker: npcRival, text: translate(characterRival.text[0]).replace('${{name}}', Memory.state.Settings.name) }); await Story.setupBattle(npcRival, characterRival.monsters); didWin = await Story.battle(); if (didWin) { await UI.buildAndShowStoryPopup({ speaker: npcRival, text: translate(characterRival.text[1]) }); } return didWin; }, 'drokoro': async () => { const story = await fetchStory('drokoro'); const monster = await fetchMonster(story.encounters.drokoro.slug); monster.level = story.encounters.drokoro.level; Memory.state.opponent = new Trainer({ monsters: [monster] }); await Memory.state.opponent.initialize(); return await Story.battle(); }, // Helper /** * @param {StorySlug} slug * * @returns {Promise} */ async progress (slug) { if (!Story[slug]) { return true; } // story already done const story = await fetchStory(slug); if (!story.canTriggerMultipleTimes && Memory.state.storyProgress[slug]) { return true; } Memory.state.currentStory = slug; Memory.saveToLocalStorage(); const isDone = await Story[slug](); if (isDone) { Memory.state.storyProgress[slug] = true; } Memory.state.currentStory = null; Memory.saveToLocalStorage(); return isDone; }, /** * @param {StorySlug} fromSlug * @param {StorySlug} toSlug * * @returns {Promise} */ async immediateProgress (fromSlug, toSlug) { Memory.state.storyProgress[fromSlug] = true; Memory.saveToLocalStorage(); return await Story.progress(toSlug); }, /** * @returns {Promise} */ async battle () { const previousArea = Object.assign({}, Memory.state.currentArea); Game.isStoryBattle = true; Memory.state.Game.isInBattle = true; Memory.saveToLocalStorage(); UI.drawBattle(); UI.showBattle(); await new Promise((resolve) => { const interval = setInterval(() => { if (!Game.isStoryBattle) { clearInterval(interval); resolve(); } }, 100); }); // reset incremented trainer progress if (Memory.state.areaProgress[previousArea.slug]) { Memory.state.areaProgress[previousArea.slug].trainerProgress = previousArea.trainerProgress; } if (previousArea.slug === Memory.state.currentArea.slug) { Memory.state.currentArea.trainerProgress = previousArea.trainerProgress; UI.drawStatus(); } return Game.didWinStoryBattle; }, /** * @param {Npc} npc * @param {DB_StoryEncounter[]} monsters * @param {Object[]} inventory */ async setupBattle (npc, monsters, inventory = []) { Memory.state.opponent = new Trainer({ monsters: await Promise.all( monsters.map(async (monsterData) => { if (monsterData.slug === 'RIVAL') { monsterData.slug = Memory.state.rivalMonster; } const monster = await fetchMonster(monsterData.slug); monster.level = monsterData.level; return monster; }) ), inventory: inventory, name: translate(npc.slug), sprite: npc.spriteName + '.png', }); await Memory.state.opponent.initialize(); }, };