unburden implemented

This commit is contained in:
muscode13 2024-10-01 11:40:48 -06:00
parent c9664b66d3
commit 0d894e9009
5 changed files with 232 additions and 1 deletions

View File

@ -1692,6 +1692,7 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr {
const stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)]; const stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)];
pokemon.scene.tryTransferHeldItemModifier(stolenItem, pokemon, false).then(success => { pokemon.scene.tryTransferHeldItemModifier(stolenItem, pokemon, false).then(success => {
if (success) { if (success) {
defender.turnData.itemsLost += 1;
pokemon.scene.queueMessage(i18next.t("abilityTriggers:postAttackStealHeldItem", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), defenderName: defender.name, stolenItemType: stolenItem.type.name })); pokemon.scene.queueMessage(i18next.t("abilityTriggers:postAttackStealHeldItem", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), defenderName: defender.name, stolenItemType: stolenItem.type.name }));
} }
resolve(success); resolve(success);
@ -1785,6 +1786,7 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr {
const stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)]; const stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)];
pokemon.scene.tryTransferHeldItemModifier(stolenItem, pokemon, false).then(success => { pokemon.scene.tryTransferHeldItemModifier(stolenItem, pokemon, false).then(success => {
if (success) { if (success) {
attacker.turnData.itemsLost += 1;
pokemon.scene.queueMessage(i18next.t("abilityTriggers:postDefendStealHeldItem", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), attackerName: attacker.name, stolenItemType: stolenItem.type.name })); pokemon.scene.queueMessage(i18next.t("abilityTriggers:postDefendStealHeldItem", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), attackerName: attacker.name, stolenItemType: stolenItem.type.name }));
} }
resolve(success); resolve(success);
@ -3835,6 +3837,92 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr {
} }
} }
export class UnburdenBerryAbAttr extends PostTurnAbAttr {
private stats: BattleStat[];
private stages: number;
constructor(stats: BattleStat[], stages: number) {
super(true);
this.stats = Array.isArray(stats)
? stats
: [ stats ];
this.stages = stages;
}
applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean {
const multipleItems = pokemon.battleData.berriesEaten.length * this.stages;
if (multipleItems > 6) {
this.stages = 6;
} else {
this.stages = multipleItems;
}
if (!simulated) {
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, this.stats, this.stages));
}
return true;
}
getCondition(): AbAttrCondition {
return (pokemon: Pokemon) => pokemon.battleData.berriesEaten.length !== 0;
}
}
export class UnburdenDefStolenAbAttr extends PostDefendAbAttr {
private stats: BattleStat[];
private stages: number;
constructor(stats: BattleStat[], stages: number) {
super(true);
this.stats = Array.isArray(stats)
? stats
: [ stats ];
this.stages = stages;
}
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (!simulated) {
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, this.stats, this.stages));
pokemon.turnData.itemsLost -= 1;
}
return true;
}
getCondition(): AbAttrCondition {
return (pokemon: Pokemon) => pokemon.turnData.itemsLost > 0;
}
}
export class UnburdenAtkStolenAbAttr extends PostAttackAbAttr {
private stats: BattleStat[];
private stages: number;
constructor(stats: BattleStat[], stages: number) {
super();
this.stats = Array.isArray(stats)
? stats
: [ stats ];
this.stages = stages;
}
applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (!simulated) {
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, this.stats, this.stages));
pokemon.turnData.itemsLost -= 1;
}
return true;
}
getCondition(): AbAttrCondition {
return (pokemon: Pokemon) => pokemon.turnData.itemsLost > 0;
}
}
export class StatStageChangeMultiplierAbAttr extends AbAttr { export class StatStageChangeMultiplierAbAttr extends AbAttr {
private multiplier: integer; private multiplier: integer;
@ -5146,7 +5234,9 @@ export function initAbilities() {
new Ability(Abilities.ANGER_POINT, 4) new Ability(Abilities.ANGER_POINT, 4)
.attr(PostDefendCritStatStageChangeAbAttr, Stat.ATK, 6), .attr(PostDefendCritStatStageChangeAbAttr, Stat.ATK, 6),
new Ability(Abilities.UNBURDEN, 4) new Ability(Abilities.UNBURDEN, 4)
.unimplemented(), .attr(UnburdenBerryAbAttr, [ Stat.SPD ], 2)
.attr(UnburdenAtkStolenAbAttr, [ Stat.SPD ], 2)
.attr(UnburdenDefStolenAbAttr, [ Stat.SPD ], 2),
new Ability(Abilities.HEATPROOF, 4) new Ability(Abilities.HEATPROOF, 4)
.attr(ReceivedTypeDamageMultiplierAbAttr, Type.FIRE, 0.5) .attr(ReceivedTypeDamageMultiplierAbAttr, Type.FIRE, 0.5)
.attr(ReduceBurnDamageAbAttr, 0.5) .attr(ReduceBurnDamageAbAttr, 0.5)

View File

@ -2146,6 +2146,7 @@ export class StealHeldItemChanceAttr extends MoveEffectAttr {
const stolenItem = tierHeldItems[user.randSeedInt(tierHeldItems.length)]; const stolenItem = tierHeldItems[user.randSeedInt(tierHeldItems.length)];
user.scene.tryTransferHeldItemModifier(stolenItem, user, false).then(success => { user.scene.tryTransferHeldItemModifier(stolenItem, user, false).then(success => {
if (success) { if (success) {
target.turnData.itemsLost += 1;
user.scene.queueMessage(i18next.t("moveTriggers:stoleItem", {pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), itemName: stolenItem.type.name})); user.scene.queueMessage(i18next.t("moveTriggers:stoleItem", {pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), itemName: stolenItem.type.name}));
} }
resolve(success); resolve(success);
@ -2227,6 +2228,7 @@ export class RemoveHeldItemAttr extends MoveEffectAttr {
// Decrease item amount and update icon // Decrease item amount and update icon
!--removedItem.stackCount; !--removedItem.stackCount;
target.scene.updateModifiers(target.isPlayer()); target.scene.updateModifiers(target.isPlayer());
target.turnData.itemsLost+=1;
if (this.berriesOnly) { if (this.berriesOnly) {
user.scene.queueMessage(i18next.t("moveTriggers:incineratedItem", {pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), itemName: removedItem.type.name})); user.scene.queueMessage(i18next.t("moveTriggers:incineratedItem", {pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), itemName: removedItem.type.name}));
@ -2341,6 +2343,7 @@ export class StealEatBerryAttr extends EatBerryAttr {
} }
// if the target has berries, pick a random berry and steal it // if the target has berries, pick a random berry and steal it
this.chosenBerry = heldBerries[user.randSeedInt(heldBerries.length)]; this.chosenBerry = heldBerries[user.randSeedInt(heldBerries.length)];
target.turnData.itemsLost+=1;
const message = i18next.t("battle:stealEatBerry", {pokemonName: user.name, targetName: target.name, berryName: this.chosenBerry.type.name}); const message = i18next.t("battle:stealEatBerry", {pokemonName: user.name, targetName: target.name, berryName: this.chosenBerry.type.name});
user.scene.queueMessage(message); user.scene.queueMessage(message);
this.reduceBerryModifier(target); this.reduceBerryModifier(target);

View File

@ -5011,6 +5011,7 @@ export class PokemonTurnData {
public order: number; public order: number;
public statStagesIncreased: boolean = false; public statStagesIncreased: boolean = false;
public statStagesDecreased: boolean = false; public statStagesDecreased: boolean = false;
public itemsLost: number = 0;
} }
export enum AiType { export enum AiType {

View File

@ -2719,6 +2719,7 @@ export abstract class HeldItemTransferModifier extends PokemonHeldItemModifier {
const randItem = itemModifiers[randItemIndex]; const randItem = itemModifiers[randItemIndex];
heldItemTransferPromises.push(pokemon.scene.tryTransferHeldItemModifier(randItem, pokemon, false).then(success => { heldItemTransferPromises.push(pokemon.scene.tryTransferHeldItemModifier(randItem, pokemon, false).then(success => {
if (success) { if (success) {
targetPokemon.turnData.itemsLost += 1;
transferredModifierTypes.push(randItem.type); transferredModifierTypes.push(randItem.type);
itemModifiers.splice(randItemIndex, 1); itemModifiers.splice(randItemIndex, 1);
} }

View File

@ -0,0 +1,136 @@
import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import GameManager from "#test/utils/gameManager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { Stat } from "#enums/stat";
import { BerryType } from "#app/enums/berry-type";
import { allMoves, StealHeldItemChanceAttr } from "#app/data/move";
describe("Abilities - Unburden", () => {
let phaserGame: Phaser.Game;
let game: GameManager;
beforeAll(() => {
phaserGame = new Phaser.Game({
type: Phaser.HEADLESS,
});
});
afterEach(() => {
game.phaseInterceptor.restoreOg();
});
beforeEach(() => {
game = new GameManager(phaserGame);
game.override
.battleType("single")
.starterSpecies(Species.TREECKO)
.startingLevel(1)
.moveset([Moves.POPULATION_BOMB, Moves.KNOCK_OFF, Moves.PLUCK, Moves.THIEF])
.ability(Abilities.UNBURDEN)
.startingHeldItems([
{ name: "BERRY", count: 1, type: BerryType.SITRUS },
{ name: "BERRY", count: 2, type: BerryType.APICOT },
{ name: "BERRY", count: 2, type: BerryType.LUM },
])
.enemySpecies(Species.NINJASK)
.enemyLevel(100)
.enemyMoveset([Moves.FALSE_SWIPE])
.enemyAbility(Abilities.UNBURDEN)
.enemyHeldItems([
{ name: "BERRY", type: BerryType.SITRUS, count: 1 },
{ name: "BERRY", type: BerryType.LUM, count: 1 },
]);
});
it("should activate when a berry is eaten", async () => {
await game.classicMode.startBattle();
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerHeldItems = playerPokemon.getHeldItems().length;
game.move.select(Moves.FALSE_SWIPE);
await game.toNextTurn();
expect(playerPokemon.getHeldItems().length).toBeLessThan(playerHeldItems);
expect(playerPokemon.getStatStage(Stat.SPD)).toBe(4);
});
it("should activate when a berry is stolen", async () => {
await game.classicMode.startBattle();
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyHeldItemCt = enemyPokemon.getHeldItems().length;
game.move.select(Moves.PLUCK);
await game.toNextTurn();
expect(enemyPokemon.getHeldItems().length).toBeLessThan(enemyHeldItemCt);
expect(enemyPokemon.getStatStage(Stat.SPD)).toBe(2);
});
it("should activate when an item is knocked off", async () => {
await game.classicMode.startBattle();
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyHeldItemCt = enemyPokemon.getHeldItems().length;
game.move.select(Moves.KNOCK_OFF);
await game.toNextTurn();
expect(enemyPokemon.getHeldItems().length).toBeLessThan(enemyHeldItemCt);
expect(enemyPokemon.getStatStage(Stat.SPD)).toBe(2);
});
it("should activate when an item is stolen via attacking ability", async () => {
game.override
.ability(Abilities.MAGICIAN)
.startingHeldItems([
{ name: "MULTI_LENS", count: 3 },
]);
await game.classicMode.startBattle();
vi.spyOn(allMoves[Moves.POPULATION_BOMB], "accuracy", "get").mockReturnValue(100);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyHeldItemCt = enemyPokemon.getHeldItems().length;
game.move.select(Moves.POPULATION_BOMB);
await game.toNextTurn();
expect(enemyPokemon.getHeldItems().length).toBeLessThan(enemyHeldItemCt);
expect(enemyPokemon.getStatStage(Stat.SPD)).toBe(4);
});
it("should activate when an item is stolen via defending ability", async () => {
game.override
.enemyAbility(Abilities.PICKPOCKET)
.startingHeldItems([
{ name: "MULTI_LENS", count: 3 },
{ name: "SOUL_DEW", count: 1},
{ name: "LUCKY_EGG", count: 1 },
]);
await game.classicMode.startBattle();
vi.spyOn(allMoves[Moves.POPULATION_BOMB], "accuracy", "get").mockReturnValue(100);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerHeldItems = playerPokemon.getHeldItems().length;
game.move.select(Moves.POPULATION_BOMB);
await game.toNextTurn();
expect(playerPokemon.getHeldItems().length).toBeLessThan(playerHeldItems);
expect(playerPokemon.getStatStage(Stat.SPD)).toBe(6);
});
it("should activate when an item is stolen via move", async () => {
vi.spyOn(allMoves[Moves.THIEF], "attrs", "get").mockReturnValue([new StealHeldItemChanceAttr(1.0)]); // give Thief 100% steal rate
game.override.startingHeldItems([
{ name: "MULTI_LENS", count: 3 },
]);
await game.classicMode.startBattle();
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyHeldItemCt = enemyPokemon.getHeldItems().length;
game.move.select(Moves.THIEF);
await game.toNextTurn();
expect(enemyPokemon.getHeldItems().length).toBeLessThan(enemyHeldItemCt);
expect(enemyPokemon.getStatStage(Stat.SPD)).toBe(4);
});
it("should activate when an item is stolen via grip claw", async () => {
game.override.startingHeldItems([
{ name: "GRIP_CLAW", count: 5 },
{ name: "MULTI_LENS", count: 3 },
]);
await game.classicMode.startBattle();
vi.spyOn(allMoves[Moves.POPULATION_BOMB], "accuracy", "get").mockReturnValue(100);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyHeldItemCt = enemyPokemon.getHeldItems().length;
game.move.select(Moves.POPULATION_BOMB);
await game.toNextTurn();
expect(enemyPokemon.getHeldItems().length).toBeLessThan(enemyHeldItemCt);
expect(enemyPokemon.getStatStage(Stat.SPD)).toBe(4);
});
});