diff --git a/public/images/statuses.json b/public/images/statuses.json index 019c2dd6d..bf05b2ab0 100644 --- a/public/images/statuses.json +++ b/public/images/statuses.json @@ -4,34 +4,34 @@ "image": "statuses.png", "format": "RGBA8888", "size": { - "w": 20, - "h": 56 + "w": 22, + "h": 64 }, "scale": 1, "frames": [ { - "filename": "burn", + "filename": "pokerus", "rotated": false, "trimmed": false, "sourceSize": { - "w": 20, + "w": 22, "h": 8 }, "spriteSourceSize": { "x": 0, "y": 0, - "w": 20, + "w": 22, "h": 8 }, "frame": { "x": 0, "y": 0, - "w": 20, + "w": 22, "h": 8 } }, { - "filename": "faint", + "filename": "burn", "rotated": false, "trimmed": false, "sourceSize": { @@ -52,7 +52,7 @@ } }, { - "filename": "freeze", + "filename": "faint", "rotated": false, "trimmed": false, "sourceSize": { @@ -73,7 +73,7 @@ } }, { - "filename": "paralysis", + "filename": "freeze", "rotated": false, "trimmed": false, "sourceSize": { @@ -94,7 +94,7 @@ } }, { - "filename": "poison", + "filename": "paralysis", "rotated": false, "trimmed": false, "sourceSize": { @@ -115,7 +115,7 @@ } }, { - "filename": "sleep", + "filename": "poison", "rotated": false, "trimmed": false, "sourceSize": { @@ -136,7 +136,7 @@ } }, { - "filename": "toxic", + "filename": "sleep", "rotated": false, "trimmed": false, "sourceSize": { @@ -155,6 +155,27 @@ "w": 20, "h": 8 } + }, + { + "filename": "toxic", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 20, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 20, + "h": 8 + }, + "frame": { + "x": 0, + "y": 56, + "w": 20, + "h": 8 + } } ] } @@ -162,6 +183,6 @@ "meta": { "app": "https://www.codeandweb.com/texturepacker", "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:b1f59475db34388a7177fe541f95d9e8:47a06b96ac24cc4249538472b2604202:e6649238c018d3630e55681417c698ca$" + "smartupdate": "$TexturePacker:SmartUpdate:37686e85605d17b806f22d43081c1139:70535ffee63ba61b3397d8470c2c8982:e6649238c018d3630e55681417c698ca$" } } diff --git a/public/images/statuses.png b/public/images/statuses.png index defa8cae3..d372b989b 100644 Binary files a/public/images/statuses.png and b/public/images/statuses.png differ diff --git a/public/images/statuses/pokerus.png b/public/images/statuses/pokerus.png new file mode 100644 index 000000000..2963d0081 Binary files /dev/null and b/public/images/statuses/pokerus.png differ diff --git a/public/images/ui/starter_select_cursor_pokerus.png b/public/images/ui/starter_select_cursor_pokerus.png new file mode 100644 index 000000000..123b0a0f1 Binary files /dev/null and b/public/images/ui/starter_select_cursor_pokerus.png differ diff --git a/public/images/ui/summary_status.png b/public/images/ui/summary_status.png new file mode 100644 index 000000000..4a5b27e9e Binary files /dev/null and b/public/images/ui/summary_status.png differ diff --git a/src/battle-phases.ts b/src/battle-phases.ts index 26cdeac5e..9f2918f63 100644 --- a/src/battle-phases.ts +++ b/src/battle-phases.ts @@ -16,7 +16,7 @@ import { EvolutionPhase } from "./evolution-phase"; import { BattlePhase } from "./battle-phase"; import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } from "./data/battle-stat"; import { Biome, biomeLinks } from "./data/biome"; -import { ModifierPoolType, ModifierType, ModifierTypeOption, PokemonModifierType, PokemonMoveModifierType, TmModifierType, getPlayerModifierTypeOptionsForWave, regenerateModifierPoolThresholds } from "./modifier/modifier-type"; +import { ModifierPoolType, ModifierType, ModifierTypeFunc, ModifierTypeOption, PokemonModifierType, PokemonMoveModifierType, TmModifierType, getPlayerModifierTypeOptionsForWave, modifierTypes, regenerateModifierPoolThresholds } from "./modifier/modifier-type"; import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; import { BattlerTagLapseType, BattlerTagType, HideSpriteTag as HiddenTag, TrappedTag } from "./data/battler-tag"; import { getPokemonMessage } from "./messages"; @@ -109,6 +109,8 @@ export class SelectStarterPhase extends BattlePhase { ? !starter.female ? Gender.MALE : Gender.FEMALE : Gender.GENDERLESS; const starterPokemon = new PlayerPokemon(this.scene, starter.species, startingLevel, starter.abilityIndex, starter.formIndex, starterGender, starter.shiny); + if (starter.pokerus) + starterPokemon.pokerus = true; starterPokemon.setVisible(false); party.push(starterPokemon); loadPokemonAssets.push(starterPokemon.loadAssets()); @@ -2036,6 +2038,8 @@ export class VictoryPhase extends PokemonPhase { expMultiplier += multipleParticipantExpBonusModifier.getStackCount() * 0.2; } else if (expShareModifier) expMultiplier += (expShareModifier.getStackCount() * 0.2) / participantIds.size; + if (partyMember.pokerus) + expMultiplier *= 1.5; const pokemonExp = new Utils.NumberHolder(expValue * expMultiplier); this.scene.applyModifiers(PokemonExpBoosterModifier, true, partyMember, pokemonExp); partyMemberExp.push(Math.floor(pokemonExp.value)); @@ -2078,7 +2082,10 @@ export class VictoryPhase extends PokemonPhase { if (this.scene.currentBattle.battleType === BattleType.TRAINER) this.scene.pushPhase(new TrainerVictoryPhase(this.scene)); if (this.scene.gameMode === GameMode.ENDLESS || this.scene.currentBattle.waveIndex < this.scene.finalWave) { - this.scene.pushPhase(new SelectModifierPhase(this.scene)); + if (this.scene.currentBattle.waveIndex > 30 || this.scene.currentBattle.waveIndex % 10) + this.scene.pushPhase(new SelectModifierPhase(this.scene)); + else + this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.GOLDEN_EXP_CHARM)) this.scene.pushPhase(new NewBattlePhase(this.scene)); } else this.scene.pushPhase(new GameOverPhase(this.scene, true)); @@ -2098,7 +2105,7 @@ export class TrainerVictoryPhase extends BattlePhase { const modifierRewardFuncs = this.scene.currentBattle.trainer.config.modifierRewardFuncs; for (let modifierRewardFunc of modifierRewardFuncs) - this.scene.unshiftPhase(new ModifierRewardPhase(this.scene, modifierRewardFunc())); + this.scene.unshiftPhase(new ModifierRewardPhase(this.scene, modifierRewardFunc)); this.scene.ui.showText(`You defeated\n${this.scene.currentBattle.trainer.getName()}!`, null, () => { const defeatMessages = this.scene.currentBattle.trainer.config.victoryMessages; @@ -2130,10 +2137,12 @@ export class TrainerVictoryPhase extends BattlePhase { export class ModifierRewardPhase extends BattlePhase { private modifierType: ModifierType; - constructor(scene: BattleScene, modifierType: ModifierType) { + constructor(scene: BattleScene, modifierTypeFunc: ModifierTypeFunc) { super(scene); - this.modifierType = modifierType; + this.modifierType = modifierTypeFunc(); + if (!this.modifierType.id) + this.modifierType.id = Object.keys(modifierTypes).find(k => modifierTypes[k] === modifierTypeFunc); } start() { diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 612486a66..2905af433 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -215,6 +215,7 @@ export default class BattleScene extends Phaser.Scene { this.loadImage('summary_bg', 'ui'); this.loadImage('summary_overlay_shiny', 'ui'); this.loadImage('summary_profile', 'ui'); + this.loadImage('summary_status', 'ui'); this.loadImage('summary_stats', 'ui'); this.loadImage('summary_stats_overlay_exp', 'ui'); this.loadImage('summary_moves', 'ui'); @@ -232,6 +233,7 @@ export default class BattleScene extends Phaser.Scene { this.loadImage('starter_select_message', 'ui'); this.loadImage('starter_select_cursor', 'ui'); this.loadImage('starter_select_cursor_highlight', 'ui'); + this.loadImage('starter_select_cursor_pokerus', 'ui'); this.loadImage('starter_select_gen_cursor', 'ui'); this.loadImage('starter_select_gen_cursor_highlight', 'ui'); @@ -649,6 +651,7 @@ export default class BattleScene extends Phaser.Scene { const availablePartyMemberCount = this.getParty().filter(p => !p.isFainted()).length; if (lastBattle) { this.getEnemyParty().forEach(enemyPokemon => enemyPokemon.destroy()); + this.trySpreadPokerus(); if (showTrainer) { playerField.forEach((_, p) => this.unshiftPhase(new ReturnPhase(this, p))); this.unshiftPhase(new ShowTrainerPhase(this)); @@ -717,16 +720,43 @@ export default class BattleScene extends Phaser.Scene { return this.arena; } + trySpreadPokerus(): void { + const party = this.getParty(); + const infectedIndexes: integer[] = []; + party.forEach((pokemon, p) => { + if (!pokemon.pokerus || infectedIndexes.indexOf(p) > -1) + return; + + this.executeWithSeedOffset(() => { + if (p) { + const partyMember = party[p - 1]; + if (!partyMember.pokerus && !Utils.randSeedInt(10)) { + partyMember.pokerus = true; + infectedIndexes.push(p - 1); + } + } + + if (p < party.length - 1) { + const partyMember = party[p + 1]; + if (!partyMember.pokerus && !Utils.randSeedInt(10)) { + partyMember.pokerus = true; + infectedIndexes.push(p + 1); + } + } + }, this.currentBattle.waveIndex + (p << 8)); + }); + } + resetSeed(waveIndex?: integer): void { this.waveSeed = Utils.shiftCharCodes(this.seed, waveIndex || this.currentBattle.waveIndex); Phaser.Math.RND.sow([ this.waveSeed ]); } - executeWithSeedOffset(func: Function, offset: integer): void { + executeWithSeedOffset(func: Function, offset: integer, seedOverride?: string): void { if (!func) return; const state = Phaser.Math.RND.state(); - Phaser.Math.RND.sow([ Utils.shiftCharCodes(this.seed, offset) ]); + Phaser.Math.RND.sow([ Utils.shiftCharCodes(seedOverride || this.seed, offset) ]); func(); Phaser.Math.RND.state(state); } diff --git a/src/data/trainer-type.ts b/src/data/trainer-type.ts index 53a7e7c98..953e4788c 100644 --- a/src/data/trainer-type.ts +++ b/src/data/trainer-type.ts @@ -286,7 +286,7 @@ export class TrainerConfig { public encounterBgm: string; public femaleEncounterBgm: string; public victoryBgm: string; - public modifierRewardFuncs: (() => ModifierType)[] = []; + public modifierRewardFuncs: ModifierTypeFunc[] = []; public partyTemplates: TrainerPartyTemplate[]; public partyTemplateFunc: PartyTemplateFunc; public partyMemberFuncs: PartyMemberFuncs = {}; diff --git a/src/pokemon.ts b/src/pokemon.ts index 8e4a1bb4e..38de8d031 100644 --- a/src/pokemon.ts +++ b/src/pokemon.ts @@ -55,6 +55,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { public moveset: PokemonMove[]; public status: Status; public winCount: integer; + public pokerus: boolean; public summonData: PokemonSummonData; public battleSummonData: PokemonBattleSummonData; @@ -96,6 +97,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.moveset = dataSource.moveset; this.status = dataSource.status; this.winCount = dataSource.winCount; + this.pokerus = !!dataSource.pokerus; } else { this.generateAndPopulateMoveset(); @@ -142,6 +144,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } this.winCount = 0; + this.pokerus = false; } if (!species.isObtainable()) diff --git a/src/system/pokemon-data.ts b/src/system/pokemon-data.ts index 1b47ede55..5c89fd5f7 100644 --- a/src/system/pokemon-data.ts +++ b/src/system/pokemon-data.ts @@ -24,6 +24,7 @@ export default class PokemonData { public moveset: PokemonMove[]; public status: Status; public winCount: integer; + public pokerus: boolean; public summonData: PokemonSummonData; @@ -44,6 +45,7 @@ export default class PokemonData { this.stats = source.stats; this.ivs = source.ivs; this.winCount = source.winCount; + this.pokerus = !!source.pokerus; if (sourcePokemon) { this.moveset = sourcePokemon.moveset; @@ -51,11 +53,7 @@ export default class PokemonData { if (this.player) this.summonData = sourcePokemon.summonData; } else { - this.moveset = source.moveset.map((m: any) => { - const move = new PokemonMove(m.moveId, m.ppUsed, m.ppUp); - move.disableTurns = m.disableTurns; - return move; - }); + this.moveset = source.moveset.map((m: any) => new PokemonMove(m.moveId, m.ppUsed, m.ppUp)); this.status = source.status ? new Status(source.status.effect, source.status.turnCount, source.status.cureTurn) : undefined; diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 43c6a0361..fecb7c4e8 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -1,5 +1,5 @@ import BattleScene, { Button } from "../battle-scene"; -import PokemonSpecies, { allSpecies } from "../data/pokemon-species"; +import PokemonSpecies, { allSpecies, getPokemonSpecies } from "../data/pokemon-species"; import { Species } from "../data/species"; import { TextStyle, addTextObject, getTextColor } from "./text"; import { Mode } from "./ui"; @@ -21,6 +21,7 @@ export interface Starter { formIndex: integer; female: boolean; abilityIndex: integer; + pokerus: boolean; } export default class StarterSelectUiHandler extends MessageUiHandler { @@ -49,6 +50,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { private speciesLoaded: Map = new Map(); private starterGens: integer[] = []; private starterCursors: integer[] = []; + private pokerusGens: integer[] = []; + private pokerusCursors: integer[] = []; private starterDetails: [boolean, integer, boolean, integer][] = []; private speciesStarterDexEntry: DexEntryDetails; private speciesStarterDexTree: StarterDexUnlockTree; @@ -60,6 +63,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { private assetLoadCancelled: Utils.BooleanHolder; private cursorObj: Phaser.GameObjects.Image; private starterCursorObjs: Phaser.GameObjects.Image[]; + private pokerusCursorObjs: Phaser.GameObjects.Image[]; private starterIcons: Phaser.GameObjects.Sprite[]; private genCursorObj: Phaser.GameObjects.Image; private genCursorHighlightObj: Phaser.GameObjects.Image; @@ -127,6 +131,14 @@ export default class StarterSelectUiHandler extends MessageUiHandler { return container; }); + this.pokerusCursorObjs = new Array(3).fill(null).map(() => { + const cursorObj = this.scene.add.image(0, 0, 'starter_select_cursor_pokerus'); + cursorObj.setVisible(false); + cursorObj.setOrigin(0, 0); + this.starterSelectContainer.add(cursorObj); + return cursorObj; + }); + this.starterCursorObjs = new Array(3).fill(null).map(() => { const cursorObj = this.scene.add.image(0, 0, 'starter_select_cursor_highlight'); cursorObj.setVisible(false); @@ -147,6 +159,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.genCursorObj.setVisible(false); this.genCursorObj.setOrigin(0, 0); this.starterSelectContainer.add(this.genCursorObj); + + const starterSpecies: Species[] = []; for (let g = 0; g < this.starterSelectGenIconContainers.length; g++) { let s = 0; @@ -157,6 +171,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { break; if (pokemonPrevolutions.hasOwnProperty(species.speciesId) || species.generation !== g + 1) continue; + starterSpecies.push(species.speciesId); this.speciesLoaded.set(species.speciesId, false); this.genSpecies[g].push(species); const dexEntry = this.scene.gameData.getDefaultDexEntry(species); @@ -207,6 +222,41 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.message.setOrigin(0, 1); this.starterSelectMessageBoxContainer.add(this.message); + const date = new Date(); + date.setUTCHours(0, 0, 0, 0); + + this.scene.executeWithSeedOffset(() => { + for (let c = 0; c < 3; c++) { + let randomSpeciesId: Species; + let species: PokemonSpecies; + let pokerusCursor: integer; + + const generateSpecies = () => { + randomSpeciesId = Phaser.Math.RND.pick(starterSpecies); + species = getPokemonSpecies(randomSpeciesId); + pokerusCursor = this.genSpecies[species.generation - 1].indexOf(species); + }; + + let dupe = false; + + do { + generateSpecies(); + + for (let pc = 0; pc < c; pc++) { + if (this.pokerusGens[pc] === species.generation -1 && this.pokerusCursors[pc] === pokerusCursor) { + dupe = true; + break; + } + } + } while (dupe); + + this.pokerusGens.push(species.generation - 1); + this.pokerusCursors.push(pokerusCursor); + this.pokerusCursorObjs[c].setPosition(148 + 18 * (pokerusCursor % 9), 10 + 18 * Math.floor(pokerusCursor / 9)); + console.log(species.name); + } + }, 0, date.getTime().toString()); + this.updateInstructions(); } @@ -297,12 +347,14 @@ export default class StarterSelectUiHandler extends MessageUiHandler { const originalStarterSelectCallback = this.starterSelectCallback; this.starterSelectCallback = null; originalStarterSelectCallback(new Array(3).fill(0).map(function (_, i) { + const starterSpecies = thisObj.genSpecies[thisObj.starterGens[i]][thisObj.starterCursors[i]]; return { - species: thisObj.genSpecies[thisObj.starterGens[i]][thisObj.starterCursors[i]], + species: starterSpecies, shiny: thisObj.starterDetails[i][0], formIndex: thisObj.starterDetails[i][1], female: thisObj.starterDetails[i][2], - abilityIndex: thisObj.starterDetails[i][3] + abilityIndex: thisObj.starterDetails[i][3], + pokerus: !![ 0, 1, 2 ].filter(n => thisObj.pokerusGens[n] === starterSpecies.generation - 1 && thisObj.pokerusCursors[n] === thisObj.genSpecies[starterSpecies.generation - 1].indexOf(starterSpecies)).length }; })); }; @@ -435,6 +487,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { for (let s = 0; s < this.starterCursorObjs.length; s++) this.starterCursorObjs[s].setVisible(this.starterGens[s] === cursor); + for (let s = 0; s < this.pokerusCursorObjs.length; s++) + this.pokerusCursorObjs[s].setVisible(this.pokerusGens[s] === cursor); } else { changed = super.setCursor(cursor); diff --git a/src/ui/summary-ui-handler.ts b/src/ui/summary-ui-handler.ts index aa114135b..098dbea44 100644 --- a/src/ui/summary-ui-handler.ts +++ b/src/ui/summary-ui-handler.ts @@ -12,6 +12,7 @@ import { getLevelTotalExp } from "../data/exp"; import { Stat, getStatName } from "../data/pokemon-stat"; import { abilities } from "../data/ability"; import { PokemonHeldItemModifier } from "../modifier/modifier"; +import { StatusEffect } from "../data/status-effect"; enum Page { PROFILE, @@ -36,6 +37,8 @@ export default class SummaryUiHandler extends UiHandler { private pokeball: Phaser.GameObjects.Sprite; private levelText: Phaser.GameObjects.Text; private genderText: Phaser.GameObjects.Text; + private statusContainer: Phaser.GameObjects.Container; + private status: Phaser.GameObjects.Image; private summaryPageContainer: Phaser.GameObjects.Container; private movesContainer: Phaser.GameObjects.Container; private moveDescriptionText: Phaser.GameObjects.Text; @@ -56,6 +59,7 @@ export default class SummaryUiHandler extends UiHandler { private newMove: Move; private moveSelectFunction: Function; private transitioning: boolean; + private statusVisible: boolean; private moveEffectsVisible: boolean; private moveSelect: boolean; @@ -81,11 +85,15 @@ export default class SummaryUiHandler extends UiHandler { this.tabSprite.setOrigin(1, 1); this.summaryContainer.add(this.tabSprite); + const summaryLabel = addTextObject(this.scene, 4, -165, 'Pokémon Info', TextStyle.SUMMARY); + summaryLabel.setOrigin(0, 1); + this.summaryContainer.add(summaryLabel); + this.shinyOverlay = this.scene.add.image(6, -54, 'summary_overlay_shiny'); this.shinyOverlay.setOrigin(0, 1); this.summaryContainer.add(this.shinyOverlay); - this.numberText = addTextObject(this.scene, 17, -150, '000', TextStyle.SUMMARY); + this.numberText = addTextObject(this.scene, 17, -149, '000', TextStyle.SUMMARY); this.numberText.setOrigin(0, 1); this.summaryContainer.add(this.numberText); @@ -108,6 +116,25 @@ export default class SummaryUiHandler extends UiHandler { this.genderText.setOrigin(0, 1); this.summaryContainer.add(this.genderText); + this.statusContainer = this.scene.add.container(-106, -16); + + const statusBg = this.scene.add.image(0, 0, 'summary_status'); + statusBg.setOrigin(0, 0); + + this.statusContainer.add(statusBg); + + const statusLabel = addTextObject(this.scene, 3, 0, 'Status', TextStyle.SUMMARY); + statusLabel.setOrigin(0, 0); + + this.statusContainer.add(statusLabel); + + this.status = this.scene.add.sprite(91, 4, 'statuses'); + this.status.setOrigin(0.5, 0); + + this.statusContainer.add(this.status); + + this.summaryContainer.add(this.statusContainer); + this.moveEffectContainer = this.scene.add.container(106, -62); this.summaryContainer.add(this.moveEffectContainer); @@ -194,6 +221,14 @@ export default class SummaryUiHandler extends UiHandler { this.showMoveSelect(); break; } + + const fromSummary = args.length >= 2; + + if (this.pokemon.status || this.pokemon.pokerus) { + this.showStatus(!fromSummary); + this.status.setFrame(this.pokemon.status ? StatusEffect[this.pokemon.status.effect].toLowerCase() : 'pokerus'); + } else + this.hideStatus(!fromSummary); } processInput(button: Button) { @@ -605,6 +640,30 @@ export default class SummaryUiHandler extends UiHandler { } } + showStatus(instant?: boolean) { + if (this.statusVisible) + return; + this.statusVisible = true; + this.scene.tweens.add({ + targets: this.statusContainer, + x: 0, + duration: instant ? 0 : 250, + ease: 'Sine.easeOut' + }); + } + + hideStatus(instant?: boolean) { + if (!this.statusVisible) + return; + this.statusVisible = false; + this.scene.tweens.add({ + targets: this.statusContainer, + x: -106, + duration: instant ? 0 : 250, + ease: 'Sine.easeIn' + }); + } + getSelectedMove(): Move { if (this.cursor !== Page.MOVES) return null;