mirror of
https://github.com/tobspr/shapez.io.git
synced 2025-06-13 13:04:03 +00:00
New difficulty
This commit is contained in:
parent
62fc46f29f
commit
4f846edb9b
BIN
res/ui/building_icons/block.png
Normal file
BIN
res/ui/building_icons/block.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
BIN
res/ui/building_tutorials/block.png
Normal file
BIN
res/ui/building_tutorials/block.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 55 KiB |
BIN
res/ui/icons/puzzle_completion_rate.png
Normal file
BIN
res/ui/icons/puzzle_completion_rate.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 680 B |
BIN
res_raw/sprites/blueprints/block.png
Normal file
BIN
res_raw/sprites/blueprints/block.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
BIN
res_raw/sprites/buildings/block.png
Normal file
BIN
res_raw/sprites/buildings/block.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.0 KiB |
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
30
src/js/game/buildings/block.js
Normal file
30
src/js/game/buildings/block.js
Normal file
@ -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) {}
|
||||
}
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -32,31 +32,16 @@ export class HUDPuzzlePlayMetadata extends BaseHUDPart {
|
||||
<div class="info key">
|
||||
<label>${T.ingame.puzzleMetadata.shortKey}</label><span>${puzzle.meta.shortKey}</span>
|
||||
</div>
|
||||
<div class="info rating">
|
||||
<label>${T.ingame.puzzleMetadata.rating}</label>
|
||||
<span>${
|
||||
puzzle.meta.difficulty
|
||||
? puzzle.meta.difficulty.toFixed(2) +
|
||||
" (" +
|
||||
T.ingame.puzzleCompletion.difficulties[Math.round(puzzle.meta.difficulty)] +
|
||||
")"
|
||||
: T.puzzleMenu.difficultyNotDetermined
|
||||
}</span>
|
||||
</div>
|
||||
<div class="info rating">
|
||||
<label>${T.ingame.puzzleMetadata.averageDuration}</label>
|
||||
<span>${
|
||||
puzzle.meta.averageTime
|
||||
? formatSeconds(puzzle.meta.averageTime)
|
||||
: T.puzzleMenu.difficultyNotDetermined
|
||||
}</span>
|
||||
<span>${puzzle.meta.averageTime ? formatSeconds(puzzle.meta.averageTime) : "-"}</span>
|
||||
</div>
|
||||
<div class="info rating">
|
||||
<label>${T.ingame.puzzleMetadata.completionRate}</label>
|
||||
<span>${
|
||||
puzzle.meta.downloads > 10
|
||||
puzzle.meta.downloads > 0
|
||||
? ((puzzle.meta.completions / puzzle.meta.downloads) * 100.0).toFixed(1) + "%"
|
||||
: T.puzzleMenu.difficultyNotDetermined
|
||||
: "-"
|
||||
}</span>
|
||||
</div>
|
||||
|
||||
|
@ -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") },
|
||||
|
@ -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(
|
||||
|
@ -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 => {
|
||||
|
@ -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];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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 }>}
|
||||
*/
|
||||
|
@ -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", {
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
*/
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user