Merge branch 'beta' into powder

This commit is contained in:
innerthunder 2024-11-18 14:02:53 -08:00
commit db55b1012e
25 changed files with 415 additions and 91 deletions

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "pokemon-rogue-battle", "name": "pokemon-rogue-battle",
"version": "1.1.6", "version": "1.2.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "pokemon-rogue-battle", "name": "pokemon-rogue-battle",
"version": "1.1.6", "version": "1.2.0",
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"@material/material-color-utilities": "^0.2.7", "@material/material-color-utilities": "^0.2.7",

View File

@ -1,7 +1,7 @@
{ {
"name": "pokemon-rogue-battle", "name": "pokemon-rogue-battle",
"private": true, "private": true,
"version": "1.1.6", "version": "1.2.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"start": "vite", "start": "vite",

@ -1 +1 @@
Subproject commit 5775faa6b3184082df73f6cdb96b253ea7dae3fe Subproject commit ed1b1df4776ccd4330e8ac1d2f44de611d04c2bc

View File

@ -4951,9 +4951,10 @@ class ForceSwitchOutHelper {
} }
/** /**
* For wild Pokémon battles, the Pokémon will flee if the conditions are met (waveIndex and double battles). * For wild Pokémon battles, the Pokémon will flee if the conditions are met (waveIndex and double battles).
* It will not flee if it is a Mystery Encounter with fleeing disabled (checked in `getSwitchOutCondition()`) or if it is a wave 10x wild boss
*/ */
} else { } else {
if (!pokemon.scene.currentBattle.waveIndex && pokemon.scene.currentBattle.waveIndex % 10 === 0) { if (!pokemon.scene.currentBattle.waveIndex || pokemon.scene.currentBattle.waveIndex % 10 === 0) {
return false; return false;
} }

View File

@ -3302,11 +3302,23 @@ export const tmSpecies: TmSpecies = {
Species.FERALIGATR, Species.FERALIGATR,
Species.SENTRET, Species.SENTRET,
Species.FURRET, Species.FURRET,
Species.HOOTHOOT,
Species.NOCTOWL, Species.NOCTOWL,
Species.LEDYBA,
Species.LEDIAN,
Species.SPINARAK,
Species.ARIADOS,
Species.CROBAT, Species.CROBAT,
Species.CHINCHOU, Species.CHINCHOU,
Species.LANTURN, Species.LANTURN,
Species.PICHU,
Species.CLEFFA,
Species.IGGLYBUFF, Species.IGGLYBUFF,
Species.TYROGUE,
Species.TOGEPI,
Species.TOGETIC,
Species.NATU,
Species.XATU,
Species.MAREEP, Species.MAREEP,
Species.FLAAFFY, Species.FLAAFFY,
Species.AMPHAROS, Species.AMPHAROS,
@ -3328,6 +3340,7 @@ export const tmSpecies: TmSpecies = {
Species.UMBREON, Species.UMBREON,
Species.MURKROW, Species.MURKROW,
Species.SLOWKING, Species.SLOWKING,
Species.MISDREAVUS,
Species.GIRAFARIG, Species.GIRAFARIG,
Species.PINECO, Species.PINECO,
Species.FORRETRESS, Species.FORRETRESS,
@ -3338,11 +3351,21 @@ export const tmSpecies: TmSpecies = {
Species.GRANBULL, Species.GRANBULL,
Species.QWILFISH, Species.QWILFISH,
Species.SCIZOR, Species.SCIZOR,
Species.SHUCKLE,
Species.HERACROSS, Species.HERACROSS,
Species.SNEASEL,
Species.TEDDIURSA, Species.TEDDIURSA,
Species.URSARING, Species.URSARING,
Species.SLUGMA,
Species.MAGCARGO,
Species.SWINUB, Species.SWINUB,
Species.PILOSWINE, Species.PILOSWINE,
Species.CORSOLA,
Species.REMORAID,
Species.OCTILLERY,
Species.DELIBIRD,
Species.MANTINE,
Species.SKARMORY,
Species.HOUNDOUR, Species.HOUNDOUR,
Species.HOUNDOOM, Species.HOUNDOOM,
Species.KINGDRA, Species.KINGDRA,
@ -3350,9 +3373,12 @@ export const tmSpecies: TmSpecies = {
Species.DONPHAN, Species.DONPHAN,
Species.PORYGON2, Species.PORYGON2,
Species.STANTLER, Species.STANTLER,
Species.TYROGUE,
Species.HITMONTOP, Species.HITMONTOP,
Species.SMOOCHUM,
Species.ELEKID, Species.ELEKID,
Species.MAGBY, Species.MAGBY,
Species.MILTANK,
Species.BLISSEY, Species.BLISSEY,
Species.RAIKOU, Species.RAIKOU,
Species.ENTEI, Species.ENTEI,
@ -3362,6 +3388,9 @@ export const tmSpecies: TmSpecies = {
Species.TYRANITAR, Species.TYRANITAR,
Species.LUGIA, Species.LUGIA,
Species.HO_OH, Species.HO_OH,
Species.CELEBI,
Species.TREECKO,
Species.GROVYLE,
Species.SCEPTILE, Species.SCEPTILE,
Species.TORCHIC, Species.TORCHIC,
Species.COMBUSKEN, Species.COMBUSKEN,
@ -3371,41 +3400,116 @@ export const tmSpecies: TmSpecies = {
Species.SWAMPERT, Species.SWAMPERT,
Species.POOCHYENA, Species.POOCHYENA,
Species.MIGHTYENA, Species.MIGHTYENA,
Species.ZIGZAGOON,
Species.LINOONE,
Species.BEAUTIFLY,
Species.DUSTOX,
Species.LOTAD, Species.LOTAD,
Species.LOMBRE, Species.LOMBRE,
Species.LUDICOLO, Species.LUDICOLO,
Species.SEEDOT, Species.SEEDOT,
Species.NUZLEAF, Species.NUZLEAF,
Species.SHIFTRY, Species.SHIFTRY,
Species.TAILLOW,
Species.SWELLOW,
Species.WINGULL,
Species.PELIPPER,
Species.RALTS,
Species.KIRLIA,
Species.GARDEVOIR,
Species.SURSKIT,
Species.MASQUERAIN,
Species.SHROOMISH,
Species.BRELOOM,
Species.VIGOROTH, Species.VIGOROTH,
Species.SLAKING, Species.SLAKING,
Species.NINCADA,
Species.NINJASK,
Species.SHEDINJA,
Species.WHISMUR,
Species.LOUDRED,
Species.EXPLOUD,
Species.MAKUHITA, Species.MAKUHITA,
Species.HARIYAMA, Species.HARIYAMA,
Species.AZURILL,
Species.NOSEPASS, Species.NOSEPASS,
Species.SKITTY,
Species.DELCATTY,
Species.SABLEYE,
Species.MAWILE,
Species.ARON,
Species.LAIRON,
Species.AGGRON,
Species.MEDITITE,
Species.MEDICHAM,
Species.ELECTRIKE,
Species.MANECTRIC,
Species.PLUSLE,
Species.MINUN,
Species.VOLBEAT, Species.VOLBEAT,
Species.ILLUMISE, Species.ILLUMISE,
Species.ROSELIA,
Species.GULPIN,
Species.SWALOT, Species.SWALOT,
Species.CARVANHA,
Species.SHARPEDO,
Species.WAILMER,
Species.WAILORD,
Species.NUMEL, Species.NUMEL,
Species.CAMERUPT, Species.CAMERUPT,
Species.TORKOAL, Species.TORKOAL,
Species.SPOINK,
Species.GRUMPIG,
Species.SPINDA,
Species.TRAPINCH,
Species.VIBRAVA,
Species.FLYGON, Species.FLYGON,
Species.CACNEA,
Species.CACTURNE,
Species.SWABLU,
Species.ALTARIA, Species.ALTARIA,
Species.ZANGOOSE, Species.ZANGOOSE,
Species.SEVIPER, Species.SEVIPER,
Species.LUNATONE,
Species.SOLROCK,
Species.BARBOACH, Species.BARBOACH,
Species.WHISCASH, Species.WHISCASH,
Species.CORPHISH, Species.CORPHISH,
Species.CRAWDAUNT, Species.CRAWDAUNT,
Species.BALTOY,
Species.CLAYDOL,
Species.LILEEP,
Species.CRADILY,
Species.ANORITH,
Species.ARMALDO,
Species.FEEBAS, Species.FEEBAS,
Species.MILOTIC, Species.MILOTIC,
Species.CASTFORM,
Species.KECLEON,
Species.SHUPPET,
Species.BANETTE,
Species.DUSKULL,
Species.DUSCLOPS,
Species.TROPIUS, Species.TROPIUS,
Species.CHIMECHO, Species.CHIMECHO,
Species.ABSOL,
Species.SNORUNT,
Species.GLALIE,
Species.SPHEAL,
Species.SEALEO,
Species.WALREIN,
Species.CLAMPERL,
Species.HUNTAIL,
Species.GOREBYSS,
Species.RELICANTH,
Species.LUVDISC,
Species.BAGON, Species.BAGON,
Species.SHELGON, Species.SHELGON,
Species.SALAMENCE, Species.SALAMENCE,
Species.METANG, Species.METANG,
Species.METAGROSS, Species.METAGROSS,
Species.REGIROCK, Species.REGIROCK,
Species.REGICE,
Species.REGISTEEL, Species.REGISTEEL,
Species.LATIAS, Species.LATIAS,
Species.LATIOS, Species.LATIOS,
@ -3413,6 +3517,7 @@ export const tmSpecies: TmSpecies = {
Species.GROUDON, Species.GROUDON,
Species.RAYQUAZA, Species.RAYQUAZA,
Species.JIRACHI, Species.JIRACHI,
Species.DEOXYS,
Species.TURTWIG, Species.TURTWIG,
Species.GROTLE, Species.GROTLE,
Species.TORTERRA, Species.TORTERRA,
@ -64246,12 +64351,16 @@ export const tmSpecies: TmSpecies = {
Species.BLOODMOON_URSALUNA, Species.BLOODMOON_URSALUNA,
], ],
[Moves.LIQUIDATION]: [ [Moves.LIQUIDATION]: [
Species.SQUIRTLE,
Species.WARTORTLE,
Species.BLASTOISE, Species.BLASTOISE,
Species.PSYDUCK, Species.PSYDUCK,
Species.GOLDUCK, Species.GOLDUCK,
Species.POLIWAG, Species.POLIWAG,
Species.POLIWHIRL, Species.POLIWHIRL,
Species.POLIWRATH, Species.POLIWRATH,
Species.TENTACOOL,
Species.TENTACRUEL,
Species.SLOWPOKE, Species.SLOWPOKE,
Species.SLOWBRO, Species.SLOWBRO,
Species.DEWGONG, Species.DEWGONG,
@ -64267,7 +64376,11 @@ export const tmSpecies: TmSpecies = {
Species.KABUTO, Species.KABUTO,
Species.KABUTOPS, Species.KABUTOPS,
Species.MEW, Species.MEW,
Species.TOTODILE,
Species.CROCONAW,
Species.FERALIGATR, Species.FERALIGATR,
Species.CHINCHOU,
Species.LANTURN,
Species.MARILL, Species.MARILL,
Species.AZUMARILL, Species.AZUMARILL,
Species.POLITOED, Species.POLITOED,
@ -64280,6 +64393,9 @@ export const tmSpecies: TmSpecies = {
Species.MANTINE, Species.MANTINE,
Species.KINGDRA, Species.KINGDRA,
Species.SUICUNE, Species.SUICUNE,
Species.LUGIA,
Species.MUDKIP,
Species.MARSHTOMP,
Species.SWAMPERT, Species.SWAMPERT,
Species.WINGULL, Species.WINGULL,
Species.PELIPPER, Species.PELIPPER,
@ -64296,6 +64412,8 @@ export const tmSpecies: TmSpecies = {
Species.WALREIN, Species.WALREIN,
Species.RELICANTH, Species.RELICANTH,
Species.LUVDISC, Species.LUVDISC,
Species.LATIAS,
Species.LATIOS,
Species.KYOGRE, Species.KYOGRE,
Species.PIPLUP, Species.PIPLUP,
Species.PRINPLUP, Species.PRINPLUP,
@ -64407,11 +64525,13 @@ export const tmSpecies: TmSpecies = {
Species.ONIX, Species.ONIX,
Species.HYPNO, Species.HYPNO,
Species.LICKITUNG, Species.LICKITUNG,
Species.RHYHORN,
Species.RHYDON, Species.RHYDON,
Species.LAPRAS, Species.LAPRAS,
Species.SNORLAX, Species.SNORLAX,
Species.DRAGONITE, Species.DRAGONITE,
Species.MEW, Species.MEW,
Species.MEGANIUM,
Species.SUDOWOODO, Species.SUDOWOODO,
Species.QUAGSIRE, Species.QUAGSIRE,
Species.FORRETRESS, Species.FORRETRESS,
@ -64445,6 +64565,8 @@ export const tmSpecies: TmSpecies = {
Species.REGISTEEL, Species.REGISTEEL,
Species.GROUDON, Species.GROUDON,
Species.TORTERRA, Species.TORTERRA,
Species.RAMPARDOS,
Species.BASTIODON,
Species.BRONZONG, Species.BRONZONG,
Species.HIPPOPOTAS, Species.HIPPOPOTAS,
Species.HIPPOWDON, Species.HIPPOWDON,
@ -64459,6 +64581,7 @@ export const tmSpecies: TmSpecies = {
Species.HEATRAN, Species.HEATRAN,
Species.REGIGIGAS, Species.REGIGIGAS,
Species.ARCEUS, Species.ARCEUS,
Species.EMBOAR,
Species.ROGGENROLA, Species.ROGGENROLA,
Species.BOLDORE, Species.BOLDORE,
Species.GIGALITH, Species.GIGALITH,
@ -64471,6 +64594,7 @@ export const tmSpecies: TmSpecies = {
Species.CUBCHOO, Species.CUBCHOO,
Species.BEARTIC, Species.BEARTIC,
Species.GOLURK, Species.GOLURK,
Species.COBALION,
Species.RESHIRAM, Species.RESHIRAM,
Species.ZEKROM, Species.ZEKROM,
Species.KYUREM, Species.KYUREM,

View File

@ -1553,9 +1553,14 @@ export class ContactBurnProtectedTag extends DamageProtectedTag {
} }
} }
/**
* `BattlerTag` class for effects that cause the affected Pokemon to survive lethal attacks at 1 HP.
* Used for {@link https://bulbapedia.bulbagarden.net/wiki/Endure_(move) | Endure} and
* Endure Tokens.
*/
export class EnduringTag extends BattlerTag { export class EnduringTag extends BattlerTag {
constructor(sourceMove: Moves) { constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType, sourceMove: Moves) {
super(BattlerTagType.ENDURING, BattlerTagLapseType.TURN_END, 0, sourceMove); super(tagType, lapseType, 0, sourceMove);
} }
onAdd(pokemon: Pokemon): void { onAdd(pokemon: Pokemon): void {
@ -3062,7 +3067,9 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source
case BattlerTagType.BURNING_BULWARK: case BattlerTagType.BURNING_BULWARK:
return new ContactBurnProtectedTag(sourceMove); return new ContactBurnProtectedTag(sourceMove);
case BattlerTagType.ENDURING: case BattlerTagType.ENDURING:
return new EnduringTag(sourceMove); return new EnduringTag(tagType, BattlerTagLapseType.TURN_END, sourceMove);
case BattlerTagType.ENDURE_TOKEN:
return new EnduringTag(tagType, BattlerTagLapseType.AFTER_HIT, sourceMove);
case BattlerTagType.STURDY: case BattlerTagType.STURDY:
return new SturdyTag(sourceMove); return new SturdyTag(sourceMove);
case BattlerTagType.PERISH_SONG: case BattlerTagType.PERISH_SONG:

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

@ -92,5 +92,6 @@ export enum BattlerTagType {
COMMANDED = "COMMANDED", COMMANDED = "COMMANDED",
GRUDGE = "GRUDGE", GRUDGE = "GRUDGE",
PSYCHO_SHIFT = "PSYCHO_SHIFT", PSYCHO_SHIFT = "PSYCHO_SHIFT",
ENDURE_TOKEN = "ENDURE_TOKEN",
POWDER = "POWDER", POWDER = "POWDER",
} }

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);
@ -2617,6 +2622,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
const fixedDamage = new Utils.IntegerHolder(0); const fixedDamage = new Utils.IntegerHolder(0);
applyMoveAttrs(FixedDamageAttr, source, this, move, fixedDamage); applyMoveAttrs(FixedDamageAttr, source, this, move, fixedDamage);
if (fixedDamage.value) { if (fixedDamage.value) {
const multiLensMultiplier = new Utils.NumberHolder(1);
source.scene.applyModifiers(PokemonMultiHitModifier, source.isPlayer(), source, move.id, null, multiLensMultiplier);
fixedDamage.value = Utils.toDmgValue(fixedDamage.value * multiLensMultiplier.value);
return { return {
cancelled: false, cancelled: false,
result: HitResult.EFFECTIVE, result: HitResult.EFFECTIVE,
@ -2955,6 +2964,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
surviveDamage.value = this.lapseTag(BattlerTagType.ENDURING); surviveDamage.value = this.lapseTag(BattlerTagType.ENDURING);
} else if (this.hp > 1 && this.getTag(BattlerTagType.STURDY)) { } else if (this.hp > 1 && this.getTag(BattlerTagType.STURDY)) {
surviveDamage.value = this.lapseTag(BattlerTagType.STURDY); surviveDamage.value = this.lapseTag(BattlerTagType.STURDY);
} else if (this.hp >= 1 && this.getTag(BattlerTagType.ENDURE_TOKEN)) {
surviveDamage.value = this.lapseTag(BattlerTagType.ENDURE_TOKEN);
} }
if (!surviveDamage.value) { if (!surviveDamage.value) {
this.scene.applyModifiers(SurviveDamageModifier, this.isPlayer(), this, surviveDamage); this.scene.applyModifiers(SurviveDamageModifier, this.isPlayer(), this, surviveDamage);
@ -3090,10 +3101,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
lapseTag(tagType: BattlerTagType): boolean { lapseTag(tagType: BattlerTagType): boolean {
const tags = this.summonData?.tags; if (!this.summonData) {
if (isNullOrUndefined(tags)) {
return false; return false;
} }
const tags = this.summonData.tags;
const tag = tags.find(t => t.tagType === tagType); const tag = tags.find(t => t.tagType === tagType);
if (tag && !(tag.lapse(this, BattlerTagLapseType.CUSTOM))) { if (tag && !(tag.lapse(this, BattlerTagLapseType.CUSTOM))) {
tag.onRemove(this); tag.onRemove(this);
@ -3103,6 +3114,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
lapseTags(lapseType: BattlerTagLapseType): void { lapseTags(lapseType: BattlerTagLapseType): void {
if (!this.summonData) {
return;
}
const tags = this.summonData.tags; const tags = this.summonData.tags;
tags.filter(t => lapseType === BattlerTagLapseType.FAINT || ((t.lapseTypes.some(lType => lType === lapseType)) && !(t.lapse(this, lapseType)))).forEach(t => { tags.filter(t => lapseType === BattlerTagLapseType.FAINT || ((t.lapseTypes.some(lType => lType === lapseType)) && !(t.lapse(this, lapseType)))).forEach(t => {
t.onRemove(this); t.onRemove(this);
@ -3111,6 +3125,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
removeTag(tagType: BattlerTagType): boolean { removeTag(tagType: BattlerTagType): boolean {
if (!this.summonData) {
return false;
}
const tags = this.summonData.tags; const tags = this.summonData.tags;
const tag = tags.find(t => t.tagType === tagType); const tag = tags.find(t => t.tagType === tagType);
if (tag) { if (tag) {

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;
} }
@ -2733,10 +2727,18 @@ export class PokemonMultiHitModifier extends PokemonHeldItemModifier {
* Additional strikes beyond that are given a 0.25x damage multiplier * Additional strikes beyond that are given a 0.25x damage multiplier
*/ */
private applyDamageModifier(pokemon: Pokemon, damageMultiplier: NumberHolder): boolean { private applyDamageModifier(pokemon: Pokemon, damageMultiplier: NumberHolder): boolean {
damageMultiplier.value = (pokemon.turnData.hitsLeft === pokemon.turnData.hitCount) if (pokemon.turnData.hitsLeft === pokemon.turnData.hitCount) {
? (1 - (0.25 * this.getStackCount())) // Reduce first hit by 25% for each stack count
: 0.25; damageMultiplier.value *= 1 - 0.25 * this.getStackCount();
return true; return true;
} else if (pokemon.turnData.hitCount - pokemon.turnData.hitsLeft !== this.getStackCount() + 1) {
// Deal 25% damage for each remaining Multi Lens hit
damageMultiplier.value *= 0.25;
return true;
} else {
// An extra hit not caused by Multi Lens -- assume it is Parental Bond
return false;
}
} }
getMaxHeldItemCount(pokemon: Pokemon): number { getMaxHeldItemCount(pokemon: Pokemon): number {
@ -2818,7 +2820,7 @@ export class MoneyRewardModifier extends ConsumableModifier {
battleScene.getPlayerParty().map(p => { battleScene.getPlayerParty().map(p => {
if (p.species?.speciesId === Species.GIMMIGHOUL || p.fusionSpecies?.speciesId === Species.GIMMIGHOUL) { if (p.species?.speciesId === Species.GIMMIGHOUL || p.fusionSpecies?.speciesId === Species.GIMMIGHOUL) {
p.evoCounter ? p.evoCounter++ : p.evoCounter = 1; p.evoCounter ? p.evoCounter += Math.min(Math.floor(this.moneyMultiplier), 3) : p.evoCounter = Math.min(Math.floor(this.moneyMultiplier), 3);
const modifier = getModifierType(modifierTypes.EVOLUTION_TRACKER_GIMMIGHOUL).newModifier(p) as EvoTrackerModifier; const modifier = getModifierType(modifierTypes.EVOLUTION_TRACKER_GIMMIGHOUL).newModifier(p) as EvoTrackerModifier;
battleScene.addModifier(modifier); battleScene.addModifier(modifier);
} }
@ -3647,8 +3649,8 @@ export class EnemyEndureChanceModifier extends EnemyPersistentModifier {
} }
/** /**
* Applies {@linkcode EnemyEndureChanceModifier} * Applies a chance of enduring a lethal hit of an attack
* @param target {@linkcode Pokemon} to apply the {@linkcode BattlerTagType.ENDURING} chance to * @param target the {@linkcode Pokemon} to apply the {@linkcode BattlerTagType.ENDURING} chance to
* @returns `true` if {@linkcode Pokemon} endured * @returns `true` if {@linkcode Pokemon} endured
*/ */
override apply(target: Pokemon): boolean { override apply(target: Pokemon): boolean {
@ -3656,7 +3658,7 @@ export class EnemyEndureChanceModifier extends EnemyPersistentModifier {
return false; return false;
} }
target.addTag(BattlerTagType.ENDURING, 1); target.addTag(BattlerTagType.ENDURE_TOKEN, 1);
target.battleData.endured = true; target.battleData.endured = 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

@ -129,7 +129,7 @@ class SessionVersionConverter extends VersionConverter {
if (curMajor === 1) { if (curMajor === 1) {
if (curMinor === 0) { if (curMinor === 0) {
if (curPatch <= 4) { if (curPatch <= 5) {
console.log("Applying v1.0.4 session data migration!"); console.log("Applying v1.0.4 session data migration!");
this.callMigrators(data, v1_0_4.sessionMigrators); this.callMigrators(data, v1_0_4.sessionMigrators);
} }

View File

@ -1,6 +1,8 @@
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 { CustomPokemonData } from "#app/data/custom-pokemon-data";
import { isNullOrUndefined } from "#app/utils";
export const systemMigrators = [ export const systemMigrators = [
/** /**
@ -46,12 +48,14 @@ export const systemMigrators = [
* @param data {@linkcode SystemSaveData} * @param data {@linkcode SystemSaveData}
*/ */
function fixStarterData(data: SystemSaveData) { function fixStarterData(data: SystemSaveData) {
for (const starterId of defaultStarterSpecies) { if (!isNullOrUndefined(data.starterData)) {
if (data.starterData[starterId]?.abilityAttr) { for (const starterId of defaultStarterSpecies) {
data.starterData[starterId].abilityAttr |= AbilityAttr.ABILITY_1; if (data.starterData[starterId]?.abilityAttr) {
} data.starterData[starterId].abilityAttr |= AbilityAttr.ABILITY_1;
if (data.dexData[starterId]?.caughtAttr) { }
data.dexData[starterId].caughtAttr |= DexAttr.FEMALE; if (data.dexData[starterId]?.caughtAttr) {
data.dexData[starterId].caughtAttr |= DexAttr.FEMALE;
}
} }
} }
} }
@ -131,5 +135,28 @@ export const sessionMigrators = [
m.className = "ResetNegativeStatStageModifier"; m.className = "ResetNegativeStatStageModifier";
} }
}); });
},
/**
* Converts old Pokemon natureOverride and mysteryEncounterData
* to use the new conjoined {@linkcode Pokemon.customPokemonData} structure instead.
* @param data {@linkcode SessionSaveData}
*/
function migrateCustomPokemonDataAndNatureOverrides(data: SessionSaveData) {
// Fix Pokemon nature overrides and custom data migration
data.party.forEach(pokemon => {
if (pokemon["mysteryEncounterPokemonData"]) {
pokemon.customPokemonData = new CustomPokemonData(pokemon["mysteryEncounterPokemonData"]);
pokemon["mysteryEncounterPokemonData"] = null;
}
if (pokemon["fusionMysteryEncounterPokemonData"]) {
pokemon.fusionCustomPokemonData = new CustomPokemonData(pokemon["fusionMysteryEncounterPokemonData"]);
pokemon["fusionMysteryEncounterPokemonData"] = null;
}
pokemon.customPokemonData = pokemon.customPokemonData ?? new CustomPokemonData();
if (!isNullOrUndefined(pokemon["natureOverride"]) && pokemon["natureOverride"] >= 0) {
pokemon.customPokemonData.nature = pokemon["natureOverride"];
pokemon["natureOverride"] = -1;
}
});
} }
] as const; ] as const;

View File

@ -1,32 +1,5 @@
import { SessionSaveData } from "../../game-data";
import { CustomPokemonData } from "#app/data/custom-pokemon-data";
export const systemMigrators = [] as const; export const systemMigrators = [] as const;
export const settingsMigrators = [] as const; export const settingsMigrators = [] as const;
export const sessionMigrators = [ export const sessionMigrators = [] as const;
/**
* Converts old Pokemon natureOverride and mysteryEncounterData
* to use the new conjoined {@linkcode Pokemon.customPokemonData} structure instead.
* @param data {@linkcode SessionSaveData}
*/
function migrateCustomPokemonDataAndNatureOverrides(data: SessionSaveData) {
// Fix Pokemon nature overrides and custom data migration
data.party.forEach(pokemon => {
if (pokemon["mysteryEncounterPokemonData"]) {
pokemon.customPokemonData = new CustomPokemonData(pokemon["mysteryEncounterPokemonData"]);
pokemon["mysteryEncounterPokemonData"] = null;
}
if (pokemon["fusionMysteryEncounterPokemonData"]) {
pokemon.fusionCustomPokemonData = new CustomPokemonData(pokemon["fusionMysteryEncounterPokemonData"]);
pokemon["fusionMysteryEncounterPokemonData"] = null;
}
pokemon.customPokemonData = pokemon.customPokemonData ?? new CustomPokemonData();
if (pokemon["natureOverride"] && pokemon["natureOverride"] >= 0) {
pokemon.customPokemonData.nature = pokemon["natureOverride"];
pokemon["natureOverride"] = -1;
}
});
}
] as const;

View File

@ -313,7 +313,7 @@ describe("Abilities - Parental Bond", () => {
await game.phaseInterceptor.to("MoveEndPhase", false); await game.phaseInterceptor.to("MoveEndPhase", false);
expect(enemyPokemon.hp).toBe(enemyStartingHp - 300); expect(enemyPokemon.hp).toBe(enemyStartingHp - 200);
} }
); );

View File

@ -613,4 +613,23 @@ describe("Abilities - Wimp Out", () => {
confirmNoSwitch(); confirmNoSwitch();
}); });
it("should not activate on wave X0 bosses", async () => {
game.override.enemyAbility(Abilities.WIMP_OUT)
.startingLevel(5850)
.startingWave(10);
await game.classicMode.startBattle([ Species.GOLISOPOD ]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
// Use 2 turns of False Swipe due to opponent's health bar shield
game.move.select(Moves.FALSE_SWIPE);
await game.toNextTurn();
game.move.select(Moves.FALSE_SWIPE);
await game.toNextTurn();
const isVisible = enemyPokemon.visible;
const hasFled = enemyPokemon.switchOutStatus;
expect(isVisible && !hasFled).toBe(true);
});
}); });

View File

@ -32,8 +32,8 @@ describe("Items - Multi Lens", () => {
.enemySpecies(Species.SNORLAX) .enemySpecies(Species.SNORLAX)
.enemyAbility(Abilities.BALL_FETCH) .enemyAbility(Abilities.BALL_FETCH)
.enemyMoveset(Moves.SPLASH) .enemyMoveset(Moves.SPLASH)
.startingLevel(100) .startingLevel(99) // Check for proper rounding on Seismic Toss damage reduction
.enemyLevel(100); .enemyLevel(99);
}); });
it.each([ it.each([
@ -114,4 +114,25 @@ describe("Items - Multi Lens", () => {
expect(magikarp.turnData.hitCount).toBe(2); expect(magikarp.turnData.hitCount).toBe(2);
}); });
it("should enhance fixed-damage moves while also applying damage reduction", async () => {
game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }])
.moveset(Moves.SEISMIC_TOSS);
await game.classicMode.startBattle([ Species.MAGIKARP ]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.scene.getEnemyPokemon()!;
const spy = vi.spyOn(enemyPokemon, "getAttackDamage");
game.move.select(Moves.SEISMIC_TOSS);
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
await game.phaseInterceptor.to("MoveEndPhase");
const damageResults = spy.mock.results.map(result => result.value?.damage);
expect(damageResults).toHaveLength(2);
expect(damageResults[0]).toBe(Math.floor(playerPokemon.level * 0.75));
expect(damageResults[1]).toBe(Math.floor(playerPokemon.level * 0.25));
});
}); });

View File

@ -0,0 +1,65 @@
import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import GameManager from "#test/utils/gameManager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
describe("Moves - Endure", () => {
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.override
.moveset([ Moves.THUNDER, Moves.BULLET_SEED, Moves.TOXIC ])
.ability(Abilities.SKILL_LINK)
.startingLevel(100)
.battleType("single")
.disableCrits()
.enemySpecies(Species.MAGIKARP)
.enemyAbility(Abilities.NO_GUARD)
.enemyMoveset(Moves.ENDURE);
});
it("should let the pokemon survive with 1 HP", async () => {
await game.classicMode.startBattle([ Species.ARCEUS ]);
game.move.select(Moves.THUNDER);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getEnemyPokemon()!.hp).toBe(1);
});
it("should let the pokemon survive with 1 HP when hit with a multihit move", async () => {
await game.classicMode.startBattle([ Species.ARCEUS ]);
game.move.select(Moves.BULLET_SEED);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getEnemyPokemon()!.hp).toBe(1);
});
it("shouldn't prevent fainting from indirect damage", async () => {
game.override.enemyLevel(100);
await game.classicMode.startBattle([ Species.ARCEUS ]);
const enemy = game.scene.getEnemyPokemon()!;
enemy.hp = 2;
game.move.select(Moves.TOXIC);
await game.phaseInterceptor.to("VictoryPhase");
expect(enemy.isFainted()).toBe(true);
});
});

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
} }
} }