Skip to content

Commit

Permalink
[Balance] Add Memory Mushroom to Shop (#4555)
Browse files Browse the repository at this point in the history
* Add Memory Mushroom to Shop + escape TM selection

* consolidate learn move type params into an enum

* Rewrite lock capsule test

* Disable luck upgrades for copied SMPhases

* Mem Mushroom Cost 4x Update modifier-type.ts

* Add undefined cost check to `addModifier`

* Increase shop options row limit

* Prevent SMPhase copies from updating the seed

---------

Co-authored-by: damocleas <[email protected]>
Co-authored-by: NightKev <[email protected]>
  • Loading branch information
3 people authored Oct 16, 2024
1 parent c04d81b commit 093f3d9
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 49 deletions.
6 changes: 4 additions & 2 deletions src/battle-scene.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Pokemon, { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
import PokemonSpecies, { allSpecies, getPokemonSpecies, PokemonSpeciesFilter } from "#app/data/pokemon-species";
import { Constructor, isNullOrUndefined, randSeedInt } from "#app/utils";
import * as Utils from "#app/utils";
import { ConsumableModifier, ConsumablePokemonModifier, DoubleBattleChanceBoosterModifier, ExpBalanceModifier, ExpShareModifier, FusePokemonModifier, HealingBoosterModifier, Modifier, ModifierBar, ModifierPredicate, MultipleParticipantExpBonusModifier, overrideHeldItems, overrideModifiers, PersistentModifier, PokemonExpBoosterModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier, PokemonHpRestoreModifier, PokemonIncrementingStatModifier, TerastallizeModifier, TurnHeldItemTransferModifier } from "./modifier/modifier";
import { ConsumableModifier, ConsumablePokemonModifier, DoubleBattleChanceBoosterModifier, ExpBalanceModifier, ExpShareModifier, FusePokemonModifier, HealingBoosterModifier, Modifier, ModifierBar, ModifierPredicate, MultipleParticipantExpBonusModifier, overrideHeldItems, overrideModifiers, PersistentModifier, PokemonExpBoosterModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier, PokemonHpRestoreModifier, PokemonIncrementingStatModifier, RememberMoveModifier, TerastallizeModifier, TurnHeldItemTransferModifier } from "./modifier/modifier";
import { PokeballType } from "#app/data/pokeball";
import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from "#app/data/battle-anims";
import { Phase } from "#app/phase";
Expand Down Expand Up @@ -2425,7 +2425,7 @@ export default class BattleScene extends SceneBase {
return Math.floor(moneyValue / 10) * 10;
}

addModifier(modifier: Modifier | null, ignoreUpdate?: boolean, playSound?: boolean, virtual?: boolean, instant?: boolean): Promise<boolean> {
addModifier(modifier: Modifier | null, ignoreUpdate?: boolean, playSound?: boolean, virtual?: boolean, instant?: boolean, cost?: number): Promise<boolean> {
if (!modifier) {
return Promise.resolve(false);
}
Expand Down Expand Up @@ -2482,6 +2482,8 @@ export default class BattleScene extends SceneBase {
}
} else if (modifier instanceof FusePokemonModifier) {
args.push(this.getPokemonById(modifier.fusePokemonId) as PlayerPokemon);
} else if (modifier instanceof RememberMoveModifier && !Utils.isNullOrUndefined(cost)) {
args.push(cost);
}

if (modifier.shouldApply(pokemon, ...args)) {
Expand Down
3 changes: 2 additions & 1 deletion src/modifier/modifier-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2227,7 +2227,8 @@ export function getPlayerShopModifierTypeOptionsForWave(waveIndex: integer, base
],
[
new ModifierTypeOption(modifierTypes.HYPER_POTION(), 0, baseCost * 0.8),
new ModifierTypeOption(modifierTypes.MAX_REVIVE(), 0, baseCost * 2.75)
new ModifierTypeOption(modifierTypes.MAX_REVIVE(), 0, baseCost * 2.75),
new ModifierTypeOption(modifierTypes.MEMORY_MUSHROOM(), 0, baseCost * 4)
],
[
new ModifierTypeOption(modifierTypes.MAX_POTION(), 0, baseCost * 1.5),
Expand Down
9 changes: 5 additions & 4 deletions src/modifier/modifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import Pokemon, { type PlayerPokemon } from "#app/field/pokemon";
import { getPokemonNameWithAffix } from "#app/messages";
import Overrides from "#app/overrides";
import { EvolutionPhase } from "#app/phases/evolution-phase";
import { LearnMovePhase } from "#app/phases/learn-move-phase";
import { LearnMovePhase, LearnMoveType } from "#app/phases/learn-move-phase";
import { LevelUpPhase } from "#app/phases/level-up-phase";
import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
import { achvs } from "#app/system/achv";
Expand Down Expand Up @@ -2235,7 +2235,7 @@ export class TmModifier extends ConsumablePokemonModifier {
*/
override apply(playerPokemon: PlayerPokemon): boolean {

playerPokemon.scene.unshiftPhase(new LearnMovePhase(playerPokemon.scene, playerPokemon.scene.getParty().indexOf(playerPokemon), this.type.moveId, true));
playerPokemon.scene.unshiftPhase(new LearnMovePhase(playerPokemon.scene, playerPokemon.scene.getParty().indexOf(playerPokemon), this.type.moveId, LearnMoveType.TM));

return true;
}
Expand All @@ -2255,8 +2255,9 @@ export class RememberMoveModifier extends ConsumablePokemonModifier {
* @param playerPokemon The {@linkcode PlayerPokemon} that should remember the move
* @returns always `true`
*/
override apply(playerPokemon: PlayerPokemon): boolean {
playerPokemon.scene.unshiftPhase(new LearnMovePhase(playerPokemon.scene, playerPokemon.scene.getParty().indexOf(playerPokemon), playerPokemon.getLearnableLevelMoves()[this.levelMoveIndex]));
override apply(playerPokemon: PlayerPokemon, cost?: number): boolean {

playerPokemon.scene.unshiftPhase(new LearnMovePhase(playerPokemon.scene, playerPokemon.scene.getParty().indexOf(playerPokemon), playerPokemon.getLearnableLevelMoves()[this.levelMoveIndex], LearnMoveType.MEMORY, cost));

return true;
}
Expand Down
37 changes: 31 additions & 6 deletions src/phases/learn-move-phase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,37 @@ import BattleScene from "#app/battle-scene";
import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims";
import Move, { allMoves } from "#app/data/move";
import { SpeciesFormChangeMoveLearnedTrigger } from "#app/data/pokemon-forms";
import { Moves } from "#app/enums/moves";
import { Moves } from "#enums/moves";
import { getPokemonNameWithAffix } from "#app/messages";
import Overrides from "#app/overrides";
import EvolutionSceneHandler from "#app/ui/evolution-scene-handler";
import { SummaryUiMode } from "#app/ui/summary-ui-handler";
import { Mode } from "#app/ui/ui";
import i18next from "i18next";
import { PlayerPartyMemberPokemonPhase } from "./player-party-member-pokemon-phase";
import { PlayerPartyMemberPokemonPhase } from "#app/phases/player-party-member-pokemon-phase";
import Pokemon from "#app/field/pokemon";
import { SelectModifierPhase } from "#app/phases/select-modifier-phase";

export enum LearnMoveType {
/** For learning a move via level-up, evolution, or other non-item-based event */
LEARN_MOVE,
/** For learning a move via Memory Mushroom */
MEMORY,
/** For learning a move via TM */
TM
}

export class LearnMovePhase extends PlayerPartyMemberPokemonPhase {
private moveId: Moves;
private messageMode: Mode;
private fromTM: boolean;
private learnMoveType;
private cost: number;

constructor(scene: BattleScene, partyMemberIndex: integer, moveId: Moves, fromTM?: boolean) {
constructor(scene: BattleScene, partyMemberIndex: integer, moveId: Moves, learnMoveType: LearnMoveType = LearnMoveType.LEARN_MOVE, cost: number = -1) {
super(scene, partyMemberIndex);
this.moveId = moveId;
this.fromTM = fromTM ?? false;
this.learnMoveType = learnMoveType;
this.cost = cost;
}

start() {
Expand Down Expand Up @@ -136,11 +149,23 @@ export class LearnMovePhase extends PlayerPartyMemberPokemonPhase {
* @param Pokemon The Pokemon learning the move
*/
async learnMove(index: number, move: Move, pokemon: Pokemon, textMessage?: string) {
if (this.fromTM) {
if (this.learnMoveType === LearnMoveType.TM) {
if (!pokemon.usedTMs) {
pokemon.usedTMs = [];
}
pokemon.usedTMs.push(this.moveId);
this.scene.tryRemovePhase((phase) => phase instanceof SelectModifierPhase);
} else if (this.learnMoveType === LearnMoveType.MEMORY) {
if (this.cost !== -1) {
if (!Overrides.WAIVE_ROLL_FEE_OVERRIDE) {
this.scene.money -= this.cost;
this.scene.updateMoneyText();
this.scene.animateMoneyChanged(false);
}
this.scene.playSound("se/buy");
} else {
this.scene.tryRemovePhase((phase) => phase instanceof SelectModifierPhase);
}
}
pokemon.setMove(index, this.moveId);
initMoveAnim(this.scene, this.moveId).then(() => {
Expand Down
66 changes: 45 additions & 21 deletions src/phases/select-modifier-phase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,32 @@ export class SelectModifierPhase extends BattlePhase {
private rerollCount: integer;
private modifierTiers?: ModifierTier[];
private customModifierSettings?: CustomModifierSettings;
private isCopy: boolean;

constructor(scene: BattleScene, rerollCount: integer = 0, modifierTiers?: ModifierTier[], customModifierSettings?: CustomModifierSettings) {
private typeOptions: ModifierTypeOption[];

constructor(scene: BattleScene, rerollCount: integer = 0, modifierTiers?: ModifierTier[], customModifierSettings?: CustomModifierSettings, isCopy: boolean = false) {
super(scene);

this.rerollCount = rerollCount;
this.modifierTiers = modifierTiers;
this.customModifierSettings = customModifierSettings;
this.isCopy = isCopy;
}

start() {
super.start();

if (!this.rerollCount) {
if (!this.rerollCount && !this.isCopy) {
this.updateSeed();
} else {
} else if (this.rerollCount) {
this.scene.reroll = false;
}

const party = this.scene.getParty();
regenerateModifierPoolThresholds(party, this.getPoolType(), this.rerollCount);
if (!this.isCopy) {
regenerateModifierPoolThresholds(party, this.getPoolType(), this.rerollCount);
}
const modifierCount = new Utils.IntegerHolder(3);
if (this.isPlayer()) {
this.scene.applyModifiers(ExtraModifierModifier, true, modifierCount);
Expand All @@ -54,7 +60,7 @@ export class SelectModifierPhase extends BattlePhase {
}
}

const typeOptions: ModifierTypeOption[] = this.getModifierTypeOptions(modifierCount.value);
this.typeOptions = this.getModifierTypeOptions(modifierCount.value);

const modifierSelectCallback = (rowCursor: integer, cursor: integer) => {
if (rowCursor < 0 || cursor < 0) {
Expand All @@ -63,13 +69,13 @@ export class SelectModifierPhase extends BattlePhase {
this.scene.ui.revertMode();
this.scene.ui.setMode(Mode.MESSAGE);
super.end();
}, () => this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback, this.getRerollCost(typeOptions, this.scene.lockModifierTiers)));
}, () => this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), this.typeOptions, modifierSelectCallback, this.getRerollCost(this.scene.lockModifierTiers)));
});
return false;
}
let modifierType: ModifierType;
let cost: integer;
const rerollCost = this.getRerollCost(typeOptions, this.scene.lockModifierTiers);
const rerollCost = this.getRerollCost(this.scene.lockModifierTiers);
switch (rowCursor) {
case 0:
switch (cursor) {
Expand All @@ -79,7 +85,7 @@ export class SelectModifierPhase extends BattlePhase {
return false;
} else {
this.scene.reroll = true;
this.scene.unshiftPhase(new SelectModifierPhase(this.scene, this.rerollCount + 1, typeOptions.map(o => o.type?.tier).filter(t => t !== undefined) as ModifierTier[]));
this.scene.unshiftPhase(new SelectModifierPhase(this.scene, this.rerollCount + 1, this.typeOptions.map(o => o.type?.tier).filter(t => t !== undefined) as ModifierTier[]));
this.scene.ui.clearText();
this.scene.ui.setMode(Mode.MESSAGE).then(() => super.end());
if (!Overrides.WAIVE_ROLL_FEE_OVERRIDE) {
Expand All @@ -98,13 +104,13 @@ export class SelectModifierPhase extends BattlePhase {
const itemModifier = itemModifiers[itemIndex];
this.scene.tryTransferHeldItemModifier(itemModifier, party[toSlotIndex], true, itemQuantity);
} else {
this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback, this.getRerollCost(typeOptions, this.scene.lockModifierTiers));
this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), this.typeOptions, modifierSelectCallback, this.getRerollCost(this.scene.lockModifierTiers));
}
}, PartyUiHandler.FilterItemMaxStacks);
break;
case 2:
this.scene.ui.setModeWithoutClear(Mode.PARTY, PartyUiMode.CHECK, -1, () => {
this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback, this.getRerollCost(typeOptions, this.scene.lockModifierTiers));
this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), this.typeOptions, modifierSelectCallback, this.getRerollCost(this.scene.lockModifierTiers));
});
break;
case 3:
Expand All @@ -115,21 +121,21 @@ export class SelectModifierPhase extends BattlePhase {
}
this.scene.lockModifierTiers = !this.scene.lockModifierTiers;
const uiHandler = this.scene.ui.getHandler() as ModifierSelectUiHandler;
uiHandler.setRerollCost(this.getRerollCost(typeOptions, this.scene.lockModifierTiers));
uiHandler.setRerollCost(this.getRerollCost(this.scene.lockModifierTiers));
uiHandler.updateLockRaritiesText();
uiHandler.updateRerollCostText();
return false;
}
return true;
case 1:
if (typeOptions.length === 0) {
if (this.typeOptions.length === 0) {
this.scene.ui.clearText();
this.scene.ui.setMode(Mode.MESSAGE);
super.end();
return true;
}
if (typeOptions[cursor].type) {
modifierType = typeOptions[cursor].type;
if (this.typeOptions[cursor].type) {
modifierType = this.typeOptions[cursor].type;
}
break;
default:
Expand All @@ -151,8 +157,16 @@ export class SelectModifierPhase extends BattlePhase {
}

const applyModifier = (modifier: Modifier, playSound: boolean = false) => {
const result = this.scene.addModifier(modifier, false, playSound);
if (cost) {
const result = this.scene.addModifier(modifier, false, playSound, undefined, undefined, cost);
// Queue a copy of this phase when applying a TM or Memory Mushroom.
// If the player selects either of these, then escapes out of consuming them,
// they are returned to a shop in the same state.
if (modifier.type instanceof RememberMoveModifierType ||
modifier.type instanceof TmModifierType) {
this.scene.unshiftPhase(this.copy());
}

if (cost && !(modifier.type instanceof RememberMoveModifierType)) {
result.then(success => {
if (success) {
if (!Overrides.WAIVE_ROLL_FEE_OVERRIDE) {
Expand Down Expand Up @@ -189,7 +203,7 @@ export class SelectModifierPhase extends BattlePhase {
applyModifier(modifier, true);
});
} else {
this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback, this.getRerollCost(typeOptions, this.scene.lockModifierTiers));
this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), this.typeOptions, modifierSelectCallback, this.getRerollCost(this.scene.lockModifierTiers));
}
}, modifierType.selectFilter);
} else {
Expand All @@ -216,7 +230,7 @@ export class SelectModifierPhase extends BattlePhase {
applyModifier(modifier!, true); // TODO: is the bang correct?
});
} else {
this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback, this.getRerollCost(typeOptions, this.scene.lockModifierTiers));
this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), this.typeOptions, modifierSelectCallback, this.getRerollCost(this.scene.lockModifierTiers));
}
}, pokemonModifierType.selectFilter, modifierType instanceof PokemonMoveModifierType ? (modifierType as PokemonMoveModifierType).moveSelectFilter : undefined, tmMoveId, isPpRestoreModifier);
}
Expand All @@ -226,7 +240,7 @@ export class SelectModifierPhase extends BattlePhase {

return !cost!;// TODO: is the bang correct?
};
this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback, this.getRerollCost(typeOptions, this.scene.lockModifierTiers));
this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), this.typeOptions, modifierSelectCallback, this.getRerollCost(this.scene.lockModifierTiers));
}

