From 2030caa0c85102b1cf6131dd9eefef4a347cda42 Mon Sep 17 00:00:00 2001 From: Daniel Weipert Date: Sat, 2 Sep 2023 14:59:09 +0200 Subject: translations and story --- resources/css/page.css | 5 ++ resources/js/classes/Npc.js | 2 +- resources/js/db.js | 6 ++ resources/js/game.js | 36 ++++++--- resources/js/story.js | 181 +++++++++++++++++++++++++++++++++++++------- resources/js/ui.js | 7 +- 6 files changed, 195 insertions(+), 42 deletions(-) (limited to 'resources') diff --git a/resources/css/page.css b/resources/css/page.css index 2091829..b59ad6d 100644 --- a/resources/css/page.css +++ b/resources/css/page.css @@ -47,3 +47,8 @@ svg [data-interactable="false"] { stroke: transparent; fill: transparent; } + +svg [data-interactable="false"][data-blackout="true"] { + stroke: black; + fill: black; +} diff --git a/resources/js/classes/Npc.js b/resources/js/classes/Npc.js index 82dba7e..c13b9b4 100644 --- a/resources/js/classes/Npc.js +++ b/resources/js/classes/Npc.js @@ -6,7 +6,7 @@ class Npc { this.slug = slug; this.name = translate(slug); - this.spriteName = DB.npcs[slug]?.template[0].sprite_name; + this.spriteName = DB.npcs[slug]?.template[0].combat_front; } get sprite () { diff --git a/resources/js/db.js b/resources/js/db.js index d564851..aff2c7c 100644 --- a/resources/js/db.js +++ b/resources/js/db.js @@ -13,12 +13,18 @@ * @typedef {Object} DB_Story * @property {number} money * @property {string} area + * @property {boolean} canTriggerMultipleTimes * @property {Object} characters + * @property {Object} encounters * * @typedef {Object} DB_StoryCharacter * @property {string} slug * @property {string[]} text * @property {Object[]} monsters + * + * @typedef {Object} DB_StoryEncounter + * @property {string} slug + * @property {number} level */ // diff --git a/resources/js/game.js b/resources/js/game.js index 6c3a393..309d013 100644 --- a/resources/js/game.js +++ b/resources/js/game.js @@ -761,14 +761,6 @@ const Game = { } const nextTrainer = Memory.state.currentArea.trainers[nextTrainerIdx]; - if (nextTrainer.name.startsWith('Rival')) { - for (const idx in nextTrainer.monsters) { - if (nextTrainer.monsters[idx].slug === 'STARTER') { - nextTrainer.monsters[idx].slug = Memory.state.rivalMonster; - } - } - } - const trainer = new Trainer(nextTrainer); await trainer.initialize() Memory.state.opponent = trainer; @@ -797,11 +789,37 @@ const Game = { Memory.state.currentArea = await fetchArea(areaSlug); + + // on enter + let storyIsDone = true; + if (Memory.state.currentArea.events?.onEnter.length > 0) { + Game.isLoadingArea = false; + + for (const event of Memory.state.currentArea.events.onEnter) { + if (event.type === 'story') { + UI.showMap(); + UI.drawTown(); + + storyIsDone = await Story.progress(event.story); + } + } + } + + if (!storyIsDone) { + return; + } + + Game.isLoadingArea = true; + + + // after events resolved + if (Game.isTown(Memory.state.currentArea)) { if (Object.values(Memory.state.currentArea.locations).some((location) => location.type === 'healingCenter')) { Memory.state.lastVisitedTown = areaSlug; } - } else { + } + else { if (Memory.state.currentArea.encounters.length > 0) { await Game.encounterWildMonster(); } else if (Memory.state.currentArea.trainers.length > 0) { diff --git a/resources/js/story.js b/resources/js/story.js index d5e6486..bb6fc4b 100644 --- a/resources/js/story.js +++ b/resources/js/story.js @@ -1,5 +1,5 @@ const Story = { - async start () { + 'start': async () => { const settingsPopup = UI.createPopup(); settingsPopup.querySelector('[data-template-slot="content"]').append(UI.createSettingsMenu()); UI.drawPopup(settingsPopup); @@ -10,10 +10,10 @@ const Story = { })); }); - await Story.immediateProgress('start', 'introduction'); + return await Story.immediateProgress('start', 'introduction'); }, - async introduction () { + 'introduction': async () => { const story = await fetchStory('introduction'); const characterCeo = story.characters.ceo; const characterShopKeeper = story.characters.shopKeeper; @@ -55,9 +55,11 @@ const Story = { resolve(); })); }); + + return true; }, - async selectStarterMonster () { + 'select-starter-monster': async () => { const story = await fetchStory('select-starter-monster'); const characterDante = story.characters.dante; const npcDante = await fetchNpc(characterDante.slug); @@ -93,35 +95,19 @@ const Story = { })); }); - await Story.immediateProgress('selectStarterMonster', 'battleRivalOne'); + return await Story.immediateProgress('select-starter-monster', 'battle-rival-one'); }, - async battleRivalOne () { + 'battle-rival-one': async () => { const story = await fetchStory('battle-rival-one'); const characterRival = story.characters.rival; const npcRival = await fetchNpc(story.characters.rival.slug); - Memory.state.opponent = new Trainer({ - monsters: await Promise.all( - characterRival.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; - }) - ) - }); - await Memory.state.opponent.initialize(); - await UI.buildAndShowStoryPopup({ speaker: npcRival, text: translate(characterRival.text[0]) }); + await Story.setupBattle(npcRival, characterRival.monsters); + const didWin = await Story.battle(); - await Story.battle(); - - if (Game.didWinStoryBattle) { + if (didWin) { await UI.buildAndShowStoryPopup({ speaker: npcRival, text: translate(characterRival.text[1]) }); } else { await UI.buildAndShowStoryPopup({ speaker: npcRival, text: translate(characterRival.text[2]) }); @@ -129,6 +115,94 @@ const Story = { 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(); }, @@ -136,33 +210,51 @@ const Story = { /** * @param {StorySlug} slug + * + * @returns {Promise} */ async progress (slug) { if (!Story[slug]) { - return; + 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(); - await Story[slug](); + const isDone = await Story[slug](); + + if (isDone) { + Memory.state.storyProgress[slug] = true; + } - 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(); - await Story.progress(toSlug); + return await Story.progress(toSlug); }, + /** + * @returns {Promise} + */ async battle () { const previousArea = Object.assign({}, Memory.state.currentArea); @@ -191,5 +283,36 @@ const Story = { 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(); }, }; diff --git a/resources/js/ui.js b/resources/js/ui.js index 04186f5..ede8a1f 100644 --- a/resources/js/ui.js +++ b/resources/js/ui.js @@ -757,9 +757,10 @@ const UI = { } }))); - template.querySelectorAll('[data-interactable="true"]').forEach((node) => { + template.querySelectorAll('[data-interactable="true"]').forEach(async (node) => { if (node.dataset.story) { - if (node.dataset.storyOnce && Memory.state.storyProgress[node.dataset.story]) { + const story = await fetchStory(node.dataset.story); + if (!story.canTriggerMultipleTimes && Memory.state.storyProgress[node.dataset.story]) { node.dataset.interactable = false; return; } @@ -1932,7 +1933,7 @@ const UI = { * @returns {HTMLElement} */ createStoryPopup () { - const popup = UI.createPopup(); + const popup = UI.createPopup({ isClosable: false }); return popup; }, -- cgit v1.2.3