From 592ccc7c6acd4510c609b321a688b7200aa5c5cd Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Wed, 26 Apr 2023 12:50:21 -0400 Subject: [PATCH] Add starter select ability selection --- public/images/ui/starter_select_bg.png | Bin 1435 -> 1436 bytes src/battle-phases.ts | 2 +- src/battle-scene.ts | 4 + src/data/pokemon-species.ts | 10 ++- src/pokemon.ts | 3 + src/system/game-data.ts | 113 ++++++++++++++++++++----- src/ui/starter-select-ui-handler.ts | 96 ++++++++++++++++----- src/ui/summary-ui-handler.ts | 2 +- src/ui/text.ts | 7 +- 9 files changed, 190 insertions(+), 47 deletions(-) diff --git a/public/images/ui/starter_select_bg.png b/public/images/ui/starter_select_bg.png index 7dd9a7dac9020502e3046100855ecb47d30a97e5..e96e743f4ff454a0b95da26208ddf89b3d10dea9 100644 GIT binary patch literal 1436 zcmeAS@N?(olHy`uVBq!ia0y~yV6+3WH*v55$=kMz)fpIAlRaG=Ln>~)y{lL*6)AJ< zp}no(iHXV+PfYYkQQahT(*1$5i<_I{rh{CUEFfVj+)HcWqJDj z+@h|s%cA@FERNUydHF$C{eIHcsG~Wy|G!-4N&X`C`RL8M|0hd?1ZU6hyT5Zi^SMI# z{~y-Ad;BI#(RZIdx&1Mjl_O@Ayx9G$t-rSX3R^uj zn0a0CkAJpx4Ccl)_f9dHo7c?WyEU}oz|!5nZ{9e2?epXHwYB^^Zaw_{L+pBV+=FHJ zUNPNq`RM9$rmVDQ^-SSc7xQkrS)b!$>e_Sl?$zVsN`Gg)&3eqP+UEaqs^9)zMN$gKO|u4003}i(z6w2k0&R5#(n$)9+lBq^A0L)~^1CBJsOlDkTZ~ z-kp>Cf--z#jRlnv=s|(^9KfHK#iXpFdm&h9t`E5|4Lh;+U}*p%!k>>Ez{x}T(>0K zpXKQioiDCXuy&sX(}53+5_SxS*%N*+@W?kjWHzW_XyeBe#37@swCL?O`zyAkbvvx* zT)KN)T=MYbYy68dFYmG9wD|bJ@W8>>MN7UOKH+(wv;BSM#&3t1^19Z4WQ5w#rtq{x zw4YJ+$GUf|S>~}WIg3`#-rRfdpW%TIfj7#+T}|)F9bezOZu;?_56^8rhD~=`wW>~f zkzD(>RkAi+{YG&`)qi&E|95I)@%MKd4}Q4Vt+({zmdrSBz6%efY@{XZs$}JsGt{5E z{)4dhAb!U1_~Wi6b7Yvr%lm)r-1TQ-*=5%wYO{6~O7IkY`SyLq!^bS!c5AKr_|2j& zw}h|t;Qh^lH=|?gfT<|oTqx#*`m0r8HGlR^zawXVuCKX!g7Wis74Q3GIQD1VGFk7} f=nX6Ze@XWl7~)y{lL*6)AJ< zp}no(iHXV+PfYYkQQahT(*1$5i<_ICD-}0kXB@(mO ze=fc9b;I^*&k{rfGt9$M8HX5ITYyEy#A>eTNqj~7H<-M+v4z06To{>MpDzIW$5 zz3yk}y8C5UJo}u-cK=F#Oq-m)F=}nnwt4lxEDuZUJz7(^Exz7puFH{U%O97&d)-ic z=eT{%>fb+pNWA^Kd9w5hJFeINZQ3q97K+#2|0S&4@@Ltf2u7yquiB4pk6LTDcHh;k zr&k(Y%j~P~mp^cpXa4q02hQ@|PtVU4RVaA*uFkA%^VdH=cD{el7;ar7fB#X{Y3@5$ zqGKCoYwYXjxVf&c(6rde_EzrgjN>;Q9VTwywfol(rzP>5V{`v3IPyVr@1>X93)wDw z2eJ+w>thg^|A-kIKD_*gh`fd!A$*iw!wwU-`G3^33#jpP``Gx%84nJwX?TQ*6Mldk zM1%t{Ee1ITY`H;Amp>khL5{+-80HwD1MV*si9e;OcKKwazVg#2I~_h6&Uut=9_x~` zXyxq9z4!hZI(!V=@nhR=tyLetS=8mushGMRr3ii8RtJ*_^o#xXs=(xoB6{@dlwk z6PmcM=I!dZ;Sy|~F1`0}a>K*L8{V$#%GzVw7cU*F-!J*___T*>^;$zh|C+hj^5uqF zSBS``?s@XaYjNh~J=0_s95$^mGpPAuW#h;AZ_fLVjQBi; zP{L-Jbp`rWldK4(euUH-R@#F8fqUt{u)_i(SeRFm{-fLZ?@Mjg!jaICBq0=wD a$p5cr;L*3Q?Fg{YW$<+Mb6Mw<&;$UVX}_WX diff --git a/src/battle-phases.ts b/src/battle-phases.ts index a8ded16c1..54c5b1da4 100644 --- a/src/battle-phases.ts +++ b/src/battle-phases.ts @@ -43,7 +43,7 @@ export class SelectStarterPhase extends BattlePhase { const starterGender = starter.species.malePercent !== null ? !starter.female ? Gender.MALE : Gender.FEMALE : Gender.GENDERLESS; - const starterPokemon = new PlayerPokemon(this.scene, starter.species, startingLevel, 0, starter.formIndex, starterGender, starter.shiny); + const starterPokemon = new PlayerPokemon(this.scene, starter.species, startingLevel, starter.abilityIndex, starter.formIndex, starterGender, starter.shiny); starterPokemon.setVisible(false); party.push(starterPokemon); loadPokemonAssets.push(starterPokemon.loadAssets()); diff --git a/src/battle-scene.ts b/src/battle-scene.ts index c5c8e7db0..f617cd7b3 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -36,6 +36,7 @@ export enum Button { CYCLE_SHINY, CYCLE_FORM, CYCLE_GENDER, + CYCLE_ABILITY, QUICK_START, AUTO, SPEED_UP, @@ -403,6 +404,7 @@ export default class BattleScene extends Phaser.Scene { [Button.CYCLE_SHINY]: [keyCodes.R], [Button.CYCLE_FORM]: [keyCodes.F], [Button.CYCLE_GENDER]: [keyCodes.G], + [Button.CYCLE_ABILITY]: [keyCodes.E], [Button.QUICK_START]: [keyCodes.Q], [Button.AUTO]: [keyCodes.F2], [Button.SPEED_UP]: [keyCodes.PLUS], @@ -546,6 +548,8 @@ export default class BattleScene extends Phaser.Scene { this.ui.processInput(Button.CYCLE_FORM); else if (this.isButtonPressed(Button.CYCLE_GENDER)) this.ui.processInput(Button.CYCLE_GENDER); + else if (this.isButtonPressed(Button.CYCLE_ABILITY)) + this.ui.processInput(Button.CYCLE_ABILITY); else return; } diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index 2dcce3317..cf00f5d00 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -59,10 +59,18 @@ export abstract class PokemonSpeciesForm { this.genderDiffs = genderDiffs; } - isOfType(type: integer) { + isOfType(type: integer): boolean { return this.type1 === type || (this.type2 !== null && this.type2 === type); } + getAbilityCount(): integer { + return this.ability2 ? this.abilityHidden ? 3 : 2 : this.abilityHidden ? 2 : 1; + } + + getAbility(abilityIndex: integer): Abilities { + return !abilityIndex ? this.ability1 : abilityIndex === 1 && this.ability2 ? this.ability2 : this.abilityHidden + } + getSpriteAtlasPath(female: boolean, formIndex?: integer, shiny?: boolean): string { return this.getSpriteId(female, formIndex, shiny).replace(/\_{2}/g, '/'); } diff --git a/src/pokemon.ts b/src/pokemon.ts index 78cc36d1a..de45acd2f 100644 --- a/src/pokemon.ts +++ b/src/pokemon.ts @@ -879,6 +879,9 @@ export class PlayerPokemon extends Pokemon { this.handleSpecialEvolutions(evolution); this.species = getPokemonSpecies(evolution.speciesId); this.name = this.species.name.toUpperCase(); + const abilityCount = this.species.getAbilityCount(); + if (this.abilityIndex >= abilityCount) // Shouldn't happen + this.abilityIndex = abilityCount - 1; this.getSpeciesForm().generateIconAnim(this.scene, this.gender === Gender.FEMALE, this.formIndex); this.compatibleTms.splice(0, this.compatibleTms.length); this.generateCompatibleTms(); diff --git a/src/system/game-data.ts b/src/system/game-data.ts index e422ec380..309b242a7 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -2,7 +2,7 @@ import BattleScene from "../battle-scene"; import { Gender } from "../data/gender"; import Pokemon from "../pokemon"; import { pokemonPrevolutions } from "../data/pokemon-evolutions"; -import PokemonSpecies, { allSpecies } from "../data/pokemon-species"; +import PokemonSpecies, { allSpecies, getPokemonSpecies } from "../data/pokemon-species"; import { Species } from "../data/species"; import * as Utils from "../utils"; @@ -10,6 +10,7 @@ interface SaveData { trainerId: integer; secretId: integer; dexData: DexData; + timestamp: integer } export interface DexData { @@ -25,6 +26,7 @@ export interface DexEntryDetails { shiny: boolean; formIndex: integer; female: boolean; + abilityIndex: integer; entry: DexEntry; } @@ -32,6 +34,7 @@ export interface StarterDexUnlockTree { shiny: boolean | Map formIndex: integer | Map female: boolean | Map + abilityIndex: integer | Map key: string, entry: DexEntry } @@ -59,7 +62,8 @@ export class GameData { const data: SaveData = { trainerId: this.trainerId, secretId: this.secretId, - dexData: this.dexData + dexData: this.dexData, + timestamp: new Date().getTime() }; localStorage.setItem('data', btoa(JSON.stringify(data))); @@ -78,6 +82,9 @@ export class GameData { this.secretId = data.secretId; this.dexData = data.dexData; + if (data.timestamp === undefined) + this.convertDexData(data.dexData); + return true; } @@ -108,12 +115,15 @@ export class GameData { for (let species of allSpecies) { data[species.speciesId] = {}; + const abilityCount = species.getAbilityCount(); if (species.forms?.length) - initDexSubData(data[species.speciesId] as DexData, 2).map(sd => species.malePercent !== null ? initDexSubData(sd, species.forms.length).map(fd => initDexEntries(fd, 2)) : initDexEntries(sd, species.forms.length)); + initDexSubData(data[species.speciesId] as DexData, 2).map(sd => species.malePercent !== null + ? initDexSubData(sd, species.forms.length).map(fd => initDexSubData(fd, 2).map(gd => initDexEntries(gd, abilityCount))) + : initDexSubData(sd, species.forms.length).map(fd => initDexEntries(fd, abilityCount))); else if (species.malePercent !== null) - initDexSubData(data[species.speciesId] as DexData, 2).map(sd => initDexEntries(sd, 2)); + initDexSubData(data[species.speciesId] as DexData, 2).map(sd => initDexSubData(sd, 2).map(gd => initDexEntries(gd, abilityCount))); else - initDexEntries(data[species.speciesId] as DexData, 2) + initDexSubData(data[species.speciesId] as DexData, 2).map(sd => initDexEntries(sd, abilityCount)) } const defaultStarters: Species[] = [ @@ -125,7 +135,7 @@ export class GameData { ]; for (let ds of defaultStarters) { - let entry = data[ds][0][Gender.MALE] as DexEntry; + let entry = data[ds][0][Gender.MALE][0] as DexEntry; entry.seen = true; entry.caught = true; } @@ -162,31 +172,37 @@ export class GameData { } getPokemonDexEntry(pokemon: Pokemon) { - return this.getDexEntry(pokemon.species, pokemon.shiny, pokemon.formIndex, pokemon.gender === Gender.FEMALE); + return this.getDexEntry(pokemon.species, pokemon.shiny, pokemon.formIndex, pokemon.gender === Gender.FEMALE, pokemon.abilityIndex); } - getDexEntry(species: PokemonSpecies, shiny: boolean, formIndex: integer, female: boolean): DexEntry { + getDexEntry(species: PokemonSpecies, shiny: boolean, formIndex: integer, female: boolean, abilityIndex: integer): DexEntry { const shinyIndex = !shiny ? 0 : 1; const genderIndex = !female ? 0 : 1; const data = this.dexData[species.speciesId]; if (species.forms?.length) { if (species.malePercent !== null) - return data[shinyIndex][formIndex][genderIndex]; - return data[shinyIndex][formIndex]; + return data[shinyIndex][formIndex][genderIndex][abilityIndex]; + return data[shinyIndex][formIndex][abilityIndex]; } else if (species.malePercent !== null) - return data[shinyIndex][genderIndex]; - return data[shinyIndex] as DexEntry; + return data[shinyIndex][genderIndex][abilityIndex]; + return data[shinyIndex][abilityIndex] as DexEntry; } - getDefaultDexEntry(species: PokemonSpecies, forceShiny?: boolean, forceFormIndex?: integer, forceFemale?: boolean): DexEntryDetails { + getDefaultDexEntry(species: PokemonSpecies, forceShiny?: boolean, forceFormIndex?: integer, forceFemale?: boolean, forceAbilityIndex?: integer): DexEntryDetails { const hasForms = !!species.forms?.length; + const hasGender = species.malePercent !== null; let shiny = false; let formIndex = 0; let female = false; + let abilityIndex = 0; let entry = null; const traverseData = (data: DexData, level: integer) => { const keys = Object.keys(data); + if ((!hasForms && level === 1) || (!hasGender && level === 2)) { + traverseData(data, level + 1); + return; + } keys.forEach((key: string, k: integer) => { if (entry) return; @@ -207,13 +223,18 @@ export class GameData { if (forceFemale !== undefined && female !== forceFemale) return break; + case 3: + abilityIndex = k; + if (forceAbilityIndex !== undefined && abilityIndex !== forceAbilityIndex) + return; + break; } if ('caught' in data[key]) { if (data[key].caught) entry = data[key] as DexEntry; } else - traverseData(data[key] as DexData, level + (hasForms ? 1 : 2)); + traverseData(data[key] as DexData, level + 1); }); }; @@ -224,6 +245,7 @@ export class GameData { shiny: shiny, formIndex: formIndex, female: female, + abilityIndex: abilityIndex, entry: entry }; } @@ -240,13 +262,14 @@ export class GameData { case 'shiny': const shinyMap = new Map(); for (let s = 0; s < 2; s++) { - const props = { shiny: !!s, formIndex: null }; + const props = { shiny: !!s }; shinyMap.set(!!s, { shiny: !!s, formIndex: hasForms ? getTreeOrValueMap('formIndex', props as StarterDexUnlockTree) : null, female: !hasForms && hasGender ? getTreeOrValueMap('female', props as StarterDexUnlockTree) : null, - key: hasForms ? 'formIndex' : hasGender ? 'female' : 'entry', - entry: hasForms || hasGender ? null : this.dexData[species.speciesId][!s ? 0 : 1] + abilityIndex: !hasForms && !hasGender ? getTreeOrValueMap('abilityIndex', props as StarterDexUnlockTree) : null, + key: hasForms ? 'formIndex' : hasGender ? 'female' : 'abilityIndex', + entry: null, }); } return shinyMap; @@ -258,25 +281,46 @@ export class GameData { shiny: parent.shiny, formIndex: f, female: hasGender ? getTreeOrValueMap('female', props as StarterDexUnlockTree) : null, - key: hasGender ? 'female' : 'entry', - entry: hasGender ? null : this.dexData[species.speciesId][!parent.shiny ? 0 : 1][f] + abilityIndex: !hasGender ? getTreeOrValueMap('abilityIndex', props as StarterDexUnlockTree) : null, + key: hasGender ? 'female' : 'abilityIndex', + entry: null }); } return formMap; case 'female': const genderMap = new Map(); for (let g = 0; g < 2; g++) { + const props = { shiny: parent.shiny, formIndex: parent.formIndex, female: !!g }; genderMap.set(!!g, { shiny: parent.shiny, formIndex: parent.formIndex, female: !!g, - key: 'entry', - entry: hasForms - ? this.dexData[species.speciesId][!parent.shiny ? 0 : 1][parent.formIndex as integer][g] - : this.dexData[species.speciesId][!parent.shiny ? 0 : 1][g] + abilityIndex: getTreeOrValueMap('abilityIndex', props as StarterDexUnlockTree), + key: 'abilityIndex', + entry: null }); } return genderMap; + case 'abilityIndex': + const abilityMap = new Map(); + const abilityCount = species.getAbilityCount(); + for (let a = 0; a < abilityCount; a++) { + abilityMap.set(a, { + shiny: parent.shiny, + formIndex: parent.formIndex, + female: parent.female, + abilityIndex: a, + key: 'entry', + entry: hasForms + ? hasGender + ? this.dexData[species.speciesId][!parent.shiny ? 0 : 1][parent.formIndex as integer][!parent.female ? 0 : 1][a] + : this.dexData[species.speciesId][!parent.shiny ? 0 : 1][parent.formIndex as integer][a] + : hasGender + ? this.dexData[species.speciesId][!parent.shiny ? 0 : 1][!parent.female ? 0 : 1][a] + : this.dexData[species.speciesId][!parent.shiny ? 0 : 1][a] + }); + } + return abilityMap; } }; @@ -284,10 +328,33 @@ export class GameData { shiny: getTreeOrValueMap('shiny'), formIndex: null, female: null, + abilityIndex: null, key: 'shiny', entry: null }; return root; } + + convertDexData(dexData: DexData): void { + const traverseData = (speciesId: Species, data: DexData) => { + const keys = Object.keys(data); + keys.forEach((key: string, k: integer) => { + if ('caught' in data[key]) { + const abilityCount = getPokemonSpecies(speciesId).getAbilityCount(); + data[key] = { + 0: data[key] + }; + for (let a = 1; a < abilityCount; a++) + data[key][a] = { seen: false, caught: false }; + } else + traverseData(speciesId, data[key]); + }); + } + + Object.keys(dexData).forEach((species: string, s: integer) => { + const speciesId = parseInt(species); + traverseData(speciesId, dexData[species]); + }); + } } \ No newline at end of file diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index dddf1bd02..6845a9d37 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -1,13 +1,14 @@ import BattleScene, { Button } from "../battle-scene"; import PokemonSpecies, { allSpecies } from "../data/pokemon-species"; import { Species } from "../data/species"; -import { TextStyle, addTextObject } from "./text"; +import { TextStyle, addTextObject, getTextColor } from "./text"; import { Mode } from "./ui"; import * as Utils from "../utils"; import MessageUiHandler from "./message-ui-handler"; import { DexEntryDetails, StarterDexUnlockTree } from "../system/game-data"; import { Gender, getGenderColor, getGenderSymbol } from "../data/gender"; import { pokemonPrevolutions } from "../data/pokemon-evolutions"; +import { abilities } from "../data/ability"; export type StarterSelectCallback = (starters: Starter[]) => void; @@ -16,6 +17,7 @@ export interface Starter { shiny: boolean; formIndex: integer; female: boolean; + abilityIndex: integer; } export default class StarterSelectUiHandler extends MessageUiHandler { @@ -25,6 +27,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { private pokemonSprite: Phaser.GameObjects.Sprite; private pokemonNameText: Phaser.GameObjects.Text; private pokemonGenderText: Phaser.GameObjects.Text; + private pokemonAbilityLabelText: Phaser.GameObjects.Text; + private pokemonAbilityText: Phaser.GameObjects.Text; private instructionsText: Phaser.GameObjects.Text; private starterSelectMessageBoxContainer: Phaser.GameObjects.Container; @@ -32,6 +36,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { private shinyCursor: integer = 0; private formCursor: integer = 0; private genderCursor: integer = 0; + private abilityCursor: integer = 0; private genCursor: integer = 0; private genSpecies: PokemonSpecies[][] = []; @@ -39,12 +44,13 @@ export default class StarterSelectUiHandler extends MessageUiHandler { private speciesLoaded: Map = new Map(); private starterGens: integer[] = []; private starterCursors: integer[] = []; - private starterDetails: [boolean, integer, boolean][] = []; + private starterDetails: [boolean, integer, boolean, integer][] = []; private speciesStarterDexEntry: DexEntryDetails; private speciesStarterDexTree: StarterDexUnlockTree; private canCycleShiny: boolean; private canCycleForm: boolean; private canCycleGender: boolean; + private canCycleAbility: boolean; private assetLoadCancelled: Utils.BooleanHolder; private cursorObj: Phaser.GameObjects.Image; @@ -86,6 +92,14 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonGenderText.setOrigin(0, 0); this.starterSelectContainer.add(this.pokemonGenderText); + this.pokemonAbilityLabelText = addTextObject(this.scene, 6, 126, 'ABILITY:', TextStyle.SUMMARY, { fontSize: '64px' }); + this.pokemonAbilityLabelText.setOrigin(0, 0); + this.starterSelectContainer.add(this.pokemonAbilityLabelText); + + this.pokemonAbilityText = addTextObject(this.scene, 38, 126, '', TextStyle.SUMMARY, { fontSize: '64px' }); + this.pokemonAbilityText.setOrigin(0, 0); + this.starterSelectContainer.add(this.pokemonAbilityText); + const genText = addTextObject(this.scene, 115, 6, 'I\nII\nIII\nIV\nV', TextStyle.WINDOW); genText.setLineSpacing(16); this.starterSelectContainer.add(genText); @@ -161,7 +175,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, 1, 132, '', TextStyle.PARTY, { fontSize: '52px' }); + this.instructionsText = addTextObject(this.scene, 4, 140, '', TextStyle.PARTY, { fontSize: '42px' }); this.starterSelectContainer.add(this.instructionsText); this.starterSelectMessageBoxContainer = this.scene.add.container(0, this.scene.game.canvas.height / 6); @@ -248,7 +262,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.starterIcons[this.starterCursors.length].play(species.getIconKey(this.speciesStarterDexEntry?.female)); this.starterGens.push(this.genCursor); this.starterCursors.push(this.cursor); - this.starterDetails.push([ !!this.shinyCursor, this.formCursor, !!this.genderCursor ]); + this.starterDetails.push([ !!this.shinyCursor, this.formCursor, !!this.genderCursor, this.abilityCursor ]); if (this.speciesLoaded.get(species.speciesId)) species.cry(this.scene); if (this.starterCursors.length === 3) { @@ -263,7 +277,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { species: thisObj.genSpecies[thisObj.starterGens[i]][thisObj.starterCursors[i]], shiny: thisObj.starterDetails[i][0], formIndex: thisObj.starterDetails[i][1], - female: thisObj.starterDetails[i][2] + female: thisObj.starterDetails[i][2], + abilityIndex: thisObj.starterDetails[i][3] }; })); }, () => { @@ -292,7 +307,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { switch (button) { case Button.CYCLE_SHINY: if (this.canCycleShiny) { - this.setSpeciesDetails(this.lastSpecies, !this.shinyCursor, undefined, undefined); + this.setSpeciesDetails(this.lastSpecies, !this.shinyCursor, undefined, undefined, undefined); if (this.shinyCursor) this.scene.sound.play('sparkle'); else @@ -301,13 +316,20 @@ export default class StarterSelectUiHandler extends MessageUiHandler { break; case Button.CYCLE_FORM: if (this.canCycleForm) { - this.setSpeciesDetails(this.lastSpecies, undefined, (this.formCursor + 1) % this.lastSpecies.forms.length, undefined); + this.setSpeciesDetails(this.lastSpecies, undefined, (this.formCursor + 1) % this.lastSpecies.forms.length, undefined, undefined); success = true; } break; case Button.CYCLE_GENDER: if (this.canCycleGender) { - this.setSpeciesDetails(this.lastSpecies, undefined, undefined, !this.genderCursor); + this.setSpeciesDetails(this.lastSpecies, undefined, undefined, !this.genderCursor, undefined); + success = true; + } + break; + case Button.CYCLE_ABILITY: + if (this.canCycleAbility) { + const abilityCount = this.lastSpecies.getAbilityCount(); + this.setSpeciesDetails(this.lastSpecies, undefined, undefined, undefined, (this.abilityCursor + 1) % abilityCount); success = true; } break; @@ -341,19 +363,31 @@ export default class StarterSelectUiHandler extends MessageUiHandler { let instructionLines = [ 'Arrow Keys/WASD: Move' ]; + let cycleInstructionLines = []; if (!this.genMode) instructionLines.push('A/Space/Enter: Select'); if (this.starterCursors.length) instructionLines.push('X/Backspace/Esc: Undo'); if (this.speciesStarterDexTree) { if (this.canCycleShiny) - instructionLines.push('R: Cycle Shiny'); - if (this.canCycleForm) - instructionLines.push('F: Cycle Form'); - if (this.canCycleGender) - instructionLines.push('G: Cycle Gender'); + cycleInstructionLines.push('R: Cycle Shiny'); + if (this.canCycleForm) + cycleInstructionLines.push('F: Cycle Form'); + if (this.canCycleGender) + cycleInstructionLines.push('G: Cycle Gender'); + if (this.canCycleAbility) + cycleInstructionLines.push('E: Cycle Ability'); } + if (cycleInstructionLines.length > 2) { + cycleInstructionLines[0] += ' | ' + cycleInstructionLines.splice(1, 1); + if (cycleInstructionLines.length > 2) + cycleInstructionLines[1] += ' | ' + cycleInstructionLines.splice(2, 1); + } + + for (let cil of cycleInstructionLines) + instructionLines.push(cil); + this.instructionsText.setText(instructionLines.join('\n')); } @@ -418,23 +452,25 @@ export default class StarterSelectUiHandler extends MessageUiHandler { if (species && this.speciesStarterDexEntry) { this.pokemonNumberText.setText(Utils.padInt(species.speciesId, 3)); this.pokemonNameText.setText(species.name.toUpperCase()); - - this.setSpeciesDetails(species, !!this.speciesStarterDexEntry?.shiny, this.speciesStarterDexEntry?.formIndex || 0, !!this.speciesStarterDexEntry?.female); + + this.setSpeciesDetails(species, !!this.speciesStarterDexEntry?.shiny, this.speciesStarterDexEntry?.formIndex, !!this.speciesStarterDexEntry?.female, this.speciesStarterDexEntry?.abilityIndex); } else { this.pokemonNumberText.setText(Utils.padInt(0, 3)); this.pokemonNameText.setText(species ? '???' : ''); - this.setSpeciesDetails(species, false, 0, false); + this.setSpeciesDetails(species, false, 0, false, 0); } } - setSpeciesDetails(species: PokemonSpecies, shiny: boolean, formIndex: integer, female: boolean): void { + setSpeciesDetails(species: PokemonSpecies, shiny: boolean, formIndex: integer, female: boolean, abilityIndex: integer): void { if (shiny !== undefined) this.shinyCursor = !shiny ? 0 : 1; if (formIndex !== undefined) this.formCursor = formIndex; if (female !== undefined) this.genderCursor = !female ? 0 : 1; + if (abilityIndex !== undefined) + this.abilityCursor = abilityIndex; this.pokemonSprite.setVisible(false); @@ -444,8 +480,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } if (species) { - const defaultDexEntry = this.scene.gameData.getDefaultDexEntry(species, shiny, formIndex, female) || this.scene.gameData.getDefaultDexEntry(species); - const dexEntry = this.scene.gameData.getDexEntry(species, !!this.shinyCursor, this.formCursor, !!this.genderCursor); + const defaultDexEntry = this.scene.gameData.getDefaultDexEntry(species, shiny, formIndex, female, abilityIndex) || this.scene.gameData.getDefaultDexEntry(species); + const dexEntry = this.scene.gameData.getDexEntry(species, !!this.shinyCursor, this.formCursor, !!this.genderCursor, this.abilityCursor); if (!dexEntry.caught) { if (shiny === undefined || (defaultDexEntry && shiny !== defaultDexEntry.shiny)) @@ -454,10 +490,13 @@ export default class StarterSelectUiHandler extends MessageUiHandler { formIndex = defaultDexEntry.formIndex || 0; if (female === undefined || (defaultDexEntry && female !== defaultDexEntry.female)) female = defaultDexEntry.female; + if (abilityIndex === undefined || (defaultDexEntry && abilityIndex !== defaultDexEntry.abilityIndex)) + abilityIndex = defaultDexEntry.abilityIndex; } else { shiny = !!this.shinyCursor; formIndex = this.formCursor; female = !!this.genderCursor; + abilityIndex = this.abilityCursor; } if (this.speciesStarterDexTree) { @@ -484,6 +523,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { values = []; } if (!tree.entry) { + if (!tree[tree.key]) + console.log(tree, tree.key); for (let key of tree[tree.key].keys()) calcUnlockedCount(tree[tree.key].get(key), prop); } else if (tree.entry.caught) { @@ -512,6 +553,12 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.canCycleGender = count > 1; } else this.canCycleGender = false; + + if (this.lastSpecies.getAbilityCount() > 1) { + calcUnlockedCount(tree, 'abilityIndex', true); + this.canCycleAbility = count > 1; + } else + this.canCycleAbility = false; } if (species.malePercent !== null) { @@ -521,8 +568,17 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonGenderText.setShadowColor(getGenderColor(gender, true)); } else this.pokemonGenderText.setText(''); - } else + + const ability = this.lastSpecies.getAbility(abilityIndex); + this.pokemonAbilityText.setText(abilities[ability].name.toUpperCase()); + + 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.pokemonGenderText.setText(''); + this.pokemonAbilityText.setText(''); + } this.updateInstructions(); } diff --git a/src/ui/summary-ui-handler.ts b/src/ui/summary-ui-handler.ts index 6c04ac29d..561321bbc 100644 --- a/src/ui/summary-ui-handler.ts +++ b/src/ui/summary-ui-handler.ts @@ -435,7 +435,7 @@ export default class SummaryUiHandler extends UiHandler { if (this.pokemon.species.type2) profileContainer.add(getTypeIcon(1, this.pokemon.species.type2)); - const ability = abilities[this.pokemon.species[`ability${!this.pokemon.abilityIndex ? '1' : '2'}`]]; + const ability = abilities[this.pokemon.species.getAbility(this.pokemon.abilityIndex)]; const abilityNameText = addTextObject(this.scene, 7, 66, ability.name.toUpperCase(), TextStyle.SUMMARY); abilityNameText.setOrigin(0, 1); diff --git a/src/ui/text.ts b/src/ui/text.ts index 2236ab976..f5ad2854c 100644 --- a/src/ui/text.ts +++ b/src/ui/text.ts @@ -43,8 +43,13 @@ export function addTextObject(scene: Phaser.Scene, x: number, y: number, content shadowColor = getTextColor(style, true); - if (extraStyleOptions) + if (extraStyleOptions) { + if (extraStyleOptions.fontSize) { + const sizeRatio = parseInt(extraStyleOptions.fontSize.slice(0, -2)) / parseInt(styleOptions.fontSize.slice(0, -2)); + shadowSize *= sizeRatio; + } styleOptions = Object.assign(styleOptions, extraStyleOptions); + } const ret = scene.add.text(x, y, content, styleOptions); ret.setScale(0.1666666667);