mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-06-21 17:12:44 +02:00
Merge remote-tracking branch 'upstream/beta' into modifier-fixes
This commit is contained in:
commit
5531b3c5cd
@ -2534,48 +2534,38 @@ export class AllyStatMultiplierAbAttr extends AbAttr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ability attribute for Gorilla Tactics
|
* Takes effect whenever a move succesfully executes, such as gorilla tactics' move-locking.
|
||||||
* @extends PostAttackAbAttr
|
* (More specifically, whenever a move is pushed to the move history)
|
||||||
|
* @extends AbAttr
|
||||||
*/
|
*/
|
||||||
export class GorillaTacticsAbAttr extends PostAttackAbAttr {
|
export class ExecutedMoveAbAttr extends AbAttr {
|
||||||
constructor() {
|
canApplyExecutedMove(
|
||||||
super((_user, _target, _move) => true, false);
|
_pokemon: Pokemon,
|
||||||
}
|
_simulated: boolean,
|
||||||
|
|
||||||
override canApplyPostAttack(
|
|
||||||
pokemon: Pokemon,
|
|
||||||
passive: boolean,
|
|
||||||
simulated: boolean,
|
|
||||||
defender: Pokemon,
|
|
||||||
move: Move,
|
|
||||||
hitResult: HitResult | null,
|
|
||||||
args: any[],
|
|
||||||
): boolean {
|
): boolean {
|
||||||
return (
|
return true;
|
||||||
(super.canApplyPostAttack(pokemon, passive, simulated, defender, move, hitResult, args) && simulated) ||
|
|
||||||
!pokemon.getTag(BattlerTagType.GORILLA_TACTICS)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
applyExecutedMove(
|
||||||
*
|
_pokemon: Pokemon,
|
||||||
* @param {Pokemon} pokemon the {@linkcode Pokemon} with this ability
|
_simulated: boolean,
|
||||||
* @param _passive n/a
|
): void {}
|
||||||
* @param simulated whether the ability is being simulated
|
}
|
||||||
* @param _defender n/a
|
|
||||||
* @param _move n/a
|
/**
|
||||||
* @param _hitResult n/a
|
* Ability attribute for Gorilla Tactics
|
||||||
* @param _args n/a
|
* @extends ExecutedMoveAbAttr
|
||||||
*/
|
*/
|
||||||
override applyPostAttack(
|
export class GorillaTacticsAbAttr extends ExecutedMoveAbAttr {
|
||||||
pokemon: Pokemon,
|
constructor(showAbility: boolean = false) {
|
||||||
_passive: boolean,
|
super(showAbility);
|
||||||
simulated: boolean,
|
}
|
||||||
_defender: Pokemon,
|
|
||||||
_move: Move,
|
override canApplyExecutedMove(pokemon: Pokemon, simulated: boolean): boolean {
|
||||||
_hitResult: HitResult | null,
|
return simulated || !pokemon.getTag(BattlerTagType.GORILLA_TACTICS);
|
||||||
_args: any[],
|
}
|
||||||
): void {
|
|
||||||
|
override applyExecutedMove(pokemon: Pokemon, simulated: boolean): void {
|
||||||
if (!simulated) {
|
if (!simulated) {
|
||||||
pokemon.addTag(BattlerTagType.GORILLA_TACTICS);
|
pokemon.addTag(BattlerTagType.GORILLA_TACTICS);
|
||||||
}
|
}
|
||||||
@ -7797,6 +7787,22 @@ export function applyPreAttackAbAttrs(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function applyExecutedMoveAbAttrs(
|
||||||
|
attrType: Constructor<ExecutedMoveAbAttr>,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
simulated: boolean = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal<ExecutedMoveAbAttr>(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
attr => attr.applyExecutedMove(pokemon, simulated),
|
||||||
|
attr => attr.canApplyExecutedMove(pokemon, simulated),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function applyPostAttackAbAttrs(
|
export function applyPostAttackAbAttrs(
|
||||||
attrType: Constructor<PostAttackAbAttr>,
|
attrType: Constructor<PostAttackAbAttr>,
|
||||||
pokemon: Pokemon,
|
pokemon: Pokemon,
|
||||||
|
@ -5988,6 +5988,7 @@ export const tmSpecies: TmSpecies = {
|
|||||||
SpeciesId.FEZANDIPITI,
|
SpeciesId.FEZANDIPITI,
|
||||||
SpeciesId.ARCHALUDON,
|
SpeciesId.ARCHALUDON,
|
||||||
SpeciesId.IRON_CROWN,
|
SpeciesId.IRON_CROWN,
|
||||||
|
SpeciesId.TERAPAGOS,
|
||||||
SpeciesId.ALOLA_RATICATE,
|
SpeciesId.ALOLA_RATICATE,
|
||||||
SpeciesId.ALOLA_RAICHU,
|
SpeciesId.ALOLA_RAICHU,
|
||||||
SpeciesId.ALOLA_SANDSLASH,
|
SpeciesId.ALOLA_SANDSLASH,
|
||||||
@ -16248,6 +16249,7 @@ export const tmSpecies: TmSpecies = {
|
|||||||
SpeciesId.CALYREX,
|
SpeciesId.CALYREX,
|
||||||
SpeciesId.SANDY_SHOCKS,
|
SpeciesId.SANDY_SHOCKS,
|
||||||
SpeciesId.IRON_JUGULIS,
|
SpeciesId.IRON_JUGULIS,
|
||||||
|
SpeciesId.TERAPAGOS,
|
||||||
SpeciesId.ALOLA_DUGTRIO,
|
SpeciesId.ALOLA_DUGTRIO,
|
||||||
SpeciesId.GALAR_SLOWPOKE,
|
SpeciesId.GALAR_SLOWPOKE,
|
||||||
SpeciesId.GALAR_SLOWBRO,
|
SpeciesId.GALAR_SLOWBRO,
|
||||||
@ -39466,6 +39468,8 @@ export const tmSpecies: TmSpecies = {
|
|||||||
SpeciesId.FARFETCHD,
|
SpeciesId.FARFETCHD,
|
||||||
SpeciesId.DODUO,
|
SpeciesId.DODUO,
|
||||||
SpeciesId.DODRIO,
|
SpeciesId.DODRIO,
|
||||||
|
SpeciesId.DEWGONG,
|
||||||
|
SpeciesId.GRIMER,
|
||||||
SpeciesId.MUK,
|
SpeciesId.MUK,
|
||||||
SpeciesId.GASTLY,
|
SpeciesId.GASTLY,
|
||||||
SpeciesId.HAUNTER,
|
SpeciesId.HAUNTER,
|
||||||
@ -39477,6 +39481,7 @@ export const tmSpecies: TmSpecies = {
|
|||||||
SpeciesId.CUBONE,
|
SpeciesId.CUBONE,
|
||||||
SpeciesId.MAROWAK,
|
SpeciesId.MAROWAK,
|
||||||
SpeciesId.HITMONLEE,
|
SpeciesId.HITMONLEE,
|
||||||
|
SpeciesId.HITMONCHAN,
|
||||||
SpeciesId.LICKITUNG,
|
SpeciesId.LICKITUNG,
|
||||||
SpeciesId.TANGELA,
|
SpeciesId.TANGELA,
|
||||||
SpeciesId.GOLDEEN,
|
SpeciesId.GOLDEEN,
|
||||||
@ -48806,6 +48811,7 @@ export const tmSpecies: TmSpecies = {
|
|||||||
SpeciesId.GARGANACL,
|
SpeciesId.GARGANACL,
|
||||||
SpeciesId.GLIMMET,
|
SpeciesId.GLIMMET,
|
||||||
SpeciesId.GLIMMORA,
|
SpeciesId.GLIMMORA,
|
||||||
|
SpeciesId.TERAPAGOS,
|
||||||
SpeciesId.ALOLA_GEODUDE,
|
SpeciesId.ALOLA_GEODUDE,
|
||||||
SpeciesId.ALOLA_GRAVELER,
|
SpeciesId.ALOLA_GRAVELER,
|
||||||
SpeciesId.ALOLA_GOLEM,
|
SpeciesId.ALOLA_GOLEM,
|
||||||
@ -53077,6 +53083,7 @@ export const tmSpecies: TmSpecies = {
|
|||||||
SpeciesId.MIRAIDON,
|
SpeciesId.MIRAIDON,
|
||||||
SpeciesId.ARCHALUDON,
|
SpeciesId.ARCHALUDON,
|
||||||
SpeciesId.IRON_CROWN,
|
SpeciesId.IRON_CROWN,
|
||||||
|
SpeciesId.TERAPAGOS,
|
||||||
[
|
[
|
||||||
SpeciesId.WORMADAM,
|
SpeciesId.WORMADAM,
|
||||||
"trash",
|
"trash",
|
||||||
|
@ -3,10 +3,12 @@ import { globalScene } from "#app/global-scene";
|
|||||||
import {
|
import {
|
||||||
AddSecondStrikeAbAttr,
|
AddSecondStrikeAbAttr,
|
||||||
AlwaysHitAbAttr,
|
AlwaysHitAbAttr,
|
||||||
|
applyExecutedMoveAbAttrs,
|
||||||
applyPostAttackAbAttrs,
|
applyPostAttackAbAttrs,
|
||||||
applyPostDamageAbAttrs,
|
applyPostDamageAbAttrs,
|
||||||
applyPostDefendAbAttrs,
|
applyPostDefendAbAttrs,
|
||||||
applyPreAttackAbAttrs,
|
applyPreAttackAbAttrs,
|
||||||
|
ExecutedMoveAbAttr,
|
||||||
IgnoreMoveEffectsAbAttr,
|
IgnoreMoveEffectsAbAttr,
|
||||||
MaxMultiHitAbAttr,
|
MaxMultiHitAbAttr,
|
||||||
PostAttackAbAttr,
|
PostAttackAbAttr,
|
||||||
@ -380,6 +382,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
// Add to the move history entry
|
// Add to the move history entry
|
||||||
if (this.firstHit) {
|
if (this.firstHit) {
|
||||||
user.pushMoveHistory(this.moveHistoryEntry);
|
user.pushMoveHistory(this.moveHistoryEntry);
|
||||||
|
applyExecutedMoveAbAttrs(ExecutedMoveAbAttr, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -31,6 +31,8 @@ import Overrides from "#app/overrides";
|
|||||||
import type { CustomModifierSettings } from "#app/modifier/modifier-type";
|
import type { CustomModifierSettings } from "#app/modifier/modifier-type";
|
||||||
import { isNullOrUndefined, NumberHolder } from "#app/utils/common";
|
import { isNullOrUndefined, NumberHolder } from "#app/utils/common";
|
||||||
|
|
||||||
|
export type ModifierSelectCallback = (rowCursor: number, cursor: number) => boolean;
|
||||||
|
|
||||||
export class SelectModifierPhase extends BattlePhase {
|
export class SelectModifierPhase extends BattlePhase {
|
||||||
public readonly phaseName = "SelectModifierPhase";
|
public readonly phaseName = "SelectModifierPhase";
|
||||||
private rerollCount: number;
|
private rerollCount: number;
|
||||||
@ -57,6 +59,10 @@ export class SelectModifierPhase extends BattlePhase {
|
|||||||
start() {
|
start() {
|
||||||
super.start();
|
super.start();
|
||||||
|
|
||||||
|
if (!this.isPlayer()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.rerollCount && !this.isCopy) {
|
if (!this.rerollCount && !this.isCopy) {
|
||||||
this.updateSeed();
|
this.updateSeed();
|
||||||
} else if (this.rerollCount) {
|
} else if (this.rerollCount) {
|
||||||
@ -67,27 +73,9 @@ export class SelectModifierPhase extends BattlePhase {
|
|||||||
if (!this.isCopy) {
|
if (!this.isCopy) {
|
||||||
regenerateModifierPoolThresholds(party, this.getPoolType(), this.rerollCount);
|
regenerateModifierPoolThresholds(party, this.getPoolType(), this.rerollCount);
|
||||||
}
|
}
|
||||||
const modifierCount = new NumberHolder(3);
|
const modifierCount = this.getModifierCount();
|
||||||
if (this.isPlayer()) {
|
|
||||||
globalScene.applyModifiers(ExtraModifierModifier, true, modifierCount);
|
|
||||||
globalScene.applyModifiers(TempExtraModifierModifier, true, modifierCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If custom modifiers are specified, overrides default item count
|
this.typeOptions = this.getModifierTypeOptions(modifierCount);
|
||||||
if (this.customModifierSettings) {
|
|
||||||
const newItemCount =
|
|
||||||
(this.customModifierSettings.guaranteedModifierTiers?.length || 0) +
|
|
||||||
(this.customModifierSettings.guaranteedModifierTypeOptions?.length || 0) +
|
|
||||||
(this.customModifierSettings.guaranteedModifierTypeFuncs?.length || 0);
|
|
||||||
if (this.customModifierSettings.fillRemaining) {
|
|
||||||
const originalCount = modifierCount.value;
|
|
||||||
modifierCount.value = originalCount > newItemCount ? originalCount : newItemCount;
|
|
||||||
} else {
|
|
||||||
modifierCount.value = newItemCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.typeOptions = this.getModifierTypeOptions(modifierCount.value);
|
|
||||||
|
|
||||||
const modifierSelectCallback = (rowCursor: number, cursor: number) => {
|
const modifierSelectCallback = (rowCursor: number, cursor: number) => {
|
||||||
if (rowCursor < 0 || cursor < 0) {
|
if (rowCursor < 0 || cursor < 0) {
|
||||||
@ -99,256 +87,312 @@ export class SelectModifierPhase extends BattlePhase {
|
|||||||
globalScene.ui.setMode(UiMode.MESSAGE);
|
globalScene.ui.setMode(UiMode.MESSAGE);
|
||||||
super.end();
|
super.end();
|
||||||
},
|
},
|
||||||
() =>
|
() => this.resetModifierSelect(modifierSelectCallback),
|
||||||
globalScene.ui.setMode(
|
|
||||||
UiMode.MODIFIER_SELECT,
|
|
||||||
this.isPlayer(),
|
|
||||||
this.typeOptions,
|
|
||||||
modifierSelectCallback,
|
|
||||||
this.getRerollCost(globalScene.lockModifierTiers),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let modifierType: ModifierType;
|
|
||||||
let cost: number;
|
|
||||||
const rerollCost = this.getRerollCost(globalScene.lockModifierTiers);
|
|
||||||
switch (rowCursor) {
|
switch (rowCursor) {
|
||||||
|
// Execute one of the options from the bottom row
|
||||||
case 0:
|
case 0:
|
||||||
switch (cursor) {
|
switch (cursor) {
|
||||||
case 0:
|
case 0:
|
||||||
if (rerollCost < 0 || globalScene.money < rerollCost) {
|
return this.rerollModifiers();
|
||||||
globalScene.ui.playError();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
globalScene.reroll = true;
|
|
||||||
globalScene.phaseManager.unshiftNew(
|
|
||||||
"SelectModifierPhase",
|
|
||||||
this.rerollCount + 1,
|
|
||||||
this.typeOptions.map(o => o.type?.tier).filter(t => t !== undefined) as ModifierTier[],
|
|
||||||
);
|
|
||||||
globalScene.ui.clearText();
|
|
||||||
globalScene.ui.setMode(UiMode.MESSAGE).then(() => super.end());
|
|
||||||
if (!Overrides.WAIVE_ROLL_FEE_OVERRIDE) {
|
|
||||||
globalScene.money -= rerollCost;
|
|
||||||
globalScene.updateMoneyText();
|
|
||||||
globalScene.animateMoneyChanged(false);
|
|
||||||
}
|
|
||||||
globalScene.playSound("se/buy");
|
|
||||||
break;
|
|
||||||
case 1:
|
case 1:
|
||||||
globalScene.ui.setModeWithoutClear(
|
return this.openModifierTransferScreen(modifierSelectCallback);
|
||||||
UiMode.PARTY,
|
// Check the party, pass a callback to restore the modifier select screen.
|
||||||
PartyUiMode.MODIFIER_TRANSFER,
|
|
||||||
-1,
|
|
||||||
(fromSlotIndex: number, itemIndex: number, itemQuantity: number, toSlotIndex: number) => {
|
|
||||||
if (
|
|
||||||
toSlotIndex !== undefined &&
|
|
||||||
fromSlotIndex < 6 &&
|
|
||||||
toSlotIndex < 6 &&
|
|
||||||
fromSlotIndex !== toSlotIndex &&
|
|
||||||
itemIndex > -1
|
|
||||||
) {
|
|
||||||
const itemModifiers = globalScene.findModifiers(
|
|
||||||
m =>
|
|
||||||
m instanceof PokemonHeldItemModifier &&
|
|
||||||
m.isTransferable &&
|
|
||||||
m.pokemonId === party[fromSlotIndex].id,
|
|
||||||
) as PokemonHeldItemModifier[];
|
|
||||||
const itemModifier = itemModifiers[itemIndex];
|
|
||||||
globalScene.tryTransferHeldItemModifier(
|
|
||||||
itemModifier,
|
|
||||||
party[toSlotIndex],
|
|
||||||
true,
|
|
||||||
itemQuantity,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
globalScene.ui.setMode(
|
|
||||||
UiMode.MODIFIER_SELECT,
|
|
||||||
this.isPlayer(),
|
|
||||||
this.typeOptions,
|
|
||||||
modifierSelectCallback,
|
|
||||||
this.getRerollCost(globalScene.lockModifierTiers),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
PartyUiHandler.FilterItemMaxStacks,
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case 2:
|
case 2:
|
||||||
globalScene.ui.setModeWithoutClear(UiMode.PARTY, PartyUiMode.CHECK, -1, () => {
|
globalScene.ui.setModeWithoutClear(UiMode.PARTY, PartyUiMode.CHECK, -1, () => {
|
||||||
globalScene.ui.setMode(
|
this.resetModifierSelect(modifierSelectCallback);
|
||||||
UiMode.MODIFIER_SELECT,
|
|
||||||
this.isPlayer(),
|
|
||||||
this.typeOptions,
|
|
||||||
modifierSelectCallback,
|
|
||||||
this.getRerollCost(globalScene.lockModifierTiers),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
break;
|
return true;
|
||||||
case 3:
|
case 3:
|
||||||
if (rerollCost < 0) {
|
return this.toggleRerollLock();
|
||||||
// Reroll lock button is also disabled when reroll is disabled
|
default:
|
||||||
globalScene.ui.playError();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
globalScene.lockModifierTiers = !globalScene.lockModifierTiers;
|
|
||||||
const uiHandler = globalScene.ui.getHandler() as ModifierSelectUiHandler;
|
|
||||||
uiHandler.setRerollCost(this.getRerollCost(globalScene.lockModifierTiers));
|
|
||||||
uiHandler.updateLockRaritiesText();
|
|
||||||
uiHandler.updateRerollCostText();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
// Pick an option from the rewards
|
||||||
case 1:
|
case 1:
|
||||||
if (this.typeOptions.length === 0) {
|
return this.selectRewardModifierOption(cursor, modifierSelectCallback);
|
||||||
globalScene.ui.clearText();
|
// Pick an option from the shop
|
||||||
globalScene.ui.setMode(UiMode.MESSAGE);
|
default: {
|
||||||
super.end();
|
return this.selectShopModifierOption(rowCursor, cursor, modifierSelectCallback);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (this.typeOptions[cursor].type) {
|
|
||||||
modifierType = this.typeOptions[cursor].type;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
const shopOptions = getPlayerShopModifierTypeOptionsForWave(
|
|
||||||
globalScene.currentBattle.waveIndex,
|
|
||||||
globalScene.getWaveMoneyAmount(1),
|
|
||||||
);
|
|
||||||
const shopOption =
|
|
||||||
shopOptions[
|
|
||||||
rowCursor > 2 || shopOptions.length <= SHOP_OPTIONS_ROW_LIMIT ? cursor : cursor + SHOP_OPTIONS_ROW_LIMIT
|
|
||||||
];
|
|
||||||
if (shopOption.type) {
|
|
||||||
modifierType = shopOption.type;
|
|
||||||
}
|
|
||||||
// Apply Black Sludge to healing item cost
|
|
||||||
const healingItemCost = new NumberHolder(shopOption.cost);
|
|
||||||
globalScene.applyModifier(HealShopCostModifier, true, healingItemCost);
|
|
||||||
cost = healingItemCost.value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cost! && globalScene.money < cost && !Overrides.WAIVE_ROLL_FEE_OVERRIDE) {
|
|
||||||
// TODO: is the bang on cost correct?
|
|
||||||
globalScene.ui.playError();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const applyModifier = (modifier: Modifier, playSound = false) => {
|
|
||||||
const result = globalScene.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) {
|
|
||||||
globalScene.phaseManager.unshiftPhase(this.copy());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cost && !(modifier.type instanceof RememberMoveModifierType)) {
|
|
||||||
if (result) {
|
|
||||||
if (!Overrides.WAIVE_ROLL_FEE_OVERRIDE) {
|
|
||||||
globalScene.money -= cost;
|
|
||||||
globalScene.updateMoneyText();
|
|
||||||
globalScene.animateMoneyChanged(false);
|
|
||||||
}
|
|
||||||
globalScene.playSound("se/buy");
|
|
||||||
(globalScene.ui.getHandler() as ModifierSelectUiHandler).updateCostText();
|
|
||||||
} else {
|
|
||||||
globalScene.ui.playError();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
globalScene.ui.clearText();
|
|
||||||
globalScene.ui.setMode(UiMode.MESSAGE);
|
|
||||||
super.end();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (modifierType! instanceof PokemonModifierType) {
|
|
||||||
//TODO: is the bang correct?
|
|
||||||
if (modifierType instanceof FusePokemonModifierType) {
|
|
||||||
globalScene.ui.setModeWithoutClear(
|
|
||||||
UiMode.PARTY,
|
|
||||||
PartyUiMode.SPLICE,
|
|
||||||
-1,
|
|
||||||
(fromSlotIndex: number, spliceSlotIndex: number) => {
|
|
||||||
if (
|
|
||||||
spliceSlotIndex !== undefined &&
|
|
||||||
fromSlotIndex < 6 &&
|
|
||||||
spliceSlotIndex < 6 &&
|
|
||||||
fromSlotIndex !== spliceSlotIndex
|
|
||||||
) {
|
|
||||||
globalScene.ui.setMode(UiMode.MODIFIER_SELECT, this.isPlayer()).then(() => {
|
|
||||||
const modifier = modifierType.newModifier(party[fromSlotIndex], party[spliceSlotIndex])!; //TODO: is the bang correct?
|
|
||||||
applyModifier(modifier, true);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
globalScene.ui.setMode(
|
|
||||||
UiMode.MODIFIER_SELECT,
|
|
||||||
this.isPlayer(),
|
|
||||||
this.typeOptions,
|
|
||||||
modifierSelectCallback,
|
|
||||||
this.getRerollCost(globalScene.lockModifierTiers),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
modifierType.selectFilter,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
const pokemonModifierType = modifierType as PokemonModifierType;
|
|
||||||
const isMoveModifier = modifierType instanceof PokemonMoveModifierType;
|
|
||||||
const isTmModifier = modifierType instanceof TmModifierType;
|
|
||||||
const isRememberMoveModifier = modifierType instanceof RememberMoveModifierType;
|
|
||||||
const isPpRestoreModifier =
|
|
||||||
modifierType instanceof PokemonPpRestoreModifierType || modifierType instanceof PokemonPpUpModifierType;
|
|
||||||
const partyUiMode = isMoveModifier
|
|
||||||
? PartyUiMode.MOVE_MODIFIER
|
|
||||||
: isTmModifier
|
|
||||||
? PartyUiMode.TM_MODIFIER
|
|
||||||
: isRememberMoveModifier
|
|
||||||
? PartyUiMode.REMEMBER_MOVE_MODIFIER
|
|
||||||
: PartyUiMode.MODIFIER;
|
|
||||||
const tmMoveId = isTmModifier ? (modifierType as TmModifierType).moveId : undefined;
|
|
||||||
globalScene.ui.setModeWithoutClear(
|
|
||||||
UiMode.PARTY,
|
|
||||||
partyUiMode,
|
|
||||||
-1,
|
|
||||||
(slotIndex: number, option: PartyOption) => {
|
|
||||||
if (slotIndex < 6) {
|
|
||||||
globalScene.ui.setMode(UiMode.MODIFIER_SELECT, this.isPlayer()).then(() => {
|
|
||||||
const modifier = !isMoveModifier
|
|
||||||
? !isRememberMoveModifier
|
|
||||||
? modifierType.newModifier(party[slotIndex])
|
|
||||||
: modifierType.newModifier(party[slotIndex], option as number)
|
|
||||||
: modifierType.newModifier(party[slotIndex], option - PartyOption.MOVE_1);
|
|
||||||
applyModifier(modifier!, true); // TODO: is the bang correct?
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
globalScene.ui.setMode(
|
|
||||||
UiMode.MODIFIER_SELECT,
|
|
||||||
this.isPlayer(),
|
|
||||||
this.typeOptions,
|
|
||||||
modifierSelectCallback,
|
|
||||||
this.getRerollCost(globalScene.lockModifierTiers),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
pokemonModifierType.selectFilter,
|
|
||||||
modifierType instanceof PokemonMoveModifierType
|
|
||||||
? (modifierType as PokemonMoveModifierType).moveSelectFilter
|
|
||||||
: undefined,
|
|
||||||
tmMoveId,
|
|
||||||
isPpRestoreModifier,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
applyModifier(modifierType!.newModifier()!); // TODO: is the bang correct?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return !cost!; // TODO: is the bang correct?
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.resetModifierSelect(modifierSelectCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pick a modifier from among the rewards and apply it
|
||||||
|
private selectRewardModifierOption(cursor: number, modifierSelectCallback: ModifierSelectCallback): boolean {
|
||||||
|
if (this.typeOptions.length === 0) {
|
||||||
|
globalScene.ui.clearText();
|
||||||
|
globalScene.ui.setMode(UiMode.MESSAGE);
|
||||||
|
super.end();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const modifierType = this.typeOptions[cursor].type;
|
||||||
|
return this.applyChosenModifier(modifierType, 0, modifierSelectCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pick a modifier from the shop and apply it
|
||||||
|
private selectShopModifierOption(
|
||||||
|
rowCursor: number,
|
||||||
|
cursor: number,
|
||||||
|
modifierSelectCallback: ModifierSelectCallback,
|
||||||
|
): boolean {
|
||||||
|
const shopOptions = getPlayerShopModifierTypeOptionsForWave(
|
||||||
|
globalScene.currentBattle.waveIndex,
|
||||||
|
globalScene.getWaveMoneyAmount(1),
|
||||||
|
);
|
||||||
|
const shopOption =
|
||||||
|
shopOptions[
|
||||||
|
rowCursor > 2 || shopOptions.length <= SHOP_OPTIONS_ROW_LIMIT ? cursor : cursor + SHOP_OPTIONS_ROW_LIMIT
|
||||||
|
];
|
||||||
|
const modifierType = shopOption.type;
|
||||||
|
// Apply Black Sludge to healing item cost
|
||||||
|
const healingItemCost = new NumberHolder(shopOption.cost);
|
||||||
|
globalScene.applyModifier(HealShopCostModifier, true, healingItemCost);
|
||||||
|
const cost = healingItemCost.value;
|
||||||
|
|
||||||
|
if (globalScene.money < cost && !Overrides.WAIVE_ROLL_FEE_OVERRIDE) {
|
||||||
|
globalScene.ui.playError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.applyChosenModifier(modifierType, cost, modifierSelectCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply a chosen modifier: do an effect or open the party menu
|
||||||
|
private applyChosenModifier(
|
||||||
|
modifierType: ModifierType,
|
||||||
|
cost: number,
|
||||||
|
modifierSelectCallback: ModifierSelectCallback,
|
||||||
|
): boolean {
|
||||||
|
if (modifierType instanceof PokemonModifierType) {
|
||||||
|
if (modifierType instanceof FusePokemonModifierType) {
|
||||||
|
this.openFusionMenu(modifierType, cost, modifierSelectCallback);
|
||||||
|
} else {
|
||||||
|
this.openModifierMenu(modifierType, cost, modifierSelectCallback);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.applyModifier(modifierType.newModifier()!);
|
||||||
|
}
|
||||||
|
return !cost;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reroll rewards
|
||||||
|
private rerollModifiers() {
|
||||||
|
const rerollCost = this.getRerollCost(globalScene.lockModifierTiers);
|
||||||
|
if (rerollCost < 0 || globalScene.money < rerollCost) {
|
||||||
|
globalScene.ui.playError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
globalScene.reroll = true;
|
||||||
|
globalScene.phaseManager.unshiftNew(
|
||||||
|
"SelectModifierPhase",
|
||||||
|
this.rerollCount + 1,
|
||||||
|
this.typeOptions.map(o => o.type?.tier).filter(t => t !== undefined) as ModifierTier[],
|
||||||
|
);
|
||||||
|
globalScene.ui.clearText();
|
||||||
|
globalScene.ui.setMode(UiMode.MESSAGE).then(() => super.end());
|
||||||
|
if (!Overrides.WAIVE_ROLL_FEE_OVERRIDE) {
|
||||||
|
globalScene.money -= rerollCost;
|
||||||
|
globalScene.updateMoneyText();
|
||||||
|
globalScene.animateMoneyChanged(false);
|
||||||
|
}
|
||||||
|
globalScene.playSound("se/buy");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer modifiers among party pokemon
|
||||||
|
private openModifierTransferScreen(modifierSelectCallback: ModifierSelectCallback) {
|
||||||
|
const party = globalScene.getPlayerParty();
|
||||||
|
globalScene.ui.setModeWithoutClear(
|
||||||
|
UiMode.PARTY,
|
||||||
|
PartyUiMode.MODIFIER_TRANSFER,
|
||||||
|
-1,
|
||||||
|
(fromSlotIndex: number, itemIndex: number, itemQuantity: number, toSlotIndex: number) => {
|
||||||
|
if (
|
||||||
|
toSlotIndex !== undefined &&
|
||||||
|
fromSlotIndex < 6 &&
|
||||||
|
toSlotIndex < 6 &&
|
||||||
|
fromSlotIndex !== toSlotIndex &&
|
||||||
|
itemIndex > -1
|
||||||
|
) {
|
||||||
|
const itemModifiers = globalScene.findModifiers(
|
||||||
|
m => m instanceof PokemonHeldItemModifier && m.isTransferable && m.pokemonId === party[fromSlotIndex].id,
|
||||||
|
) as PokemonHeldItemModifier[];
|
||||||
|
const itemModifier = itemModifiers[itemIndex];
|
||||||
|
globalScene.tryTransferHeldItemModifier(
|
||||||
|
itemModifier,
|
||||||
|
party[toSlotIndex],
|
||||||
|
true,
|
||||||
|
itemQuantity,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.resetModifierSelect(modifierSelectCallback);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
PartyUiHandler.FilterItemMaxStacks,
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle reroll lock
|
||||||
|
private toggleRerollLock() {
|
||||||
|
const rerollCost = this.getRerollCost(globalScene.lockModifierTiers);
|
||||||
|
if (rerollCost < 0) {
|
||||||
|
// Reroll lock button is also disabled when reroll is disabled
|
||||||
|
globalScene.ui.playError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
globalScene.lockModifierTiers = !globalScene.lockModifierTiers;
|
||||||
|
const uiHandler = globalScene.ui.getHandler() as ModifierSelectUiHandler;
|
||||||
|
uiHandler.setRerollCost(this.getRerollCost(globalScene.lockModifierTiers));
|
||||||
|
uiHandler.updateLockRaritiesText();
|
||||||
|
uiHandler.updateRerollCostText();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Applies the effects of the chosen modifier
|
||||||
|
private applyModifier(modifier: Modifier, cost = 0, playSound = false): void {
|
||||||
|
const result = globalScene.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) {
|
||||||
|
globalScene.phaseManager.unshiftPhase(this.copy());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cost && !(modifier.type instanceof RememberMoveModifierType)) {
|
||||||
|
if (result) {
|
||||||
|
if (!Overrides.WAIVE_ROLL_FEE_OVERRIDE) {
|
||||||
|
globalScene.money -= cost;
|
||||||
|
globalScene.updateMoneyText();
|
||||||
|
globalScene.animateMoneyChanged(false);
|
||||||
|
}
|
||||||
|
globalScene.playSound("se/buy");
|
||||||
|
(globalScene.ui.getHandler() as ModifierSelectUiHandler).updateCostText();
|
||||||
|
} else {
|
||||||
|
globalScene.ui.playError();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
globalScene.ui.clearText();
|
||||||
|
globalScene.ui.setMode(UiMode.MESSAGE);
|
||||||
|
super.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Opens the party menu specifically for fusions
|
||||||
|
private openFusionMenu(
|
||||||
|
modifierType: PokemonModifierType,
|
||||||
|
cost: number,
|
||||||
|
modifierSelectCallback: ModifierSelectCallback,
|
||||||
|
): void {
|
||||||
|
const party = globalScene.getPlayerParty();
|
||||||
|
globalScene.ui.setModeWithoutClear(
|
||||||
|
UiMode.PARTY,
|
||||||
|
PartyUiMode.SPLICE,
|
||||||
|
-1,
|
||||||
|
(fromSlotIndex: number, spliceSlotIndex: number) => {
|
||||||
|
if (
|
||||||
|
spliceSlotIndex !== undefined &&
|
||||||
|
fromSlotIndex < 6 &&
|
||||||
|
spliceSlotIndex < 6 &&
|
||||||
|
fromSlotIndex !== spliceSlotIndex
|
||||||
|
) {
|
||||||
|
globalScene.ui.setMode(UiMode.MODIFIER_SELECT, this.isPlayer()).then(() => {
|
||||||
|
const modifier = modifierType.newModifier(party[fromSlotIndex], party[spliceSlotIndex])!; //TODO: is the bang correct?
|
||||||
|
this.applyModifier(modifier, cost, true);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.resetModifierSelect(modifierSelectCallback);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifierType.selectFilter,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Opens the party menu to apply one of various modifiers
|
||||||
|
private openModifierMenu(
|
||||||
|
modifierType: PokemonModifierType,
|
||||||
|
cost: number,
|
||||||
|
modifierSelectCallback: ModifierSelectCallback,
|
||||||
|
): void {
|
||||||
|
const party = globalScene.getPlayerParty();
|
||||||
|
const pokemonModifierType = modifierType as PokemonModifierType;
|
||||||
|
const isMoveModifier = modifierType instanceof PokemonMoveModifierType;
|
||||||
|
const isTmModifier = modifierType instanceof TmModifierType;
|
||||||
|
const isRememberMoveModifier = modifierType instanceof RememberMoveModifierType;
|
||||||
|
const isPpRestoreModifier =
|
||||||
|
modifierType instanceof PokemonPpRestoreModifierType || modifierType instanceof PokemonPpUpModifierType;
|
||||||
|
const partyUiMode = isMoveModifier
|
||||||
|
? PartyUiMode.MOVE_MODIFIER
|
||||||
|
: isTmModifier
|
||||||
|
? PartyUiMode.TM_MODIFIER
|
||||||
|
: isRememberMoveModifier
|
||||||
|
? PartyUiMode.REMEMBER_MOVE_MODIFIER
|
||||||
|
: PartyUiMode.MODIFIER;
|
||||||
|
const tmMoveId = isTmModifier ? (modifierType as TmModifierType).moveId : undefined;
|
||||||
|
globalScene.ui.setModeWithoutClear(
|
||||||
|
UiMode.PARTY,
|
||||||
|
partyUiMode,
|
||||||
|
-1,
|
||||||
|
(slotIndex: number, option: PartyOption) => {
|
||||||
|
if (slotIndex < 6) {
|
||||||
|
globalScene.ui.setMode(UiMode.MODIFIER_SELECT, this.isPlayer()).then(() => {
|
||||||
|
const modifier = !isMoveModifier
|
||||||
|
? !isRememberMoveModifier
|
||||||
|
? modifierType.newModifier(party[slotIndex])
|
||||||
|
: modifierType.newModifier(party[slotIndex], option as number)
|
||||||
|
: modifierType.newModifier(party[slotIndex], option - PartyOption.MOVE_1);
|
||||||
|
this.applyModifier(modifier!, cost, true); // TODO: is the bang correct?
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.resetModifierSelect(modifierSelectCallback);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
pokemonModifierType.selectFilter,
|
||||||
|
modifierType instanceof PokemonMoveModifierType
|
||||||
|
? (modifierType as PokemonMoveModifierType).moveSelectFilter
|
||||||
|
: undefined,
|
||||||
|
tmMoveId,
|
||||||
|
isPpRestoreModifier,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function that determines how many reward slots are available
|
||||||
|
private getModifierCount(): number {
|
||||||
|
const modifierCountHolder = new NumberHolder(3);
|
||||||
|
globalScene.applyModifiers(ExtraModifierModifier, true, modifierCountHolder);
|
||||||
|
globalScene.applyModifiers(TempExtraModifierModifier, true, modifierCountHolder);
|
||||||
|
|
||||||
|
// If custom modifiers are specified, overrides default item count
|
||||||
|
if (this.customModifierSettings) {
|
||||||
|
const newItemCount =
|
||||||
|
(this.customModifierSettings.guaranteedModifierTiers?.length ?? 0) +
|
||||||
|
(this.customModifierSettings.guaranteedModifierTypeOptions?.length ?? 0) +
|
||||||
|
(this.customModifierSettings.guaranteedModifierTypeFuncs?.length ?? 0);
|
||||||
|
if (this.customModifierSettings.fillRemaining) {
|
||||||
|
const originalCount = modifierCountHolder.value;
|
||||||
|
modifierCountHolder.value = originalCount > newItemCount ? originalCount : newItemCount;
|
||||||
|
} else {
|
||||||
|
modifierCountHolder.value = newItemCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return modifierCountHolder.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function that resets the reward selection screen,
|
||||||
|
// e.g. after pressing cancel in the party ui or while learning a move
|
||||||
|
private resetModifierSelect(modifierSelectCallback: ModifierSelectCallback) {
|
||||||
globalScene.ui.setMode(
|
globalScene.ui.setMode(
|
||||||
UiMode.MODIFIER_SELECT,
|
UiMode.MODIFIER_SELECT,
|
||||||
this.isPlayer(),
|
this.isPlayer(),
|
||||||
|
@ -25,7 +25,7 @@ describe("Abilities - Dancer", () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
game = new GameManager(phaserGame);
|
game = new GameManager(phaserGame);
|
||||||
game.override.battleStyle("double");
|
game.override.battleStyle("double").enemyAbility(AbilityId.BALL_FETCH);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Reference Link: https://bulbapedia.bulbagarden.net/wiki/Dancer_(Ability)
|
// Reference Link: https://bulbapedia.bulbagarden.net/wiki/Dancer_(Ability)
|
||||||
|
@ -73,9 +73,38 @@ describe("Abilities - Gorilla Tactics", () => {
|
|||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
|
|
||||||
game.move.select(MoveId.TACKLE);
|
game.move.select(MoveId.TACKLE);
|
||||||
|
await game.move.forceEnemyMove(MoveId.SPLASH); //prevent protect from being used by the enemy
|
||||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||||
|
|
||||||
await game.phaseInterceptor.to("MoveEndPhase");
|
await game.phaseInterceptor.to("MoveEndPhase");
|
||||||
expect(darmanitan.hp).toBeLessThan(darmanitan.getMaxHp());
|
expect(darmanitan.hp).toBeLessThan(darmanitan.getMaxHp());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should activate when the opponenet protects", async () => {
|
||||||
|
await game.classicMode.startBattle([SpeciesId.GALAR_DARMANITAN]);
|
||||||
|
|
||||||
|
const darmanitan = game.field.getPlayerPokemon();
|
||||||
|
|
||||||
|
game.move.select(MoveId.TACKLE);
|
||||||
|
await game.move.selectEnemyMove(MoveId.PROTECT);
|
||||||
|
|
||||||
|
await game.toEndOfTurn();
|
||||||
|
expect(darmanitan.isMoveRestricted(MoveId.SPLASH)).toBe(true);
|
||||||
|
expect(darmanitan.isMoveRestricted(MoveId.TACKLE)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should activate when a move is succesfully executed but misses", async () => {
|
||||||
|
await game.classicMode.startBattle([SpeciesId.GALAR_DARMANITAN]);
|
||||||
|
|
||||||
|
const darmanitan = game.field.getPlayerPokemon();
|
||||||
|
|
||||||
|
game.move.select(MoveId.TACKLE);
|
||||||
|
await game.move.selectEnemyMove(MoveId.SPLASH);
|
||||||
|
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||||
|
await game.move.forceMiss();
|
||||||
|
await game.toEndOfTurn();
|
||||||
|
|
||||||
|
expect(darmanitan.isMoveRestricted(MoveId.SPLASH)).toBe(true);
|
||||||
|
expect(darmanitan.isMoveRestricted(MoveId.TACKLE)).toBe(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user