mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-12-14 22:05:34 +01:00
[Balance] The cost of buying same-species eggs can be reduced (#6837)
* [Balance] The cost of buying same-species eggs can be reduced After hatching a certain number of eggs for a starter, the cost of buying same-species eggs for that starter will be reduced (up to 50%) * Add test to validate array lengths for egg costs
This commit is contained in:
parent
46df6adab3
commit
3f5c37c881
@ -7,10 +7,11 @@ export const CLASSIC_CANDY_FRIENDSHIP_MULTIPLIER = 3;
|
|||||||
export const FRIENDSHIP_GAIN_FROM_BATTLE = 3;
|
export const FRIENDSHIP_GAIN_FROM_BATTLE = 3;
|
||||||
export const FRIENDSHIP_GAIN_FROM_RARE_CANDY = 6;
|
export const FRIENDSHIP_GAIN_FROM_RARE_CANDY = 6;
|
||||||
export const FRIENDSHIP_LOSS_FROM_FAINT = 5;
|
export const FRIENDSHIP_LOSS_FROM_FAINT = 5;
|
||||||
|
// #endregion
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to get the cumulative friendship threshold at which a candy is earned
|
* Function to get the cumulative friendship threshold at which a candy is earned
|
||||||
* @param starterCost The cost of the starter, found in {@linkcode speciesStarterCosts}
|
* @param starterCost - The cost of the starter, found in {@linkcode speciesStarterCosts}
|
||||||
* @returns aforementioned threshold
|
* @returns aforementioned threshold
|
||||||
*/
|
*/
|
||||||
export function getStarterValueFriendshipCap(starterCost: number): number {
|
export function getStarterValueFriendshipCap(starterCost: number): number {
|
||||||
@ -618,43 +619,72 @@ export const speciesStarterCosts = {
|
|||||||
[SpeciesId.BLOODMOON_URSALUNA]: 5,
|
[SpeciesId.BLOODMOON_URSALUNA]: 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
const starterCandyCosts: { passive: number; costReduction: [number, number]; egg: number; }[] = [
|
interface StarterCandyCosts {
|
||||||
{ passive: 40, costReduction: [ 25, 60 ], egg: 30 }, // 1 Cost
|
/** The candy cost to unlock the starter's passive ability */
|
||||||
{ passive: 40, costReduction: [ 25, 60 ], egg: 30 }, // 2 Cost
|
readonly passive: number;
|
||||||
{ passive: 35, costReduction: [ 20, 50 ], egg: 25 }, // 3 Cost
|
/** The candy costs to reduce the starter's point cost */
|
||||||
{ passive: 30, costReduction: [ 15, 40 ], egg: 20 }, // 4 Cost
|
readonly costReduction: readonly [number, number];
|
||||||
{ passive: 25, costReduction: [ 12, 35 ], egg: 18 }, // 5 Cost
|
/** The costs to buy a same-species egg */
|
||||||
{ passive: 20, costReduction: [ 10, 30 ], egg: 15 }, // 6 Cost
|
readonly eggCosts: readonly [number, ...number[]];
|
||||||
{ passive: 15, costReduction: [ 8, 20 ], egg: 12 }, // 7 Cost
|
/** The number of eggs required to hatch to reduce the cost for buying more eggs */
|
||||||
{ passive: 10, costReduction: [ 5, 15 ], egg: 10 }, // 8 Cost
|
readonly eggCostReductionThresholds: readonly number[];
|
||||||
{ passive: 10, costReduction: [ 5, 15 ], egg: 10 }, // 9 Cost
|
}
|
||||||
{ passive: 10, costReduction: [ 5, 15 ], egg: 10 }, // 10 Cost
|
|
||||||
|
const allStarterCandyCosts: readonly StarterCandyCosts[] = [
|
||||||
|
{ passive: 40, costReduction: [25, 60], eggCosts: [30, 27, 22, 15], eggCostReductionThresholds: [20, 40, 80] }, // 1 Cost
|
||||||
|
{ passive: 40, costReduction: [25, 60], eggCosts: [30, 27, 22, 15], eggCostReductionThresholds: [20, 40, 80] }, // 2 Cost
|
||||||
|
{ passive: 35, costReduction: [20, 50], eggCosts: [25, 22, 18, 12], eggCostReductionThresholds: [20, 40, 80] }, // 3 Cost
|
||||||
|
{ passive: 30, costReduction: [15, 40], eggCosts: [20, 18, 15, 10], eggCostReductionThresholds: [15, 30, 60] }, // 4 Cost
|
||||||
|
{ passive: 25, costReduction: [12, 35], eggCosts: [18, 16, 13, 9], eggCostReductionThresholds: [15, 30, 60] }, // 5 Cost
|
||||||
|
{ passive: 20, costReduction: [10, 30], eggCosts: [15, 13, 11, 7], eggCostReductionThresholds: [15, 30, 60] }, // 6 Cost
|
||||||
|
{ passive: 15, costReduction: [8, 20], eggCosts: [12, 10, 9, 6], eggCostReductionThresholds: [10, 20, 40] }, // 7 Cost
|
||||||
|
{ passive: 10, costReduction: [5, 15], eggCosts: [10, 9, 7, 5], eggCostReductionThresholds: [10, 20, 40] }, // 8 Cost
|
||||||
|
{ passive: 10, costReduction: [5, 15], eggCosts: [10, 9, 7, 5], eggCostReductionThresholds: [10, 20, 40] }, // 9 Cost
|
||||||
|
{ passive: 10, costReduction: [5, 15], eggCosts: [10, 9, 7, 5], eggCostReductionThresholds: [8, 16, 32] }, // 10 Cost
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Getter for {@linkcode starterCandyCosts} for passive unlock candy cost based on initial point cost
|
* Getter for {@linkcode allStarterCandyCosts} for passive unlock candy cost based on initial point cost
|
||||||
* @param starterCost the default point cost of the starter found in {@linkcode speciesStarterCosts}
|
* @param starterCost - The default point cost of the starter found in {@linkcode speciesStarterCosts}
|
||||||
* @returns the candy cost for passive unlock
|
* @returns the candy cost for passive unlock
|
||||||
*/
|
*/
|
||||||
export function getPassiveCandyCount(starterCost: number): number {
|
export function getPassiveCandyCount(starterCost: number): number {
|
||||||
return starterCandyCosts[starterCost - 1].passive;
|
return allStarterCandyCosts[starterCost - 1].passive;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Getter for {@linkcode starterCandyCosts} for value reduction unlock candy cost based on initial point cost
|
* Getter for {@linkcode allStarterCandyCosts} for value reduction unlock candy cost based on initial point cost
|
||||||
* @param starterCost the default point cost of the starter found in {@linkcode speciesStarterCosts}
|
* @param starterCost - The default point cost of the starter found in {@linkcode speciesStarterCosts}
|
||||||
* @returns respective candy cost for the two cost reductions as an array 2 numbers
|
* @returns respective candy cost for the two cost reductions as an array 2 numbers
|
||||||
*/
|
*/
|
||||||
export function getValueReductionCandyCounts(starterCost: number): [number, number] {
|
export function getValueReductionCandyCounts(starterCost: number): readonly [number, number] {
|
||||||
return starterCandyCosts[starterCost - 1].costReduction;
|
return allStarterCandyCosts[starterCost - 1].costReduction;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Getter for {@linkcode starterCandyCosts} for egg purchase candy cost based on initial point cost
|
* Getter for {@linkcode allStarterCandyCosts} for egg purchase candy cost based on initial point cost
|
||||||
* @param starterCost the default point cost of the starter found in {@linkcode speciesStarterCosts}
|
* @param starterCost - The default point cost of the starter found in {@linkcode speciesStarterCosts}
|
||||||
|
* @param hatchCount - The number of eggs hatched of the starter
|
||||||
* @returns the candy cost for the purchasable egg
|
* @returns the candy cost for the purchasable egg
|
||||||
*/
|
*/
|
||||||
export function getSameSpeciesEggCandyCounts(starterCost: number): number {
|
export function getSameSpeciesEggCandyCounts(starterCost: number, hatchCount: number): number {
|
||||||
return starterCandyCosts[starterCost - 1].egg;
|
const starterCandyCosts = allStarterCandyCosts[starterCost - 1];
|
||||||
|
let eggCostIndex = 0;
|
||||||
|
while (hatchCount >= starterCandyCosts.eggCostReductionThresholds[eggCostIndex]) {
|
||||||
|
eggCostIndex++;
|
||||||
|
}
|
||||||
|
return starterCandyCosts.eggCosts[eggCostIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ⚠️ This is used for internal testing purposes only and will not be populated outside of the test environment.
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export const __TEST_allStarterCandyCosts: readonly StarterCandyCosts[] = [];
|
||||||
|
|
||||||
|
if (import.meta.env.NODE_ENV === "test") {
|
||||||
|
for (const starterCandyCosts of allStarterCandyCosts) {
|
||||||
|
// @ts-expect-error: done this way to keep it `readonly`
|
||||||
|
__TEST_allStarterCandyCosts.push(starterCandyCosts);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2020,7 +2020,8 @@ export class PokedexPageUiHandler extends MessageUiHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Same species egg menu option.
|
// Same species egg menu option.
|
||||||
const sameSpeciesEggCost = getSameSpeciesEggCandyCounts(speciesStarterCosts[this.starterId]);
|
const hatchCount = globalScene.gameData.dexData[this.starterId].hatchedCount;
|
||||||
|
const sameSpeciesEggCost = getSameSpeciesEggCandyCounts(speciesStarterCosts[this.starterId], hatchCount);
|
||||||
options.push({
|
options.push({
|
||||||
label: `×${sameSpeciesEggCost} ${i18next.t("pokedexUiHandler:sameSpeciesEgg")}`,
|
label: `×${sameSpeciesEggCost} ${i18next.t("pokedexUiHandler:sameSpeciesEgg")}`,
|
||||||
handler: () => {
|
handler: () => {
|
||||||
@ -2367,8 +2368,9 @@ export class PokedexPageUiHandler extends MessageUiHandler {
|
|||||||
isSameSpeciesEggAvailable(): boolean {
|
isSameSpeciesEggAvailable(): boolean {
|
||||||
// Get this species ID's starter data
|
// Get this species ID's starter data
|
||||||
const starterData = globalScene.gameData.starterData[this.starterId];
|
const starterData = globalScene.gameData.starterData[this.starterId];
|
||||||
|
const hatchCount = globalScene.gameData.dexData[this.starterId].hatchedCount;
|
||||||
|
|
||||||
return starterData.candyCount >= getSameSpeciesEggCandyCounts(speciesStarterCosts[this.starterId]);
|
return starterData.candyCount >= getSameSpeciesEggCandyCounts(speciesStarterCosts[this.starterId], hatchCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
setSpecies() {
|
setSpecies() {
|
||||||
|
|||||||
@ -893,11 +893,12 @@ export class PokedexUiHandler extends MessageUiHandler {
|
|||||||
*/
|
*/
|
||||||
isSameSpeciesEggAvailable(speciesId: number): boolean {
|
isSameSpeciesEggAvailable(speciesId: number): boolean {
|
||||||
// Get this species ID's starter data
|
// Get this species ID's starter data
|
||||||
const starterData = this.gameData.starterData[this.getStarterSpeciesId(speciesId)];
|
const { gameData } = this;
|
||||||
|
const starterId = this.getStarterSpeciesId(speciesId);
|
||||||
|
const candyCount = gameData.starterData[starterId].candyCount;
|
||||||
|
const hatchCount = gameData.dexData[starterId].hatchedCount;
|
||||||
|
|
||||||
return (
|
return candyCount >= getSameSpeciesEggCandyCounts(speciesStarterCosts[starterId], hatchCount);
|
||||||
starterData.candyCount >= getSameSpeciesEggCandyCounts(speciesStarterCosts[this.getStarterSpeciesId(speciesId)])
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -1417,8 +1417,9 @@ export class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
isSameSpeciesEggAvailable(speciesId: number): boolean {
|
isSameSpeciesEggAvailable(speciesId: number): boolean {
|
||||||
// Get this species ID's starter data
|
// Get this species ID's starter data
|
||||||
const starterData = globalScene.gameData.starterData[speciesId];
|
const starterData = globalScene.gameData.starterData[speciesId];
|
||||||
|
const hatchedCount = globalScene.gameData.dexData[speciesId].hatchedCount;
|
||||||
|
|
||||||
return starterData.candyCount >= getSameSpeciesEggCandyCounts(speciesStarterCosts[speciesId]);
|
return starterData.candyCount >= getSameSpeciesEggCandyCounts(speciesStarterCosts[speciesId], hatchedCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2265,7 +2266,9 @@ export class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Same species egg menu option.
|
// Same species egg menu option.
|
||||||
const sameSpeciesEggCost = getSameSpeciesEggCandyCounts(speciesStarterCosts[this.lastSpecies.speciesId]);
|
const lastSpeciesId = this.lastSpecies.speciesId;
|
||||||
|
const hatchedCount = globalScene.gameData.dexData[lastSpeciesId].hatchedCount;
|
||||||
|
const sameSpeciesEggCost = getSameSpeciesEggCandyCounts(speciesStarterCosts[lastSpeciesId], hatchedCount);
|
||||||
options.push({
|
options.push({
|
||||||
label: `×${sameSpeciesEggCost} ${i18next.t("starterSelectUiHandler:sameSpeciesEgg")}`,
|
label: `×${sameSpeciesEggCost} ${i18next.t("starterSelectUiHandler:sameSpeciesEgg")}`,
|
||||||
handler: () => {
|
handler: () => {
|
||||||
|
|||||||
10
test/data/balance/starter-candy-costs.test.ts
Normal file
10
test/data/balance/starter-candy-costs.test.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { __TEST_allStarterCandyCosts } from "#balance/starters";
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
describe("Starter Candy Costs", () => {
|
||||||
|
it("should have the proper length of arrays in `allStarterCandyCosts`", () => {
|
||||||
|
for (const starterCandyCosts of __TEST_allStarterCandyCosts) {
|
||||||
|
expect(starterCandyCosts.eggCosts).toHaveLength(starterCandyCosts.eggCostReductionThresholds.length + 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user