diff --git a/res/ui/building_icons/block.png b/res/ui/building_icons/block.png new file mode 100644 index 00000000..a6d914f6 Binary files /dev/null and b/res/ui/building_icons/block.png differ diff --git a/res/ui/building_tutorials/block.png b/res/ui/building_tutorials/block.png new file mode 100644 index 00000000..73925265 Binary files /dev/null and b/res/ui/building_tutorials/block.png differ diff --git a/res/ui/icons/puzzle_completion_rate.png b/res/ui/icons/puzzle_completion_rate.png new file mode 100644 index 00000000..2b07ce22 Binary files /dev/null and b/res/ui/icons/puzzle_completion_rate.png differ diff --git a/res_raw/sprites/blueprints/block.png b/res_raw/sprites/blueprints/block.png new file mode 100644 index 00000000..ff6107cf Binary files /dev/null and b/res_raw/sprites/blueprints/block.png differ diff --git a/res_raw/sprites/buildings/block.png b/res_raw/sprites/buildings/block.png new file mode 100644 index 00000000..3596f682 Binary files /dev/null and b/res_raw/sprites/buildings/block.png differ diff --git a/src/css/ingame_hud/puzzle_complete_notification.scss b/src/css/ingame_hud/puzzle_complete_notification.scss index eb7ddf6f..b3b7bf14 100644 --- a/src/css/ingame_hud/puzzle_complete_notification.scss +++ b/src/css/ingame_hud/puzzle_complete_notification.scss @@ -61,8 +61,6 @@ } > .contents { - @include S(width, 400px); - @include S(height, 170px); @include InlineAnimation(0.5s ease-in-out) { 0% { transform: translateX(-100vw); @@ -75,6 +73,7 @@ transform: translateX(-2vw); } } + display: flex; flex-direction: column; align-items: center; @@ -84,6 +83,7 @@ display: flex; flex-direction: column; @include S(margin-bottom, 10px); + @include SuperSmallText; > .buttons { display: flex; @@ -92,8 +92,8 @@ @include S(margin, 10px, 0); > button { - @include S(width, 40px); - @include S(height, 40px); + @include S(width, 60px); + @include S(height, 60px); @include S(margin, 0, 10px); box-sizing: border-box; @include S(border-radius, $globalBorderRadius); @@ -101,12 +101,7 @@ &.liked-yes { /* @load-async */ - background: uiResource("icons/puzzle_action_liked_yes.png") center center / 60% - no-repeat; - } - &.liked-no { - /* @load-async */ - background: uiResource("icons/puzzle_action_liked_no.png") center center / 60% + background: uiResource("icons/puzzle_action_liked_yes.png") center center / 70% no-repeat; } @@ -124,88 +119,30 @@ } } - > .stepDifficulty { - display: flex; - flex-direction: column; - align-items: center; - @include S(margin-bottom, 10px); + > .actions { + position: absolute; + @include S(bottom, 40px); - > .desc { + display: grid; + @include S(grid-gap, 15px); + grid-auto-flow: column; + + button { @include SuperSmallText; - opacity: 0.4; - @include S(margin-bottom, 4px); } - - > .shapes { - @include S(margin-top, 10px); - display: flex; - align-items: center; - - > .rating { - @include S(border-radius, $globalBorderRadius); - pointer-events: all; - cursor: pointer; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - - @include S(margin, 0, 5px); - @include S(width, 65px); - @include S(height, 50px); - - > canvas { - @include S(width, 30px); - @include S(height, 30px); - transition: opacity 0.12s ease-in-out, background-color 0.12s ease-in-out, - box-shadow 0.12s ease-in-out; - } - - > .description { - @include SuperSmallText; - white-space: nowrap; - } - - &.active { - background-color: #151118 !important; - box-shadow: 0 0 0 D(2px) #151118; - } - - &:not(.active) { - opacity: 0.4; - } - } + .report { + background-color: $accentColorDark; } } } - > .actions { - position: absolute; - @include S(bottom, 40px); - - display: grid; - @include S(grid-gap, 15px); - grid-auto-flow: column; - - button { - @include SuperSmallText; - } - .report { - background-color: $accentColorDark; - } - } - button.close { border: 0; position: relative; - @include S(margin-top, 30px); + @include S(margin-top, 15px); background: $colorGreenBright; - @include S(padding, 10px, 40px); - - &:not(.visible) { - opacity: 0; - pointer-events: none; - } + @include Heading; + @include S(padding, 14px, 40px); } } } diff --git a/src/css/resources.scss b/src/css/resources.scss index 158db23d..c3c6ea88 100644 --- a/src/css/resources.scss +++ b/src/css/resources.scss @@ -1,6 +1,6 @@ $buildings: belt, cutter, miner, mixer, painter, rotater, balancer, stacker, trash, underground_belt, wire, constant_signal, logic_gate, lever, filter, wire_tunnel, display, virtual_processor, reader, storage, - transistor, analyzer, comparator, item_producer, constant_producer, goal_acceptor; + transistor, analyzer, comparator, item_producer, constant_producer, goal_acceptor, block; @each $building in $buildings { [data-icon="building_icons/#{$building}.png"] { @@ -14,7 +14,7 @@ $buildingsAndVariants: belt, balancer, underground_belt, underground_belt-tier2, reader, rotater-rotate180, display, constant_signal, wire, wire_tunnel, logic_gate-or, logic_gate-not, logic_gate-xor, analyzer, virtual_processor-rotater, virtual_processor-unstacker, item_producer, constant_producer, virtual_processor-stacker, virtual_processor-painter, wire-second, painter, - painter-mirrored, comparator, goal_acceptor; + painter-mirrored, comparator, goal_acceptor, block; @each $building in $buildingsAndVariants { [data-icon="building_tutorials/#{$building}.png"] { /* @load-async */ diff --git a/src/css/states/puzzle_menu.scss b/src/css/states/puzzle_menu.scss index a5b4b509..9cc5a093 100644 --- a/src/css/states/puzzle_menu.scss +++ b/src/css/states/puzzle_menu.scss @@ -158,7 +158,6 @@ > .downloads { @include SuperSmallText; color: #000; - justify-self: start; font-weight: bold; @include S(margin-right, 5px); @include S(padding-left, 12px); @@ -170,8 +169,9 @@ & { /* @load-async */ - background: uiResource("icons/puzzle_plays.png") #{D(2px)} center / #{D(8px)} - #{D(8px)} no-repeat; + background: uiResource("icons/puzzle_plays.png") #{D(2px)} #{D(2.5px)} / #{D( + 8px + )} #{D(8px)} no-repeat; } } @@ -180,26 +180,35 @@ align-items: center; justify-content: center; color: #000; - justify-self: start; font-weight: bold; - @include S(padding-left, 12px); + @include S(padding-left, 14px); opacity: 0.7; @include DarkThemeInvert; & { /* @load-async */ - background: uiResource("icons/puzzle_upvotes.png") #{D(2px)} center / #{D( - 8px - )} #{D(8px)} no-repeat; + background: uiResource("icons/puzzle_upvotes.png") #{D(2px)} #{D(2.4px)} / #{D( + 9px + )} #{D(9px)} no-repeat; } } > .difficulty { - @include S(margin-top, 1px); - @include S(margin-right, 7px); - display: inline-flex; + @include SuperSmallText; align-items: center; justify-content: center; + color: #000; + font-weight: bold; + @include S(margin-right, 3px); + @include S(padding-left, 14px); + opacity: 0.7; + @include DarkThemeInvert; + + & { + /* @load-async */ + background: uiResource("icons/puzzle_completion_rate.png") #{D(1px)} #{D(2px)} / + #{D(10px)} #{D(10px)} no-repeat; + } } } diff --git a/src/js/game/buildings/block.js b/src/js/game/buildings/block.js new file mode 100644 index 00000000..d6499648 --- /dev/null +++ b/src/js/game/buildings/block.js @@ -0,0 +1,30 @@ +/* typehints:start */ +import { Entity } from "../entity"; +/* typehints:end */ + +import { MetaBuilding } from "../meta_building"; + +export class MetaBlockBuilding extends MetaBuilding { + constructor() { + super("block"); + } + + getSilhouetteColor() { + return "#333"; + } + + /** + * + * @param {import("../../savegame/savegame_serializer").GameRoot} root + * @returns + */ + getIsRemovable(root) { + return root.gameMode.getIsEditor(); + } + + /** + * Creates the entity at the given location + * @param {Entity} entity + */ + setupEntityComponents(entity) {} +} diff --git a/src/js/game/hud/parts/buildings_toolbar.js b/src/js/game/hud/parts/buildings_toolbar.js index 2881eece..994a70ed 100644 --- a/src/js/game/hud/parts/buildings_toolbar.js +++ b/src/js/game/hud/parts/buildings_toolbar.js @@ -17,6 +17,7 @@ import { MetaStorageBuilding } from "../../buildings/storage"; import { MetaItemProducerBuilding } from "../../buildings/item_producer"; import { MetaConstantProducerBuilding } from "../../buildings/constant_producer"; import { MetaGoalAcceptorBuilding } from "../../buildings/goal_acceptor"; +import { MetaBlockBuilding } from "../../buildings/block"; export class HUDBuildingsToolbar extends HUDBaseToolbar { constructor(root) { @@ -28,6 +29,7 @@ export class HUDBuildingsToolbar extends HUDBaseToolbar { MetaBalancerBuilding, MetaUndergroundBeltBuilding, MetaMinerBuilding, + MetaBlockBuilding, MetaCutterBuilding, MetaRotaterBuilding, MetaStackerBuilding, diff --git a/src/js/game/hud/parts/puzzle_complete_notification.js b/src/js/game/hud/parts/puzzle_complete_notification.js index 4b366243..4d68daa9 100644 --- a/src/js/game/hud/parts/puzzle_complete_notification.js +++ b/src/js/game/hud/parts/puzzle_complete_notification.js @@ -14,14 +14,6 @@ import { DynamicDomAttach } from "../dynamic_dom_attach"; import { ShapeItem } from "../../items/shape_item"; import { ShapeDefinition } from "../../shape_definition"; -export const PUZZLE_RATINGS = [ - new ColorItem(enumColors.red), - new ShapeItem(ShapeDefinition.fromShortKey("CuCuCuCu")), - new ShapeItem(ShapeDefinition.fromShortKey("WwWwWwWw")), - new ShapeItem(ShapeDefinition.fromShortKey(finalGameShape)), - new ShapeItem(ShapeDefinition.fromShortKey(rocketShape)), -]; - export class HUDPuzzleCompleteNotification extends BaseHUDPart { initialize() { this.visible = false; @@ -32,8 +24,7 @@ export class HUDPuzzleCompleteNotification extends BaseHUDPart { this.root.signals.puzzleComplete.add(this.show, this); - this.selectionLiked = false; - this.selectionDifficulty = null; + this.userDidLikePuzzle = false; this.timeOfCompletion = 0; } @@ -48,18 +39,6 @@ export class HUDPuzzleCompleteNotification extends BaseHUDPart { this.elemContents = makeDiv(dialog, null, ["contents"]); this.elemActions = makeDiv(dialog, null, ["actions"]); - const reportBtn = document.createElement("button"); - reportBtn.classList.add("styledButton", "report"); - reportBtn.innerHTML = T.ingame.puzzleEditorSettings.report; - this.elemActions.appendChild(reportBtn); - this.trackClicks(reportBtn, this.report); - - const shareBtn = document.createElement("button"); - shareBtn.classList.add("styledButton", "share"); - shareBtn.innerHTML = T.ingame.puzzleEditorSettings.share; - this.elemActions.appendChild(shareBtn); - this.trackClicks(shareBtn, this.share); - const stepLike = makeDiv(this.elemContents, null, ["step", "stepLike"]); makeDiv(stepLike, null, ["title"], T.ingame.puzzleCompletion.titleLike); @@ -69,45 +48,10 @@ export class HUDPuzzleCompleteNotification extends BaseHUDPart { this.buttonLikeYes.classList.add("liked-yes"); likeButtons.appendChild(this.buttonLikeYes); this.trackClicks(this.buttonLikeYes, () => { - this.selectionLiked = !this.selectionLiked; + this.userDidLikePuzzle = !this.userDidLikePuzzle; this.updateState(); }); - const stepDifficulty = makeDiv(this.elemContents, null, ["step", "stepDifficulty"]); - makeDiv(stepDifficulty, null, ["title"], T.ingame.puzzleCompletion.titleRating); - makeDiv(stepDifficulty, null, ["desc"], T.ingame.puzzleCompletion.titleRatingDesc); - - const shapeContainer = makeDiv(stepDifficulty, null, ["shapes"]); - - this.difficultyElements = []; - let index = 0; - for (const shape of PUZZLE_RATINGS) { - const localIndex = index; - - const elem = document.createElement("div"); - elem.classList.add("rating"); - shapeContainer.appendChild(elem); - - const canvas = document.createElement("canvas"); - canvas.width = 128; - canvas.height = 128; - const context = canvas.getContext("2d"); - shape.drawFullSizeOnCanvas(context, 128); - elem.appendChild(canvas); - - this.trackClicks(elem, () => { - this.selectionDifficulty = localIndex; - this.updateState(); - }); - this.difficultyElements.push(elem); - - const desc = document.createElement("div"); - desc.classList.add("description"); - desc.innerText = T.ingame.puzzleCompletion.difficulties[localIndex]; - elem.appendChild(desc); - ++index; - } - this.btnClose = document.createElement("button"); this.btnClose.classList.add("close", "styledButton"); this.btnClose.innerText = T.ingame.puzzleCompletion.buttonSubmit; @@ -116,26 +60,8 @@ export class HUDPuzzleCompleteNotification extends BaseHUDPart { this.trackClicks(this.btnClose, this.close); } - share() { - const mode = /** @type {PuzzlePlayGameMode} */ (this.root.gameMode); - mode.sharePuzzle(); - } - - report() { - const mode = /** @type {PuzzlePlayGameMode} */ (this.root.gameMode); - mode.reportPuzzle().then(() => this.close()); - } - updateState() { - this.buttonLikeYes.classList.toggle("active", this.selectionLiked === true); - this.difficultyElements.forEach((canvas, index) => - canvas.classList.toggle("active", index === this.selectionDifficulty) - ); - - this.btnClose.classList.toggle( - "visible", - typeof this.selectionDifficulty === "number" && typeof this.selectionLiked === "boolean" - ); + this.buttonLikeYes.classList.toggle("active", this.userDidLikePuzzle === true); } show() { @@ -155,7 +81,7 @@ export class HUDPuzzleCompleteNotification extends BaseHUDPart { close() { /** @type {PuzzlePlayGameMode} */ (this.root.gameMode) - .trackCompleted(this.selectionLiked, this.selectionDifficulty, Math.round(this.timeOfCompletion)) + .trackCompleted(this.userDidLikePuzzle, Math.round(this.timeOfCompletion)) .then(() => { // this.root.gameState.moveToState("PuzzleMenuState"); this.visible = false; diff --git a/src/js/game/hud/parts/puzzle_play_metadata.js b/src/js/game/hud/parts/puzzle_play_metadata.js index a29f9779..3550a1e6 100644 --- a/src/js/game/hud/parts/puzzle_play_metadata.js +++ b/src/js/game/hud/parts/puzzle_play_metadata.js @@ -32,31 +32,16 @@ export class HUDPuzzlePlayMetadata extends BaseHUDPart {
${puzzle.meta.shortKey}
-
- - ${ - puzzle.meta.difficulty - ? puzzle.meta.difficulty.toFixed(2) + - " (" + - T.ingame.puzzleCompletion.difficulties[Math.round(puzzle.meta.difficulty)] + - ")" - : T.puzzleMenu.difficultyNotDetermined - } -
- ${ - puzzle.meta.averageTime - ? formatSeconds(puzzle.meta.averageTime) - : T.puzzleMenu.difficultyNotDetermined - } + ${puzzle.meta.averageTime ? formatSeconds(puzzle.meta.averageTime) : "-"}
${ - puzzle.meta.downloads > 10 + puzzle.meta.downloads > 0 ? ((puzzle.meta.completions / puzzle.meta.downloads) * 100.0).toFixed(1) + "%" - : T.puzzleMenu.difficultyNotDetermined + : "-" }
diff --git a/src/js/game/key_action_mapper.js b/src/js/game/key_action_mapper.js index 52ba4ad6..41208d13 100644 --- a/src/js/game/key_action_mapper.js +++ b/src/js/game/key_action_mapper.js @@ -52,6 +52,7 @@ export const KEYMAPPINGS = { // Puzzle buildings constant_producer: { keyCode: key("H") }, goal_acceptor: { keyCode: key("N") }, + block: { keyCode: key("4") }, // Primary Toolbar belt: { keyCode: key("1") }, diff --git a/src/js/game/meta_building_registry.js b/src/js/game/meta_building_registry.js index 90a253f0..0c93153d 100644 --- a/src/js/game/meta_building_registry.js +++ b/src/js/game/meta_building_registry.js @@ -4,6 +4,7 @@ import { T } from "../translations"; import { MetaAnalyzerBuilding } from "./buildings/analyzer"; import { enumBalancerVariants, MetaBalancerBuilding } from "./buildings/balancer"; import { MetaBeltBuilding } from "./buildings/belt"; +import { MetaBlockBuilding } from "./buildings/block"; import { MetaComparatorBuilding } from "./buildings/comparator"; import { MetaConstantProducerBuilding } from "./buildings/constant_producer"; import { MetaConstantSignalBuilding } from "./buildings/constant_signal"; @@ -63,6 +64,7 @@ export function initMetaBuildingRegistry() { gMetaBuildingRegistry.register(MetaComparatorBuilding); gMetaBuildingRegistry.register(MetaItemProducerBuilding); gMetaBuildingRegistry.register(MetaConstantProducerBuilding); + gMetaBuildingRegistry.register(MetaBlockBuilding); // Belt registerBuildingVariant(1, MetaBeltBuilding, defaultBuildingVariant, 0); @@ -175,6 +177,9 @@ export function initMetaBuildingRegistry() { // Goal acceptor registerBuildingVariant(63, MetaGoalAcceptorBuilding); + // Block + registerBuildingVariant(64, MetaBlockBuilding); + // Propagate instances for (const key in gBuildingVariants) { gBuildingVariants[key].metaInstance = gMetaBuildingRegistry.findByClass( diff --git a/src/js/game/modes/puzzle_play.js b/src/js/game/modes/puzzle_play.js index e5f0da03..e4d05076 100644 --- a/src/js/game/modes/puzzle_play.js +++ b/src/js/game/modes/puzzle_play.js @@ -21,13 +21,13 @@ import { MetaComparatorBuilding } from "../buildings/comparator"; import { MetaTransistorBuilding } from "../buildings/transistor"; import { MetaConstantProducerBuilding } from "../buildings/constant_producer"; import { MetaGoalAcceptorBuilding } from "../buildings/goal_acceptor"; -import { HUDConstantSignalEdit } from "../hud/parts/constant_signal_edit"; import { PuzzleSerializer } from "../../savegame/puzzle_serializer"; import { T } from "../../translations"; import { HUDPuzzlePlayMetadata } from "../hud/parts/puzzle_play_metadata"; import { createLogger } from "../../core/logging"; import { HUDPuzzleCompleteNotification } from "../hud/parts/puzzle_complete_notification"; import { HUDPuzzlePlaySettings } from "../hud/parts/puzzle_play_settings"; +import { MetaBlockBuilding } from "../buildings/block"; const logger = createLogger("puzzle-play"); const copy = require("clipboard-copy"); @@ -48,6 +48,7 @@ export class PuzzlePlayGameMode extends PuzzleGameMode { this.hiddenBuildings = [ MetaConstantProducerBuilding, MetaGoalAcceptorBuilding, + MetaBlockBuilding, MetaStorageBuilding, MetaReaderBuilding, @@ -106,16 +107,14 @@ export class PuzzlePlayGameMode extends PuzzleGameMode { /** * * @param {boolean} liked - * @param {number} difficulty * @param {number} time */ - trackCompleted(liked, difficulty, time) { + trackCompleted(liked, time) { const closeLoading = this.root.hud.parts.dialogs.showLoadingDialog(); return this.root.app.clientApi .apiCompletePuzzle(this.puzzle.meta.id, { time, - difficulty, liked, }) .catch(err => { diff --git a/src/js/game/modes/regular.js b/src/js/game/modes/regular.js index ce66eea6..ee76ba84 100644 --- a/src/js/game/modes/regular.js +++ b/src/js/game/modes/regular.js @@ -36,6 +36,7 @@ import { HUDPartTutorialHints } from "../hud/parts/tutorial_hints"; import { HUDInteractiveTutorial } from "../hud/parts/interactive_tutorial"; import { HUDSandboxController } from "../hud/parts/sandbox_controller"; import { queryParamOptions } from "../../core/query_parameters"; +import { MetaBlockBuilding } from "../buildings/block"; /** @typedef {{ * shape: string, @@ -581,7 +582,7 @@ export class RegularGameMode extends GameMode { this.additionalHudParts.sandboxController = HUDSandboxController; } - this.hiddenBuildings = [MetaConstantProducerBuilding, MetaGoalAcceptorBuilding]; + this.hiddenBuildings = [MetaConstantProducerBuilding, MetaGoalAcceptorBuilding, MetaBlockBuilding]; } /** diff --git a/src/js/platform/api.js b/src/js/platform/api.js index 0933ac2a..cbecfb15 100644 --- a/src/js/platform/api.js +++ b/src/js/platform/api.js @@ -168,7 +168,6 @@ export class ClientAPI { * @param {number} puzzleId * @param {object} payload * @param {number} payload.time - * @param {number} payload.difficulty * @param {boolean} payload.liked * @returns {Promise<{ success: true }>} */ diff --git a/src/js/platform/browser/game_analytics.js b/src/js/platform/browser/game_analytics.js index a3947be6..65fc5080 100644 --- a/src/js/platform/browser/game_analytics.js +++ b/src/js/platform/browser/game_analytics.js @@ -3,6 +3,7 @@ import { createLogger } from "../../core/logging"; import { queryParamOptions } from "../../core/query_parameters"; import { BeltComponent } from "../../game/components/belt"; import { StaticMapEntityComponent } from "../../game/components/static_map_entity"; +import { RegularGameMode } from "../../game/modes/regular"; import { GameRoot } from "../../game/root"; import { InGameState } from "../../states/ingame"; import { GameAnalyticsInterface } from "../game_analytics"; @@ -163,6 +164,10 @@ export class ShapezGameAnalytics extends GameAnalyticsInterface { return; } + if (!(root.gameMode instanceof RegularGameMode)) { + return; + } + logger.log("Sending event", category, value); this.sendToApi("/v1/game-event", { diff --git a/src/js/savegame/puzzle_serializer.js b/src/js/savegame/puzzle_serializer.js index d94f1e9f..49dd4ef6 100644 --- a/src/js/savegame/puzzle_serializer.js +++ b/src/js/savegame/puzzle_serializer.js @@ -16,6 +16,7 @@ import trim from "trim"; import { enumColors } from "../game/colors"; import { COLOR_ITEM_SINGLETONS } from "../game/items/color_item"; import { ShapeDefinition } from "../game/shape_definition"; +import { MetaBlockBuilding } from "../game/buildings/block"; const logger = createLogger("puzzle-serializer"); @@ -67,6 +68,17 @@ export class PuzzleSerializer { }); continue; } + + if (staticComp.getMetaBuilding().id === gMetaBuildingRegistry.findByClass(MetaBlockBuilding).id) { + buildings.push({ + type: "block", + pos: { + x: staticComp.origin.x, + y: staticComp.origin.y, + r: staticComp.rotation, + }, + }); + } } const mode = /** @type {PuzzleGameMode} */ (root.gameMode); @@ -160,6 +172,21 @@ export class PuzzleSerializer { entity.components.GoalAcceptor.item = item; break; } + case "block": { + const entity = root.logic.tryPlaceBuilding({ + origin: new Vector(building.pos.x, building.pos.y), + building: gMetaBuildingRegistry.findByClass(MetaBlockBuilding), + originalRotation: building.pos.r, + rotation: building.pos.r, + rotationVariant: 0, + variant: defaultBuildingVariant, + }); + if (!entity) { + logger.warn("Failed to place block:", building); + return "failed-to-place-block"; + } + break; + } default: { // @ts-ignore return "invalid-building-type: " + building.type; diff --git a/src/js/savegame/savegame_typedefs.js b/src/js/savegame/savegame_typedefs.js index 27898b68..64a06dac 100644 --- a/src/js/savegame/savegame_typedefs.js +++ b/src/js/savegame/savegame_typedefs.js @@ -73,11 +73,18 @@ * }} PuzzleGameBuildingGoal */ +/** + * @typedef {{ + * type: "block"; + * pos: { x: number; y: number; r: number } + * }} PuzzleGameBuildingBlock + */ + /** * @typedef {{ * version: number; * bounds: { w: number; h: number; }, - * buildings: (PuzzleGameBuildingGoal | PuzzleGameBuildingConstantProducer)[] + * buildings: (PuzzleGameBuildingGoal | PuzzleGameBuildingConstantProducer | PuzzleGameBuildingBlock)[] * }} PuzzleGameData */ diff --git a/src/js/states/puzzle_menu.js b/src/js/states/puzzle_menu.js index 979091f3..eb5595f6 100644 --- a/src/js/states/puzzle_menu.js +++ b/src/js/states/puzzle_menu.js @@ -51,6 +51,8 @@ const BUILTIN_PUZZLES = G_IS_DEV const logger = createLogger("puzzle-menu"); +let lastCategory = categories[0]; + export class PuzzleMenuState extends TextualGameState { constructor() { super("PuzzleMenuState"); @@ -104,6 +106,7 @@ export class PuzzleMenuState extends TextualGameState { } selectCategory(category) { + lastCategory = category; if (category === this.activeCategory) { return; } @@ -180,19 +183,10 @@ export class PuzzleMenuState extends TextualGameState { stats.classList.add("stats"); elem.appendChild(stats); - if (puzzle.difficulty !== null) { + if (puzzle.downloads > 0) { const difficulty = document.createElement("div"); difficulty.classList.add("difficulty"); - - const canvas = document.createElement("canvas"); - canvas.width = 32; - canvas.height = 32; - const context = canvas.getContext("2d"); - PUZZLE_RATINGS[ - clamp(Math.round(puzzle.difficulty), 0, PUZZLE_RATINGS.length - 1) - ].drawFullSizeOnCanvas(context, 32); - difficulty.appendChild(canvas); - + difficulty.innerText = Math.round((puzzle.completions / puzzle.downloads) * 100.0) + "%"; stats.appendChild(difficulty); } @@ -284,7 +278,7 @@ export class PuzzleMenuState extends TextualGameState { } onEnter(payload) { - this.selectCategory(categories[0]); + this.selectCategory(lastCategory); if (payload && payload.error) { this.dialogs.showWarning(payload.error.title, payload.error.desc); diff --git a/translations/base-en.yaml b/translations/base-en.yaml index 2349ac31..b9ea9d77 100644 --- a/translations/base-en.yaml +++ b/translations/base-en.yaml @@ -130,7 +130,6 @@ puzzleMenu: validatingPuzzle: Validating Puzzle submittingPuzzle: Submitting Puzzle noPuzzles: There are currently no puzzles in this section. - difficultyNotDetermined: Not yet determined categories: levels: Levels @@ -627,13 +626,6 @@ ingame: buttonSubmit: Continue - difficulties: - - No challenge - - Easy - - Medium - - Hard - - Impossible - puzzleMetadata: author: Author shortKey: Short Key @@ -875,6 +867,11 @@ buildings: name: &goal_acceptor Goal Acceptor description: Deliver shapes to the goal acceptor to set them as a goal. + block: + default: + name: &block Block + description: Allows you to block a tile. + storyRewards: # Those are the rewards gained from completing the store reward_cutter_and_trash: @@ -1304,6 +1301,7 @@ keybindings: item_producer: Item Producer (Sandbox) constant_producer: *constant_producer goal_acceptor: *goal_acceptor + block: *block # --- pipette: Pipette