[QoL] [ui] Make tutorials darken background (#4283)
* [ui] add prompt icon to the message boxes that don't have it * [ui] add background overlay during tutorials * add missing doc * Improve documentation based on suggestions Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --------- Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com> Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
This commit is contained in:
parent
4605ed4c4f
commit
128df1b6d2
|
@ -70,6 +70,8 @@ class DefaultOverrides {
|
|||
[PokeballType.MASTER_BALL]: 0,
|
||||
},
|
||||
};
|
||||
/** Set to `true` to show all tutorials */
|
||||
readonly BYPASS_TUTORIAL_SKIP: boolean = false;
|
||||
|
||||
// ----------------
|
||||
// PLAYER OVERRIDES
|
||||
|
|
105
src/tutorial.ts
105
src/tutorial.ts
|
@ -1,7 +1,9 @@
|
|||
import BattleScene from "./battle-scene";
|
||||
import AwaitableUiHandler from "./ui/awaitable-ui-handler";
|
||||
import UiHandler from "./ui/ui-handler";
|
||||
import { Mode } from "./ui/ui";
|
||||
import i18next from "i18next";
|
||||
import Overrides from "#app/overrides";
|
||||
|
||||
export enum Tutorial {
|
||||
Intro = "INTRO",
|
||||
|
@ -39,7 +41,7 @@ const tutorialHandlers = {
|
|||
scene.ui.showText(i18next.t("tutorial:starterSelect"), null, () => scene.ui.showText("", null, () => resolve()), null, true);
|
||||
});
|
||||
},
|
||||
[Tutorial.Pokerus]: (scene: BattleScene) => {
|
||||
[Tutorial.Pokerus]: (scene: BattleScene) => {
|
||||
return new Promise<void>(resolve => {
|
||||
scene.ui.showText(i18next.t("tutorial:pokerus"), null, () => scene.ui.showText("", null, () => resolve()), null, true);
|
||||
});
|
||||
|
@ -63,26 +65,87 @@ const tutorialHandlers = {
|
|||
},
|
||||
};
|
||||
|
||||
export function handleTutorial(scene: BattleScene, tutorial: Tutorial): Promise<boolean> {
|
||||
return new Promise<boolean>(resolve => {
|
||||
if (!scene.enableTutorials) {
|
||||
return resolve(false);
|
||||
}
|
||||
/**
|
||||
* Run through the specified tutorial if it hasn't been seen before and mark it as seen once done
|
||||
* This will show a tutorial overlay if defined in the current {@linkcode AwaitableUiHandler}
|
||||
* The main menu will also get disabled while the tutorial is running
|
||||
* @param scene the current {@linkcode BattleScene}
|
||||
* @param tutorial the {@linkcode Tutorial} to play
|
||||
* @returns a promise with result `true` if the tutorial was run and finished, `false` otherwise
|
||||
*/
|
||||
export async function handleTutorial(scene: BattleScene, tutorial: Tutorial): Promise<boolean> {
|
||||
if (!scene.enableTutorials && !Overrides.BYPASS_TUTORIAL_SKIP) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (scene.gameData.getTutorialFlags()[tutorial]) {
|
||||
return resolve(false);
|
||||
}
|
||||
if (scene.gameData.getTutorialFlags()[tutorial] && !Overrides.BYPASS_TUTORIAL_SKIP) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const handler = scene.ui.getHandler();
|
||||
if (handler instanceof AwaitableUiHandler) {
|
||||
handler.tutorialActive = true;
|
||||
}
|
||||
tutorialHandlers[tutorial](scene).then(() => {
|
||||
scene.gameData.saveTutorialFlag(tutorial, true);
|
||||
if (handler instanceof AwaitableUiHandler) {
|
||||
handler.tutorialActive = false;
|
||||
}
|
||||
resolve(true);
|
||||
});
|
||||
});
|
||||
const handler = scene.ui.getHandler();
|
||||
const isMenuDisabled = scene.disableMenu;
|
||||
|
||||
// starting tutorial, disable menu
|
||||
scene.disableMenu = true;
|
||||
if (handler instanceof AwaitableUiHandler) {
|
||||
handler.tutorialActive = true;
|
||||
}
|
||||
|
||||
await showTutorialOverlay(scene, handler);
|
||||
await tutorialHandlers[tutorial](scene);
|
||||
await hideTutorialOverlay(scene, handler);
|
||||
|
||||
// tutorial finished and overlay gone, re-enable menu, save tutorial as seen
|
||||
scene.disableMenu = isMenuDisabled;
|
||||
scene.gameData.saveTutorialFlag(tutorial, true);
|
||||
if (handler instanceof AwaitableUiHandler) {
|
||||
handler.tutorialActive = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the tutorial overlay if there is one
|
||||
* @param scene the current BattleScene
|
||||
* @param handler the current UiHandler
|
||||
* @returns `true` once the overlay has finished appearing, or if there is no overlay
|
||||
*/
|
||||
async function showTutorialOverlay(scene: BattleScene, handler: UiHandler) {
|
||||
if (handler instanceof AwaitableUiHandler && handler.tutorialOverlay) {
|
||||
scene.tweens.add({
|
||||
targets: handler.tutorialOverlay,
|
||||
alpha: 0.5,
|
||||
duration: 750,
|
||||
ease: "Sine.easeOut",
|
||||
onComplete: () => {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the tutorial overlay if there is one
|
||||
* @param scene the current BattleScene
|
||||
* @param handler the current UiHandler
|
||||
* @returns `true` once the overlay has finished disappearing, or if there is no overlay
|
||||
*/
|
||||
async function hideTutorialOverlay(scene: BattleScene, handler: UiHandler) {
|
||||
if (handler instanceof AwaitableUiHandler && handler.tutorialOverlay) {
|
||||
scene.tweens.add({
|
||||
targets: handler.tutorialOverlay,
|
||||
alpha: 0,
|
||||
duration: 500,
|
||||
ease: "Sine.easeOut",
|
||||
onComplete: () => {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ export default abstract class AwaitableUiHandler extends UiHandler {
|
|||
protected awaitingActionInput: boolean;
|
||||
protected onActionInput: Function | null;
|
||||
public tutorialActive: boolean = false;
|
||||
public tutorialOverlay: Phaser.GameObjects.Rectangle;
|
||||
|
||||
constructor(scene: BattleScene, mode: Mode | null = null) {
|
||||
super(scene, mode);
|
||||
|
@ -24,4 +25,21 @@ export default abstract class AwaitableUiHandler extends UiHandler {
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a semi transparent overlay that will get shown during tutorials
|
||||
* @param container the container to add the overlay to
|
||||
*/
|
||||
initTutorialOverlay(container: Phaser.GameObjects.Container) {
|
||||
if (!this.tutorialOverlay) {
|
||||
this.tutorialOverlay = new Phaser.GameObjects.Rectangle(this.scene, -1, -1, this.scene.scaledCanvas.width, this.scene.scaledCanvas.height, 0x070707);
|
||||
this.tutorialOverlay.setName("tutorial-overlay");
|
||||
this.tutorialOverlay.setOrigin(0, 0);
|
||||
this.tutorialOverlay.setAlpha(0);
|
||||
}
|
||||
|
||||
if (container) {
|
||||
container.add(this.tutorialOverlay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,12 +83,7 @@ export default class BattleMessageUiHandler extends MessageUiHandler {
|
|||
this.nameBoxContainer.add(this.nameText);
|
||||
messageContainer.add(this.nameBoxContainer);
|
||||
|
||||
const prompt = this.scene.add.sprite(0, 0, "prompt");
|
||||
prompt.setVisible(false);
|
||||
prompt.setOrigin(0, 0);
|
||||
messageContainer.add(prompt);
|
||||
|
||||
this.prompt = prompt;
|
||||
this.initPromptSprite(messageContainer);
|
||||
|
||||
const levelUpStatsContainer = this.scene.add.container(0, 0);
|
||||
levelUpStatsContainer.setVisible(false);
|
||||
|
|
|
@ -287,7 +287,6 @@ export default class EggGachaUiHandler extends MessageUiHandler {
|
|||
this.eggGachaContainer.add(this.eggGachaSummaryContainer);
|
||||
|
||||
const gachaMessageBoxContainer = this.scene.add.container(0, 148);
|
||||
this.eggGachaContainer.add(gachaMessageBoxContainer);
|
||||
|
||||
const gachaMessageBox = addWindow(this.scene, 0, 0, 320, 32);
|
||||
gachaMessageBox.setOrigin(0, 0);
|
||||
|
@ -301,8 +300,11 @@ export default class EggGachaUiHandler extends MessageUiHandler {
|
|||
|
||||
this.message = gachaMessageText;
|
||||
|
||||
this.initTutorialOverlay(this.eggGachaContainer);
|
||||
this.eggGachaContainer.add(gachaMessageBoxContainer);
|
||||
|
||||
this.initPromptSprite(gachaMessageBoxContainer);
|
||||
|
||||
this.setCursor(0);
|
||||
}
|
||||
|
||||
|
|
|
@ -45,12 +45,7 @@ export default class EvolutionSceneHandler extends MessageUiHandler {
|
|||
|
||||
this.message = message;
|
||||
|
||||
const prompt = this.scene.add.sprite(0, 0, "prompt");
|
||||
prompt.setVisible(false);
|
||||
prompt.setOrigin(0, 0);
|
||||
this.messageContainer.add(prompt);
|
||||
|
||||
this.prompt = prompt;
|
||||
this.initPromptSprite(this.messageContainer);
|
||||
}
|
||||
|
||||
show(_args: any[]): boolean {
|
||||
|
|
|
@ -157,6 +157,9 @@ export default class MenuUiHandler extends MessageUiHandler {
|
|||
menuMessageText.setOrigin(0, 0);
|
||||
this.menuMessageBoxContainer.add(menuMessageText);
|
||||
|
||||
this.initTutorialOverlay(this.menuContainer);
|
||||
this.initPromptSprite(this.menuMessageBoxContainer);
|
||||
|
||||
this.message = menuMessageText;
|
||||
|
||||
// By default we use the general purpose message window
|
||||
|
@ -433,6 +436,9 @@ export default class MenuUiHandler extends MessageUiHandler {
|
|||
|
||||
this.scene.playSound("ui/menu_open");
|
||||
|
||||
// Make sure the tutorial overlay sits above everything, but below the message box
|
||||
this.menuContainer.bringToTop(this.tutorialOverlay);
|
||||
this.menuContainer.bringToTop(this.menuMessageBoxContainer);
|
||||
handleTutorial(this.scene, Tutorial.Menu);
|
||||
|
||||
this.bgmBar.toggleBgmBar(true);
|
||||
|
|
|
@ -17,6 +17,23 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler {
|
|||
this.pendingPrompt = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the sprite to be displayed at the end of messages with prompts
|
||||
* @param container the container to add the sprite to
|
||||
*/
|
||||
initPromptSprite(container: Phaser.GameObjects.Container) {
|
||||
if (!this.prompt) {
|
||||
const promptSprite = this.scene.add.sprite(0, 0, "prompt");
|
||||
promptSprite.setVisible(false);
|
||||
promptSprite.setOrigin(0, 0);
|
||||
this.prompt = promptSprite;
|
||||
}
|
||||
|
||||
if (container) {
|
||||
container.add(this.prompt);
|
||||
}
|
||||
}
|
||||
|
||||
showText(text: string, delay?: integer | null, callback?: Function | null, callbackDelay?: integer | null, prompt?: boolean | null, promptDelay?: integer | null) {
|
||||
this.showTextInternal(text, delay, callback, callbackDelay, prompt, promptDelay);
|
||||
}
|
||||
|
@ -180,7 +197,7 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler {
|
|||
const lastLineWidth = lastLineTest.displayWidth;
|
||||
lastLineTest.destroy();
|
||||
if (this.prompt) {
|
||||
this.prompt.setPosition(lastLineWidth + 2, (textLinesCount - 1) * 18 + 2);
|
||||
this.prompt.setPosition(this.message.x + lastLineWidth + 2, this.message.y + (textLinesCount - 1) * 18 + 2);
|
||||
this.prompt.play("prompt");
|
||||
}
|
||||
this.pendingPrompt = false;
|
||||
|
|
|
@ -894,6 +894,9 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||
this.message.setOrigin(0, 0);
|
||||
this.starterSelectMessageBoxContainer.add(this.message);
|
||||
|
||||
// arrow icon for the message box
|
||||
this.initPromptSprite(this.starterSelectMessageBoxContainer);
|
||||
|
||||
this.statsContainer = new StatsContainer(this.scene, 6, 16);
|
||||
|
||||
this.scene.add.existing(this.statsContainer);
|
||||
|
@ -911,7 +914,11 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||
y: this.scene.game.canvas.height / 6 - MoveInfoOverlay.getHeight(overlayScale) - 29,
|
||||
});
|
||||
this.starterSelectContainer.add(this.moveInfoOverlay);
|
||||
|
||||
// Filter bar sits above everything, except the tutorial overlay and message box
|
||||
this.starterSelectContainer.bringToTop(this.filterBarContainer);
|
||||
this.initTutorialOverlay(this.starterSelectContainer);
|
||||
this.starterSelectContainer.bringToTop(this.starterSelectMessageBoxContainer);
|
||||
|
||||
this.scene.eventTarget.addEventListener(BattleSceneEventType.CANDY_UPGRADE_NOTIFICATION_CHANGED, (e) => this.onCandyUpgradeDisplayChanged(e));
|
||||
|
||||
|
|
Loading…
Reference in New Issue