Merge pull request #6420 from pagefaultgames/hotfix-1.10.4

Hotfix 1.10.4 to main
This commit is contained in:
NightKev 2025-08-25 23:58:57 -07:00 committed by GitHub
commit cc7391448a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 101 additions and 14 deletions

View File

@ -1,7 +1,7 @@
{
"name": "pokemon-rogue-battle",
"private": true,
"version": "1.10.3",
"version": "1.10.4",
"type": "module",
"scripts": {
"start": "vite",

View File

@ -1,3 +1,7 @@
self.addEventListener('install', function () {
console.log('Service worker installing...');
});
self.addEventListener('activate', (event) => {
event.waitUntil(self.clients.claim());
})

View File

@ -77,7 +77,8 @@ export enum EvolutionItem {
LEADERS_CREST
}
type TyrogueMove = MoveId.LOW_SWEEP | MoveId.MACH_PUNCH | MoveId.RAPID_SPIN;
const tyrogueMoves = [MoveId.LOW_SWEEP, MoveId.MACH_PUNCH, MoveId.RAPID_SPIN] as const;
type TyrogueMove = typeof tyrogueMoves[number];
/**
* Pokemon Evolution tuple type consisting of:
@ -192,7 +193,7 @@ export class SpeciesEvolutionCondition {
case EvoCondKey.WEATHER:
return cond.weather.includes(globalScene.arena.getWeatherType());
case EvoCondKey.TYROGUE:
return pokemon.getMoveset(true).find(m => m.moveId as TyrogueMove)?.moveId === cond.move;
return pokemon.getMoveset(true).find(m => (tyrogueMoves as readonly MoveId[]) .includes(m.moveId))?.moveId === cond.move;
case EvoCondKey.NATURE:
return cond.nature.includes(pokemon.getNature());
case EvoCondKey.RANDOM_FORM: {

View File

@ -2,6 +2,7 @@ import { globalScene } from "#app/global-scene";
import { allSpecies, modifierTypes } from "#data/data-lists";
import { getLevelTotalExp } from "#data/exp";
import type { PokemonSpecies } from "#data/pokemon-species";
import { AbilityId } from "#enums/ability-id";
import { Challenges } from "#enums/challenges";
import { ModifierTier } from "#enums/modifier-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
@ -10,8 +11,9 @@ import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { Nature } from "#enums/nature";
import { PartyMemberStrength } from "#enums/party-member-strength";
import { PlayerGender } from "#enums/player-gender";
import { PokemonType } from "#enums/pokemon-type";
import { MAX_POKEMON_TYPE, PokemonType } from "#enums/pokemon-type";
import { SpeciesId } from "#enums/species-id";
import { StatusEffect } from "#enums/status-effect";
import { TrainerType } from "#enums/trainer-type";
import type { PlayerPokemon, Pokemon } from "#field/pokemon";
import type { PokemonHeldItemModifier } from "#modifiers/modifier";
@ -219,6 +221,7 @@ export const WeirdDreamEncounter: MysteryEncounter = MysteryEncounterBuilder.wit
await showEncounterText(`${namespace}:option.1.dreamComplete`);
await doNewTeamPostProcess(transformations);
globalScene.phaseManager.unshiftNew("PartyHealPhase", true);
setEncounterRewards({
guaranteedModifierTypeFuncs: [
modifierTypes.MEMORY_MUSHROOM,
@ -230,7 +233,7 @@ export const WeirdDreamEncounter: MysteryEncounter = MysteryEncounterBuilder.wit
],
fillRemaining: false,
});
leaveEncounterWithoutBattle(true);
leaveEncounterWithoutBattle(false);
})
.build(),
)
@ -431,6 +434,8 @@ function getTeamTransformations(): PokemonTransformation[] {
newAbilityIndex,
undefined,
);
transformation.newPokemon.teraType = randSeedInt(MAX_POKEMON_TYPE);
}
return pokemonTransformations;
@ -440,6 +445,8 @@ async function doNewTeamPostProcess(transformations: PokemonTransformation[]) {
let atLeastOneNewStarter = false;
for (const transformation of transformations) {
const previousPokemon = transformation.previousPokemon;
const oldHpRatio = previousPokemon.getHpRatio(true);
const oldStatus = previousPokemon.status;
const newPokemon = transformation.newPokemon;
const speciesRootForm = newPokemon.species.getRootSpeciesId();
@ -462,6 +469,19 @@ async function doNewTeamPostProcess(transformations: PokemonTransformation[]) {
}
newPokemon.calculateStats();
if (oldHpRatio > 0) {
newPokemon.hp = Math.ceil(oldHpRatio * newPokemon.getMaxHp());
// Assume that the `status` instance can always safely be transferred to the new pokemon
// This is the case (as of version 1.10.4)
// Safeguard against COMATOSE here
if (!newPokemon.hasAbility(AbilityId.COMATOSE, false, true)) {
newPokemon.status = oldStatus;
}
} else {
newPokemon.hp = 0;
newPokemon.doSetStatus(StatusEffect.FAINT);
}
await newPokemon.updateInfo();
}

View File

@ -20,3 +20,6 @@ export enum PokemonType {
FAIRY,
STELLAR
}
/** The largest legal value for a {@linkcode PokemonType} (includes Stellar) */
export const MAX_POKEMON_TYPE = PokemonType.STELLAR;

View File

@ -3243,6 +3243,18 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
rand -= stabMovePool[index++][1];
}
this.moveset.push(new PokemonMove(stabMovePool[index][0]));
} else {
// If there are no damaging STAB moves, just force a random damaging move
const attackMovePool = baseWeights.filter(m => allMoves[m[0]].category !== MoveCategory.STATUS);
if (attackMovePool.length) {
const totalWeight = attackMovePool.reduce((v, m) => v + m[1], 0);
let rand = randSeedInt(totalWeight);
let index = 0;
while (rand > attackMovePool[index][1]) {
rand -= attackMovePool[index++][1];
}
this.moveset.push(new PokemonMove(attackMovePool[index][0], 0, 0));
}
}
while (baseWeights.length > this.moveset.length && this.moveset.length < 4) {

View File

@ -179,12 +179,11 @@ export class TurnStartPhase extends FieldPhase {
// https://www.smogon.com/forums/threads/sword-shield-battle-mechanics-research.3655528/page-64#post-9244179
phaseManager.pushNew("WeatherEffectPhase");
phaseManager.pushNew("PositionalTagPhase");
phaseManager.pushNew("BerryPhase");
/** Add a new phase to check who should be taking status damage */
phaseManager.pushNew("CheckStatusEffectPhase", moveOrder);
phaseManager.pushNew("PositionalTagPhase");
phaseManager.pushNew("TurnEndPhase");
/*

View File

@ -1828,9 +1828,9 @@ export class StarterSelectUiHandler extends MessageUiHandler {
// The persistent starter data to apply e.g. candy upgrades
const persistentStarterData = globalScene.gameData.starterData[this.lastSpecies.speciesId];
// The sanitized starter preferences
let starterAttributes = this.starterPreferences[this.lastSpecies.speciesId];
let starterAttributes = this.starterPreferences[this.lastSpecies.speciesId] ?? {};
// The original starter preferences
const originalStarterAttributes = this.originalStarterPreferences[this.lastSpecies.speciesId];
const originalStarterAttributes = this.originalStarterPreferences[this.lastSpecies.speciesId] ?? {};
// this gets the correct pokemon cursor depending on whether you're in the starter screen or the party icons
if (!this.starterIconsCursorObj.visible) {
@ -3408,8 +3408,9 @@ export class StarterSelectUiHandler extends MessageUiHandler {
if (species) {
const defaultDexAttr = this.getCurrentDexProps(species.speciesId);
const defaultProps = globalScene.gameData.getSpeciesDexAttrProps(species, defaultDexAttr);
// Bang is correct due to the `?` before variant
const variant = this.starterPreferences[species.speciesId]?.variant
? (this.starterPreferences[species.speciesId].variant as Variant)
? (this.starterPreferences[species.speciesId]!.variant as Variant)
: defaultProps.variant;
const tint = getVariantTint(variant);
this.pokemonShinyIcon.setFrame(getVariantIcon(variant)).setTint(tint);

View File

@ -64,7 +64,7 @@ const StarterPrefers_DEFAULT: string = "{}";
let StarterPrefers_private_latest: string = StarterPrefers_DEFAULT;
export interface StarterPreferences {
[key: number]: StarterAttributes;
[key: number]: StarterAttributes | undefined;
}
// called on starter selection show once
@ -74,10 +74,27 @@ export function loadStarterPreferences(): StarterPreferences {
localStorage.getItem(`starterPrefs_${loggedInUser?.username}`) || StarterPrefers_DEFAULT),
);
}
// called on starter selection clear, always
/**
* Check if an object has no properties of its own (its shape is `{}`)
* @param obj - Object to check
* @returns - Whether the object is bare
*/
export function isBareObject(obj: object): boolean {
for (const _ in obj) {
return false;
}
return true;
}
export function saveStarterPreferences(prefs: StarterPreferences): void {
const pStr: string = JSON.stringify(prefs);
// Fastest way to check if an object has any properties (does no allocation)
if (isBareObject(prefs)) {
console.warn("Refusing to save empty starter preferences");
return;
}
// no reason to store `{}` (for starters not customized)
const pStr: string = JSON.stringify(prefs, (_, value) => (isBareObject(value) ? undefined : value));
if (pStr !== StarterPrefers_private_latest) {
// something changed, store the update
localStorage.setItem(`starterPrefs_${loggedInUser?.username}`, pStr);

View File

@ -175,4 +175,27 @@ describe("Evolution", () => {
expect(fourForm.evoFormKey).toBe("four"); // meanwhile, according to the pokemon-forms, the evoFormKey for a 4 family maushold is "four"
}
});
it("tyrogue should evolve if move is not in first slot", async () => {
game.override
.moveset([MoveId.TACKLE, MoveId.RAPID_SPIN, MoveId.LOW_KICK])
.enemySpecies(SpeciesId.GOLEM)
.enemyMoveset(MoveId.SPLASH)
.startingWave(41)
.startingLevel(19)
.enemyLevel(30);
await game.classicMode.startBattle([SpeciesId.TYROGUE]);
const tyrogue = game.field.getPlayerPokemon();
const golem = game.field.getEnemyPokemon();
golem.hp = 1;
expect(golem.hp).toBe(1);
game.move.select(MoveId.TACKLE);
await game.phaseInterceptor.to("EndEvolutionPhase");
expect(tyrogue.species.speciesId).toBe(SpeciesId.HITMONTOP);
});
});

View File

@ -59,5 +59,12 @@
"outDir": "./build",
"noEmit": true
},
"exclude": ["node_modules", "dist", "vite.config.ts", "vitest.config.ts", "vitest.workspace.ts"]
"exclude": [
"node_modules",
"dist",
"vite.config.ts",
"vitest.config.ts",
"vitest.workspace.ts",
"public/service-worker.js"
]
}