diff --git a/res/puzzle_dlc_logo.png b/res/puzzle_dlc_logo.png new file mode 100644 index 00000000..1c430c82 Binary files /dev/null and b/res/puzzle_dlc_logo.png differ diff --git a/res_raw/sounds/music/puzzle-full.mp3 b/res_raw/sounds/music/puzzle-full.mp3 new file mode 100644 index 00000000..6ea6f544 Binary files /dev/null and b/res_raw/sounds/music/puzzle-full.mp3 differ diff --git a/src/css/ingame_hud/buildings_toolbar.scss b/src/css/ingame_hud/buildings_toolbar.scss index 4023f955..d75181dc 100644 --- a/src/css/ingame_hud/buildings_toolbar.scss +++ b/src/css/ingame_hud/buildings_toolbar.scss @@ -1,159 +1,164 @@ -.ingame_buildingsToolbar { - position: absolute; - @include S(bottom, 5px); - left: 50%; - transform: translateX(-50%); - - display: grid; - grid-template-rows: auto auto; - justify-items: center; - - background: transparent; - transition: transform 120ms ease-in-out; - will-change: transform; - - &:not(.visible) { - transform: translateX(-50%) translateY(#{D(100px)}); - } - - .buildings { - display: grid; - grid-auto-flow: column; - justify-items: center; - align-self: center; - grid-row: 2 / 3; - - background-color: rgba(240, 241, 243, 0.5); - @include S(border-radius, $globalBorderRadius); - - @include DarkThemeOverride { - background-color: rgba(darken($darkModeGameBackground, 15), 0.95); - } - - &.secondary { - grid-row: 1 / 2; - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - - .building { - .icon { - @include S(width, 30px); - @include S(height, 22px); - background-size: 45%; - } - &:not(.unlocked) { - &::before { - background-size: #{D(13px)}; - } - } - &.unlocked { - .puzzle-lock { - & { - /* @load-async */ - background: uiResource("locked_building.png") center center / #{D(11px)} #{D( - 11px - )} no-repeat; - } - - @include S(margin-left, 14px); - bottom: 67px; - } - } - } - } - - .building { - .icon { - color: $accentColorDark; - display: flex; - flex-direction: column-reverse; - position: relative; - align-items: center; - justify-content: center; - @include S(padding, 5px); - @include S(padding-bottom, 1px); - @include S(width, 35px); - @include S(height, 37px); - @include S(border-radius, $globalBorderRadius); - - background: center center / 70% no-repeat; - } - - &:not(.unlocked) { - .icon { - @include S(width, 20px); - opacity: 0.15; - } - &.editor { - .icon { - pointer-events: all; - cursor: pointer; - &:hover { - background-color: rgba(22, 30, 68, 0.1); - } - } - } - &:not(.editor) { - .icon { - background-image: uiResource("locked_building.png") !important; - } - } - } - - &.unlocked { - .icon { - pointer-events: all; - transition: all 50ms ease-in-out; - transition-property: background-color, transform; - - cursor: pointer; - &:hover { - background-color: rgba(30, 40, 90, 0.1); - } - - &.pressed { - transform: scale(0.9) !important; - } - - &.selected { - // transform: scale(1.05); - background-color: rgba(lighten($colorBlueBright, 9), 0.4); - - .keybinding { - color: #111; - } - } - } - - .puzzle-lock { - & { - /* @load-async */ - background: uiResource("locked_building.png") center center / #{D(14px)} #{D(14px)} - no-repeat; - } - - display: grid; - grid-auto-flow: column; - @include S(margin-top, 2px); - @include S(margin-left, 16px); - @include S(margin-bottom, 29px); - - position: absolute; - bottom: 20px; - transition: all 0.12s ease-in-out; - transition-property: opacity, transform; - - cursor: pointer; - pointer-events: all; - - @include S(width, 14px); - @include S(height, 14px); - - &:hover { - opacity: 0.5; - } - } - } - } - } -} +.ingame_buildingsToolbar { + position: absolute; + @include S(bottom, 5px); + left: 50%; + transform: translateX(-50%); + + display: grid; + grid-template-rows: auto auto; + justify-items: center; + + background: transparent; + transition: transform 120ms ease-in-out; + will-change: transform; + + &:not(.visible) { + transform: translateX(-50%) translateY(#{D(100px)}); + } + + .buildings { + display: grid; + grid-auto-flow: column; + justify-items: center; + align-self: center; + grid-row: 2 / 3; + + background-color: rgba(240, 241, 243, 0.5); + @include S(border-radius, $globalBorderRadius); + + @include DarkThemeOverride { + background-color: rgba(darken($darkModeGameBackground, 15), 0.95); + } + + &.secondary { + grid-row: 1 / 2; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + + .building { + .icon { + @include S(width, 30px); + @include S(height, 22px); + background-size: 45%; + } + + &:not(.unlocked) { + &::before { + background-size: #{D(13px)}; + } + } + + &.unlocked { + .puzzle-lock { + & { + /* @load-async */ + background: uiResource("locked_building.png") center center / #{D(11px)} #{D( + 11px + )} no-repeat; + } + + @include S(margin-left, 14px); + bottom: 67px; + } + } + } + } + + .building { + display: flex; + @include S(width, 40px); + position: relative; + @include S(height, 40px); + .icon { + color: $accentColorDark; + display: flex; + flex-direction: column-reverse; + position: relative; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + padding: 0; + margin: 0; + @include S(border-radius, $globalBorderRadius); + + background: center center / 70% no-repeat; + } + + &:not(.unlocked) { + @include S(width, 25px); + .icon { + opacity: 0.15; + } + &.editor { + .icon { + pointer-events: all; + cursor: pointer; + &:hover { + background-color: rgba(22, 30, 68, 0.1); + } + } + } + &:not(.editor) { + .icon { + background-image: uiResource("locked_building.png") !important; + } + } + } + + &.unlocked { + .icon { + pointer-events: all; + transition: all 50ms ease-in-out; + transition-property: background-color, transform; + cursor: pointer; + + &:hover { + background-color: rgba(30, 40, 90, 0.1); + } + + &.pressed { + transform: scale(0.9) !important; + } + + &.selected { + // transform: scale(1.05); + background-color: rgba(lighten($colorBlueBright, 9), 0.4); + + .keybinding { + color: #111; + } + } + } + + .puzzle-lock { + & { + /* @load-async */ + background: uiResource("locked_building.png") center center / #{D(14px)} #{D(14px)} + no-repeat; + } + + display: grid; + grid-auto-flow: column; + + position: absolute; + top: -30px; + left: 50%; + transform: translateX(-50%) !important; + transition: all 0.12s ease-in-out; + transition-property: opacity, transform; + + cursor: pointer; + pointer-events: all; + + @include S(width, 14px); + @include S(height, 14px); + + &:hover { + opacity: 0.5; + } + } + } + } + } +} diff --git a/src/css/ingame_hud/puzzle_complete_notification.scss b/src/css/ingame_hud/puzzle_complete_notification.scss index b3b7bf14..5f36df82 100644 --- a/src/css/ingame_hud/puzzle_complete_notification.scss +++ b/src/css/ingame_hud/puzzle_complete_notification.scss @@ -96,12 +96,13 @@ @include S(height, 60px); @include S(margin, 0, 10px); box-sizing: border-box; - @include S(border-radius, $globalBorderRadius); + border-radius: 50%; transition: opacity 0.12s ease-in-out, background-color 0.12s ease-in-out; + @include IncreasedClickArea(0px); &.liked-yes { /* @load-async */ - background: uiResource("icons/puzzle_action_liked_yes.png") center center / 70% + background: uiResource("icons/puzzle_action_liked_yes.png") center 55% / 60% no-repeat; } @@ -110,7 +111,18 @@ } &.active { - background-color: #151118 !important; + background-color: $colorRedBright !important; + @include InlineAnimation(0.3s ease-in-out) { + 0% { + transform: scale(0); + } + 50% { + transform: scale(1.2); + } + 100% { + transform: scale(1); + } + } } &:not(.active) { opacity: 0.4; @@ -119,6 +131,26 @@ } } + > .buttonBar { + display: flex; + @include S(margin-top, 20px); + + button.continue { + background: #555; + @include S(margin-right, 10px); + } + + button.menu { + background-color: $colorGreenBright; + } + + > button { + @include S(min-width, 100px); + @include S(padding, 10px, 20px); + @include IncreasedClickArea(0px); + } + } + > .actions { position: absolute; @include S(bottom, 40px); @@ -135,14 +167,5 @@ } } } - - button.close { - border: 0; - position: relative; - @include S(margin-top, 15px); - background: $colorGreenBright; - @include Heading; - @include S(padding, 14px, 40px); - } } } diff --git a/src/css/states/main_menu.scss b/src/css/states/main_menu.scss index f46230a2..005ee7af 100644 --- a/src/css/states/main_menu.scss +++ b/src/css/states/main_menu.scss @@ -88,11 +88,7 @@ @include S(grid-column-gap, 10px); display: grid; - grid-template-columns: 1fr; - - &.demo { - grid-template-columns: 1fr 1fr; - } + grid-template-columns: 1fr 1fr; .standaloneBanner { background: rgb(255, 75, 84); @@ -223,9 +219,33 @@ } } + .puzzleContainer { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + background: #4cc98a; + grid-row: 1 / 2; + grid-column: 2 / 3; + @include S(padding, 20px); + @include S(border-radius, $globalBorderRadius); + > .dlcLogo { + @include S(width, 200px); + } + + > button { + @include S(margin-top, 20px); + @include Heading; + @include S(padding, 10px, 30px); + background-color: #333; + color: #fff; + } + } + .mainContainer { display: flex; align-items: center; + grid-row: 1 / 2; justify-content: center; flex-direction: column; background: #fafafa; diff --git a/src/js/core/state_manager.js b/src/js/core/state_manager.js index 3c49ada9..e0c04bba 100644 --- a/src/js/core/state_manager.js +++ b/src/js/core/state_manager.js @@ -90,9 +90,9 @@ export class StateManager { dialogParent.classList.add("modalDialogParent"); document.body.appendChild(dialogParent); + this.currentState.internalEnterCallback(payload); this.app.sound.playThemeMusic(this.currentState.getThemeMusic()); - this.currentState.internalEnterCallback(payload); this.currentState.onResized(this.app.screenWidth, this.app.screenHeight); this.app.analytics.trackStateEnter(key); diff --git a/src/js/game/hud/parts/base_toolbar.js b/src/js/game/hud/parts/base_toolbar.js index 4abace68..15faad66 100644 --- a/src/js/game/hud/parts/base_toolbar.js +++ b/src/js/game/hud/parts/base_toolbar.js @@ -40,7 +40,6 @@ export class HUDBaseToolbar extends BaseHUDPart { * element: HTMLElement, * index: number * puzzleLocked: boolean; - * class: typeof MetaBuilding, * }>} */ this.buildingHandles = {}; } @@ -136,7 +135,6 @@ export class HUDBaseToolbar extends BaseHUDPart { selected: false, index: i, puzzleLocked: false, - class: allBuildings[i], }; } diff --git a/src/js/game/hud/parts/puzzle_complete_notification.js b/src/js/game/hud/parts/puzzle_complete_notification.js index b0235417..f223c1d6 100644 --- a/src/js/game/hud/parts/puzzle_complete_notification.js +++ b/src/js/game/hud/parts/puzzle_complete_notification.js @@ -52,19 +52,23 @@ export class HUDPuzzleCompleteNotification extends BaseHUDPart { this.updateState(); }); + const buttonBar = document.createElement("div"); + buttonBar.classList.add("buttonBar"); + this.elemContents.appendChild(buttonBar); + this.continueBtn = document.createElement("button"); - this.continueBtn.classList.add("close", "styledButton"); + this.continueBtn.classList.add("continue", "styledButton"); this.continueBtn.innerText = T.ingame.puzzleCompletion.continueBtn; - dialog.appendChild(this.continueBtn); - - this.menuBtn = document.createElement("button"); - this.menuBtn.classList.add("close", "styledButton"); - this.menuBtn.innerText = T.ingame.puzzleCompletion.menuBtn; - dialog.appendChild(this.menuBtn); - + buttonBar.appendChild(this.continueBtn); this.trackClicks(this.continueBtn, () => { this.close(false); }); + + this.menuBtn = document.createElement("button"); + this.menuBtn.classList.add("menu", "styledButton"); + this.menuBtn.innerText = T.ingame.puzzleCompletion.menuBtn; + buttonBar.appendChild(this.menuBtn); + this.trackClicks(this.menuBtn, () => { this.close(true); }); diff --git a/src/js/game/hud/parts/puzzle_editor_review.js b/src/js/game/hud/parts/puzzle_editor_review.js index b358dc6d..68f5360c 100644 --- a/src/js/game/hud/parts/puzzle_editor_review.js +++ b/src/js/game/hud/parts/puzzle_editor_review.js @@ -163,7 +163,9 @@ export class HUDPuzzleEditorReview extends BaseHUDPart { const serialized = new PuzzleSerializer().generateDumpFromGameRoot(this.root); logger.log("Submitting puzzle, title=", title, "shortKey=", shortKey); - logger.log("Serialized data:", serialized); + if (G_IS_DEV) { + logger.log("Serialized data:", serialized); + } const closeLoading = this.root.hud.parts.dialogs.showLoadingDialog(T.puzzleMenu.submittingPuzzle); diff --git a/src/js/game/hud/parts/sandbox_controller.js b/src/js/game/hud/parts/sandbox_controller.js index c8104d99..3689fa36 100644 --- a/src/js/game/hud/parts/sandbox_controller.js +++ b/src/js/game/hud/parts/sandbox_controller.js @@ -152,9 +152,6 @@ export class HUDSandboxController extends BaseHUDPart { } toggle() { - if (!this.visible) { - return; - } this.visible = !this.visible; } diff --git a/src/js/game/modes/puzzle.js b/src/js/game/modes/puzzle.js index 3f5febf1..4bf3b1e6 100644 --- a/src/js/game/modes/puzzle.js +++ b/src/js/game/modes/puzzle.js @@ -7,9 +7,6 @@ import { types } from "../../savegame/serialization"; import { enumGameModeTypes, GameMode } from "../game_mode"; import { HUDPuzzleBackToMenu } from "../hud/parts/puzzle_back_to_menu"; import { HUDPuzzleDLCLogo } from "../hud/parts/puzzle_dlc_logo"; -import { gMetaBuildingRegistry } from "../../core/global_registries"; -import { MetaBalancerBuilding } from "../buildings/balancer"; -import { MetaUndergroundBeltBuilding } from "../buildings/underground_belt"; export class PuzzleGameMode extends GameMode { static getType() { diff --git a/src/js/game/modes/puzzle_play.js b/src/js/game/modes/puzzle_play.js index eafbd7ff..46480c51 100644 --- a/src/js/game/modes/puzzle_play.js +++ b/src/js/game/modes/puzzle_play.js @@ -29,6 +29,7 @@ import { HUDPuzzleCompleteNotification } from "../hud/parts/puzzle_complete_noti import { HUDPuzzlePlaySettings } from "../hud/parts/puzzle_play_settings"; import { MetaBlockBuilding } from "../buildings/block"; import { MetaBuilding } from "../meta_building"; +import { gMetaBuildingRegistry } from "../../core/global_registries"; const logger = createLogger("puzzle-play"); const copy = require("clipboard-copy"); @@ -47,7 +48,7 @@ export class PuzzlePlayGameMode extends PuzzleGameMode { super(root); /** @type {Array} */ - const excludedBuildings = [ + let excludedBuildings = [ MetaConstantProducerBuilding, MetaGoalAcceptorBuilding, MetaBlockBuilding, @@ -70,7 +71,22 @@ export class PuzzlePlayGameMode extends PuzzleGameMode { MetaTransistorBuilding, ]; - this.hiddenBuildings = excludedBuildings.concat(puzzle.game.excludedBuildings); + if (puzzle.game.excludedBuildings) { + /** + * @type {any} + */ + const puzzleHidden = puzzle.game.excludedBuildings + .map(id => { + if (!gMetaBuildingRegistry.hasId(id)) { + return; + } + return gMetaBuildingRegistry.findById(id).constructor; + }) + .filter(x => !!x); + excludedBuildings = excludedBuildings.concat(puzzleHidden); + } + + this.hiddenBuildings = excludedBuildings; this.additionalHudParts.puzzlePlayMetadata = HUDPuzzlePlayMetadata; this.additionalHudParts.puzzlePlaySettings = HUDPuzzlePlaySettings; diff --git a/src/js/platform/sound.js b/src/js/platform/sound.js index 9d5a8461..d43c76c2 100644 --- a/src/js/platform/sound.js +++ b/src/js/platform/sound.js @@ -35,6 +35,10 @@ export const MUSIC = { menu: "menu", }; +if (G_IS_STANDALONE || G_IS_DEV) { + MUSIC.puzzle = "puzzle-full"; +} + export class SoundInstanceInterface { constructor(key, url) { this.key = key; diff --git a/src/js/savegame/puzzle_serializer.js b/src/js/savegame/puzzle_serializer.js index 8a032e9a..c7bfa652 100644 --- a/src/js/savegame/puzzle_serializer.js +++ b/src/js/savegame/puzzle_serializer.js @@ -85,12 +85,13 @@ export class PuzzleSerializer { const handles = root.hud.parts.buildingsToolbar.buildingHandles; const ids = gMetaBuildingRegistry.getAllIds(); - /** @type {Array} */ + /** @type {Array} */ let excludedBuildings = []; for (let i = 0; i < ids.length; ++i) { const handle = handles[ids[i]]; if (handle && handle.puzzleLocked) { - excludedBuildings.push(handle.class); + // @ts-ignore + excludedBuildings.push(handle.metaBuilding.getId()); } } diff --git a/src/js/savegame/savegame_typedefs.js b/src/js/savegame/savegame_typedefs.js index 3abe8c21..c5e0e5c5 100644 --- a/src/js/savegame/savegame_typedefs.js +++ b/src/js/savegame/savegame_typedefs.js @@ -87,7 +87,7 @@ import { MetaBuilding } from "../game/meta_building"; * version: number; * bounds: { w: number; h: number; }, * buildings: (PuzzleGameBuildingGoal | PuzzleGameBuildingConstantProducer | PuzzleGameBuildingBlock)[], - * excludedBuildings: Array, + * excludedBuildings: Array, * }} PuzzleGameData */ diff --git a/src/js/states/ingame.js b/src/js/states/ingame.js index 9ab38dd1..0dd6c72a 100644 --- a/src/js/states/ingame.js +++ b/src/js/states/ingame.js @@ -104,6 +104,9 @@ export class InGameState extends GameState { } getThemeMusic() { + if (this.creationPayload.gameModeId && this.creationPayload.gameModeId.includes("puzzle")) { + return MUSIC.puzzle; + } return MUSIC.theme; } diff --git a/src/js/states/main_menu.js b/src/js/states/main_menu.js index a4e19453..25d93d7a 100644 --- a/src/js/states/main_menu.js +++ b/src/js/states/main_menu.js @@ -15,7 +15,6 @@ import { startFileChoose, waitNextFrame, } from "../core/utils"; -import { enumGameModeIds } from "../game/game_mode"; import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs"; import { getApplicationSettingById } from "../profile/application_settings"; import { T } from "../translations"; @@ -83,9 +82,19 @@ export class MainMenuState extends GameState { }
-
-
-
+ + ${ + // @TODO: Only display if DLC is owned, otherwise show ad for store page + showDemoBadges + ? "" + : ` +
+ + +
` + }