class Monster { #level = 2; #hp = 0; exp = 1; tasteWarm = TasteWarm.tasteless; tasteCold = TasteCold.tasteless; gender = ''; heldItem = null; /** * @type {StatusEffect} */ statusEffect = null; statModifiers = { hp: 0, melee: 0, armour: 0, ranged: 0, dodge: 0, speed: 0, }; experienceModifier = 1; moneyModifier = 1; /** * @type {Technique[]} */ activeTechniques = []; 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.stats.hp; const possibleGenders = DB.monsters[this.slug].possible_genders; this.gender = possibleGenders[Math.floor(Math.random() * possibleGenders.length)]; } async initialize () { for (const move of this.getLearnableTechniques()) { this.activeTechniques.push(await fetchTechnique(move.technique)); } } 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 category () { return DB.monsters[this.slug].category; } get moveset () { return DB.monsters[this.slug].moveset; } /** * @returns {DB_Evolution[]} */ get evolutions () { return DB.monsters[this.slug].evolutions; } get level () { return this.#level; } set level (level) { const statsPreLevelUp = this.stats; const hpPreLevelUp = this.hp; this.#level = level; const statsPostLevelUp = this.stats; this.hp = statsPostLevelUp.hp - (statsPreLevelUp.hp - hpPreLevelUp); if (this.exp < this.getExperienceRequired(-1)) { this.exp = this.getExperienceRequired(-1); } } get hp () { return this.#hp; } set hp (hp) { this.#hp = Math.max(0, Math.min(hp, this.stats.hp)); } get name () { return translate(this.slug) || slugToName(this.slug); } /** * @returns {Object[]} */ getLearnableTechniques () { return this.moveset.filter((move) => this.level >= move.level_learned); } 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 ); } /** * @param {EvolutionPathType} path */ getPossibleEvolutions (path) { return this.evolutions.filter((evolution) => { if (evolution.path !== path) { return false; } if (evolution.path === EvolutionPathType.standard) { return this.level >= evolution.at_level; } else if (evolution.path === EvolutionPathType.item) { return true; } }); } /** * @param {EvolutionPathType} path */ canEvolve (path) { return this.getPossibleEvolutions(path).length > 0; } /** * @param {DB_Evolution} evolution */ evolve (evolution) { const statsPreEvolve = this.stats; const hpPreEvolve = this.hp; this.slug = evolution.monster_slug; const statsPostEvolve = this.stats; this.hp = statsPostEvolve.hp - (statsPreEvolve.hp - hpPreEvolve); } getTasteStatModifier (statType, baseStat) { let positive = 0; let negative = 0; let isPositive = false; let isNegative = false; if (statType === StatType.melee) { isPositive = this.tasteWarm === TasteWarm.salty; isNegative = this.tasteCold === TasteCold.sweet; } else if (statType === StatType.armour) { isPositive = this.tasteWarm === TasteWarm.hearty; isNegative = this.tasteCold === TasteCold.soft; } else if (statType === StatType.ranged) { isPositive = this.tasteWarm === TasteWarm.zesty; isNegative = this.tasteCold === TasteCold.flakey; } else if (statType === StatType.dodge) { isPositive = this.tasteWarm === TasteWarm.refined; isNegative = this.tasteCold === TasteCold.dry; } else if (statType === StatType.speed) { isPositive = this.tasteWarm === TasteWarm.peppy; isNegative = this.tasteCold === TasteCold.mild; } if (isPositive) { positive = baseStat * 10 / 100; } if (isNegative) { negative = baseStat * 10 / 100; } return Math.floor(positive) - Math.floor(negative); } get stats () { const multiplier = this.level + 7; let hp = (this.shape.hp * multiplier) + this.statModifiers.hp; let melee = (this.shape.melee * multiplier) + this.statModifiers.melee; let armour = (this.shape.armour * multiplier) + this.statModifiers.armour; let ranged = (this.shape.ranged * multiplier) + this.statModifiers.ranged; let dodge = (this.shape.dodge * multiplier) + this.statModifiers.dodge; let speed = (this.shape.speed * multiplier) + this.statModifiers.speed; // Tastes melee += this.getTasteStatModifier(StatType.melee, melee); armour += this.getTasteStatModifier(StatType.armour, armour); ranged += this.getTasteStatModifier(StatType.ranged, ranged); dodge += this.getTasteStatModifier(StatType.dodge, dodge); speed += this.getTasteStatModifier(StatType.speed, speed); return { hp: hp, [StatType.melee]: melee, [StatType.armour]: armour, [StatType.ranged]: ranged, [StatType.dodge]: dodge, [StatType.speed]: speed, }; } setStatModifier (statType, newAbsoluteValue) { this.statModifiers[statType] = newAbsoluteValue - this.stats[statType]; } resetStatModifiers () { for (const m in this.statModifiers) { this.statModifiers[m] = 0; } } };