lost-at-sea encounter - further progress

This commit is contained in:
Felix Staud 2024-07-12 14:07:33 -07:00
parent 7164b0afe4
commit c9cbdd7d44
5 changed files with 134 additions and 184 deletions

View File

@ -1,21 +1,12 @@
import { Type } from "#app/data/type.js"; import { Type } from "#app/data/type.js";
import { Moves } from "#app/enums/moves.js";
import { Species } from "#app/enums/species.js"; import { Species } from "#app/enums/species.js";
import { PlayerPokemon } from "#app/field/pokemon.js"; import { PlayerPokemon } from "#app/field/pokemon.js";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "../../../battle-scene"; import BattleScene from "../../../battle-scene";
import MysteryEncounter, { import MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier } from "../mystery-encounter";
MysteryEncounterBuilder, import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
MysteryEncounterTier, import { applyDamageToPokemon, leaveEncounterWithoutBattle, setEncounterExp } from "../mystery-encounter-utils";
} from "../mystery-encounter";
import {
EncounterOptionMode,
MysteryEncounterOptionBuilder,
} from "../mystery-encounter-option";
import {
applyDamageToPokemon,
leaveEncounterWithoutBattle,
setEncounterExp,
} from "../mystery-encounter-utils";
/** /**
* Damage percentage taken when wandering aimlessly. * Damage percentage taken when wandering aimlessly.
@ -26,7 +17,7 @@ const DAMAGE_PERCENTAGE: number = 30; // 0 - 100
/** The i18n namespace for the encounter */ /** The i18n namespace for the encounter */
const namepsace = "mysteryEncounter:lostAtSea"; const namepsace = "mysteryEncounter:lostAtSea";
let waterPkm: PlayerPokemon; let surfablePkm: PlayerPokemon;
let flyingPkm: PlayerPokemon; let flyingPkm: PlayerPokemon;
/** /**
@ -34,8 +25,7 @@ let flyingPkm: PlayerPokemon;
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/9 | GitHub Issue #9} * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/9 | GitHub Issue #9}
* @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts)
*/ */
export const LostAtSeaEncounter: MysteryEncounter = export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.LOST_AT_SEA)
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.LOST_AT_SEA)
.withEncounterTier(MysteryEncounterTier.COMMON) .withEncounterTier(MysteryEncounterTier.COMMON)
.withSceneWaveRangeRequirement(11, 179) .withSceneWaveRangeRequirement(11, 179)
.withIntroSpriteConfigs([ .withIntroSpriteConfigs([
@ -50,32 +40,20 @@ export const LostAtSeaEncounter: MysteryEncounter =
alpha: 0.25, alpha: 0.25,
}, },
]) ])
.withIntroDialogue([ .withIntroDialogue([{ text: `${namepsace}:intro` }])
{
text: `${namepsace}:intro`,
},
])
.withOnInit((scene: BattleScene) => { .withOnInit((scene: BattleScene) => {
const allowedPokemon = scene // const allowedPokemon = scene.getParty().filter((p) => p.isAllowedInBattle());
.getParty()
.filter((p) => p.isAllowedInBattle());
const { mysteryEncounter } = scene.currentBattle; const { mysteryEncounter } = scene.currentBattle;
mysteryEncounter.setDialogueToken( mysteryEncounter.setDialogueToken("damagePercentage", String(DAMAGE_PERCENTAGE));
"damagePercentage",
String(DAMAGE_PERCENTAGE)
);
// check for water pokemon // check for water pokemon
waterPkm = findPokemonByType(allowedPokemon, Type.WATER); // surfablePkm = findPokemonThatCanLearnMove(allowedPokemon, Type.WATER);
mysteryEncounter.setDialogueToken("waterPkm", waterPkm?.name ?? "<NONE>"); // mysteryEncounter.setDialogueToken("waterPkm", surfablePkm?.name ?? "");
// check for flying pokemon // check for flying pokemon
flyingPkm = findPokemonByType(allowedPokemon, Type.FLYING); // flyingPkm = findPokemonThatCanLearnMove(allowedPokemon, Type.FLYING);
mysteryEncounter.setDialogueToken( // mysteryEncounter.setDialogueToken("flyingPkm", flyingPkm?.name ?? "");
"flyingPkm",
flyingPkm?.name ?? "<NONE>"
);
return true; return true;
}) })
@ -88,7 +66,7 @@ export const LostAtSeaEncounter: MysteryEncounter =
* Receives EXP similar to defeating a Lapras * Receives EXP similar to defeating a Lapras
*/ */
new MysteryEncounterOptionBuilder() new MysteryEncounterOptionBuilder()
.withPokemonTypeRequirement(Type.WATER, true, 1) .withPokemonCanLearnMoveRequirement(Moves.SURF)
.withOptionMode(EncounterOptionMode.DISABLED_OR_DEFAULT) .withOptionMode(EncounterOptionMode.DISABLED_OR_DEFAULT)
.withDialogue({ .withDialogue({
buttonLabel: `${namepsace}:option:1:label`, buttonLabel: `${namepsace}:option:1:label`,
@ -99,9 +77,7 @@ export const LostAtSeaEncounter: MysteryEncounter =
}, },
], ],
}) })
.withOptionPhase(async (scene: BattleScene) => .withOptionPhase(async (scene: BattleScene) => handleGuidingOptionPhase(scene, surfablePkm))
handleGuidingOptionPhase(scene, waterPkm)
)
.build() .build()
) )
.withOption( .withOption(
@ -121,9 +97,7 @@ export const LostAtSeaEncounter: MysteryEncounter =
}, },
], ],
}) })
.withOptionPhase(async (scene: BattleScene) => .withOptionPhase(async (scene: BattleScene) => handleGuidingOptionPhase(scene, flyingPkm))
handleGuidingOptionPhase(scene, flyingPkm)
)
.build() .build()
) )
.withSimpleOption( .withSimpleOption(
@ -140,9 +114,7 @@ export const LostAtSeaEncounter: MysteryEncounter =
], ],
}, },
async (scene: BattleScene) => { async (scene: BattleScene) => {
const allowedPokemon = scene const allowedPokemon = scene.getParty().filter((p) => p.isAllowedInBattle());
.getParty()
.filter((p) => p.isAllowedInBattle());
allowedPokemon.forEach((pkm) => { allowedPokemon.forEach((pkm) => {
const percentage = DAMAGE_PERCENTAGE / 100; const percentage = DAMAGE_PERCENTAGE / 100;
@ -150,41 +122,26 @@ export const LostAtSeaEncounter: MysteryEncounter =
return applyDamageToPokemon(pkm, damage); return applyDamageToPokemon(pkm, damage);
}); });
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle(scene);
return true; return true;
} }
) )
.build(); .build();
/**
* Find a pokemon inside the given party by a given type
*
* @param party player pokemon party
* @param type type to search for
* @returns
*/
function findPokemonByType(party: PlayerPokemon[], type: Type) {
return party.find((p) => p.getTypes(true).includes(type));
}
/** /**
* Generic handler for using a guiding pokemon to guide you back. * Generic handler for using a guiding pokemon to guide you back.
* *
* @param scene Battle scene * @param scene Battle scene
* @param guidePokemon pokemon choosen as a guide * @param guidePokemon pokemon choosen as a guide
*/ */
function handleGuidingOptionPhase( function handleGuidingOptionPhase(scene: BattleScene, guidePokemon: PlayerPokemon) {
scene: BattleScene,
guidePokemon: PlayerPokemon
) {
/** Base EXP value for guiding pokemon. Currently Lapras base-value */ /** Base EXP value for guiding pokemon. Currently Lapras base-value */
const baseExpValue: number = 187; const baseExpValue: number = 187;
if (guidePokemon) { if (guidePokemon) {
setEncounterExp(scene, guidePokemon.id, baseExpValue, true); setEncounterExp(scene, guidePokemon.id, baseExpValue, true);
} else { } else {
console.warn( console.warn("Lost at sea: No guide pokemon found but pokemon guides player. huh!?");
"Lost at sea: No guide pokemon found but pokemon guides player. huh!?"
);
} }
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle(scene);

View File

@ -1,9 +1,11 @@
import { OptionTextDisplay } from "#app/data/mystery-encounters/mystery-encounter-dialogue"; import { OptionTextDisplay } from "#app/data/mystery-encounters/mystery-encounter-dialogue";
import { Moves } from "#app/enums/moves.js";
import { PlayerPokemon } from "#app/field/pokemon"; import { PlayerPokemon } from "#app/field/pokemon";
import BattleScene from "../../battle-scene"; import BattleScene from "../../battle-scene";
import * as Utils from "../../utils"; import * as Utils from "../../utils";
import { Type } from "../type"; import { Type } from "../type";
import { EncounterPokemonRequirement, EncounterSceneRequirement, MoneyRequirement, TypeRequirement } from "./mystery-encounter-requirements"; import { EncounterPokemonRequirement, EncounterSceneRequirement, MoneyRequirement, TypeRequirement } from "./mystery-encounter-requirements";
import { CanLearnMoveRequirement, CanlearnMoveRequirementOptions } from "./requirements/can-learn-move-requirement";
export enum EncounterOptionMode { export enum EncounterOptionMode {
/** Default style */ /** Default style */
@ -194,8 +196,18 @@ export class MysteryEncounterOptionBuilder implements Partial<MysteryEncounterOp
* @returns * @returns
*/ */
withPokemonTypeRequirement(type: Type | Type[], excludeFainted?: boolean, minNumberOfPokemon?: number, invertQuery?: boolean) { withPokemonTypeRequirement(type: Type | Type[], excludeFainted?: boolean, minNumberOfPokemon?: number, invertQuery?: boolean) {
const types = Array.isArray(type) ? type : [type]; return this.withPrimaryPokemonRequirement(new TypeRequirement(type, excludeFainted, minNumberOfPokemon, invertQuery));
return this.withPrimaryPokemonRequirement(new TypeRequirement(types, excludeFainted, minNumberOfPokemon, invertQuery)); }
/**
* Player is required to have a pokemon that can learn a certain move/moveset
*
* @param move the required move/moves
* @param options see {@linkcode CanlearnMoveRequirementOptions}
* @returns
*/
withPokemonCanLearnMoveRequirement(move: Moves | Moves[], options?: CanlearnMoveRequirementOptions) {
return this.withPrimaryPokemonRequirement(new CanLearnMoveRequirement(move, options));
} }
withSecondaryPokemonRequirement(requirement: EncounterPokemonRequirement, excludePrimaryFromSecondaryRequirements?: boolean): this & Required<Pick<MysteryEncounterOption, "secondaryPokemonRequirements">> { withSecondaryPokemonRequirement(requirement: EncounterPokemonRequirement, excludePrimaryFromSecondaryRequirements?: boolean): this & Required<Pick<MysteryEncounterOption, "secondaryPokemonRequirements">> {

View File

@ -1,15 +1,16 @@
import IMysteryEncounter from "./mystery-encounter"; import { Biome } from "#enums/biome";
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { DarkDealEncounter } from "./encounters/dark-deal-encounter"; import { DarkDealEncounter } from "./encounters/dark-deal-encounter";
import { DepartmentStoreSaleEncounter } from "./encounters/department-store-sale-encounter";
import { FieldTripEncounter } from "./encounters/field-trip-encounter";
import { FightOrFlightEncounter } from "./encounters/fight-or-flight-encounter";
import { LostAtSeaEncounter } from "./encounters/lost-at-sea-encounter";
import { MysteriousChallengersEncounter } from "./encounters/mysterious-challengers-encounter"; import { MysteriousChallengersEncounter } from "./encounters/mysterious-challengers-encounter";
import { MysteriousChestEncounter } from "./encounters/mysterious-chest-encounter"; import { MysteriousChestEncounter } from "./encounters/mysterious-chest-encounter";
import { FightOrFlightEncounter } from "./encounters/fight-or-flight-encounter";
import { TrainingSessionEncounter } from "./encounters/training-session-encounter";
import { Biome } from "#enums/biome";
import { SleepingSnorlaxEncounter } from "./encounters/sleeping-snorlax-encounter";
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { DepartmentStoreSaleEncounter } from "./encounters/department-store-sale-encounter";
import { ShadyVitaminDealerEncounter } from "./encounters/shady-vitamin-dealer-encounter"; import { ShadyVitaminDealerEncounter } from "./encounters/shady-vitamin-dealer-encounter";
import { LostAtSeaEncounter } from "./encounters/lost-at-sea-encounter"; import { SleepingSnorlaxEncounter } from "./encounters/sleeping-snorlax-encounter";
import { TrainingSessionEncounter } from "./encounters/training-session-encounter";
import IMysteryEncounter from "./mystery-encounter";
// Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * <number of missed spawns>) / 256 // Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * <number of missed spawns>) / 256
export const BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT = 1; export const BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT = 1;

View File

@ -7,7 +7,7 @@ import { EncounterPokemonRequirement } from "../mystery-encounter-requirements";
/** /**
* {@linkcode CanLearnMoveRequirement} options * {@linkcode CanLearnMoveRequirement} options
*/ */
interface Options { export interface CanlearnMoveRequirementOptions {
excludeLevelMoves?: boolean; excludeLevelMoves?: boolean;
excludeTmMoves?: boolean; excludeTmMoves?: boolean;
excludeEggMoves?: boolean; excludeEggMoves?: boolean;
@ -26,20 +26,11 @@ export class CanLearnMoveRequirement extends EncounterPokemonRequirement {
private readonly excludeEggMoves?: boolean; private readonly excludeEggMoves?: boolean;
private readonly includeFainted?: boolean; private readonly includeFainted?: boolean;
constructor(requiredMoves: Moves | Moves[], options: Options = {}) { constructor(requiredMoves: Moves | Moves[], options: CanlearnMoveRequirementOptions = {}) {
super(); super();
this.requiredMoves = Array.isArray(requiredMoves) this.requiredMoves = Array.isArray(requiredMoves) ? requiredMoves : [requiredMoves];
? requiredMoves
: [requiredMoves];
const { const { excludeLevelMoves, excludeTmMoves, excludeEggMoves, includeFainted, minNumberOfPokemon, invertQuery } = options;
excludeLevelMoves,
excludeTmMoves,
excludeEggMoves,
includeFainted,
minNumberOfPokemon,
invertQuery,
} = options;
this.excludeLevelMoves = excludeLevelMoves ?? false; this.excludeLevelMoves = excludeLevelMoves ?? false;
this.excludeTmMoves = excludeTmMoves ?? false; this.excludeTmMoves = excludeTmMoves ?? false;
@ -50,11 +41,7 @@ export class CanLearnMoveRequirement extends EncounterPokemonRequirement {
} }
override meetsRequirement(scene: BattleScene): boolean { override meetsRequirement(scene: BattleScene): boolean {
const partyPokemon = scene const partyPokemon = scene.getParty().filter((pkm) => (this.includeFainted ? pkm.isAllowed() : pkm.isAllowedInBattle()));
.getParty()
.filter((pkm) =>
this.includeFainted ? pkm.isAllowed() : pkm.isAllowedInBattle()
);
if (isNullOrUndefined(partyPokemon) || this?.requiredMoves?.length < 0) { if (isNullOrUndefined(partyPokemon) || this?.requiredMoves?.length < 0) {
return false; return false;
@ -67,26 +54,19 @@ export class CanLearnMoveRequirement extends EncounterPokemonRequirement {
if (!this.invertQuery) { if (!this.invertQuery) {
return partyPokemon.filter((pokemon) => return partyPokemon.filter((pokemon) =>
// every required move should be included // every required move should be included
this.requiredMoves.every((requiredMove) => this.requiredMoves.every((requiredMove) => this.getAllPokemonMoves(pokemon).includes(requiredMove))
this.getAllPokemonMoves(pokemon).includes(requiredMove)
)
); );
} else { } else {
return partyPokemon.filter( return partyPokemon.filter(
(pokemon) => (pokemon) =>
// none of the "required" moves should be included // none of the "required" moves should be included
!this.requiredMoves.some((requiredMove) => !this.requiredMoves.some((requiredMove) => this.getAllPokemonMoves(pokemon).includes(requiredMove))
this.getAllPokemonMoves(pokemon).includes(requiredMove)
)
); );
} }
} }
override getDialogueToken( override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
_scene: BattleScene, return pokemon ? ["pokemonCanLearnMove", pokemon.name] : null;
_pokemon?: PlayerPokemon
): [string, string] {
return ["requiredMoves", this.requiredMoves.join(", ")];
} }
private getPokemonLevelMoves(pkm: PlayerPokemon): Moves[] { private getPokemonLevelMoves(pkm: PlayerPokemon): Moves[] {

View File

@ -6,10 +6,10 @@ export const lostAtSea = {
query: "What will you do?", query: "What will you do?",
option: { option: {
1: { 1: {
label: "Use @ec{waterPkm}", // pkm has to be of type water label: "Use @ec{pokemonCanLearnMove}", // pkm has to be of type water
tooltip: tooltip:
"Use @ec{waterPkm} to guide you back. @ec{waterPkm} earns EXP as if having defeated a Lapras.", "Use @ec{pokemonCanLearnMove} to guide you back. @ec{pokemonCanLearnMove} earns EXP as if having defeated a Lapras.",
selected: "@ec{waterPkm} guides you back and earns EXP.", selected: "@ec{pokemonCanLearnMove} guides you back and earns EXP.",
}, },
2: { 2: {
label: "Use @ec{flyingPkm}", // pkm has to be of type flying label: "Use @ec{flyingPkm}", // pkm has to be of type flying