Merge branch 'beta' into sheerForceImp

This commit is contained in:
Mumble 2024-11-16 15:37:21 -08:00 committed by GitHub
commit e381162096
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 110 additions and 41 deletions

View File

@ -138,7 +138,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
newNature = randSeedInt(25) as Nature; newNature = randSeedInt(25) as Nature;
} }
chosenPokemon.customPokemonData.nature = newNature; chosenPokemon.setCustomNature(newNature);
encounter.setDialogueToken("newNature", getNatureName(newNature)); encounter.setDialogueToken("newNature", getNatureName(newNature));
queueEncounterMessage(scene, `${namespace}:cheap_side_effects`); queueEncounterMessage(scene, `${namespace}:cheap_side_effects`);
setEncounterExp(scene, [ chosenPokemon.id ], 100); setEncounterExp(scene, [ chosenPokemon.id ], 100);

View File

@ -226,8 +226,8 @@ export const TrainingSessionEncounter: MysteryEncounter =
const onBeforeRewardsPhase = () => { const onBeforeRewardsPhase = () => {
queueEncounterMessage(scene, `${namespace}:option.2.finished`); queueEncounterMessage(scene, `${namespace}:option.2.finished`);
// Add the pokemon back to party with Nature change // Add the pokemon back to party with Nature change
playerPokemon.setNature(encounter.misc.chosenNature); playerPokemon.setCustomNature(encounter.misc.chosenNature);
scene.gameData.setPokemonCaught(playerPokemon, false); scene.gameData.unlockSpeciesNature(playerPokemon.species, encounter.misc.chosenNature);
// Add pokemon and modifiers back // Add pokemon and modifiers back
scene.getPlayerParty().push(playerPokemon); scene.getPlayerParty().push(playerPokemon);

View File

@ -7,22 +7,22 @@ import i18next from "i18next";
/** /**
* Will inject all relevant dialogue tokens that exist in the {@linkcode BattleScene.currentBattle.mysteryEncounter.dialogueTokens}, into i18n text. * Will inject all relevant dialogue tokens that exist in the {@linkcode BattleScene.currentBattle.mysteryEncounter.dialogueTokens}, into i18n text.
* Also adds BBCodeText fragments for colored text, if applicable * Also adds BBCodeText fragments for colored text, if applicable
* @param scene
* @param keyOrString * @param keyOrString
* @param primaryStyle Can define a text style to be applied to the entire string. Must be defined for BBCodeText styles to be applied correctly * @param primaryStyle Can define a text style to be applied to the entire string. Must be defined for BBCodeText styles to be applied correctly
* @param uiTheme
*/ */
export function getEncounterText(scene: BattleScene, keyOrString?: string, primaryStyle?: TextStyle, uiTheme: UiTheme = UiTheme.DEFAULT): string | null { export function getEncounterText(scene: BattleScene, keyOrString?: string, primaryStyle?: TextStyle): string | null {
if (isNullOrUndefined(keyOrString)) { if (isNullOrUndefined(keyOrString)) {
return null; return null;
} }
const uiTheme = scene.uiTheme ?? UiTheme.DEFAULT;
let textString: string | null = getTextWithDialogueTokens(scene, keyOrString); let textString: string | null = getTextWithDialogueTokens(scene, keyOrString);
// Can only color the text if a Primary Style is defined // Can only color the text if a Primary Style is defined
// primaryStyle is applied to all text that does not have its own specified style // primaryStyle is applied to all text that does not have its own specified style
if (primaryStyle && textString) { if (primaryStyle && textString) {
textString = getTextWithColors(textString, primaryStyle, uiTheme); textString = getTextWithColors(textString, primaryStyle, uiTheme, true);
} }
return textString; return textString;

View File

@ -1072,6 +1072,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.calculateStats(); this.calculateStats();
} }
setCustomNature(nature: Nature): void {
this.customPokemonData.nature = nature;
this.calculateStats();
}
generateNature(naturePool?: Nature[]): void { generateNature(naturePool?: Nature[]): void {
if (naturePool === undefined) { if (naturePool === undefined) {
naturePool = Utils.getEnumValues(Nature); naturePool = Utils.getEnumValues(Nature);

View File

@ -230,6 +230,13 @@ export class GameMode implements GameModeConfig {
return waveIndex % 10 === 0; return waveIndex % 10 === 0;
} }
/**
* @returns `true` if the current battle is against classic mode's final boss
*/
isBattleClassicFinalBoss(waveIndex: number): boolean {
return (this.modeId === GameModes.CLASSIC || this.modeId === GameModes.CHALLENGE) && this.isWaveFinal(waveIndex);
}
/** /**
* Every 50 waves of an Endless mode is a boss * Every 50 waves of an Endless mode is a boss
* At this time it is paradox pokemon * At this time it is paradox pokemon

View File

@ -1,5 +1,5 @@
import type BattleScene from "#app/battle-scene"; import type BattleScene from "#app/battle-scene";
import { FusionSpeciesFormEvolution, pokemonEvolutions, pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; import { FusionSpeciesFormEvolution, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
import { getBerryEffectFunc, getBerryPredicate } from "#app/data/berry"; import { getBerryEffectFunc, getBerryPredicate } from "#app/data/berry";
import { getLevelTotalExp } from "#app/data/exp"; import { getLevelTotalExp } from "#app/data/exp";
import { allMoves } from "#app/data/move"; import { allMoves } from "#app/data/move";
@ -2197,14 +2197,8 @@ export class PokemonNatureChangeModifier extends ConsumablePokemonModifier {
* @returns * @returns
*/ */
override apply(playerPokemon: PlayerPokemon): boolean { override apply(playerPokemon: PlayerPokemon): boolean {
playerPokemon.customPokemonData.nature = this.nature; playerPokemon.setCustomNature(this.nature);
let speciesId = playerPokemon.species.speciesId; playerPokemon.scene.gameData.unlockSpeciesNature(playerPokemon.species, this.nature);
playerPokemon.scene.gameData.dexData[speciesId].natureAttr |= 1 << (this.nature + 1);
while (pokemonPrevolutions.hasOwnProperty(speciesId)) {
speciesId = pokemonPrevolutions[speciesId];
playerPokemon.scene.gameData.dexData[speciesId].natureAttr |= 1 << (this.nature + 1);
}
return true; return true;
} }

View File

@ -141,10 +141,6 @@ export class EncounterPhase extends BattlePhase {
} else if (!(battle.waveIndex % 1000)) { } else if (!(battle.waveIndex % 1000)) {
enemyPokemon.formIndex = 1; enemyPokemon.formIndex = 1;
enemyPokemon.updateScale(); enemyPokemon.updateScale();
const bossMBH = this.scene.findModifier(m => m instanceof TurnHeldItemTransferModifier && m.pokemonId === enemyPokemon.id, false) as TurnHeldItemTransferModifier;
this.scene.removeModifier(bossMBH!);
bossMBH?.setTransferrableFalse();
this.scene.addEnemyModifier(bossMBH!);
} }
} }
@ -225,7 +221,8 @@ export class EncounterPhase extends BattlePhase {
this.scene.ui.setMode(Mode.MESSAGE).then(() => { this.scene.ui.setMode(Mode.MESSAGE).then(() => {
if (!this.loaded) { if (!this.loaded) {
this.trySetWeatherIfNewBiome(); // Set weather before session gets saved this.trySetWeatherIfNewBiome(); // Set weather before session gets saved
this.scene.gameData.saveAll(this.scene, true, battle.waveIndex % 10 === 1 || (this.scene.lastSavePlayTime ?? 0) >= 300).then(success => { // Game syncs to server on waves X1 and X6 (As of 1.2.0)
this.scene.gameData.saveAll(this.scene, true, battle.waveIndex % 5 === 1 || (this.scene.lastSavePlayTime ?? 0) >= 300).then(success => {
this.scene.disableMenu = false; this.scene.disableMenu = false;
if (!success) { if (!success) {
return this.scene.reset(true); return this.scene.reset(true);
@ -442,6 +439,15 @@ export class EncounterPhase extends BattlePhase {
if (enemyPokemon.isShiny()) { if (enemyPokemon.isShiny()) {
this.scene.unshiftPhase(new ShinySparklePhase(this.scene, BattlerIndex.ENEMY + e)); this.scene.unshiftPhase(new ShinySparklePhase(this.scene, BattlerIndex.ENEMY + e));
} }
/** This sets Eternatus' held item to be untransferrable, preventing it from being stolen */
if (enemyPokemon.species.speciesId === Species.ETERNATUS && (this.scene.gameMode.isBattleClassicFinalBoss(this.scene.currentBattle.waveIndex) || this.scene.gameMode.isEndlessMajorBoss(this.scene.currentBattle.waveIndex))) {
const enemyMBH = this.scene.findModifier(m => m instanceof TurnHeldItemTransferModifier, false) as TurnHeldItemTransferModifier;
if (enemyMBH) {
this.scene.removeModifier(enemyMBH, true);
enemyMBH.setTransferrableFalse();
this.scene.addEnemyModifier(enemyMBH);
}
}
}); });
if (![ BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER ].includes(this.scene.currentBattle.battleType)) { if (![ BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER ].includes(this.scene.currentBattle.battleType)) {

View File

@ -506,9 +506,9 @@ export class GameData {
const starterIds = Object.keys(this.starterData).map(s => parseInt(s) as Species); const starterIds = Object.keys(this.starterData).map(s => parseInt(s) as Species);
for (const s of starterIds) { for (const s of starterIds) {
this.starterData[s].candyCount += this.dexData[s].caughtCount; this.starterData[s].candyCount += systemData.dexData[s].caughtCount;
this.starterData[s].candyCount += this.dexData[s].hatchedCount * 2; this.starterData[s].candyCount += systemData.dexData[s].hatchedCount * 2;
if (this.dexData[s].caughtAttr & DexAttr.SHINY) { if (systemData.dexData[s].caughtAttr & DexAttr.SHINY) {
this.starterData[s].candyCount += 4; this.starterData[s].candyCount += 4;
} }
} }
@ -1791,6 +1791,32 @@ export class GameData {
}); });
} }
/**
* Checks whether the root species of a given {@PokemonSpecies} has been unlocked in the dex
*/
isRootSpeciesUnlocked(species: PokemonSpecies): boolean {
return !!this.dexData[species.getRootSpeciesId()]?.caughtAttr;
}
/**
* Unlocks the given {@linkcode Nature} for a {@linkcode PokemonSpecies} and its prevolutions.
* Will fail silently if root species has not been unlocked
*/
unlockSpeciesNature(species: PokemonSpecies, nature: Nature): void {
if (!this.isRootSpeciesUnlocked(species)) {
return;
}
//recursively unlock nature for species and prevolutions
const _unlockSpeciesNature = (speciesId: Species) => {
this.dexData[speciesId].natureAttr |= 1 << (nature + 1);
if (pokemonPrevolutions.hasOwnProperty(speciesId)) {
_unlockSpeciesNature(pokemonPrevolutions[speciesId]);
}
};
_unlockSpeciesNature(species.speciesId);
}
updateSpeciesDexIvs(speciesId: Species, ivs: integer[]): void { updateSpeciesDexIvs(speciesId: Species, ivs: integer[]): void {
let dexEntry: DexEntry; let dexEntry: DexEntry;
do { do {

View File

@ -1,6 +1,7 @@
import { SettingKeys } from "../../settings/settings"; import { SettingKeys } from "#app/system/settings/settings";
import { AbilityAttr, defaultStarterSpecies, DexAttr, SystemSaveData, SessionSaveData } from "../../game-data"; import { AbilityAttr, defaultStarterSpecies, DexAttr, SystemSaveData, SessionSaveData } from "#app/system/game-data";
import { allSpecies } from "../../../data/pokemon-species"; import { allSpecies } from "#app/data/pokemon-species";
import { isNullOrUndefined } from "#app/utils";
export const systemMigrators = [ export const systemMigrators = [
/** /**
@ -46,6 +47,7 @@ export const systemMigrators = [
* @param data {@linkcode SystemSaveData} * @param data {@linkcode SystemSaveData}
*/ */
function fixStarterData(data: SystemSaveData) { function fixStarterData(data: SystemSaveData) {
if (!isNullOrUndefined(data.starterData)) {
for (const starterId of defaultStarterSpecies) { for (const starterId of defaultStarterSpecies) {
if (data.starterData[starterId]?.abilityAttr) { if (data.starterData[starterId]?.abilityAttr) {
data.starterData[starterId].abilityAttr |= AbilityAttr.ABILITY_1; data.starterData[starterId].abilityAttr |= AbilityAttr.ABILITY_1;
@ -55,6 +57,7 @@ export const systemMigrators = [
} }
} }
} }
}
] as const; ] as const;
export const settingsMigrators = [ export const settingsMigrators = [

View File

@ -369,9 +369,9 @@ export default class MysteryEncounterUiHandler extends UiHandler {
let text: string | null; let text: string | null;
if (option.hasRequirements() && this.optionsMeetsReqs[i] && (option.optionMode === MysteryEncounterOptionMode.DEFAULT_OR_SPECIAL || option.optionMode === MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)) { if (option.hasRequirements() && this.optionsMeetsReqs[i] && (option.optionMode === MysteryEncounterOptionMode.DEFAULT_OR_SPECIAL || option.optionMode === MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)) {
// Options with special requirements that are met are automatically colored green // Options with special requirements that are met are automatically colored green
text = getEncounterText(this.scene, label, TextStyle.SUMMARY_GREEN); text = getEncounterText(this.scene, label, TextStyle.ME_OPTION_SPECIAL);
} else { } else {
text = getEncounterText(this.scene, label, optionDialogue.style ? optionDialogue.style : TextStyle.WINDOW); text = getEncounterText(this.scene, label, optionDialogue.style ? optionDialogue.style : TextStyle.ME_OPTION_DEFAULT);
} }
if (text) { if (text) {

View File

@ -518,7 +518,8 @@ export default class RunInfoUiHandler extends UiHandler {
const runTime = Utils.getPlayTimeString(this.runInfo.playTime); const runTime = Utils.getPlayTimeString(this.runInfo.playTime);
runInfoText.appendText(`${i18next.t("runHistory:runLength")}: ${runTime}`, false); runInfoText.appendText(`${i18next.t("runHistory:runLength")}: ${runTime}`, false);
const runMoney = Utils.formatMoney(this.scene.moneyFormat, this.runInfo.money); const runMoney = Utils.formatMoney(this.scene.moneyFormat, this.runInfo.money);
runInfoText.appendText(`[color=${getTextColor(TextStyle.MONEY)}]${i18next.t("battleScene:moneyOwned", { formattedMoney : runMoney })}[/color]`); const moneyTextColor = getTextColor(TextStyle.MONEY_WINDOW, false, this.scene.uiTheme);
runInfoText.appendText(`[color=${moneyTextColor}]${i18next.t("battleScene:moneyOwned", { formattedMoney : runMoney })}[/color]`);
runInfoText.setPosition(7, 70); runInfoText.setPosition(7, 70);
runInfoTextContainer.add(runInfoText); runInfoTextContainer.add(runInfoText);
// Luck // Luck

View File

@ -22,7 +22,8 @@ export enum TextStyle {
SUMMARY_GOLD, SUMMARY_GOLD,
SUMMARY_GRAY, SUMMARY_GRAY,
SUMMARY_GREEN, SUMMARY_GREEN,
MONEY, MONEY, // Money default styling (pale yellow)
MONEY_WINDOW, // Money displayed in Windows (needs different colors based on theme)
STATS_LABEL, STATS_LABEL,
STATS_VALUE, STATS_VALUE,
SETTINGS_VALUE, SETTINGS_VALUE,
@ -38,7 +39,9 @@ export enum TextStyle {
MOVE_PP_EMPTY, MOVE_PP_EMPTY,
SMALLER_WINDOW_ALT, SMALLER_WINDOW_ALT,
BGM_BAR, BGM_BAR,
PERFECT_IV PERFECT_IV,
ME_OPTION_DEFAULT, // Default style for choices in ME
ME_OPTION_SPECIAL, // Style for choices with special requirements in ME
} }
export interface TextStyleOptions { export interface TextStyleOptions {
@ -139,6 +142,8 @@ export function getTextStyleOptions(style: TextStyle, uiTheme: UiTheme, extraSty
case TextStyle.SUMMARY_GREEN: case TextStyle.SUMMARY_GREEN:
case TextStyle.WINDOW: case TextStyle.WINDOW:
case TextStyle.WINDOW_ALT: case TextStyle.WINDOW_ALT:
case TextStyle.ME_OPTION_DEFAULT:
case TextStyle.ME_OPTION_SPECIAL:
shadowXpos = 3; shadowXpos = 3;
shadowYpos = 3; shadowYpos = 3;
break; break;
@ -177,6 +182,7 @@ export function getTextStyleOptions(style: TextStyle, uiTheme: UiTheme, extraSty
break; break;
case TextStyle.BATTLE_INFO: case TextStyle.BATTLE_INFO:
case TextStyle.MONEY: case TextStyle.MONEY:
case TextStyle.MONEY_WINDOW:
case TextStyle.TOOLTIP_TITLE: case TextStyle.TOOLTIP_TITLE:
styleOptions.fontSize = defaultFontSize - 24; styleOptions.fontSize = defaultFontSize - 24;
shadowXpos = 3.5; shadowXpos = 3.5;
@ -238,13 +244,22 @@ export function getBBCodeFrag(content: string, textStyle: TextStyle, uiTheme: Ui
* - "red text" with TextStyle.SUMMARY_RED applied * - "red text" with TextStyle.SUMMARY_RED applied
* @param content string with styling that need to be applied for BBCodeTextObject * @param content string with styling that need to be applied for BBCodeTextObject
* @param primaryStyle Primary style is required in order to escape BBCode styling properly. * @param primaryStyle Primary style is required in order to escape BBCode styling properly.
* @param uiTheme * @param uiTheme the {@linkcode UiTheme} to get TextStyle for
* @param forWindow set to `true` if the text is to be displayed in a window ({@linkcode BattleScene.addWindow})
* it will replace all instances of the default MONEY TextStyle by {@linkcode TextStyle.MONEY_WINDOW}
*/ */
export function getTextWithColors(content: string, primaryStyle: TextStyle, uiTheme: UiTheme = UiTheme.DEFAULT): string { export function getTextWithColors(content: string, primaryStyle: TextStyle, uiTheme: UiTheme, forWindow?: boolean): string {
// Apply primary styling before anything else // Apply primary styling before anything else
let text = getBBCodeFrag(content, primaryStyle, uiTheme) + "[/color][/shadow]"; let text = getBBCodeFrag(content, primaryStyle, uiTheme) + "[/color][/shadow]";
const primaryStyleString = [ ...text.match(new RegExp(/\[color=[^\[]*\]\[shadow=[^\[]*\]/i))! ][0]; const primaryStyleString = [ ...text.match(new RegExp(/\[color=[^\[]*\]\[shadow=[^\[]*\]/i))! ][0];
/* For money text displayed in game windows, we can't use the default {@linkcode TextStyle.MONEY}
* or it will look wrong in legacy mode because of the different window background color
* So, for text to be displayed in windows replace all "@[MONEY]" with "@[MONEY_WINDOW]" */
if (forWindow) {
text = text.replace(/@\[MONEY\]/g, (_substring: string) => "@[MONEY_WINDOW]");
}
// Set custom colors // Set custom colors
text = text.replace(/@\[([^{]*)\]{([^}]*)}/gi, (substring, textStyle: string, textToColor: string) => { text = text.replace(/@\[([^{]*)\]{([^}]*)}/gi, (substring, textStyle: string, textToColor: string) => {
return "[/color][/shadow]" + getBBCodeFrag(textToColor, TextStyle[textStyle], uiTheme) + "[/color][/shadow]" + primaryStyleString; return "[/color][/shadow]" + getBBCodeFrag(textToColor, TextStyle[textStyle], uiTheme) + "[/color][/shadow]" + primaryStyleString;
@ -310,7 +325,12 @@ export function getTextColor(textStyle: TextStyle, shadow?: boolean, uiTheme: Ui
return !shadow ? "#f89890" : "#984038"; return !shadow ? "#f89890" : "#984038";
case TextStyle.SUMMARY_GOLD: case TextStyle.SUMMARY_GOLD:
case TextStyle.MONEY: case TextStyle.MONEY:
return !shadow ? "#e8e8a8" : "#a0a060"; return !shadow ? "#e8e8a8" : "#a0a060"; // Pale Yellow/Gold
case TextStyle.MONEY_WINDOW:
if (isLegacyTheme) {
return !shadow ? "#f8b050" : "#c07800"; // Gold
}
return !shadow ? "#e8e8a8" : "#a0a060"; // Pale Yellow/Gold
case TextStyle.SETTINGS_LOCKED: case TextStyle.SETTINGS_LOCKED:
case TextStyle.SUMMARY_GRAY: case TextStyle.SUMMARY_GRAY:
return !shadow ? "#a0a0a0" : "#636363"; return !shadow ? "#a0a0a0" : "#636363";
@ -332,6 +352,13 @@ export function getTextColor(textStyle: TextStyle, shadow?: boolean, uiTheme: Ui
return !shadow ? "#484848" : "#d0d0c8"; return !shadow ? "#484848" : "#d0d0c8";
case TextStyle.BGM_BAR: case TextStyle.BGM_BAR:
return !shadow ? "#f8f8f8" : "#6b5a73"; return !shadow ? "#f8f8f8" : "#6b5a73";
case TextStyle.ME_OPTION_DEFAULT:
return !shadow ? "#f8f8f8" : "#6b5a73"; // White
case TextStyle.ME_OPTION_SPECIAL:
if (isLegacyTheme) {
return !shadow ? "#f8b050" : "#c07800"; // Gold
}
return !shadow ? "#78c850" : "#306850"; // Green
} }
} }