add jsdocs and more cleanup
This commit is contained in:
parent
f62af4ab68
commit
e795a62629
|
@ -64,7 +64,7 @@ export const SafariZoneEncounter: IMysteryEncounter =
|
|||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
// Start safari encounter
|
||||
const encounter = scene.currentBattle.mysteryEncounter;
|
||||
encounter.encounterVariant = MysteryEncounterVariant.REPEATED_ENCOUNTER;
|
||||
encounter.encounterVariant = MysteryEncounterVariant.CONTINUOUS_ENCOUNTER;
|
||||
encounter.misc = {
|
||||
safariPokemonRemaining: 3
|
||||
};
|
||||
|
|
|
@ -28,7 +28,7 @@ export enum MysteryEncounterVariant {
|
|||
BOSS_BATTLE,
|
||||
NO_BATTLE,
|
||||
/** For spawning new encounter queries instead of continuing to next wave */
|
||||
REPEATED_ENCOUNTER
|
||||
CONTINUOUS_ENCOUNTER
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -115,11 +115,6 @@ export default interface IMysteryEncounter {
|
|||
* You probably shouldn't do anything with this unless you have a very specific need
|
||||
*/
|
||||
introVisuals?: MysteryEncounterIntroVisuals;
|
||||
/**
|
||||
* Used for keeping RNG consistent on session resets, but increments when cycling through multiple "Encounters" on the same wave
|
||||
* You should never need to modify this
|
||||
*/
|
||||
seedOffset?: any;
|
||||
|
||||
/**
|
||||
* Flags
|
||||
|
@ -172,6 +167,12 @@ export default interface IMysteryEncounter {
|
|||
* Unless you know what you're doing, you should use MysteryEncounterBuilder to create an instance for this class
|
||||
*/
|
||||
export default class IMysteryEncounter implements IMysteryEncounter {
|
||||
/**
|
||||
* Used for keeping RNG consistent on session resets, but increments when cycling through multiple "Encounters" on the same wave
|
||||
* You should only need to interact via getter/update methods
|
||||
*/
|
||||
private seedOffset?: any;
|
||||
|
||||
constructor(encounter: IMysteryEncounter) {
|
||||
if (!isNullOrUndefined(encounter)) {
|
||||
Object.assign(this, encounter);
|
||||
|
@ -371,6 +372,20 @@ export default class IMysteryEncounter implements IMysteryEncounter {
|
|||
this.dialogueTokens[key] = value;
|
||||
}
|
||||
|
||||
getSeedOffset?() {
|
||||
return this.seedOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maintains seed offset for RNG consistency
|
||||
* Increments if the same MysteryEncounter has multiple option select cycles
|
||||
* @param scene
|
||||
*/
|
||||
updateSeedOffset?(scene: BattleScene) {
|
||||
const currentOffset = this.seedOffset ?? scene.currentBattle.waveIndex * 1000;
|
||||
this.seedOffset = currentOffset + 512;
|
||||
}
|
||||
|
||||
private capitalizeFirstLetter?(str: string) {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||
}
|
||||
|
@ -442,7 +457,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
|||
* @param callback - {@linkcode OptionPhaseCallback}
|
||||
* @returns
|
||||
*/
|
||||
withSimpleOption(dialogue: OptionTextDisplay, callback: OptionPhaseCallback): this {
|
||||
withSimpleOption(dialogue: OptionTextDisplay, callback: OptionPhaseCallback): this & Pick<IMysteryEncounter, "options"> {
|
||||
return this.withOption(new MysteryEncounterOptionBuilder().withOptionMode(EncounterOptionMode.DEFAULT).withDialogue(dialogue).withOptionPhase(callback).build());
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,10 @@ import { Gender } from "#app/data/gender";
|
|||
import { Moves } from "#enums/moves";
|
||||
import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims";
|
||||
|
||||
/**
|
||||
* Animates exclamation sprite over trainer's head at start of encounter
|
||||
* @param scene
|
||||
*/
|
||||
export function doTrainerExclamation(scene: BattleScene) {
|
||||
const exclamationSprite = scene.addFieldSprite(0, 0, "exclaim");
|
||||
exclamationSprite.setName("exclamation");
|
||||
|
@ -476,6 +480,7 @@ export function setEncounterExp(scene: BattleScene, participantId: integer | int
|
|||
const nonFaintedPartyMembers = party.filter(p => p.hp);
|
||||
const expPartyMembers = nonFaintedPartyMembers.filter(p => p.level < scene.getMaxExpLevel());
|
||||
const partyMemberExp = [];
|
||||
// EXP value calculation is based off Pokemon.getExpValue
|
||||
let expValue = Math.floor(baseExpValue * (useWaveIndex ? scene.currentBattle.waveIndex : 1) / 5 + 1);
|
||||
|
||||
if (participantIds?.length > 0) {
|
||||
|
@ -597,7 +602,7 @@ export function handleMysteryEncounterVictory(scene: BattleScene, addHealPhase:
|
|||
|
||||
// If in repeated encounter variant, do nothing
|
||||
// Variant must eventually be swapped in order to handle "true" end of the encounter
|
||||
if (scene.currentBattle.mysteryEncounter.encounterVariant === MysteryEncounterVariant.REPEATED_ENCOUNTER) {
|
||||
if (scene.currentBattle.mysteryEncounter.encounterVariant === MysteryEncounterVariant.CONTINUOUS_ENCOUNTER) {
|
||||
return;
|
||||
} else if (scene.currentBattle.mysteryEncounter.encounterVariant === MysteryEncounterVariant.NO_BATTLE) {
|
||||
scene.pushPhase(new EggLapsePhase(scene));
|
||||
|
@ -657,7 +662,12 @@ export function transitionMysteryEncounterIntroVisuals(scene: BattleScene, hide:
|
|||
});
|
||||
}
|
||||
|
||||
export function handleEncounterStartOfBattleEffects(scene: BattleScene) {
|
||||
/**
|
||||
* Will queue moves for any pokemon to use before the first CommandPhase of a battle
|
||||
* Mostly useful for allowing MysteryEncounter enemies to "cheat" and use moves before the first turn
|
||||
* @param scene
|
||||
*/
|
||||
export function handleMysteryEncounterBattleStartEffects(scene: BattleScene) {
|
||||
const encounter = scene.currentBattle?.mysteryEncounter;
|
||||
if (scene.currentBattle.battleType === BattleType.MYSTERY_ENCOUNTER && encounter.encounterVariant !== MysteryEncounterVariant.NO_BATTLE && !encounter.startOfBattleEffectsComplete) {
|
||||
const effects = encounter.startOfBattleEffects;
|
||||
|
|
|
@ -349,6 +349,10 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets container and all child sprites to visible
|
||||
* @param value - true for visible, false for hidden
|
||||
*/
|
||||
setVisible(value: boolean): this {
|
||||
this.getSprites().forEach(sprite => {
|
||||
sprite.setVisible(value);
|
||||
|
|
|
@ -101,6 +101,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
public battleSummonData: PokemonBattleSummonData;
|
||||
public turnData: PokemonTurnData;
|
||||
|
||||
/** Used by Mystery Encounters to execute pokemon-specific logic (such as stat boosts) at start of battle */
|
||||
public mysteryEncounterBattleEffects: (pokemon: Pokemon) => void = null;
|
||||
|
||||
public fieldPosition: FieldPosition;
|
||||
|
|
|
@ -67,7 +67,7 @@ import { Species } from "#enums/species";
|
|||
import { TrainerType } from "#enums/trainer-type";
|
||||
import { MysteryEncounterVariant } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases";
|
||||
import { doTrainerExclamation, handleEncounterStartOfBattleEffects, handleMysteryEncounterVictory } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import { doTrainerExclamation, handleMysteryEncounterBattleStartEffects, handleMysteryEncounterVictory } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import ModifierSelectUiHandler, { SHOP_OPTIONS_ROW_LIMIT } from "#app/ui/modifier-select-ui-handler";
|
||||
import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
|
||||
|
@ -2053,8 +2053,7 @@ export class TurnInitPhase extends FieldPhase {
|
|||
//this.scene.pushPhase(new MoveAnimTestPhase(this.scene));
|
||||
this.scene.eventTarget.dispatchEvent(new TurnInitEvent());
|
||||
|
||||
// Start of battle effects for Mystery Encounters
|
||||
handleEncounterStartOfBattleEffects(this.scene);
|
||||
handleMysteryEncounterBattleStartEffects(this.scene);
|
||||
|
||||
this.scene.getField().forEach((pokemon, i) => {
|
||||
if (pokemon?.isActive()) {
|
||||
|
|
|
@ -28,6 +28,8 @@ import { BattlerTagLapseType } from "#app/data/battler-tags";
|
|||
export class MysteryEncounterPhase extends Phase {
|
||||
optionSelectSettings: OptionSelectSettings;
|
||||
|
||||
private FIRST_DIALOGUE_PROMPT_DELAY = 300;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param scene
|
||||
|
@ -46,9 +48,7 @@ export class MysteryEncounterPhase extends Phase {
|
|||
this.scene.clearPhaseQueue();
|
||||
this.scene.clearPhaseQueueSplice();
|
||||
|
||||
// Generates seed offset for RNG consistency, but incremented if the same MysteryEncounter has multiple option select cycles
|
||||
const offset = this.scene.currentBattle.mysteryEncounter.seedOffset ?? this.scene.currentBattle.waveIndex * 1000;
|
||||
this.scene.currentBattle.mysteryEncounter.seedOffset = offset + 512;
|
||||
this.scene.currentBattle.mysteryEncounter.updateSeedOffset(this.scene);
|
||||
|
||||
if (!this.optionSelectSettings) {
|
||||
// Sets flag that ME was encountered, only if this is not a followup option select phase
|
||||
|
@ -79,7 +79,7 @@ export class MysteryEncounterPhase extends Phase {
|
|||
this.continueEncounter();
|
||||
}
|
||||
});
|
||||
}, this.scene.currentBattle.mysteryEncounter.seedOffset);
|
||||
}, this.scene.currentBattle.mysteryEncounter.getSeedOffset());
|
||||
} else {
|
||||
this.continueEncounter();
|
||||
}
|
||||
|
@ -109,9 +109,9 @@ export class MysteryEncounterPhase extends Phase {
|
|||
}
|
||||
|
||||
if (title) {
|
||||
this.scene.ui.showDialogue(text, title, null, nextAction, 0, i === 0 ? 300 : 0);
|
||||
this.scene.ui.showDialogue(text, title, null, nextAction, 0, i === 0 ? this.FIRST_DIALOGUE_PROMPT_DELAY : 0);
|
||||
} else {
|
||||
this.scene.ui.showText(text, null, nextAction, i === 0 ? 300 : 0, true);
|
||||
this.scene.ui.showText(text, null, nextAction, i === 0 ? this.FIRST_DIALOGUE_PROMPT_DELAY : 0, true);
|
||||
}
|
||||
i++;
|
||||
};
|
||||
|
@ -154,14 +154,14 @@ export class MysteryEncounterOptionSelectedPhase extends Phase {
|
|||
this.onOptionSelect(this.scene).finally(() => {
|
||||
this.end();
|
||||
});
|
||||
}, this.scene.currentBattle.mysteryEncounter.seedOffset);
|
||||
}, this.scene.currentBattle.mysteryEncounter.getSeedOffset());
|
||||
});
|
||||
} else {
|
||||
this.scene.executeWithSeedOffset(() => {
|
||||
this.onOptionSelect(this.scene).finally(() => {
|
||||
this.end();
|
||||
});
|
||||
}, this.scene.currentBattle.mysteryEncounter.seedOffset);
|
||||
}, this.scene.currentBattle.mysteryEncounter.getSeedOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -279,7 +279,7 @@ export class MysteryEncounterBattlePhase extends Phase {
|
|||
} else {
|
||||
const trainer = this.scene.currentBattle.trainer;
|
||||
let message: string;
|
||||
scene.executeWithSeedOffset(() => message = Utils.randSeedItem(encounterMessages), this.scene.currentBattle.mysteryEncounter.seedOffset);
|
||||
scene.executeWithSeedOffset(() => message = Utils.randSeedItem(encounterMessages), this.scene.currentBattle.mysteryEncounter.getSeedOffset());
|
||||
|
||||
const showDialogueAndSummon = () => {
|
||||
scene.ui.showDialogue(message, trainer.getName(TrainerSlot.NONE, true), null, () => {
|
||||
|
@ -438,7 +438,7 @@ export class PostMysteryEncounterPhase extends Phase {
|
|||
this.continueEncounter();
|
||||
}
|
||||
});
|
||||
}, this.scene.currentBattle.mysteryEncounter.seedOffset);
|
||||
}, this.scene.currentBattle.mysteryEncounter.getSeedOffset());
|
||||
} else {
|
||||
this.continueEncounter();
|
||||
}
|
||||
|
|
|
@ -7,6 +7,12 @@ import GameManager from "../utils/gameManager";
|
|||
import MessageUiHandler from "#app/ui/message-ui-handler";
|
||||
import { Status, StatusEffect } from "#app/data/status-effect";
|
||||
|
||||
/**
|
||||
* Runs a MysteryEncounter to either the start of a battle, or to the MysteryEncounterRewardsPhase, depending on the option selected
|
||||
* @param game
|
||||
* @param optionNo - human number, not index
|
||||
* @param isBattle - if selecting option should lead to battle, set to true
|
||||
*/
|
||||
export async function runSelectMysteryEncounterOption(game: GameManager, optionNo: number, isBattle: boolean = false) {
|
||||
// Handle any eventual queued messages (e.g. weather phase, etc.)
|
||||
game.onNextPrompt("MessagePhase", Mode.MESSAGE, () => {
|
||||
|
@ -81,6 +87,10 @@ export async function runSelectMysteryEncounterOption(game: GameManager, optionN
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For any MysteryEncounter that has a battle, can call this to skip battle and proceed to MysteryEncounterRewardsPhase
|
||||
* @param game
|
||||
*/
|
||||
export async function skipBattleRunMysteryEncounterRewardsPhase(game: GameManager) {
|
||||
game.scene.clearPhaseQueue();
|
||||
game.scene.clearPhaseQueueSplice();
|
||||
|
|
|
@ -74,7 +74,7 @@ describe("Fiery Fallout - Mystery Encounter", () => {
|
|||
game.override.startingBiome(Biome.MOUNTAIN);
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle.mysteryEncounter.encounterType).not.toBe(MysteryEncounterType.LOST_AT_SEA);
|
||||
expect(scene.currentBattle.mysteryEncounter.encounterType).not.toBe(MysteryEncounterType.FIERY_FALLOUT);
|
||||
});
|
||||
|
||||
it("should not run below wave 41", async () => {
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
/**
|
||||
* Class will intercept any text or dialogue message calls and log them for test purposes
|
||||
*/
|
||||
export default class TextInterceptor {
|
||||
private scene;
|
||||
public logs = [];
|
||||
|
|
Loading…
Reference in New Issue