mirror of
				https://github.com/tobspr/shapez.io.git
				synced 2025-06-13 13:04:03 +00:00 
			
		
		
		
	Ingame achievement viewer
This commit is contained in:
		
							parent
							
								
									45cef34f02
								
							
						
					
					
						commit
						7499737605
					
				@ -7,7 +7,7 @@
 | 
			
		||||
    "license": "MIT",
 | 
			
		||||
    "private": true,
 | 
			
		||||
    "scripts": {
 | 
			
		||||
        "dev": "cd gulp && yarn gulp main.serveDev",
 | 
			
		||||
        "dev": "cd gulp && yarn gulp",
 | 
			
		||||
        "devStandalone": "cd gulp && yarn gulp main.serveStandalone",
 | 
			
		||||
        "tslint": "cd src/js && tsc",
 | 
			
		||||
        "lint": "eslint src/js",
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										121
									
								
								src/css/ingame_hud/achievements.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								src/css/ingame_hud/achievements.scss
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,121 @@
 | 
			
		||||
#ingame_HUD_Achievements {
 | 
			
		||||
    .content {
 | 
			
		||||
        @include S(padding-right, 10px);
 | 
			
		||||
        display: flex;
 | 
			
		||||
        flex-direction: column;
 | 
			
		||||
        @include S(width, 500px);
 | 
			
		||||
 | 
			
		||||
        .achievement {
 | 
			
		||||
            display: grid;
 | 
			
		||||
            grid-template-columns: auto 1fr auto;
 | 
			
		||||
            background: #eee;
 | 
			
		||||
            @include S(border-radius, $globalBorderRadius);
 | 
			
		||||
            @include S(margin-bottom, 4px);
 | 
			
		||||
            @include S(padding, 5px, 10px);
 | 
			
		||||
            @include S(grid-row-gap, 1px);
 | 
			
		||||
            @include S(height, 85px);
 | 
			
		||||
            grid-template-rows: #{D(20px)} auto;
 | 
			
		||||
 | 
			
		||||
            &.hidden {
 | 
			
		||||
                display: none;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            &:last-child {
 | 
			
		||||
                margin-bottom: 0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @include DarkThemeOverride {
 | 
			
		||||
                background: $darkModeControlsBackground;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            .icon {
 | 
			
		||||
                @include S(width, 70px);
 | 
			
		||||
                @include S(height, 70px);
 | 
			
		||||
                background: center center / 80% no-repeat;
 | 
			
		||||
                align-self: center;
 | 
			
		||||
                justify-self: center;
 | 
			
		||||
                grid-column: 1;
 | 
			
		||||
                grid-row: 1 / 4;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            .info {
 | 
			
		||||
                display: flex;
 | 
			
		||||
                flex-direction: column;
 | 
			
		||||
                justify-content: center;
 | 
			
		||||
                grid-column: 2;
 | 
			
		||||
                grid-row: 2 / 3;
 | 
			
		||||
                .title {
 | 
			
		||||
                    @include PlainText;
 | 
			
		||||
                    display: flex;
 | 
			
		||||
                    align-items: center;
 | 
			
		||||
                    flex-direction: row-reverse;
 | 
			
		||||
                    justify-content: flex-end;
 | 
			
		||||
                    color: $colorGreenBright;
 | 
			
		||||
                }
 | 
			
		||||
                .description {
 | 
			
		||||
                    @include PlainText;
 | 
			
		||||
                    color: lighten($colorGreenBright, 20);
 | 
			
		||||
                    align-self: start;
 | 
			
		||||
                    justify-self: end;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            button.reset {
 | 
			
		||||
                grid-column: 3;
 | 
			
		||||
                grid-row: 2;
 | 
			
		||||
                align-self: center;
 | 
			
		||||
                justify-self: end;
 | 
			
		||||
 | 
			
		||||
                transition: all 0.2s ease-in-out;
 | 
			
		||||
                transition-property: background-color, opacity;
 | 
			
		||||
 | 
			
		||||
                background-color: $colorGreenBright;
 | 
			
		||||
                color: #fff;
 | 
			
		||||
 | 
			
		||||
                &:hover {
 | 
			
		||||
                    background-color: lighten($colorGreenBright, 10);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            &.reset {
 | 
			
		||||
                button.reset {
 | 
			
		||||
                    background-color: $colorRedBright;
 | 
			
		||||
 | 
			
		||||
                    &:hover {
 | 
			
		||||
                        background-color: darken($colorRedBright, 10);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                .info {
 | 
			
		||||
                    .title {
 | 
			
		||||
                        font-weight: bold;
 | 
			
		||||
                        color: $colorRedBright;
 | 
			
		||||
                    }
 | 
			
		||||
                    .description {
 | 
			
		||||
                        color: lighten($colorRedBright, 10);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            &:not(.unlocked) {
 | 
			
		||||
                button.reset {
 | 
			
		||||
                    background-color: #aaa;
 | 
			
		||||
                    cursor: default;
 | 
			
		||||
                    pointer-events: none;
 | 
			
		||||
                    opacity: 0.3;
 | 
			
		||||
                }
 | 
			
		||||
                .info {
 | 
			
		||||
                    .title {
 | 
			
		||||
                        color: black;
 | 
			
		||||
                        @include DarkThemeOverride {
 | 
			
		||||
                            color: #fff;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    .description {
 | 
			
		||||
                        color: #aaa;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -8,6 +8,10 @@
 | 
			
		||||
 | 
			
		||||
    backdrop-filter: blur(D(1px));
 | 
			
		||||
 | 
			
		||||
    .stats ~ .achievements {
 | 
			
		||||
        grid-column: 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    > button,
 | 
			
		||||
    > .button {
 | 
			
		||||
        @include PlainText;
 | 
			
		||||
@ -53,12 +57,20 @@
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &.achievements {
 | 
			
		||||
            grid-column: 3;
 | 
			
		||||
            & {
 | 
			
		||||
                /* @load-async */
 | 
			
		||||
                background-image: uiResource("icons/achievements.png");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &.save {
 | 
			
		||||
            & {
 | 
			
		||||
                /* @load-async */
 | 
			
		||||
                background-image: uiResource("icons/save.png");
 | 
			
		||||
            }
 | 
			
		||||
            grid-column: 3;
 | 
			
		||||
            grid-column: 4;
 | 
			
		||||
            @include MakeAnimationWrappedEvenOdd(0.5s ease-in-out) {
 | 
			
		||||
                0% {
 | 
			
		||||
                    transform: scale(1, 1);
 | 
			
		||||
@ -92,7 +104,7 @@
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &.settings {
 | 
			
		||||
            grid-column: 4;
 | 
			
		||||
            grid-column: 5;
 | 
			
		||||
            & {
 | 
			
		||||
                /* @load-async */
 | 
			
		||||
                background-image: uiResource("icons/settings_menu_settings.png");
 | 
			
		||||
 | 
			
		||||
@ -32,6 +32,7 @@
 | 
			
		||||
@import "states/puzzle_menu";
 | 
			
		||||
@import "states/mods";
 | 
			
		||||
 | 
			
		||||
@import "ingame_hud/achievements.scss";
 | 
			
		||||
@import "ingame_hud/buildings_toolbar";
 | 
			
		||||
@import "ingame_hud/building_placer";
 | 
			
		||||
@import "ingame_hud/beta_overlay";
 | 
			
		||||
@ -109,6 +110,7 @@ ingame_HUD_SandboxController,
 | 
			
		||||
ingame_HUD_BetaOverlay,
 | 
			
		||||
 | 
			
		||||
// Dialogs
 | 
			
		||||
ingame_HUD_Achievements,
 | 
			
		||||
ingame_HUD_Shop,
 | 
			
		||||
ingame_HUD_Statistics,
 | 
			
		||||
ingame_HUD_ShapeViewer,
 | 
			
		||||
 | 
			
		||||
@ -62,7 +62,7 @@ $buildingsAndVariants: belt, balancer, underground_belt, underground_belt-tier2,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
$icons: notification_saved, notification_success, notification_upgrade, notification_info,
 | 
			
		||||
    notification_warning, notification_error;
 | 
			
		||||
    notification_warning, notification_error, notification_achievement;
 | 
			
		||||
@each $icon in $icons {
 | 
			
		||||
    [data-icon="icons/#{$icon}.png"] {
 | 
			
		||||
        /* @load-async */
 | 
			
		||||
@ -70,6 +70,20 @@ $icons: notification_saved, notification_success, notification_upgrade, notifica
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
$achievements: reset, hidden, belt500Tiles, blueprint100k, blueprint1m, completeLvl26, cutShape, darkMode,
 | 
			
		||||
    destroy1000, irrelevantShape, level100, level50, logoBefore18, mam, mapMarkers15, noBeltUpgradesUntilBp,
 | 
			
		||||
    noInverseRotater, oldLevel17, openWires, paintShape, place5000Wires, placeBlueprint, placeBp1000, play1h,
 | 
			
		||||
    play10h, play20h, produceLogo, produceMsLogo, produceRocket, rotateShape, speedrunBp30, speedrunBp60,
 | 
			
		||||
    speedrunBp120, stack4Layers, stackShape, store100Unique, storeShape, throughputBp25, throughputBp50,
 | 
			
		||||
    throughputLogo25, throughputLogo50, throughputRocket10, throughputRocket20, trash1000, unlockWires,
 | 
			
		||||
    upgradesTier5, upgradesTier8;
 | 
			
		||||
@each $achievement in $achievements {
 | 
			
		||||
    [data-icon="achievements/#{$achievement}.png"] {
 | 
			
		||||
        /* @load-async */
 | 
			
		||||
        background-image: uiResource("res/ui/achievements/#{$achievement}.png") !important;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
$languages: en, de, cs, da, et, es-419, fr, it, pt-BR, sv, tr, el, ru, uk, zh-TW, zh-CN, nb, mt-MT, ar, nl, vi,
 | 
			
		||||
    th, hu, pl, ja, kor, no, pt-PT, fi, ro, he;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										214
									
								
								src/js/game/hud/parts/achievements.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								src/js/game/hud/parts/achievements.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,214 @@
 | 
			
		||||
import { InputReceiver } from "../../../core/input_receiver";
 | 
			
		||||
import { makeDiv } from "../../../core/utils";
 | 
			
		||||
import { ACHIEVEMENTS, HIDDEN_ACHIEVEMENTS } from "../../../platform/achievement_provider";
 | 
			
		||||
import { T } from "../../../translations";
 | 
			
		||||
import { KeyActionMapper, KEYMAPPINGS } from "../../key_action_mapper";
 | 
			
		||||
import { BaseHUDPart } from "../base_hud_part";
 | 
			
		||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
 | 
			
		||||
 | 
			
		||||
export class HUDAchievements extends BaseHUDPart {
 | 
			
		||||
    createElements(parent) {
 | 
			
		||||
        this.background = makeDiv(parent, "ingame_HUD_Achievements", ["ingameDialog"]);
 | 
			
		||||
 | 
			
		||||
        // DIALOG Inner / Wrapper
 | 
			
		||||
        this.dialogInner = makeDiv(this.background, null, ["dialogInner"]);
 | 
			
		||||
        this.title = makeDiv(this.dialogInner, null, ["title"], T.ingame.achievements.title);
 | 
			
		||||
        this.closeButton = makeDiv(this.title, null, ["closeButton"]);
 | 
			
		||||
        this.trackClicks(this.closeButton, this.close);
 | 
			
		||||
        this.contentDiv = makeDiv(this.dialogInner, null, ["content"]);
 | 
			
		||||
 | 
			
		||||
        this.resetElement = {};
 | 
			
		||||
 | 
			
		||||
        // Wrapper
 | 
			
		||||
        this.resetElement.elem = makeDiv(this.contentDiv, null, ["achievement", "reset", "unlocked"]);
 | 
			
		||||
 | 
			
		||||
        // Icon
 | 
			
		||||
        this.resetElement.icon = makeDiv(this.resetElement.elem, null, ["icon"]);
 | 
			
		||||
        this.resetElement.icon.setAttribute("data-icon", "achievements/reset.png");
 | 
			
		||||
 | 
			
		||||
        // Info
 | 
			
		||||
        this.resetElement.info = makeDiv(this.resetElement.elem, null, ["info"]);
 | 
			
		||||
 | 
			
		||||
        // Title
 | 
			
		||||
        this.resetElement.title = makeDiv(
 | 
			
		||||
            this.resetElement.info,
 | 
			
		||||
            null,
 | 
			
		||||
            ["title"],
 | 
			
		||||
            T.achievements.reset.title
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Description
 | 
			
		||||
        this.resetElement.description = makeDiv(
 | 
			
		||||
            this.resetElement.info,
 | 
			
		||||
            null,
 | 
			
		||||
            ["description"],
 | 
			
		||||
            T.achievements.reset.description
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Reset button
 | 
			
		||||
        this.resetElement.resetButton = document.createElement("button");
 | 
			
		||||
        this.resetElement.resetButton.classList.add("reset", "styledButton");
 | 
			
		||||
        this.resetElement.resetButton.innerText = T.ingame.achievements.buttonReset;
 | 
			
		||||
        this.resetElement.elem.appendChild(this.resetElement.resetButton);
 | 
			
		||||
        this.trackClicks(this.resetElement.resetButton, () => {
 | 
			
		||||
            const signals = this.root.hud.parts.dialogs.showWarning(
 | 
			
		||||
                T.dialogs.resetAchievements.title,
 | 
			
		||||
                T.dialogs.resetAchievements.description,
 | 
			
		||||
                ["cancel:bad:escape", "ok:good:enter"]
 | 
			
		||||
            );
 | 
			
		||||
            signals.ok.add(() => {
 | 
			
		||||
                // TODO: Fix buttons
 | 
			
		||||
                for (const achievementKey in ACHIEVEMENTS) {
 | 
			
		||||
                    if (!this.root.achievementProxy.provider.collection.map.has(achievementKey))
 | 
			
		||||
                        this.root.achievementProxy.provider.collection.lock(ACHIEVEMENTS[achievementKey]);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.achievementToElements = {};
 | 
			
		||||
 | 
			
		||||
        // ACHIEVEMENTS
 | 
			
		||||
        for (const achievementKey in ACHIEVEMENTS) {
 | 
			
		||||
            const handle = {};
 | 
			
		||||
 | 
			
		||||
            // Wrapper
 | 
			
		||||
            handle.elem = makeDiv(this.contentDiv, null, ["achievement"]);
 | 
			
		||||
 | 
			
		||||
            // Icon
 | 
			
		||||
            handle.icon = makeDiv(handle.elem, null, ["icon"]);
 | 
			
		||||
            handle.icon.setAttribute("data-icon", "achievements/" + achievementKey + ".png");
 | 
			
		||||
 | 
			
		||||
            // Info
 | 
			
		||||
            handle.info = makeDiv(handle.elem, null, ["info"]);
 | 
			
		||||
 | 
			
		||||
            // Title
 | 
			
		||||
            const title = makeDiv(handle.info, null, ["title"], T.achievements[achievementKey].title);
 | 
			
		||||
 | 
			
		||||
            // Description
 | 
			
		||||
            handle.elemDescription = makeDiv(
 | 
			
		||||
                handle.info,
 | 
			
		||||
                null,
 | 
			
		||||
                ["description"],
 | 
			
		||||
                T.achievements[achievementKey].description
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            // Reset button
 | 
			
		||||
            handle.resetButton = document.createElement("button");
 | 
			
		||||
            handle.resetButton.classList.add("reset", "styledButton");
 | 
			
		||||
            handle.resetButton.innerText = T.ingame.achievements.buttonReset;
 | 
			
		||||
            handle.elem.appendChild(handle.resetButton);
 | 
			
		||||
 | 
			
		||||
            this.trackClicks(handle.resetButton, () => {
 | 
			
		||||
                // TODO: Fix buttons
 | 
			
		||||
                this.root.achievementProxy.provider.collection.lock(ACHIEVEMENTS[achievementKey]);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            // Assign handle
 | 
			
		||||
            this.achievementToElements[achievementKey] = handle;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.hiddenElement = {};
 | 
			
		||||
        // Wrapper
 | 
			
		||||
        this.hiddenElement.hidden = makeDiv(this.contentDiv, null, ["achievement"]);
 | 
			
		||||
 | 
			
		||||
        // Icon
 | 
			
		||||
        this.hiddenElement.icon = makeDiv(this.hiddenElement.hidden, null, ["icon"]);
 | 
			
		||||
        this.hiddenElement.icon.setAttribute("data-icon", "achievements/hidden.png");
 | 
			
		||||
 | 
			
		||||
        // Info
 | 
			
		||||
        this.hiddenElement.info = makeDiv(this.hiddenElement.hidden, null, ["info"]);
 | 
			
		||||
 | 
			
		||||
        // Title
 | 
			
		||||
        this.hiddenElement.title = makeDiv(
 | 
			
		||||
            this.hiddenElement.info,
 | 
			
		||||
            null,
 | 
			
		||||
            ["title"],
 | 
			
		||||
            T.achievements.hidden.title
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Description
 | 
			
		||||
        this.hiddenElement.description = makeDiv(
 | 
			
		||||
            this.hiddenElement.info,
 | 
			
		||||
            null,
 | 
			
		||||
            ["description"],
 | 
			
		||||
            T.achievements.hidden.description.replace("<amountHidden>", HIDDEN_ACHIEVEMENTS.length + "")
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    renderStatus() {
 | 
			
		||||
        let unlocked = 0;
 | 
			
		||||
        let hidden = 0;
 | 
			
		||||
        for (const achievementKey in this.achievementToElements) {
 | 
			
		||||
            const handle = this.achievementToElements[achievementKey];
 | 
			
		||||
 | 
			
		||||
            //Check if user has achievement
 | 
			
		||||
            if (!this.root.achievementProxy.provider.collection.map.get(ACHIEVEMENTS[achievementKey])) {
 | 
			
		||||
                if (!handle.elem.classList.contains("unlocked")) handle.elem.classList.add("unlocked");
 | 
			
		||||
                if (handle.elem.classList.contains("hidden")) handle.elem.classList.remove("hidden");
 | 
			
		||||
                unlocked++;
 | 
			
		||||
            } else {
 | 
			
		||||
                if (handle.elem.classList.contains("unlocked")) handle.elem.classList.remove("unlocked");
 | 
			
		||||
 | 
			
		||||
                if (HIDDEN_ACHIEVEMENTS.includes(ACHIEVEMENTS[achievementKey])) {
 | 
			
		||||
                    if (!handle.elem.classList.contains("hidden")) handle.elem.classList.add("hidden");
 | 
			
		||||
                    hidden++;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.hiddenElement.description.innerHTML = T.achievements.hidden.description.replace(
 | 
			
		||||
            "<amountHidden>",
 | 
			
		||||
            hidden + ""
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        if (unlocked > 0) {
 | 
			
		||||
            if (!this.resetElement.elem.classList.contains("unlocked"))
 | 
			
		||||
                this.resetElement.elem.classList.add("unlocked");
 | 
			
		||||
        } else if (this.resetElement.elem.classList.contains("unlocked"))
 | 
			
		||||
            this.resetElement.elem.classList.remove("unlocked");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    initialize() {
 | 
			
		||||
        this.domAttach = new DynamicDomAttach(this.root, this.background, {
 | 
			
		||||
            attachClass: "visible",
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.inputReciever = new InputReceiver("achievements");
 | 
			
		||||
        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.menuOpenAchievements).add(this.close, this);
 | 
			
		||||
 | 
			
		||||
        this.close();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cleanup() {
 | 
			
		||||
        // Cleanup detectors
 | 
			
		||||
        for (const achievementKey in this.achievementToElements) {
 | 
			
		||||
            const handle = this.achievementToElements[achievementKey];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    show() {
 | 
			
		||||
        this.visible = true;
 | 
			
		||||
        this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    close() {
 | 
			
		||||
        this.visible = false;
 | 
			
		||||
        this.root.app.inputMgr.makeSureDetached(this.inputReciever);
 | 
			
		||||
        this.update();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    update() {
 | 
			
		||||
        this.domAttach.update(this.visible);
 | 
			
		||||
        if (this.visible) {
 | 
			
		||||
            this.renderStatus();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    isBlockingOverlay() {
 | 
			
		||||
        return this.visible;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -33,6 +33,12 @@ export class HUDGameMenu extends BaseHUDPart {
 | 
			
		||||
                visible: () =>
 | 
			
		||||
                    !this.root.app.settings.getAllSettings().offerHints || this.root.hubGoals.level >= 3,
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                id: "achievements",
 | 
			
		||||
                label: "Achievements",
 | 
			
		||||
                handler: () => this.root.hud.parts.achievements.show(),
 | 
			
		||||
                keybinding: KEYMAPPINGS.ingame.menuOpenAchievements,
 | 
			
		||||
            },
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        /** @type {Array<{
 | 
			
		||||
 | 
			
		||||
@ -10,6 +10,7 @@ export const enumNotificationType = {
 | 
			
		||||
    info: "info",
 | 
			
		||||
    warning: "warning",
 | 
			
		||||
    error: "error",
 | 
			
		||||
    achievement: "achievement",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const notificationDuration = 3;
 | 
			
		||||
@ -29,6 +30,10 @@ export class HUDNotifications extends BaseHUDPart {
 | 
			
		||||
        this.root.signals.gameSaved.add(() =>
 | 
			
		||||
            this.internalShowNotification(T.ingame.notifications.gameSaved, enumNotificationType.saved)
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        this.root.signals.achievementCompleted.add(key =>
 | 
			
		||||
            this.internalShowNotification(T.achievements[key].title, enumNotificationType.achievement)
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
@ -60,6 +60,7 @@ export const KEYMAPPINGS = {
 | 
			
		||||
    ingame: {
 | 
			
		||||
        menuOpenShop: { keyCode: keyToKeyCode("F") },
 | 
			
		||||
        menuOpenStats: { keyCode: keyToKeyCode("G") },
 | 
			
		||||
        menuOpenAchievements: { keyCode: keyToKeyCode("H") },
 | 
			
		||||
        menuClose: { keyCode: keyToKeyCode("Q") },
 | 
			
		||||
 | 
			
		||||
        toggleHud: { keyCode: KEYCODES.F2 },
 | 
			
		||||
 | 
			
		||||
@ -38,6 +38,7 @@ import { MetaItemProducerBuilding } from "../buildings/item_producer";
 | 
			
		||||
import { MOD_SIGNALS } from "../../mods/mod_signals";
 | 
			
		||||
import { finalGameShape, generateLevelsForVariant } from "./levels";
 | 
			
		||||
import { WEB_STEAM_SSO_AUTHENTICATED } from "../../core/steam_sso";
 | 
			
		||||
import { HUDAchievements } from "../hud/parts/achievements";
 | 
			
		||||
 | 
			
		||||
/** @typedef {{
 | 
			
		||||
 *   shape: string,
 | 
			
		||||
@ -350,6 +351,7 @@ export class RegularGameMode extends GameMode {
 | 
			
		||||
            tutorialVideoOffer: HUDTutorialVideoOffer,
 | 
			
		||||
            gameMenu: HUDGameMenu,
 | 
			
		||||
            constantSignalEdit: HUDConstantSignalEdit,
 | 
			
		||||
            achievements: HUDAchievements,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if (!IS_MOBILE) {
 | 
			
		||||
 | 
			
		||||
@ -189,6 +189,7 @@ export class GameRoot {
 | 
			
		||||
            // Called with an achievement key and necessary args to validate it can be unlocked.
 | 
			
		||||
            achievementCheck: /** @type {TypedSignal<[string, any]>} */ (new Signal()),
 | 
			
		||||
            bulkAchievementCheck: /** @type {TypedSignal<(string|any)[]>} */ (new Signal()),
 | 
			
		||||
            achievementCompleted: /** @type {TypedSignal<[string, any]>} */ (new Signal()),
 | 
			
		||||
 | 
			
		||||
            // Puzzle mode
 | 
			
		||||
            puzzleComplete: /** @type {TypedSignal<[]>} */ (new Signal()),
 | 
			
		||||
 | 
			
		||||
@ -149,6 +149,13 @@ export class PreloadState extends GameState {
 | 
			
		||||
                return this.app.settings.initialize();
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
            .then(() => this.setStatus("Initializing achievement storage"))
 | 
			
		||||
            .then(() => {
 | 
			
		||||
                if (this.app.achievementProvider.storage)
 | 
			
		||||
                    return this.app.achievementProvider.storage.initialize();
 | 
			
		||||
                else return Promise.resolve();
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
            .then(() => {
 | 
			
		||||
                // Initialize fullscreen
 | 
			
		||||
                if (this.app.platformWrapper.getSupportsFullscreen()) {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user