Add natures

This commit is contained in:
Flashfyre 2024-01-05 22:24:05 -05:00
parent 6dc868272f
commit 8cc1982859
16 changed files with 386 additions and 78 deletions

View File

@ -89,11 +89,6 @@ body {
line-height: 0.9;
}
#apadLabelMenu {
margin-left: 10%;
line-height: 1.1;
}
#apad > :nth-child(2) {
position: relative;
right: var(--controls-size);
@ -101,12 +96,12 @@ body {
#apad .apadRectBtn {
position: relative;
text-align: center;
padding-right: 10%;
border-radius: 10%;
margin-top: calc(var(--controls-size) * -0.4);
bottom: calc(var(--controls-size) * 0.05);
left: calc(var(--controls-size) * 0.21);
width: calc(var(--controls-size) * 0.6);
height: calc(var(--controls-size) * 0.4);
height: calc(var(--controls-size) * 0.3);
}
#apad .apadSqBtn {
@ -118,21 +113,34 @@ body {
#apad .apadBtnContainer {
position: relative;
display: flex;
}
#apad .apadRectBtnContainer {
flex-wrap: wrap;
margin-top: calc(var(--controls-size) * -0.8);
left: calc(var(--controls-size) * 0.175);
height: calc(var(--controls-size) * 0.8);
}
#apad .apadSqBtnContainer {
flex-wrap: wrap;
justify-content: space-evenly;
align-items: center;
margin-bottom: calc(var(--controls-size) * -0.8);
top: calc(var(--controls-size) * -0.9);
left: calc(var(--controls-size) * 0.1);
width: calc(var(--controls-size) * 0.8);
height: calc(var(--controls-size) * 0.8);
}
#touchControls:not([data-ui-mode='STARTER_SELECT']) #apad .apadBtnContainer {
#apad .apadRectBtnContainer > #apadMenu {
align-self: flex-end;
}
#touchControls:not([data-ui-mode='STARTER_SELECT']) #apad .apadRectBtnContainer > #apadCycleNature, #touchControls:not([data-ui-mode='STARTER_SELECT']) #apad .apadSqBtnContainer {
display: none;
}
#apad .apadRectBtn + .apadBtnContainer {
#apad .apadRectBtnContainer + .apadSqBtnContainer {
top: calc(var(--controls-size) * -1.9);
left: calc(var(--controls-size) * -0.9);
}

View File

