diff --git a/src/battle-scene.ts b/src/battle-scene.ts index a8e8d238ab6..e28dedccbd5 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -114,6 +114,8 @@ export default class BattleScene extends SceneBase { public experimentalSprites: boolean = false; public moveAnimations: boolean = true; public expGainsSpeed: integer = 0; + public skipSeenDialogues: boolean = false; + /** * Defines the experience gain display mode. * diff --git a/src/phases.ts b/src/phases.ts index b896c6f9484..67f33b5e031 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -959,14 +959,15 @@ export class EncounterPhase extends BattlePhase { if (!encounterMessages?.length) { doSummon(); } else { + let message: string; + this.scene.executeWithSeedOffset(() => message = Utils.randSeedItem(encounterMessages), this.scene.currentBattle.waveIndex); + const showDialogueAndSummon = () => { - let message: string; - this.scene.executeWithSeedOffset(() => message = Utils.randSeedItem(encounterMessages), this.scene.currentBattle.waveIndex); this.scene.ui.showDialogue(message, trainer.getName(TrainerSlot.NONE,true), null, () => { this.scene.charSprite.hide().then(() => this.scene.hideFieldOverlay(250).then(() => doSummon())); }); }; - if (this.scene.currentBattle.trainer.config.hasCharSprite) { + if (this.scene.currentBattle.trainer.config.hasCharSprite && !this.scene.ui.shouldSkipDialogue(message)) { this.scene.showFieldOverlay(500).then(() => this.scene.charSprite.showCharacter(trainer.getKey(), getCharVariantFromDialogue(encounterMessages[0])).then(() => showDialogueAndSummon())); } else { showDialogueAndSummon(); @@ -3789,21 +3790,18 @@ export class TrainerVictoryPhase extends BattlePhase { this.scene.ui.showText(i18next.t("battle:trainerDefeated", { trainerName: this.scene.currentBattle.trainer.getName(TrainerSlot.NONE, true) }), null, () => { const victoryMessages = this.scene.currentBattle.trainer.getVictoryMessages(); - const showMessage = () => { - let message: string; - this.scene.executeWithSeedOffset(() => message = Utils.randSeedItem(victoryMessages), this.scene.currentBattle.waveIndex); - const messagePages = message.split(/\$/g).map(m => m.trim()); + let message: string; + this.scene.executeWithSeedOffset(() => message = Utils.randSeedItem(victoryMessages), this.scene.currentBattle.waveIndex); - for (let p = messagePages.length - 1; p >= 0; p--) { - const originalFunc = showMessageOrEnd; - showMessageOrEnd = () => this.scene.ui.showDialogue(messagePages[p], this.scene.currentBattle.trainer.getName(), null, originalFunc); - } + const showMessage = () => { + const originalFunc = showMessageOrEnd; + showMessageOrEnd = () => this.scene.ui.showDialogue(message, this.scene.currentBattle.trainer.getName(), null, originalFunc); showMessageOrEnd(); }; let showMessageOrEnd = () => this.end(); if (victoryMessages?.length) { - if (this.scene.currentBattle.trainer.config.hasCharSprite) { + if (this.scene.currentBattle.trainer.config.hasCharSprite && !this.scene.ui.shouldSkipDialogue(message)) { const originalFunc = showMessageOrEnd; showMessageOrEnd = () => this.scene.charSprite.hide().then(() => this.scene.hideFieldOverlay(250).then(() => originalFunc())); this.scene.showFieldOverlay(500).then(() => this.scene.charSprite.showCharacter(this.scene.currentBattle.trainer.getKey(), getCharVariantFromDialogue(victoryMessages[0])).then(() => showMessage())); @@ -4014,19 +4012,27 @@ export class GameOverPhase extends BattlePhase { }; if (this.victory && this.scene.gameMode.isClassic) { - this.scene.ui.fadeIn(500).then(() => { - this.scene.charSprite.showCharacter(`rival_${this.scene.gameData.gender === PlayerGender.FEMALE ? "m" : "f"}`, getCharVariantFromDialogue(miscDialogue.ending[this.scene.gameData.gender === PlayerGender.FEMALE ? 0 : 1])).then(() => { - this.scene.ui.showDialogue(miscDialogue.ending[this.scene.gameData.gender === PlayerGender.FEMALE ? 0 : 1], this.scene.gameData.gender === PlayerGender.FEMALE ? trainerConfigs[TrainerType.RIVAL].name : trainerConfigs[TrainerType.RIVAL].nameFemale, null, () => { - this.scene.ui.fadeOut(500).then(() => { - this.scene.charSprite.hide().then(() => { - const endCardPhase = new EndCardPhase(this.scene); - this.scene.unshiftPhase(endCardPhase); - clear(endCardPhase); + const message = miscDialogue.ending[this.scene.gameData.gender === PlayerGender.FEMALE ? 0 : 1]; + + if (!this.scene.ui.shouldSkipDialogue(message)) { + this.scene.ui.fadeIn(500).then(() => { + this.scene.charSprite.showCharacter(`rival_${this.scene.gameData.gender === PlayerGender.FEMALE ? "m" : "f"}`, getCharVariantFromDialogue(miscDialogue.ending[this.scene.gameData.gender === PlayerGender.FEMALE ? 0 : 1])).then(() => { + this.scene.ui.showDialogue(message, this.scene.gameData.gender === PlayerGender.FEMALE ? trainerConfigs[TrainerType.RIVAL].name : trainerConfigs[TrainerType.RIVAL].nameFemale, null, () => { + this.scene.ui.fadeOut(500).then(() => { + this.scene.charSprite.hide().then(() => { + const endCardPhase = new EndCardPhase(this.scene); + this.scene.unshiftPhase(endCardPhase); + clear(endCardPhase); + }); }); }); }); }); - }); + } else { + const endCardPhase = new EndCardPhase(this.scene); + this.scene.unshiftPhase(endCardPhase); + clear(endCardPhase); + } } else { clear(); } diff --git a/src/system/game-data.ts b/src/system/game-data.ts index dc73634e70b..35b6bc1435c 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -40,7 +40,8 @@ export enum GameDataType { SYSTEM, SESSION, SETTINGS, - TUTORIALS + TUTORIALS, + SEEN_DIALOGUES } export enum PlayerGender { @@ -68,6 +69,8 @@ export function getDataTypeKey(dataType: GameDataType, slotId: integer = 0): str return "settings"; case GameDataType.TUTORIALS: return "tutorials"; + case GameDataType.SEEN_DIALOGUES: + return "seenDialogues"; } } @@ -201,6 +204,10 @@ export interface TutorialFlags { [key: string]: boolean } +export interface SeenDialogues { + [key: string]: boolean; +} + const systemShortKeys = { seenAttr: "$sa", caughtAttr: "$ca", @@ -716,9 +723,10 @@ export class GameData { } public saveTutorialFlag(tutorial: Tutorial, flag: boolean): boolean { + const key = getDataTypeKey(GameDataType.TUTORIALS); let tutorials: object = {}; - if (localStorage.hasOwnProperty("tutorials")) { - tutorials = JSON.parse(localStorage.getItem("tutorials")); + if (localStorage.hasOwnProperty(key)) { + tutorials = JSON.parse(localStorage.getItem(key)); } Object.keys(Tutorial).map(t => t as Tutorial).forEach(t => { @@ -730,20 +738,21 @@ export class GameData { } }); - localStorage.setItem("tutorials", JSON.stringify(tutorials)); + localStorage.setItem(key, JSON.stringify(tutorials)); return true; } public getTutorialFlags(): TutorialFlags { + const key = getDataTypeKey(GameDataType.TUTORIALS); const ret: TutorialFlags = {}; Object.values(Tutorial).map(tutorial => tutorial as Tutorial).forEach(tutorial => ret[Tutorial[tutorial]] = false); - if (!localStorage.hasOwnProperty("tutorials")) { + if (!localStorage.hasOwnProperty(key)) { return ret; } - const tutorials = JSON.parse(localStorage.getItem("tutorials")); + const tutorials = JSON.parse(localStorage.getItem(key)); for (const tutorial of Object.keys(tutorials)) { ret[tutorial] = tutorials[tutorial]; @@ -752,6 +761,34 @@ export class GameData { return ret; } + public saveSeenDialogue(dialogue: string): boolean { + const key = getDataTypeKey(GameDataType.SEEN_DIALOGUES); + const dialogues: object = this.getSeenDialogues(); + + dialogues[dialogue] = true; + localStorage.setItem(key, JSON.stringify(dialogues)); + console.log("Dialogue saved as seen:", dialogue); + + return true; + } + + public getSeenDialogues(): SeenDialogues { + const key = getDataTypeKey(GameDataType.SEEN_DIALOGUES); + const ret: SeenDialogues = {}; + + if (!localStorage.hasOwnProperty(key)) { + return ret; + } + + const dialogues = JSON.parse(localStorage.getItem(key)); + + for (const dialogue of Object.keys(dialogues)) { + ret[dialogue] = dialogues[dialogue]; + } + + return ret; + } + private getSessionSaveData(scene: BattleScene): SessionSaveData { return { seed: scene.seed, diff --git a/src/system/settings.ts b/src/system/settings.ts index f2a3548da4a..5f526376998 100644 --- a/src/system/settings.ts +++ b/src/system/settings.ts @@ -19,6 +19,7 @@ export enum Setting { Window_Type = "WINDOW_TYPE", Tutorials = "TUTORIALS", Enable_Retries = "ENABLE_RETRIES", + Skip_Seen_Dialogues = "SKIP_SEEN_DIALOGUES", Candy_Upgrade_Notification = "CANDY_UPGRADE_NOTIFICATION", Candy_Upgrade_Display = "CANDY_UPGRADE_DISPLAY", Money_Format = "MONEY_FORMAT", @@ -54,6 +55,7 @@ export const settingOptions: SettingOptions = { [Setting.Window_Type]: new Array(5).fill(null).map((_, i) => (i + 1).toString()), [Setting.Tutorials]: ["Off", "On"], [Setting.Enable_Retries]: ["Off", "On"], + [Setting.Skip_Seen_Dialogues]: ["Off", "On"], [Setting.Candy_Upgrade_Notification]: ["Off", "Passives Only", "On"], [Setting.Candy_Upgrade_Display]: ["Icon", "Animation"], [Setting.Money_Format]: ["Normal", "Abbreviated"], @@ -81,6 +83,7 @@ export const settingDefaults: SettingDefaults = { [Setting.Window_Type]: 0, [Setting.Tutorials]: 1, [Setting.Enable_Retries]: 0, + [Setting.Skip_Seen_Dialogues]: 0, [Setting.Candy_Upgrade_Notification]: 0, [Setting.Candy_Upgrade_Display]: 0, [Setting.Money_Format]: 0, @@ -198,6 +201,9 @@ export function setSetting(scene: BattleScene, setting: Setting, value: integer) case Setting.Vibration: scene.enableVibration = settingOptions[setting][value] !== "Disabled" && hasTouchscreen(); break; + case Setting.Skip_Seen_Dialogues: + scene.skipSeenDialogues = settingOptions[setting][value] === "On"; + break; case Setting.Language: if (value) { if (scene.ui) { diff --git a/src/ui/ui.ts b/src/ui/ui.ts index 7092f16a57c..90cba657fbf 100644 --- a/src/ui/ui.ts +++ b/src/ui/ui.ts @@ -259,15 +259,26 @@ export default class UI extends Phaser.GameObjects.Container { } // Add the prefix to the text const localizationKey = playerGenderPrefix + text; + // Get localized dialogue (if available) + let hasi18n = false; if (i18next.exists(localizationKey as ParseKeys) ) { - - text = i18next.t(localizationKey as ParseKeys); + hasi18n = true; + + // Skip dialogue if the player has enabled the option and the dialogue has been already seen + if ((this.scene as BattleScene).skipSeenDialogues && (this.scene as BattleScene).gameData.getSeenDialogues()[localizationKey] === true) { + console.log(`Dialogue ${localizationKey} skipped`); + callback(); + return; + } } + let showMessageAndCallback = () => { + hasi18n && (this.scene as BattleScene).gameData.saveSeenDialogue(localizationKey); + callback(); + }; if (text.indexOf("$") > -1) { const messagePages = text.split(/\$/g).map(m => m.trim()); - let showMessageAndCallback = () => callback(); for (let p = messagePages.length - 1; p >= 0; p--) { const originalFunc = showMessageAndCallback; showMessageAndCallback = () => this.showDialogue(messagePages[p], name, null, originalFunc); @@ -276,13 +287,29 @@ export default class UI extends Phaser.GameObjects.Container { } else { const handler = this.getHandler(); if (handler instanceof MessageUiHandler) { - (handler as MessageUiHandler).showDialogue(text, name, delay, callback, callbackDelay, true, promptDelay); + (handler as MessageUiHandler).showDialogue(text, name, delay, showMessageAndCallback, callbackDelay, true, promptDelay); } else { - this.getMessageHandler().showDialogue(text, name, delay, callback, callbackDelay, true, promptDelay); + this.getMessageHandler().showDialogue(text, name, delay, showMessageAndCallback, callbackDelay, true, promptDelay); } } } + shouldSkipDialogue(text): boolean { + let playerGenderPrefix = "PGM"; + if ((this.scene as BattleScene).gameData.gender === PlayerGender.FEMALE) { + playerGenderPrefix = "PGF"; + } + + const key = playerGenderPrefix + text; + + if (i18next.exists(key as ParseKeys) ) { + if ((this.scene as BattleScene).skipSeenDialogues && (this.scene as BattleScene).gameData.getSeenDialogues()[key] === true) { + return true; + } + } + return false; + } + showTooltip(title: string, content: string, overlap?: boolean): void { this.tooltipContainer.setVisible(true); this.tooltipTitle.setText(title || "");