diff options
author | Daniel Weipert <code@drogueronin.de> | 2023-08-21 22:44:21 +0200 |
---|---|---|
committer | Daniel Weipert <code@drogueronin.de> | 2023-08-21 22:44:21 +0200 |
commit | 54e5ffaeb79f989463c144e58dfcd677b752c5a9 (patch) | |
tree | 08e199c521d4ca6ba17f3499204d80f4be774917 | |
parent | eef0dd25f921a8b900e902631b79fc90d70c0309 (diff) |
log actions
-rw-r--r-- | index.html | 24 | ||||
-rw-r--r-- | resources/css/battle.css | 59 | ||||
-rw-r--r-- | resources/css/menu.css | 18 | ||||
-rw-r--r-- | resources/js/classes/State.js | 17 | ||||
-rw-r--r-- | resources/js/db.js | 2 | ||||
-rw-r--r-- | resources/js/game.js | 89 | ||||
-rw-r--r-- | resources/js/helpers.js | 2 | ||||
-rw-r--r-- | resources/js/memory.js | 4 | ||||
-rw-r--r-- | resources/js/ui.js | 40 |
9 files changed, 213 insertions, 42 deletions
@@ -22,12 +22,14 @@ <div id="techniques"></div> + <div id="log"></div> + <div id="status"> <span>Money: <span data-template-slot="money"></span></span> - <span>Monster Progress: <span data-template-slot="monsterProgress"></span></span> - <span>Trainer Progress: <span data-template-slot="trainerProgress"></span></span> - <button data-template-slot="nextTrainer">⇒ Next Trainer</button> - <button data-template-slot="nextArea">⇒ Next Area</button> + <span><img src="/modules/tuxemon/mods/tuxemon/gfx/ui/menu/tuxemon.png" title="Monster Progress" /> <span data-template-slot="monsterProgress"></span></span> + <span><img src="/modules/tuxemon/mods/tuxemon/gfx/ui/menu/player.png" title="Trainer Progress" style="filter: invert(1);" /> <span data-template-slot="trainerProgress"></span></span> + <button data-template-slot="nextTrainer" class="menu-button">⇒ Next Trainer</button> + <button data-template-slot="nextArea" class="menu-button">⇒ Next Area</button> </div> <div id="menu"> @@ -43,8 +45,13 @@ <img src="/modules/tuxemon/mods/tuxemon/gfx/ui/item/backpack.png" title="Inventory" width="64" height="64" /> </div> + <div id="menu__log"> + <img src="/modules/tuxemon/mods/tuxemon/gfx/ui/menu/journal.png" title="Log" width="64" height="64" /> + </div> + <div id="menu__journal"> - <img src="/modules/tuxemon/mods/tuxemon/gfx/ui/menu/journal.png" title="Journal" width="64" height="64" /> + <img src="/modules/tuxemon/mods/tuxemon/gfx/ui/menu/save.png" title="Save" width="64" height="64" /> + <img src="/modules/tuxemon/mods/tuxemon/gfx/ui/menu/load.png" title="Load" width="64" height="64" /> </div> <div id="menu__settings"> @@ -98,9 +105,10 @@ </div> </div> <div class="battle__monster-visual"> - <div class="battle__monster-sprite"> - <img data-template-slot="sprite" class="battle__monster-img" src="" draggable="false" /> - <img data-template-slot="trainerSprite" class="battle__monster__trainer-sprite" src="" draggable="false" /> + <img data-template-slot="sprite" class="battle__monster-sprite" src="" draggable="false" /> + <div class="battle__trainer-visual"> + <img data-template-slot="trainerSprite" class="battle__trainer-sprite" src="" draggable="false" /> + <div data-template-slot="trainerName"></div> </div> </div> </div> diff --git a/resources/css/battle.css b/resources/css/battle.css index 1e52802..748dad1 100644 --- a/resources/css/battle.css +++ b/resources/css/battle.css @@ -66,39 +66,43 @@ .exp-label { margin-right: 0.5rem; } + + .battle__monster-visual { flex-grow: 1; flex-basis: 50%; position: relative; -} - -.battle__monster-sprite { text-align: center; } -.battle__monster-img { +.battle__monster-sprite { cursor: pointer; transition-property: filter; width: 90px; } -.battle__monster-img.damaged { - filter: brightness(2); -} -.battle__technique-animation { - position: fixed; - z-index: 10; +.battle__monster-sprite.damaged { + filter: brightness(2); } -.battle__monster__trainer-sprite { +.battle__trainer-visual { position: absolute; top: 0; right: 0; +} + +.battle__trainer-sprite { width: 120px; max-width: 25vw; } + +.battle__technique-animation { + position: fixed; + z-index: 10; +} + .battle__monster-technique { background-color: beige; border: 2px solid #000; @@ -123,13 +127,7 @@ } @media (max-width: 600px) { - .battle__monster--has-trainer-sprite .battle__monster-sprite { - width: 30vw; - } -} -@media (max-width: 450px) { - .battle__monster--has-trainer-sprite .battle__monster-sprite { - width: auto; + .battle__monster--has-trainer-sprite .battle__monster-visual { text-align: left; } } @@ -218,7 +216,6 @@ #techniques { display: grid; grid-template-columns: 1fr 1fr; - grid-template-rows: 1fr 1fr; background: beige; } @@ -253,6 +250,30 @@ } + + +#log { + height: 20vh; + background-color: #fff; + border: 0.5rem solid rgba(0, 0, 0, 0.75); + padding: 0.5rem; + line-height: 1.75rem; + font-size: 1.25rem; + overflow-y: scroll; + + transition-property: height, border-width, padding; + transition-duration: 0.25s; +} + +#log.log--is-hidden { + height: 0; + border-width: 0; + padding: 0; +} + + + + .action-feedback { position: absolute; z-index: 11; diff --git a/resources/css/menu.css b/resources/css/menu.css index eee0b5f..84c6c77 100644 --- a/resources/css/menu.css +++ b/resources/css/menu.css @@ -96,9 +96,25 @@ #status { color: #fff; background-color: #000; - padding: 0.25rem; + padding: 0.5rem; + display: flex; justify-content: space-between; + align-items: center; + + font-size: 1.25rem; +} + +#status > span { + display: flex; + align-items: center; +} +#status > span > span { + margin-left: 0.5rem; +} + +#status .menu-button:hover { + background-color: rgba(255, 255, 255, 0.5); } diff --git a/resources/js/classes/State.js b/resources/js/classes/State.js index 6180c52..b7e9f5d 100644 --- a/resources/js/classes/State.js +++ b/resources/js/classes/State.js @@ -1,8 +1,17 @@ class State { - /** - * @type {string} - */ - language = 'en_US'; + Settings = { + /** + * @type {string} + */ + language: 'en_US', + + colors: { + player: '#00ff00', + player: '#ff0000', + }, + + logMaxLength: 1000, + }; /** * @type {Object.<string, Area>} diff --git a/resources/js/db.js b/resources/js/db.js index aaf5423..5850a01 100644 --- a/resources/js/db.js +++ b/resources/js/db.js @@ -84,7 +84,7 @@ async function initializeDB () { DB.elements[element] = await fetch(`/modules/tuxemon/mods/tuxemon/db/element/${element}.json`).then((response) => response.json()); } - await fetchTranslation(Memory.state.language); + await fetchTranslation(Memory.state.Settings.language); } /** diff --git a/resources/js/game.js b/resources/js/game.js index fad882b..d5e8669 100644 --- a/resources/js/game.js +++ b/resources/js/game.js @@ -14,6 +14,8 @@ const Game = { }, }, + logMessages: [], + isLoadingArea: false, isProgressingTurn: false, playerIsChoosingNextMonster: false, @@ -32,6 +34,8 @@ const Game = { await Game.applyStatusEffect(Memory.state.opponent.activeMonster); await Game.applyStatusEffect(Memory.state.player.activeMonster); + Game.logTurn('begin'); + // Phases for (const phaseKey of Object.keys(Game.phases)) { for (const event of Game.phases[phaseKey].player) { @@ -53,6 +57,8 @@ const Game = { Game.phases[phaseKey].opponent = []; } + Game.logTurn('end'); + UI.progressTurn(); Game.isProgressingTurn = false; }, @@ -186,6 +192,14 @@ const Game = { async tryUseTechnique (technique, user, target) { let canUse = true; + const log = (message, indentation) => { + Game.addPhaseEvent(Game.phases.preAction, user, () => { + Game.log(message, indentation); + }); + }; + + log(`${user.name} attempts to use ${technique.name}!`); + // recharge if (technique.isRecharging()) { if (Game.doBattleAnimation) { @@ -194,6 +208,8 @@ const Game = { UI.drawActionFeedback(feedbackNode); } + log('But has to recharge!', 1); + canUse = false; } @@ -204,6 +220,8 @@ const Game = { UI.drawActionFeedback(feedbackNode); } + log('But is nodding off!', 1); + canUse = false; } @@ -216,6 +234,8 @@ const Game = { technique.use(); Game.doBattleAnimation && UI.drawDamageMiss(UI.createDamageMiss()); + log('But missed!', 1); + return; } @@ -231,6 +251,10 @@ const Game = { async useTechnique (technique, user, target) { technique.use(); + Game.addPhaseEvent(Game.phases.action, user, () => { + Game.log(`${user.name} is using ${technique.name}!`); + }); + for (const techniqueEffectCode of technique.effects) { const techniqueEffect = new TechniqueEffect(techniqueEffectCode); techniqueEffect.setUser(user); @@ -250,6 +274,8 @@ const Game = { UI.drawDamage(damageNode); UI.drawTechniqueAnimation(technique); } + + Game.log(`Deals ${damage} to ${target.name}!`, 1); }); } @@ -271,7 +297,12 @@ const Game = { // healing else if (techniqueEffect.type === 'healing') { for (const recipient of techniqueEffect.recipients) { - recipient.hp += (user.level + 7) * technique.healingPower; + Game.addPhaseEvent(Game.phases.action, user, () => { + const heal = (user.level + 7) * technique.healingPower; + recipient.hp += heal; + + Game.log(`Heals ${heal}!`, 1); + }); } } @@ -293,7 +324,7 @@ const Game = { statusEffect.issuer = user; } - Game.addPhaseEvent(Game.phases.postAction, user, () => { + Game.addPhaseEvent(Game.phases.action, user, () => { // add status effect const potency = Math.random(); const success = technique.potency >= potency; @@ -304,6 +335,8 @@ const Game = { if (recipient.statusEffect) continue; recipient.statusEffect = statusEffect; + + Game.log(`${techniqueEffect.recipients.map((recipient) => recipient.name).join(', ')} ${techniqueEffect.recipients.length > 1 ? 'are' : 'is'} now ${statusEffect.name}!`, 1); } } }); @@ -327,6 +360,8 @@ const Game = { // if still 0 turns left after remove action if (monster.statusEffect.turnsLeft === 0) { + Game.log(`${monster.name} is not ${monster.statusEffect.name} anymore!`); + monster.statusEffect = null; } else { Game.applyStatusEffect(monster); @@ -336,6 +371,10 @@ const Game = { return; } + const logStatusIs = () => { + Game.log(`${monster.name} is ${monster.statusEffect.name}!`); + } + // poison / burn if (monster.statusEffect.slug === 'poison' || monster.statusEffect.slug === 'burn') { const statusEffectDamage = Math.floor(monster.stats.hp / 8); @@ -348,6 +387,8 @@ const Game = { UI.applyStatusEffectToDamage(damageNode, monster.statusEffect); UI.drawDamage(damageNode); } + + logStatusIs(); }); } @@ -369,6 +410,8 @@ const Game = { UI.applyStatusEffectToDamage(damageNode, monster.statusEffect); UI.drawDamage(damageNode); } + + logStatusIs(); }); } @@ -384,6 +427,8 @@ const Game = { UI.applyStatusEffectToDamage(feedbackNode, monster.statusEffect); UI.drawActionFeedback(feedbackNode); } + + logStatusIs(); }); } @@ -394,6 +439,8 @@ const Game = { Game.addPhaseEvent(Game.phases.preAction, monster, () => { technique.potency = technique.stats.potency * 0.5; technique.power = technique.stats.power * 0.5; + + logStatusIs(); }); monster.statusEffect.onRemove = () => { @@ -410,6 +457,8 @@ const Game = { Game.addPhaseEvent(Game.phases.preAction, monster, () => { technique.potency = technique.stats.potency * 0.5; technique.power = technique.stats.power * 0.5; + + logStatusIs(); }); monster.statusEffect.onRemove = () => { @@ -423,6 +472,10 @@ const Game = { else if (monster.statusEffect.slug === 'charging') { const nextStatusEffect = await fetchStatusEffect('chargedup'); + Game.addPhaseEvent(Game.phases.preAction, monster, () => { + logStatusIs(); + }); + monster.statusEffect.onRemove = () => { monster.statusEffect = nextStatusEffect; }; @@ -438,6 +491,8 @@ const Game = { Game.addPhaseEvent(Game.phases.preAction, monster, () => { monster.setStatModifier(statType, modifiedValue); + + logStatusIs(); }); } @@ -533,6 +588,33 @@ const Game = { })); }, + /** + * @param {string} message + * @param {number} indentation + * @param {string} style + */ + log (message, indentation = 0, style) { + Game.logMessages.push({ + message: message, + indentation: indentation, + style: style, + }); + UI.drawLog(); + }, + + /** + * @param {('begin' | 'end')} + */ + logTurn (state) { + Game.log( + '- '.repeat(8) + `Turn ${Memory.state.turn} · ${slugToName(state)}` + ' -'.repeat(8), + 0, + { + textAlign: 'center', + } + ); + }, + /* Progression */ @@ -718,8 +800,9 @@ const Game = { Memory.state.player.monsters.push(caughtMonster); + Game.log(`Caught ${caughtMonster.name}!`); + await Game.encounterWildMonster(); - await Game.progressTurn(); }, diff --git a/resources/js/helpers.js b/resources/js/helpers.js index 53a5c87..2395878 100644 --- a/resources/js/helpers.js +++ b/resources/js/helpers.js @@ -55,5 +55,5 @@ function randomString () { * @param {string} msgid */ function translate (msgid) { - return DB.translations[Memory.state.language][msgid]; + return DB.translations[Memory.state.Settings.language][msgid]; } diff --git a/resources/js/memory.js b/resources/js/memory.js index 1732472..550ea55 100644 --- a/resources/js/memory.js +++ b/resources/js/memory.js @@ -131,8 +131,8 @@ const Memory = { */ const loadedState = JSON.parse(atob(saveData)); - Memory.state.language = loadedState.language; - await fetchTranslation(Memory.state.language); + Memory.state.Settings.language = loadedState.language; + await fetchTranslation(Memory.state.Settings.language); Memory.state.turn = loadedState.turn; Memory.state.money = loadedState.money; diff --git a/resources/js/ui.js b/resources/js/ui.js index 06e55ed..bfd9204 100644 --- a/resources/js/ui.js +++ b/resources/js/ui.js @@ -42,6 +42,8 @@ const UI = { techniques: document.querySelector('#techniques'), + log: document.querySelector('#log'), + status: document.querySelector('#status'), nextTrainer: document.querySelector('#status [data-template-slot="nextTrainer"]'), nextArea: document.querySelector('#status [data-template-slot="nextArea"]'), @@ -49,6 +51,7 @@ const UI = { menuParty: document.querySelector('#menu__party'), menuInventory: document.querySelector('#menu__inventory'), menuCatch: document.querySelector('#menu__catch'), + menuLog: document.querySelector('#menu__log'), menuJournal: document.querySelector('#menu__journal'), menuSettings: document.querySelector('#menu__settings'), }, @@ -299,10 +302,12 @@ const UI = { if (Game.isBattleType('trainer')) { battleMonsterNode.classList.add('battle__monster--is-trainer'); + battleMonsterNode.querySelector('[data-template-slot="trainerName"]').textContent = Memory.state.opponent.name; if (Memory.state.opponent.sprite) { battleMonsterNode.classList.add('battle__monster--has-trainer-sprite'); battleMonsterNode.querySelector('[data-template-slot="trainerSprite"]').src = `/modules/tuxemon/mods/tuxemon/gfx/sprites/player/${Memory.state.opponent.sprite}`; + battleMonsterNode.querySelector('[data-template-slot="trainerSprite"]').title = Memory.state.opponent.name; } } @@ -456,6 +461,34 @@ const UI = { requestAnimationFrame(techniqueAnimationLoop); }, + drawLog () { + if (UI.elements.log.children.length > Memory.state.Settings.logMaxLength) { + UI.elements.log.innerHTML = ''; + } + + for (const message of Game.logMessages) { + const textNode = document.createElement('div'); + + textNode.innerHTML = '> '.repeat(message.indentation) + message.message; + + if (message.style) { + for (const property of Object.keys(message.style)) { + const value = message.style[property]; + textNode.style[property] = value; + } + } + + UI.elements.log.appendChild(textNode); + UI.elements.log.scrollTop = UI.elements.log.scrollHeight; + } + + Game.logMessages = []; + }, + + toggleLog () { + UI.elements.log.classList.toggle('log--is-hidden'); + }, + /** * @param {Area} area */ @@ -892,7 +925,7 @@ const UI = { languageOptionNode.value = languageCode; languageOptionNode.textContent = languageName; - if (languageCode === Memory.state.language) { + if (languageCode === Memory.state.Settings.language) { languageOptionNode.selected = true; } @@ -901,9 +934,9 @@ const UI = { languageSelectNode.addEventListener('change', async () => { const selected = [...languageSelectNode.children].find((node) => node.selected === true); - Memory.state.language = selected.value; + Memory.state.Settings.language = selected.value; - await fetchTranslation(Memory.state.language); + await fetchTranslation(Memory.state.Settings.language); UI.drawOpponentMonster(); UI.drawActiveMonster(); UI.drawActiveTechniques(); @@ -1227,5 +1260,6 @@ const UI = { // UI element click bindings UI.elements.menuParty.addEventListener('click', UI.openPartyMenu); UI.elements.menuInventory.addEventListener('click', UI.openInventoryMenu); +UI.elements.menuLog.addEventListener('click', UI.toggleLog); UI.elements.menuJournal.addEventListener('click', UI.openJournalMenu); UI.elements.menuSettings.addEventListener('click', UI.openSettingsMenu); |