diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 2fc0d562c2d..3ed3a86dc09 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -22,6 +22,9 @@ import AbilityBar from './ui/ability-bar'; import { Abilities, BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, IncrementMovePriorityAbAttr, applyAbAttrs, initAbilities } from './data/ability'; import Battle, { BattleType, FixedBattleConfig, fixedBattles } from './battle'; import { GameMode, GameModes, gameModes } from './game-mode'; + +import GameModeModifiers from './system/game-mode-modifiers'; + import FieldSpritePipeline from './pipelines/field-sprite'; import SpritePipeline from './pipelines/sprite'; import PartyExpBar from './ui/party-exp-bar'; @@ -149,6 +152,9 @@ export default class BattleScene extends SceneBase { public arenaNextEnemy: ArenaBase; public arena: Arena; public gameMode: GameMode; + + public gameModeModifiers: GameModeModifiers; + public score: integer; public lockModifierTiers: boolean; public trainer: Phaser.GameObjects.Sprite; diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index 83b722ddf7c..0cb8be244e6 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -959,14 +959,14 @@ const modifierPool: ModifierPool = { }, 18), new WeightedModifierType(modifierTypes.REVIVE, (party: Pokemon[]) => { const faintedPartyMemberCount = Math.min(party.filter(p => p.isFainted()).length, 3); - return faintedPartyMemberCount * 9; + return party[0].scene.gameModeModifiers.isPermaDeath? 0 : faintedPartyMemberCount * 9; }, 3), new WeightedModifierType(modifierTypes.MAX_REVIVE, (party: Pokemon[]) => { const faintedPartyMemberCount = Math.min(party.filter(p => p.isFainted()).length, 3); - return faintedPartyMemberCount * 3; + return party[0].scene.gameModeModifiers.isPermaDeath ? 0 : faintedPartyMemberCount * 3; }, 9), new WeightedModifierType(modifierTypes.SACRED_ASH, (party: Pokemon[]) => { - return party.filter(p => p.isFainted()).length >= Math.ceil(party.length / 2) ? 1 : 0; + return party[0].scene.gameModeModifiers.isPermaDeath ? 0 : party.filter(p => p.isFainted()).length >= Math.ceil(party.length / 2) ? 1 : 0; }, 1), new WeightedModifierType(modifierTypes.HYPER_POTION, (party: Pokemon[]) => { const thresholdPartyMemberCount = Math.min(party.filter(p => p.getInverseHp() >= 100 || p.getHpRatio() <= 0.625).length, 3); @@ -1286,7 +1286,7 @@ export function getPlayerModifierTypeOptions(count: integer, party: PlayerPokemo return options; } -export function getPlayerShopModifierTypeOptionsForWave(waveIndex: integer, baseCost: integer): ModifierTypeOption[] { +export function getPlayerShopModifierTypeOptionsForWave(waveIndex: integer, baseCost: integer, permadeath:boolean = false): ModifierTypeOption[] { if (!(waveIndex % 10)) return []; @@ -1294,7 +1294,7 @@ export function getPlayerShopModifierTypeOptionsForWave(waveIndex: integer, base [ new ModifierTypeOption(modifierTypes.POTION(), 0, baseCost * 0.2), new ModifierTypeOption(modifierTypes.ETHER(), 0, baseCost * 0.4), - new ModifierTypeOption(modifierTypes.REVIVE(), 0, baseCost * 2) + ... !permadeath ? [new ModifierTypeOption(modifierTypes.REVIVE(), 0, baseCost * 2)] : [], ], [ new ModifierTypeOption(modifierTypes.SUPER_POTION(), 0, baseCost * 0.45), @@ -1306,7 +1306,7 @@ export function getPlayerShopModifierTypeOptionsForWave(waveIndex: integer, base ], [ new ModifierTypeOption(modifierTypes.HYPER_POTION(), 0, baseCost * 0.8), - new ModifierTypeOption(modifierTypes.MAX_REVIVE(), 0, baseCost * 2.75) + ... !permadeath ? [new ModifierTypeOption(modifierTypes.MAX_REVIVE(), 0, baseCost * 2.75)] : [], ], [ new ModifierTypeOption(modifierTypes.MAX_POTION(), 0, baseCost * 1.5), @@ -1316,7 +1316,7 @@ export function getPlayerShopModifierTypeOptionsForWave(waveIndex: integer, base new ModifierTypeOption(modifierTypes.FULL_RESTORE(), 0, baseCost * 2.25) ], [ - new ModifierTypeOption(modifierTypes.SACRED_ASH(), 0, baseCost * 12) + ... !permadeath ? [new ModifierTypeOption(modifierTypes.SACRED_ASH(), 0, baseCost * 12)] : [] ] ]; return options.slice(0, Math.ceil(Math.max(waveIndex + 10, 0) / 30)).flat(); diff --git a/src/phases.ts b/src/phases.ts index b8414e6b77f..006e12375fb 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -3293,6 +3293,9 @@ export class GameOverPhase extends BattlePhase { if (this.scene.gameMode.isClassic) { firstClear = this.scene.validateAchv(achvs.CLASSIC_VICTORY); this.scene.gameData.gameStats.sessionsWon++; + if (this.scene.gameModeModifiers.isPermaDeath){ + this.scene.validateAchv(achvs.PERMADEATH_VICTORY); + } } else if (this.scene.gameMode.isDaily && success[1]) this.scene.gameData.gameStats.dailyRunSessionsWon++; } @@ -4065,7 +4068,7 @@ export class SelectModifierPhase extends BattlePhase { modifierType = typeOptions[cursor].type; break; default: - const shopOptions = getPlayerShopModifierTypeOptionsForWave(this.scene.currentBattle.waveIndex, this.scene.getWaveMoneyAmount(1)); + const shopOptions = getPlayerShopModifierTypeOptionsForWave(this.scene.currentBattle.waveIndex, this.scene.getWaveMoneyAmount(1),this.scene.gameModeModifiers.isPermaDeath); const shopOption = shopOptions[rowCursor > 2 || shopOptions.length <= SHOP_OPTIONS_ROW_LIMIT ? cursor : cursor + SHOP_OPTIONS_ROW_LIMIT]; modifierType = shopOption.type; cost = shopOption.cost; @@ -4228,11 +4231,12 @@ export class PartyHealPhase extends BattlePhase { this.scene.fadeOutBgm(1000, false); this.scene.ui.fadeOut(1000).then(() => { for (let pokemon of this.scene.getParty()) { - pokemon.hp = pokemon.getMaxHp(); - pokemon.resetStatus(); - for (let move of pokemon.moveset) + if (!this.scene.gameModeModifiers.isPermaDeath && pokemon.hp == 0){ + pokemon.hp = pokemon.getMaxHp(); + pokemon.resetStatus(); + for (let move of pokemon.moveset) move.ppUsed = 0; - pokemon.updateInfo(true); + pokemon.updateInfo(true); } const healSong = this.scene.playSoundWithoutBgm('heal'); this.scene.time.delayedCall(Utils.fixedInt(healSong.totalDuration * 1000), () => { @@ -4241,7 +4245,7 @@ export class PartyHealPhase extends BattlePhase { this.scene.playBgm(); this.scene.ui.fadeIn(500).then(() => this.end()); }); - }); + }}); } } diff --git a/src/system/achv.ts b/src/system/achv.ts index 2307347db3a..8fb02d26b3e 100644 --- a/src/system/achv.ts +++ b/src/system/achv.ts @@ -139,7 +139,8 @@ export const achvs = { HATCH_SHINY: new Achv('Shiny Egg', 'Hatch a shiny Pokémon from an egg', 'golden_mystic_ticket', 100).setSecret(), HIDDEN_ABILITY: new Achv('Hidden Potential', 'Catch a Pokémon with a hidden ability', 'ability_charm', 75), PERFECT_IVS: new Achv('Certificate of Authenticity', 'Get perfect IVs on a Pokémon', 'blunder_policy', 100), - CLASSIC_VICTORY: new Achv('Undefeated', 'Beat the game in classic mode', 'relic_crown', 150) + CLASSIC_VICTORY: new Achv('Undefeated', 'Beat the game in classic mode', 'relic_crown', 150), + PERMADEATH_VICTORY: new Achv('Mr. Fuji', 'Beat the game using the permadeath modifier', 'silph_scope', 50) }; { diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 248a3705122..70165b460e2 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -9,6 +9,7 @@ import PersistentModifierData from "./modifier-data"; import ArenaData from "./arena-data"; import { Unlockables } from "./unlockables"; import { GameModes, gameModes } from "../game-mode"; +import GameModeModifiers from "./game-mode-modifiers"; import { BattleType } from "../battle"; import TrainerData from "./trainer-data"; import { trainerConfigs } from "../data/trainer-config"; @@ -80,6 +81,7 @@ export interface SessionSaveData { seed: string; playTime: integer; gameMode: GameModes; + gameModeModiefiers: GameModeModifiers party: PokemonData[]; enemyParty: PokemonData[]; modifiers: PersistentModifierData[]; @@ -468,6 +470,7 @@ export class GameData { seed: scene.seed, playTime: scene.sessionPlayTime, gameMode: scene.gameMode.modeId, + gameModeModiefiers: scene.gameModeModifiers, party: scene.getParty().map(p => new PokemonData(p)), enemyParty: scene.getEnemyParty().map(p => new PokemonData(p)), modifiers: scene.findModifiers(() => true).map(m => new PersistentModifierData(m, true)), @@ -554,6 +557,7 @@ export class GameData { console.debug(sessionData); scene.gameMode = gameModes[sessionData.gameMode || GameModes.CLASSIC]; + scene.gameModeModifiers = sessionData.gameModeModiefiers || new GameModeModifiers({}) scene.setSeed(sessionData.seed || scene.game.config.seed[0]); scene.resetSeed(); diff --git a/src/system/game-mode-modifiers.ts b/src/system/game-mode-modifiers.ts new file mode 100644 index 00000000000..ccddc45f985 --- /dev/null +++ b/src/system/game-mode-modifiers.ts @@ -0,0 +1,17 @@ +import { Type } from "../data/type"; + +interface GameModeModifiersConfig { + isPermaDeath?: boolean; + isMonotype?: boolean; + monoTypeType?: Type; +} + +export default class GameModeModifiers { + public isPermaDeath: boolean; + public isMonotype: boolean; + public monoTypeType: Type; + + constructor(config: GameModeModifiersConfig) { + Object.assign(this, config); + } +} \ No newline at end of file diff --git a/src/ui/modifier-select-ui-handler.ts b/src/ui/modifier-select-ui-handler.ts index 6b4cf30b0df..dd96dea9fbb 100644 --- a/src/ui/modifier-select-ui-handler.ts +++ b/src/ui/modifier-select-ui-handler.ts @@ -107,7 +107,7 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler { const typeOptions = args[1] as ModifierTypeOption[]; const shopTypeOptions = !this.scene.gameMode.hasNoShop - ? getPlayerShopModifierTypeOptionsForWave(this.scene.currentBattle.waveIndex, this.scene.getWaveMoneyAmount(1)) + ? getPlayerShopModifierTypeOptionsForWave(this.scene.currentBattle.waveIndex, this.scene.getWaveMoneyAmount(1),this.scene.gameModeModifiers.isPermaDeath) : []; const optionsYOffset = shopTypeOptions.length >= SHOP_OPTIONS_ROW_LIMIT ? -8 : -24; diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 546c9ca5b29..e68a544c68a 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -24,6 +24,7 @@ import { Type } from "../data/type"; import { Moves } from "../data/enums/moves"; import { speciesEggMoves } from "../data/egg-moves"; import { TitlePhase } from "../phases"; +import GameModeModifiers from "../system/game-mode-modifiers"; export type StarterSelectCallback = (starters: Starter[]) => void; @@ -112,6 +113,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { private starterSelectCallback: StarterSelectCallback; private gameMode: GameModes; + private gameModeModifierConfig = {isPermaDeath: false} constructor(scene: BattleScene) { super(scene, Mode.STARTER_SELECT); @@ -1264,7 +1266,54 @@ export default class StarterSelectUiHandler extends MessageUiHandler { return true; } - tryStart(manualTrigger: boolean = false): boolean { + showRunMods(manualTrigger: boolean = false, permadeath: boolean = false): boolean { + const ui = this.getUi(); + let options = []; + options.push( + { + label: permadeath ? 'Permadeath (enabled)' : 'Permadeath (disabled)', + handler: () => { + ui.setMode(Mode.STARTER_SELECT); + this.gameModeModifierConfig.isPermaDeath = !permadeath + this.showRunMods(manualTrigger,!permadeath) + } + }) + options.push( + { + label: 'Done', + handler: () => { + this.scene.gameModeModifiers = new GameModeModifiers(this.gameModeModifierConfig) + ui.setMode(Mode.STARTER_SELECT); + this.tryStartRun(manualTrigger) + } + }) + ui.showText('Select desired run modifiers', null, () => { + ui.setModeWithoutClear(Mode.OPTION_SELECT, { + options: options, + yOffset: 20 + }); + }); + return true + } + + + tryStart(manualTrigger: boolean = false): boolean{ + const cancel = () => { + this.clearText(); + ui.setMode(Mode.STARTER_SELECT); + this.tryStartRun(manualTrigger) + }; + const ui = this.getUi(); + ui.showText('Enable custom game modifiers?', null, () => { + ui.setModeWithoutClear(Mode.CONFIRM, () => { + ui.setMode(Mode.STARTER_SELECT); + this.showRunMods(manualTrigger) + }, cancel, null, null, 19); + }); + return true + } + + tryStartRun(manualTrigger: boolean = false): boolean { if (!this.starterGens.length) return false; @@ -1278,7 +1327,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { }; ui.showText('Begin with these Pokémon?', null, () => { - ui.setModeWithoutClear(Mode.CONFIRM, () => { + ui.setModeWithoutClear(Mode.CONFIRM, () => { const startRun = (gameMode: GameModes) => { this.scene.gameMode = gameModes[gameMode]; this.scene.money = this.scene.gameMode.getStartingMoney();