[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,
|
[PokeballType.MASTER_BALL]: 0,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
/** Set to `true` to show all tutorials */
|
||||||
|
readonly BYPASS_TUTORIAL_SKIP: boolean = false;
|
||||||
|
|
||||||
// ----------------
|
// ----------------
|
||||||
// PLAYER OVERRIDES
|
// PLAYER OVERRIDES
|
||||||
|
|
105
src/tutorial.ts
105
src/tutorial.ts
|
@ -1,7 +1,9 @@
|
||||||
import BattleScene from "./battle-scene";
|
import BattleScene from "./battle-scene";
|
||||||
import AwaitableUiHandler from "./ui/awaitable-ui-handler";
|
import AwaitableUiHandler from "./ui/awaitable-ui-handler";
|
||||||
|
import UiHandler from "./ui/ui-handler";
|
||||||
import { Mode } from "./ui/ui";
|
import { Mode } from "./ui/ui";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
import Overrides from "#app/overrides";
|
||||||
|
|
||||||
export enum Tutorial {
|
export enum Tutorial {
|
||||||
Intro = "INTRO",
|
Intro = "INTRO",
|
||||||
|
@ -39,7 +41,7 @@ const tutorialHandlers = {
|
||||||
scene.ui.showText(i18next.t("tutorial:starterSelect"), null, () => scene.ui.showText("", null, () => resolve()), null, true);
|
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 => {
|
return new Promise<void>(resolve => {
|
||||||
scene.ui.showText(i18next.t("tutorial:pokerus"), null, () => scene.ui.showText("", null, () => resolve()), null, true);
|
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 => {
|
* Run through the specified tutorial if it hasn't been seen before and mark it as seen once done
|
||||||
if (!scene.enableTutorials) {
|
* This will show a tutorial overlay if defined in the current {@linkcode AwaitableUiHandler}
|
||||||
return resolve(false);
|
* 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]) {
|
if (scene.gameData.getTutorialFlags()[tutorial] && !Overrides.BYPASS_TUTORIAL_SKIP) {
|
||||||
return resolve(false);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const handler = scene.ui.getHandler();
|
const handler = scene.ui.getHandler();
|
||||||
if (handler instanceof AwaitableUiHandler) {
|
const isMenuDisabled = scene.disableMenu;
|
||||||
handler.tutorialActive = true;
|
|
||||||
}
|
// starting tutorial, disable menu
|
||||||
tutorialHandlers[tutorial](scene).then(() => {
|
scene.disableMenu = true;
|
||||||
scene.gameData.saveTutorialFlag(tutorial, true);
|
if (handler instanceof AwaitableUiHandler) {
|
||||||
if (handler instanceof AwaitableUiHandler) {
|
handler.tutorialActive = true;
|
||||||
handler.tutorialActive = false;
|
}
|
||||||
}
|
|
||||||
resolve(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 awaitingActionInput: boolean;
|
||||||
protected onActionInput: Function | null;
|
protected onActionInput: Function | null;
|
||||||
public tutorialActive: boolean = false;
|
public tutorialActive: boolean = false;
|
||||||
|
public tutorialOverlay: Phaser.GameObjects.Rectangle;
|
||||||
|
|
||||||
constructor(scene: BattleScene, mode: Mode | null = null) {
|
constructor(scene: BattleScene, mode: Mode | null = null) {
|
||||||
super(scene, mode);
|
super(scene, mode);
|
||||||
|
@ -24,4 +25,21 @@ export default abstract class AwaitableUiHandler extends UiHandler {
|
||||||
|
|
||||||
return false;
|
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);
|
this.nameBoxContainer.add(this.nameText);
|
||||||
messageContainer.add(this.nameBoxContainer);
|
messageContainer.add(this.nameBoxContainer);
|
||||||
|
|
||||||
const prompt = this.scene.add.sprite(0, 0, "prompt");
|
this.initPromptSprite(messageContainer);
|
||||||
prompt.setVisible(false);
|
|
||||||
prompt.setOrigin(0, 0);
|
|
||||||
messageContainer.add(prompt);
|
|
||||||
|
|
||||||
this.prompt = prompt;
|
|
||||||
|
|
||||||
const levelUpStatsContainer = this.scene.add.container(0, 0);
|
const levelUpStatsContainer = this.scene.add.container(0, 0);
|
||||||
levelUpStatsContainer.setVisible(false);
|
levelUpStatsContainer.setVisible(false);
|
||||||
|
|
|
@ -287,7 +287,6 @@ export default class EggGachaUiHandler extends MessageUiHandler {
|
||||||
this.eggGachaContainer.add(this.eggGachaSummaryContainer);
|
this.eggGachaContainer.add(this.eggGachaSummaryContainer);
|
||||||
|
|
||||||
const gachaMessageBoxContainer = this.scene.add.container(0, 148);
|
const gachaMessageBoxContainer = this.scene.add.container(0, 148);
|
||||||
this.eggGachaContainer.add(gachaMessageBoxContainer);
|
|
||||||
|
|
||||||
const gachaMessageBox = addWindow(this.scene, 0, 0, 320, 32);
|
const gachaMessageBox = addWindow(this.scene, 0, 0, 320, 32);
|
||||||
gachaMessageBox.setOrigin(0, 0);
|
gachaMessageBox.setOrigin(0, 0);
|
||||||
|
@ -301,8 +300,11 @@ export default class EggGachaUiHandler extends MessageUiHandler {
|
||||||
|
|
||||||
this.message = gachaMessageText;
|
this.message = gachaMessageText;
|
||||||
|
|
||||||
|
this.initTutorialOverlay(this.eggGachaContainer);
|
||||||
this.eggGachaContainer.add(gachaMessageBoxContainer);
|
this.eggGachaContainer.add(gachaMessageBoxContainer);
|
||||||
|
|
||||||
|
this.initPromptSprite(gachaMessageBoxContainer);
|
||||||
|
|
||||||
this.setCursor(0);
|
this.setCursor(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,12 +45,7 @@ export default class EvolutionSceneHandler extends MessageUiHandler {
|
||||||
|
|
||||||
this.message = message;
|
this.message = message;
|
||||||
|
|
||||||
const prompt = this.scene.add.sprite(0, 0, "prompt");
|
this.initPromptSprite(this.messageContainer);
|
||||||
prompt.setVisible(false);
|
|
||||||
prompt.setOrigin(0, 0);
|
|
||||||
this.messageContainer.add(prompt);
|
|
||||||
|
|
||||||
this.prompt = prompt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
show(_args: any[]): boolean {
|
show(_args: any[]): boolean {
|
||||||
|
|
|
@ -157,6 +157,9 @@ export default class MenuUiHandler extends MessageUiHandler {
|
||||||
menuMessageText.setOrigin(0, 0);
|
menuMessageText.setOrigin(0, 0);
|
||||||
this.menuMessageBoxContainer.add(menuMessageText);
|
this.menuMessageBoxContainer.add(menuMessageText);
|
||||||
|
|
||||||
|
this.initTutorialOverlay(this.menuContainer);
|
||||||
|
this.initPromptSprite(this.menuMessageBoxContainer);
|
||||||
|
|
||||||
this.message = menuMessageText;
|
this.message = menuMessageText;
|
||||||
|
|
||||||
// By default we use the general purpose message window
|
// 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");
|
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);
|
handleTutorial(this.scene, Tutorial.Menu);
|
||||||
|
|
||||||
this.bgmBar.toggleBgmBar(true);
|
this.bgmBar.toggleBgmBar(true);
|
||||||
|
|
|
@ -17,6 +17,23 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler {
|
||||||
this.pendingPrompt = false;
|
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) {
|
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);
|
this.showTextInternal(text, delay, callback, callbackDelay, prompt, promptDelay);
|
||||||
}
|
}
|
||||||
|
@ -180,7 +197,7 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler {
|
||||||
const lastLineWidth = lastLineTest.displayWidth;
|
const lastLineWidth = lastLineTest.displayWidth;
|
||||||
lastLineTest.destroy();
|
lastLineTest.destroy();
|
||||||
if (this.prompt) {
|
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.prompt.play("prompt");
|
||||||
}
|
}
|
||||||
this.pendingPrompt = false;
|
this.pendingPrompt = false;
|
||||||
|
|
|
@ -894,6 +894,9 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||||
this.message.setOrigin(0, 0);
|
this.message.setOrigin(0, 0);
|
||||||
this.starterSelectMessageBoxContainer.add(this.message);
|
this.starterSelectMessageBoxContainer.add(this.message);
|
||||||
|
|
||||||
|
// arrow icon for the message box
|
||||||
|
this.initPromptSprite(this.starterSelectMessageBoxContainer);
|
||||||
|
|
||||||
this.statsContainer = new StatsContainer(this.scene, 6, 16);
|
this.statsContainer = new StatsContainer(this.scene, 6, 16);
|
||||||
|
|
||||||
this.scene.add.existing(this.statsContainer);
|
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,
|
y: this.scene.game.canvas.height / 6 - MoveInfoOverlay.getHeight(overlayScale) - 29,
|
||||||
});
|
});
|
||||||
this.starterSelectContainer.add(this.moveInfoOverlay);
|
this.starterSelectContainer.add(this.moveInfoOverlay);
|
||||||
|
|
||||||
|
// Filter bar sits above everything, except the tutorial overlay and message box
|
||||||
this.starterSelectContainer.bringToTop(this.filterBarContainer);
|
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));
|
this.scene.eventTarget.addEventListener(BattleSceneEventType.CANDY_UPGRADE_NOTIFICATION_CHANGED, (e) => this.onCandyUpgradeDisplayChanged(e));
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue