[Beta] Stop Transform giving copied moves negative fractional `ppUp` (#4722)

* [Beta] Stop Transform giving copied moves negative fractional `ppUp`

* Remove some bangs/etc

---------

Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
This commit is contained in:
PigeonBar 2024-10-25 19:34:40 -04:00 committed by GitHub
parent 6418f46bf7
commit f87ac116fa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 67 additions and 32 deletions

View File

@ -2453,16 +2453,19 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr {
pokemon.setStatStage(s, target.getStatStage(s)); pokemon.setStatStage(s, target.getStatStage(s));
} }
pokemon.summonData.moveset = target.getMoveset().map(m => { pokemon.summonData.moveset = target.getMoveset().map((m) => {
const pp = m?.getMove().pp ?? 0; if (m) {
// if PP value is less than 5, do nothing. If greater, we need to reduce the value to 5 using a negative ppUp value. // If PP value is less than 5, do nothing. If greater, we need to reduce the value to 5.
const ppUp = pp <= 5 ? 0 : (5 - pp) / Math.max(Math.floor(pp / 5), 1); return new PokemonMove(m.moveId, 0, 0, false, Math.min(m.getMove().pp, 5));
return new PokemonMove(m?.moveId ?? Moves.NONE, 0, ppUp); } else {
console.warn(`Imposter: somehow iterating over a ${m} value when copying moveset!`);
return new PokemonMove(Moves.NONE);
}
}); });
pokemon.summonData.types = target.getTypes(); pokemon.summonData.types = target.getTypes();
promises.push(pokemon.updateInfo()); promises.push(pokemon.updateInfo());
pokemon.scene.queueMessage(i18next.t("abilityTriggers:postSummonTransform", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), targetName: target!.name, })); pokemon.scene.queueMessage(i18next.t("abilityTriggers:postSummonTransform", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), targetName: target.name, }));
pokemon.scene.playSound("battle_anims/PRSFX- Transform"); pokemon.scene.playSound("battle_anims/PRSFX- Transform");
promises.push(pokemon.loadAssets(false).then(() => { promises.push(pokemon.loadAssets(false).then(() => {
pokemon.playAnim(); pokemon.playAnim();

View File

@ -6668,11 +6668,14 @@ export class TransformAttr extends MoveEffectAttr {
user.setStatStage(s, target.getStatStage(s)); user.setStatStage(s, target.getStatStage(s));
} }
user.summonData.moveset = target.getMoveset().map(m => { user.summonData.moveset = target.getMoveset().map((m) => {
const pp = m?.getMove().pp ?? 0; if (m) {
// if PP value is less than 5, do nothing. If greater, we need to reduce the value to 5 using a negative ppUp value. // If PP value is less than 5, do nothing. If greater, we need to reduce the value to 5.
const ppUp = pp <= 5 ? 0 : (5 - pp) / Math.max(Math.floor(pp / 5), 1); return new PokemonMove(m.moveId, 0, 0, false, Math.min(m.getMove().pp, 5));
return new PokemonMove(m?.moveId!, 0, ppUp); } else {
console.warn(`Transform: somehow iterating over a ${m} value when copying moveset!`);
return new PokemonMove(Moves.NONE);
}
}); });
user.summonData.types = target.getTypes(); user.summonData.types = target.getTypes();
promises.push(user.updateInfo()); promises.push(user.updateInfo());

View File

@ -4431,7 +4431,7 @@ export class PlayerPokemon extends Pokemon {
this.scene.removePartyMemberModifiers(fusedPartyMemberIndex); this.scene.removePartyMemberModifiers(fusedPartyMemberIndex);
this.scene.getParty().splice(fusedPartyMemberIndex, 1)[0]; this.scene.getParty().splice(fusedPartyMemberIndex, 1)[0];
const newPartyMemberIndex = this.scene.getParty().indexOf(this); const newPartyMemberIndex = this.scene.getParty().indexOf(this);
pokemon.getMoveset(true).map(m => this.scene.unshiftPhase(new LearnMovePhase(this.scene, newPartyMemberIndex, m!.getMove().id))); // TODO: is the bang correct? pokemon.getMoveset(true).map((m: PokemonMove) => this.scene.unshiftPhase(new LearnMovePhase(this.scene, newPartyMemberIndex, m.getMove().id)));
pokemon.destroy(); pokemon.destroy();
this.updateFusionPalette(); this.updateFusionPalette();
resolve(); resolve();
@ -4452,8 +4452,12 @@ export class PlayerPokemon extends Pokemon {
/** Returns a deep copy of this Pokemon's moveset array */ /** Returns a deep copy of this Pokemon's moveset array */
copyMoveset(): PokemonMove[] { copyMoveset(): PokemonMove[] {
const newMoveset : PokemonMove[] = []; const newMoveset : PokemonMove[] = [];
this.moveset.forEach(move => this.moveset.forEach((move) => {
newMoveset.push(new PokemonMove(move!.moveId, 0, move!.ppUp, move!.virtual))); // TODO: are those bangs correct? // TODO: refactor `moveset` to not accept `null`s
if (move) {
newMoveset.push(new PokemonMove(move.moveId, 0, move.ppUp, move.virtual, move.maxPpOverride));
}
});
return newMoveset; return newMoveset;
} }
@ -5180,15 +5184,22 @@ export interface DamageCalculationResult {
**/ **/
export class PokemonMove { export class PokemonMove {
public moveId: Moves; public moveId: Moves;
public ppUsed: integer; public ppUsed: number;
public ppUp: integer; public ppUp: number;
public virtual: boolean; public virtual: boolean;
constructor(moveId: Moves, ppUsed?: integer, ppUp?: integer, virtual?: boolean) { /**
* If defined and nonzero, overrides the maximum PP of the move (e.g., due to move being copied by Transform).
* This also nullifies all effects of `ppUp`.
*/
public maxPpOverride?: number;
constructor(moveId: Moves, ppUsed: number = 0, ppUp: number = 0, virtual: boolean = false, maxPpOverride?: number) {
this.moveId = moveId; this.moveId = moveId;
this.ppUsed = ppUsed || 0; this.ppUsed = ppUsed;
this.ppUp = ppUp || 0; this.ppUp = ppUp;
this.virtual = !!virtual; this.virtual = virtual;
this.maxPpOverride = maxPpOverride;
} }
/** /**
@ -5225,7 +5236,7 @@ export class PokemonMove {
} }
getMovePp(): integer { getMovePp(): integer {
return this.getMove().pp + this.ppUp * Utils.toDmgValue(this.getMove().pp / 5); return this.maxPpOverride || (this.getMove().pp + this.ppUp * Utils.toDmgValue(this.getMove().pp / 5));
} }
getPpRatio(): number { getPpRatio(): number {
@ -5242,6 +5253,6 @@ export class PokemonMove {
* @return {PokemonMove} A valid pokemonmove object * @return {PokemonMove} A valid pokemonmove object
*/ */
static loadMove(source: PokemonMove | any): PokemonMove { static loadMove(source: PokemonMove | any): PokemonMove {
return new PokemonMove(source.moveId, source.ppUsed, source.ppUp, source.virtual); return new PokemonMove(source.moveId, source.ppUsed, source.ppUp, source.virtual, source.maxPpOverride);
} }
} }

View File

@ -384,7 +384,7 @@ export class PokemonPpUpModifierType extends PokemonMoveModifierType {
(_pokemon: PlayerPokemon) => { (_pokemon: PlayerPokemon) => {
return null; return null;
}, (pokemonMove: PokemonMove) => { }, (pokemonMove: PokemonMove) => {
if (pokemonMove.getMove().pp < 5 || pokemonMove.ppUp >= 3) { if (pokemonMove.getMove().pp < 5 || pokemonMove.ppUp >= 3 || pokemonMove.maxPpOverride) {
return PartyUiHandler.NoEffectMessage; return PartyUiHandler.NoEffectMessage;
} }
return null; return null;

View File

@ -2166,7 +2166,7 @@ export class PokemonPpUpModifier extends ConsumablePokemonMoveModifier {
override apply(playerPokemon: PlayerPokemon): boolean { override apply(playerPokemon: PlayerPokemon): boolean {
const move = playerPokemon.getMoveset()[this.moveIndex]; const move = playerPokemon.getMoveset()[this.moveIndex];
if (move) { if (move && !move.maxPpOverride) {
move.ppUp = Math.min(move.ppUp + this.upPoints, 3); move.ppUp = Math.min(move.ppUp + this.upPoints, 3);
} }

View File

@ -135,7 +135,7 @@ export default class PokemonData {
} }
} }
} else { } else {
this.moveset = (source.moveset || [ new PokemonMove(Moves.TACKLE), new PokemonMove(Moves.GROWL) ]).filter(m => m).map((m: any) => new PokemonMove(m.moveId, m.ppUsed, m.ppUp)); this.moveset = (source.moveset || [ new PokemonMove(Moves.TACKLE), new PokemonMove(Moves.GROWL) ]).filter(m => m).map((m: any) => new PokemonMove(m.moveId, m.ppUsed, m.ppUp, m.virtual, m.maxPpOverride));
if (!forHistory) { if (!forHistory) {
this.status = source.status this.status = source.status
? new Status(source.status.effect, source.status.toxicTurnCount, source.status.sleepTurnsRemaining) ? new Status(source.status.effect, source.status.toxicTurnCount, source.status.sleepTurnsRemaining)

View File

@ -60,18 +60,19 @@ describe("Abilities - Imposter", () => {
const playerMoveset = player.getMoveset(); const playerMoveset = player.getMoveset();
const enemyMoveset = player.getMoveset(); const enemyMoveset = player.getMoveset();
expect(playerMoveset.length).toBe(enemyMoveset.length);
for (let i = 0; i < playerMoveset.length && i < enemyMoveset.length; i++) { for (let i = 0; i < playerMoveset.length && i < enemyMoveset.length; i++) {
// TODO: Checks for 5 PP should be done here when that gets addressed
expect(playerMoveset[i]?.moveId).toBe(enemyMoveset[i]?.moveId); expect(playerMoveset[i]?.moveId).toBe(enemyMoveset[i]?.moveId);
} }
const playerTypes = player.getTypes(); const playerTypes = player.getTypes();
const enemyTypes = enemy.getTypes(); const enemyTypes = enemy.getTypes();
expect(playerTypes.length).toBe(enemyTypes.length);
for (let i = 0; i < playerTypes.length && i < enemyTypes.length; i++) { for (let i = 0; i < playerTypes.length && i < enemyTypes.length; i++) {
expect(playerTypes[i]).toBe(enemyTypes[i]); expect(playerTypes[i]).toBe(enemyTypes[i]);
} }
}, 20000); });
it("should copy in-battle overridden stats", async () => { it("should copy in-battle overridden stats", async () => {
game.override.enemyMoveset([ Moves.POWER_SPLIT ]); game.override.enemyMoveset([ Moves.POWER_SPLIT ]);
@ -104,7 +105,15 @@ describe("Abilities - Imposter", () => {
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to(TurnEndPhase);
player.getMoveset().forEach(move => { player.getMoveset().forEach(move => {
expect(move!.getMovePp()).toBeLessThanOrEqual(5); // Should set correct maximum PP without touching `ppUp`
if (move) {
if (move.moveId === Moves.SKETCH) {
expect(move.getMovePp()).toBe(1);
} else {
expect(move.getMovePp()).toBe(5);
}
expect(move.ppUp).toBe(0);
}
}); });
}); });
}); });

View File

@ -60,18 +60,19 @@ describe("Moves - Transform", () => {
const playerMoveset = player.getMoveset(); const playerMoveset = player.getMoveset();
const enemyMoveset = player.getMoveset(); const enemyMoveset = player.getMoveset();
expect(playerMoveset.length).toBe(enemyMoveset.length);
for (let i = 0; i < playerMoveset.length && i < enemyMoveset.length; i++) { for (let i = 0; i < playerMoveset.length && i < enemyMoveset.length; i++) {
// TODO: Checks for 5 PP should be done here when that gets addressed
expect(playerMoveset[i]?.moveId).toBe(enemyMoveset[i]?.moveId); expect(playerMoveset[i]?.moveId).toBe(enemyMoveset[i]?.moveId);
} }
const playerTypes = player.getTypes(); const playerTypes = player.getTypes();
const enemyTypes = enemy.getTypes(); const enemyTypes = enemy.getTypes();
expect(playerTypes.length).toBe(enemyTypes.length);
for (let i = 0; i < playerTypes.length && i < enemyTypes.length; i++) { for (let i = 0; i < playerTypes.length && i < enemyTypes.length; i++) {
expect(playerTypes[i]).toBe(enemyTypes[i]); expect(playerTypes[i]).toBe(enemyTypes[i]);
} }
}, 20000); });
it("should copy in-battle overridden stats", async () => { it("should copy in-battle overridden stats", async () => {
game.override.enemyMoveset([ Moves.POWER_SPLIT ]); game.override.enemyMoveset([ Moves.POWER_SPLIT ]);
@ -104,7 +105,15 @@ describe("Moves - Transform", () => {
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to(TurnEndPhase);
player.getMoveset().forEach(move => { player.getMoveset().forEach(move => {
expect(move!.getMovePp()).toBeLessThanOrEqual(5); // Should set correct maximum PP without touching `ppUp`
if (move) {
if (move.moveId === Moves.SKETCH) {
expect(move.getMovePp()).toBe(1);
} else {
expect(move.getMovePp()).toBe(5);
}
expect(move.ppUp).toBe(0);
}
}); });
}); });
}); });