mirror of
				https://github.com/tobspr/shapez.io.git
				synced 2025-06-13 13:04:03 +00:00 
			
		
		
		
	Fix mouse panning
This commit is contained in:
		
							parent
							
								
									ed32238412
								
							
						
					
					
						commit
						24eb060000
					
				@ -872,6 +872,10 @@ export class Camera extends BasicSerializableObject {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.root.hud.shouldPauseGame() || this.root.hud.hasBlockingOverlayOpen()) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.desiredCenter || this.desiredZoom || this.currentlyMoving || this.currentlyPinching) {
 | 
			
		||||
            // Performing another method of movement right now
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
@ -194,9 +194,6 @@ export class GameHUD {
 | 
			
		||||
     * Returns true if the rendering can be paused
 | 
			
		||||
     */
 | 
			
		||||
    hasBlockingOverlayOpen() {
 | 
			
		||||
        if (this.root.camera.getIsMapOverlayActive()) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        for (const key in this.parts) {
 | 
			
		||||
            if (this.parts[key].isBlockingOverlay()) {
 | 
			
		||||
                return true;
 | 
			
		||||
 | 
			
		||||
@ -1,211 +1,215 @@
 | 
			
		||||
/* typehints:start */
 | 
			
		||||
import { Application } from "../../../application";
 | 
			
		||||
/* typehints:end */
 | 
			
		||||
 | 
			
		||||
import { SOUNDS } from "../../../platform/sound";
 | 
			
		||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
 | 
			
		||||
import { BaseHUDPart } from "../base_hud_part";
 | 
			
		||||
import { Dialog, DialogLoading, DialogOptionChooser } from "../../../core/modal_dialog_elements";
 | 
			
		||||
import { makeDiv } from "../../../core/utils";
 | 
			
		||||
import { T } from "../../../translations";
 | 
			
		||||
import { THIRDPARTY_URLS } from "../../../core/config";
 | 
			
		||||
 | 
			
		||||
export class HUDModalDialogs extends BaseHUDPart {
 | 
			
		||||
    constructor(root, app) {
 | 
			
		||||
        // Important: Root is not always available here! Its also used in the main menu
 | 
			
		||||
        super(root);
 | 
			
		||||
 | 
			
		||||
        /** @type {Application} */
 | 
			
		||||
        this.app = root ? root.app : app;
 | 
			
		||||
 | 
			
		||||
        this.dialogParent = null;
 | 
			
		||||
        this.dialogStack = [];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // For use inside of the game, implementation of base hud part
 | 
			
		||||
    initialize() {
 | 
			
		||||
        this.dialogParent = document.getElementById("ingame_HUD_ModalDialogs");
 | 
			
		||||
        this.domWatcher = new DynamicDomAttach(this.root, this.dialogParent);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    shouldPauseRendering() {
 | 
			
		||||
        return this.dialogStack.length > 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    shouldPauseGame() {
 | 
			
		||||
        return this.shouldPauseRendering();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    createElements(parent) {
 | 
			
		||||
        return makeDiv(parent, "ingame_HUD_ModalDialogs");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // For use outside of the game
 | 
			
		||||
    initializeToElement(element) {
 | 
			
		||||
        assert(element, "No element for dialogs given");
 | 
			
		||||
        this.dialogParent = element;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Methods
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {string} title
 | 
			
		||||
     * @param {string} text
 | 
			
		||||
     * @param {Array<string>} buttons
 | 
			
		||||
     */
 | 
			
		||||
    showInfo(title, text, buttons = ["ok:good"]) {
 | 
			
		||||
        const dialog = new Dialog({
 | 
			
		||||
            app: this.app,
 | 
			
		||||
            title: title,
 | 
			
		||||
            contentHTML: text,
 | 
			
		||||
            buttons: buttons,
 | 
			
		||||
            type: "info",
 | 
			
		||||
        });
 | 
			
		||||
        this.internalShowDialog(dialog);
 | 
			
		||||
 | 
			
		||||
        if (this.app) {
 | 
			
		||||
            this.app.sound.playUiSound(SOUNDS.dialogOk);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return dialog.buttonSignals;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {string} title
 | 
			
		||||
     * @param {string} text
 | 
			
		||||
     * @param {Array<string>} buttons
 | 
			
		||||
     */
 | 
			
		||||
    showWarning(title, text, buttons = ["ok:good"]) {
 | 
			
		||||
        const dialog = new Dialog({
 | 
			
		||||
            app: this.app,
 | 
			
		||||
            title: title,
 | 
			
		||||
            contentHTML: text,
 | 
			
		||||
            buttons: buttons,
 | 
			
		||||
            type: "warning",
 | 
			
		||||
        });
 | 
			
		||||
        this.internalShowDialog(dialog);
 | 
			
		||||
 | 
			
		||||
        if (this.app) {
 | 
			
		||||
            this.app.sound.playUiSound(SOUNDS.dialogError);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return dialog.buttonSignals;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {string} feature
 | 
			
		||||
     * @param {string} textPrefab
 | 
			
		||||
     */
 | 
			
		||||
    showFeatureRestrictionInfo(feature, textPrefab = T.dialogs.featureRestriction.desc) {
 | 
			
		||||
        const dialog = new Dialog({
 | 
			
		||||
            app: this.app,
 | 
			
		||||
            title: T.dialogs.featureRestriction.title,
 | 
			
		||||
            contentHTML: textPrefab.replace("<feature>", feature),
 | 
			
		||||
            buttons: ["cancel:bad", "getStandalone:good"],
 | 
			
		||||
            type: "warning",
 | 
			
		||||
        });
 | 
			
		||||
        this.internalShowDialog(dialog);
 | 
			
		||||
 | 
			
		||||
        if (this.app) {
 | 
			
		||||
            this.app.sound.playUiSound(SOUNDS.dialogOk);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.app.analytics.trackUiClick("demo_dialog_show");
 | 
			
		||||
 | 
			
		||||
        dialog.buttonSignals.cancel.add(() => {
 | 
			
		||||
            this.app.analytics.trackUiClick("demo_dialog_cancel");
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        dialog.buttonSignals.getStandalone.add(() => {
 | 
			
		||||
            this.app.analytics.trackUiClick("demo_dialog_click");
 | 
			
		||||
            window.open(THIRDPARTY_URLS.standaloneStorePage);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return dialog.buttonSignals;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    showOptionChooser(title, options) {
 | 
			
		||||
        const dialog = new DialogOptionChooser({
 | 
			
		||||
            app: this.app,
 | 
			
		||||
            title,
 | 
			
		||||
            options,
 | 
			
		||||
        });
 | 
			
		||||
        this.internalShowDialog(dialog);
 | 
			
		||||
        return dialog.buttonSignals;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Returns method to be called when laoding finishd
 | 
			
		||||
    showLoadingDialog() {
 | 
			
		||||
        const dialog = new DialogLoading(this.app);
 | 
			
		||||
        this.internalShowDialog(dialog);
 | 
			
		||||
        return this.closeDialog.bind(this, dialog);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internalShowDialog(dialog) {
 | 
			
		||||
        const elem = dialog.createElement();
 | 
			
		||||
        dialog.setIndex(this.dialogStack.length);
 | 
			
		||||
 | 
			
		||||
        // Hide last dialog in queue
 | 
			
		||||
        if (this.dialogStack.length > 0) {
 | 
			
		||||
            this.dialogStack[this.dialogStack.length - 1].hide();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.dialogStack.push(dialog);
 | 
			
		||||
 | 
			
		||||
        // Append dialog
 | 
			
		||||
        dialog.show();
 | 
			
		||||
        dialog.closeRequested.add(this.closeDialog.bind(this, dialog));
 | 
			
		||||
 | 
			
		||||
        // Append to HTML
 | 
			
		||||
        this.dialogParent.appendChild(elem);
 | 
			
		||||
 | 
			
		||||
        document.body.classList.toggle("modalDialogActive", this.dialogStack.length > 0);
 | 
			
		||||
 | 
			
		||||
        // IMPORTANT: Attach element directly, otherwise double submit is possible
 | 
			
		||||
        this.update();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    update() {
 | 
			
		||||
        if (this.domWatcher) {
 | 
			
		||||
            this.domWatcher.update(this.dialogStack.length > 0);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    closeDialog(dialog) {
 | 
			
		||||
        dialog.destroy();
 | 
			
		||||
 | 
			
		||||
        let index = -1;
 | 
			
		||||
        for (let i = 0; i < this.dialogStack.length; ++i) {
 | 
			
		||||
            if (this.dialogStack[i] === dialog) {
 | 
			
		||||
                index = i;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        assert(index >= 0, "Dialog not in dialog stack");
 | 
			
		||||
        this.dialogStack.splice(index, 1);
 | 
			
		||||
 | 
			
		||||
        if (this.dialogStack.length > 0) {
 | 
			
		||||
            // Show the dialog which was previously open
 | 
			
		||||
            this.dialogStack[this.dialogStack.length - 1].show();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        document.body.classList.toggle("modalDialogActive", this.dialogStack.length > 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    close() {
 | 
			
		||||
        for (let i = 0; i < this.dialogStack.length; ++i) {
 | 
			
		||||
            const dialog = this.dialogStack[i];
 | 
			
		||||
            dialog.destroy();
 | 
			
		||||
        }
 | 
			
		||||
        this.dialogStack = [];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cleanup() {
 | 
			
		||||
        super.cleanup();
 | 
			
		||||
        for (let i = 0; i < this.dialogStack.length; ++i) {
 | 
			
		||||
            this.dialogStack[i].destroy();
 | 
			
		||||
        }
 | 
			
		||||
        this.dialogStack = [];
 | 
			
		||||
        this.dialogParent = null;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
/* typehints:start */
 | 
			
		||||
import { Application } from "../../../application";
 | 
			
		||||
/* typehints:end */
 | 
			
		||||
 | 
			
		||||
import { SOUNDS } from "../../../platform/sound";
 | 
			
		||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
 | 
			
		||||
import { BaseHUDPart } from "../base_hud_part";
 | 
			
		||||
import { Dialog, DialogLoading, DialogOptionChooser } from "../../../core/modal_dialog_elements";
 | 
			
		||||
import { makeDiv } from "../../../core/utils";
 | 
			
		||||
import { T } from "../../../translations";
 | 
			
		||||
import { THIRDPARTY_URLS } from "../../../core/config";
 | 
			
		||||
 | 
			
		||||
export class HUDModalDialogs extends BaseHUDPart {
 | 
			
		||||
    constructor(root, app) {
 | 
			
		||||
        // Important: Root is not always available here! Its also used in the main menu
 | 
			
		||||
        super(root);
 | 
			
		||||
 | 
			
		||||
        /** @type {Application} */
 | 
			
		||||
        this.app = root ? root.app : app;
 | 
			
		||||
 | 
			
		||||
        this.dialogParent = null;
 | 
			
		||||
        this.dialogStack = [];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // For use inside of the game, implementation of base hud part
 | 
			
		||||
    initialize() {
 | 
			
		||||
        this.dialogParent = document.getElementById("ingame_HUD_ModalDialogs");
 | 
			
		||||
        this.domWatcher = new DynamicDomAttach(this.root, this.dialogParent);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    shouldPauseRendering() {
 | 
			
		||||
        return this.dialogStack.length > 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    shouldPauseGame() {
 | 
			
		||||
        return this.shouldPauseRendering();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    createElements(parent) {
 | 
			
		||||
        return makeDiv(parent, "ingame_HUD_ModalDialogs");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // For use outside of the game
 | 
			
		||||
    initializeToElement(element) {
 | 
			
		||||
        assert(element, "No element for dialogs given");
 | 
			
		||||
        this.dialogParent = element;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    isBlockingOverlay() {
 | 
			
		||||
        return this.dialogStack.length > 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Methods
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {string} title
 | 
			
		||||
     * @param {string} text
 | 
			
		||||
     * @param {Array<string>} buttons
 | 
			
		||||
     */
 | 
			
		||||
    showInfo(title, text, buttons = ["ok:good"]) {
 | 
			
		||||
        const dialog = new Dialog({
 | 
			
		||||
            app: this.app,
 | 
			
		||||
            title: title,
 | 
			
		||||
            contentHTML: text,
 | 
			
		||||
            buttons: buttons,
 | 
			
		||||
            type: "info",
 | 
			
		||||
        });
 | 
			
		||||
        this.internalShowDialog(dialog);
 | 
			
		||||
 | 
			
		||||
        if (this.app) {
 | 
			
		||||
            this.app.sound.playUiSound(SOUNDS.dialogOk);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return dialog.buttonSignals;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {string} title
 | 
			
		||||
     * @param {string} text
 | 
			
		||||
     * @param {Array<string>} buttons
 | 
			
		||||
     */
 | 
			
		||||
    showWarning(title, text, buttons = ["ok:good"]) {
 | 
			
		||||
        const dialog = new Dialog({
 | 
			
		||||
            app: this.app,
 | 
			
		||||
            title: title,
 | 
			
		||||
            contentHTML: text,
 | 
			
		||||
            buttons: buttons,
 | 
			
		||||
            type: "warning",
 | 
			
		||||
        });
 | 
			
		||||
        this.internalShowDialog(dialog);
 | 
			
		||||
 | 
			
		||||
        if (this.app) {
 | 
			
		||||
            this.app.sound.playUiSound(SOUNDS.dialogError);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return dialog.buttonSignals;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {string} feature
 | 
			
		||||
     * @param {string} textPrefab
 | 
			
		||||
     */
 | 
			
		||||
    showFeatureRestrictionInfo(feature, textPrefab = T.dialogs.featureRestriction.desc) {
 | 
			
		||||
        const dialog = new Dialog({
 | 
			
		||||
            app: this.app,
 | 
			
		||||
            title: T.dialogs.featureRestriction.title,
 | 
			
		||||
            contentHTML: textPrefab.replace("<feature>", feature),
 | 
			
		||||
            buttons: ["cancel:bad", "getStandalone:good"],
 | 
			
		||||
            type: "warning",
 | 
			
		||||
        });
 | 
			
		||||
        this.internalShowDialog(dialog);
 | 
			
		||||
 | 
			
		||||
        if (this.app) {
 | 
			
		||||
            this.app.sound.playUiSound(SOUNDS.dialogOk);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.app.analytics.trackUiClick("demo_dialog_show");
 | 
			
		||||
 | 
			
		||||
        dialog.buttonSignals.cancel.add(() => {
 | 
			
		||||
            this.app.analytics.trackUiClick("demo_dialog_cancel");
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        dialog.buttonSignals.getStandalone.add(() => {
 | 
			
		||||
            this.app.analytics.trackUiClick("demo_dialog_click");
 | 
			
		||||
            window.open(THIRDPARTY_URLS.standaloneStorePage);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return dialog.buttonSignals;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    showOptionChooser(title, options) {
 | 
			
		||||
        const dialog = new DialogOptionChooser({
 | 
			
		||||
            app: this.app,
 | 
			
		||||
            title,
 | 
			
		||||
            options,
 | 
			
		||||
        });
 | 
			
		||||
        this.internalShowDialog(dialog);
 | 
			
		||||
        return dialog.buttonSignals;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Returns method to be called when laoding finishd
 | 
			
		||||
    showLoadingDialog() {
 | 
			
		||||
        const dialog = new DialogLoading(this.app);
 | 
			
		||||
        this.internalShowDialog(dialog);
 | 
			
		||||
        return this.closeDialog.bind(this, dialog);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internalShowDialog(dialog) {
 | 
			
		||||
        const elem = dialog.createElement();
 | 
			
		||||
        dialog.setIndex(this.dialogStack.length);
 | 
			
		||||
 | 
			
		||||
        // Hide last dialog in queue
 | 
			
		||||
        if (this.dialogStack.length > 0) {
 | 
			
		||||
            this.dialogStack[this.dialogStack.length - 1].hide();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.dialogStack.push(dialog);
 | 
			
		||||
 | 
			
		||||
        // Append dialog
 | 
			
		||||
        dialog.show();
 | 
			
		||||
        dialog.closeRequested.add(this.closeDialog.bind(this, dialog));
 | 
			
		||||
 | 
			
		||||
        // Append to HTML
 | 
			
		||||
        this.dialogParent.appendChild(elem);
 | 
			
		||||
 | 
			
		||||
        document.body.classList.toggle("modalDialogActive", this.dialogStack.length > 0);
 | 
			
		||||
 | 
			
		||||
        // IMPORTANT: Attach element directly, otherwise double submit is possible
 | 
			
		||||
        this.update();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    update() {
 | 
			
		||||
        if (this.domWatcher) {
 | 
			
		||||
            this.domWatcher.update(this.dialogStack.length > 0);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    closeDialog(dialog) {
 | 
			
		||||
        dialog.destroy();
 | 
			
		||||
 | 
			
		||||
        let index = -1;
 | 
			
		||||
        for (let i = 0; i < this.dialogStack.length; ++i) {
 | 
			
		||||
            if (this.dialogStack[i] === dialog) {
 | 
			
		||||
                index = i;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        assert(index >= 0, "Dialog not in dialog stack");
 | 
			
		||||
        this.dialogStack.splice(index, 1);
 | 
			
		||||
 | 
			
		||||
        if (this.dialogStack.length > 0) {
 | 
			
		||||
            // Show the dialog which was previously open
 | 
			
		||||
            this.dialogStack[this.dialogStack.length - 1].show();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        document.body.classList.toggle("modalDialogActive", this.dialogStack.length > 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    close() {
 | 
			
		||||
        for (let i = 0; i < this.dialogStack.length; ++i) {
 | 
			
		||||
            const dialog = this.dialogStack[i];
 | 
			
		||||
            dialog.destroy();
 | 
			
		||||
        }
 | 
			
		||||
        this.dialogStack = [];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cleanup() {
 | 
			
		||||
        super.cleanup();
 | 
			
		||||
        for (let i = 0; i < this.dialogStack.length; ++i) {
 | 
			
		||||
            this.dialogStack[i].destroy();
 | 
			
		||||
        }
 | 
			
		||||
        this.dialogStack = [];
 | 
			
		||||
        this.dialogParent = null;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -54,6 +54,10 @@ export class HUDSettingsMenu extends BaseHUDPart {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    isBlockingOverlay() {
 | 
			
		||||
        return this.visible;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    returnToMenu() {
 | 
			
		||||
        this.root.gameState.goBackToMenu();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1,251 +1,255 @@
 | 
			
		||||
import { ClickDetector } from "../../../core/click_detector";
 | 
			
		||||
import { InputReceiver } from "../../../core/input_receiver";
 | 
			
		||||
import { formatBigNumber, makeDiv } from "../../../core/utils";
 | 
			
		||||
import { T } from "../../../translations";
 | 
			
		||||
import { KeyActionMapper, KEYMAPPINGS } from "../../key_action_mapper";
 | 
			
		||||
import { UPGRADES } from "../../upgrades";
 | 
			
		||||
import { BaseHUDPart } from "../base_hud_part";
 | 
			
		||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
 | 
			
		||||
 | 
			
		||||
export class HUDShop extends BaseHUDPart {
 | 
			
		||||
    createElements(parent) {
 | 
			
		||||
        this.background = makeDiv(parent, "ingame_HUD_Shop", ["ingameDialog"]);
 | 
			
		||||
 | 
			
		||||
        // DIALOG Inner / Wrapper
 | 
			
		||||
        this.dialogInner = makeDiv(this.background, null, ["dialogInner"]);
 | 
			
		||||
        this.title = makeDiv(this.dialogInner, null, ["title"], T.ingame.shop.title);
 | 
			
		||||
        this.closeButton = makeDiv(this.title, null, ["closeButton"]);
 | 
			
		||||
        this.trackClicks(this.closeButton, this.close);
 | 
			
		||||
        this.contentDiv = makeDiv(this.dialogInner, null, ["content"]);
 | 
			
		||||
 | 
			
		||||
        this.upgradeToElements = {};
 | 
			
		||||
 | 
			
		||||
        // Upgrades
 | 
			
		||||
        for (const upgradeId in UPGRADES) {
 | 
			
		||||
            const handle = {};
 | 
			
		||||
            handle.requireIndexToElement = [];
 | 
			
		||||
 | 
			
		||||
            // Wrapper
 | 
			
		||||
            handle.elem = makeDiv(this.contentDiv, null, ["upgrade"]);
 | 
			
		||||
            handle.elem.setAttribute("data-upgrade-id", upgradeId);
 | 
			
		||||
 | 
			
		||||
            // Title
 | 
			
		||||
            const title = makeDiv(handle.elem, null, ["title"], T.shopUpgrades[upgradeId].name);
 | 
			
		||||
 | 
			
		||||
            // Title > Tier
 | 
			
		||||
            handle.elemTierLabel = makeDiv(title, null, ["tier"]);
 | 
			
		||||
 | 
			
		||||
            // Icon
 | 
			
		||||
            handle.icon = makeDiv(handle.elem, null, ["icon"]);
 | 
			
		||||
            handle.icon.setAttribute("data-icon", "upgrades/" + upgradeId + ".png");
 | 
			
		||||
 | 
			
		||||
            // Description
 | 
			
		||||
            handle.elemDescription = makeDiv(handle.elem, null, ["description"], "??");
 | 
			
		||||
            handle.elemRequirements = makeDiv(handle.elem, null, ["requirements"]);
 | 
			
		||||
 | 
			
		||||
            // Buy button
 | 
			
		||||
            handle.buyButton = document.createElement("button");
 | 
			
		||||
            handle.buyButton.classList.add("buy", "styledButton");
 | 
			
		||||
            handle.buyButton.innerText = T.ingame.shop.buttonUnlock;
 | 
			
		||||
            handle.elem.appendChild(handle.buyButton);
 | 
			
		||||
 | 
			
		||||
            this.trackClicks(handle.buyButton, () => this.tryUnlockNextTier(upgradeId));
 | 
			
		||||
 | 
			
		||||
            // Assign handle
 | 
			
		||||
            this.upgradeToElements[upgradeId] = handle;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    rerenderFull() {
 | 
			
		||||
        for (const upgradeId in this.upgradeToElements) {
 | 
			
		||||
            const handle = this.upgradeToElements[upgradeId];
 | 
			
		||||
            const { tiers } = UPGRADES[upgradeId];
 | 
			
		||||
 | 
			
		||||
            const currentTier = this.root.hubGoals.getUpgradeLevel(upgradeId);
 | 
			
		||||
            const currentTierMultiplier = this.root.hubGoals.upgradeImprovements[upgradeId];
 | 
			
		||||
            const tierHandle = tiers[currentTier];
 | 
			
		||||
 | 
			
		||||
            // Set tier
 | 
			
		||||
            handle.elemTierLabel.innerText = T.ingame.shop.tier.replace(
 | 
			
		||||
                "<x>",
 | 
			
		||||
                "" + T.ingame.shop.tierLabels[currentTier]
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            handle.elemTierLabel.setAttribute("data-tier", currentTier);
 | 
			
		||||
 | 
			
		||||
            // Cleanup detectors
 | 
			
		||||
            for (let i = 0; i < handle.requireIndexToElement.length; ++i) {
 | 
			
		||||
                const requiredHandle = handle.requireIndexToElement[i];
 | 
			
		||||
                requiredHandle.container.remove();
 | 
			
		||||
                requiredHandle.pinDetector.cleanup();
 | 
			
		||||
                requiredHandle.infoDetector.cleanup();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Cleanup
 | 
			
		||||
            handle.requireIndexToElement = [];
 | 
			
		||||
 | 
			
		||||
            handle.elem.classList.toggle("maxLevel", !tierHandle);
 | 
			
		||||
 | 
			
		||||
            if (!tierHandle) {
 | 
			
		||||
                // Max level
 | 
			
		||||
                handle.elemDescription.innerText = T.ingame.shop.maximumLevel.replace(
 | 
			
		||||
                    "<currentMult>",
 | 
			
		||||
                    currentTierMultiplier.toString()
 | 
			
		||||
                );
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Set description
 | 
			
		||||
            handle.elemDescription.innerText = T.shopUpgrades[upgradeId].description
 | 
			
		||||
                .replace("<currentMult>", currentTierMultiplier.toString())
 | 
			
		||||
                .replace("<newMult>", (currentTierMultiplier + tierHandle.improvement).toString())
 | 
			
		||||
                // Backwards compatibility
 | 
			
		||||
                .replace("<gain>", (tierHandle.improvement * 100.0).toString());
 | 
			
		||||
 | 
			
		||||
            tierHandle.required.forEach(({ shape, amount }) => {
 | 
			
		||||
                const container = makeDiv(handle.elemRequirements, null, ["requirement"]);
 | 
			
		||||
 | 
			
		||||
                const shapeDef = this.root.shapeDefinitionMgr.getShapeFromShortKey(shape);
 | 
			
		||||
                const shapeCanvas = shapeDef.generateAsCanvas(120);
 | 
			
		||||
                shapeCanvas.classList.add();
 | 
			
		||||
                container.appendChild(shapeCanvas);
 | 
			
		||||
 | 
			
		||||
                const progressContainer = makeDiv(container, null, ["amount"]);
 | 
			
		||||
                const progressBar = document.createElement("label");
 | 
			
		||||
                progressBar.classList.add("progressBar");
 | 
			
		||||
                progressContainer.appendChild(progressBar);
 | 
			
		||||
 | 
			
		||||
                const progressLabel = document.createElement("label");
 | 
			
		||||
                progressContainer.appendChild(progressLabel);
 | 
			
		||||
 | 
			
		||||
                const pinButton = document.createElement("button");
 | 
			
		||||
                pinButton.classList.add("pin");
 | 
			
		||||
                container.appendChild(pinButton);
 | 
			
		||||
 | 
			
		||||
                const viewInfoButton = document.createElement("button");
 | 
			
		||||
                viewInfoButton.classList.add("showInfo");
 | 
			
		||||
                container.appendChild(viewInfoButton);
 | 
			
		||||
 | 
			
		||||
                const currentGoalShape = this.root.hubGoals.currentGoal.definition.getHash();
 | 
			
		||||
                if (shape === currentGoalShape) {
 | 
			
		||||
                    pinButton.classList.add("isGoal");
 | 
			
		||||
                } else if (this.root.hud.parts.pinnedShapes.isShapePinned(shape)) {
 | 
			
		||||
                    pinButton.classList.add("alreadyPinned");
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                const pinDetector = new ClickDetector(pinButton, {
 | 
			
		||||
                    consumeEvents: true,
 | 
			
		||||
                    preventDefault: true,
 | 
			
		||||
                });
 | 
			
		||||
                pinDetector.click.add(() => {
 | 
			
		||||
                    if (this.root.hud.parts.pinnedShapes.isShapePinned(shape)) {
 | 
			
		||||
                        this.root.hud.signals.shapeUnpinRequested.dispatch(shape);
 | 
			
		||||
                        pinButton.classList.add("unpinned");
 | 
			
		||||
                        pinButton.classList.remove("pinned", "alreadyPinned");
 | 
			
		||||
                    } else {
 | 
			
		||||
                        this.root.hud.signals.shapePinRequested.dispatch(shapeDef);
 | 
			
		||||
                        pinButton.classList.add("pinned");
 | 
			
		||||
                        pinButton.classList.remove("unpinned");
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                const infoDetector = new ClickDetector(viewInfoButton, {
 | 
			
		||||
                    consumeEvents: true,
 | 
			
		||||
                    preventDefault: true,
 | 
			
		||||
                });
 | 
			
		||||
                infoDetector.click.add(() =>
 | 
			
		||||
                    this.root.hud.signals.viewShapeDetailsRequested.dispatch(shapeDef)
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                handle.requireIndexToElement.push({
 | 
			
		||||
                    container,
 | 
			
		||||
                    progressLabel,
 | 
			
		||||
                    progressBar,
 | 
			
		||||
                    definition: shapeDef,
 | 
			
		||||
                    required: amount,
 | 
			
		||||
                    pinDetector,
 | 
			
		||||
                    infoDetector,
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    renderCountsAndStatus() {
 | 
			
		||||
        for (const upgradeId in this.upgradeToElements) {
 | 
			
		||||
            const handle = this.upgradeToElements[upgradeId];
 | 
			
		||||
            for (let i = 0; i < handle.requireIndexToElement.length; ++i) {
 | 
			
		||||
                const { progressLabel, progressBar, definition, required } = handle.requireIndexToElement[i];
 | 
			
		||||
 | 
			
		||||
                const haveAmount = this.root.hubGoals.getShapesStored(definition);
 | 
			
		||||
                const progress = Math.min(haveAmount / required, 1.0);
 | 
			
		||||
 | 
			
		||||
                progressLabel.innerText = formatBigNumber(haveAmount) + " / " + formatBigNumber(required);
 | 
			
		||||
                progressBar.style.width = progress * 100.0 + "%";
 | 
			
		||||
                progressBar.classList.toggle("complete", progress >= 1.0);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            handle.buyButton.classList.toggle("buyable", this.root.hubGoals.canUnlockUpgrade(upgradeId));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    initialize() {
 | 
			
		||||
        this.domAttach = new DynamicDomAttach(this.root, this.background, {
 | 
			
		||||
            attachClass: "visible",
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.inputReciever = new InputReceiver("shop");
 | 
			
		||||
        this.keyActionMapper = new KeyActionMapper(this.root, this.inputReciever);
 | 
			
		||||
 | 
			
		||||
        this.keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.close, this);
 | 
			
		||||
        this.keyActionMapper.getBinding(KEYMAPPINGS.ingame.menuClose).add(this.close, this);
 | 
			
		||||
        this.keyActionMapper.getBinding(KEYMAPPINGS.ingame.menuOpenShop).add(this.close, this);
 | 
			
		||||
 | 
			
		||||
        this.close();
 | 
			
		||||
 | 
			
		||||
        this.rerenderFull();
 | 
			
		||||
        this.root.signals.upgradePurchased.add(this.rerenderFull, this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cleanup() {
 | 
			
		||||
        document.body.classList.remove("ingameDialogOpen");
 | 
			
		||||
 | 
			
		||||
        // Cleanup detectors
 | 
			
		||||
        for (const upgradeId in this.upgradeToElements) {
 | 
			
		||||
            const handle = this.upgradeToElements[upgradeId];
 | 
			
		||||
            for (let i = 0; i < handle.requireIndexToElement.length; ++i) {
 | 
			
		||||
                const requiredHandle = handle.requireIndexToElement[i];
 | 
			
		||||
                requiredHandle.container.remove();
 | 
			
		||||
                requiredHandle.pinDetector.cleanup();
 | 
			
		||||
                requiredHandle.infoDetector.cleanup();
 | 
			
		||||
            }
 | 
			
		||||
            handle.requireIndexToElement = [];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    show() {
 | 
			
		||||
        this.visible = true;
 | 
			
		||||
        document.body.classList.add("ingameDialogOpen");
 | 
			
		||||
        // this.background.classList.add("visible");
 | 
			
		||||
        this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
 | 
			
		||||
        this.rerenderFull();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    close() {
 | 
			
		||||
        this.visible = false;
 | 
			
		||||
        document.body.classList.remove("ingameDialogOpen");
 | 
			
		||||
        this.root.app.inputMgr.makeSureDetached(this.inputReciever);
 | 
			
		||||
        this.update();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    update() {
 | 
			
		||||
        this.domAttach.update(this.visible);
 | 
			
		||||
        if (this.visible) {
 | 
			
		||||
            this.renderCountsAndStatus();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    tryUnlockNextTier(upgradeId) {
 | 
			
		||||
        // Nothing
 | 
			
		||||
        this.root.hubGoals.tryUnlockUpgrade(upgradeId);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
import { ClickDetector } from "../../../core/click_detector";
 | 
			
		||||
import { InputReceiver } from "../../../core/input_receiver";
 | 
			
		||||
import { formatBigNumber, makeDiv } from "../../../core/utils";
 | 
			
		||||
import { T } from "../../../translations";
 | 
			
		||||
import { KeyActionMapper, KEYMAPPINGS } from "../../key_action_mapper";
 | 
			
		||||
import { UPGRADES } from "../../upgrades";
 | 
			
		||||
import { BaseHUDPart } from "../base_hud_part";
 | 
			
		||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
 | 
			
		||||
 | 
			
		||||
export class HUDShop extends BaseHUDPart {
 | 
			
		||||
    createElements(parent) {
 | 
			
		||||
        this.background = makeDiv(parent, "ingame_HUD_Shop", ["ingameDialog"]);
 | 
			
		||||
 | 
			
		||||
        // DIALOG Inner / Wrapper
 | 
			
		||||
        this.dialogInner = makeDiv(this.background, null, ["dialogInner"]);
 | 
			
		||||
        this.title = makeDiv(this.dialogInner, null, ["title"], T.ingame.shop.title);
 | 
			
		||||
        this.closeButton = makeDiv(this.title, null, ["closeButton"]);
 | 
			
		||||
        this.trackClicks(this.closeButton, this.close);
 | 
			
		||||
        this.contentDiv = makeDiv(this.dialogInner, null, ["content"]);
 | 
			
		||||
 | 
			
		||||
        this.upgradeToElements = {};
 | 
			
		||||
 | 
			
		||||
        // Upgrades
 | 
			
		||||
        for (const upgradeId in UPGRADES) {
 | 
			
		||||
            const handle = {};
 | 
			
		||||
            handle.requireIndexToElement = [];
 | 
			
		||||
 | 
			
		||||
            // Wrapper
 | 
			
		||||
            handle.elem = makeDiv(this.contentDiv, null, ["upgrade"]);
 | 
			
		||||
            handle.elem.setAttribute("data-upgrade-id", upgradeId);
 | 
			
		||||
 | 
			
		||||
            // Title
 | 
			
		||||
            const title = makeDiv(handle.elem, null, ["title"], T.shopUpgrades[upgradeId].name);
 | 
			
		||||
 | 
			
		||||
            // Title > Tier
 | 
			
		||||
            handle.elemTierLabel = makeDiv(title, null, ["tier"]);
 | 
			
		||||
 | 
			
		||||
            // Icon
 | 
			
		||||
            handle.icon = makeDiv(handle.elem, null, ["icon"]);
 | 
			
		||||
            handle.icon.setAttribute("data-icon", "upgrades/" + upgradeId + ".png");
 | 
			
		||||
 | 
			
		||||
            // Description
 | 
			
		||||
            handle.elemDescription = makeDiv(handle.elem, null, ["description"], "??");
 | 
			
		||||
            handle.elemRequirements = makeDiv(handle.elem, null, ["requirements"]);
 | 
			
		||||
 | 
			
		||||
            // Buy button
 | 
			
		||||
            handle.buyButton = document.createElement("button");
 | 
			
		||||
            handle.buyButton.classList.add("buy", "styledButton");
 | 
			
		||||
            handle.buyButton.innerText = T.ingame.shop.buttonUnlock;
 | 
			
		||||
            handle.elem.appendChild(handle.buyButton);
 | 
			
		||||
 | 
			
		||||
            this.trackClicks(handle.buyButton, () => this.tryUnlockNextTier(upgradeId));
 | 
			
		||||
 | 
			
		||||
            // Assign handle
 | 
			
		||||
            this.upgradeToElements[upgradeId] = handle;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    rerenderFull() {
 | 
			
		||||
        for (const upgradeId in this.upgradeToElements) {
 | 
			
		||||
            const handle = this.upgradeToElements[upgradeId];
 | 
			
		||||
            const { tiers } = UPGRADES[upgradeId];
 | 
			
		||||
 | 
			
		||||
            const currentTier = this.root.hubGoals.getUpgradeLevel(upgradeId);
 | 
			
		||||
            const currentTierMultiplier = this.root.hubGoals.upgradeImprovements[upgradeId];
 | 
			
		||||
            const tierHandle = tiers[currentTier];
 | 
			
		||||
 | 
			
		||||
            // Set tier
 | 
			
		||||
            handle.elemTierLabel.innerText = T.ingame.shop.tier.replace(
 | 
			
		||||
                "<x>",
 | 
			
		||||
                "" + T.ingame.shop.tierLabels[currentTier]
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            handle.elemTierLabel.setAttribute("data-tier", currentTier);
 | 
			
		||||
 | 
			
		||||
            // Cleanup detectors
 | 
			
		||||
            for (let i = 0; i < handle.requireIndexToElement.length; ++i) {
 | 
			
		||||
                const requiredHandle = handle.requireIndexToElement[i];
 | 
			
		||||
                requiredHandle.container.remove();
 | 
			
		||||
                requiredHandle.pinDetector.cleanup();
 | 
			
		||||
                requiredHandle.infoDetector.cleanup();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Cleanup
 | 
			
		||||
            handle.requireIndexToElement = [];
 | 
			
		||||
 | 
			
		||||
            handle.elem.classList.toggle("maxLevel", !tierHandle);
 | 
			
		||||
 | 
			
		||||
            if (!tierHandle) {
 | 
			
		||||
                // Max level
 | 
			
		||||
                handle.elemDescription.innerText = T.ingame.shop.maximumLevel.replace(
 | 
			
		||||
                    "<currentMult>",
 | 
			
		||||
                    currentTierMultiplier.toString()
 | 
			
		||||
                );
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Set description
 | 
			
		||||
            handle.elemDescription.innerText = T.shopUpgrades[upgradeId].description
 | 
			
		||||
                .replace("<currentMult>", currentTierMultiplier.toString())
 | 
			
		||||
                .replace("<newMult>", (currentTierMultiplier + tierHandle.improvement).toString())
 | 
			
		||||
                // Backwards compatibility
 | 
			
		||||
                .replace("<gain>", (tierHandle.improvement * 100.0).toString());
 | 
			
		||||
 | 
			
		||||
            tierHandle.required.forEach(({ shape, amount }) => {
 | 
			
		||||
                const container = makeDiv(handle.elemRequirements, null, ["requirement"]);
 | 
			
		||||
 | 
			
		||||
                const shapeDef = this.root.shapeDefinitionMgr.getShapeFromShortKey(shape);
 | 
			
		||||
                const shapeCanvas = shapeDef.generateAsCanvas(120);
 | 
			
		||||
                shapeCanvas.classList.add();
 | 
			
		||||
                container.appendChild(shapeCanvas);
 | 
			
		||||
 | 
			
		||||
                const progressContainer = makeDiv(container, null, ["amount"]);
 | 
			
		||||
                const progressBar = document.createElement("label");
 | 
			
		||||
                progressBar.classList.add("progressBar");
 | 
			
		||||
                progressContainer.appendChild(progressBar);
 | 
			
		||||
 | 
			
		||||
                const progressLabel = document.createElement("label");
 | 
			
		||||
                progressContainer.appendChild(progressLabel);
 | 
			
		||||
 | 
			
		||||
                const pinButton = document.createElement("button");
 | 
			
		||||
                pinButton.classList.add("pin");
 | 
			
		||||
                container.appendChild(pinButton);
 | 
			
		||||
 | 
			
		||||
                const viewInfoButton = document.createElement("button");
 | 
			
		||||
                viewInfoButton.classList.add("showInfo");
 | 
			
		||||
                container.appendChild(viewInfoButton);
 | 
			
		||||
 | 
			
		||||
                const currentGoalShape = this.root.hubGoals.currentGoal.definition.getHash();
 | 
			
		||||
                if (shape === currentGoalShape) {
 | 
			
		||||
                    pinButton.classList.add("isGoal");
 | 
			
		||||
                } else if (this.root.hud.parts.pinnedShapes.isShapePinned(shape)) {
 | 
			
		||||
                    pinButton.classList.add("alreadyPinned");
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                const pinDetector = new ClickDetector(pinButton, {
 | 
			
		||||
                    consumeEvents: true,
 | 
			
		||||
                    preventDefault: true,
 | 
			
		||||
                });
 | 
			
		||||
                pinDetector.click.add(() => {
 | 
			
		||||
                    if (this.root.hud.parts.pinnedShapes.isShapePinned(shape)) {
 | 
			
		||||
                        this.root.hud.signals.shapeUnpinRequested.dispatch(shape);
 | 
			
		||||
                        pinButton.classList.add("unpinned");
 | 
			
		||||
                        pinButton.classList.remove("pinned", "alreadyPinned");
 | 
			
		||||
                    } else {
 | 
			
		||||
                        this.root.hud.signals.shapePinRequested.dispatch(shapeDef);
 | 
			
		||||
                        pinButton.classList.add("pinned");
 | 
			
		||||
                        pinButton.classList.remove("unpinned");
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                const infoDetector = new ClickDetector(viewInfoButton, {
 | 
			
		||||
                    consumeEvents: true,
 | 
			
		||||
                    preventDefault: true,
 | 
			
		||||
                });
 | 
			
		||||
                infoDetector.click.add(() =>
 | 
			
		||||
                    this.root.hud.signals.viewShapeDetailsRequested.dispatch(shapeDef)
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                handle.requireIndexToElement.push({
 | 
			
		||||
                    container,
 | 
			
		||||
                    progressLabel,
 | 
			
		||||
                    progressBar,
 | 
			
		||||
                    definition: shapeDef,
 | 
			
		||||
                    required: amount,
 | 
			
		||||
                    pinDetector,
 | 
			
		||||
                    infoDetector,
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    renderCountsAndStatus() {
 | 
			
		||||
        for (const upgradeId in this.upgradeToElements) {
 | 
			
		||||
            const handle = this.upgradeToElements[upgradeId];
 | 
			
		||||
            for (let i = 0; i < handle.requireIndexToElement.length; ++i) {
 | 
			
		||||
                const { progressLabel, progressBar, definition, required } = handle.requireIndexToElement[i];
 | 
			
		||||
 | 
			
		||||
                const haveAmount = this.root.hubGoals.getShapesStored(definition);
 | 
			
		||||
                const progress = Math.min(haveAmount / required, 1.0);
 | 
			
		||||
 | 
			
		||||
                progressLabel.innerText = formatBigNumber(haveAmount) + " / " + formatBigNumber(required);
 | 
			
		||||
                progressBar.style.width = progress * 100.0 + "%";
 | 
			
		||||
                progressBar.classList.toggle("complete", progress >= 1.0);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            handle.buyButton.classList.toggle("buyable", this.root.hubGoals.canUnlockUpgrade(upgradeId));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    initialize() {
 | 
			
		||||
        this.domAttach = new DynamicDomAttach(this.root, this.background, {
 | 
			
		||||
            attachClass: "visible",
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.inputReciever = new InputReceiver("shop");
 | 
			
		||||
        this.keyActionMapper = new KeyActionMapper(this.root, this.inputReciever);
 | 
			
		||||
 | 
			
		||||
        this.keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.close, this);
 | 
			
		||||
        this.keyActionMapper.getBinding(KEYMAPPINGS.ingame.menuClose).add(this.close, this);
 | 
			
		||||
        this.keyActionMapper.getBinding(KEYMAPPINGS.ingame.menuOpenShop).add(this.close, this);
 | 
			
		||||
 | 
			
		||||
        this.close();
 | 
			
		||||
 | 
			
		||||
        this.rerenderFull();
 | 
			
		||||
        this.root.signals.upgradePurchased.add(this.rerenderFull, this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cleanup() {
 | 
			
		||||
        document.body.classList.remove("ingameDialogOpen");
 | 
			
		||||
 | 
			
		||||
        // Cleanup detectors
 | 
			
		||||
        for (const upgradeId in this.upgradeToElements) {
 | 
			
		||||
            const handle = this.upgradeToElements[upgradeId];
 | 
			
		||||
            for (let i = 0; i < handle.requireIndexToElement.length; ++i) {
 | 
			
		||||
                const requiredHandle = handle.requireIndexToElement[i];
 | 
			
		||||
                requiredHandle.container.remove();
 | 
			
		||||
                requiredHandle.pinDetector.cleanup();
 | 
			
		||||
                requiredHandle.infoDetector.cleanup();
 | 
			
		||||
            }
 | 
			
		||||
            handle.requireIndexToElement = [];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    show() {
 | 
			
		||||
        this.visible = true;
 | 
			
		||||
        document.body.classList.add("ingameDialogOpen");
 | 
			
		||||
        // this.background.classList.add("visible");
 | 
			
		||||
        this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
 | 
			
		||||
        this.rerenderFull();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    close() {
 | 
			
		||||
        this.visible = false;
 | 
			
		||||
        document.body.classList.remove("ingameDialogOpen");
 | 
			
		||||
        this.root.app.inputMgr.makeSureDetached(this.inputReciever);
 | 
			
		||||
        this.update();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    update() {
 | 
			
		||||
        this.domAttach.update(this.visible);
 | 
			
		||||
        if (this.visible) {
 | 
			
		||||
            this.renderCountsAndStatus();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    tryUnlockNextTier(upgradeId) {
 | 
			
		||||
        // Nothing
 | 
			
		||||
        this.root.hubGoals.tryUnlockUpgrade(upgradeId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    isBlockingOverlay() {
 | 
			
		||||
        return this.visible;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -155,6 +155,10 @@ export class HUDStatistics extends BaseHUDPart {
 | 
			
		||||
        document.body.classList.remove("ingameDialogOpen");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    isBlockingOverlay() {
 | 
			
		||||
        return this.visible;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    show() {
 | 
			
		||||
        this.visible = true;
 | 
			
		||||
        document.body.classList.add("ingameDialogOpen");
 | 
			
		||||
 | 
			
		||||
@ -1,156 +1,160 @@
 | 
			
		||||
import { globalConfig } from "../../../core/config";
 | 
			
		||||
import { gMetaBuildingRegistry } from "../../../core/global_registries";
 | 
			
		||||
import { makeDiv } from "../../../core/utils";
 | 
			
		||||
import { SOUNDS } from "../../../platform/sound";
 | 
			
		||||
import { T } from "../../../translations";
 | 
			
		||||
import { defaultBuildingVariant } from "../../meta_building";
 | 
			
		||||
import { enumHubGoalRewards } from "../../tutorial_goals";
 | 
			
		||||
import { BaseHUDPart } from "../base_hud_part";
 | 
			
		||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
 | 
			
		||||
import { enumHubGoalRewardsToContentUnlocked } from "../../tutorial_goals_mappings";
 | 
			
		||||
import { InputReceiver } from "../../../core/input_receiver";
 | 
			
		||||
 | 
			
		||||
export class HUDUnlockNotification extends BaseHUDPart {
 | 
			
		||||
    initialize() {
 | 
			
		||||
        this.visible = false;
 | 
			
		||||
 | 
			
		||||
        this.domAttach = new DynamicDomAttach(this.root, this.element, {
 | 
			
		||||
            timeToKeepSeconds: 0,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (!(G_IS_DEV && globalConfig.debug.disableUnlockDialog)) {
 | 
			
		||||
            this.root.signals.storyGoalCompleted.add(this.showForLevel, this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.buttonShowTimeout = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    createElements(parent) {
 | 
			
		||||
        this.inputReciever = new InputReceiver("unlock-notification");
 | 
			
		||||
 | 
			
		||||
        this.element = makeDiv(parent, "ingame_HUD_UnlockNotification", ["noBlur"]);
 | 
			
		||||
 | 
			
		||||
        const dialog = makeDiv(this.element, null, ["dialog"]);
 | 
			
		||||
 | 
			
		||||
        this.elemTitle = makeDiv(dialog, null, ["title"]);
 | 
			
		||||
        this.elemSubTitle = makeDiv(dialog, null, ["subTitle"], T.ingame.levelCompleteNotification.completed);
 | 
			
		||||
 | 
			
		||||
        this.elemContents = makeDiv(dialog, null, ["contents"]);
 | 
			
		||||
 | 
			
		||||
        this.btnClose = document.createElement("button");
 | 
			
		||||
        this.btnClose.classList.add("close", "styledButton");
 | 
			
		||||
        this.btnClose.innerText = T.ingame.levelCompleteNotification.buttonNextLevel;
 | 
			
		||||
        dialog.appendChild(this.btnClose);
 | 
			
		||||
 | 
			
		||||
        this.trackClicks(this.btnClose, this.requestClose);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {number} level
 | 
			
		||||
     * @param {enumHubGoalRewards} reward
 | 
			
		||||
     */
 | 
			
		||||
    showForLevel(level, reward) {
 | 
			
		||||
        this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
 | 
			
		||||
        this.elemTitle.innerText = T.ingame.levelCompleteNotification.levelTitle.replace(
 | 
			
		||||
            "<level>",
 | 
			
		||||
            ("" + level).padStart(2, "0")
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        const rewardName = T.storyRewards[reward].title;
 | 
			
		||||
 | 
			
		||||
        let html = `
 | 
			
		||||
        <div class="rewardName">
 | 
			
		||||
            ${T.ingame.levelCompleteNotification.unlockText.replace("<reward>", rewardName)}
 | 
			
		||||
        </div>
 | 
			
		||||
        
 | 
			
		||||
        <div class="rewardDesc">
 | 
			
		||||
            ${T.storyRewards[reward].desc}
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        `;
 | 
			
		||||
 | 
			
		||||
        html += "<div class='images'>";
 | 
			
		||||
        const gained = enumHubGoalRewardsToContentUnlocked[reward];
 | 
			
		||||
        if (gained) {
 | 
			
		||||
            gained.forEach(([metaBuildingClass, variant]) => {
 | 
			
		||||
                const metaBuilding = gMetaBuildingRegistry.findByClass(metaBuildingClass);
 | 
			
		||||
                html += `<div class="buildingExplanation" data-icon="building_tutorials/${
 | 
			
		||||
                    metaBuilding.getId() + (variant === defaultBuildingVariant ? "" : "-" + variant)
 | 
			
		||||
                }.png"></div>`;
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        html += "</div>";
 | 
			
		||||
 | 
			
		||||
        this.elemContents.innerHTML = html;
 | 
			
		||||
        this.visible = true;
 | 
			
		||||
        this.root.soundProxy.playUi(SOUNDS.levelComplete);
 | 
			
		||||
 | 
			
		||||
        if (this.buttonShowTimeout) {
 | 
			
		||||
            clearTimeout(this.buttonShowTimeout);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.element.querySelector("button.close").classList.remove("unlocked");
 | 
			
		||||
 | 
			
		||||
        if (this.root.app.settings.getAllSettings().offerHints) {
 | 
			
		||||
            this.buttonShowTimeout = setTimeout(
 | 
			
		||||
                () => this.element.querySelector("button.close").classList.add("unlocked"),
 | 
			
		||||
                G_IS_DEV ? 100 : 5000
 | 
			
		||||
            );
 | 
			
		||||
        } else {
 | 
			
		||||
            this.element.querySelector("button.close").classList.add("unlocked");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cleanup() {
 | 
			
		||||
        this.root.app.inputMgr.makeSureDetached(this.inputReciever);
 | 
			
		||||
        if (this.buttonShowTimeout) {
 | 
			
		||||
            clearTimeout(this.buttonShowTimeout);
 | 
			
		||||
            this.buttonShowTimeout = null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    requestClose() {
 | 
			
		||||
        this.root.app.adProvider.showVideoAd().then(() => {
 | 
			
		||||
            this.close();
 | 
			
		||||
 | 
			
		||||
            if (!this.root.app.settings.getAllSettings().offerHints) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (this.root.hubGoals.level === 3) {
 | 
			
		||||
                const { showUpgrades } = this.root.hud.parts.dialogs.showInfo(
 | 
			
		||||
                    T.dialogs.upgradesIntroduction.title,
 | 
			
		||||
                    T.dialogs.upgradesIntroduction.desc,
 | 
			
		||||
                    ["showUpgrades:good:timeout"]
 | 
			
		||||
                );
 | 
			
		||||
                showUpgrades.add(() => this.root.hud.parts.shop.show());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (this.root.hubGoals.level === 5) {
 | 
			
		||||
                const { showKeybindings } = this.root.hud.parts.dialogs.showInfo(
 | 
			
		||||
                    T.dialogs.keybindingsIntroduction.title,
 | 
			
		||||
                    T.dialogs.keybindingsIntroduction.desc,
 | 
			
		||||
                    ["showKeybindings:misc", "ok:good:timeout"]
 | 
			
		||||
                );
 | 
			
		||||
                showKeybindings.add(() => this.root.gameState.goToKeybindings());
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    close() {
 | 
			
		||||
        this.root.app.inputMgr.makeSureDetached(this.inputReciever);
 | 
			
		||||
        if (this.buttonShowTimeout) {
 | 
			
		||||
            clearTimeout(this.buttonShowTimeout);
 | 
			
		||||
            this.buttonShowTimeout = null;
 | 
			
		||||
        }
 | 
			
		||||
        this.visible = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    update() {
 | 
			
		||||
        this.domAttach.update(this.visible);
 | 
			
		||||
        if (!this.visible && this.buttonShowTimeout) {
 | 
			
		||||
            clearTimeout(this.buttonShowTimeout);
 | 
			
		||||
            this.buttonShowTimeout = null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
import { globalConfig } from "../../../core/config";
 | 
			
		||||
import { gMetaBuildingRegistry } from "../../../core/global_registries";
 | 
			
		||||
import { makeDiv } from "../../../core/utils";
 | 
			
		||||
import { SOUNDS } from "../../../platform/sound";
 | 
			
		||||
import { T } from "../../../translations";
 | 
			
		||||
import { defaultBuildingVariant } from "../../meta_building";
 | 
			
		||||
import { enumHubGoalRewards } from "../../tutorial_goals";
 | 
			
		||||
import { BaseHUDPart } from "../base_hud_part";
 | 
			
		||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
 | 
			
		||||
import { enumHubGoalRewardsToContentUnlocked } from "../../tutorial_goals_mappings";
 | 
			
		||||
import { InputReceiver } from "../../../core/input_receiver";
 | 
			
		||||
 | 
			
		||||
export class HUDUnlockNotification extends BaseHUDPart {
 | 
			
		||||
    initialize() {
 | 
			
		||||
        this.visible = false;
 | 
			
		||||
 | 
			
		||||
        this.domAttach = new DynamicDomAttach(this.root, this.element, {
 | 
			
		||||
            timeToKeepSeconds: 0,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (!(G_IS_DEV && globalConfig.debug.disableUnlockDialog)) {
 | 
			
		||||
            this.root.signals.storyGoalCompleted.add(this.showForLevel, this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.buttonShowTimeout = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    createElements(parent) {
 | 
			
		||||
        this.inputReciever = new InputReceiver("unlock-notification");
 | 
			
		||||
 | 
			
		||||
        this.element = makeDiv(parent, "ingame_HUD_UnlockNotification", ["noBlur"]);
 | 
			
		||||
 | 
			
		||||
        const dialog = makeDiv(this.element, null, ["dialog"]);
 | 
			
		||||
 | 
			
		||||
        this.elemTitle = makeDiv(dialog, null, ["title"]);
 | 
			
		||||
        this.elemSubTitle = makeDiv(dialog, null, ["subTitle"], T.ingame.levelCompleteNotification.completed);
 | 
			
		||||
 | 
			
		||||
        this.elemContents = makeDiv(dialog, null, ["contents"]);
 | 
			
		||||
 | 
			
		||||
        this.btnClose = document.createElement("button");
 | 
			
		||||
        this.btnClose.classList.add("close", "styledButton");
 | 
			
		||||
        this.btnClose.innerText = T.ingame.levelCompleteNotification.buttonNextLevel;
 | 
			
		||||
        dialog.appendChild(this.btnClose);
 | 
			
		||||
 | 
			
		||||
        this.trackClicks(this.btnClose, this.requestClose);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {number} level
 | 
			
		||||
     * @param {enumHubGoalRewards} reward
 | 
			
		||||
     */
 | 
			
		||||
    showForLevel(level, reward) {
 | 
			
		||||
        this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
 | 
			
		||||
        this.elemTitle.innerText = T.ingame.levelCompleteNotification.levelTitle.replace(
 | 
			
		||||
            "<level>",
 | 
			
		||||
            ("" + level).padStart(2, "0")
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        const rewardName = T.storyRewards[reward].title;
 | 
			
		||||
 | 
			
		||||
        let html = `
 | 
			
		||||
        <div class="rewardName">
 | 
			
		||||
            ${T.ingame.levelCompleteNotification.unlockText.replace("<reward>", rewardName)}
 | 
			
		||||
        </div>
 | 
			
		||||
        
 | 
			
		||||
        <div class="rewardDesc">
 | 
			
		||||
            ${T.storyRewards[reward].desc}
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        `;
 | 
			
		||||
 | 
			
		||||
        html += "<div class='images'>";
 | 
			
		||||
        const gained = enumHubGoalRewardsToContentUnlocked[reward];
 | 
			
		||||
        if (gained) {
 | 
			
		||||
            gained.forEach(([metaBuildingClass, variant]) => {
 | 
			
		||||
                const metaBuilding = gMetaBuildingRegistry.findByClass(metaBuildingClass);
 | 
			
		||||
                html += `<div class="buildingExplanation" data-icon="building_tutorials/${
 | 
			
		||||
                    metaBuilding.getId() + (variant === defaultBuildingVariant ? "" : "-" + variant)
 | 
			
		||||
                }.png"></div>`;
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        html += "</div>";
 | 
			
		||||
 | 
			
		||||
        this.elemContents.innerHTML = html;
 | 
			
		||||
        this.visible = true;
 | 
			
		||||
        this.root.soundProxy.playUi(SOUNDS.levelComplete);
 | 
			
		||||
 | 
			
		||||
        if (this.buttonShowTimeout) {
 | 
			
		||||
            clearTimeout(this.buttonShowTimeout);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.element.querySelector("button.close").classList.remove("unlocked");
 | 
			
		||||
 | 
			
		||||
        if (this.root.app.settings.getAllSettings().offerHints) {
 | 
			
		||||
            this.buttonShowTimeout = setTimeout(
 | 
			
		||||
                () => this.element.querySelector("button.close").classList.add("unlocked"),
 | 
			
		||||
                G_IS_DEV ? 100 : 5000
 | 
			
		||||
            );
 | 
			
		||||
        } else {
 | 
			
		||||
            this.element.querySelector("button.close").classList.add("unlocked");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cleanup() {
 | 
			
		||||
        this.root.app.inputMgr.makeSureDetached(this.inputReciever);
 | 
			
		||||
        if (this.buttonShowTimeout) {
 | 
			
		||||
            clearTimeout(this.buttonShowTimeout);
 | 
			
		||||
            this.buttonShowTimeout = null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    isBlockingOverlay() {
 | 
			
		||||
        return this.visible;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    requestClose() {
 | 
			
		||||
        this.root.app.adProvider.showVideoAd().then(() => {
 | 
			
		||||
            this.close();
 | 
			
		||||
 | 
			
		||||
            if (!this.root.app.settings.getAllSettings().offerHints) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (this.root.hubGoals.level === 3) {
 | 
			
		||||
                const { showUpgrades } = this.root.hud.parts.dialogs.showInfo(
 | 
			
		||||
                    T.dialogs.upgradesIntroduction.title,
 | 
			
		||||
                    T.dialogs.upgradesIntroduction.desc,
 | 
			
		||||
                    ["showUpgrades:good:timeout"]
 | 
			
		||||
                );
 | 
			
		||||
                showUpgrades.add(() => this.root.hud.parts.shop.show());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (this.root.hubGoals.level === 5) {
 | 
			
		||||
                const { showKeybindings } = this.root.hud.parts.dialogs.showInfo(
 | 
			
		||||
                    T.dialogs.keybindingsIntroduction.title,
 | 
			
		||||
                    T.dialogs.keybindingsIntroduction.desc,
 | 
			
		||||
                    ["showKeybindings:misc", "ok:good:timeout"]
 | 
			
		||||
                );
 | 
			
		||||
                showKeybindings.add(() => this.root.gameState.goToKeybindings());
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    close() {
 | 
			
		||||
        this.root.app.inputMgr.makeSureDetached(this.inputReciever);
 | 
			
		||||
        if (this.buttonShowTimeout) {
 | 
			
		||||
            clearTimeout(this.buttonShowTimeout);
 | 
			
		||||
            this.buttonShowTimeout = null;
 | 
			
		||||
        }
 | 
			
		||||
        this.visible = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    update() {
 | 
			
		||||
        this.domAttach.update(this.visible);
 | 
			
		||||
        if (!this.visible && this.buttonShowTimeout) {
 | 
			
		||||
            clearTimeout(this.buttonShowTimeout);
 | 
			
		||||
            this.buttonShowTimeout = null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user