@ -41,10 +41,15 @@
<div id="apadCancel" class="apadCircBtn apadBtn" data-key="CANCEL">
<text id="apadLabelCancel" class="apadLabel">B</text>
</div>
<div id="apadMenu" class="apadRectBtn apadBtn" data-key="MENU">
<text id="apadLabelMenu" class="apadLabel apadLabelSmall">Menu</text>
<div class="apadBtnContainer apadRectBtnContainer">
<div id="apadCycleNature" class="apadRectBtn apadBtn" data-key="CYCLE_NATURE">
<text class="apadLabel apadLabelSmall">N</text>
</div>
<div id="apadMenu" class="apadRectBtn apadBtn" data-key="MENU">
<text class="apadLabel apadLabelSmall">Menu</text>
</div>
</div>
<div class="apadBtnContainer">
<div class="apadBtnContainer apadSqBtnContainer">
<div id="apadCycleShiny" class="apadSqBtn apadBtn" data-key="CYCLE_SHINY">
<text class="apadLabel apadLabelSmall">R</text>
</div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -25,7 +25,7 @@ import { Gender } from "./data/gender";
import { Weather, WeatherType, getRandomWeatherType, getWeatherDamageMessage, getWeatherLapseMessage } from "./data/weather";
import { TempBattleStat } from "./data/temp-battle-stat";
import { ArenaTagType, ArenaTrapTag, TrickRoomTag } from "./data/arena-tag";
import { Abilities, CheckTrappedAbAttr, IgnoreOpponentStatChangesAbAttr, PostAttackAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreWeatherEffectAbAttrs } from "./data/ability";
import { Abilities, CheckTrappedAbAttr, IgnoreOpponentStatChangesAbAttr, PostAttackAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreWeatherEffectAbAttrs } from "./data/ability";
import { Unlockables, getUnlockableName } from "./system/unlockables";
import { getBiomeKey } from "./arena";
import { BattleType, BattlerIndex, TurnCommand } from "./battle";
@ -39,6 +39,7 @@ import { vouchers } from "./system/voucher";
import { loggedInUser, updateUserInfo } from "./account";
import { GameDataType } from "./system/game-data";
import { addPokeballCaptureStars, addPokeballOpenParticles } from "./anims";
import { Nature } from "./data/nature";
export class LoginPhase extends BattlePhase {
private showText: boolean;
@ -228,7 +229,7 @@ export class SelectStarterPhase extends BattlePhase {
? !starterProps.female ? Gender.MALE : Gender.FEMALE
: Gender.GENDERLESS;
const starterIvs = this.scene.gameData.dexData[starter.species.speciesId].ivs.slice(0);
const starterPokemon = new PlayerPokemon(this.scene, starter.species, startingLevel, starterProps.abilityIndex, starterProps.formIndex, starterGender, starterProps.shiny, starterIvs);
const starterPokemon = new PlayerPokemon(this.scene, starter.species, startingLevel, starterProps.abilityIndex, starterProps.formIndex, starterGender, starterProps.shiny, starterIvs, starter.nature);
if (starter.pokerus)
starterPokemon.pokerus = true;
if (this.scene.gameMode === GameMode.SPLICED_ENDLESS)
@ -372,6 +373,9 @@ export class EncounterPhase extends BattlePhase {
else {
const enemySpecies = this.scene.randomSpecies(battle.waveIndex, level, true);
battle.enemyParty[e] = new EnemyPokemon(this.scene, enemySpecies, level, false);
this.scene.getParty().slice(0, !battle.double ? 1 : 2).reverse().forEach(playerPokemon => {
applyAbAttrs(SyncEncounterNatureAbAttr, playerPokemon, null, battle.enemyParty[e]);
});
}
}
const enemyPokemon = this.scene.getEnemyParty()[e];

View File

@ -63,6 +63,7 @@ export enum Button {
CYCLE_FORM,
CYCLE_GENDER,
CYCLE_ABILITY,
CYCLE_NATURE,
QUICK_START,
AUTO,
SPEED_UP,
@ -575,6 +576,7 @@ export default class BattleScene extends Phaser.Scene {
[Button.CYCLE_FORM]: [keyCodes.F],
[Button.CYCLE_GENDER]: [keyCodes.G],
[Button.CYCLE_ABILITY]: [keyCodes.E],
[Button.CYCLE_NATURE]: [keyCodes.N],
[Button.QUICK_START]: [keyCodes.Q],
[Button.AUTO]: [keyCodes.F2],
[Button.SPEED_UP]: [keyCodes.PLUS],
@ -1032,6 +1034,8 @@ export default class BattleScene extends Phaser.Scene {
inputSuccess = this.ui.processInput(Button.CYCLE_GENDER);
else if (this.isButtonPressed(Button.CYCLE_ABILITY))
inputSuccess = this.ui.processInput(Button.CYCLE_ABILITY);
else if (this.isButtonPressed(Button.CYCLE_NATURE))
inputSuccess = this.ui.processInput(Button.CYCLE_NATURE);
else
return;
} else if (this.isButtonPressed(Button.SPEED_UP)) {

View File

@ -1005,6 +1005,18 @@ export class WeightMultiplierAbAttr extends AbAttr {
}
}
export class SyncEncounterNatureAbAttr extends AbAttr {
constructor() {
super(false);
}
apply(pokemon: Pokemon, cancelled: Utils.BooleanHolder, args: any[]): boolean {
(args[0] as Pokemon).setNature(pokemon.nature);
return true;
}
}
function applyAbAttrsInternal<TAttr extends AbAttr>(attrType: { new(...args: any[]): TAttr },
pokemon: Pokemon, applyFunc: AbAttrApplyFunc<TAttr>, isAsync?: boolean, showAbilityInstant?: boolean): Promise<void> {
return new Promise(resolve => {
@ -1505,7 +1517,8 @@ export function initAbilities() {
.attr(TypeImmunityAbAttr, Type.GROUND, (pokemon: Pokemon) => !pokemon.getTag(BattlerTagType.IGNORE_FLYING) && !pokemon.scene.arena.getTag(ArenaTagType.GRAVITY)),
new Ability(Abilities.EFFECT_SPORE, "Effect Spore", "Contact with the Pokémon may inflict poison, sleep, or paralysis on its attacker.", 3)
.attr(PostDefendContactApplyStatusEffectAbAttr, 10, StatusEffect.POISON, StatusEffect.PARALYSIS, StatusEffect.SLEEP),
new Ability(Abilities.SYNCHRONIZE, "Synchronize (N)", "The attacker will receive the same status condition if it inflicts a burn, poison, or paralysis to the Pokémon.", 3),
new Ability(Abilities.SYNCHRONIZE, "Synchronize (N)", "The attacker will receive the same status condition if it inflicts a burn, poison, or paralysis to the Pokémon.", 3)
.attr(SyncEncounterNatureAbAttr),
new Ability(Abilities.CLEAR_BODY, "Clear Body", "Prevents other Pokémon's moves or Abilities from lowering the Pokémon's stats.", 3)
.attr(ProtectStatAbAttr),
new Ability(Abilities.NATURAL_CURE, "Natural Cure (N)", "All status conditions heal when the Pokémon switches out.", 3),

130
src/data/nature.ts Normal file
View File

@ -0,0 +1,130 @@
import { Stat, getStatName } from "./pokemon-stat";
import * as Utils from "../utils";
import { TextStyle, getBBCodeFrag } from "../ui/text";
export enum Nature {
HARDY,
LONELY,
BRAVE,
ADAMANT,
NAUGHTY,
BOLD,
DOCILE,
RELAXED,
IMPISH,
LAX,
TIMID,
HASTY,
SERIOUS,
JOLLY,
NAIVE,
MODEST,
MILD,
QUIET,
BASHFUL,
RASH,
CALM,
GENTLE,
SASSY,
CAREFUL,
QUIRKY
}
export function getNatureName(nature: Nature, includeStatEffects: boolean = false, forStarterSelect: boolean = false): string {
let ret = Utils.toReadableString(Nature[nature]);
if (includeStatEffects) {
const stats = Utils.getEnumValues(Stat).slice(1);
let increasedStat: Stat = null;
let decreasedStat: Stat = null;
for (let stat of stats) {
const multiplier = getNatureStatMultiplier(nature, stat);
if (multiplier > 1)
increasedStat = stat;
else
decreasedStat = stat;
}
const textStyle = forStarterSelect ? TextStyle.SUMMARY : TextStyle.WINDOW;
if (increasedStat && decreasedStat)
ret = `${getBBCodeFrag(`${ret}${!forStarterSelect ? '\n' : ' '}(`, textStyle)}${getBBCodeFrag(`+${getStatName(increasedStat, true)}`, TextStyle.SUMMARY_BLUE)}${getBBCodeFrag('/', textStyle)}${getBBCodeFrag(`-${getStatName(decreasedStat, true)}`, TextStyle.SUMMARY_PINK)}${getBBCodeFrag(')', textStyle)}`;
else
ret = getBBCodeFrag(`${ret}${!forStarterSelect ? '\n' : ' '}(-)`, textStyle);
}
return ret;
}
export function getNatureStatMultiplier(nature: Nature, stat: Stat): number {
switch (stat) {
case Stat.ATK:
switch (nature) {
case Nature.LONELY:
case Nature.BRAVE:
case Nature.ADAMANT:
case Nature.NAUGHTY:
return 1.1;
case Nature.BOLD:
case Nature.TIMID:
case Nature.MODEST:
case Nature.CALM:
return 0.9;
}
break;
case Stat.DEF:
switch (nature) {
case Nature.BOLD:
case Nature.RELAXED:
case Nature.IMPISH:
case Nature.LAX:
return 1.1;
case Nature.LONELY:
case Nature.HASTY:
case Nature.MILD:
case Nature.GENTLE:
return 0.9;
}
break;
case Stat.SPATK:
switch (nature) {
case Nature.MODEST:
case Nature.MILD:
case Nature.QUIET:
case Nature.RASH:
return 1.1;
case Nature.ADAMANT:
case Nature.IMPISH:
case Nature.JOLLY:
case Nature.CAREFUL:
return 0.9;
}
break;
case Stat.SPDEF:
switch (nature) {
case Nature.CALM:
case Nature.GENTLE:
case Nature.SASSY:
case Nature.CAREFUL:
return 1.1;
case Nature.NAUGHTY:
case Nature.LAX:
case Nature.NAIVE:
case Nature.RASH:
return 0.9;
}
break;
case Stat.SPD:
switch (nature) {
case Nature.TIMID:
case Nature.HASTY:
case Nature.JOLLY:
case Nature.NAIVE:
return 1.1;
case Nature.BRAVE:
case Nature.RELAXED:
case Nature.QUIET:
case Nature.SASSY:
return 0.9;
}
break;
}
return 1;
}

View File

@ -7,26 +7,26 @@ export enum Stat {
SPD
};
export function getStatName(stat: Stat) {
export function getStatName(stat: Stat, shorten: boolean = false) {
let ret: string;
switch (stat) {
case Stat.HP:
ret = 'Max. HP';
ret = !shorten ? 'Max. HP' : 'MaxHP';
break;
case Stat.ATK:
ret = 'Attack';
ret = !shorten ? 'Attack' : 'Atk';
break;
case Stat.DEF:
ret = 'Defense';
ret = !shorten ? 'Defense' : 'Def';
break;
case Stat.SPATK:
ret = 'Sp. Atk';
ret = !shorten ? 'Sp. Atk' : 'SpAtk';
break;
case Stat.SPDEF:
ret = 'Sp. Def';
ret = !shorten ? 'Sp. Def' : 'SpDef';
break;
case Stat.SPD:
ret = 'Speed';
ret = !shorten ? 'Speed' : 'Spd';
break;
}
return ret;

View File

@ -7,13 +7,14 @@ import { EGG_SEED, Egg, GachaType, getLegendaryGachaSpeciesForTimestamp, getType
import EggHatchSceneHandler from "./ui/egg-hatch-scene-handler";
import { ModifierTier } from "./modifier/modifier-type";
import { Species } from "./data/species";
import Pokemon, { PlayerPokemon } from "./pokemon";
import { PlayerPokemon } from "./pokemon";
import { getPokemonSpecies, speciesStarters } from "./data/pokemon-species";
import { StatsContainer } from "./ui/stats-container";
import { TextStyle, addTextObject } from "./ui/text";
import { TextStyle, addBBCodeTextObject, addTextObject } from "./ui/text";
import { Gender, getGenderColor, getGenderSymbol } from "./data/gender";
import { achvs } from "./system/achv";
import { addWindow } from "./ui/window";
import { getNatureName } from "./data/nature";
export class EggHatchPhase extends BattlePhase {
private egg: Egg;
@ -91,55 +92,58 @@ export class EggHatchPhase extends BattlePhase {
this.eggHatchOverlay.setAlpha(0);
this.scene.fieldUI.add(this.eggHatchOverlay);
const infoBg = addWindow(this.scene, 0, 0, 96, 116);
const infoBg = addWindow(this.scene, 0, 0, 104, 132);
infoBg.setOrigin(0.5, 0.5);
this.infoContainer = this.scene.add.container(this.eggHatchBg.displayWidth + infoBg.width / 2, this.eggHatchBg.displayHeight / 2);
this.statsContainer = new StatsContainer(this.scene, -48, -54, true);
this.statsContainer = new StatsContainer(this.scene, -48, -64, true);
this.infoContainer.add(infoBg);
this.infoContainer.add(this.statsContainer);
const pokemonGenderLabelText = addTextObject(this.scene, -16, 32, 'Gender:', TextStyle.WINDOW, { fontSize: '64px' });
const pokemonGenderLabelText = addTextObject(this.scene, -18, 20, 'Gender:', TextStyle.WINDOW, { fontSize: '64px' });
pokemonGenderLabelText.setOrigin(1, 0);
pokemonGenderLabelText.setVisible(false);
this.infoContainer.add(pokemonGenderLabelText);
const pokemonGenderText = addTextObject(this.scene, -12, 32, '', TextStyle.WINDOW, { fontSize: '64px' });
const pokemonGenderText = addTextObject(this.scene, -14, 20, '', TextStyle.WINDOW, { fontSize: '64px' });
pokemonGenderText.setOrigin(0, 0);
pokemonGenderText.setVisible(false);
this.infoContainer.add(pokemonGenderText);
const pokemonAbilityLabelText = addTextObject(this.scene, -16, 32, 'Ability:', TextStyle.WINDOW, { fontSize: '64px' });
const pokemonAbilityLabelText = addTextObject(this.scene, -18, 20, 'Ability:', TextStyle.WINDOW, { fontSize: '64px' });
pokemonAbilityLabelText.setOrigin(1, 0);
this.infoContainer.add(pokemonAbilityLabelText);
const pokemonAbilityText = addTextObject(this.scene, -12, 32, '', TextStyle.WINDOW, { fontSize: '64px' });
const pokemonAbilityText = addTextObject(this.scene, -14, 20, '', TextStyle.WINDOW, { fontSize: '64px' });
pokemonAbilityText.setOrigin(0, 0);
this.infoContainer.add(pokemonAbilityText);
const pokemonNatureLabelText = addTextObject(this.scene, -18, 30, 'Nature:', TextStyle.WINDOW, { fontSize: '64px' });
pokemonNatureLabelText.setOrigin(1, 0);
this.infoContainer.add(pokemonNatureLabelText);
const pokemonNatureText = addBBCodeTextObject(this.scene, -14, 30, '', TextStyle.WINDOW, { fontSize: '64px', lineSpacing: 3, maxLines: 2 });
pokemonNatureText.setOrigin(0, 0);
this.infoContainer.add(pokemonNatureText);
this.eggHatchContainer.add(this.infoContainer);
const pokemon = this.generatePokemon();
if (pokemon.fusionSpecies)
pokemon.clearFusionSpecies();
let abilityYOffset = 5;
if (pokemon.gender > Gender.GENDERLESS) {
pokemonGenderText.setText(getGenderSymbol(pokemon.gender));
pokemonGenderText.setColor(getGenderColor(pokemon.gender));
pokemonGenderText.setShadowColor(getGenderColor(pokemon.gender, true));
pokemonGenderLabelText.setVisible(true);
pokemonGenderText.setVisible(true);
abilityYOffset = 10;
}
[ pokemonAbilityLabelText, pokemonAbilityText ].map(t => t.y += abilityYOffset);
pokemonAbilityText.setText(pokemon.getAbility().name);
pokemonNatureText.setText(getNatureName(pokemon.nature, true));
const originalIvs: integer[] = this.scene.gameData.dexData[pokemon.species.speciesId].caughtAttr
? this.scene.gameData.dexData[pokemon.species.speciesId].ivs
@ -205,7 +209,7 @@ export class EggHatchPhase extends BattlePhase {
targets: this.infoContainer,
duration: Utils.fixedInt(750),
ease: 'Cubic.easeInOut',
x: this.eggHatchBg.displayWidth - 48
x: this.eggHatchBg.displayWidth - 52
});
this.scene.playSoundWithoutBgm('evolution_fanfare');

View File

@ -33,6 +33,7 @@ import { LevelMoves } from './data/pokemon-level-moves';
import { DamageAchv, achvs } from './system/achv';
import { DexAttr } from './system/game-data';
import { QuantizerCelebi, argbFromRgba, rgbaFromArgb } from '@material/material-color-utilities';
import { Nature, getNatureStatMultiplier } from './data/nature';
export enum FieldPosition {
CENTER,
@ -62,6 +63,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
public hp: integer;
public stats: integer[];
public ivs: integer[];
public nature: Nature;
public moveset: PokemonMove[];
public status: Status;
public friendship: integer;
@ -88,7 +90,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
private shinySparkle: Phaser.GameObjects.Sprite;
constructor(scene: BattleScene, x: number, y: number, species: PokemonSpecies, level: integer, abilityIndex?: integer, formIndex?: integer, gender?: Gender, shiny?: boolean, ivs?: integer[], dataSource?: Pokemon | PokemonData) {
constructor(scene: BattleScene, x: number, y: number, species: PokemonSpecies, level: integer, abilityIndex?: integer, formIndex?: integer, gender?: Gender, shiny?: boolean, ivs?: integer[], nature?: Nature, dataSource?: Pokemon | PokemonData) {
super(scene, x, y);
if (!species.isObtainable() && this.isPlayer())
@ -123,6 +125,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.hp = dataSource.hp;
this.stats = dataSource.stats;
this.ivs = dataSource.ivs;
this.nature = dataSource.nature || 0 as Nature;
this.moveset = dataSource.moveset;
this.status = dataSource.status;
this.friendship = dataSource.friendship !== undefined ? dataSource.friendship : this.species.baseFriendship;
@ -146,6 +149,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
Utils.binToDec(Utils.decToBin(this.id).substring(25, 30))
];
this.nature = nature !== undefined
? nature
: Utils.randSeedInt(25) as Nature;
if (this.gender === undefined) {
if (this.species.malePercent === null)
this.gender = Gender.GENDERLESS;
@ -519,12 +526,22 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (lastMaxHp && value > lastMaxHp)
this.hp += value - lastMaxHp;
}
} else
value = Math.min(value + 5, 99999);
} else {
value += 5;
const natureStatMultiplier = getNatureStatMultiplier(this.nature, s);
if (natureStatMultiplier !== 1)
value = Math[natureStatMultiplier > 1 ? 'ceil' : 'floor'](value * natureStatMultiplier);
value = Math.min(value, 99999);
}
this.stats[s] = value;
}
}
setNature(nature: Nature): void {
this.nature = nature;
this.calculateStats();
}
getMaxHp(): integer {
return this.getStat(Stat.HP);
}
@ -1725,8 +1742,8 @@ export default interface Pokemon {
export class PlayerPokemon extends Pokemon {
public compatibleTms: Moves[];
constructor(scene: BattleScene, species: PokemonSpecies, level: integer, abilityIndex: integer, formIndex: integer, gender?: Gender, shiny?: boolean, ivs?: integer[], dataSource?: Pokemon | PokemonData) {
super(scene, 106, 148, species, level, abilityIndex, formIndex, gender, shiny, ivs, dataSource);
constructor(scene: BattleScene, species: PokemonSpecies, level: integer, abilityIndex: integer, formIndex: integer, gender?: Gender, shiny?: boolean, ivs?: integer[], nature?: Nature, dataSource?: Pokemon | PokemonData) {
super(scene, 106, 148, species, level, abilityIndex, formIndex, gender, shiny, ivs, nature, dataSource);
this.generateCompatibleTms();
}
@ -1791,7 +1808,7 @@ export class PlayerPokemon extends Pokemon {
return new Promise(resolve => {
const species = getPokemonSpecies(evolution.speciesId);
const formIndex = Math.max(this.species.forms.findIndex(f => f.formKey === evolution.evoFormKey), 0);
const ret = new PlayerPokemon(this.scene, species, this.level, this.abilityIndex, formIndex, this.gender, this.shiny, this.ivs, this);
const ret = new PlayerPokemon(this.scene, species, this.level, this.abilityIndex, formIndex, this.gender, this.shiny, this.ivs, this.nature, this);
ret.loadAssets().then(() => resolve(ret));
});
}
@ -1822,7 +1839,7 @@ export class PlayerPokemon extends Pokemon {
if (this.species.speciesId === Species.NINCADA && evolution.speciesId === Species.NINJASK) {
const newEvolution = pokemonEvolutions[this.species.speciesId][1];
if (newEvolution.condition.predicate(this)) {
const newPokemon = new PlayerPokemon(this.scene, this.species, this.level, this.abilityIndex, this.formIndex, this.gender, this.shiny);
const newPokemon = new PlayerPokemon(this.scene, this.species, this.level, this.abilityIndex, this.formIndex, this.gender, this.shiny, this.ivs, this.nature);
this.scene.getParty().push(newPokemon);
newPokemon.evolve(newEvolution);
const modifiers = this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier
@ -1897,7 +1914,7 @@ export class EnemyPokemon extends Pokemon {
constructor(scene: BattleScene, species: PokemonSpecies, level: integer, trainer: boolean, dataSource?: PokemonData) {
super(scene, 236, 84, species, level, dataSource?.abilityIndex, dataSource?.formIndex,
dataSource?.gender, dataSource ? dataSource.shiny : false, null, dataSource);
dataSource?.gender, dataSource ? dataSource.shiny : false, null, dataSource ? dataSource.nature : undefined, dataSource);
this.trainer = trainer;
@ -2096,7 +2113,7 @@ export class EnemyPokemon extends Pokemon {
this.pokeball = pokeballType;
this.metLevel = this.level;
this.metBiome = this.scene.arena.biomeType;
const newPokemon = new PlayerPokemon(this.scene, this.species, this.level, this.abilityIndex, this.formIndex, this.gender, this.shiny, null, this);
const newPokemon = new PlayerPokemon(this.scene, this.species, this.level, this.abilityIndex, this.formIndex, this.gender, this.shiny, this.ivs, this.nature, this);
party.push(newPokemon);
ret = newPokemon;
}

View File

@ -20,6 +20,7 @@ import { VoucherType, vouchers } from "./voucher";
import { AES, enc } from "crypto-js";
import { Mode } from "../ui/ui";
import { loggedInUser, updateUserInfo } from "../account";
import { Nature } from "../data/nature";
const saveKey = 'x0i2O7WRiANTqPmZ'; // Temporary; secure encryption is not yet necessary
@ -94,6 +95,7 @@ export interface DexData {
export interface DexEntry {
seenAttr: bigint;
caughtAttr: bigint;
natureAttr: integer,
seenCount: integer;
caughtCount: integer;
hatchedCount: integer;
@ -121,6 +123,7 @@ export interface DexAttrProps {
const systemShortKeys = {
seenAttr: '$sa',
caughtAttr: '$ca',
natureAttr: '$na',
seenCount: '$s' ,
caughtCount: '$c',
ivs: '$i'
@ -133,6 +136,7 @@ export class GameData {
public secretId: integer;
public dexData: DexData;
private defaultDexData: DexData;
public unlocks: Unlocks;
@ -261,6 +265,7 @@ export class GameData {
this.dexData = Object.assign(this.dexData, systemData.dexData);
this.consolidateDexData(this.dexData);
this.defaultDexData = null;
resolve(true);
}
@ -292,7 +297,7 @@ export class GameData {
return ret;
}
return k.endsWith('Attr') ? BigInt(v) : v;
return k.endsWith('Attr') && k !== 'natureAttr' ? BigInt(v) : v;
}) as SystemSaveData;
}
@ -657,7 +662,7 @@ export class GameData {
for (let species of allSpecies) {
data[species.speciesId] = {
seenAttr: 0n, caughtAttr: 0n, seenCount: 0, caughtCount: 0, hatchedCount: 0, ivs: [ 0, 0, 0, 0, 0, 0 ]
seenAttr: 0n, caughtAttr: 0n, natureAttr: 0, seenCount: 0, caughtCount: 0, hatchedCount: 0, ivs: [ 0, 0, 0, 0, 0, 0 ]
};
}
@ -675,14 +680,24 @@ export class GameData {
const defaultStarterAttr = DexAttr.NON_SHINY | DexAttr.MALE | DexAttr.ABILITY_1 | DexAttr.DEFAULT_FORM;
for (let ds of defaultStarters) {
let entry = data[ds] as DexEntry;
const defaultStarterNatures: Nature[] = [];
this.scene.executeWithSeedOffset(() => {
const neutralNatures = [ Nature.HARDY, Nature.DOCILE, Nature.SERIOUS, Nature.BASHFUL, Nature.QUIRKY ];
for (let s = 0; s < defaultStarters.length; s++)
defaultStarterNatures.push(Phaser.Math.RND.pick(neutralNatures));
}, 0, 'default');
for (let ds = 0; ds < defaultStarters.length; ds++) {
let entry = data[defaultStarters[ds]] as DexEntry;
entry.seenAttr = defaultStarterAttr;
entry.caughtAttr = defaultStarterAttr;
entry.natureAttr = Math.pow(2, defaultStarterNatures[ds] + 1);
for (let i in entry.ivs)
entry.ivs[i] = 10;
}
this.defaultDexData = Object.assign({}, data);
this.dexData = data;
}
@ -702,6 +717,7 @@ export class GameData {
const dexEntry = this.dexData[species.speciesId];
const caughtAttr = dexEntry.caughtAttr;
dexEntry.caughtAttr |= pokemon.getDexAttr();
dexEntry.natureAttr |= Math.pow(2, pokemon.nature + 1);
if (incrementCount) {
if (!fromEgg)
dexEntry.caughtCount++;
@ -767,6 +783,28 @@ export class GameData {
};
}
getSpeciesDefaultNature(species: PokemonSpecies): Nature {
const dexEntry = this.dexData[species.speciesId];
for (let n = 0; n < 25; n++) {
if (dexEntry.natureAttr & Math.pow(2, n + 1))
return n as Nature;
}
return 0 as Nature;
}
getSpeciesDefaultNatureAttr(species: PokemonSpecies): integer {
return Math.pow(2, this.getSpeciesDefaultNature(species));
}
getNaturesForAttr(natureAttr: integer): Nature[] {
let ret: Nature[] = [];
for (let n = 0; n < 25; n++) {
if (natureAttr & Math.pow(2, n + 1))
ret.push(n);
}
return ret;
}
getFormIndex(attr: bigint): integer {
if (!attr || attr < DexAttr.DEFAULT_FORM)
return 0;
@ -785,6 +823,8 @@ export class GameData {
const entry = dexData[k] as DexEntry;
if (!entry.hasOwnProperty('hatchedCount'))
entry.hatchedCount = 0;
if (!entry.hasOwnProperty('natureAttr') || (entry.caughtAttr && !entry.natureAttr))
entry.natureAttr = this.defaultDexData[k].natureAttr || Math.pow(2, Utils.randInt(25, 1));
}
}
}

View File

@ -2,6 +2,7 @@ import { BattleType } from "../battle";
import BattleScene from "../battle-scene";
import { Biome } from "../data/biome";
import { Gender } from "../data/gender";
import { Nature } from "../data/nature";
import { PokeballType } from "../data/pokeball";
import { getPokemonSpecies } from "../data/pokemon-species";
import { Species } from "../data/species";
@ -23,6 +24,7 @@ export default class PokemonData {
public hp: integer;
public stats: integer[];
public ivs: integer[];
public nature: Nature;
public moveset: PokemonMove[];
public status: Status;
public friendship: integer;
@ -55,6 +57,7 @@ export default class PokemonData {
this.hp = source.hp;
this.stats = source.stats;
this.ivs = source.ivs;
this.nature = source.nature !== undefined ? source.nature : 0 as Nature;
this.friendship = source.friendship !== undefined ? source.friendship : getPokemonSpecies(this.species).baseFriendship;
this.metLevel = source.metLevel || 5;
this.metBiome = source.metBiome !== undefined ? source.metBiome : -1;
@ -92,7 +95,7 @@ export default class PokemonData {
toPokemon(scene: BattleScene, battleType?: BattleType): Pokemon {
const species = getPokemonSpecies(this.species);
if (this.player)
return new PlayerPokemon(scene, species, this.level, this.abilityIndex, this.formIndex, this.gender, this.shiny, null, this);
return new PlayerPokemon(scene, species, this.level, this.abilityIndex, this.formIndex, this.gender, this.shiny, this.ivs, this.nature, this);
return new EnemyPokemon(scene, species, this.level, battleType === BattleType.TRAINER, this);
}
}

View File

@ -1,7 +1,7 @@
import BattleScene, { Button } from "../battle-scene";
import PokemonSpecies, { SpeciesFormKey, allSpecies, getPokemonSpecies, speciesStarters as speciesStarterValues } from "../data/pokemon-species";
import { Species } from "../data/species";
import { TextStyle, addTextObject, getTextColor } from "./text";
import { TextStyle, addBBCodeTextObject, addTextObject, getTextColor } from "./text";
import { Mode } from "./ui";
import MessageUiHandler from "./message-ui-handler";
import { Gender, getGenderColor, getGenderSymbol } from "../data/gender";
@ -14,12 +14,15 @@ import * as Utils from "../utils";
import PokemonIconAnimHandler, { PokemonIconAnimMode } from "./pokemon-icon-anim-handler";
import { StatsContainer } from "./stats-container";
import { addWindow } from "./window";
import { Nature, getNatureName } from "../data/nature";
import BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext";
export type StarterSelectCallback = (starters: Starter[]) => void;
export interface Starter {
species: PokemonSpecies;
dexAttr: bigint;
nature: Nature;
pokerus: boolean;
}
@ -36,6 +39,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
private pokemonGenderText: Phaser.GameObjects.Text;
private pokemonAbilityLabelText: Phaser.GameObjects.Text;
private pokemonAbilityText: Phaser.GameObjects.Text;
private pokemonNatureLabelText: Phaser.GameObjects.Text;
private pokemonNatureText: BBCodeText;
private genOptionsText: Phaser.GameObjects.Text;
private instructionsText: Phaser.GameObjects.Text;
private starterSelectMessageBoxContainer: Phaser.GameObjects.Container;
@ -44,6 +49,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
private genMode: boolean;
private statsMode: boolean;
private dexAttrCursor: bigint = 0n;
private natureCursor: integer = 0;
private genCursor: integer = 0;
private genScrollCursor: integer = 0;
@ -55,11 +61,13 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
private pokerusGens: integer[] = [];
private pokerusCursors: integer[] = [];
private starterAttr: bigint[] = [];
private starterNatures: Nature[] = [];
private speciesStarterDexEntry: DexEntry;
private canCycleShiny: boolean;
private canCycleForm: boolean;
private canCycleGender: boolean;
private canCycleAbility: boolean;
private canCycleNature: boolean;
private value: integer = 0;
private assetLoadCancelled: Utils.BooleanHolder;
@ -131,10 +139,19 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.pokemonAbilityLabelText.setVisible(false);
this.starterSelectContainer.add(this.pokemonAbilityLabelText);
this.pokemonAbilityText = addTextObject(this.scene, 38, 126, '', TextStyle.SUMMARY, { fontSize: '64px' });
this.pokemonAbilityText = addTextObject(this.scene, 34, 126, '', TextStyle.SUMMARY, { fontSize: '64px' });
this.pokemonAbilityText.setOrigin(0, 0);
this.starterSelectContainer.add(this.pokemonAbilityText);
this.pokemonNatureLabelText = addTextObject(this.scene, 6, 135, 'Nature:', TextStyle.SUMMARY, { fontSize: '64px' });
this.pokemonNatureLabelText.setOrigin(0, 0);
this.pokemonNatureLabelText.setVisible(false);
this.starterSelectContainer.add(this.pokemonNatureLabelText);
this.pokemonNatureText = addBBCodeTextObject(this.scene, 34, 135, '', TextStyle.SUMMARY, { fontSize: '64px' });
this.pokemonNatureText.setOrigin(0, 0);
this.starterSelectContainer.add(this.pokemonNatureText);
this.genOptionsText = addTextObject(this.scene, 124, 7, '', TextStyle.WINDOW, { fontSize: 72, lineSpacing: 39, align: 'center' });
this.genOptionsText.setShadowOffset(4.5, 4.5);
this.genOptionsText.setOrigin(0.5, 0);
@ -254,7 +271,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.pokemonSprite = this.scene.add.sprite(53, 63, `pkmn__sub`);
this.starterSelectContainer.add(this.pokemonSprite);
this.instructionsText = addTextObject(this.scene, 4, 140, '', TextStyle.PARTY, { fontSize: '42px' });
this.instructionsText = addTextObject(this.scene, 4, 148, '', TextStyle.PARTY, { fontSize: '42px' });
this.starterSelectContainer.add(this.instructionsText);
this.starterSelectMessageBoxContainer = this.scene.add.container(0, this.scene.game.canvas.height / 6);
@ -422,6 +439,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.starterGens.push(this.getGenCursorWithScroll());
this.starterCursors.push(this.cursor);
this.starterAttr.push(this.dexAttrCursor);
this.starterNatures.push(this.natureCursor as unknown as Nature);
if (this.speciesLoaded.get(species.speciesId))
species.cry(this.scene);
if (this.starterCursors.length === 6 || this.value === 10)
@ -453,7 +471,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
switch (button) {
case Button.CYCLE_SHINY:
if (this.canCycleShiny) {
this.setSpeciesDetails(this.lastSpecies, !props.shiny, undefined, undefined, undefined);
this.setSpeciesDetails(this.lastSpecies, !props.shiny, undefined, undefined, undefined, undefined);
if (this.dexAttrCursor & DexAttr.SHINY)
this.scene.playSound('sparkle');
else
@ -469,13 +487,13 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
if (this.speciesStarterDexEntry.caughtAttr & this.scene.gameData.getFormAttr(newFormIndex))
break;
} while (newFormIndex !== props.formIndex);
this.setSpeciesDetails(this.lastSpecies, undefined, newFormIndex, undefined, undefined);
this.setSpeciesDetails(this.lastSpecies, undefined, newFormIndex, undefined, undefined, undefined);
success = true;
}
break;
case Button.CYCLE_GENDER:
if (this.canCycleGender) {
this.setSpeciesDetails(this.lastSpecies, undefined, undefined, !props.female, undefined);
this.setSpeciesDetails(this.lastSpecies, undefined, undefined, !props.female, undefined, undefined);
success = true;
}
break;
@ -496,7 +514,16 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
break;
}
} while (newAbilityIndex !== props.abilityIndex);
this.setSpeciesDetails(this.lastSpecies, undefined, undefined, undefined, newAbilityIndex);
this.setSpeciesDetails(this.lastSpecies, undefined, undefined, undefined, newAbilityIndex, undefined);
success = true;
}
break;
case Button.CYCLE_NATURE:
if (this.canCycleNature) {
const natures = this.scene.gameData.getNaturesForAttr(this.speciesStarterDexEntry.natureAttr);
const natureIndex = natures.indexOf(this.natureCursor);
const newNature = natures[natureIndex < natures.length - 1 ? natureIndex + 1 : 0];
this.setSpeciesDetails(this.lastSpecies, undefined, undefined, undefined, undefined, newNature);
success = true;
}
break;
@ -534,14 +561,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
}
updateInstructions(): void {
let instructionLines = [
'Arrow Keys/WASD: Move'
];
let instructionLines = [ ];
let cycleInstructionLines = [];
if (!this.genMode)
instructionLines.push('A/Space/Enter: Select');
if (this.starterCursors.length)
instructionLines.push('X/Backspace/Esc: Undo');
if (this.speciesStarterDexEntry?.caughtAttr) {
if (this.canCycleShiny)
cycleInstructionLines.push('R: Cycle Shiny');
@ -551,6 +572,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
cycleInstructionLines.push('G: Cycle Gender');
if (this.canCycleAbility)
cycleInstructionLines.push('E: Cycle Ability');
if (this.canCycleNature)
cycleInstructionLines.push('N: Cycle Nature');
}
if (cycleInstructionLines.length > 2) {
@ -656,6 +679,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
setSpecies(species: PokemonSpecies) {
this.speciesStarterDexEntry = species ? this.scene.gameData.dexData[species.speciesId] : null;
this.dexAttrCursor = species ? this.scene.gameData.getSpeciesDefaultDexAttr(species) : 0n;
this.natureCursor = species ? this.scene.gameData.getSpeciesDefaultNature(species) : 0;
if (this.statsMode) {
if (this.speciesStarterDexEntry?.caughtAttr) {
@ -685,24 +709,43 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.pokemonGrowthRateText.setShadowColor(getGrowthRateColor(species.growthRate, true));
this.pokemonGrowthRateLabelText.setVisible(true);
this.pokemonAbilityLabelText.setVisible(true);
this.pokemonNatureLabelText.setVisible(true);
this.iconAnimHandler.addOrUpdate(this.starterSelectGenIconContainers[species.generation - 1].getAt(this.genSpecies[species.generation - 1].indexOf(species)) as Phaser.GameObjects.Sprite, PokemonIconAnimMode.PASSIVE);
const defaultDexAttr = this.scene.gameData.getSpeciesDefaultDexAttr(species);
const props = this.scene.gameData.getSpeciesDexAttrProps(species, defaultDexAttr);
let starterIndex = -1;
this.setSpeciesDetails(species, props.shiny, props.formIndex, props.female, props.abilityIndex);
this.starterGens.every((g, i) => {
const starterSpecies = this.genSpecies[g][this.starterCursors[i]];
if (starterSpecies.speciesId === species.speciesId) {
starterIndex = i;
return false;
}
return true;
});
if (starterIndex > -1) {
const props = this.scene.gameData.getSpeciesDexAttrProps(species, this.starterAttr[starterIndex]);
this.setSpeciesDetails(species, props.shiny, props.formIndex, props.female, props.abilityIndex, this.starterNatures[starterIndex]);
} else {
const defaultDexAttr = this.scene.gameData.getSpeciesDefaultDexAttr(species);
const defaultNature = this.scene.gameData.getSpeciesDefaultNature(species);
const props = this.scene.gameData.getSpeciesDexAttrProps(species, defaultDexAttr);
this.setSpeciesDetails(species, props.shiny, props.formIndex, props.female, props.abilityIndex, defaultNature);
}
} else {
this.pokemonNumberText.setText(Utils.padInt(0, 3));
this.pokemonNameText.setText(species ? '???' : '');
this.pokemonGrowthRateText.setText('');
this.pokemonGrowthRateLabelText.setVisible(false);
this.pokemonAbilityLabelText.setVisible(false);
this.pokemonNatureLabelText.setVisible(false);
this.setSpeciesDetails(species, false, 0, false, 0);
this.setSpeciesDetails(species, false, 0, false, 0, 0);
}
}
setSpeciesDetails(species: PokemonSpecies, shiny: boolean, formIndex: integer, female: boolean, abilityIndex: integer): void {
setSpeciesDetails(species: PokemonSpecies, shiny: boolean, formIndex: integer, female: boolean, abilityIndex: integer, natureIndex: integer): void {
const oldProps = species ? this.scene.gameData.getSpeciesDexAttrProps(species, this.dexAttrCursor) : null;
this.dexAttrCursor = 0n;
@ -711,8 +754,12 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.dexAttrCursor |= (female !== undefined ? !female : !(female = oldProps.female)) ? DexAttr.MALE : DexAttr.FEMALE;
this.dexAttrCursor |= (abilityIndex !== undefined ? !abilityIndex : !(abilityIndex = oldProps.abilityIndex)) ? DexAttr.ABILITY_1 : species.ability2 && abilityIndex === 1 ? DexAttr.ABILITY_2 : DexAttr.ABILITY_HIDDEN;
this.dexAttrCursor |= this.scene.gameData.getFormAttr(formIndex !== undefined ? formIndex : (formIndex = oldProps.formIndex));
if (natureIndex === undefined)
natureIndex = this.scene.gameData.getSpeciesDefaultNature(species);
}
this.natureCursor = natureIndex;
this.pokemonSprite.setVisible(false);
if (this.assetLoadCancelled) {
@ -724,6 +771,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
const dexEntry = this.scene.gameData.dexData[species.speciesId];
if (!dexEntry.caughtAttr) {
const props = this.scene.gameData.getSpeciesDexAttrProps(species, this.scene.gameData.getSpeciesDefaultDexAttr(species));
const defaultNature = this.scene.gameData.getSpeciesDefaultNature(species);
if (shiny === undefined || shiny !== props.shiny)
shiny = props.shiny;
if (formIndex === undefined || formIndex !== props.formIndex)
@ -732,9 +780,27 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
female = props.female;
if (abilityIndex === undefined || abilityIndex !== props.abilityIndex)
abilityIndex = props.abilityIndex;
if (natureIndex === undefined || natureIndex !== defaultNature)
natureIndex = defaultNature;
}
if (this.speciesStarterDexEntry?.caughtAttr) {
let starterIndex = -1;
this.starterGens.every((g, i) => {
const starterSpecies = this.genSpecies[g][this.starterCursors[i]];
if (starterSpecies.speciesId === species.speciesId) {
starterIndex = i;
return false;
}
return true;
});
if (starterIndex > -1) {
this.starterAttr[starterIndex] = this.dexAttrCursor;
this.starterNatures[starterIndex] = this.natureCursor;
}
const assetLoadCancelled = new Utils.BooleanHolder(false);
this.assetLoadCancelled = assetLoadCancelled;
@ -754,6 +820,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.canCycleAbility = [ dexEntry.caughtAttr & DexAttr.ABILITY_1, dexEntry.caughtAttr & DexAttr.ABILITY_2, dexEntry.caughtAttr & DexAttr.ABILITY_HIDDEN ].filter(a => a).length > 1;
this.canCycleForm = species.forms.filter(f => !f.formKey || f.formKey.indexOf(SpeciesFormKey.MEGA) === -1)
.map((_, f) => dexEntry.caughtAttr & this.scene.gameData.getFormAttr(f)).filter(a => a).length > 1;
this.canCycleNature = this.scene.gameData.getNaturesForAttr(dexEntry.natureAttr).length > 1;
}
if (dexEntry.caughtAttr && species.malePercent !== null) {
@ -771,11 +838,16 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
const isHidden = ability === this.lastSpecies.abilityHidden;
this.pokemonAbilityText.setColor(getTextColor(!isHidden ? TextStyle.SUMMARY : TextStyle.SUMMARY_GOLD));
this.pokemonAbilityText.setShadowColor(getTextColor(!isHidden ? TextStyle.SUMMARY : TextStyle.SUMMARY_GOLD, true));
} else
this.pokemonNatureText.setText(getNatureName(natureIndex as unknown as Nature, true, true));
} else {
this.pokemonAbilityText.setText('');
this.pokemonNatureText.setText('');
}
} else {
this.pokemonGenderText.setText('');
this.pokemonAbilityText.setText('');
this.pokemonNatureText.setText('');
}
this.updateInstructions();
@ -785,6 +857,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.starterGens.pop();
this.starterCursors.pop();
this.starterAttr.pop();
this.starterNatures.pop();
this.starterCursorObjs[this.starterCursors.length].setVisible(false);
this.starterIcons[this.starterCursors.length].setTexture('pokemon_icons_0');
this.starterIcons[this.starterCursors.length].setFrame('unknown');
@ -832,6 +905,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
return {
species: starterSpecies,
dexAttr: thisObj.starterAttr[i],
nature: thisObj.starterNatures[i] as Nature,
pokerus: !![ 0, 1, 2 ].filter(n => thisObj.pokerusGens[n] === starterSpecies.generation - 1 && thisObj.pokerusCursors[n] === thisObj.genSpecies[starterSpecies.generation - 1].indexOf(starterSpecies)).length
};
}));

View File

@ -71,7 +71,7 @@ export class StatsContainer extends Phaser.GameObjects.Container {
let label = ivs[i].toString();
if (this.showDiff && originalIvs) {
if (originalIvs[i] < ivs[i])
label += ` ([color=${getGenderColor(Gender.MALE)}][shadow=${getGenderColor(Gender.MALE, true)}]+${ivs[i] - originalIvs[i]}[/shadow][/color])`;
label += ` ([color=${getTextColor(TextStyle.SUMMARY_BLUE)}][shadow=${getTextColor(TextStyle.SUMMARY_BLUE, true)}]+${ivs[i] - originalIvs[i]}[/shadow][/color])`;
else
label += ' (-)';
}

View File

@ -7,12 +7,13 @@ import { Type } from "../data/type";
import { TextStyle, addBBCodeTextObject, addTextObject, getBBCodeFrag, getTextColor } from "./text";
import Move, { MoveCategory } from "../data/move";
import { getPokeballAtlasKey } from "../data/pokeball";
import { getGenderColor, getGenderSymbol } from "../data/gender";
import { Gender, getGenderColor, getGenderSymbol } from "../data/gender";
import { getLevelTotalExp } from "../data/exp";
import { Stat, getStatName } from "../data/pokemon-stat";
import { PokemonHeldItemModifier } from "../modifier/modifier";
import { StatusEffect } from "../data/status-effect";
import { getBiomeName } from "../data/biome";
import { Nature, getNatureStatMultiplier } from "../data/nature";
enum Page {
PROFILE,
@ -531,7 +532,7 @@ export default class SummaryUiHandler extends UiHandler {
});
}
let memoString = `${getBBCodeFrag(`${this.pokemon.metBiome === -1 ? 'apparently ' : ''}met at Lv`, TextStyle.WINDOW)}${getBBCodeFrag(this.pokemon.metLevel.toString(), TextStyle.SUMMARY_RED)}${getBBCodeFrag(',', TextStyle.WINDOW)}\n${getBBCodeFrag(getBiomeName(this.pokemon.metBiome), TextStyle.SUMMARY_RED)}${getBBCodeFrag('.', TextStyle.WINDOW)}`;
let memoString = `${getBBCodeFrag(Utils.toReadableString(Nature[this.pokemon.nature]), TextStyle.SUMMARY_RED)} nature,\n${getBBCodeFrag(`${this.pokemon.metBiome === -1 ? 'apparently ' : ''}met at Lv`, TextStyle.WINDOW)}${getBBCodeFrag(this.pokemon.metLevel.toString(), TextStyle.SUMMARY_RED)}${getBBCodeFrag(',', TextStyle.WINDOW)}\n${getBBCodeFrag(getBiomeName(this.pokemon.metBiome), TextStyle.SUMMARY_RED)}${getBBCodeFrag('.', TextStyle.WINDOW)}`;
const memoText = addBBCodeTextObject(this.scene, 7, 113, memoString, TextStyle.WINDOW);
memoText.setOrigin(0, 0);
@ -550,7 +551,9 @@ export default class SummaryUiHandler extends UiHandler {
const rowIndex = s % 3;
const colIndex = Math.floor(s / 3);
const statLabel = addTextObject(this.scene, 27 + 115 * colIndex, 56 + 16 * rowIndex, statName, TextStyle.SUMMARY);
const natureStatMultiplier = getNatureStatMultiplier(this.pokemon.nature, s);
const statLabel = addTextObject(this.scene, 27 + 115 * colIndex, 56 + 16 * rowIndex, statName, natureStatMultiplier === 1 ? TextStyle.SUMMARY : natureStatMultiplier > 1 ? TextStyle.SUMMARY_BLUE : TextStyle.SUMMARY_PINK);
statLabel.setOrigin(0.5, 0);
statsContainer.add(statLabel);

View File

@ -9,6 +9,7 @@ export enum TextStyle {
PARTY_RED,
SUMMARY,
SUMMARY_RED,
SUMMARY_BLUE,
SUMMARY_PINK,
SUMMARY_GOLD,
MONEY,
@ -129,6 +130,8 @@ export function getTextColor(textStyle: TextStyle, shadow?: boolean): string {
case TextStyle.SUMMARY_RED:
case TextStyle.TOOLTIP_TITLE:
return !shadow ? '#e70808' : '#ffbd73';
case TextStyle.SUMMARY_BLUE:
return !shadow ? '#40c8f8' : '#006090';
case TextStyle.SUMMARY_PINK:
return !shadow ? '#f89890' : '#984038';
case TextStyle.SUMMARY_GOLD: