diff options
author | Daniel Weipert <code@drogueronin.de> | 2023-08-15 13:18:57 +0200 |
---|---|---|
committer | Daniel Weipert <code@drogueronin.de> | 2023-08-15 13:18:57 +0200 |
commit | 8ab8988d01199f64151c532c59ff6c08735d4e37 (patch) | |
tree | 17d5bbdf36aa744119f37e0fffc6ede78d13fa70 |
initial commit
-rw-r--r-- | .gitignore | 0 | ||||
-rw-r--r-- | .gitmodules | 3 | ||||
-rw-r--r-- | Readme.md | 1 | ||||
-rw-r--r-- | all-monsters.php | 10 | ||||
-rw-r--r-- | db/all-monsters.json | 1 | ||||
-rw-r--r-- | index.html | 81 | ||||
m--------- | modules/tuxemon | 0 | ||||
-rw-r--r-- | script.js | 583 | ||||
-rw-r--r-- | style.css | 190 |
9 files changed, 869 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/.gitignore diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..c3786a0 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "modules/tuxemon"] + path = modules/tuxemon + url = https://github.com/Tuxemon/Tuxemon diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..7de2ce4 --- /dev/null +++ b/Readme.md @@ -0,0 +1 @@ +- https://wiki.tuxemon.org/Category:Monster diff --git a/all-monsters.php b/all-monsters.php new file mode 100644 index 0000000..cf687c7 --- /dev/null +++ b/all-monsters.php @@ -0,0 +1,10 @@ +<?php + +$allMonsters = []; +foreach (scandir(__DIR__ . '/modules/tuxemon/mods/tuxemon/db/monster') as $file) { + if (in_array($file, ['.', '..'])) continue; + + $allMonsters[] = pathinfo($file, PATHINFO_FILENAME); +} + +file_put_contents(__DIR__ . '/db/all-monsters.json', json_encode($allMonsters)); diff --git a/db/all-monsters.json b/db/all-monsters.json new file mode 100644 index 0000000..4011363 --- /dev/null +++ b/db/all-monsters.json @@ -0,0 +1 @@ +["aardart","aardorn","abesnaki","agnidon","agnigon","agnite","agnsher","allagon","altie","ambuwl","angesnow","angrito","anoleaf","anu","apeoro","araignee","arthrobolt","av8r","axylightl","b_ver_1","bamboon","banling","baobaraffe","baoby","bearloch","beenstalker","bigfin","birdling","blasdoor","bolt","boltnu","botbot","boxali","brewdin","bricgard","brickhemoth","budaye","bugnin","bumbulus","bursa","cairfrey","capinyah","capiti","cardiling","cardinale","cardiwing","cataspike","cateye","chenipode","cherubat","chibiro","chillimp","chloragon","chrome_robo","chromeye","cochini","coleorus","conglolem","conifrost","conileaf","coproblight","corvix","cowpignon","criniotherme","d0llf1n","dandicub","dandylion","dankush","dark_robo","demosnow","dinoflop","djinnbo","dollfin","dracune","dragarbor","drashimi","drokoro","dune_pincher","eaglace","elofly","elostorm","elowind","embazook","embra","enduros","eruptibus","exapode","exclawvate","eyenemy","eyesore","f7u1t3ra","fancair","ferricran","firomenis","flacono","flambear","flisces","fluoresfin","fluttaflap","foofle","fordin","forturtle","foxfire","foxko","fribbit","frondly","fruitera","furnursus","fuzzina","fuzzlet","galnec","gectile","ghosteeth","glombroc","graffiki","grimachin","grinflare","grintot","grintrock","grumpi","gryfix","hampotamos","happito","hatchling","hectapod","heronquak","hotline","howl","hydrone","ignibus","imbrickcile","incandesfin","jelillow","jemuar","joulraton","k9","katacoon","katapill","kernel","komodraw","l3gk0","lambert","lapinou","legko","lendos","lightmare","loliferno","lucifice","lunight","manosting","masknake","mauai","medushock","memnomnom","merlicun","metesaur","miaownolith","mingdyn","mk01_alpha","mk01_beta","mk01_delta","mk01_gamma","mk01_omega","mk01_proto","moloch","mrmoswitch","mystikapi","narcileaf","neutrito","nimbulex","noctalo","noctula","nostray","nudiflot_female","nudiflot_male","nudikill","nudimind","nuenflu","nut","octabode","ouroboutlet","pairagrim","pairagrin","pantherafira","pharfan","picc","pigabyte","pilthropus","pipis","poinchin","polyrock","possessun","potturmeist","potturney","propellercat","prophetoise","puparmor","pyraminx","pythock","pythwire","qetzlrokilus","r0ck1tt3n","rabbitosaur","rhincus","rhinocarpe","rinocereed","rockat","rockitten","rocktot","rosarin","ruption","sadito","sampsack","sampsage","sapragon","sapsnap","saurchin","sclairus","seirein","selket","selmatek","seraphice","shammer","sharpfin","shelagu","shnark","shybulb","skwib","slichen","sludgehog","snaki","snarlon","snock","snokari","snowrilla","sockeserp","solight","spighter","spoilurm","spycozeus","squabbit","squink","statursus","strella","sumchon","tadcool","tarpeur","taupypus","teddisun","tetrchimp","tigrock","tikoal","tikorch","tobishimi","toufigel","tourbidi","trapsnap","tsushimi","tumblebee","tumbledillo","tumblequill","tumbleworm","turnipper","tux","tweesher","uf0","uglip","uneye","urcheedle","urcine","vamporm","velocitile","vigueur","vivicinder","vividactil","vivipere","viviphyta","vivisource","viviteel","vivitrans","vivitron","volcoli","waysprite","weavifly","windeye","woodoor","wrougon","xeon","xeon_2","yiinaang","ziggurat","zunna"]
\ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..5b1c653 --- /dev/null +++ b/index.html @@ -0,0 +1,81 @@ +<html> + <head> + <link rel="stylesheet" href="style.css" /> + </head> + <body> + <div class="wrap"> + <div>Money: <div id="money"></div></div> + <div id="party"></div> + + <div id="battle"> + <div id="battle__enemy"></div> + + <div id="battle__player"></div> + </div> + </div> + + <template id="tpl___party__monster"> + <div class="party__monster"> + <div><div class="monster__level"></div></div> + <img src="" /> + <div class="monster__exp"> + <div class="monster__exp-bar"></div> + </div> + <div class="monster__exp-text"></div> + <div class="monster__active-technique"></div> + </div> + </template> + + <template id="tpl___battle__monster"> + <div class="battle__monster"> + <div class="battle__monster-info"> + <div class="battle__monster-info-box"> + <div> + <span class="battle__monster-info__name">{NAME}</span> <span class="battle__monster-info__gender">{GENDER}</span> Lv. <span class="battle__monster-info__level">{LEVEL}</span> + </div> + <div class="hp"> + <div class="hp-bar-wrap"> + <div class="hp-bar"></div> + </div> + <div class="hp-text"></div> + </div> + </div> + <div class="battle__monster-info-exp"> + <div class="exp-label">XP</div> + <div class="exp"> + <div class="exp-bar-wrap"> + <div class="exp-bar"></div> + </div> + <div class="exp-text"></div> + </div> + </div> + </div> + <div class="battle__monster-visual"> + <div class="battle__monster-sprite"> + <img src="" draggable="false" /> + </div> + <div class="battle__monster-technique"> + + </div> + </div> + </div> + </template> + + <template id="tpl___popup"> + <div class="popup__overlay"> + <div class="popup"></div> + </div> + </template> + + <template id="tpl___moveset__list"> + <div class="moveset__list"></div> + </template> + <template id="tpl___moveset__item"> + <div class="moveset__item"> + name, lvlrequired, types, power + </div> + </template> + + <script type="text/javascript" src="script.js"></script> + </body> +</html> diff --git a/modules/tuxemon b/modules/tuxemon new file mode 160000 +Subproject 136c50bff1fb5b04a1cb0032030fb868c0bca9e diff --git a/script.js b/script.js new file mode 100644 index 0000000..c5c2028 --- /dev/null +++ b/script.js @@ -0,0 +1,583 @@ +const DB = { + allMonsters: [], + monsters: {}, + shapes: {}, + elements: {}, + techniques: {}, +}; + +const ElementType = { + aether: 'aether', + wood: 'wood', + fire: 'fire', + earth: 'earth', + metal: 'metal', + water: 'water', +}; +const ElementTypeColor = { + [ElementType.aether]: 'rgba(255, 255, 255, 1)', + [ElementType.wood]: 'rgb(0, 255, 0, 1)', + [ElementType.fire]: 'red', + [ElementType.earth]: 'brown', + [ElementType.metal]: 'silver', + [ElementType.water]: 'blue', +}; +const TasteWarm = { + tasteless: 'tasteless', + peppy: 'peppy', + salty: 'salty', + hearty: 'hearty', + zesty: 'zesty', + refined: 'refined', +}; +const TasteCold = { + tasteless: 'tasteless', + mild: 'mild', + sweet: 'sweet', + soft: 'soft', + flakey: 'flakey', + dry: 'dry', +}; +const TechniqueRange = { + melee: 'melee', + touch: 'touch', + ranged: 'ranged', + reach: 'reach', + reliable: 'reliable', +}; +const StatusType = { + melee: 'melee', + armour: 'armour', + ranged: 'ranged', + dodge: 'dodge', + speed: 'speed', +}; + +class State { + money = 0; + monsters = []; + + partyMonsters = []; + activeMonster = null; + activeTechnique = null; + + enemy = { + monster: null, + }; +}; + +class Monster { + #level = 1; + exp = 1; + hp = 0; + + tasteWarm = TasteWarm.tasteless; + tasteCold = TasteCold.tasteless; + + name = ''; + gender = ''; + + statusModifiers = { + hp: 0, + melee: 0, + armour: 0, + ranged: 0, + dodge: 0, + speed: 0, + }; + + experienceModifier = 1; + moneyModifier = 1; + + constructor (slug) { + this.slug = slug; + + const tasteWarm = Object.keys(TasteWarm).slice(1); + this.tasteWarm = tasteWarm[Math.floor(Math.random() * tasteWarm.length)]; + const tasteCold = Object.keys(TasteCold).slice(1); + this.tasteCold = tasteCold[Math.floor(Math.random() * tasteCold.length)]; + + this.hp = this.status.hp; + + const possibleGenders = DB.monsters[this.slug].possible_genders; + this.gender = possibleGenders[Math.floor(Math.random() * possibleGenders.length)] + } + + get shape () { + for (const shapeData of DB.shapes) { + if (shapeData.slug === DB.monsters[this.slug].shape) { + return shapeData; + } + } + } + + get types () { + return DB.monsters[this.slug].types; + } + + get moveset () { + return DB.monsters[this.slug].moveset; + } + + get evolutions () { + return DB.monsters[this.slug].evolutions; + } + + get level () { + return this.#level; + } + + set level (level) { + const statusPreLevelUp = this.status; + const hpPreLevelUp = this.hp; + + this.#level = level; + + const statusPostLevelUp = this.status; + + this.hp = statusPostLevelUp.hp - (statusPreLevelUp.hp - hpPreLevelUp); + + if (this.exp < this.getExperienceRequired(-1)) { + this.exp = this.getExperienceRequired(-1); + } + } + + get name () { + return slugToName(this.slug); + } + + canLevelUp () { + return this.exp >= this.getExperienceRequired(); + } + + levelUp () { + while (this.canLevelUp()) { + this.level++; + } + } + + getExperienceRequired (levelOffset = 0) { + return Math.max( + Math.pow(this.level + levelOffset, 3), + 1 + ); + } + + canEvolve () { + if (this.evolutions.length === 0) { + return; + } + + return this.level >= this.evolutions[0].at_level; + } + + evolve () { + const evolution = this.evolutions[0]; + + const statusPreEvolve = this.status; + const hpPreEvolve = this.hp; + + this.slug = evolution.monster_slug; + + const statusPostEvolve = this.status; + + this.hp = statusPostEvolve.hp - (statusPreEvolve.hp - hpPreEvolve); + } + + getTasteStatusModifier (statusName, baseStatus) { + let positive = 0; + let negative = 0; + + let isPositive = false; + let isNegative = false; + if (statusName === 'melee') { + isPositive = this.tasteWarm === TasteWarm.salty; + isNegative = this.tasteCold === TasteCold.sweet; + } + else if (statusName === 'armour') { + isPositive = this.tasteWarm === TasteWarm.hearty; + isNegative = this.tasteCold === TasteCold.soft; + } + else if (statusName === 'ranged') { + isPositive = this.tasteWarm === TasteWarm.zesty; + isNegative = this.tasteCold === TasteCold.flakey; + } + else if (statusName === 'dodge') { + isPositive = this.tasteWarm === TasteWarm.refined; + isNegative = this.tasteCold === TasteCold.dry; + } + else if (statusName === 'speed') { + isPositive = this.tasteWarm === TasteWarm.peppy; + isNegative = this.tasteCold === TasteCold.mild; + } + + if (isPositive) { + positive = baseStatus * 10 / 100; + } + if (isNegative) { + negative = baseStatus * 10 / 100; + } + + return Math.floor(positive) - Math.floor(negative); + } + + get status () { + const multiplier = this.level + 7; + let hp = (this.shape.hp * multiplier) + this.statusModifiers.hp; + let melee = (this.shape.melee * multiplier) + this.statusModifiers.melee; + let armour = (this.shape.armour * multiplier) + this.statusModifiers.armour; + let ranged = (this.shape.ranged * multiplier) + this.statusModifiers.ranged; + let dodge = (this.shape.dodge * multiplier) + this.statusModifiers.dodge; + let speed = (this.shape.speed * multiplier) + this.statusModifiers.speed; + + // Tastes + melee += this.getTasteStatusModifier('melee', melee); + armour += this.getTasteStatusModifier('armour', melee); + ranged += this.getTasteStatusModifier('ranged', melee); + dodge += this.getTasteStatusModifier('dodge', melee); + speed += this.getTasteStatusModifier('speed', melee); + + return { + hp, + melee, + armour, + ranged, + dodge, + speed, + }; + } +}; + +class Technique { + constructor (slug) { + this.slug = slug; + } + + get types () { + return DB.techniques[this.slug].types; + } + + get power () { + return DB.techniques[this.slug].power; + } + + get range () { + return DB.techniques[this.slug].range; + } +} + +function simpleDamageMultiplier (techniqueTypes, targetTypes) { + let multiplier = 1; + + for (const techniqueType of techniqueTypes) { + if (techniqueType === ElementType.aether) { + continue; + } + + for (const targetType of targetTypes) { + if (targetType === ElementType.aether) { + continue; + } + + multiplier *= DB.elements[techniqueType].types.find((type) => type.against === targetType).multiplier; + } + } + + return Math.max(0.25, Math.min(multiplier, 4)); +} +function simpleDamageCalculation (technique, user, target) { + let userBaseStrength = user.level + 7; + let userStrength = 1; + let targetResist = 1; + + if (technique.range === TechniqueRange.melee) { + userStrength = userBaseStrength * user.status.melee; + targetResist = target.status.armour; + } + else if (technique.range === TechniqueRange.touch) { + userStrength = userBaseStrength * user.status.melee; + targetResist = target.status.dodge; + } + else if (technique.range === TechniqueRange.ranged) { + userStrength = userBaseStrength * user.status.ranged; + targetResist = target.status.dodge; + } + else if (technique.range === TechniqueRange.reach) { + userStrength = userBaseStrength * user.status.ranged; + targetResist = target.status.armour; + } + else if (technique.range === TechniqueRange.reliable) { + userStrength = userBaseStrength; + targetResist = 1; + } + + const multiplier = simpleDamageMultiplier(technique.types, target.types); + const moveStrength = technique.power * multiplier; + const damage = Math.floor((userStrength * moveStrength) / targetResist); + + return damage; +} + +function calculateAwardedExperience (playerMonster, enemyMonster) { + return Math.max( + Math.floor( + enemyMonster.getExperienceRequired(-1) / enemyMonster.level * enemyMonster.experienceModifier / playerMonster.level + ), + 1 + ); +} + +async function fetchMonster (slug) { + if (! DB.monsters[slug]) { + DB.monsters[slug] = await fetch(`/modules/tuxemon/mods/tuxemon/db/monster/${slug}.json`).then((response) => response.json()); + } + + return new Monster(slug); +} +async function fetchTechnique (slug) { + if (! DB.techniques[slug]) { + DB.techniques[slug] = await fetch(`/modules/tuxemon/mods/tuxemon/db/technique/${slug}.json`).then((response) => response.json()); + } + + return new Technique(slug); +} + +function standardizeColor (color) { + var ctx = document.createElement('canvas').getContext('2d'); + ctx.fillStyle = color; + + return ctx.fillStyle; +} +function mixColors(...colors) { + let r = 0; + let g = 0; + let b = 0; + + for (const color of colors) { + const [cr, cg, cb] = color.match(/\w\w/g).map((c) => parseInt(c, 16)); + + r += cr; + g += cg; + b += cb; + } + + r = r / colors.length; + g = g / colors.length; + b = b / colors.length; + + return `rgb(${r}, ${g}, ${b})`; +} + +function slugToName (slug) { + return slug.split('_').map((item) => item.charAt(0).toUpperCase() + item.slice(1)).join(' '); +} + +(async function () { + DB.allMonsters = await fetch('/db/all-monsters.json').then((response) => response.json()); + DB.shapes = await fetch('/modules/tuxemon/mods/tuxemon/db/shape/shapes.json').then((response) => response.json()); + for (const element of Object.keys(ElementType)) { + DB.elements[element] = await fetch(`/modules/tuxemon/mods/tuxemon/db/element/${element}.json`).then((response) => response.json()); + } + + const state = new State(); + state.enemy.monster = await fetchMonster('drashimi'); + + state.partyMonsters = [ + await fetchMonster('corvix'), + await fetchMonster('lunight'), + await fetchMonster('prophetoise'), + await fetchMonster('drashimi'), + ]; + state.activeMonster = state.partyMonsters[0]; + state.activeTechnique = await fetchTechnique(state.activeMonster.moveset[0].technique); + + const templatePartyMonster = document.querySelector('#tpl___party__monster').innerHTML; + const templateBattleMonster = document.querySelector('#tpl___battle__monster').innerHTML; + const templatePopup = document.querySelector('#tpl___popup').innerHTML; + const templateMovesetList = document.querySelector('#tpl___moveset__list').innerHTML; + const templateMovesetItem = document.querySelector('#tpl___moveset__item').innerHTML; + + const party = document.querySelector('#party'); + const money = document.querySelector('#money'); + + const battle = document.querySelector('#battle'); + const battleEnemy = document.querySelector('#battle__enemy'); + const battlePlayer = document.querySelector('#battle__player'); + + const UI = { + activeMonster: null, + + getTemplate (template) { + var tpl = document.createElement('div'); + tpl.innerHTML = template.trim(); + + return tpl.firstChild; + }, + + setExp (monster, parentNode) { + const expBar = parentNode.querySelector('.exp-bar'); + const expText = parentNode.querySelector('.exp-text'); + + const expToNextLevel = monster.getExperienceRequired() - monster.getExperienceRequired(-1); + const currentExp = monster.exp - monster.getExperienceRequired(-1); + expBar.style.width = (currentExp / expToNextLevel) * 100 + '%'; + expText.textContent = monster.exp + ' / ' + monster.getExperienceRequired(); + }, + + setHp (monster, parentNode) { + const hpBar = parentNode.querySelector('.hp-bar'); + const hpText = parentNode.querySelector('.hp-text'); + + const percentHp = (monster.hp / monster.status.hp) * 100; + if (percentHp > 60) { + hpBar.style.backgroundColor = 'green'; + } else if (percentHp > 15) { + hpBar.style.backgroundColor = 'rgb(240, 240, 100)'; + } else { + hpBar.style.backgroundColor = 'red'; + } + + hpBar.style.width = percentHp + '%'; + hpText.textContent = monster.hp + ' / ' + monster.status.hp; + }, + + createDamage (event, damage) { + const damageNode = document.createElement('div'); + damageNode.classList.add('damage'); + damageNode.textContent = damage; + + const damageMultiplier = simpleDamageMultiplier(state.activeTechnique.types, state.enemy.monster.types); + damageNode.style.fontSize = damageMultiplier + 'rem'; + + damageNode.style.left = event.pageX - battleEnemy.offsetLeft; + damageNode.style.top = event.pageY - battleEnemy.offsetTop; + + damageNode.style.color = mixColors(...state.activeTechnique.types.map((type) => standardizeColor(ElementTypeColor[type]))); + + const damageNodeDuration = 2; + damageNode.style.animationDuration = damageNodeDuration + 's'; + + battleEnemy.appendChild(damageNode); + setTimeout(() => damageNode.remove(), (damageNodeDuration * 1000) - 500); + + const enemyImg = battleEnemy.querySelector('img'); + const imgClickDuration = 0.1; + enemyImg.style.transitionDuration = imgClickDuration + 's'; + enemyImg.classList.add('damaged'); + clearTimeout(clickTimeout); + clickTimeout = setTimeout(() => enemyImg.classList.remove('damaged'), imgClickDuration * 1000); + }, + + addPartyMonster (slug) { + let partyMonster = document.createElement('div'); + partyMonster.innerHTML = templatePartyMonster.trim(); + partyMonster = partyMonster.firstChild; + + partyMonster.dataset.slug = slug; + partyMonster.querySelector('img').src = `/modules/tuxemon/mods/tuxemon/gfx/sprites/battle/${slug}-front.png`; + + partyMonster.addEventListener('click', async (event) => { + let target = event.target; + while (target.parentNode.id !== 'party') { + target = target.parentNode; + } + + state.activeMonster = state.partyMonsters[Array.prototype.indexOf.call(document.querySelector('#party').children, target)]; + state.activeTechnique = await fetchTechnique(state.activeMonster.moveset[0].technique); + + UI.setBattleMonster(state.activeMonster, 'player'); + }); + partyMonster.style.cursor = 'pointer'; + + party.appendChild(partyMonster); + }, + + setBattleMonster (monster, where) { + let battleMonster = document.createElement('div'); + battleMonster.innerHTML = templateBattleMonster.trim(); + battleMonster = battleMonster.firstChild; + + battleMonster.querySelector('.battle__monster-info__name').textContent = monster.name; + battleMonster.querySelector('.battle__monster-info__gender').textContent = monster.gender === 'male' ? '♂' : monster.gender === 'female' ? '♀' : '⚲'; + battleMonster.querySelector('.battle__monster-info__level').textContent = monster.level; + UI.setHp(monster, battleMonster.querySelector('.hp')); + battleMonster.querySelector('img').src = `/modules/tuxemon/mods/tuxemon/gfx/sprites/battle/${monster.slug}-front.png`; + + if (where === 'player') { + UI.setExp(monster, battleMonster.querySelector('.exp')); + battleMonster.querySelector('.battle__monster-technique').textContent = + state.activeTechnique.slug + ' - ' + state.activeTechnique.types + ' - ' + state.activeTechnique.power; + battleMonster.querySelector('.battle__monster-technique').addEventListener('click', UI.openMovesetSelection); + + battleMonster.classList.add('battle__monster--player'); + battlePlayer.replaceChildren(battleMonster); + } else { + battleMonster.classList.add('battle__monster--enemy'); + // battleEnemy.replaceChildren(battleMonster); + battleEnemy.querySelector('.battle__monster') && battleEnemy.removeChild(battleEnemy.querySelector('.battle__monster')); + battleEnemy.appendChild(battleMonster); + } + }, + + async openMovesetSelection () { + const popup = UI.getTemplate(templatePopup); + popup.addEventListener('click', ({ target }) => target === popup && popup.remove()); + + const movesetList = UI.getTemplate(templateMovesetList); + for (const move of state.activeMonster.moveset) { + const technique = await fetchTechnique(move.technique); + const movesetItem = UI.getTemplate(templateMovesetItem); + + movesetItem.textContent = slugToName(technique.slug) + ' - ' + technique.types + ' - ' + technique.power; + movesetItem.addEventListener('click', () => { + state.activeTechnique = technique; + UI.setBattleMonster(state.activeMonster, 'player'); + + popup.remove(); + }); + + movesetList.appendChild(movesetItem); + } + + popup.querySelector('.popup').appendChild(movesetList); + document.body.appendChild(popup); + }, + }; + + for (const monster of state.partyMonsters) { + UI.addPartyMonster(monster.slug); + } + + UI.setBattleMonster(state.activeMonster, 'player'); + UI.setBattleMonster(state.enemy.monster, 'enemy'); + + var clickTimeout; + document.querySelector('#battle__enemy').addEventListener('click', async (event) => { + const damage = simpleDamageCalculation(state.activeTechnique, state.activeMonster, state.enemy.monster); + UI.createDamage(event, damage); + + state.enemy.monster.hp -= damage; + if (state.enemy.monster.hp <= 0) { + const faintedMonster = state.enemy.monster; + + state.enemy.monster = await fetchMonster(DB.allMonsters[Math.floor(Math.random() * DB.allMonsters.length)]); + state.enemy.monster.level = Math.ceil(Math.random() * state.activeMonster.level); + state.enemy.monster.moneyModifier = state.enemy.monster.level; + UI.setBattleMonster(state.enemy.monster, 'enemy'); + + state.money += faintedMonster.level * faintedMonster.moneyModifier; + + state.activeMonster.exp += calculateAwardedExperience(state.activeMonster, faintedMonster); + state.activeMonster.levelUp(); + if (state.activeMonster.canEvolve()) { + await fetchMonster(state.activeMonster.evolutions[0].monster_slug); + state.activeMonster.evolve(); + state.activeTechnique = await fetchTechnique(state.activeMonster.moveset[0].technique); + } + + UI.setBattleMonster(state.activeMonster, 'player'); + } + UI.setHp(state.enemy.monster, battleEnemy); + money.textContent = state.money; + }); +})(); diff --git a/style.css b/style.css new file mode 100644 index 0000000..8ffcee1 --- /dev/null +++ b/style.css @@ -0,0 +1,190 @@ +* { + box-sizing: border-box; +} + +body { + margin: 0; +} + +img { + display: inline-block; + max-width: 100%; +} + +.wrap { + margin: 0 auto; + width: 1200px; + height: 100vh; + display: flex; + justify-content: center; + align-items: center; +} + +.popup__overlay { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background-color: rgba(0, 0, 0, 0.8); + display: flex; + justify-content: center; + align-items: center; +} +.popup { + background-color: #fff; + padding: 1rem; +} + +#battle { + user-select: none; + min-width: 750px; + min-height: 300px; + padding: 1rem; + + display: flex; + flex-direction: column; + justify-content: space-between; + + background-image: url('/modules/tuxemon/mods/tuxemon/gfx/backgrounds/test/back02.png'); + background-image: url('https://wiki.tuxemon.org/images/9/9f/Sea_background.png'); + background-size: cover; +} + +#battle__enemy { + position: relative; + cursor: pointer; + flex-grow: 1; +} + +.battle__monster { + display: flex; + justify-content: space-between; + align-items: center; +} +.battle__monster-info { + border: 2px solid #000; + background-color: beige; + + flex-grow: 1; + flex-basis: 50%; +} +.battle__monster-info-box { + display: flex; + flex-direction: column; + + padding: 0.25rem; +} +.battle__monster-info__gender { + line-height: 1em; +} +.battle__monster-info-exp { + display: flex; + align-items: center; + + padding: 0.25rem; + + background-color: #000; + color: yellow; +} +.exp-label { + margin-right: 0.5rem; +} +.battle__monster-visual { + flex-grow: 1; + flex-basis: 50%; + text-align: center; +} + +.battle__monster-sprite { + margin-bottom: 0.25rem; +} +.battle__monster-sprite img { + transition-property: filter; +} +.battle__monster-sprite img.damaged { + filter: brightness(2); +} + +.battle__monster-technique { + background-color: beige; + border: 2px solid #000; + display: inline; + padding: 0.25rem; +} + +.battle__monster--player { + flex-direction: row-reverse; +} +.battle__monster--enemy .battle__monster-info-exp, +.battle__monster--enemy .battle__monster-technique { + display: none; +} + +.exp { + width: 100%; +} +.exp-bar-wrap { + width: 100%; + border: 1px solid rgba(0, 0, 0, 0.5); + background-color: lightgray; +} +.exp-bar { + background-color: blue; + height: 7px; + transition: background-color; + width: 0%; +} +.hp-bar-wrap { + width: 100%; + border: 1px solid rgba(0, 0, 0, 0.5); +} +.hp-bar { + background-color: green; + height: 10px; + transition: background-color; +} + +#enemy { + position: relative; + user-select: none; + cursor: pointer; + min-width: 750px; + min-height: 300px; + display: flex; + justify-content: center; + align-items: center; + border: 1px solid #000; + flex-direction: column; + + background-image: url('/modules/tuxemon/mods/tuxemon/gfx/backgrounds/test/back02.png'); + background-image: url('https://wiki.tuxemon.org/images/9/9f/Sea_background.png'); + background-size: cover; +} + +#enemy img { + transition-property: filter; +} +#enemy img.clicked { + filter: brightness(2); +} + + +.damage { + position: absolute; + color: red; + + animation: float-up-and-disappear; +} + +@keyframes float-up-and-disappear { + from { + opacity: 1; + } + + to { + top: 0; + opacity: 0; + font-size: 0; + } +} |