[Balance] Allow candy gain for uncaught pokemon (#6791)

* allow candy gain for uncaught mons

* carry over friendship

* apply suggestions

Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>

* Add friendship/candy related tests

* Refactor friendship cap tests

* Fix typo

* Apply suggestions from code review

Co-authored-by: Bertie690 <136088738+Bertie690@users.noreply.github.com>

* Fix test

* Update test/field/pokemon.test.ts

Co-authored-by: Bertie690 <136088738+Bertie690@users.noreply.github.com>

* Replace `.startBattle` with `.runToSummon`

---------

Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
Co-authored-by: Bertie690 <136088738+Bertie690@users.noreply.github.com>
This commit is contained in:
Fabi 2025-12-02 13:54:12 +01:00 committed by GitHub
parent b0a6b027fa
commit e1b0e0f0ae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 89 additions and 10 deletions

View File

@ -5956,9 +5956,10 @@ export class PlayerPokemon extends Pokemon {
// Add to candy progress for this mon's starter species and its fused species (if it has one)
starterData.forEach(([sd, id]: [StarterDataEntry, SpeciesId]) => {
sd.friendship = (sd.friendship || 0) + candyFriendshipAmount;
if (sd.friendship >= getStarterValueFriendshipCap(speciesStarterCosts[id])) {
globalScene.gameData.addStarterCandy(getPokemonSpecies(id), 1);
sd.friendship = 0;
const friendshipCap = getStarterValueFriendshipCap(speciesStarterCosts[id]);
if (sd.friendship >= friendshipCap) {
globalScene.gameData.addStarterCandy(getPokemonSpecies(id), Math.floor(sd.friendship / friendshipCap));
sd.friendship %= friendshipCap;
}
});
}

View File

@ -1803,17 +1803,12 @@ export class GameData {
/**
* Adds a candy to the player's game data for a given {@linkcode PokemonSpecies}.
* Will do nothing if the player does not have the Pokemon owned in their system save data.
* @param species
* @param count
*/
addStarterCandy(species: PokemonSpecies, count: number): void {
// Only gain candies if the Pokemon has already been marked as caught in dex (ignore "rental" pokemon)
const speciesRootForm = species.getRootSpeciesId();
if (globalScene.gameData.dexData[speciesRootForm].caughtAttr) {
globalScene.candyBar.showStarterSpeciesCandy(species.speciesId, count);
this.starterData[species.speciesId].candyCount += count;
}
globalScene.candyBar.showStarterSpeciesCandy(species.speciesId, count);
this.starterData[species.speciesId].candyCount += count;
}
/**

View File

@ -1,4 +1,7 @@
import type { BattleScene } from "#app/battle-scene";
import { RARE_CANDY_FRIENDSHIP_CAP } from "#app/constants";
import { globalScene } from "#app/global-scene";
import { getStarterValueFriendshipCap, speciesStarterCosts } from "#balance/starters";
import { CustomPokemonData } from "#data/pokemon-data";
import { MoveId } from "#enums/move-id";
import { PokeballType } from "#enums/pokeball";
@ -226,4 +229,84 @@ describe("Spec - Pokemon", () => {
expect(pokemon.friendship).toBe(friendship);
}
});
describe("Friendship", () => {
it("should cap friendship at 255", async () => {
await game.classicMode.runToSummon([SpeciesId.FEEBAS]);
const feebas = game.field.getPlayerPokemon();
feebas.addFriendship(999);
expect(feebas.friendship).toBe(255);
});
it("should not go below 0 friendship", async () => {
await game.classicMode.runToSummon([SpeciesId.FEEBAS]);
const feebas = game.field.getPlayerPokemon();
feebas.addFriendship(-999);
expect(feebas.friendship).toBe(0);
});
it("should respect Rare Candy friendship gain cap", async () => {
await game.classicMode.runToSummon([SpeciesId.FEEBAS]);
const feebas = game.field.getPlayerPokemon();
feebas.addFriendship(999, true);
expect(feebas.friendship).toBe(RARE_CANDY_FRIENDSHIP_CAP);
});
it("should get 3x candy friendship in classic mode", async () => {
await game.classicMode.runToSummon([SpeciesId.FEEBAS]);
const feebas = game.field.getPlayerPokemon();
const pokemonData = globalScene.gameData.starterData[SpeciesId.FEEBAS];
feebas.friendship = 0;
pokemonData.friendship = 0;
feebas.addFriendship(10);
expect(feebas.friendship).toBe(10);
expect(pokemonData.friendship).toBe(30);
});
it("should carry over excess friendship into next candy, even if capped", async () => {
await game.classicMode.runToSummon([SpeciesId.FEEBAS]);
const feebas = game.field.getPlayerPokemon();
const pokemonData = globalScene.gameData.starterData[SpeciesId.FEEBAS];
feebas.friendship = 0;
pokemonData.friendship = 15;
pokemonData.candyCount = 0;
const cap = getStarterValueFriendshipCap(speciesStarterCosts[SpeciesId.FEEBAS]);
expect(cap).toBeLessThan(2015);
feebas.addFriendship(2000, true);
// Friendship gain was capped, but candy friendship overflowed several times over
expect(feebas.friendship).toBe(RARE_CANDY_FRIENDSHIP_CAP);
expect(pokemonData.friendship).toBe(6015 % cap);
expect(pokemonData.candyCount).toBe(Math.floor(6015 / cap));
});
});
it("should allow gaining candy for uncaught Pokémon", async () => {
await game.classicMode.runToSummon([SpeciesId.FEEBAS]);
const feebas = game.field.getPlayerPokemon();
const pokemonData = globalScene.gameData.starterData[SpeciesId.FEEBAS];
feebas.friendship = 0;
pokemonData.candyCount = 0;
// mark feebas as uncaught
const dexEntry = globalScene.gameData.dexData[SpeciesId.FEEBAS];
dexEntry.caughtAttr = 0n;
feebas.addFriendship(2000);
expect(dexEntry.caughtAttr).toBe(0n);
expect(pokemonData.candyCount).toBeGreaterThan(0);
});
});