mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-08-21 14:59:26 +02:00
Refactor test-dialogue and menu text handling to prevent conflicts; update tryAdjustText function with latest implementation and documentation
This commit is contained in:
parent
e6d88354fb
commit
a28522b0b0
@ -68,11 +68,14 @@ export default class CommandUiHandler extends UiHandler {
|
||||
messageHandler.movesWindowContainer.setVisible(false);
|
||||
|
||||
const messageMaxWidth = this.scene.game.canvas.width - messageHandler.commandWindow.getBounds().width - messageHandler.message.getBounds().x;
|
||||
messageHandler.message.setWordWrapWidth(messageMaxWidth);
|
||||
const commandMessage = i18next.t("commandUiHandler:actionMessage", { pokemonName: getPokemonNameWithAffix(commandPhase.getPokemon()) });
|
||||
messageHandler.adjustText(commandMessage, messageHandler.message, messageMaxWidth, { ignoreTextBalance: "all" });
|
||||
messageHandler.tryAdjustText(commandMessage, {
|
||||
maxWidth: messageMaxWidth,
|
||||
guideHeight: messageHandler.bg
|
||||
});
|
||||
|
||||
messageHandler.showText(commandMessage, 0);
|
||||
|
||||
if (this.getCursor() === Command.POKEMON) {
|
||||
this.setCursor(Command.FIGHT);
|
||||
} else {
|
||||
|
@ -35,12 +35,9 @@ const donateUrl = "https://github.com/sponsors/patapancakes";
|
||||
|
||||
export default class MenuUiHandler extends MessageUiHandler {
|
||||
private readonly textPadding = 8;
|
||||
private readonly defaultMessageBoxWidth = 220;
|
||||
private readonly defaultWordWrapWidth = 1224;
|
||||
|
||||
private menuContainer: Phaser.GameObjects.Container;
|
||||
private menuMessageBoxContainer: Phaser.GameObjects.Container;
|
||||
private messageBoxBg: Phaser.GameObjects.NineSlice;
|
||||
private menuOverlay: Phaser.GameObjects.Rectangle;
|
||||
|
||||
private menuBg: Phaser.GameObjects.NineSlice;
|
||||
@ -57,6 +54,7 @@ export default class MenuUiHandler extends MessageUiHandler {
|
||||
// Windows for the default message box and the message box for testing dialogue
|
||||
private menuMessageBox: Phaser.GameObjects.NineSlice;
|
||||
private dialogueMessageBox: Phaser.GameObjects.NineSlice;
|
||||
private isTestDialog: boolean;
|
||||
|
||||
protected scale: number = 0.1666666667;
|
||||
|
||||
@ -144,14 +142,10 @@ export default class MenuUiHandler extends MessageUiHandler {
|
||||
this.menuMessageBoxContainer.setVisible(false);
|
||||
|
||||
// Window for general messages
|
||||
this.menuMessageBox = addWindow(this.scene, 0, 0, this.defaultMessageBoxWidth, 48);
|
||||
this.menuMessageBox = addWindow(this.scene, 0, 0, (this.scene.game.canvas.width / 6) - this.menuBg.width - 1, 48);
|
||||
this.menuMessageBox.setOrigin(0, 0);
|
||||
this.menuMessageBoxContainer.add(this.menuMessageBox);
|
||||
|
||||
const menuMessageBox = addWindow(this.scene, 0, -0, (this.scene.game.canvas.width / 6) - this.menuBg.width - 1, 48);
|
||||
menuMessageBox.setOrigin(0, 0);
|
||||
this.menuMessageBoxContainer.add(menuMessageBox);
|
||||
this.messageBoxBg = menuMessageBox;
|
||||
// Full-width window used for testing dialog messages in debug mode
|
||||
this.dialogueMessageBox = addWindow(this.scene, -this.textPadding, 0, this.scene.game.canvas.width / 6 + this.textPadding * 2, 49, false, false, 0, 0, WindowVariant.THIN);
|
||||
this.dialogueMessageBox.setOrigin(0, 0);
|
||||
@ -160,7 +154,6 @@ export default class MenuUiHandler extends MessageUiHandler {
|
||||
const menuMessageText = addTextObject(this.scene, this.textPadding, this.textPadding, "", TextStyle.WINDOW, { maxLines: 2 });
|
||||
menuMessageText.setName("menu-message");
|
||||
menuMessageText.setOrigin(0, 0);
|
||||
menuMessageText.setWordWrapWidth(menuMessageBox.getBounds().width * 0.95);
|
||||
|
||||
this.menuMessageBoxContainer.add(menuMessageText);
|
||||
|
||||
@ -665,13 +658,12 @@ export default class MenuUiHandler extends MessageUiHandler {
|
||||
this.message.setWordWrapWidth(isDialogMode ? this.scene.ui.getMessageHandler().wordWrapWidth : this.defaultWordWrapWidth);
|
||||
this.message.setX(isDialogMode ? this.textPadding + 1 : this.textPadding);
|
||||
this.message.setY(isDialogMode ? this.textPadding + 0.4 : this.textPadding);
|
||||
this.isTestDialog = isDialogMode;
|
||||
}
|
||||
|
||||
showText(text: string, delay?: number, callback?: Function, callbackDelay?: number, prompt?: boolean, promptDelay?: number): void {
|
||||
this.menuMessageBoxContainer.setVisible(!!text);
|
||||
this.adjustText(text, this.message, this.messageBoxBg.getBounds().width, {
|
||||
ignoreTextBalance: "all"
|
||||
});
|
||||
this.tryAdjustText(text, { ignoreLanguages: this.isTestDialog ? "all" : null });
|
||||
|
||||
super.showText(text, delay, callback, callbackDelay, prompt, promptDelay);
|
||||
}
|
||||
@ -696,6 +688,7 @@ export default class MenuUiHandler extends MessageUiHandler {
|
||||
this.menuContainer.setVisible(false);
|
||||
this.bgmBar.toggleBgmBar(false);
|
||||
this.eraseCursor();
|
||||
this.showText("");
|
||||
}
|
||||
|
||||
eraseCursor() {
|
||||
@ -704,6 +697,10 @@ export default class MenuUiHandler extends MessageUiHandler {
|
||||
}
|
||||
this.cursorObj = null;
|
||||
}
|
||||
|
||||
get defaultWordWrapWidth() {
|
||||
return this.menuMessageBox.getBounds().width / this.scale;
|
||||
}
|
||||
}
|
||||
|
||||
interface ConditionalMenu {
|
||||
|
@ -4,11 +4,6 @@ import { Mode } from "./ui";
|
||||
import * as Utils from "../utils";
|
||||
import i18next from "i18next";
|
||||
|
||||
type argsAjustText = {
|
||||
ignoreTextBalance?:Array<string>|"all";
|
||||
ignoreLanguages?:Array<string>;
|
||||
};
|
||||
|
||||
export default abstract class MessageUiHandler extends AwaitableUiHandler {
|
||||
protected textTimer: Phaser.Time.TimerEvent | null;
|
||||
protected textCallbackTimer: Phaser.Time.TimerEvent | null;
|
||||
@ -16,6 +11,7 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler {
|
||||
|
||||
public message: Phaser.GameObjects.Text;
|
||||
public prompt: Phaser.GameObjects.Sprite;
|
||||
protected promptOut: { x: number, y: number } | null;
|
||||
|
||||
constructor(scene: BattleScene, mode: Mode | null = null) {
|
||||
super(scene, mode);
|
||||
@ -195,15 +191,18 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler {
|
||||
}
|
||||
|
||||
showPrompt(callback?: Function | null, callbackDelay?: integer | null) {
|
||||
const wrappedTextLines = this.message.runWordWrap(this.message.text).split(/\n/g);
|
||||
const textLinesCount = wrappedTextLines.length;
|
||||
const lastTextLine = wrappedTextLines[wrappedTextLines.length - 1];
|
||||
const lastLineTest = this.scene.add.text(0, 0, lastTextLine, { font: "96px emerald" });
|
||||
lastLineTest.setScale(this.message.scale);
|
||||
const lastLineWidth = lastLineTest.displayWidth;
|
||||
lastLineTest.destroy();
|
||||
if (this.prompt) {
|
||||
this.prompt.setPosition(this.message.x + lastLineWidth + 2, this.message.y + (textLinesCount - 1) * 18 + 2);
|
||||
this.prompt.setScale(parseInt(this.message.style.fontSize.toString()) / 100 + 0.04);
|
||||
const textSize = Phaser.GameObjects.GetTextSize(this.message, this.message.style.getTextMetrics(), this.message.getWrappedText(this.message.text));
|
||||
const lastLineWidth = textSize.lineWidths[textSize.lineWidths.length - 1];
|
||||
let x = lastLineWidth * this.message.scale + this.message.x + 2;
|
||||
let y = this.message.y + (textSize.height * this.message.scale / (20 - textSize.lines) - 0.5) * 18;
|
||||
if (this.promptOut) {
|
||||
x = this.promptOut.x * this.message.scale - (this.message.x * 2) + 2;
|
||||
y = (this.promptOut.y - (this.message.y * 2)) * this.message.scale / 1.3;
|
||||
this.promptOut = null;
|
||||
}
|
||||
this.prompt.setPosition(x, y);
|
||||
this.prompt.play("prompt");
|
||||
}
|
||||
this.pendingPrompt = false;
|
||||
@ -239,49 +238,156 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Use before showText(), ex:
|
||||
* ``` ts
|
||||
* // Handler extends MessageUiHandler.ts...
|
||||
* const ui = this.getUi();
|
||||
* this.tryAdjustText(text, opts); // Or ui.getMessageHandler().tryAdjustText()...
|
||||
* ui.showText(...);
|
||||
*
|
||||
* // Or in showText():
|
||||
* showText(...) {
|
||||
* this.tryAdjustText(text, opts);
|
||||
* super.showText(...);
|
||||
* }
|
||||
* ```
|
||||
* @param text
|
||||
* @param textObject
|
||||
* @param maxWidth
|
||||
* @param opts options additional
|
||||
* @argument ignoreLanguages ignore adjust for some language.
|
||||
* @argument ignoreBalanceText ignore Text Balance for some languages or for all.
|
||||
* @argument padding default 0.
|
||||
* @argument ignoreLanguages ignore adjust for some languages or for all.
|
||||
* @argument maxWidth default this.message.style.wordWrapWidth or this.message.parentContainer.getBounds().width.
|
||||
* @argument guideHeight default this.message.parentContainer, If the container has many elements or `this.message` does not have a clear guide, use the parent container as a reference guide by default.
|
||||
*/
|
||||
|
||||
adjustText(text: string, textObject: Phaser.GameObjects.Text, maxWidth: number, opts: argsAjustText = {}): void {
|
||||
tryAdjustText(text: string, opts?: argsAdjustText): void {
|
||||
const currentLanguage = i18next.resolvedLanguage!;
|
||||
if (opts.ignoreLanguages && opts.ignoreLanguages[0] && !opts.ignoreLanguages.some(localKey => localKey === currentLanguage)) {
|
||||
if (opts?.ignoreLanguages && opts.ignoreLanguages[0] && (opts.ignoreLanguages === "all" || !opts.ignoreLanguages.some(localKey => localKey === currentLanguage))) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fontSizeToNumber = (FS: number | string): number => {
|
||||
return parseInt(FS.toString().replace("px", ""));
|
||||
const referenceGuide = opts?.guideHeight ?? this.message.parentContainer;
|
||||
|
||||
// If any style changes were made in previous tryAdjustText() calls, revert to the original data.
|
||||
// [Note] Be aware that if dynamic styles are being applied to the same this.message from another source for attributes such as fontSize, maxLines, wordWrap, this may cause issues.
|
||||
if (this.message.getData("originalMaxLines")) {
|
||||
this.message.style.setMaxLines(this.message.getData("originalMaxLines"));
|
||||
this.message.data.remove("originalMaxLines");
|
||||
}
|
||||
const maxWidth = this.message.getData("originalMaxWidth") ?? Math.floor(opts?.maxWidth ?? this.message.style.wordWrapWidth ?? referenceGuide.getBounds().width);
|
||||
this.message.setData("originalMaxWidth", this.message.getData("originalMaxWidth") ?? maxWidth);
|
||||
this.message.setWordWrapWidth(maxWidth);
|
||||
|
||||
const fontSize = this.message.getData("originalFontSize") ?? parseInt(this.message.style.fontSize.toString());
|
||||
this.message.setData("originalFontSize", fontSize);
|
||||
this.message.setFontSize(fontSize);
|
||||
|
||||
const scale = this.message.scale;
|
||||
|
||||
const textWrapped = () => this.message.getWrappedText(text);
|
||||
const textSize = () => Phaser.GameObjects.GetTextSize(this.message, this.message.style.getTextMetrics(), textWrapped());
|
||||
|
||||
const xToPaddingLeft = ((this.message.x ** 2) - this.message.x / 2); // Approximate equivalent to what the padding.left should be
|
||||
const paddingX = (xToPaddingLeft * 1.5) / (this.message.x * scale) - this.message.x || 0; // If it's too large, scale it down to maintain aspect ratio with x
|
||||
|
||||
const yToPaddingY = ((this.message.y ** 2) - this.message.y / 2) * 2; // Approximate equivalent to what the padding.y (padding.top + padding.bottom) should be
|
||||
const paddingY = (yToPaddingY * 1.5) / (this.message.y * scale) - (this.message.y * 2) || 0; // If it's too large, scale it down to maintain aspect ratio with y
|
||||
|
||||
// FontSize adjust
|
||||
let fontDecrement = fontSize;
|
||||
const adjustFontSize = (condition: () => boolean = () => (textWrapped().length > this.message.style.maxLines)): void => {
|
||||
if (Utils.isNullOrUndefined(text) || text === "") {
|
||||
return;
|
||||
}
|
||||
const minFontSize = 40;
|
||||
while (condition() && fontDecrement > minFontSize) {
|
||||
fontDecrement--;
|
||||
this.message.setFontSize(fontDecrement);
|
||||
|
||||
// If the text has been shrunk so much that another line can fit in the text, add it
|
||||
// This is to preserve the maximum possible font size.
|
||||
if ((textSize().height + textSize().lineHeight + paddingY) < referenceGuide.getBounds().height) {
|
||||
const linesNeed = Math.round((textSize().height + paddingY) / textSize().lineHeight);
|
||||
if (linesNeed > this.message.style.maxLines) {
|
||||
if (!this.message.getData("originalMaxLines")) {
|
||||
// We save the current value as it will be modified, so we can return it in the next showText()
|
||||
this.message.setData("originalMaxLines", this.message.style.maxLines);
|
||||
}
|
||||
this.message.style.setMaxLines(linesNeed);
|
||||
}
|
||||
}
|
||||
|
||||
if (textSize().height + paddingY - 15 > referenceGuide.getBounds().height) {
|
||||
if (!this.message.getData("originalMaxLines")) {
|
||||
// We save the current value as it will be modified, so we can return it in the next showText()
|
||||
this.message.setData("originalMaxLines", this.message.style.maxLines);
|
||||
}
|
||||
this.message.style.setMaxLines(--this.message.style.maxLines);
|
||||
adjustFontSize();
|
||||
break;
|
||||
}
|
||||
|
||||
// checking the maximum width
|
||||
this.message.setWordWrapWidth(maxWidth);
|
||||
adjustWordWrap();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// If fontSize was modified before, revert to original
|
||||
const fontSize = textObject.getData("originalFontSize") ?? fontSizeToNumber(textObject.style.fontSize);
|
||||
textObject.setData("originalFontSize", textObject.getData("originalFontSize") ?? fontSize);
|
||||
textObject.setFontSize(fontSize);
|
||||
|
||||
const textWrapped = () => textObject.getWrappedText(text);
|
||||
const textSize = () => Phaser.GameObjects.GetTextSize(textObject, textObject.style.getTextMetrics(), textWrapped());
|
||||
const balanceText = typeof opts.ignoreTextBalance === "string" ? opts.ignoreTextBalance === "all" : (opts.ignoreTextBalance && opts.ignoreTextBalance[0] && opts.ignoreTextBalance.some(localKey => localKey === currentLanguage));
|
||||
|
||||
// Text Balance
|
||||
if (!balanceText && textWrapped()[1] && textWrapped().length <= textObject.style.maxLines && textWrapped()[0].length * 0.25 > textWrapped()[1].length) {
|
||||
textObject.setWordWrapWidth(maxWidth * 0.65);
|
||||
// wordWrapWidth adjust
|
||||
let widthDecrement = maxWidth;
|
||||
const adjustWordWrap = (): void => {
|
||||
if (Utils.isNullOrUndefined(text) || text === "") {
|
||||
return;
|
||||
}
|
||||
|
||||
// Text ajust
|
||||
if (textWrapped().length > textObject.style.maxLines || (textSize().width + textObject.x) > maxWidth) {
|
||||
|
||||
let fontDecrement = fontSize;
|
||||
while (textWrapped().length > textObject.style.maxLines || (textSize().width + textObject.x) > maxWidth) {
|
||||
fontDecrement -= 1;
|
||||
textObject.setFontSize(fontDecrement);
|
||||
if (textSize().width + paddingX + Math.round((widthDecrement - textSize().width) / 1.15) >= maxWidth) {
|
||||
while (textSize().width + paddingX + Math.round((widthDecrement - textSize().width) / 1.15) >= maxWidth && widthDecrement > 100) {
|
||||
widthDecrement--;
|
||||
this.message.setWordWrapWidth(widthDecrement);
|
||||
}
|
||||
textObject.setFontSize(fontDecrement - textObject.x / 2);
|
||||
|
||||
}
|
||||
|
||||
// If after trying to adjust the wordWrapWidth it remains the same, it means that..
|
||||
//.. there is no space between words, so the fontSize is adjusted to fit.
|
||||
if (textSize().width + (paddingX * 1.2) >= maxWidth) {
|
||||
this.message.setWordWrapWidth(maxWidth);
|
||||
adjustFontSize(() => textSize().width + (paddingX * 1.2) >= maxWidth);
|
||||
}
|
||||
};
|
||||
adjustWordWrap();
|
||||
|
||||
if (textWrapped().length > this.message.style.maxLines) {
|
||||
|
||||
adjustFontSize();
|
||||
|
||||
adjustWordWrap(); // after adjustFontSize, respect "padding"
|
||||
|
||||
// Some line breaks (\n) may also prevent the text from being displayed..
|
||||
//.. so if the text is still too large due to the previous issue, adjust it.
|
||||
if (textSize().height + paddingY - 15 > referenceGuide.getBounds().height) {
|
||||
adjustFontSize(() => (textSize().height + paddingY - 15 > referenceGuide.getBounds().height));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const lastLine = textSize().lineWidths[textSize().lineWidths.length - 1];
|
||||
|
||||
// If when adjusting the text the prompt goes outside..
|
||||
// .. save the data to put the prompt in the bottom right corner
|
||||
if (!this.promptOut && lastLine + (paddingX * 1.5) >= maxWidth) {
|
||||
this.promptOut = {
|
||||
x: maxWidth,
|
||||
y: referenceGuide.getBounds().height
|
||||
};
|
||||
} else {
|
||||
this.promptOut = null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
interface argsAdjustText {
|
||||
ignoreLanguages?: Array<string> | "all" | null;
|
||||
maxWidth?: number;
|
||||
guideHeight?: Phaser.GameObjects.Container | Phaser.GameObjects.Sprite | Phaser.GameObjects.NineSlice
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user