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 {
|
> .contents {
|
||||||
@include S(width, 400px);
|
|
||||||
@include S(height, 170px);
|
|
||||||
@include InlineAnimation(0.5s ease-in-out) {
|
@include InlineAnimation(0.5s ease-in-out) {
|
||||||
0% {
|
0% {
|
||||||
transform: translateX(-100vw);
|
transform: translateX(-100vw);
|
||||||
@ -75,6 +73,7 @@
|
|||||||
transform: translateX(-2vw);
|
transform: translateX(-2vw);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -84,6 +83,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@include S(margin-bottom, 10px);
|
@include S(margin-bottom, 10px);
|
||||||
|
@include SuperSmallText;
|
||||||
|
|
||||||
> .buttons {
|
> .buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -92,8 +92,8 @@
|
|||||||
@include S(margin, 10px, 0);
|
@include S(margin, 10px, 0);
|
||||||
|
|
||||||
> button {
|
> button {
|
||||||
@include S(width, 40px);
|
@include S(width, 60px);
|
||||||
@include S(height, 40px);
|
@include S(height, 60px);
|
||||||
@include S(margin, 0, 10px);
|
@include S(margin, 0, 10px);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@include S(border-radius, $globalBorderRadius);
|
@include S(border-radius, $globalBorderRadius);
|
||||||
@ -101,12 +101,7 @@
|
|||||||
|
|
||||||
&.liked-yes {
|
&.liked-yes {
|
||||||
/* @load-async */
|
/* @load-async */
|
||||||
background: uiResource("icons/puzzle_action_liked_yes.png") center center / 60%
|
background: uiResource("icons/puzzle_action_liked_yes.png") center center / 70%
|
||||||
no-repeat;
|
|
||||||
}
|
|
||||||
&.liked-no {
|
|
||||||
/* @load-async */
|
|
||||||
background: uiResource("icons/puzzle_action_liked_no.png") center center / 60%
|
|
||||||
no-repeat;
|
no-repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,61 +119,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> .stepDifficulty {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
@include S(margin-bottom, 10px);
|
|
||||||
|
|
||||||
> .desc {
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .actions {
|
> .actions {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@include S(bottom, 40px);
|
@include S(bottom, 40px);
|
||||||
@ -194,18 +134,15 @@
|
|||||||
background-color: $accentColorDark;
|
background-color: $accentColorDark;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
button.close {
|
button.close {
|
||||||
border: 0;
|
border: 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
@include S(margin-top, 30px);
|
@include S(margin-top, 15px);
|
||||||
background: $colorGreenBright;
|
background: $colorGreenBright;
|
||||||
@include S(padding, 10px, 40px);
|
@include Heading;
|
||||||
|
@include S(padding, 14px, 40px);
|
||||||
&:not(.visible) {
|
|
||||||
opacity: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
$buildings: belt, cutter, miner, mixer, painter, rotater, balancer, stacker, trash, underground_belt, wire,
|
$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,
|
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 {
|
@each $building in $buildings {
|
||||||
[data-icon="building_icons/#{$building}.png"] {
|
[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,
|
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,
|
logic_gate-xor, analyzer, virtual_processor-rotater, virtual_processor-unstacker, item_producer,
|
||||||
constant_producer, virtual_processor-stacker, virtual_processor-painter, wire-second, painter,
|
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 {
|
@each $building in $buildingsAndVariants {
|
||||||
[data-icon="building_tutorials/#{$building}.png"] {
|
[data-icon="building_tutorials/#{$building}.png"] {
|
||||||
/* @load-async */
|
/* @load-async */
|
||||||
|
@ -158,7 +158,6 @@
|
|||||||
> .downloads {
|
> .downloads {
|
||||||
@include SuperSmallText;
|
@include SuperSmallText;
|
||||||
color: #000;
|
color: #000;
|
||||||
justify-self: start;
|
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@include S(margin-right, 5px);
|
@include S(margin-right, 5px);
|
||||||
@include S(padding-left, 12px);
|
@include S(padding-left, 12px);
|
||||||
@ -170,8 +169,9 @@
|
|||||||
|
|
||||||
& {
|
& {
|
||||||
/* @load-async */
|
/* @load-async */
|
||||||
background: uiResource("icons/puzzle_plays.png") #{D(2px)} center / #{D(8px)}
|
background: uiResource("icons/puzzle_plays.png") #{D(2px)} #{D(2.5px)} / #{D(
|
||||||
#{D(8px)} no-repeat;
|
8px
|
||||||
|
)} #{D(8px)} no-repeat;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,26 +180,35 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: #000;
|
color: #000;
|
||||||
justify-self: start;
|
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@include S(padding-left, 12px);
|
@include S(padding-left, 14px);
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
@include DarkThemeInvert;
|
@include DarkThemeInvert;
|
||||||
|
|
||||||
& {
|
& {
|
||||||
/* @load-async */
|
/* @load-async */
|
||||||
background: uiResource("icons/puzzle_upvotes.png") #{D(2px)} center / #{D(
|
background: uiResource("icons/puzzle_upvotes.png") #{D(2px)} #{D(2.4px)} / #{D(
|
||||||
8px
|
9px
|
||||||
)} #{D(8px)} no-repeat;
|
)} #{D(9px)} no-repeat;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> .difficulty {
|
> .difficulty {
|
||||||
@include S(margin-top, 1px);
|
@include SuperSmallText;
|
||||||
@include S(margin-right, 7px);
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: 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 { MetaItemProducerBuilding } from "../../buildings/item_producer";
|
||||||
import { MetaConstantProducerBuilding } from "../../buildings/constant_producer";
|
import { MetaConstantProducerBuilding } from "../../buildings/constant_producer";
|
||||||
import { MetaGoalAcceptorBuilding } from "../../buildings/goal_acceptor";
|
import { MetaGoalAcceptorBuilding } from "../../buildings/goal_acceptor";
|
||||||
|
import { MetaBlockBuilding } from "../../buildings/block";
|
||||||
|
|
||||||
export class HUDBuildingsToolbar extends HUDBaseToolbar {
|
export class HUDBuildingsToolbar extends HUDBaseToolbar {
|
||||||
constructor(root) {
|
constructor(root) {
|
||||||
@ -28,6 +29,7 @@ export class HUDBuildingsToolbar extends HUDBaseToolbar {
|
|||||||
MetaBalancerBuilding,
|
MetaBalancerBuilding,
|
||||||
MetaUndergroundBeltBuilding,
|
MetaUndergroundBeltBuilding,
|
||||||
MetaMinerBuilding,
|
MetaMinerBuilding,
|
||||||
|
MetaBlockBuilding,
|
||||||
MetaCutterBuilding,
|
MetaCutterBuilding,
|
||||||
MetaRotaterBuilding,
|
MetaRotaterBuilding,
|
||||||
MetaStackerBuilding,
|
MetaStackerBuilding,
|
||||||
|
@ -14,14 +14,6 @@ import { DynamicDomAttach } from "../dynamic_dom_attach";
|
|||||||
import { ShapeItem } from "../../items/shape_item";
|
import { ShapeItem } from "../../items/shape_item";
|
||||||
import { ShapeDefinition } from "../../shape_definition";
|
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 {
|
export class HUDPuzzleCompleteNotification extends BaseHUDPart {
|
||||||
initialize() {
|
initialize() {
|
||||||
this.visible = false;
|
this.visible = false;
|
||||||
@ -32,8 +24,7 @@ export class HUDPuzzleCompleteNotification extends BaseHUDPart {
|
|||||||
|
|
||||||
this.root.signals.puzzleComplete.add(this.show, this);
|
this.root.signals.puzzleComplete.add(this.show, this);
|
||||||
|
|
||||||
this.selectionLiked = false;
|
this.userDidLikePuzzle = false;
|
||||||
this.selectionDifficulty = null;
|
|
||||||
this.timeOfCompletion = 0;
|
this.timeOfCompletion = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,18 +39,6 @@ export class HUDPuzzleCompleteNotification extends BaseHUDPart {
|
|||||||
this.elemContents = makeDiv(dialog, null, ["contents"]);
|
this.elemContents = makeDiv(dialog, null, ["contents"]);
|
||||||
this.elemActions = makeDiv(dialog, null, ["actions"]);
|
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"]);
|
const stepLike = makeDiv(this.elemContents, null, ["step", "stepLike"]);
|
||||||
makeDiv(stepLike, null, ["title"], T.ingame.puzzleCompletion.titleLike);
|
makeDiv(stepLike, null, ["title"], T.ingame.puzzleCompletion.titleLike);
|
||||||
|
|
||||||
@ -69,45 +48,10 @@ export class HUDPuzzleCompleteNotification extends BaseHUDPart {
|
|||||||
this.buttonLikeYes.classList.add("liked-yes");
|
this.buttonLikeYes.classList.add("liked-yes");
|
||||||
likeButtons.appendChild(this.buttonLikeYes);
|
likeButtons.appendChild(this.buttonLikeYes);
|
||||||
this.trackClicks(this.buttonLikeYes, () => {
|
this.trackClicks(this.buttonLikeYes, () => {
|
||||||
this.selectionLiked = !this.selectionLiked;
|
this.userDidLikePuzzle = !this.userDidLikePuzzle;
|
||||||
this.updateState();
|
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 = document.createElement("button");
|
||||||
this.btnClose.classList.add("close", "styledButton");
|
this.btnClose.classList.add("close", "styledButton");
|
||||||
this.btnClose.innerText = T.ingame.puzzleCompletion.buttonSubmit;
|
this.btnClose.innerText = T.ingame.puzzleCompletion.buttonSubmit;
|
||||||
@ -116,26 +60,8 @@ export class HUDPuzzleCompleteNotification extends BaseHUDPart {
|
|||||||
this.trackClicks(this.btnClose, this.close);
|
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() {
|
updateState() {
|
||||||
this.buttonLikeYes.classList.toggle("active", this.selectionLiked === true);
|
this.buttonLikeYes.classList.toggle("active", this.userDidLikePuzzle === 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"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
show() {
|
show() {
|
||||||
@ -155,7 +81,7 @@ export class HUDPuzzleCompleteNotification extends BaseHUDPart {
|
|||||||
|
|
||||||
close() {
|
close() {
|
||||||
/** @type {PuzzlePlayGameMode} */ (this.root.gameMode)
|
/** @type {PuzzlePlayGameMode} */ (this.root.gameMode)
|
||||||
.trackCompleted(this.selectionLiked, this.selectionDifficulty, Math.round(this.timeOfCompletion))
|
.trackCompleted(this.userDidLikePuzzle, Math.round(this.timeOfCompletion))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// this.root.gameState.moveToState("PuzzleMenuState");
|
// this.root.gameState.moveToState("PuzzleMenuState");
|
||||||
this.visible = false;
|
this.visible = false;
|
||||||
|
@ -32,31 +32,16 @@ export class HUDPuzzlePlayMetadata extends BaseHUDPart {
|
|||||||
<div class="info key">
|
<div class="info key">
|
||||||
<label>${T.ingame.puzzleMetadata.shortKey}</label><span>${puzzle.meta.shortKey}</span>
|
<label>${T.ingame.puzzleMetadata.shortKey}</label><span>${puzzle.meta.shortKey}</span>
|
||||||
</div>
|
</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">
|
<div class="info rating">
|
||||||
<label>${T.ingame.puzzleMetadata.averageDuration}</label>
|
<label>${T.ingame.puzzleMetadata.averageDuration}</label>
|
||||||
<span>${
|
<span>${puzzle.meta.averageTime ? formatSeconds(puzzle.meta.averageTime) : "-"}</span>
|
||||||
puzzle.meta.averageTime
|
|
||||||
? formatSeconds(puzzle.meta.averageTime)
|
|
||||||
: T.puzzleMenu.difficultyNotDetermined
|
|
||||||
}</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="info rating">
|
<div class="info rating">
|
||||||
<label>${T.ingame.puzzleMetadata.completionRate}</label>
|
<label>${T.ingame.puzzleMetadata.completionRate}</label>
|
||||||
<span>${
|
<span>${
|
||||||
puzzle.meta.downloads > 10
|
puzzle.meta.downloads > 0
|
||||||
? ((puzzle.meta.completions / puzzle.meta.downloads) * 100.0).toFixed(1) + "%"
|
? ((puzzle.meta.completions / puzzle.meta.downloads) * 100.0).toFixed(1) + "%"
|
||||||
: T.puzzleMenu.difficultyNotDetermined
|
: "-"
|
||||||
}</span>
|
}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -52,6 +52,7 @@ export const KEYMAPPINGS = {
|
|||||||
// Puzzle buildings
|
// Puzzle buildings
|
||||||
constant_producer: { keyCode: key("H") },
|
constant_producer: { keyCode: key("H") },
|
||||||
goal_acceptor: { keyCode: key("N") },
|
goal_acceptor: { keyCode: key("N") },
|
||||||
|
block: { keyCode: key("4") },
|
||||||
|
|
||||||
// Primary Toolbar
|
// Primary Toolbar
|
||||||
belt: { keyCode: key("1") },
|
belt: { keyCode: key("1") },
|
||||||
|
@ -4,6 +4,7 @@ import { T } from "../translations";
|
|||||||
import { MetaAnalyzerBuilding } from "./buildings/analyzer";
|
import { MetaAnalyzerBuilding } from "./buildings/analyzer";
|
||||||
import { enumBalancerVariants, MetaBalancerBuilding } from "./buildings/balancer";
|
import { enumBalancerVariants, MetaBalancerBuilding } from "./buildings/balancer";
|
||||||
import { MetaBeltBuilding } from "./buildings/belt";
|
import { MetaBeltBuilding } from "./buildings/belt";
|
||||||
|
import { MetaBlockBuilding } from "./buildings/block";
|
||||||
import { MetaComparatorBuilding } from "./buildings/comparator";
|
import { MetaComparatorBuilding } from "./buildings/comparator";
|
||||||
import { MetaConstantProducerBuilding } from "./buildings/constant_producer";
|
import { MetaConstantProducerBuilding } from "./buildings/constant_producer";
|
||||||
import { MetaConstantSignalBuilding } from "./buildings/constant_signal";
|
import { MetaConstantSignalBuilding } from "./buildings/constant_signal";
|
||||||
@ -63,6 +64,7 @@ export function initMetaBuildingRegistry() {
|
|||||||
gMetaBuildingRegistry.register(MetaComparatorBuilding);
|
gMetaBuildingRegistry.register(MetaComparatorBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaItemProducerBuilding);
|
gMetaBuildingRegistry.register(MetaItemProducerBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaConstantProducerBuilding);
|
gMetaBuildingRegistry.register(MetaConstantProducerBuilding);
|
||||||
|
gMetaBuildingRegistry.register(MetaBlockBuilding);
|
||||||
|
|
||||||
// Belt
|
// Belt
|
||||||
registerBuildingVariant(1, MetaBeltBuilding, defaultBuildingVariant, 0);
|
registerBuildingVariant(1, MetaBeltBuilding, defaultBuildingVariant, 0);
|
||||||
@ -175,6 +177,9 @@ export function initMetaBuildingRegistry() {
|
|||||||
// Goal acceptor
|
// Goal acceptor
|
||||||
registerBuildingVariant(63, MetaGoalAcceptorBuilding);
|
registerBuildingVariant(63, MetaGoalAcceptorBuilding);
|
||||||
|
|
||||||
|
// Block
|
||||||
|
registerBuildingVariant(64, MetaBlockBuilding);
|
||||||
|
|
||||||
// Propagate instances
|
// Propagate instances
|
||||||
for (const key in gBuildingVariants) {
|
for (const key in gBuildingVariants) {
|
||||||
gBuildingVariants[key].metaInstance = gMetaBuildingRegistry.findByClass(
|
gBuildingVariants[key].metaInstance = gMetaBuildingRegistry.findByClass(
|
||||||
|
@ -21,13 +21,13 @@ import { MetaComparatorBuilding } from "../buildings/comparator";
|
|||||||
import { MetaTransistorBuilding } from "../buildings/transistor";
|
import { MetaTransistorBuilding } from "../buildings/transistor";
|
||||||
import { MetaConstantProducerBuilding } from "../buildings/constant_producer";
|
import { MetaConstantProducerBuilding } from "../buildings/constant_producer";
|
||||||
import { MetaGoalAcceptorBuilding } from "../buildings/goal_acceptor";
|
import { MetaGoalAcceptorBuilding } from "../buildings/goal_acceptor";
|
||||||
import { HUDConstantSignalEdit } from "../hud/parts/constant_signal_edit";
|
|
||||||
import { PuzzleSerializer } from "../../savegame/puzzle_serializer";
|
import { PuzzleSerializer } from "../../savegame/puzzle_serializer";
|
||||||
import { T } from "../../translations";
|
import { T } from "../../translations";
|
||||||
import { HUDPuzzlePlayMetadata } from "../hud/parts/puzzle_play_metadata";
|
import { HUDPuzzlePlayMetadata } from "../hud/parts/puzzle_play_metadata";
|
||||||
import { createLogger } from "../../core/logging";
|
import { createLogger } from "../../core/logging";
|
||||||
import { HUDPuzzleCompleteNotification } from "../hud/parts/puzzle_complete_notification";
|
import { HUDPuzzleCompleteNotification } from "../hud/parts/puzzle_complete_notification";
|
||||||
import { HUDPuzzlePlaySettings } from "../hud/parts/puzzle_play_settings";
|
import { HUDPuzzlePlaySettings } from "../hud/parts/puzzle_play_settings";
|
||||||
|
import { MetaBlockBuilding } from "../buildings/block";
|
||||||
|
|
||||||
const logger = createLogger("puzzle-play");
|
const logger = createLogger("puzzle-play");
|
||||||
const copy = require("clipboard-copy");
|
const copy = require("clipboard-copy");
|
||||||
@ -48,6 +48,7 @@ export class PuzzlePlayGameMode extends PuzzleGameMode {
|
|||||||
this.hiddenBuildings = [
|
this.hiddenBuildings = [
|
||||||
MetaConstantProducerBuilding,
|
MetaConstantProducerBuilding,
|
||||||
MetaGoalAcceptorBuilding,
|
MetaGoalAcceptorBuilding,
|
||||||
|
MetaBlockBuilding,
|
||||||
|
|
||||||
MetaStorageBuilding,
|
MetaStorageBuilding,
|
||||||
MetaReaderBuilding,
|
MetaReaderBuilding,
|
||||||
@ -106,16 +107,14 @@ export class PuzzlePlayGameMode extends PuzzleGameMode {
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {boolean} liked
|
* @param {boolean} liked
|
||||||
* @param {number} difficulty
|
|
||||||
* @param {number} time
|
* @param {number} time
|
||||||
*/
|
*/
|
||||||
trackCompleted(liked, difficulty, time) {
|
trackCompleted(liked, time) {
|
||||||
const closeLoading = this.root.hud.parts.dialogs.showLoadingDialog();
|
const closeLoading = this.root.hud.parts.dialogs.showLoadingDialog();
|
||||||
|
|
||||||
return this.root.app.clientApi
|
return this.root.app.clientApi
|
||||||
.apiCompletePuzzle(this.puzzle.meta.id, {
|
.apiCompletePuzzle(this.puzzle.meta.id, {
|
||||||
time,
|
time,
|
||||||
difficulty,
|
|
||||||
liked,
|
liked,
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
|
@ -36,6 +36,7 @@ import { HUDPartTutorialHints } from "../hud/parts/tutorial_hints";
|
|||||||
import { HUDInteractiveTutorial } from "../hud/parts/interactive_tutorial";
|
import { HUDInteractiveTutorial } from "../hud/parts/interactive_tutorial";
|
||||||
import { HUDSandboxController } from "../hud/parts/sandbox_controller";
|
import { HUDSandboxController } from "../hud/parts/sandbox_controller";
|
||||||
import { queryParamOptions } from "../../core/query_parameters";
|
import { queryParamOptions } from "../../core/query_parameters";
|
||||||
|
import { MetaBlockBuilding } from "../buildings/block";
|
||||||
|
|
||||||
/** @typedef {{
|
/** @typedef {{
|
||||||
* shape: string,
|
* shape: string,
|
||||||
@ -581,7 +582,7 @@ export class RegularGameMode extends GameMode {
|
|||||||
this.additionalHudParts.sandboxController = HUDSandboxController;
|
this.additionalHudParts.sandboxController = HUDSandboxController;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.hiddenBuildings = [MetaConstantProducerBuilding, MetaGoalAcceptorBuilding];
|
this.hiddenBuildings = [MetaConstantProducerBuilding, MetaGoalAcceptorBuilding, MetaBlockBuilding];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -168,7 +168,6 @@ export class ClientAPI {
|
|||||||
* @param {number} puzzleId
|
* @param {number} puzzleId
|
||||||
* @param {object} payload
|
* @param {object} payload
|
||||||
* @param {number} payload.time
|
* @param {number} payload.time
|
||||||
* @param {number} payload.difficulty
|
|
||||||
* @param {boolean} payload.liked
|
* @param {boolean} payload.liked
|
||||||
* @returns {Promise<{ success: true }>}
|
* @returns {Promise<{ success: true }>}
|
||||||
*/
|
*/
|
||||||
|
@ -3,6 +3,7 @@ import { createLogger } from "../../core/logging";
|
|||||||
import { queryParamOptions } from "../../core/query_parameters";
|
import { queryParamOptions } from "../../core/query_parameters";
|
||||||
import { BeltComponent } from "../../game/components/belt";
|
import { BeltComponent } from "../../game/components/belt";
|
||||||
import { StaticMapEntityComponent } from "../../game/components/static_map_entity";
|
import { StaticMapEntityComponent } from "../../game/components/static_map_entity";
|
||||||
|
import { RegularGameMode } from "../../game/modes/regular";
|
||||||
import { GameRoot } from "../../game/root";
|
import { GameRoot } from "../../game/root";
|
||||||
import { InGameState } from "../../states/ingame";
|
import { InGameState } from "../../states/ingame";
|
||||||
import { GameAnalyticsInterface } from "../game_analytics";
|
import { GameAnalyticsInterface } from "../game_analytics";
|
||||||
@ -163,6 +164,10 @@ export class ShapezGameAnalytics extends GameAnalyticsInterface {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!(root.gameMode instanceof RegularGameMode)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
logger.log("Sending event", category, value);
|
logger.log("Sending event", category, value);
|
||||||
|
|
||||||
this.sendToApi("/v1/game-event", {
|
this.sendToApi("/v1/game-event", {
|
||||||
|
@ -16,6 +16,7 @@ import trim from "trim";
|
|||||||
import { enumColors } from "../game/colors";
|
import { enumColors } from "../game/colors";
|
||||||
import { COLOR_ITEM_SINGLETONS } from "../game/items/color_item";
|
import { COLOR_ITEM_SINGLETONS } from "../game/items/color_item";
|
||||||
import { ShapeDefinition } from "../game/shape_definition";
|
import { ShapeDefinition } from "../game/shape_definition";
|
||||||
|
import { MetaBlockBuilding } from "../game/buildings/block";
|
||||||
|
|
||||||
const logger = createLogger("puzzle-serializer");
|
const logger = createLogger("puzzle-serializer");
|
||||||
|
|
||||||
@ -67,6 +68,17 @@ export class PuzzleSerializer {
|
|||||||
});
|
});
|
||||||
continue;
|
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);
|
const mode = /** @type {PuzzleGameMode} */ (root.gameMode);
|
||||||
@ -160,6 +172,21 @@ export class PuzzleSerializer {
|
|||||||
entity.components.GoalAcceptor.item = item;
|
entity.components.GoalAcceptor.item = item;
|
||||||
break;
|
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: {
|
default: {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return "invalid-building-type: " + building.type;
|
return "invalid-building-type: " + building.type;
|
||||||
|
@ -73,11 +73,18 @@
|
|||||||
* }} PuzzleGameBuildingGoal
|
* }} PuzzleGameBuildingGoal
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {{
|
||||||
|
* type: "block";
|
||||||
|
* pos: { x: number; y: number; r: number }
|
||||||
|
* }} PuzzleGameBuildingBlock
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* version: number;
|
* version: number;
|
||||||
* bounds: { w: number; h: number; },
|
* bounds: { w: number; h: number; },
|
||||||
* buildings: (PuzzleGameBuildingGoal | PuzzleGameBuildingConstantProducer)[]
|
* buildings: (PuzzleGameBuildingGoal | PuzzleGameBuildingConstantProducer | PuzzleGameBuildingBlock)[]
|
||||||
* }} PuzzleGameData
|
* }} PuzzleGameData
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -51,6 +51,8 @@ const BUILTIN_PUZZLES = G_IS_DEV
|
|||||||
|
|
||||||
const logger = createLogger("puzzle-menu");
|
const logger = createLogger("puzzle-menu");
|
||||||
|
|
||||||
|
let lastCategory = categories[0];
|
||||||
|
|
||||||
export class PuzzleMenuState extends TextualGameState {
|
export class PuzzleMenuState extends TextualGameState {
|
||||||
constructor() {
|
constructor() {
|
||||||
super("PuzzleMenuState");
|
super("PuzzleMenuState");
|
||||||
@ -104,6 +106,7 @@ export class PuzzleMenuState extends TextualGameState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
selectCategory(category) {
|
selectCategory(category) {
|
||||||
|
lastCategory = category;
|
||||||
if (category === this.activeCategory) {
|
if (category === this.activeCategory) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -180,19 +183,10 @@ export class PuzzleMenuState extends TextualGameState {
|
|||||||
stats.classList.add("stats");
|
stats.classList.add("stats");
|
||||||
elem.appendChild(stats);
|
elem.appendChild(stats);
|
||||||
|
|
||||||
if (puzzle.difficulty !== null) {
|
if (puzzle.downloads > 0) {
|
||||||
const difficulty = document.createElement("div");
|
const difficulty = document.createElement("div");
|
||||||
difficulty.classList.add("difficulty");
|
difficulty.classList.add("difficulty");
|
||||||
|
difficulty.innerText = Math.round((puzzle.completions / puzzle.downloads) * 100.0) + "%";
|
||||||
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);
|
|
||||||
|
|
||||||
stats.appendChild(difficulty);
|
stats.appendChild(difficulty);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,7 +278,7 @@ export class PuzzleMenuState extends TextualGameState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onEnter(payload) {
|
onEnter(payload) {
|
||||||
this.selectCategory(categories[0]);
|
this.selectCategory(lastCategory);
|
||||||
|
|
||||||
if (payload && payload.error) {
|
if (payload && payload.error) {
|
||||||
this.dialogs.showWarning(payload.error.title, payload.error.desc);
|
this.dialogs.showWarning(payload.error.title, payload.error.desc);
|
||||||
|
@ -130,7 +130,6 @@ puzzleMenu:
|
|||||||
validatingPuzzle: Validating Puzzle
|
validatingPuzzle: Validating Puzzle
|
||||||
submittingPuzzle: Submitting Puzzle
|
submittingPuzzle: Submitting Puzzle
|
||||||
noPuzzles: There are currently no puzzles in this section.
|
noPuzzles: There are currently no puzzles in this section.
|
||||||
difficultyNotDetermined: Not yet determined
|
|
||||||
|
|
||||||
categories:
|
categories:
|
||||||
levels: Levels
|
levels: Levels
|
||||||
@ -627,13 +626,6 @@ ingame:
|
|||||||
|
|
||||||
buttonSubmit: Continue
|
buttonSubmit: Continue
|
||||||
|
|
||||||
difficulties:
|
|
||||||
- No challenge
|
|
||||||
- Easy
|
|
||||||
- Medium
|
|
||||||
- Hard
|
|
||||||
- Impossible
|
|
||||||
|
|
||||||
puzzleMetadata:
|
puzzleMetadata:
|
||||||
author: Author
|
author: Author
|
||||||
shortKey: Short Key
|
shortKey: Short Key
|
||||||
@ -875,6 +867,11 @@ buildings:
|
|||||||
name: &goal_acceptor Goal Acceptor
|
name: &goal_acceptor Goal Acceptor
|
||||||
description: Deliver shapes to the goal acceptor to set them as a goal.
|
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:
|
storyRewards:
|
||||||
# Those are the rewards gained from completing the store
|
# Those are the rewards gained from completing the store
|
||||||
reward_cutter_and_trash:
|
reward_cutter_and_trash:
|
||||||
@ -1304,6 +1301,7 @@ keybindings:
|
|||||||
item_producer: Item Producer (Sandbox)
|
item_producer: Item Producer (Sandbox)
|
||||||
constant_producer: *constant_producer
|
constant_producer: *constant_producer
|
||||||
goal_acceptor: *goal_acceptor
|
goal_acceptor: *goal_acceptor
|
||||||
|
block: *block
|
||||||
# ---
|
# ---
|
||||||
|
|
||||||
pipette: Pipette
|
pipette: Pipette
|
||||||
|
Loading…
Reference in New Issue
Block a user