From 68eca464f77c7865266995830420688fe916d451 Mon Sep 17 00:00:00 2001 From: Benjamin Odom Date: Sat, 4 May 2024 20:37:31 -0500 Subject: [PATCH] Prevent Fusing Status Errors (#465) --- public/manifest.webmanifest | 2 +- src/data/status-effect.ts | 40 +++++++++++++++++++++++++++++++++++++ src/field/pokemon.ts | 23 ++++++++++++++++++++- src/phases.ts | 19 ++++++++++++++---- src/utils.ts | 10 ++++++++++ 5 files changed, 88 insertions(+), 6 deletions(-) diff --git a/public/manifest.webmanifest b/public/manifest.webmanifest index 4b5f2dbad..cefde7f86 100644 --- a/public/manifest.webmanifest +++ b/public/manifest.webmanifest @@ -3,7 +3,7 @@ "short_name": "PokéRogue", "description": "A Pokémon fangame heavily inspired by the roguelite genre. Battle endlessly while gathering stacking items, exploring many different biomes, and reaching Pokémon stats you never thought possible.", "scope": "/", - "start_url": "https://pokerogue.net", + "start_url": "/", "display": "fullscreen", "background_color": "#8c8c8c", "theme_color": "#8c8c8c", diff --git a/src/data/status-effect.ts b/src/data/status-effect.ts index 4ae095265..c14d49a32 100644 --- a/src/data/status-effect.ts +++ b/src/data/status-effect.ts @@ -134,4 +134,44 @@ export function getStatusEffectCatchRateMultiplier(statusEffect: StatusEffect): } return 1; +} + +/** +* Returns a random non-volatile StatusEffect +*/ +export function generateRandomStatusEffect(): StatusEffect { + return Utils.randIntRange(1, 6); +} + +/** +* Returns a random non-volatile StatusEffect between the two provided +* @param statusEffectA The first StatusEffect +* @param statusEffectA The second StatusEffect +*/ +export function getRandomStatusEffect(statusEffectA: StatusEffect, statusEffectB: StatusEffect): StatusEffect { + if (statusEffectA === StatusEffect.NONE || statusEffectA === StatusEffect.FAINT) { + return statusEffectB; + } + if (statusEffectB === StatusEffect.NONE || statusEffectB === StatusEffect.FAINT) { + return statusEffectA; + } + + return Utils.randIntRange(0, 2) ? statusEffectA : statusEffectB; +} + +/** +* Returns a random non-volatile StatusEffect between the two provided +* @param statusA The first Status +* @param statusB The second Status +*/ +export function getRandomStatus(statusA: Status, statusB: Status): Status { + if (statusA === undefined || statusA.effect === StatusEffect.NONE || statusA.effect === StatusEffect.FAINT) { + return statusB; + } + if (statusB === undefined || statusB.effect === StatusEffect.NONE || statusB.effect === StatusEffect.FAINT) { + return statusA; + } + + + return Utils.randIntRange(0, 2) ? statusA : statusB; } \ No newline at end of file diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index adbe8a8d8..7281090b3 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -14,7 +14,7 @@ import { AttackTypeBoosterModifier, DamageMoneyRewardModifier, EnemyDamageBooste import { PokeballType } from '../data/pokeball'; import { Gender } from '../data/gender'; import { initMoveAnim, loadMoveAnimAssets } from '../data/battle-anims'; -import { Status, StatusEffect } from '../data/status-effect'; +import { Status, StatusEffect, getRandomStatus } from '../data/status-effect'; import { pokemonEvolutions, pokemonPrevolutions, SpeciesFormEvolution, SpeciesEvolutionCondition, FusionSpeciesFormEvolution } from '../data/pokemon-evolutions'; import { reverseCompatibleTms, tmSpecies } from '../data/tms'; import { DamagePhase, FaintPhase, LearnMovePhase, ObtainStatusEffectPhase, StatChangePhase, SwitchSummonPhase } from '../phases'; @@ -2542,6 +2542,10 @@ export class PlayerPokemon extends Pokemon { this.generateCompatibleTms(); } + /** + * Returns a Promise to fuse two PlayerPokemon together + * @param pokemon The PlayerPokemon to fuse to this one + */ fuse(pokemon: PlayerPokemon): Promise { return new Promise(resolve => { this.fusionSpecies = pokemon.species; @@ -2555,8 +2559,25 @@ export class PlayerPokemon extends Pokemon { this.scene.validateAchv(achvs.SPLICE); this.scene.gameData.gameStats.pokemonFused++; + // Store the average HP% that each Pokemon has + const newHpPercent = ((pokemon.hp / pokemon.stats[Stat.HP]) + (this.hp / this.stats[Stat.HP])) / 2; + this.generateName(); this.calculateStats(); + + // Set this Pokemon's HP to the average % of both fusion components + this.hp = Math.round(this.stats[Stat.HP] * newHpPercent); + if (!this.isFainted()) { + // If this Pokemon hasn't fainted, make sure the HP wasn't set over the new maximum + this.hp = Math.min(this.hp, this.stats[Stat.HP]); + this.status = getRandomStatus(this.status, pokemon.status); // Get a random valid status between the two + } + else if (!pokemon.isFainted()) { + // If this Pokemon fainted but the other hasn't, make sure the HP wasn't set to zero + this.hp = Math.max(this.hp, 1); + this.status = pokemon.status; // Inherit the other Pokemon's status + } + this.generateCompatibleTms(); this.updateInfo(true); const fusedPartyMemberIndex = this.scene.getParty().indexOf(pokemon); diff --git a/src/phases.ts b/src/phases.ts index 661bbc2ec..e65e76daa 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -1179,14 +1179,25 @@ export class SummonPhase extends PartyMemberPokemonPhase { this.preSummon(); } + /** + * Sends out a Pokemon before the battle begins and shows the appropriate messages + */ preSummon(): void { const partyMember = this.getPokemon(); + // If the Pokemon about to be sent out is fainted, switch to the first non-fainted Pokemon if (partyMember.isFainted()) { + console.warn("The Pokemon about to be sent out is fainted. Attempting to resolve..."); const party = this.getParty(); - const nonFaintedIndex = party.slice(this.partyMemberIndex).findIndex(p => !p.isFainted()) + this.partyMemberIndex; - const nonFaintedPartyMember = party[nonFaintedIndex]; - party[nonFaintedIndex] = partyMember; - party[this.partyMemberIndex] = nonFaintedPartyMember; + + const nonFaintedIndex = party.findIndex(x => !x.isFainted()); // Find the first non-fainted Pokemon index + if (nonFaintedIndex === -1) { + console.error("Party Details:\n", party); + throw new Error("All available Pokemon were fainted!"); + } + + // Swaps the fainted Pokemon and the first non-fainted Pokemon in the party + [party[this.partyMemberIndex], party[nonFaintedIndex]] = [party[nonFaintedIndex], party[this.partyMemberIndex]]; + console.warn("Swapped %s %O with %s %O", partyMember?.name, partyMember, party[0]?.name, party[0]); } if (this.player) { diff --git a/src/utils.ts b/src/utils.ts index 577e35c74..3ae440a0b 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -62,6 +62,11 @@ export function padInt(value: integer, length: integer, padWith?: string): strin return valueStr; } +/** +* Returns a random integer between min and min + range +* @param range The amount of possible numbers +* @param min The starting number +*/ export function randInt(range: integer, min: integer = 0): integer { if (range === 1) return min; @@ -74,6 +79,11 @@ export function randSeedInt(range: integer, min: integer = 0): integer { return Phaser.Math.RND.integerInRange(min, (range - 1) + min); } +/** +* Returns a random integer between min and max (non-inclusive) +* @param min The lowest number +* @param max The highest number +*/ export function randIntRange(min: integer, max: integer): integer { return randInt(max - min, min); }