updateSeed(): void {
Expand All @@ -237,13 +251,13 @@ export class SelectModifierPhase extends BattlePhase {
return true;
}

getRerollCost(typeOptions: ModifierTypeOption[], lockRarities: boolean): number {
getRerollCost(lockRarities: boolean): number {
let baseValue = 0;
if (Overrides.WAIVE_ROLL_FEE_OVERRIDE) {
return baseValue;
} else if (lockRarities) {
const tierValues = [ 50, 125, 300, 750, 2000 ];
for (const opt of typeOptions) {
for (const opt of this.typeOptions) {
baseValue += tierValues[opt.type.tier ?? 0];
}
} else {
Expand Down Expand Up @@ -271,6 +285,16 @@ export class SelectModifierPhase extends BattlePhase {
return getPlayerModifierTypeOptions(modifierCount, this.scene.getParty(), this.scene.lockModifierTiers ? this.modifierTiers : undefined, this.customModifierSettings);
}

copy(): SelectModifierPhase {
return new SelectModifierPhase(
this.scene,
this.rerollCount,
this.modifierTiers,
{ guaranteedModifierTypeOptions: this.typeOptions, rerollMultiplier: this.customModifierSettings?.rerollMultiplier, allowLuckUpgrades: false },
true
);
}

addModifier(modifier: Modifier): Promise<boolean> {
return this.scene.addModifier(modifier, false, true);
}
Expand Down
20 changes: 11 additions & 9 deletions src/test/items/lock_capsule.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Abilities } from "#app/enums/abilities";
import { Moves } from "#app/enums/moves";
import { ModifierTypeOption, modifierTypes } from "#app/modifier/modifier-type";
import { ModifierTier } from "#app/modifier/modifier-tier";
import { SelectModifierPhase } from "#app/phases/select-modifier-phase";
import { Mode } from "#app/ui/ui";
import GameManager from "#test/utils/gameManager";
import Phase from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
Expand Down Expand Up @@ -32,15 +33,16 @@ describe("Items - Lock Capsule", () => {
});

it("doesn't set the cost of common tier items to 0", async () => {
await game.startBattle();
await game.classicMode.startBattle();
game.scene.overridePhase(new SelectModifierPhase(game.scene, 0, undefined, { guaranteedModifierTiers: [ ModifierTier.COMMON, ModifierTier.COMMON, ModifierTier.COMMON ], fillRemaining: false }));

game.move.select(Moves.SURF);
await game.phaseInterceptor.to(SelectModifierPhase, false);

const rewards = game.scene.getCurrentPhase() as SelectModifierPhase;
const potion = new ModifierTypeOption(modifierTypes.POTION(), 0, 40); // Common tier item
const rerollCost = rewards.getRerollCost([ potion, potion, potion ], true);
game.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => {
const selectModifierPhase = game.scene.getCurrentPhase() as SelectModifierPhase;
const rerollCost = selectModifierPhase.getRerollCost(true);
expect(rerollCost).toBe(150);
});

expect(rerollCost).toBe(150);
game.doSelectModifier();
await game.phaseInterceptor.to("SelectModifierPhase");
}, 20000);
});
8 changes: 4 additions & 4 deletions src/test/phases/select-modifier-phase.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,11 @@ describe("SelectModifierPhase", () => {
new ModifierTypeOption(modifierTypes.REVIVE(), 0, 1000)
];

const selectModifierPhase1 = new SelectModifierPhase(scene);
const selectModifierPhase2 = new SelectModifierPhase(scene, 0, undefined, { rerollMultiplier: 2 });
const selectModifierPhase1 = new SelectModifierPhase(scene, 0, undefined, { guaranteedModifierTypeOptions: options });
const selectModifierPhase2 = new SelectModifierPhase(scene, 0, undefined, { guaranteedModifierTypeOptions: options, rerollMultiplier: 2 });

const cost1 = selectModifierPhase1.getRerollCost(options, false);
const cost2 = selectModifierPhase2.getRerollCost(options, false);
const cost1 = selectModifierPhase1.getRerollCost(false);
const cost2 = selectModifierPhase2.getRerollCost(false);
expect(cost2).toEqual(cost1 * 2);
});

Expand Down
Loading

0 comments on commit 093f3d9

Please sign in to comment.