[Challenge] Inverse battle challenge (#3525)
* add inverse battle challenge. refactoring type.ts for inverse battle challenge * update type integer -> number * add inverse battle condition to thunder wave, conversion 2. * add inverse_battle test code, add checking gameMode in runToSummon not to overwrite gameMode to CLASSIC always * update startBattle with isClassicMode default = true * add inverse achievement * fix achv validation condition * remove unnecessary new line * update defaultWidth 160 -> 200 * update locales * fix korean translation * fix korean translation2 * Update src/locales/de/achv.ts Co-authored-by: Jannik Tappert <38758606+CodeTappert@users.noreply.github.com> * Update src/locales/de/challenges.ts Co-authored-by: Jannik Tappert <38758606+CodeTappert@users.noreply.github.com> * Update src/locales/de/challenges.ts Co-authored-by: Jannik Tappert <38758606+CodeTappert@users.noreply.github.com> * resize challenge description 96 -> 84 * update challenge select UI size. * revert font size to 84. update de translation * Update src/locales/fr/challenges.ts Co-authored-by: Lugiad' <adrien.grivel@hotmail.fr> * Update src/locales/fr/achv.ts Co-authored-by: Lugiad' <adrien.grivel@hotmail.fr> * Update src/locales/es/challenges.ts Co-authored-by: Asdar <asdargmng@gmail.com> * Update src/locales/fr/challenges.ts Co-authored-by: Lugiad' <adrien.grivel@hotmail.fr> * Update src/locales/fr/achv.ts Co-authored-by: Lugiad' <adrien.grivel@hotmail.fr> * Update src/locales/es/achv.ts Co-authored-by: Asdar <asdargmng@gmail.com> * Update src/locales/fr/achv.ts Co-authored-by: Lugiad' <adrien.grivel@hotmail.fr> * shrink de font size on achivement * set middle align to achv title * Update src/locales/zh_CN/achv.ts Co-authored-by: mercurius-00 <80205689+mercurius-00@users.noreply.github.com> * Update src/locales/zh_TW/achv.ts Co-authored-by: mercurius-00 <80205689+mercurius-00@users.noreply.github.com> * Update src/locales/zh_CN/challenges.ts Co-authored-by: mercurius-00 <80205689+mercurius-00@users.noreply.github.com> * Update src/locales/zh_TW/challenges.ts Co-authored-by: mercurius-00 <80205689+mercurius-00@users.noreply.github.com> * fix zh_TW ahiv.ts * fix import code on inverse battle test for updated phase * Update src/data/type.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * update requested changes * Update src/locales/pt_BR/achv.ts Co-authored-by: José Ricardo Fleury Oliveira <josefleury@discente.ufg.br> * Update src/locales/pt_BR/achv.ts Co-authored-by: José Ricardo Fleury Oliveira <josefleury@discente.ufg.br> * Update src/locales/pt_BR/challenges.ts Co-authored-by: José Ricardo Fleury Oliveira <josefleury@discente.ufg.br> * [draft] update inverse battle apply function * change the way how to use applyChallenge for inverse type * resolve confilct * fix test codes * remove unnecessary multiplier variable and break codes * update getTypeDamageMultiplier argument type from `number` to `Type` * Fix inverse types tests (#1) * Fix Inverse Battle tests * Add timeout parameter to tests * update requested changes * update requested changes * update requested changes2 * update comments * Update src/test/utils/helpers/challengeModeHelper.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Update src/test/utils/helpers/challengeModeHelper.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * fix mis pasted code * revert loadChallenge code for FreshStartChallenge * code refactoring * restore challenge.json lost translations * revert UI changes * revert unreverted newlines * Run History inclusion * requested changes from torranx * update WaterSuperEffectTypeMultiplierAttr for inverse battle matchup. * fix test code. adding flying press test code * update requested change from xavion3 * updated requested change from xavion 2 * update requested changes from xavion 3 * remove exception code which is not valid * attach partial mark to Freeze dry. requested by xavion * add missing game over phase code when we delete old phases.ts * fix test codes * merge conflict * fix achv condition * updated achv block condition. we don't want to change desc now * resolve conflict * Eternatus Moveset Tinkering * Cleaning it up --------- Co-authored-by: Jannik Tappert <38758606+CodeTappert@users.noreply.github.com> Co-authored-by: Lugiad' <adrien.grivel@hotmail.fr> Co-authored-by: Asdar <asdargmng@gmail.com> Co-authored-by: mercurius-00 <80205689+mercurius-00@users.noreply.github.com> Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> Co-authored-by: José Ricardo Fleury Oliveira <josefleury@discente.ufg.br> Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com> Co-authored-by: frutescens <info@laptop>
This commit is contained in:
parent
1e95068f14
commit
c112abbcd2
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
|
@ -55,6 +55,11 @@ export enum ChallengeType {
|
|||
* @see {@link Challenge.applyFixedBattle}
|
||||
*/
|
||||
FIXED_BATTLES,
|
||||
/**
|
||||
* Modifies the effectiveness of Type matchups in battle
|
||||
* @see {@linkcode Challenge.applyTypeEffectiveness}
|
||||
*/
|
||||
TYPE_EFFECTIVENESS,
|
||||
/**
|
||||
* Modifies what level the AI pokemon are. UNIMPLEMENTED.
|
||||
*/
|
||||
|
@ -327,6 +332,15 @@ export abstract class Challenge {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* An apply function for TYPE_EFFECTIVENESS challenges. Derived classes should alter this.
|
||||
* @param effectiveness {@linkcode Utils.NumberHolder} The current effectiveness of the move.
|
||||
* @returns Whether this function did anything.
|
||||
*/
|
||||
applyTypeEffectiveness(effectiveness: Utils.NumberHolder): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* An apply function for AI_LEVEL challenges. Derived classes should alter this.
|
||||
* @param level {@link Utils.IntegerHolder} The generated level.
|
||||
|
@ -651,10 +665,7 @@ export class FreshStartChallenge extends Challenge {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @overrides
|
||||
*/
|
||||
getDifficulty(): number {
|
||||
override getDifficulty(): number {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -666,6 +677,38 @@ export class FreshStartChallenge extends Challenge {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements an inverse battle challenge.
|
||||
*/
|
||||
export class InverseBattleChallenge extends Challenge {
|
||||
constructor() {
|
||||
super(Challenges.INVERSE_BATTLE, 1);
|
||||
}
|
||||
|
||||
static loadChallenge(source: InverseBattleChallenge | any): InverseBattleChallenge {
|
||||
const newChallenge = new InverseBattleChallenge();
|
||||
newChallenge.value = source.value;
|
||||
newChallenge.severity = source.severity;
|
||||
return newChallenge;
|
||||
}
|
||||
|
||||
override getDifficulty(): number {
|
||||
return 0;
|
||||
}
|
||||
|
||||
applyTypeEffectiveness(effectiveness: Utils.NumberHolder): boolean {
|
||||
if (effectiveness.value < 1) {
|
||||
effectiveness.value = 2;
|
||||
return true;
|
||||
} else if (effectiveness.value > 1) {
|
||||
effectiveness.value = 0.5;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lowers the amount of starter points available.
|
||||
*/
|
||||
|
@ -785,6 +828,14 @@ export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType
|
|||
* @returns True if any challenge was successfully applied.
|
||||
*/
|
||||
export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.FIXED_BATTLES, waveIndex: Number, battleConfig: FixedBattleConfig): boolean;
|
||||
/**
|
||||
* Apply all challenges that modify type effectiveness.
|
||||
* @param gameMode {@linkcode GameMode} The current gameMode
|
||||
* @param challengeType {@linkcode ChallengeType} ChallengeType.TYPE_EFFECTIVENESS
|
||||
* @param effectiveness {@linkcode Utils.NumberHolder} The current effectiveness of the move.
|
||||
* @returns True if any challenge was successfully applied.
|
||||
*/
|
||||
export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.TYPE_EFFECTIVENESS, effectiveness: Utils.NumberHolder): boolean;
|
||||
/**
|
||||
* Apply all challenges that modify what level AI are.
|
||||
* @param gameMode {@link GameMode} The current gameMode
|
||||
|
@ -866,6 +917,9 @@ export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType
|
|||
case ChallengeType.FIXED_BATTLES:
|
||||
ret ||= c.applyFixedBattle(args[0], args[1]);
|
||||
break;
|
||||
case ChallengeType.TYPE_EFFECTIVENESS:
|
||||
ret ||= c.applyTypeEffectiveness(args[0]);
|
||||
break;
|
||||
case ChallengeType.AI_LEVEL:
|
||||
ret ||= c.applyLevelChange(args[0], args[1], args[2], args[3]);
|
||||
break;
|
||||
|
@ -907,6 +961,8 @@ export function copyChallenge(source: Challenge | any): Challenge {
|
|||
return LowerStarterPointsChallenge.loadChallenge(source);
|
||||
case Challenges.FRESH_START:
|
||||
return FreshStartChallenge.loadChallenge(source);
|
||||
case Challenges.INVERSE_BATTLE:
|
||||
return InverseBattleChallenge.loadChallenge(source);
|
||||
}
|
||||
throw new Error("Unknown challenge copied");
|
||||
}
|
||||
|
@ -918,5 +974,6 @@ export function initChallenges() {
|
|||
new SingleGenerationChallenge(),
|
||||
new SingleTypeChallenge(),
|
||||
new FreshStartChallenge(),
|
||||
new InverseBattleChallenge(),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import { EncoreTag, GulpMissileTag, HelpingHandTag, SemiInvulnerableTag, ShellTr
|
|||
import { getPokemonNameWithAffix } from "../messages";
|
||||
import Pokemon, { AttackMoveResult, EnemyPokemon, HitResult, MoveResult, PlayerPokemon, PokemonMove, TurnMove } from "../field/pokemon";
|
||||
import { StatusEffect, getStatusEffectHealText, isNonVolatileStatusEffect, getNonVolatileStatusEffects } from "./status-effect";
|
||||
import { getTypeResistances, Type } from "./type";
|
||||
import { getTypeDamageMultiplier, Type } from "./type";
|
||||
import { Constructor } from "#app/utils";
|
||||
import * as Utils from "../utils";
|
||||
import { WeatherType } from "./weather";
|
||||
|
@ -37,6 +37,9 @@ import { StatChangePhase } from "#app/phases/stat-change-phase";
|
|||
import { SwitchPhase } from "#app/phases/switch-phase";
|
||||
import { SwitchSummonPhase } from "#app/phases/switch-summon-phase";
|
||||
import { SpeciesFormChangeRevertWeatherFormTrigger } from "./pokemon-forms";
|
||||
import { NumberHolder } from "#app/utils";
|
||||
import { GameMode } from "#app/game-mode";
|
||||
import { applyChallenges, ChallengeType } from "./challenge";
|
||||
|
||||
export enum MoveCategory {
|
||||
PHYSICAL,
|
||||
|
@ -4180,8 +4183,12 @@ export class WaterSuperEffectTypeMultiplierAttr extends VariableMoveTypeMultipli
|
|||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
const multiplier = args[0] as Utils.NumberHolder;
|
||||
if (target.isOfType(Type.WATER)) {
|
||||
multiplier.value *= 4; // Increased twice because initial reduction against water
|
||||
return true;
|
||||
const effectivenessAgainstWater = new Utils.NumberHolder(getTypeDamageMultiplier(move.type, Type.WATER));
|
||||
applyChallenges(user.scene.gameMode, ChallengeType.TYPE_EFFECTIVENESS, effectivenessAgainstWater);
|
||||
if (effectivenessAgainstWater.value !== 0) {
|
||||
multiplier.value *= 2 / effectivenessAgainstWater.value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -6203,7 +6210,7 @@ export class ResistLastMoveTypeAttr extends MoveEffectAttr {
|
|||
return false;
|
||||
}
|
||||
const userTypes = user.getTypes();
|
||||
const validTypes = getTypeResistances(moveData.type).filter(t => !userTypes.includes(t)); // valid types are ones that are not already the user's types
|
||||
const validTypes = this.getTypeResistances(user.scene.gameMode, moveData.type).filter(t => !userTypes.includes(t)); // valid types are ones that are not already the user's types
|
||||
if (!validTypes.length) {
|
||||
return false;
|
||||
}
|
||||
|
@ -6215,6 +6222,25 @@ export class ResistLastMoveTypeAttr extends MoveEffectAttr {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the types resisting a given type. Used by Conversion 2
|
||||
* @returns An array populated with Types, or an empty array if no resistances exist (Unknown or Stellar type)
|
||||
*/
|
||||
getTypeResistances(gameMode: GameMode, type: number): Type[] {
|
||||
const typeResistances: Type[] = [];
|
||||
|
||||
for (let i = 0; i < Object.keys(Type).length; i++) {
|
||||
const multiplier = new NumberHolder(1);
|
||||
multiplier.value = getTypeDamageMultiplier(type, i);
|
||||
applyChallenges(gameMode, ChallengeType.TYPE_EFFECTIVENESS, multiplier);
|
||||
if (multiplier.value < 1) {
|
||||
typeResistances.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
return typeResistances;
|
||||
}
|
||||
|
||||
getCondition(): MoveConditionFunc {
|
||||
return (user, target, move) => {
|
||||
const moveHistory = target.getLastXMoves();
|
||||
|
@ -7940,7 +7966,8 @@ export function initMoves() {
|
|||
.target(MoveTarget.ALL_NEAR_OTHERS),
|
||||
new AttackMove(Moves.FREEZE_DRY, Type.ICE, MoveCategory.SPECIAL, 70, 100, 20, 10, 0, 6)
|
||||
.attr(StatusEffectAttr, StatusEffect.FREEZE)
|
||||
.attr(WaterSuperEffectTypeMultiplierAttr),
|
||||
.attr(WaterSuperEffectTypeMultiplierAttr)
|
||||
.partial(), // This currently just multiplies the move's power instead of changing its effectiveness. It also doesn't account for abilities that modify type effectiveness such as tera shell.
|
||||
new AttackMove(Moves.DISARMING_VOICE, Type.FAIRY, MoveCategory.SPECIAL, 40, -1, 15, -1, 0, 6)
|
||||
.soundBased()
|
||||
.target(MoveTarget.ALL_NEAR_ENEMIES),
|
||||
|
|
318
src/data/type.ts
318
src/data/type.ts
|
@ -23,7 +23,7 @@ export enum Type {
|
|||
|
||||
export type TypeDamageMultiplier = 0 | 0.125 | 0.25 | 0.5 | 1 | 2 | 4 | 8;
|
||||
|
||||
export function getTypeDamageMultiplier(attackType: integer, defType: integer): TypeDamageMultiplier {
|
||||
export function getTypeDamageMultiplier(attackType: Type, defType: Type): TypeDamageMultiplier {
|
||||
if (attackType === Type.UNKNOWN || defType === Type.UNKNOWN) {
|
||||
return 1;
|
||||
}
|
||||
|
@ -33,26 +33,10 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||
switch (attackType) {
|
||||
case Type.FIGHTING:
|
||||
return 2;
|
||||
case Type.NORMAL:
|
||||
case Type.FLYING:
|
||||
case Type.POISON:
|
||||
case Type.GROUND:
|
||||
case Type.ROCK:
|
||||
case Type.BUG:
|
||||
case Type.STEEL:
|
||||
case Type.FIRE:
|
||||
case Type.WATER:
|
||||
case Type.GRASS:
|
||||
case Type.ELECTRIC:
|
||||
case Type.PSYCHIC:
|
||||
case Type.ICE:
|
||||
case Type.DRAGON:
|
||||
case Type.DARK:
|
||||
case Type.FAIRY:
|
||||
return 1;
|
||||
case Type.GHOST:
|
||||
default:
|
||||
return 0;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
case Type.FIGHTING:
|
||||
switch (attackType) {
|
||||
|
@ -60,25 +44,12 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||
case Type.PSYCHIC:
|
||||
case Type.FAIRY:
|
||||
return 2;
|
||||
case Type.NORMAL:
|
||||
case Type.FIGHTING:
|
||||
case Type.POISON:
|
||||
case Type.GROUND:
|
||||
case Type.GHOST:
|
||||
case Type.STEEL:
|
||||
case Type.FIRE:
|
||||
case Type.WATER:
|
||||
case Type.GRASS:
|
||||
case Type.ELECTRIC:
|
||||
case Type.ICE:
|
||||
case Type.DRAGON:
|
||||
return 1;
|
||||
case Type.ROCK:
|
||||
case Type.BUG:
|
||||
case Type.DARK:
|
||||
return 0.5;
|
||||
default:
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
case Type.FLYING:
|
||||
switch (attackType) {
|
||||
|
@ -86,43 +57,20 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||
case Type.ELECTRIC:
|
||||
case Type.ICE:
|
||||
return 2;
|
||||
case Type.NORMAL:
|
||||
case Type.FLYING:
|
||||
case Type.POISON:
|
||||
case Type.GHOST:
|
||||
case Type.STEEL:
|
||||
case Type.FIRE:
|
||||
case Type.WATER:
|
||||
case Type.PSYCHIC:
|
||||
case Type.DRAGON:
|
||||
case Type.DARK:
|
||||
case Type.FAIRY:
|
||||
return 1;
|
||||
case Type.FIGHTING:
|
||||
case Type.BUG:
|
||||
case Type.GRASS:
|
||||
return 0.5;
|
||||
case Type.GROUND:
|
||||
default:
|
||||
return 0;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
case Type.POISON:
|
||||
switch (attackType) {
|
||||
case Type.GROUND:
|
||||
case Type.PSYCHIC:
|
||||
return 2;
|
||||
case Type.NORMAL:
|
||||
case Type.FLYING:
|
||||
case Type.ROCK:
|
||||
case Type.GHOST:
|
||||
case Type.STEEL:
|
||||
case Type.FIRE:
|
||||
case Type.WATER:
|
||||
case Type.ELECTRIC:
|
||||
case Type.ICE:
|
||||
case Type.DRAGON:
|
||||
case Type.DARK:
|
||||
return 1;
|
||||
case Type.FIGHTING:
|
||||
case Type.POISON:
|
||||
case Type.BUG:
|
||||
|
@ -130,7 +78,7 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||
case Type.FAIRY:
|
||||
return 0.5;
|
||||
default:
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
case Type.GROUND:
|
||||
switch (attackType) {
|
||||
|
@ -138,25 +86,13 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||
case Type.GRASS:
|
||||
case Type.ICE:
|
||||
return 2;
|
||||
case Type.NORMAL:
|
||||
case Type.FIGHTING:
|
||||
case Type.FLYING:
|
||||
case Type.GROUND:
|
||||
case Type.BUG:
|
||||
case Type.GHOST:
|
||||
case Type.STEEL:
|
||||
case Type.FIRE:
|
||||
case Type.PSYCHIC:
|
||||
case Type.DRAGON:
|
||||
case Type.DARK:
|
||||
case Type.FAIRY:
|
||||
return 1;
|
||||
case Type.POISON:
|
||||
case Type.ROCK:
|
||||
return 0.5;
|
||||
case Type.ELECTRIC:
|
||||
default:
|
||||
return 0;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
case Type.ROCK:
|
||||
switch (attackType) {
|
||||
|
@ -166,23 +102,13 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||
case Type.WATER:
|
||||
case Type.GRASS:
|
||||
return 2;
|
||||
case Type.ROCK:
|
||||
case Type.BUG:
|
||||
case Type.GHOST:
|
||||
case Type.ELECTRIC:
|
||||
case Type.PSYCHIC:
|
||||
case Type.ICE:
|
||||
case Type.DRAGON:
|
||||
case Type.DARK:
|
||||
case Type.FAIRY:
|
||||
return 1;
|
||||
case Type.NORMAL:
|
||||
case Type.FLYING:
|
||||
case Type.POISON:
|
||||
case Type.FIRE:
|
||||
return 0.5;
|
||||
default:
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
case Type.BUG:
|
||||
switch (attackType) {
|
||||
|
@ -190,51 +116,26 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||
case Type.ROCK:
|
||||
case Type.FIRE:
|
||||
return 2;
|
||||
case Type.NORMAL:
|
||||
case Type.POISON:
|
||||
case Type.BUG:
|
||||
case Type.GHOST:
|
||||
case Type.STEEL:
|
||||
case Type.WATER:
|
||||
case Type.ELECTRIC:
|
||||
case Type.PSYCHIC:
|
||||
case Type.ICE:
|
||||
case Type.DRAGON:
|
||||
case Type.DARK:
|
||||
case Type.FAIRY:
|
||||
return 1;
|
||||
case Type.FIGHTING:
|
||||
case Type.GROUND:
|
||||
case Type.GRASS:
|
||||
return 0.5;
|
||||
default:
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
case Type.GHOST:
|
||||
switch (attackType) {
|
||||
case Type.GHOST:
|
||||
case Type.DARK:
|
||||
return 2;
|
||||
case Type.FLYING:
|
||||
case Type.GROUND:
|
||||
case Type.ROCK:
|
||||
case Type.STEEL:
|
||||
case Type.FIRE:
|
||||
case Type.WATER:
|
||||
case Type.GRASS:
|
||||
case Type.ELECTRIC:
|
||||
case Type.PSYCHIC:
|
||||
case Type.ICE:
|
||||
case Type.DRAGON:
|
||||
case Type.FAIRY:
|
||||
return 1;
|
||||
case Type.POISON:
|
||||
case Type.BUG:
|
||||
return 0.5;
|
||||
case Type.NORMAL:
|
||||
case Type.FIGHTING:
|
||||
default:
|
||||
return 0;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
case Type.STEEL:
|
||||
switch (attackType) {
|
||||
|
@ -242,11 +143,6 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||
case Type.GROUND:
|
||||
case Type.FIRE:
|
||||
return 2;
|
||||
case Type.GHOST:
|
||||
case Type.WATER:
|
||||
case Type.ELECTRIC:
|
||||
case Type.DARK:
|
||||
return 1;
|
||||
case Type.NORMAL:
|
||||
case Type.FLYING:
|
||||
case Type.ROCK:
|
||||
|
@ -259,8 +155,9 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||
case Type.FAIRY:
|
||||
return 0.5;
|
||||
case Type.POISON:
|
||||
default:
|
||||
return 0;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
case Type.FIRE:
|
||||
switch (attackType) {
|
||||
|
@ -268,16 +165,6 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||
case Type.ROCK:
|
||||
case Type.WATER:
|
||||
return 2;
|
||||
case Type.NORMAL:
|
||||
case Type.FIGHTING:
|
||||
case Type.FLYING:
|
||||
case Type.POISON:
|
||||
case Type.GHOST:
|
||||
case Type.ELECTRIC:
|
||||
case Type.PSYCHIC:
|
||||
case Type.DRAGON:
|
||||
case Type.DARK:
|
||||
return 1;
|
||||
case Type.BUG:
|
||||
case Type.STEEL:
|
||||
case Type.FIRE:
|
||||
|
@ -286,33 +173,20 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||
case Type.FAIRY:
|
||||
return 0.5;
|
||||
default:
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
case Type.WATER:
|
||||
switch (attackType) {
|
||||
case Type.GRASS:
|
||||
case Type.ELECTRIC:
|
||||
return 2;
|
||||
case Type.NORMAL:
|
||||
case Type.FIGHTING:
|
||||
case Type.FLYING:
|
||||
case Type.POISON:
|
||||
case Type.GROUND:
|
||||
case Type.ROCK:
|
||||
case Type.BUG:
|
||||
case Type.GHOST:
|
||||
case Type.PSYCHIC:
|
||||
case Type.DRAGON:
|
||||
case Type.DARK:
|
||||
case Type.FAIRY:
|
||||
return 1;
|
||||
case Type.STEEL:
|
||||
case Type.FIRE:
|
||||
case Type.WATER:
|
||||
case Type.ICE:
|
||||
return 0.5;
|
||||
default:
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
case Type.GRASS:
|
||||
switch (attackType) {
|
||||
|
@ -322,49 +196,24 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||
case Type.FIRE:
|
||||
case Type.ICE:
|
||||
return 2;
|
||||
case Type.NORMAL:
|
||||
case Type.FIGHTING:
|
||||
case Type.ROCK:
|
||||
case Type.GHOST:
|
||||
case Type.STEEL:
|
||||
case Type.PSYCHIC:
|
||||
case Type.DRAGON:
|
||||
case Type.DARK:
|
||||
case Type.FAIRY:
|
||||
return 1;
|
||||
case Type.GROUND:
|
||||
case Type.WATER:
|
||||
case Type.GRASS:
|
||||
case Type.ELECTRIC:
|
||||
return 0.5;
|
||||
default:
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
case Type.ELECTRIC:
|
||||
switch (attackType) {
|
||||
case Type.GROUND:
|
||||
return 2;
|
||||
case Type.NORMAL:
|
||||
case Type.FIGHTING:
|
||||
case Type.POISON:
|
||||
case Type.ROCK:
|
||||
case Type.BUG:
|
||||
case Type.GHOST:
|
||||
case Type.FIRE:
|
||||
case Type.WATER:
|
||||
case Type.GRASS:
|
||||
case Type.PSYCHIC:
|
||||
case Type.ICE:
|
||||
case Type.DRAGON:
|
||||
case Type.DARK:
|
||||
case Type.FAIRY:
|
||||
return 1;
|
||||
case Type.FLYING:
|
||||
case Type.STEEL:
|
||||
case Type.ELECTRIC:
|
||||
return 0.5;
|
||||
default:
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
case Type.PSYCHIC:
|
||||
switch (attackType) {
|
||||
|
@ -372,25 +221,11 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||
case Type.GHOST:
|
||||
case Type.DARK:
|
||||
return 2;
|
||||
case Type.NORMAL:
|
||||
case Type.FLYING:
|
||||
case Type.POISON:
|
||||
case Type.GROUND:
|
||||
case Type.ROCK:
|
||||
case Type.STEEL:
|
||||
case Type.FIRE:
|
||||
case Type.WATER:
|
||||
case Type.GRASS:
|
||||
case Type.ELECTRIC:
|
||||
case Type.ICE:
|
||||
case Type.DRAGON:
|
||||
case Type.FAIRY:
|
||||
return 1;
|
||||
case Type.FIGHTING:
|
||||
case Type.PSYCHIC:
|
||||
return 0.5;
|
||||
default:
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
case Type.ICE:
|
||||
switch (attackType) {
|
||||
|
@ -399,24 +234,10 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||
case Type.STEEL:
|
||||
case Type.FIRE:
|
||||
return 2;
|
||||
case Type.NORMAL:
|
||||
case Type.FLYING:
|
||||
case Type.POISON:
|
||||
case Type.GROUND:
|
||||
case Type.BUG:
|
||||
case Type.GHOST:
|
||||
case Type.WATER:
|
||||
case Type.GRASS:
|
||||
case Type.ELECTRIC:
|
||||
case Type.PSYCHIC:
|
||||
case Type.DRAGON:
|
||||
case Type.DARK:
|
||||
case Type.FAIRY:
|
||||
return 1;
|
||||
case Type.ICE:
|
||||
return 0.5;
|
||||
default:
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
case Type.DRAGON:
|
||||
switch (attackType) {
|
||||
|
@ -424,25 +245,13 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||
case Type.DRAGON:
|
||||
case Type.FAIRY:
|
||||
return 2;
|
||||
case Type.NORMAL:
|
||||
case Type.FIGHTING:
|
||||
case Type.FLYING:
|
||||
case Type.POISON:
|
||||
case Type.GROUND:
|
||||
case Type.ROCK:
|
||||
case Type.BUG:
|
||||
case Type.GHOST:
|
||||
case Type.STEEL:
|
||||
case Type.PSYCHIC:
|
||||
case Type.DARK:
|
||||
return 1;
|
||||
case Type.FIRE:
|
||||
case Type.WATER:
|
||||
case Type.GRASS:
|
||||
case Type.ELECTRIC:
|
||||
return 0.5;
|
||||
default:
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
case Type.DARK:
|
||||
switch (attackType) {
|
||||
|
@ -450,106 +259,33 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||
case Type.BUG:
|
||||
case Type.FAIRY:
|
||||
return 2;
|
||||
case Type.NORMAL:
|
||||
case Type.FLYING:
|
||||
case Type.POISON:
|
||||
case Type.GROUND:
|
||||
case Type.ROCK:
|
||||
case Type.STEEL:
|
||||
case Type.FIRE:
|
||||
case Type.WATER:
|
||||
case Type.GRASS:
|
||||
case Type.ELECTRIC:
|
||||
case Type.ICE:
|
||||
case Type.DRAGON:
|
||||
return 1;
|
||||
case Type.GHOST:
|
||||
case Type.DARK:
|
||||
return 0.5;
|
||||
case Type.PSYCHIC:
|
||||
default:
|
||||
return 0;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
case Type.FAIRY:
|
||||
switch (attackType) {
|
||||
case Type.POISON:
|
||||
case Type.STEEL:
|
||||
return 2;
|
||||
case Type.NORMAL:
|
||||
case Type.FLYING:
|
||||
case Type.GROUND:
|
||||
case Type.ROCK:
|
||||
case Type.GHOST:
|
||||
case Type.FIRE:
|
||||
case Type.WATER:
|
||||
case Type.GRASS:
|
||||
case Type.ELECTRIC:
|
||||
case Type.PSYCHIC:
|
||||
case Type.ICE:
|
||||
case Type.FAIRY:
|
||||
return 1;
|
||||
case Type.FIGHTING:
|
||||
case Type.BUG:
|
||||
case Type.DARK:
|
||||
return 0.5;
|
||||
case Type.DRAGON:
|
||||
default:
|
||||
return 0;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
case Type.STELLAR:
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the types resisting a given type
|
||||
* @returns An array populated with Types, or an empty array if no resistances exist (Unknown or Stellar type)
|
||||
*/
|
||||
export function getTypeResistances(type: number): Type[] {
|
||||
switch (type) {
|
||||
case Type.NORMAL:
|
||||
return [Type.ROCK, Type.STEEL, Type.GHOST];
|
||||
case Type.FIGHTING:
|
||||
return [Type.FLYING, Type.POISON, Type.BUG, Type.PSYCHIC, Type.FAIRY, Type.GHOST];
|
||||
case Type.FLYING:
|
||||
return [Type.ROCK, Type.ELECTRIC, Type.STEEL];
|
||||
case Type.POISON:
|
||||
return [Type.POISON, Type.GROUND, Type.ROCK, Type.GHOST, Type.STEEL];
|
||||
case Type.GROUND:
|
||||
return [Type.BUG, Type.GRASS, Type.FLYING];
|
||||
case Type.ROCK:
|
||||
return [Type.FIGHTING, Type.GROUND, Type.STEEL];
|
||||
case Type.BUG:
|
||||
return [Type.FIGHTING, Type.FLYING, Type.POISON, Type.GHOST, Type.STEEL, Type.FIRE, Type.FAIRY];
|
||||
case Type.GHOST:
|
||||
return [Type.DARK, Type.NORMAL];
|
||||
case Type.STEEL:
|
||||
return [Type.STEEL, Type.FIRE, Type.WATER, Type.ELECTRIC];
|
||||
case Type.FIRE:
|
||||
return [Type.ROCK, Type.FIRE, Type.WATER, Type.DRAGON];
|
||||
case Type.WATER:
|
||||
return [Type.WATER, Type.GRASS, Type.DRAGON];
|
||||
case Type.GRASS:
|
||||
return [Type.FLYING, Type.POISON, Type.BUG, Type.STEEL, Type.FIRE, Type.GRASS, Type.DRAGON];
|
||||
case Type.ELECTRIC:
|
||||
return [Type.GRASS, Type.ELECTRIC, Type.DRAGON, Type.GROUND];
|
||||
case Type.PSYCHIC:
|
||||
return [Type.STEEL, Type.PSYCHIC];
|
||||
case Type.ICE:
|
||||
return [Type.STEEL, Type.FIRE, Type.WATER, Type.ICE];
|
||||
case Type.DRAGON:
|
||||
return [Type.STEEL, Type.FAIRY];
|
||||
case Type.DARK:
|
||||
return [Type.FIGHTING, Type.DARK, Type.FAIRY];
|
||||
case Type.FAIRY:
|
||||
return [Type.POISON, Type.STEEL, Type.FIRE];
|
||||
case Type.UNKNOWN:
|
||||
case Type.STELLAR:
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,5 +3,6 @@ export enum Challenges {
|
|||
SINGLE_TYPE,
|
||||
LOWER_MAX_STARTER_COST,
|
||||
LOWER_STARTER_POINTS,
|
||||
FRESH_START
|
||||
FRESH_START,
|
||||
INVERSE_BATTLE,
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@ import { BerryType } from "#enums/berry-type";
|
|||
import { Biome } from "#enums/biome";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import { Challenges } from "#enums/challenges";
|
||||
import { getPokemonNameWithAffix } from "#app/messages.js";
|
||||
import { DamagePhase } from "#app/phases/damage-phase.js";
|
||||
import { FaintPhase } from "#app/phases/faint-phase.js";
|
||||
|
@ -1315,12 +1316,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return getTypeDamageMultiplier(moveType, defType);
|
||||
const multiplier = new Utils.NumberHolder(getTypeDamageMultiplier(moveType, defType));
|
||||
applyChallenges(this.scene.gameMode, ChallengeType.TYPE_EFFECTIVENESS, multiplier);
|
||||
return multiplier.value;
|
||||
}).reduce((acc, cur) => acc * cur, 1) as TypeDamageMultiplier;
|
||||
|
||||
const typeMultiplierAgainstFlying = new Utils.NumberHolder(getTypeDamageMultiplier(moveType, Type.FLYING));
|
||||
applyChallenges(this.scene.gameMode, ChallengeType.TYPE_EFFECTIVENESS, typeMultiplierAgainstFlying);
|
||||
// Handle strong winds lowering effectiveness of types super effective against pure flying
|
||||
if (!ignoreStrongWinds && arena.weather?.weatherType === WeatherType.STRONG_WINDS && !arena.weather.isEffectSuppressed(this.scene) && this.isOfType(Type.FLYING) && getTypeDamageMultiplier(moveType, Type.FLYING) === 2) {
|
||||
if (!ignoreStrongWinds && arena.weather?.weatherType === WeatherType.STRONG_WINDS && !arena.weather.isEffectSuppressed(this.scene) && this.isOfType(Type.FLYING) && typeMultiplierAgainstFlying.value === 2) {
|
||||
multiplier /= 2;
|
||||
if (!simulated) {
|
||||
this.scene.queueMessage(i18next.t("weather:strongWindsEffectMessage"));
|
||||
|
@ -3852,6 +3856,9 @@ export class EnemyPokemon extends Pokemon {
|
|||
new PokemonMove(Moves.FLAMETHROWER),
|
||||
new PokemonMove(Moves.COSMIC_POWER)
|
||||
];
|
||||
if (this.scene.gameMode.hasChallenge(Challenges.INVERSE_BATTLE)) {
|
||||
this.moveset[2] = new PokemonMove(Moves.THUNDERBOLT);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
super.generateAndPopulateMoveset();
|
||||
|
|
|
@ -269,5 +269,9 @@
|
|||
"FRESH_START": {
|
||||
"name": "Hussa, noch einmal von vorn!",
|
||||
"description": "Schließe die 'Neuanfang' Herausforderung ab"
|
||||
},
|
||||
"INVERSE_BATTLE": {
|
||||
"name": "Spieglein, Spieglein an der Wand",
|
||||
"description": "Schließe die 'Umkehrkampf' Herausforderung ab"
|
||||
}
|
||||
}
|
|
@ -25,5 +25,12 @@
|
|||
"desc": "Du kannst nur die ursprünglichen Starter verwenden, genau so, als hättest du gerade erst mit Pokérogue begonnen.",
|
||||
"value.0": "Aus",
|
||||
"value.1": "An"
|
||||
},
|
||||
"inverseBattle": {
|
||||
"name": "Umkehrkampf",
|
||||
"shortName": "Umkehrkampf",
|
||||
"desc": "Die Typen-Effektivität wird umgekehrt, und kein Typ ist gegen einen anderen Typ immun.\nDeaktiviert die Erfolge anderer Herausforderungen.",
|
||||
"value.0": "Aus",
|
||||
"value.1": "An"
|
||||
}
|
||||
}
|
|
@ -260,5 +260,9 @@
|
|||
"FRESH_START": {
|
||||
"name": "First Try!",
|
||||
"description": "Complete the Fresh Start challenge."
|
||||
},
|
||||
"INVERSE_BATTLE": {
|
||||
"name": "Mirror rorriM",
|
||||
"description": "Complete the Inverse Battle challenge.\n.egnellahc elttaB esrevnI eht etelpmoC"
|
||||
}
|
||||
}
|
|
@ -279,5 +279,9 @@
|
|||
"FRESH_START": {
|
||||
"name": "First Try!",
|
||||
"description": "Complete the Fresh Start challenge."
|
||||
},
|
||||
"INVERSE_BATTLE": {
|
||||
"name": "Mirror rorriM",
|
||||
"description": "Complete the Inverse Battle challenge.\n.egnellahc elttaB esrevnI eht etelpmoC"
|
||||
}
|
||||
}
|
|
@ -25,5 +25,12 @@
|
|||
"desc": "You can only use the original starters, and only as if you had just started PokéRogue.",
|
||||
"value.0": "Off",
|
||||
"value.1": "On"
|
||||
},
|
||||
"inverseBattle": {
|
||||
"name": "Inverse Battle",
|
||||
"shortName": "Inverse",
|
||||
"desc": "Type matchups are reversed and no type is immune to any other type.\nDisables other challenges' achievements.",
|
||||
"value.0": "Off",
|
||||
"value.1": "On"
|
||||
}
|
||||
}
|
|
@ -170,5 +170,9 @@
|
|||
"CLASSIC_VICTORY": {
|
||||
"name": "Imbatible",
|
||||
"description": "Completa el juego en modo clásico."
|
||||
},
|
||||
"INVERSE_BATTLE": {
|
||||
"name": "Espejo ojepsE",
|
||||
"description": "Completa el reto de Combate Inverso.\n.osrevnI etabmoC ed oter le atelpmoC"
|
||||
}
|
||||
}
|
|
@ -18,5 +18,12 @@
|
|||
"name": "Monotipo",
|
||||
"desc": "Solo puedes usar Pokémon with the {{type}} type.",
|
||||
"desc_default": "Solo puedes usar Pokémon del tipo elegido."
|
||||
},
|
||||
"inverseBattle": {
|
||||
"name": "Combate Inverso",
|
||||
"shortName": "Inverso",
|
||||
"desc": "La efectividad de los tipos es invertida. No hay inmunidades entre tipos.\nEste reto deshabilita logros de otros retos.",
|
||||
"value.0": "Desactivado",
|
||||
"value.1": "Activado"
|
||||
}
|
||||
}
|
|
@ -274,5 +274,9 @@
|
|||
"FRESH_START": {
|
||||
"name": "Du premier coup !",
|
||||
"description": "Terminer un challenge « Nouveau départ »."
|
||||
},
|
||||
"INVERSE_BATTLE": {
|
||||
"name": "La teuté à verlan",
|
||||
"description": "Terminer un challenge en Combat Inversé.\nMineter un lenjcha en Ba-con Versin."
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,5 +25,12 @@
|
|||
"desc": "Vous ne pouvez choisir que les starters de base du jeu, comme si vous le recommenciez.",
|
||||
"value.0": "Non",
|
||||
"value.1": "Oui"
|
||||
},
|
||||
"inverseBattle": {
|
||||
"name": "Combat Inversé",
|
||||
"shortName": "Inversé",
|
||||
"desc": "Les affinités de la table des types sont inversées et plus aucun type n’a d’immunité.\nDésactive les succès des autres challenges.",
|
||||
"value.0": "Non",
|
||||
"value.1": "Oui"
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@
|
|||
},
|
||||
"freshStart": {
|
||||
"name": "出直し",
|
||||
"shortName": "出直し",
|
||||
"desc": "ポケローグを 始めた ばかりの ような ままで ゲーム開始の 最初のパートナーしか 使えません",
|
||||
"value.0": "オフ",
|
||||
"value.1": "オン"
|
||||
|
|
|
@ -260,5 +260,9 @@
|
|||
"FRESH_START": {
|
||||
"name": "첫트!",
|
||||
"description": "새 출발 챌린지 모드 클리어."
|
||||
},
|
||||
"INVERSE_BATTLE": {
|
||||
"name": "상성 전문가(였던 것)",
|
||||
"description": "거꾸로 배틀 챌린지 모드 클리어."
|
||||
}
|
||||
}
|
|
@ -25,5 +25,12 @@
|
|||
"desc": "포켓로그를 처음 시작했던 때처럼 강화가 전혀 되지 않은 오리지널 스타팅 포켓몬만 고를 수 있습니다.",
|
||||
"value.0": "해제",
|
||||
"value.1": "설정"
|
||||
},
|
||||
"inverseBattle": {
|
||||
"name": "거꾸로 배틀",
|
||||
"shortName": "거꾸로",
|
||||
"desc": "타입 상성이 반대로 바뀌고 면역 타입은 약점 타입이 됩니다.\n설정 시 다른 챌린지 업적은 달성할 수 없습니다.",
|
||||
"value.0": "해제",
|
||||
"value.1": "설정"
|
||||
}
|
||||
}
|
|
@ -264,5 +264,9 @@
|
|||
"FRESH_START": {
|
||||
"name": "De Primeira!",
|
||||
"description": "Complete o desafio de novo começo."
|
||||
},
|
||||
"INVERSE_BATTLE": {
|
||||
"name": "A torre da derrotA",
|
||||
"description": "Complete o desafio da Batalha Inversa.\n.asrevnI ahlataB ad oifased o etelpmoC"
|
||||
}
|
||||
}
|
|
@ -25,5 +25,12 @@
|
|||
"desc": "Você só pode usar os iniciais originais, como se tivesse acabado de começar o PokéRogue.",
|
||||
"value.0": "Desligado",
|
||||
"value.1": "Ligado"
|
||||
},
|
||||
"inverseBattle": {
|
||||
"name": "Batalha Inversa",
|
||||
"shortName": "Inversa",
|
||||
"desc": "Fraquezas e resistências de tipos são invertidas e nenhum tipo é imune a outro tipo.\nDesativa as conquistas de outros desafios.",
|
||||
"value.0": "Desligado",
|
||||
"value.1": "Ligado"
|
||||
}
|
||||
}
|
|
@ -268,5 +268,9 @@
|
|||
"FRESH_START": {
|
||||
"name": "初次尝试!",
|
||||
"description": "完成初次尝试挑战"
|
||||
},
|
||||
"INVERSE_BATTLE": {
|
||||
"name": "镜子子镜",
|
||||
"description": "完成逆转之战挑战\n战挑战之转逆成完"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,5 +25,12 @@
|
|||
"desc": "你只能使用御三家,就像是你第一次玩宝可梦肉鸽一样。",
|
||||
"value.0": "关闭",
|
||||
"value.1": "开启"
|
||||
},
|
||||
"inverseBattle": {
|
||||
"name": "逆转之战",
|
||||
"shortName": "逆转之战",
|
||||
"desc": "属性相克关系被反转,且没有任何属性对其他属性免疫。\n禁用其他挑战的成就。",
|
||||
"value.0": "关闭",
|
||||
"value.1": "开启"
|
||||
}
|
||||
}
|
|
@ -252,5 +252,9 @@
|
|||
},
|
||||
"MONO_FAIRY": {
|
||||
"name": "林克,醒醒!"
|
||||
},
|
||||
"INVERSE_BATTLE": {
|
||||
"name": "鏡子子鏡",
|
||||
"description": "完成逆轉之戰挑戰\n戰挑戰之轉逆成完"
|
||||
}
|
||||
}
|
|
@ -19,5 +19,12 @@
|
|||
"name": "單屬性",
|
||||
"desc": "你只能使用{{type}}\n屬性的寶可夢",
|
||||
"desc_default": "你只能使用所選\n屬性的寶可夢"
|
||||
},
|
||||
"inverseBattle": {
|
||||
"name": "逆轉之戰",
|
||||
"shortName": "逆轉之戰",
|
||||
"desc": "屬性相克關系被反轉,且沒有任何屬性對其他屬性免疫。\n禁用其他挑戰的成就。",
|
||||
"value.0": "關閉",
|
||||
"value.1": "開啓"
|
||||
}
|
||||
}
|
|
@ -5,8 +5,9 @@ import { pokemonEvolutions } from "#app/data/pokemon-evolutions";
|
|||
import i18next from "i18next";
|
||||
import * as Utils from "../utils";
|
||||
import { PlayerGender } from "#enums/player-gender";
|
||||
import { Challenge, FreshStartChallenge, SingleGenerationChallenge, SingleTypeChallenge } from "#app/data/challenge.js";
|
||||
import { ConditionFn } from "#app/@types/common.js";
|
||||
import { Challenge, FreshStartChallenge, InverseBattleChallenge, SingleGenerationChallenge, SingleTypeChallenge } from "#app/data/challenge";
|
||||
import { Challenges } from "#app/enums/challenges";
|
||||
import { ConditionFn } from "#app/@types/common";
|
||||
|
||||
export enum AchvTier {
|
||||
COMMON,
|
||||
|
@ -137,8 +138,8 @@ export class ModifierAchv extends Achv {
|
|||
}
|
||||
|
||||
export class ChallengeAchv extends Achv {
|
||||
constructor(localizationKey: string, name: string, description: string, iconImage: string, score: integer, challengeFunc: (challenge: Challenge) => boolean) {
|
||||
super(localizationKey, name, description, iconImage, score, (_scene: BattleScene, args: any[]) => challengeFunc((args[0] as Challenge)));
|
||||
constructor(localizationKey: string, name: string, description: string, iconImage: string, score: integer, challengeFunc: (challenge: Challenge, scene: BattleScene) => boolean) {
|
||||
super(localizationKey, name, description, iconImage, score, (_scene: BattleScene, args: any[]) => challengeFunc(args[0] as Challenge, _scene));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -275,6 +276,8 @@ export function getAchievementDescription(localizationKey: string): string {
|
|||
return i18next.t("achv:MonoType.description", { context: genderStr, "type": i18next.t(`pokemonInfo:Type.${localizationKey.slice(5)}`) });
|
||||
case "FRESH_START":
|
||||
return i18next.t("achv:FRESH_START.description", { context: genderStr });
|
||||
case "INVERSE_BATTLE":
|
||||
return i18next.t("achv:INVERSE_BATTLE.description", { context: genderStr });
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
|
@ -323,34 +326,35 @@ export const achvs = {
|
|||
PERFECT_IVS: new Achv("PERFECT_IVS", "", "PERFECT_IVS.description", "blunder_policy", 100),
|
||||
CLASSIC_VICTORY: new Achv("CLASSIC_VICTORY", "", "CLASSIC_VICTORY.description", "relic_crown", 150),
|
||||
UNEVOLVED_CLASSIC_VICTORY: new Achv("UNEVOLVED_CLASSIC_VICTORY", "", "UNEVOLVED_CLASSIC_VICTORY.description", "eviolite", 175, c => c.getParty().some(p => p.getSpeciesForm(true).speciesId in pokemonEvolutions)),
|
||||
MONO_GEN_ONE_VICTORY: new ChallengeAchv("MONO_GEN_ONE", "", "MONO_GEN_ONE.description", "ribbon_gen1", 100, c => c instanceof SingleGenerationChallenge && c.value === 1),
|
||||
MONO_GEN_TWO_VICTORY: new ChallengeAchv("MONO_GEN_TWO", "", "MONO_GEN_TWO.description", "ribbon_gen2", 100, c => c instanceof SingleGenerationChallenge && c.value === 2),
|
||||
MONO_GEN_THREE_VICTORY: new ChallengeAchv("MONO_GEN_THREE", "", "MONO_GEN_THREE.description", "ribbon_gen3", 100, c => c instanceof SingleGenerationChallenge && c.value === 3),
|
||||
MONO_GEN_FOUR_VICTORY: new ChallengeAchv("MONO_GEN_FOUR", "", "MONO_GEN_FOUR.description", "ribbon_gen4", 100, c => c instanceof SingleGenerationChallenge && c.value === 4),
|
||||
MONO_GEN_FIVE_VICTORY: new ChallengeAchv("MONO_GEN_FIVE", "", "MONO_GEN_FIVE.description", "ribbon_gen5", 100, c => c instanceof SingleGenerationChallenge && c.value === 5),
|
||||
MONO_GEN_SIX_VICTORY: new ChallengeAchv("MONO_GEN_SIX", "", "MONO_GEN_SIX.description", "ribbon_gen6", 100, c => c instanceof SingleGenerationChallenge && c.value === 6),
|
||||
MONO_GEN_SEVEN_VICTORY: new ChallengeAchv("MONO_GEN_SEVEN", "", "MONO_GEN_SEVEN.description", "ribbon_gen7", 100, c => c instanceof SingleGenerationChallenge && c.value === 7),
|
||||
MONO_GEN_EIGHT_VICTORY: new ChallengeAchv("MONO_GEN_EIGHT", "", "MONO_GEN_EIGHT.description", "ribbon_gen8", 100, c => c instanceof SingleGenerationChallenge && c.value === 8),
|
||||
MONO_GEN_NINE_VICTORY: new ChallengeAchv("MONO_GEN_NINE", "", "MONO_GEN_NINE.description", "ribbon_gen9", 100, c => c instanceof SingleGenerationChallenge && c.value === 9),
|
||||
MONO_NORMAL: new ChallengeAchv("MONO_NORMAL", "", "MONO_NORMAL.description", "silk_scarf", 100, c => c instanceof SingleTypeChallenge && c.value === 1),
|
||||
MONO_FIGHTING: new ChallengeAchv("MONO_FIGHTING", "", "MONO_FIGHTING.description", "black_belt", 100, c => c instanceof SingleTypeChallenge && c.value === 2),
|
||||
MONO_FLYING: new ChallengeAchv("MONO_FLYING", "", "MONO_FLYING.description", "sharp_beak", 100, c => c instanceof SingleTypeChallenge && c.value === 3),
|
||||
MONO_POISON: new ChallengeAchv("MONO_POISON", "", "MONO_POISON.description", "poison_barb", 100, c => c instanceof SingleTypeChallenge && c.value === 4),
|
||||
MONO_GROUND: new ChallengeAchv("MONO_GROUND", "", "MONO_GROUND.description", "soft_sand", 100, c => c instanceof SingleTypeChallenge && c.value === 5),
|
||||
MONO_ROCK: new ChallengeAchv("MONO_ROCK", "", "MONO_ROCK.description", "hard_stone", 100, c => c instanceof SingleTypeChallenge && c.value === 6),
|
||||
MONO_BUG: new ChallengeAchv("MONO_BUG", "", "MONO_BUG.description", "silver_powder", 100, c => c instanceof SingleTypeChallenge && c.value === 7),
|
||||
MONO_GHOST: new ChallengeAchv("MONO_GHOST", "", "MONO_GHOST.description", "spell_tag", 100, c => c instanceof SingleTypeChallenge && c.value === 8),
|
||||
MONO_STEEL: new ChallengeAchv("MONO_STEEL", "", "MONO_STEEL.description", "metal_coat", 100, c => c instanceof SingleTypeChallenge && c.value === 9),
|
||||
MONO_FIRE: new ChallengeAchv("MONO_FIRE", "", "MONO_FIRE.description", "charcoal", 100, c => c instanceof SingleTypeChallenge && c.value === 10),
|
||||
MONO_WATER: new ChallengeAchv("MONO_WATER", "", "MONO_WATER.description", "mystic_water", 100, c => c instanceof SingleTypeChallenge && c.value === 11),
|
||||
MONO_GRASS: new ChallengeAchv("MONO_GRASS", "", "MONO_GRASS.description", "miracle_seed", 100, c => c instanceof SingleTypeChallenge && c.value === 12),
|
||||
MONO_ELECTRIC: new ChallengeAchv("MONO_ELECTRIC", "", "MONO_ELECTRIC.description", "magnet", 100, c => c instanceof SingleTypeChallenge && c.value === 13),
|
||||
MONO_PSYCHIC: new ChallengeAchv("MONO_PSYCHIC", "", "MONO_PSYCHIC.description", "twisted_spoon", 100, c => c instanceof SingleTypeChallenge && c.value === 14),
|
||||
MONO_ICE: new ChallengeAchv("MONO_ICE", "", "MONO_ICE.description", "never_melt_ice", 100, c => c instanceof SingleTypeChallenge && c.value === 15),
|
||||
MONO_DRAGON: new ChallengeAchv("MONO_DRAGON", "", "MONO_DRAGON.description", "dragon_fang", 100, c => c instanceof SingleTypeChallenge && c.value === 16),
|
||||
MONO_DARK: new ChallengeAchv("MONO_DARK", "", "MONO_DARK.description", "black_glasses", 100, c => c instanceof SingleTypeChallenge && c.value === 17),
|
||||
MONO_FAIRY: new ChallengeAchv("MONO_FAIRY", "", "MONO_FAIRY.description", "fairy_feather", 100, c => c instanceof SingleTypeChallenge && c.value === 18),
|
||||
FRESH_START: new ChallengeAchv("FRESH_START", "", "FRESH_START.description", "reviver_seed", 100, c => c instanceof FreshStartChallenge && c.value === 1),
|
||||
MONO_GEN_ONE_VICTORY: new ChallengeAchv("MONO_GEN_ONE", "", "MONO_GEN_ONE.description", "ribbon_gen1", 100, (c, scene) => c instanceof SingleGenerationChallenge && c.value === 1 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||
MONO_GEN_TWO_VICTORY: new ChallengeAchv("MONO_GEN_TWO", "", "MONO_GEN_TWO.description", "ribbon_gen2", 100, (c, scene) => c instanceof SingleGenerationChallenge && c.value === 2 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||
MONO_GEN_THREE_VICTORY: new ChallengeAchv("MONO_GEN_THREE", "", "MONO_GEN_THREE.description", "ribbon_gen3", 100, (c, scene) => c instanceof SingleGenerationChallenge && c.value === 3 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||
MONO_GEN_FOUR_VICTORY: new ChallengeAchv("MONO_GEN_FOUR", "", "MONO_GEN_FOUR.description", "ribbon_gen4", 100, (c, scene) => c instanceof SingleGenerationChallenge && c.value === 4 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||
MONO_GEN_FIVE_VICTORY: new ChallengeAchv("MONO_GEN_FIVE", "", "MONO_GEN_FIVE.description", "ribbon_gen5", 100, (c, scene) => c instanceof SingleGenerationChallenge && c.value === 5 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||
MONO_GEN_SIX_VICTORY: new ChallengeAchv("MONO_GEN_SIX", "", "MONO_GEN_SIX.description", "ribbon_gen6", 100, (c, scene) => c instanceof SingleGenerationChallenge && c.value === 6 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||
MONO_GEN_SEVEN_VICTORY: new ChallengeAchv("MONO_GEN_SEVEN", "", "MONO_GEN_SEVEN.description", "ribbon_gen7", 100, (c, scene) => c instanceof SingleGenerationChallenge && c.value === 7 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||
MONO_GEN_EIGHT_VICTORY: new ChallengeAchv("MONO_GEN_EIGHT", "", "MONO_GEN_EIGHT.description", "ribbon_gen8", 100, (c, scene) => c instanceof SingleGenerationChallenge && c.value === 8 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||
MONO_GEN_NINE_VICTORY: new ChallengeAchv("MONO_GEN_NINE", "", "MONO_GEN_NINE.description", "ribbon_gen9", 100, (c, scene) => c instanceof SingleGenerationChallenge && c.value === 9 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||
MONO_NORMAL: new ChallengeAchv("MONO_NORMAL", "", "MONO_NORMAL.description", "silk_scarf", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 1 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||
MONO_FIGHTING: new ChallengeAchv("MONO_FIGHTING", "", "MONO_FIGHTING.description", "black_belt", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 2 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||
MONO_FLYING: new ChallengeAchv("MONO_FLYING", "", "MONO_FLYING.description", "sharp_beak", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 3 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||
MONO_POISON: new ChallengeAchv("MONO_POISON", "", "MONO_POISON.description", "poison_barb", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 4 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||
MONO_GROUND: new ChallengeAchv("MONO_GROUND", "", "MONO_GROUND.description", "soft_sand", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 5 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||
MONO_ROCK: new ChallengeAchv("MONO_ROCK", "", "MONO_ROCK.description", "hard_stone", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 6 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||
MONO_BUG: new ChallengeAchv("MONO_BUG", "", "MONO_BUG.description", "silver_powder", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 7 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||
MONO_GHOST: new ChallengeAchv("MONO_GHOST", "", "MONO_GHOST.description", "spell_tag", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 8 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||
MONO_STEEL: new ChallengeAchv("MONO_STEEL", "", "MONO_STEEL.description", "metal_coat", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 9 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||
MONO_FIRE: new ChallengeAchv("MONO_FIRE", "", "MONO_FIRE.description", "charcoal", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 10 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||
MONO_WATER: new ChallengeAchv("MONO_WATER", "", "MONO_WATER.description", "mystic_water", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 11 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||
MONO_GRASS: new ChallengeAchv("MONO_GRASS", "", "MONO_GRASS.description", "miracle_seed", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 12 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||
MONO_ELECTRIC: new ChallengeAchv("MONO_ELECTRIC", "", "MONO_ELECTRIC.description", "magnet", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 13 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||
MONO_PSYCHIC: new ChallengeAchv("MONO_PSYCHIC", "", "MONO_PSYCHIC.description", "twisted_spoon", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 14 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||
MONO_ICE: new ChallengeAchv("MONO_ICE", "", "MONO_ICE.description", "never_melt_ice", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 15 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||
MONO_DRAGON: new ChallengeAchv("MONO_DRAGON", "", "MONO_DRAGON.description", "dragon_fang", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 16 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||
MONO_DARK: new ChallengeAchv("MONO_DARK", "", "MONO_DARK.description", "black_glasses", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 17 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||
MONO_FAIRY: new ChallengeAchv("MONO_FAIRY", "", "MONO_FAIRY.description", "fairy_feather", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 18 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||
FRESH_START: new ChallengeAchv("FRESH_START", "", "FRESH_START.description", "reviver_seed", 100, (c, scene) => c instanceof FreshStartChallenge && c.value > 0 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||
INVERSE_BATTLE: new ChallengeAchv("INVERSE_BATTLE", "", "INVERSE_BATTLE.description", "inverse", 100, c => c instanceof InverseBattleChallenge && c.value > 0),
|
||||
};
|
||||
|
||||
export function initAchievements() {
|
||||
|
|
|
@ -0,0 +1,203 @@
|
|||
import { BattlerIndex } from "#app/battle";
|
||||
import { allMoves } from "#app/data/move";
|
||||
import { Type } from "#app/data/type";
|
||||
import { MoveEndPhase } from "#app/phases/move-end-phase";
|
||||
import { TurnEndPhase } from "#app/phases/turn-end-phase";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { ArenaTagType } from "#enums/arena-tag-type";
|
||||
import { Challenges } from "#enums/challenges";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import { StatusEffect } from "#enums/status-effect";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
const TIMEOUT = 20 * 1000;
|
||||
|
||||
describe("Inverse Battle", () => {
|
||||
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.challengeMode.addChallenge(Challenges.INVERSE_BATTLE, 1, 1);
|
||||
|
||||
game.override
|
||||
.battleType("single")
|
||||
.starterSpecies(Species.FEEBAS)
|
||||
.ability(Abilities.BALL_FETCH)
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyAbility(Abilities.BALL_FETCH);
|
||||
});
|
||||
|
||||
it("1. immune types are 2x effective - Thunderbolt against Ground Type", async () => {
|
||||
game.override.enemySpecies(Species.SANDSHREW);
|
||||
|
||||
await game.challengeMode.startBattle();
|
||||
|
||||
const player = game.scene.getPlayerPokemon()!;
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
|
||||
expect(enemy.getMoveEffectiveness(player, allMoves[Moves.THUNDERBOLT])).toBe(2);
|
||||
}, TIMEOUT);
|
||||
|
||||
it("2. 2x effective types are 0.5x effective - Thunderbolt against Flying Type", async () => {
|
||||
game.override.enemySpecies(Species.PIDGEY);
|
||||
|
||||
await game.challengeMode.startBattle();
|
||||
|
||||
const player = game.scene.getPlayerPokemon()!;
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
|
||||
expect(enemy.getMoveEffectiveness(player, allMoves[Moves.THUNDERBOLT])).toBe(0.5);
|
||||
}, TIMEOUT);
|
||||
|
||||
it("3. 0.5x effective types are 2x effective - Thunderbolt against Electric Type", async () => {
|
||||
game.override.enemySpecies(Species.CHIKORITA);
|
||||
|
||||
await game.challengeMode.startBattle();
|
||||
|
||||
const player = game.scene.getPlayerPokemon()!;
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
|
||||
expect(enemy.getMoveEffectiveness(player, allMoves[Moves.THUNDERBOLT])).toBe(2);
|
||||
}, TIMEOUT);
|
||||
|
||||
it("4. Stealth Rock follows the inverse matchups - Stealth Rock against Charizard deals 1/32 of max HP", async () => {
|
||||
game.scene.arena.addTag(ArenaTagType.STEALTH_ROCK, 1, Moves.STEALTH_ROCK, 0);
|
||||
game.override
|
||||
.enemySpecies(Species.CHARIZARD)
|
||||
.enemyLevel(100);
|
||||
|
||||
await game.challengeMode.startBattle();
|
||||
|
||||
const charizard = game.scene.getEnemyPokemon()!;
|
||||
|
||||
const maxHp = charizard.getMaxHp();
|
||||
const damage_prediction = Math.max(Math.round(charizard.getMaxHp() / 32), 1);
|
||||
console.log("Damage calcuation before round: " + charizard.getMaxHp() / 32);
|
||||
const currentHp = charizard.hp;
|
||||
const expectedHP = maxHp - damage_prediction;
|
||||
|
||||
console.log("Charizard's max HP: " + maxHp, "Damage: " + damage_prediction, "Current HP: " + currentHp, "Expected HP: " + expectedHP);
|
||||
expect(currentHp).toBeGreaterThan(maxHp * 31 / 32 - 1);
|
||||
}, TIMEOUT);
|
||||
|
||||
it("5. Freeze Dry is 2x effective against Water Type like other Ice type Move - Freeze Dry against Squirtle", async () => {
|
||||
game.override.enemySpecies(Species.SQUIRTLE);
|
||||
|
||||
await game.challengeMode.startBattle();
|
||||
|
||||
const player = game.scene.getPlayerPokemon()!;
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
|
||||
expect(enemy.getMoveEffectiveness(player, allMoves[Moves.FREEZE_DRY])).toBe(2);
|
||||
}, TIMEOUT);
|
||||
|
||||
it("6. Water Absorb should heal against water moves - Water Absorb against Water gun", async () => {
|
||||
game.override
|
||||
.moveset([Moves.WATER_GUN])
|
||||
.enemyAbility(Abilities.WATER_ABSORB);
|
||||
|
||||
await game.challengeMode.startBattle();
|
||||
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
enemy.hp = enemy.getMaxHp() - 1;
|
||||
game.move.select(Moves.WATER_GUN);
|
||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEndPhase);
|
||||
|
||||
expect(enemy.hp).toBe(enemy.getMaxHp());
|
||||
}, TIMEOUT);
|
||||
|
||||
it("7. Fire type does not get burned - Will-O-Wisp against Charmander", async () => {
|
||||
game.override
|
||||
.moveset([Moves.WILL_O_WISP])
|
||||
.enemySpecies(Species.CHARMANDER);
|
||||
|
||||
await game.challengeMode.startBattle();
|
||||
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
|
||||
game.move.select(Moves.WILL_O_WISP);
|
||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||
await game.move.forceHit();
|
||||
|
||||
await game.phaseInterceptor.to(MoveEndPhase);
|
||||
|
||||
expect(enemy.status?.effect).not.toBe(StatusEffect.BURN);
|
||||
}, TIMEOUT);
|
||||
|
||||
it("8. Electric type does not get paralyzed - Nuzzle against Pikachu", async () => {
|
||||
game.override
|
||||
.moveset([Moves.NUZZLE])
|
||||
.enemySpecies(Species.PIKACHU)
|
||||
.enemyLevel(50);
|
||||
|
||||
await game.challengeMode.startBattle();
|
||||
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
|
||||
game.move.select(Moves.NUZZLE);
|
||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEndPhase);
|
||||
|
||||
expect(enemy.status?.effect).not.toBe(StatusEffect.PARALYSIS);
|
||||
}, TIMEOUT);
|
||||
|
||||
|
||||
it("10. Anticipation should trigger on 2x effective moves - Anticipation against Thunderbolt", async () => {
|
||||
game.override
|
||||
.moveset([Moves.THUNDERBOLT])
|
||||
.enemySpecies(Species.SANDSHREW)
|
||||
.enemyAbility(Abilities.ANTICIPATION);
|
||||
|
||||
await game.challengeMode.startBattle();
|
||||
|
||||
expect(game.scene.getEnemyPokemon()?.summonData.abilitiesApplied[0]).toBe(Abilities.ANTICIPATION);
|
||||
}, TIMEOUT);
|
||||
|
||||
it("11. Conversion 2 should change the type to the resistive type - Conversion 2 against Dragonite", async () => {
|
||||
game.override
|
||||
.moveset([Moves.CONVERSION_2])
|
||||
.enemyMoveset([Moves.DRAGON_CLAW, Moves.DRAGON_CLAW, Moves.DRAGON_CLAW, Moves.DRAGON_CLAW]);
|
||||
|
||||
await game.challengeMode.startBattle();
|
||||
|
||||
const player = game.scene.getPlayerPokemon()!;
|
||||
|
||||
game.move.select(Moves.CONVERSION_2);
|
||||
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(player.getTypes()[0]).toBe(Type.DRAGON);
|
||||
}, TIMEOUT);
|
||||
|
||||
it("12. Flying Press should be 0.25x effective against Grass + Dark Type - Flying Press against Meowscarada", async () => {
|
||||
game.override
|
||||
.moveset([Moves.FLYING_PRESS])
|
||||
.enemySpecies(Species.MEOWSCARADA);
|
||||
|
||||
await game.challengeMode.startBattle();
|
||||
|
||||
const player = game.scene.getPlayerPokemon()!;
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
|
||||
expect(enemy.getMoveEffectiveness(player, allMoves[Moves.FLYING_PRESS])).toBe(0.25);
|
||||
}, TIMEOUT);
|
||||
});
|
|
@ -40,6 +40,7 @@ import fs from "fs";
|
|||
import { vi } from "vitest";
|
||||
import { ClassicModeHelper } from "./helpers/classicModeHelper";
|
||||
import { DailyModeHelper } from "./helpers/dailyModeHelper";
|
||||
import { ChallengeModeHelper } from "./helpers/challengeModeHelper";
|
||||
import { MoveHelper } from "./helpers/moveHelper";
|
||||
import { OverridesHelper } from "./helpers/overridesHelper";
|
||||
import { SettingsHelper } from "./helpers/settingsHelper";
|
||||
|
@ -57,6 +58,7 @@ export default class GameManager {
|
|||
public readonly move: MoveHelper;
|
||||
public readonly classicMode: ClassicModeHelper;
|
||||
public readonly dailyMode: DailyModeHelper;
|
||||
public readonly challengeMode: ChallengeModeHelper;
|
||||
public readonly settings: SettingsHelper;
|
||||
|
||||
/**
|
||||
|
@ -77,6 +79,7 @@ export default class GameManager {
|
|||
this.move = new MoveHelper(this);
|
||||
this.classicMode = new ClassicModeHelper(this);
|
||||
this.dailyMode = new DailyModeHelper(this);
|
||||
this.challengeMode = new ChallengeModeHelper(this);
|
||||
this.settings = new SettingsHelper(this);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
import { BattleStyle } from "#app/enums/battle-style";
|
||||
import { Species } from "#app/enums/species";
|
||||
import overrides from "#app/overrides";
|
||||
import { EncounterPhase } from "#app/phases/encounter-phase";
|
||||
import { SelectStarterPhase } from "#app/phases/select-starter-phase";
|
||||
import { Mode } from "#app/ui/ui";
|
||||
import { generateStarter } from "../gameManagerUtils";
|
||||
import { GameManagerHelper } from "./gameManagerHelper";
|
||||
import { Challenge } from "#app/data/challenge";
|
||||
import { CommandPhase } from "#app/phases/command-phase";
|
||||
import { TurnInitPhase } from "#app/phases/turn-init-phase";
|
||||
import { Challenges } from "#enums/challenges";
|
||||
import { copyChallenge } from "data/challenge";
|
||||
|
||||
/**
|
||||
* Helper to handle Challenge mode specifics
|
||||
*/
|
||||
export class ChallengeModeHelper extends GameManagerHelper {
|
||||
|
||||
challenges: Challenge[] = [];
|
||||
|
||||
/**
|
||||
* Adds a challenge to the challenge mode helper.
|
||||
* @param id - The challenge id.
|
||||
* @param value - The challenge value.
|
||||
* @param severity - The challenge severity.
|
||||
*/
|
||||
addChallenge(id: Challenges, value: number, severity: number) {
|
||||
const challenge = copyChallenge({ id, value, severity });
|
||||
this.challenges.push(challenge);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the Challenge game to the summon phase.
|
||||
* @param gameMode - Optional game mode to set.
|
||||
* @returns A promise that resolves when the summon phase is reached.
|
||||
*/
|
||||
async runToSummon(species?: Species[]) {
|
||||
await this.game.runToTitle();
|
||||
|
||||
this.game.onNextPrompt("TitlePhase", Mode.TITLE, () => {
|
||||
this.game.scene.gameMode.challenges = this.challenges;
|
||||
const starters = generateStarter(this.game.scene, species);
|
||||
const selectStarterPhase = new SelectStarterPhase(this.game.scene);
|
||||
this.game.scene.pushPhase(new EncounterPhase(this.game.scene, false));
|
||||
selectStarterPhase.initBattle(starters);
|
||||
});
|
||||
|
||||
await this.game.phaseInterceptor.run(EncounterPhase);
|
||||
if (overrides.OPP_HELD_ITEMS_OVERRIDE.length === 0) {
|
||||
this.game.removeEnemyHeldItems();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transitions to the start of a battle.
|
||||
* @param species - Optional array of species to start the battle with.
|
||||
* @returns A promise that resolves when the battle is started.
|
||||
*/
|
||||
async startBattle(species?: Species[]) {
|
||||
await this.runToSummon(species);
|
||||
|
||||
if (this.game.scene.battleStyle === BattleStyle.SWITCH) {
|
||||
this.game.onNextPrompt("CheckSwitchPhase", Mode.CONFIRM, () => {
|
||||
this.game.setMode(Mode.MESSAGE);
|
||||
this.game.endPhase();
|
||||
}, () => this.game.isCurrentPhase(CommandPhase) || this.game.isCurrentPhase(TurnInitPhase));
|
||||
|
||||
this.game.onNextPrompt("CheckSwitchPhase", Mode.CONFIRM, () => {
|
||||
this.game.setMode(Mode.MESSAGE);
|
||||
this.game.endPhase();
|
||||
}, () => this.game.isCurrentPhase(CommandPhase) || this.game.isCurrentPhase(TurnInitPhase));
|
||||
}
|
||||
|
||||
await this.game.phaseInterceptor.to(CommandPhase);
|
||||
console.log("==================[New Turn]==================");
|
||||
}
|
||||
}
|
|
@ -374,23 +374,14 @@ export default class RunInfoUiHandler extends UiHandler {
|
|||
case GameModes.CHALLENGE:
|
||||
modeText.appendText(`${i18next.t("gameMode:challenge")}`, false);
|
||||
modeText.appendText(`\t\t${i18next.t("runHistory:challengeRules")}: `);
|
||||
const runChallenges = this.runInfo.challenges;
|
||||
const rules: string[] = [];
|
||||
for (let i = 0; i < runChallenges.length; i++) {
|
||||
if (runChallenges[i].id === Challenges.SINGLE_GENERATION && runChallenges[i].value !== 0) {
|
||||
rules.push(i18next.t(`runHistory:challengeMonoGen${runChallenges[i].value}`));
|
||||
} else if (runChallenges[i].id === Challenges.SINGLE_TYPE && runChallenges[i].value !== 0) {
|
||||
rules.push(i18next.t(`pokemonInfo:Type.${Type[runChallenges[i].value-1]}` as const));
|
||||
} else if (runChallenges[i].id === Challenges.FRESH_START && runChallenges[i].value !== 0) {
|
||||
rules.push(i18next.t("challenges:freshStart.name"));
|
||||
}
|
||||
}
|
||||
const rules: string[] = this.challengeParser();
|
||||
if (rules) {
|
||||
for (let i = 0; i < rules.length; i++) {
|
||||
const newline = i > 0 && i%2 === 0;
|
||||
if (i > 0) {
|
||||
modeText.appendText(" + ", false);
|
||||
modeText.appendText(" + ", newline);
|
||||
}
|
||||
modeText.appendText(rules[i], false);
|
||||
modeText.appendText(rules[i], newline);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -466,6 +457,34 @@ export default class RunInfoUiHandler extends UiHandler {
|
|||
this.runContainer.add(this.runInfoContainer);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function parses the Challenges section of the Run Entry and returns a list of active challenge.
|
||||
* @return string[] of active challenge names
|
||||
*/
|
||||
private challengeParser(): string[] {
|
||||
const rules: string[] = [];
|
||||
for (let i = 0; i < this.runInfo.challenges.length; i++) {
|
||||
if (this.runInfo.challenges[i].value !== 0) {
|
||||
switch (this.runInfo.challenges[i].id) {
|
||||
case Challenges.SINGLE_GENERATION:
|
||||
rules.push(i18next.t(`runHistory:challengeMonoGen${this.runInfo.challenges[i].value}`));
|
||||
break;
|
||||
case Challenges.SINGLE_TYPE:
|
||||
rules.push(i18next.t(`pokemonInfo:Type.${Type[this.runInfo.challenges[i].value-1]}` as const));
|
||||
break;
|
||||
case Challenges.FRESH_START:
|
||||
rules.push(i18next.t("challenges:freshStart.name"));
|
||||
break;
|
||||
case Challenges.INVERSE_BATTLE:
|
||||
//
|
||||
rules.push(i18next.t("challenges:inverseBattle.shortName").split("").reverse().join(""));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses and displays the run's player party.
|
||||
* Default Information: Icon, Level, Nature, Ability, Passive, Shiny Status, Fusion Status, Stats, and Moves.
|
||||
|
|
Loading…
Reference in New Issue