diff --git a/public/images/events/pride-update.png b/public/images/events/pride-update.png index face6df1790..de496f49796 100644 Binary files a/public/images/events/pride-update.png and b/public/images/events/pride-update.png differ diff --git a/public/images/finn-sprite.png b/public/images/finn-sprite.png new file mode 100644 index 00000000000..31c6e1ff4db Binary files /dev/null and b/public/images/finn-sprite.png differ diff --git a/public/images/ivy-sprite.png b/public/images/ivy-sprite.png new file mode 100644 index 00000000000..599406b2437 Binary files /dev/null and b/public/images/ivy-sprite.png differ diff --git a/public/images/window_speech.png b/public/images/window_speech.png new file mode 100644 index 00000000000..d97c7cb80ac Binary files /dev/null and b/public/images/window_speech.png differ diff --git a/src/loading-scene.ts b/src/loading-scene.ts index cce131f271f..0b2993479ac 100644 --- a/src/loading-scene.ts +++ b/src/loading-scene.ts @@ -40,6 +40,8 @@ export class LoadingScene extends SceneBase { this.loadImage("loading_bg", "arenas"); this.loadImage("logo", ""); this.loadImage("pride-update", "events"); + this.loadImage("ivy-sprite", ""); + this.loadImage("finn-sprite", ""); // Load menu images this.loadAtlas("bg", "ui"); @@ -53,6 +55,7 @@ export class LoadingScene extends SceneBase { this.loadImage(`window_${w}${getWindowVariantSuffix(wv)}`, "ui/windows"); } } + this.loadImage("window_speech", ""); this.loadImage("discord", "ui"); this.loadImage("reddit", "ui"); this.loadImage("github", "ui"); diff --git a/src/locales/en/settings.ts b/src/locales/en/settings.ts index f68a649269f..81690d4540c 100644 --- a/src/locales/en/settings.ts +++ b/src/locales/en/settings.ts @@ -32,8 +32,8 @@ export const settings: SimpleTranslationEntries = { "language": "Language", "change": "Change", "uiTheme": "UI Theme", - "default": "Default", - "legacy": "Legacy", + "default": "Gen V", + "legacy": "Gen III", "windowType": "Window Type", "moneyFormat": "Money Format", "damageNumbers": "Damage Numbers", diff --git a/src/phases.ts b/src/phases.ts index ac2e14db939..b8eef87b2bd 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -199,15 +199,6 @@ export class TitlePhase extends Phase { showOptions(): OptionSelectConfig { const options: OptionSelectItem[] = []; - // if (loggedInUser.lastSessionSlot > -1) { - // options.push({ - // label: i18next.t("menu:continue"), - // handler: () => { - // this.loadSaveSlot(this.lastSessionData ? -1 : loggedInUser.lastSessionSlot); - // return true; - // } - // }); - // } options.push({ label: i18next.t("menu:loadGame"), handler: () => this.loadGameHandler(), @@ -232,9 +223,9 @@ export class TitlePhase extends Phase { return { options: options, noCancel: true, - xOffset: 220, - yOffset: 13, - noBg: true, + xOffset: 216, + yOffset: 1, + noBg: true }; } @@ -389,7 +380,7 @@ export class TitlePhase extends Phase { } else { this.scene.playBgm(); } - + this.scene.ui.getMessageHandler().bg.setVisible(true); this.scene.pushPhase(new EncounterPhase(this.scene, this.loaded)); if (this.loaded) { diff --git a/src/timed-event-manager.ts b/src/timed-event-manager.ts index 1d02726b0ff..3e2ce0e2761 100644 --- a/src/timed-event-manager.ts +++ b/src/timed-event-manager.ts @@ -77,13 +77,14 @@ export class TimedEventDisplay extends Phaser.GameObjects.Container { } setup() { - this.banner = new Phaser.GameObjects.Image(this.scene, 0, 0, this.event.bannerFilename); + this.banner = new Phaser.GameObjects.Image(this.scene, 3, -1, this.event.bannerFilename); this.banner.setName("img-event-banner"); this.banner.setOrigin(0, 0); - this.banner.setScale(0.05); + this.banner.setScale(0.10); + this.bannerShadow = new Phaser.GameObjects.Rectangle( this.scene, - this.banner.x - 2, + this.banner.x + 2, this.banner.y + 2, this.banner.width * this.banner.scaleX, this.banner.height * this.banner.scaleY, @@ -92,15 +93,16 @@ export class TimedEventDisplay extends Phaser.GameObjects.Container { this.bannerShadow.setName("rect-event-banner-shadow"); this.bannerShadow.setAlpha(0.5); this.bannerShadow.setOrigin(0,0); + this.eventTimerText = addTextObject( this.scene, - this.banner.x + 3, - this.banner.y - 10, + this.banner.x, + this.banner.y + 49, this.timeToGo(this.event.endDate), - TextStyle.BATTLE_INFO + TextStyle.SUMMARY_ALT, + { fontSize: 72 } ); this.eventTimerText.setName("text-event-timer"); - this.eventTimerText.setScale(0.15); this.eventTimerText.setOrigin(0,0); this.add([this.eventTimerText, this.bannerShadow, this.banner]); diff --git a/src/ui/abstact-option-select-ui-handler.ts b/src/ui/abstact-option-select-ui-handler.ts index b978d0ba879..8f797214dae 100644 --- a/src/ui/abstact-option-select-ui-handler.ts +++ b/src/ui/abstact-option-select-ui-handler.ts @@ -16,6 +16,7 @@ export interface OptionSelectConfig { supportHover?: boolean; noBg?: boolean; textStyle?: TextStyle; + extraStyleOptions?: Phaser.Types.GameObjects.Text.TextStyle; } export interface OptionSelectItem { @@ -81,12 +82,18 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler { Phaser.Actions.Call(this.optionSelectText, (text) => text.destroy(), this); } const optionText = this.config?.options.length > this.config?.maxOptions ? this.getOptionsWithScroll() : options; + const optionTextStyle = { + maxLines: options.length, lineSpacing: 12 + }; + if (this.config.extraStyleOptions) { + Object.assign(optionTextStyle, this.config.extraStyleOptions); + } this.optionSelectText = []; this.optionSelectText = optionText.map(o => { const ret = addTextObject(this.scene, 0, 0, o.item ? ` ${o.label}` : o.label, ( this.config?.textStyle ?? TextStyle.WINDOW), - { maxLines: options.length, lineSpacing: 12 } + optionTextStyle ); ret.setName(`text-${o.label}`); return ret; diff --git a/src/ui/components/speech-bubble.ts b/src/ui/components/speech-bubble.ts new file mode 100644 index 00000000000..1aa15e0962a --- /dev/null +++ b/src/ui/components/speech-bubble.ts @@ -0,0 +1,52 @@ +export class SpeechBubble extends Phaser.GameObjects.Container { + private bubble: Phaser.GameObjects.NineSlice; + private trail: Phaser.GameObjects.Triangle; + private trailStrokes: Phaser.GameObjects.Line[]; + private text: Phaser.GameObjects.Text; + + constructor(scene, x, y, text?: Phaser.GameObjects.Text) { + super(scene, x, y); + + this.text = text; + this.text.setPadding(0, 0); + this.text.setPosition(-(this.text.width / 12), -(this.text.height / 12)); + + this.bubble = new Phaser.GameObjects.NineSlice( + scene, + 0, 0, + "window_speech", null, + (this.text.width / 6) + 18, 100); + this.bubble.setName("speech-bubble"); + + this.trail = new Phaser.GameObjects.Triangle( + scene, + 0, 0, + -51, 21, + -51, 11, + -39, 13, + 0xffffff, + 1 + ); + this.trail.setName("speech-bubble-trail"); + + this.trailStrokes = []; + this.trailStrokes.push( + new Phaser.GameObjects.Line( + scene, + 0, 0, + -57, 19, + -57, 13, + 0xa6a6a6, 1 + ), + new Phaser.GameObjects.Line( + scene, + 0, 0, + -52, 19, + -41, 13, + 0xa6a6a6, 1 + ), + ); + + this.add([this.bubble, this.trail, ...this.trailStrokes, this.text]); + } +} diff --git a/src/ui/settings/abstract-settings-ui-handler.ts b/src/ui/settings/abstract-settings-ui-handler.ts index 6763c8d3d85..acb456095bd 100644 --- a/src/ui/settings/abstract-settings-ui-handler.ts +++ b/src/ui/settings/abstract-settings-ui-handler.ts @@ -27,10 +27,12 @@ export default class AbstractSettingsUiHandler extends UiHandler { private settingLabels: Phaser.GameObjects.Text[]; private optionValueLabels: Phaser.GameObjects.Text[][]; + private reloadRequiredText: Phaser.GameObjects.Text; protected navigationIcons: InputsIcons; private cursorObj: Phaser.GameObjects.NineSlice; + private actionGroup: Phaser.GameObjects.Group; private reloadSettings: Array; private reloadRequired: boolean; @@ -39,6 +41,7 @@ export default class AbstractSettingsUiHandler extends UiHandler { protected title: string; protected settings: Array; protected localStorageKey: string; + protected actionButtons: boolean = true; constructor(scene: BattleScene, mode?: Mode) { super(scene, mode); @@ -53,6 +56,8 @@ export default class AbstractSettingsUiHandler extends UiHandler { setup() { const ui = this.getUi(); + this.actionGroup = new Phaser.GameObjects.Group(this.scene); + this.settingsContainer = this.scene.add.container(1, -(this.scene.game.canvas.height / 6) + 1); this.settingsContainer.setName(`settings-${this.title}`); this.settingsContainer.setInteractive(new Phaser.Geom.Rectangle(0, 0, this.scene.game.canvas.width / 6, this.scene.game.canvas.height / 6 - 20), Phaser.Geom.Rectangle.Contains); @@ -61,7 +66,12 @@ export default class AbstractSettingsUiHandler extends UiHandler { this.navigationContainer = new NavigationMenu(this.scene, 0, 0); - this.optionsBg = addWindow(this.scene, 0, this.navigationContainer.height, (this.scene.game.canvas.width / 6) - 2, (this.scene.game.canvas.height / 6) - 16 - this.navigationContainer.height - 2); + this.optionsBg = addWindow( + this.scene, 0, + this.navigationContainer.height - 1, + (this.scene.game.canvas.width / 6) - 2, + (this.scene.game.canvas.height / 6) - 16 - this.navigationContainer.height - 2 + ); this.optionsBg.setName("window-options-bg"); this.optionsBg.setOrigin(0, 0); @@ -69,23 +79,35 @@ export default class AbstractSettingsUiHandler extends UiHandler { actionsBg.setOrigin(0, 0); const iconAction = this.scene.add.sprite(0, 0, "keyboard"); + iconAction.setName("icon-action"); iconAction.setOrigin(0, -0.1); iconAction.setPositionRelative(actionsBg, this.navigationContainer.width - 32, 4); this.navigationIcons["BUTTON_ACTION"] = iconAction; const actionText = addTextObject(this.scene, 0, 0, i18next.t("settings:action"), TextStyle.SETTINGS_LABEL); + actionText.setName("text-action"); actionText.setOrigin(0, 0.15); actionText.setPositionRelative(iconAction, -actionText.width/6-2, 0); const iconCancel = this.scene.add.sprite(0, 0, "keyboard"); + iconCancel.setName("icon-cancel"); iconCancel.setOrigin(0, -0.1); iconCancel.setPositionRelative(actionsBg, this.navigationContainer.width - 100, 4); this.navigationIcons["BUTTON_CANCEL"] = iconCancel; const cancelText = addTextObject(this.scene, 0, 0, i18next.t("settings:back"), TextStyle.SETTINGS_LABEL); + cancelText.setName("text-cancel"); cancelText.setOrigin(0, 0.15); cancelText.setPositionRelative(iconCancel, -cancelText.width/6-2, 0); + this.reloadRequiredText = addTextObject( + this.scene, + 8, 159, + `* ${i18next.t("settings:requireReload")}`, + TextStyle.SETTINGS_LABEL + ); + this.reloadRequiredText.setName("reload-required"); + this.optionsContainer = this.scene.add.container(0, 0); this.settingLabels = []; @@ -97,7 +119,7 @@ export default class AbstractSettingsUiHandler extends UiHandler { .forEach((setting, s) => { let settingName = setting.label; if (setting?.requireReload) { - settingName += ` (${i18next.t("settings:requireReload")})`; + settingName += " *"; } this.settingLabels[s] = addTextObject(this.scene, 8, 28 + s * 16, settingName, TextStyle.SETTINGS_LABEL); @@ -138,6 +160,11 @@ export default class AbstractSettingsUiHandler extends UiHandler { this.settingsContainer.add(iconCancel); this.settingsContainer.add(actionText); this.settingsContainer.add(cancelText); + this.settingsContainer.add(this.reloadRequiredText); + + this.actionGroup.addMultiple([ + iconAction, iconCancel, actionText, cancelText + ]); ui.add(this.settingsContainer); @@ -185,6 +212,8 @@ export default class AbstractSettingsUiHandler extends UiHandler { this.settings.forEach((setting, s) => this.setOptionCursor(s, settings.hasOwnProperty(setting.key) ? settings[setting.key] : this.settings[s].default)); this.settingsContainer.setVisible(true); + Phaser.Actions.SetVisible(this.actionGroup.getChildren(), this.actionButtons); + this.reloadRequiredText.setVisible(!this.actionButtons); this.setCursor(0); this.getUi().moveTo(this.settingsContainer, this.getUi().length - 1); diff --git a/src/ui/settings/settings-display-ui-handler.ts b/src/ui/settings/settings-display-ui-handler.ts index f0e84f29e00..82dc4459721 100644 --- a/src/ui/settings/settings-display-ui-handler.ts +++ b/src/ui/settings/settings-display-ui-handler.ts @@ -3,6 +3,7 @@ import { Mode } from "../ui"; "#app/inputs-controller.js"; import AbstractSettingsUiHandler from "./abstract-settings-ui-handler"; import { Setting, SettingKeys, SettingType } from "#app/system/settings/settings"; +import TitleUiHandler from "#app/ui/title-ui-handler.js"; export default class SettingsDisplayUiHandler extends AbstractSettingsUiHandler { /** @@ -15,6 +16,7 @@ export default class SettingsDisplayUiHandler extends AbstractSettingsUiHandler super(scene, mode); this.title = "Display"; this.settings = Setting.filter(s => s.type === SettingType.DISPLAY); + this.actionButtons = false; /** * Update to current language from default value. @@ -90,4 +92,9 @@ export default class SettingsDisplayUiHandler extends AbstractSettingsUiHandler this.localStorageKey = "settings"; } + + clear() { + super.clear(); + (this.getUi().handlers[Mode.TITLE] as TitleUiHandler).update(); + } } diff --git a/src/ui/settings/settings-ui-handler.ts b/src/ui/settings/settings-ui-handler.ts index a6332be1343..f7241b06498 100644 --- a/src/ui/settings/settings-ui-handler.ts +++ b/src/ui/settings/settings-ui-handler.ts @@ -15,5 +15,6 @@ export default class SettingsUiHandler extends AbstractSettingsUiHandler { this.title = "General"; this.settings = Setting.filter(s => s.type === SettingType.GENERAL); this.localStorageKey = "settings"; + this.actionButtons = false; } } diff --git a/src/ui/text.ts b/src/ui/text.ts index e5a3407395e..430a90b4a7c 100644 --- a/src/ui/text.ts +++ b/src/ui/text.ts @@ -122,8 +122,8 @@ function getTextStyleOptions(style: TextStyle, uiTheme: UiTheme, extraStyleOptio case TextStyle.MONEY: case TextStyle.TOOLTIP_TITLE: styleOptions.fontSize = defaultFontSize - 24; - shadowXpos = 3.5; - shadowYpos = 3.5; + shadowXpos = 3; + shadowYpos = 3; break; case TextStyle.PARTY: case TextStyle.PARTY_RED: @@ -146,8 +146,11 @@ function getTextStyleOptions(style: TextStyle, uiTheme: UiTheme, extraStyleOptio if (extraStyleOptions) { if (extraStyleOptions.fontSize) { - const sizeRatio = parseInt(extraStyleOptions.fontSize.toString().slice(0, -2)) / parseInt(styleOptions.fontSize.toString().slice(0, -2)); + const currentSize = typeof styleOptions.fontSize === "string" ? Number(styleOptions.fontSize.slice(0, -2)) : styleOptions.fontSize; + const newSize = typeof extraStyleOptions.fontSize === "string" ? Number(extraStyleOptions.fontSize.slice(0, -2)) : extraStyleOptions.fontSize; + const sizeRatio = newSize / currentSize; shadowXpos *= sizeRatio; + shadowYpos *= sizeRatio; } styleOptions = Object.assign(styleOptions, extraStyleOptions); } diff --git a/src/ui/title-ui-handler.ts b/src/ui/title-ui-handler.ts index e4097da522f..ea80ee582fe 100644 --- a/src/ui/title-ui-handler.ts +++ b/src/ui/title-ui-handler.ts @@ -6,15 +6,24 @@ import { TextStyle, addTextObject } from "./text"; import { getSplashMessages } from "../data/splash-messages"; import i18next from "i18next"; import { TimedEventDisplay } from "#app/timed-event-manager.js"; +import { Color } from "#app/enums/color.js"; +import { PlayerGender } from "#app/enums/player-gender.js"; +import { SpeechBubble } from "#app/ui/components/speech-bubble.js"; export default class TitleUiHandler extends OptionSelectUiHandler { private titleContainer: Phaser.GameObjects.Container; private logo: Phaser.GameObjects.Image; private playerCountLabel: Phaser.GameObjects.Text; + private playerCountWidth: number; private splashMessage: string; private splashMessageText: Phaser.GameObjects.Text; private eventDisplay: TimedEventDisplay; private iconContainer: TitleIcons; + private menuOverlay: Phaser.GameObjects.Rectangle; + private rivalSprite: Phaser.GameObjects.Sprite; + private spriteShadow: Phaser.GameObjects.Ellipse; + private bubble: SpeechBubble; + private rivalText: Phaser.GameObjects.Text; private titleStatsTimer: NodeJS.Timeout; @@ -27,30 +36,93 @@ export default class TitleUiHandler extends OptionSelectUiHandler { const ui = this.getUi(); + const overlayColor = this.scene.uiTheme ? Color.OFF_WHITE : Color.DARK_GREY; + this.titleContainer = this.scene.add.container(0, -(this.scene.game.canvas.height / 6)); this.titleContainer.setName("title"); this.titleContainer.setAlpha(0); ui.add(this.titleContainer); - this.logo = this.scene.add.image((this.scene.scaledCanvas.width / 4) + 3, 8, "logo"); + this.logo = this.scene.add.image( + (this.scene.scaledCanvas.width / 4) + 3, + 8, + "logo" + ); this.logo.setName("logo"); this.logo.setOrigin(0.5, 0); this.titleContainer.add(this.logo); - this.iconContainer = new TitleIcons(this.scene, 15, this.scene.scaledCanvas.height - 15); + this.menuOverlay = this.scene.add.rectangle( + 8, 59, + 92, 71, + Number(`0x${overlayColor.slice(1)}`), + 0.5 + ); + this.menuOverlay.setName("title-options-bg"); + this.menuOverlay.setOrigin(0); + this.menuOverlay.setBlendMode(Phaser.BlendModes.OVERLAY); + this.titleContainer.add(this.menuOverlay); + + this.iconContainer = new TitleIcons( + this.scene, + 15, this.scene.scaledCanvas.height - 15 + ); this.iconContainer.setup(); this.titleContainer.add(this.iconContainer); + + this.rivalSprite = new Phaser.GameObjects.Sprite( + this.scene, + 176, 127, + `${this.scene.gameData.gender === PlayerGender.MALE ? "ivy" : "finn" }-sprite` + ); + this.rivalSprite.setName("rival"); + + this.spriteShadow = new Phaser.GameObjects.Ellipse( + this.scene, + this.rivalSprite.x, (this.rivalSprite.y + this.rivalSprite.height / 2) - 1, + this.rivalSprite.width / 2, this.rivalSprite.height / 10, + Number(`0x${Color.DARK_GREY}`), 0.5 + ); + this.spriteShadow.setName("sprite-shadow"); + this.spriteShadow.setBlendMode(Phaser.BlendModes.OVERLAY); + this.titleContainer.add([this.rivalSprite, this.spriteShadow]); + if (this.scene.eventManager.isEventActive()) { - this.eventDisplay = new TimedEventDisplay(this.scene, 170, 66, this.scene.eventManager.activeEvent()); + this.eventDisplay = new TimedEventDisplay(this.scene, 189, 49, this.scene.eventManager.activeEvent()); this.eventDisplay.setup(); this.titleContainer.add(this.eventDisplay); + } else { + this.rivalText = addTextObject( + this.scene, + 190, + 98, + "Check the Discord for the latest changes!", + TextStyle.WINDOW_ALT, + { fontSize: 49 } + ); + this.rivalText.setOrigin(0); + this.rivalText.setName("text-rival-changelog"); } - this.playerCountLabel = addTextObject(this.scene, 8, this.scene.scaledCanvas.height - 132, i18next.t("menu:playersOnline", { count: 0 }), TextStyle.MESSAGE, { fontSize: "54px" }); - console.log(this.playerCountLabel); + this.bubble = new SpeechBubble(this.scene, 244, 102, (this.eventDisplay?.getByName("text-event-timer") ?? this.rivalText) as Phaser.GameObjects.Text); + this.titleContainer.add(this.bubble); + + this.playerCountLabel = addTextObject( + this.scene, + this.scene.scaledCanvas.width - 60, + this.scene.scaledCanvas.height - 20, + i18next.t("menu:playersOnline", { count: 0 }), + TextStyle.MESSAGE, + { + fontSize: "60px", + align: "right", + } + ); + this.playerCountLabel.setName("player-count"); this.playerCountLabel.setOrigin(0); + this.playerCountWidth = this.playerCountLabel.width; this.titleContainer.add(this.playerCountLabel); this.splashMessageText = addTextObject(this.scene, this.logo.x + 64, this.logo.y + this.logo.displayHeight - 8, "", TextStyle.MONEY, { fontSize: "54px" }); @@ -75,6 +147,8 @@ export default class TitleUiHandler extends OptionSelectUiHandler { .then(request => request.json()) .then((stats: { playerCount: number, battleCount: number }) => { this.playerCountLabel.setText(i18next.t("menu:playersOnline", { count: stats.playerCount })); + this.playerCountLabel.setX(this.playerCountLabel.x - ((this.playerCountLabel.width - this.playerCountWidth) / 6)); + this.playerCountWidth = this.playerCountLabel.width; }) .catch(err => { console.error("Failed to fetch title stats:\n", err); @@ -86,7 +160,7 @@ export default class TitleUiHandler extends OptionSelectUiHandler { if (ret) { this.splashMessage = Utils.randItem(getSplashMessages()); - this.splashMessageText.setText(this.splashMessage.replace("{COUNT}", "?")); + this.splashMessageText.setText(this.splashMessage); const ui = this.getUi(); @@ -94,7 +168,9 @@ export default class TitleUiHandler extends OptionSelectUiHandler { this.eventDisplay.show(); } + this.bubble.setVisible(true); this.iconContainer.setVisible(true); + this.update(); this.updateTitleStats(); @@ -113,6 +189,12 @@ export default class TitleUiHandler extends OptionSelectUiHandler { return ret; } + update() { + const playerMale = this.scene.gameData.gender === PlayerGender.MALE; + this.rivalSprite.setTexture(`${playerMale ? "ivy" : "finn" }-sprite`); + this.spriteShadow.setY((this.rivalSprite.y + this.rivalSprite.height / 2) - (playerMale ? 2 : 3)); + } + clear(): void { super.clear(); @@ -120,6 +202,7 @@ export default class TitleUiHandler extends OptionSelectUiHandler { this.eventDisplay?.setVisible(false); this.iconContainer?.setVisible(false); + this.bubble.setVisible(false); clearInterval(this.titleStatsTimer); this.titleStatsTimer = null; @@ -192,9 +275,11 @@ class Icon extends Phaser.GameObjects.Sprite { this.setAlpha(this.DEFAULT_ALPHA); this.on(Phaser.Input.Events.GAMEOBJECT_POINTER_OVER, () => { this.setAlpha(1); + scene.ui.showTooltip("", texture, true); }); this.on(Phaser.Input.Events.GAMEOBJECT_POINTER_OUT, () => { this.setAlpha(this.DEFAULT_ALPHA); + scene.ui.hideTooltip(); }); this.on(Phaser.Input.Events.GAMEOBJECT_POINTER_DOWN, () => { window.open(link, "_blank").focus(); diff --git a/src/ui/ui.ts b/src/ui/ui.ts index e4f0df4b766..2ee9be4db33 100644 --- a/src/ui/ui.ts +++ b/src/ui/ui.ts @@ -220,6 +220,7 @@ export default class UI extends Phaser.GameObjects.Container { this.tooltipContent = addTextObject(this.scene, 6, 16, "", TextStyle.TOOLTIP_CONTENT); this.tooltipContent.setName("text-tooltip-content"); + this.tooltipContent.setAlign("center"); this.tooltipContent.setWordWrapWidth(696); this.tooltipContainer.add(this.tooltipBg); @@ -365,8 +366,13 @@ export default class UI extends Phaser.GameObjects.Container { update(): void { if (this.tooltipContainer.visible) { - const reverse = this.scene.game.input.mousePointer.x >= this.scene.game.canvas.width - this.tooltipBg.width * 6 - 12; - this.tooltipContainer.setPosition(!reverse ? this.scene.game.input.mousePointer.x / 6 + 2 : this.scene.game.input.mousePointer.x / 6 - this.tooltipBg.width - 2, this.scene.game.input.mousePointer.y / 6 + 2); + const reverseX = this.scene.game.input.mousePointer.x >= this.scene.game.canvas.width - this.tooltipBg.width * 6 - 12; + const reverseY = this.scene.game.input.mousePointer.y >= this.scene.game.canvas.height - this.tooltipBg.height * 6 - 12; + this.tooltipContainer.setPosition( + !reverseX ? this.scene.game.input.mousePointer.x / 6 + 2 : this.scene.game.input.mousePointer.x / 6 - this.tooltipBg.width - 2, + !reverseY ? this.scene.game.input.mousePointer.y / 6 + 2 : this.scene.game.input.mousePointer.y / 6 - this.tooltipBg.height - 2 + ); + this.tooltipTitle.setX(this.tooltipBg.width / 2); } } diff --git a/src/utils.ts b/src/utils.ts index 5a67df314d2..1c2b132c1fc 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -433,13 +433,19 @@ export function deltaRgb(rgb1: integer[], rgb2: integer[]): integer { return Math.ceil(Math.sqrt(2 * drp2 + 4 * dgp2 + 3 * dbp2 + t * (drp2 - dbp2) / 256)); } -export function rgbHexToRgba(hex: string) { +/** + * Converts a hex code for a color to rgba + * @param {string} hex hex code + * @param {number} alpha transparency between 0 and 1 + * @returns rgba object + */ +export function rgbHexToRgba(hex: string, alpha?: number): { r: number, g: number, b: number, a: number } { const color = hex.match(/^([\da-f]{2})([\da-f]{2})([\da-f]{2})$/i); return { r: parseInt(color[1], 16), g: parseInt(color[2], 16), b: parseInt(color[3], 16), - a: 255 + a: 255 * (alpha ?? 1) }; }