Puzzle mode, part 1
@ -40,7 +40,7 @@ module.exports = ({
|
|||||||
G_ALL_UI_IMAGES: JSON.stringify(getAllResourceImages()),
|
G_ALL_UI_IMAGES: JSON.stringify(getAllResourceImages()),
|
||||||
};
|
};
|
||||||
|
|
||||||
const minifyNames = environment === "prod";
|
const minifyNames = false;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
mode: "production",
|
mode: "production",
|
||||||
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 3.5 KiB |
BIN
res/ui/icons/puzzle_complete_indicator.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
res/ui/icons/puzzle_upvotes.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
res/ui/icons/state_next_button.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
res/ui/puzzle_dlc_logo.png
Normal file
After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 9.6 KiB |
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 17 KiB |
@ -85,6 +85,8 @@ def generate_blueprint_sprite(infilename, outfilename):
|
|||||||
buildings = listdir("buildings")
|
buildings = listdir("buildings")
|
||||||
|
|
||||||
for buildingId in buildings:
|
for buildingId in buildings:
|
||||||
|
if not ".png" in buildingId:
|
||||||
|
continue
|
||||||
if "hub" in buildingId:
|
if "hub" in buildingId:
|
||||||
continue
|
continue
|
||||||
if "wire-" in buildingId:
|
if "wire-" in buildingId:
|
||||||
|
@ -18,18 +18,23 @@
|
|||||||
color: #333438;
|
color: #333438;
|
||||||
transition: all 0.12s ease-in-out;
|
transition: all 0.12s ease-in-out;
|
||||||
transition-property: opacity, transform;
|
transition-property: opacity, transform;
|
||||||
|
text-transform: uppercase;
|
||||||
|
@include PlainText;
|
||||||
|
|
||||||
opacity: 0.8;
|
opacity: 1;
|
||||||
&:hover {
|
&:hover {
|
||||||
opacity: 1 !important;
|
opacity: 0.9 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.pressed {
|
&.pressed {
|
||||||
transform: scale(0.9) !important;
|
transform: scale(0.95) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@include DarkThemeOverride {
|
@include S(padding-left, 25px);
|
||||||
color: #fff;
|
|
||||||
|
& {
|
||||||
|
/* @load-async */
|
||||||
|
background: uiResource("icons/state_back_button.png") left center / D(15px) no-repeat;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,26 +17,32 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
color: #333438;
|
color: #333438;
|
||||||
transition: all 0.12s ease-in-out;
|
transition: all 0.12s ease-in-out;
|
||||||
|
text-transform: uppercase;
|
||||||
transition-property: opacity, transform;
|
transition-property: opacity, transform;
|
||||||
|
@include PlainText;
|
||||||
|
@include S(padding-right, 25px);
|
||||||
|
opacity: 1;
|
||||||
|
|
||||||
opacity: 0.8;
|
|
||||||
&:hover {
|
&:hover {
|
||||||
opacity: 1 !important;
|
opacity: 0.9 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.pressed {
|
&.pressed {
|
||||||
transform: scale(0.9) !important;
|
transform: scale(0.95) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@include DarkThemeOverride {
|
& {
|
||||||
color: #fff;
|
/* @load-async */
|
||||||
|
background: uiResource("icons/state_next_button.png") right center / D(15px) no-repeat;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> .content {
|
> .content {
|
||||||
@include SuperSmallText;
|
@include SuperDuperSmallText;
|
||||||
@include S(font-size, 7px);
|
@include S(width, 180px);
|
||||||
@include S(width, 150px);
|
@include S(padding-right, 25px);
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: $accentColorDark;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
12
src/css/ingame_hud/puzzle_dlc_logo.scss
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#ingame_HUD_PuzzleDLCLogo {
|
||||||
|
position: absolute;
|
||||||
|
@include S(width, 150px);
|
||||||
|
@include S(height, 40px);
|
||||||
|
@include S(bottom, 10px);
|
||||||
|
@include S(right, 15px);
|
||||||
|
|
||||||
|
& {
|
||||||
|
/* @load-async */
|
||||||
|
background: uiResource("puzzle_dlc_logo.png") center center / contain no-repeat;
|
||||||
|
}
|
||||||
|
}
|
@ -29,6 +29,7 @@
|
|||||||
@import "states/about";
|
@import "states/about";
|
||||||
@import "states/mobile_warning";
|
@import "states/mobile_warning";
|
||||||
@import "states/changelog";
|
@import "states/changelog";
|
||||||
|
@import "states/puzzle_menu";
|
||||||
|
|
||||||
@import "ingame_hud/buildings_toolbar";
|
@import "ingame_hud/buildings_toolbar";
|
||||||
@import "ingame_hud/building_placer";
|
@import "ingame_hud/building_placer";
|
||||||
@ -59,12 +60,14 @@
|
|||||||
@import "ingame_hud/mode_menu_next";
|
@import "ingame_hud/mode_menu_next";
|
||||||
@import "ingame_hud/mode_menu";
|
@import "ingame_hud/mode_menu";
|
||||||
@import "ingame_hud/mode_settings";
|
@import "ingame_hud/mode_settings";
|
||||||
|
@import "ingame_hud/puzzle_dlc_logo";
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
$elements:
|
$elements:
|
||||||
// Base
|
// Base
|
||||||
ingame_Canvas,
|
ingame_Canvas,
|
||||||
ingame_VignetteOverlay,
|
ingame_VignetteOverlay,
|
||||||
|
ingame_HUD_PuzzleDLCLogo,
|
||||||
|
|
||||||
// Ingame overlays
|
// Ingame overlays
|
||||||
ingame_HUD_Waypoints,
|
ingame_HUD_Waypoints,
|
||||||
|
181
src/css/states/puzzle_menu.scss
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
#state_PuzzleMenuState {
|
||||||
|
> .headerBar {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr auto;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .container {
|
||||||
|
> .mainContent {
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
> .categoryChooser {
|
||||||
|
display: grid;
|
||||||
|
grid-auto-columns: 1fr;
|
||||||
|
grid-auto-flow: column;
|
||||||
|
@include S(grid-gap, 2px);
|
||||||
|
@include S(padding-right, 10px);
|
||||||
|
|
||||||
|
> .category {
|
||||||
|
background: $accentColorBright;
|
||||||
|
border-radius: 0;
|
||||||
|
color: $accentColorDark;
|
||||||
|
transition: all 0.12s ease-in-out;
|
||||||
|
transition-property: opacity, background-color, color;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
@include S(border-top-left-radius, $globalBorderRadius);
|
||||||
|
@include S(border-bottom-left-radius, $globalBorderRadius);
|
||||||
|
}
|
||||||
|
&:last-child {
|
||||||
|
border-top-right-radius: $globalBorderRadius;
|
||||||
|
border-bottom-right-radius: $globalBorderRadius;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: $colorBlueBright;
|
||||||
|
opacity: 1 !important;
|
||||||
|
color: #fff;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .puzzles {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||||
|
@include S(grid-auto-rows, 120px);
|
||||||
|
@include S(grid-gap, 3px);
|
||||||
|
@include S(grid-auto-columns, 1fr);
|
||||||
|
@include S(margin-top, 10px);
|
||||||
|
@include S(padding-right, 4px);
|
||||||
|
@include S(height, 360px);
|
||||||
|
overflow-y: scroll;
|
||||||
|
pointer-events: all;
|
||||||
|
|
||||||
|
> .puzzle {
|
||||||
|
width: 100%;
|
||||||
|
@include S(height, 120px);
|
||||||
|
background: #f3f3f8;
|
||||||
|
@include S(border-radius, $globalBorderRadius);
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr auto;
|
||||||
|
grid-template-rows: D(15px) 1fr auto;
|
||||||
|
@include S(padding, 5px);
|
||||||
|
@include S(grid-column-gap, 5px);
|
||||||
|
box-sizing: border-box;
|
||||||
|
pointer-events: all;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
@include InlineAnimation(0.12s ease-in-out) {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #f0f0f8;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .title {
|
||||||
|
grid-column: 1 / 2;
|
||||||
|
grid-row: 1/ 2;
|
||||||
|
@include PlainText;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .icon {
|
||||||
|
grid-column: 1 / 3;
|
||||||
|
grid-row: 2 / 3;
|
||||||
|
align-self: center;
|
||||||
|
justify-self: center;
|
||||||
|
@include S(width, 70px);
|
||||||
|
@include S(height, 70px);
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .author {
|
||||||
|
grid-column: 1 / 2;
|
||||||
|
grid-row: 3 / 4;
|
||||||
|
@include SuperSmallText;
|
||||||
|
color: $accentColorDark;
|
||||||
|
align-self: end;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .playcount {
|
||||||
|
grid-column: 1 / 2;
|
||||||
|
display: none;
|
||||||
|
grid-row: 3 / 4;
|
||||||
|
@include SuperSmallText;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .upvotes {
|
||||||
|
@include SuperSmallText;
|
||||||
|
grid-column: 2 / 3;
|
||||||
|
grid-row: 3 / 4;
|
||||||
|
color: $accentColorDark;
|
||||||
|
align-self: end;
|
||||||
|
justify-self: end;
|
||||||
|
font-weight: bold;
|
||||||
|
@include S(padding-right, 12px);
|
||||||
|
|
||||||
|
& {
|
||||||
|
/* @load-async */
|
||||||
|
background: uiResource("icons/puzzle_upvotes.png") calc(100% - #{D(2px)}) #{D(
|
||||||
|
3.3px
|
||||||
|
)} / #{D(8px)} #{D(8px)} no-repeat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.completed {
|
||||||
|
.icon,
|
||||||
|
.upvotes,
|
||||||
|
.playcount,
|
||||||
|
.author,
|
||||||
|
.title {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
background: #fafafa;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
@include S(top, 10px);
|
||||||
|
@include S(right, 10px);
|
||||||
|
@include S(width, 30px);
|
||||||
|
@include S(height, 30px);
|
||||||
|
opacity: 0.1;
|
||||||
|
|
||||||
|
& {
|
||||||
|
/* @load-async */
|
||||||
|
background: uiResource("icons/puzzle_complete_indicator.png") center center /
|
||||||
|
contain no-repeat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .loader {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
grid-row: 1 / 3;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
color: $accentColorBright;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -18,8 +18,10 @@ $textLineHeight: 21px;
|
|||||||
$plainTextFontSize: 13px;
|
$plainTextFontSize: 13px;
|
||||||
$plainTextLineHeight: 17px;
|
$plainTextLineHeight: 17px;
|
||||||
|
|
||||||
$supersmallTextFontSize: 10px;
|
$superDuperSmallTextFontSize: 8px;
|
||||||
$supersmallTextLineHeight: 13px;
|
$superDuperSmallTextLineHeight: 9px;
|
||||||
|
$superSmallTextFontSize: 10px;
|
||||||
|
$superSmallTextLineHeight: 13px;
|
||||||
$buttonFontSize: 14px;
|
$buttonFontSize: 14px;
|
||||||
$buttonLineHeight: 18px;
|
$buttonLineHeight: 18px;
|
||||||
|
|
||||||
@ -76,8 +78,16 @@ $mainFontScale: 1;
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mixin SuperDuperSmallText {
|
||||||
|
@include ScaleFont($superDuperSmallTextFontSize, $superDuperSmallTextLineHeight);
|
||||||
|
font-weight: $mainFontWeight;
|
||||||
|
font-family: $mainFont;
|
||||||
|
letter-spacing: $mainFontSpacing;
|
||||||
|
@include DebugText(green);
|
||||||
|
}
|
||||||
|
|
||||||
@mixin SuperSmallText {
|
@mixin SuperSmallText {
|
||||||
@include ScaleFont($supersmallTextFontSize, $supersmallTextLineHeight);
|
@include ScaleFont($superSmallTextFontSize, $superSmallTextLineHeight);
|
||||||
font-weight: $mainFontWeight;
|
font-weight: $mainFontWeight;
|
||||||
font-family: $mainFont;
|
font-family: $mainFont;
|
||||||
letter-spacing: $mainFontSpacing;
|
letter-spacing: $mainFontSpacing;
|
||||||
|
@ -31,6 +31,7 @@ import { PreloadState } from "./states/preload";
|
|||||||
import { SettingsState } from "./states/settings";
|
import { SettingsState } from "./states/settings";
|
||||||
import { ShapezGameAnalytics } from "./platform/browser/game_analytics";
|
import { ShapezGameAnalytics } from "./platform/browser/game_analytics";
|
||||||
import { RestrictionManager } from "./core/restriction_manager";
|
import { RestrictionManager } from "./core/restriction_manager";
|
||||||
|
import { PuzzleMenuState } from "./states/puzzle_menu";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {import("./platform/achievement_provider").AchievementProviderInterface} AchievementProviderInterface
|
* @typedef {import("./platform/achievement_provider").AchievementProviderInterface} AchievementProviderInterface
|
||||||
@ -159,6 +160,7 @@ export class Application {
|
|||||||
KeybindingsState,
|
KeybindingsState,
|
||||||
AboutState,
|
AboutState,
|
||||||
ChangelogState,
|
ChangelogState,
|
||||||
|
PuzzleMenuState,
|
||||||
];
|
];
|
||||||
|
|
||||||
for (let i = 0; i < states.length; ++i) {
|
for (let i = 0; i < states.length; ++i) {
|
||||||
|
@ -474,6 +474,7 @@ export class Camera extends BasicSerializableObject {
|
|||||||
|
|
||||||
// Clamp everything afterwards
|
// Clamp everything afterwards
|
||||||
this.clampZoomLevel();
|
this.clampZoomLevel();
|
||||||
|
this.clampToBounds();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -759,18 +760,16 @@ export class Camera extends BasicSerializableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clamps x, y position within set boundaries
|
* Clamps the center within set boundaries
|
||||||
* @param {Vector} vector
|
|
||||||
*/
|
*/
|
||||||
clampToBounds(vector) {
|
clampToBounds() {
|
||||||
if (!this.root.gameMode.hasBounds()) {
|
if (!this.root.gameMode.hasBounds()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bounds = this.root.gameMode.getBounds().allScaled(globalConfig.tileSize);
|
const bounds = this.root.gameMode.getBounds().allScaled(globalConfig.tileSize);
|
||||||
|
this.center.x = clamp(this.center.x, bounds.x, bounds.x + bounds.w);
|
||||||
vector.x = clamp(vector.x, bounds.x, bounds.x + bounds.w);
|
this.center.y = clamp(this.center.y, bounds.y, bounds.y + bounds.h);
|
||||||
vector.y = clamp(vector.y, bounds.y, bounds.y + bounds.h);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -876,7 +875,7 @@ export class Camera extends BasicSerializableObject {
|
|||||||
// Panning
|
// Panning
|
||||||
this.currentPan = mixVector(this.currentPan, this.desiredPan, 0.06);
|
this.currentPan = mixVector(this.currentPan, this.desiredPan, 0.06);
|
||||||
this.center = this.center.add(this.currentPan.multiplyScalar((0.5 * dt) / this.zoomLevel));
|
this.center = this.center.add(this.currentPan.multiplyScalar((0.5 * dt) / this.zoomLevel));
|
||||||
this.clampToBounds(this.center);
|
this.clampToBounds();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -942,7 +941,7 @@ export class Camera extends BasicSerializableObject {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
this.clampToBounds(this.center);
|
this.clampToBounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1029,7 +1028,7 @@ export class Camera extends BasicSerializableObject {
|
|||||||
this.center.x += moveAmount * forceX * movementSpeed;
|
this.center.x += moveAmount * forceX * movementSpeed;
|
||||||
this.center.y += moveAmount * forceY * movementSpeed;
|
this.center.y += moveAmount * forceY * movementSpeed;
|
||||||
|
|
||||||
this.clampToBounds(this.center);
|
this.clampToBounds();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -177,6 +177,16 @@ export class GameMode extends BasicSerializableObject {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @returns {boolean} */
|
||||||
|
getIsSaveable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @returns {boolean} */
|
||||||
|
getSupportsCopyPaste() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/** @returns {string} */
|
/** @returns {string} */
|
||||||
getBlueprintShapeKey() {
|
getBlueprintShapeKey() {
|
||||||
return "CbCbCbRb:CwCwCwCw";
|
return "CbCbCbRb:CwCwCwCw";
|
||||||
|
@ -1,58 +1,56 @@
|
|||||||
/* typehints:start */
|
/* typehints:start */
|
||||||
import { GameRoot } from "../root";
|
import { globalConfig, IS_MOBILE } from "../../core/config";
|
||||||
/* typehints:end */
|
|
||||||
|
|
||||||
/* dev:start */
|
|
||||||
import { TrailerMaker } from "./trailer_maker";
|
|
||||||
/* dev:end */
|
|
||||||
|
|
||||||
import { Signal } from "../../core/signal";
|
|
||||||
import { DrawParameters } from "../../core/draw_parameters";
|
import { DrawParameters } from "../../core/draw_parameters";
|
||||||
|
/* dev:end */
|
||||||
|
import { Signal } from "../../core/signal";
|
||||||
|
import { KEYMAPPINGS } from "../key_action_mapper";
|
||||||
|
import { MetaBuilding } from "../meta_building";
|
||||||
|
import { GameRoot } from "../root";
|
||||||
|
import { ShapeDefinition } from "../shape_definition";
|
||||||
|
import { HUDBetaOverlay } from "./parts/beta_overlay";
|
||||||
|
import { HUDBlueprintPlacer } from "./parts/blueprint_placer";
|
||||||
import { HUDBuildingsToolbar } from "./parts/buildings_toolbar";
|
import { HUDBuildingsToolbar } from "./parts/buildings_toolbar";
|
||||||
import { HUDBuildingPlacer } from "./parts/building_placer";
|
import { HUDBuildingPlacer } from "./parts/building_placer";
|
||||||
import { HUDBlueprintPlacer } from "./parts/blueprint_placer";
|
import { HUDCatMemes } from "./parts/cat_memes";
|
||||||
import { HUDKeybindingOverlay } from "./parts/keybinding_overlay";
|
import { HUDColorBlindHelper } from "./parts/color_blind_helper";
|
||||||
import { HUDUnlockNotification } from "./parts/unlock_notification";
|
import { HUDConstantSignalEdit } from "./parts/constant_signal_edit";
|
||||||
import { HUDGameMenu } from "./parts/game_menu";
|
import { HUDChangesDebugger } from "./parts/debug_changes";
|
||||||
import { HUDShop } from "./parts/shop";
|
|
||||||
import { IS_MOBILE, globalConfig } from "../../core/config";
|
|
||||||
import { HUDMassSelector } from "./parts/mass_selector";
|
|
||||||
import { HUDVignetteOverlay } from "./parts/vignette_overlay";
|
|
||||||
import { HUDStatistics } from "./parts/statistics";
|
|
||||||
import { MetaBuilding } from "../meta_building";
|
|
||||||
import { HUDPinnedShapes } from "./parts/pinned_shapes";
|
|
||||||
import { ShapeDefinition } from "../shape_definition";
|
|
||||||
import { HUDNotifications, enumNotificationType } from "./parts/notifications";
|
|
||||||
import { HUDSettingsMenu } from "./parts/settings_menu";
|
|
||||||
import { HUDDebugInfo } from "./parts/debug_info";
|
import { HUDDebugInfo } from "./parts/debug_info";
|
||||||
import { HUDEntityDebugger } from "./parts/entity_debugger";
|
import { HUDEntityDebugger } from "./parts/entity_debugger";
|
||||||
import { KEYMAPPINGS } from "../key_action_mapper";
|
import { HUDGameMenu } from "./parts/game_menu";
|
||||||
import { HUDWatermark } from "./parts/watermark";
|
|
||||||
import { HUDModalDialogs } from "./parts/modal_dialogs";
|
|
||||||
import { HUDPartTutorialHints } from "./parts/tutorial_hints";
|
|
||||||
import { HUDWaypoints } from "./parts/waypoints";
|
|
||||||
import { HUDInteractiveTutorial } from "./parts/interactive_tutorial";
|
import { HUDInteractiveTutorial } from "./parts/interactive_tutorial";
|
||||||
import { HUDScreenshotExporter } from "./parts/screenshot_exporter";
|
import { HUDKeybindingOverlay } from "./parts/keybinding_overlay";
|
||||||
import { HUDColorBlindHelper } from "./parts/color_blind_helper";
|
|
||||||
import { HUDShapeViewer } from "./parts/shape_viewer";
|
|
||||||
import { HUDWiresOverlay } from "./parts/wires_overlay";
|
|
||||||
import { HUDChangesDebugger } from "./parts/debug_changes";
|
|
||||||
import { queryParamOptions } from "../../core/query_parameters";
|
|
||||||
import { HUDSandboxController } from "./parts/sandbox_controller";
|
|
||||||
import { HUDWiresToolbar } from "./parts/wires_toolbar";
|
|
||||||
import { HUDWireInfo } from "./parts/wire_info";
|
|
||||||
import { HUDLeverToggle } from "./parts/lever_toggle";
|
|
||||||
import { HUDLayerPreview } from "./parts/layer_preview";
|
import { HUDLayerPreview } from "./parts/layer_preview";
|
||||||
|
import { HUDLeverToggle } from "./parts/lever_toggle";
|
||||||
|
import { HUDMassSelector } from "./parts/mass_selector";
|
||||||
import { HUDMinerHighlight } from "./parts/miner_highlight";
|
import { HUDMinerHighlight } from "./parts/miner_highlight";
|
||||||
import { HUDBetaOverlay } from "./parts/beta_overlay";
|
import { HUDModalDialogs } from "./parts/modal_dialogs";
|
||||||
import { HUDStandaloneAdvantages } from "./parts/standalone_advantages";
|
import { HUDModeMenu } from "./parts/mode_menu";
|
||||||
import { HUDCatMemes } from "./parts/cat_memes";
|
|
||||||
import { HUDTutorialVideoOffer } from "./parts/tutorial_video_offer";
|
|
||||||
import { HUDConstantSignalEdit } from "./parts/constant_signal_edit";
|
|
||||||
import { HUDModeMenuBack } from "./parts/mode_menu_back";
|
import { HUDModeMenuBack } from "./parts/mode_menu_back";
|
||||||
import { HUDModeMenuNext } from "./parts/mode_menu_next";
|
import { HUDModeMenuNext } from "./parts/mode_menu_next";
|
||||||
import { HUDModeMenu } from "./parts/mode_menu";
|
|
||||||
import { HUDModeSettings } from "./parts/mode_settings";
|
import { HUDModeSettings } from "./parts/mode_settings";
|
||||||
|
import { enumNotificationType, HUDNotifications } from "./parts/notifications";
|
||||||
|
import { HUDPinnedShapes } from "./parts/pinned_shapes";
|
||||||
|
import { HUDPuzzleDLCLogo } from "./parts/puzzle_dlc_logo";
|
||||||
|
import { HUDSandboxController } from "./parts/sandbox_controller";
|
||||||
|
import { HUDScreenshotExporter } from "./parts/screenshot_exporter";
|
||||||
|
import { HUDSettingsMenu } from "./parts/settings_menu";
|
||||||
|
import { HUDShapeViewer } from "./parts/shape_viewer";
|
||||||
|
import { HUDShop } from "./parts/shop";
|
||||||
|
import { HUDStandaloneAdvantages } from "./parts/standalone_advantages";
|
||||||
|
import { HUDStatistics } from "./parts/statistics";
|
||||||
|
import { HUDPartTutorialHints } from "./parts/tutorial_hints";
|
||||||
|
import { HUDTutorialVideoOffer } from "./parts/tutorial_video_offer";
|
||||||
|
import { HUDUnlockNotification } from "./parts/unlock_notification";
|
||||||
|
import { HUDVignetteOverlay } from "./parts/vignette_overlay";
|
||||||
|
import { HUDWatermark } from "./parts/watermark";
|
||||||
|
import { HUDWaypoints } from "./parts/waypoints";
|
||||||
|
import { HUDWiresOverlay } from "./parts/wires_overlay";
|
||||||
|
import { HUDWiresToolbar } from "./parts/wires_toolbar";
|
||||||
|
import { HUDWireInfo } from "./parts/wire_info";
|
||||||
|
/* typehints:end */
|
||||||
|
/* dev:start */
|
||||||
|
import { TrailerMaker } from "./trailer_maker";
|
||||||
|
|
||||||
export class GameHUD {
|
export class GameHUD {
|
||||||
/**
|
/**
|
||||||
@ -96,6 +94,7 @@ export class GameHUD {
|
|||||||
modeMenuNext: HUDModeMenuNext,
|
modeMenuNext: HUDModeMenuNext,
|
||||||
modeMenu: HUDModeMenu,
|
modeMenu: HUDModeMenu,
|
||||||
modeSettings: HUDModeSettings,
|
modeSettings: HUDModeSettings,
|
||||||
|
puzzleDlcLogo: HUDPuzzleDLCLogo,
|
||||||
|
|
||||||
// Must always exist
|
// Must always exist
|
||||||
pinnedShapes: HUDPinnedShapes,
|
pinnedShapes: HUDPinnedShapes,
|
||||||
|
@ -9,7 +9,7 @@ export class HUDModeMenuBack extends BaseHUDPart {
|
|||||||
this.element = makeDiv(parent, "ingame_HUD_ModeMenuBack");
|
this.element = makeDiv(parent, "ingame_HUD_ModeMenuBack");
|
||||||
this.button = document.createElement("button");
|
this.button = document.createElement("button");
|
||||||
this.button.classList.add("button");
|
this.button.classList.add("button");
|
||||||
this.button.textContent = "⬅ " + T.ingame.modeMenu[key].back.title;
|
this.button.textContent = T.ingame.modeMenu[key].back.title;
|
||||||
this.element.appendChild(this.button);
|
this.element.appendChild(this.button);
|
||||||
|
|
||||||
this.trackClicks(this.button, this.back);
|
this.trackClicks(this.button, this.back);
|
||||||
|
@ -9,7 +9,7 @@ export class HUDModeMenuNext extends BaseHUDPart {
|
|||||||
this.element = makeDiv(parent, "ingame_HUD_ModeMenuNext");
|
this.element = makeDiv(parent, "ingame_HUD_ModeMenuNext");
|
||||||
this.button = document.createElement("button");
|
this.button = document.createElement("button");
|
||||||
this.button.classList.add("button");
|
this.button.classList.add("button");
|
||||||
this.button.textContent = T.ingame.modeMenu[key].next.title + " ➡ ";
|
this.button.textContent = T.ingame.modeMenu[key].next.title;
|
||||||
this.element.appendChild(this.button);
|
this.element.appendChild(this.button);
|
||||||
|
|
||||||
this.content = makeDiv(this.element, null, ["content"], T.ingame.modeMenu[key].next.desc);
|
this.content = makeDiv(this.element, null, ["content"], T.ingame.modeMenu[key].next.desc);
|
||||||
|
13
src/js/game/hud/parts/puzzle_dlc_logo.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { makeDiv } from "../../../core/utils";
|
||||||
|
import { BaseHUDPart } from "../base_hud_part";
|
||||||
|
|
||||||
|
export class HUDPuzzleDLCLogo extends BaseHUDPart {
|
||||||
|
createElements(parent) {
|
||||||
|
this.element = makeDiv(parent, "ingame_HUD_PuzzleDLCLogo");
|
||||||
|
parent.appendChild(this.element);
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {}
|
||||||
|
|
||||||
|
next() {}
|
||||||
|
}
|
@ -13,6 +13,7 @@ export class HUDSettingsMenu extends BaseHUDPart {
|
|||||||
|
|
||||||
this.menuElement = makeDiv(this.background, null, ["menuElement"]);
|
this.menuElement = makeDiv(this.background, null, ["menuElement"]);
|
||||||
|
|
||||||
|
if (this.root.gameMode.hasHub()) {
|
||||||
this.statsElement = makeDiv(
|
this.statsElement = makeDiv(
|
||||||
this.background,
|
this.background,
|
||||||
null,
|
null,
|
||||||
@ -24,6 +25,7 @@ export class HUDSettingsMenu extends BaseHUDPart {
|
|||||||
|
|
||||||
`
|
`
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
this.buttonContainer = makeDiv(this.menuElement, null, ["buttons"]);
|
this.buttonContainer = makeDiv(this.menuElement, null, ["buttons"]);
|
||||||
|
|
||||||
@ -94,6 +96,7 @@ export class HUDSettingsMenu extends BaseHUDPart {
|
|||||||
|
|
||||||
const totalMinutesPlayed = Math.ceil(this.root.time.now() / 60);
|
const totalMinutesPlayed = Math.ceil(this.root.time.now() / 60);
|
||||||
|
|
||||||
|
if (this.root.gameMode.hasHub()) {
|
||||||
/** @type {HTMLElement} */
|
/** @type {HTMLElement} */
|
||||||
const playtimeElement = this.statsElement.querySelector(".playtime");
|
const playtimeElement = this.statsElement.querySelector(".playtime");
|
||||||
/** @type {HTMLElement} */
|
/** @type {HTMLElement} */
|
||||||
@ -112,6 +115,7 @@ export class HUDSettingsMenu extends BaseHUDPart {
|
|||||||
this.root.entityMgr.getAllWithComponent(BeltComponent).length
|
this.root.entityMgr.getAllWithComponent(BeltComponent).length
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
this.visible = false;
|
this.visible = false;
|
||||||
|
@ -11,6 +11,7 @@ import { HUDKeybindingOverlay } from "../hud/parts/keybinding_overlay";
|
|||||||
import { HUDPartTutorialHints } from "../hud/parts/tutorial_hints";
|
import { HUDPartTutorialHints } from "../hud/parts/tutorial_hints";
|
||||||
import { HUDPinnedShapes } from "../hud/parts/pinned_shapes";
|
import { HUDPinnedShapes } from "../hud/parts/pinned_shapes";
|
||||||
import { HUDWaypoints } from "../hud/parts/waypoints";
|
import { HUDWaypoints } from "../hud/parts/waypoints";
|
||||||
|
import { HUDMassSelector } from "../hud/parts/mass_selector";
|
||||||
|
|
||||||
export class PuzzleGameMode extends GameMode {
|
export class PuzzleGameMode extends GameMode {
|
||||||
static getType() {
|
static getType() {
|
||||||
@ -33,6 +34,7 @@ export class PuzzleGameMode extends GameMode {
|
|||||||
|
|
||||||
this.setHudParts({
|
this.setHudParts({
|
||||||
[HUDGameMenu.name]: false,
|
[HUDGameMenu.name]: false,
|
||||||
|
[HUDMassSelector.name]: false,
|
||||||
[HUDInteractiveTutorial.name]: false,
|
[HUDInteractiveTutorial.name]: false,
|
||||||
[HUDKeybindingOverlay.name]: false,
|
[HUDKeybindingOverlay.name]: false,
|
||||||
[HUDPartTutorialHints.name]: false,
|
[HUDPartTutorialHints.name]: false,
|
||||||
@ -122,6 +124,14 @@ export class PuzzleGameMode extends GameMode {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getIsSaveable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSupportsCopyPaste() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/** @returns {boolean} */
|
/** @returns {boolean} */
|
||||||
getIsFreeplayAvailable() {
|
getIsFreeplayAvailable() {
|
||||||
return true;
|
return true;
|
||||||
|
@ -14,6 +14,7 @@ import { HUDModeSettings } from "../hud/parts/mode_settings";
|
|||||||
import { enumGameModeIds, enumGameModeTypes, GameMode } from "../game_mode";
|
import { enumGameModeIds, enumGameModeTypes, GameMode } from "../game_mode";
|
||||||
import { ShapeDefinition } from "../shape_definition";
|
import { ShapeDefinition } from "../shape_definition";
|
||||||
import { enumHubGoalRewards } from "../tutorial_goals";
|
import { enumHubGoalRewards } from "../tutorial_goals";
|
||||||
|
import { HUDPuzzleDLCLogo } from "../hud/parts/puzzle_dlc_logo";
|
||||||
|
|
||||||
/** @typedef {{
|
/** @typedef {{
|
||||||
* shape: string,
|
* shape: string,
|
||||||
@ -522,6 +523,7 @@ export class RegularGameMode extends GameMode {
|
|||||||
[HUDModeMenuNext.name]: false,
|
[HUDModeMenuNext.name]: false,
|
||||||
[HUDModeMenu.name]: false,
|
[HUDModeMenu.name]: false,
|
||||||
[HUDModeSettings.name]: false,
|
[HUDModeSettings.name]: false,
|
||||||
|
[HUDPuzzleDLCLogo.name]: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.setBuildings({
|
this.setBuildings({
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
/* typehints:start */
|
/* typehints:start */
|
||||||
import { GameRoot } from "../root";
|
|
||||||
/* typehints:end */
|
/* typehints:end */
|
||||||
|
|
||||||
import { globalConfig } from "../../core/config";
|
import { globalConfig } from "../../core/config";
|
||||||
|
import { DrawParameters } from "../../core/draw_parameters";
|
||||||
import { ConstantSignalComponent } from "../components/constant_signal";
|
import { ConstantSignalComponent } from "../components/constant_signal";
|
||||||
import { ItemProducerComponent } from "../components/item_producer";
|
import { ItemProducerComponent } from "../components/item_producer";
|
||||||
import { GameSystemWithFilter } from "../game_system_with_filter";
|
import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||||
|
import { MapChunk } from "../map_chunk";
|
||||||
|
import { GameRoot } from "../root";
|
||||||
|
|
||||||
export class ConstantProducerSystem extends GameSystemWithFilter {
|
export class ConstantProducerSystem extends GameSystemWithFilter {
|
||||||
/** @param {GameRoot} root */
|
/** @param {GameRoot} root */
|
||||||
@ -29,6 +30,12 @@ export class ConstantProducerSystem extends GameSystemWithFilter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {DrawParameters} parameters
|
||||||
|
* @param {MapChunk} chunk
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
drawChunk(parameters, chunk) {
|
drawChunk(parameters, chunk) {
|
||||||
const contents = chunk.containedEntitiesByLayer.regular;
|
const contents = chunk.containedEntitiesByLayer.regular;
|
||||||
for (let i = 0; i < contents.length; ++i) {
|
for (let i = 0; i < contents.length; ++i) {
|
||||||
@ -48,7 +55,7 @@ export class ConstantProducerSystem extends GameSystemWithFilter {
|
|||||||
|
|
||||||
// TODO: Better looking overlay
|
// TODO: Better looking overlay
|
||||||
const center = staticComp.getTileSpaceBounds().getCenter().toWorldSpace();
|
const center = staticComp.getTileSpaceBounds().getCenter().toWorldSpace();
|
||||||
item.drawItemCenteredClipped(center.x, center.y, parameters, globalConfig.tileSize);
|
item.drawItemCenteredClipped(center.x, center.y + 1, parameters, globalConfig.tileSize * 0.65);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,12 +61,14 @@ export class ConstantSignalSystem extends GameSystemWithFilter {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const items = [
|
const items = [
|
||||||
BOOL_FALSE_SINGLETON,
|
|
||||||
BOOL_TRUE_SINGLETON,
|
|
||||||
...Object.values(COLOR_ITEM_SINGLETONS),
|
...Object.values(COLOR_ITEM_SINGLETONS),
|
||||||
this.root.shapeDefinitionMgr.getShapeItemFromShortKey(this.root.gameMode.getBlueprintShapeKey()),
|
this.root.shapeDefinitionMgr.getShapeItemFromShortKey(this.root.gameMode.getBlueprintShapeKey()),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (entity.components.ConstantSignal.type === enumConstantSignalType.wired) {
|
||||||
|
items.unshift(BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.root.gameMode.hasHub()) {
|
if (this.root.gameMode.hasHub()) {
|
||||||
items.push(
|
items.push(
|
||||||
this.root.shapeDefinitionMgr.getShapeItemFromDefinition(
|
this.root.shapeDefinitionMgr.getShapeItemFromDefinition(
|
||||||
|
@ -8,6 +8,7 @@ import { KeyActionMapper } from "../game/key_action_mapper";
|
|||||||
import { Savegame } from "../savegame/savegame";
|
import { Savegame } from "../savegame/savegame";
|
||||||
import { GameCore } from "../game/core";
|
import { GameCore } from "../game/core";
|
||||||
import { MUSIC } from "../platform/sound";
|
import { MUSIC } from "../platform/sound";
|
||||||
|
import { enumGameModeIds } from "../game/game_mode";
|
||||||
|
|
||||||
const logger = createLogger("state/ingame");
|
const logger = createLogger("state/ingame");
|
||||||
|
|
||||||
@ -150,8 +151,12 @@ export class InGameState extends GameState {
|
|||||||
* Goes back to the menu state
|
* Goes back to the menu state
|
||||||
*/
|
*/
|
||||||
goBackToMenu() {
|
goBackToMenu() {
|
||||||
|
if ([enumGameModeIds.puzzleEdit, enumGameModeIds.puzzlePlay].includes(this.gameModeId)) {
|
||||||
|
this.saveThenGoToState("PuzzleMenuState");
|
||||||
|
} else {
|
||||||
this.saveThenGoToState("MainMenuState");
|
this.saveThenGoToState("MainMenuState");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Goes back to the settings state
|
* Goes back to the settings state
|
||||||
@ -437,6 +442,11 @@ export class InGameState extends GameState {
|
|||||||
logger.warn("Skipping double save and returning same promise");
|
logger.warn("Skipping double save and returning same promise");
|
||||||
return this.currentSavePromise;
|
return this.currentSavePromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this.core.root.gameMode.getIsSaveable()) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
logger.log("Starting to save game ...");
|
logger.log("Starting to save game ...");
|
||||||
this.savegame.updateData(this.core.root);
|
this.savegame.updateData(this.core.root);
|
||||||
|
|
||||||
|
@ -207,12 +207,12 @@ export class MainMenuState extends GameState {
|
|||||||
|
|
||||||
const qs = this.htmlElement.querySelector.bind(this.htmlElement);
|
const qs = this.htmlElement.querySelector.bind(this.htmlElement);
|
||||||
|
|
||||||
if (G_IS_DEV && globalConfig.debug.fastGameEnter) {
|
if (G_IS_DEV && globalConfig.debug.testPuzzleMode) {
|
||||||
if (globalConfig.debug.testPuzzleMode) {
|
this.onPuzzleModeButtonClicked();
|
||||||
this.onPuzzleEditButtonClicked();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (G_IS_DEV && globalConfig.debug.fastGameEnter) {
|
||||||
const games = this.app.savegameMgr.getSavegamesMetaData();
|
const games = this.app.savegameMgr.getSavegamesMetaData();
|
||||||
if (games.length > 0 && globalConfig.debug.resumeGameOnFastEnter) {
|
if (games.length > 0 && globalConfig.debug.resumeGameOnFastEnter) {
|
||||||
this.resumeGame(games[0]);
|
this.resumeGame(games[0]);
|
||||||
@ -369,7 +369,7 @@ export class MainMenuState extends GameState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onPuzzleModeButtonClicked() {
|
onPuzzleModeButtonClicked() {
|
||||||
this.renderPuzzleModeMenu();
|
this.moveToState("PuzzleMenuState");
|
||||||
}
|
}
|
||||||
|
|
||||||
onBackButtonClicked() {
|
onBackButtonClicked() {
|
||||||
|
218
src/js/states/puzzle_menu.js
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
import { TextualGameState } from "../core/textual_game_state";
|
||||||
|
import { formatBigNumberFull } from "../core/utils";
|
||||||
|
import { enumGameModeIds } from "../game/game_mode";
|
||||||
|
import { ShapeDefinition } from "../game/shape_definition";
|
||||||
|
import { T } from "../translations";
|
||||||
|
|
||||||
|
const categories = ["levels", "new", "topRated", "myPuzzles"];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {{
|
||||||
|
* shortKey: string;
|
||||||
|
* upvotes: number;
|
||||||
|
* playcount: number;
|
||||||
|
* title: string;
|
||||||
|
* author: string;
|
||||||
|
* completed: boolean;
|
||||||
|
* }} PuzzleMetadata
|
||||||
|
*/
|
||||||
|
|
||||||
|
const SAMPLE_PUZZLE = {
|
||||||
|
shortKey: "CuCuCuCu",
|
||||||
|
upvotes: 10000,
|
||||||
|
playcount: 1000,
|
||||||
|
title: "Level 1",
|
||||||
|
author: "verylongsteamnamewhichbreaks",
|
||||||
|
completed: false,
|
||||||
|
};
|
||||||
|
const BUILTIN_PUZZLES = [
|
||||||
|
{ ...SAMPLE_PUZZLE, completed: true },
|
||||||
|
{ ...SAMPLE_PUZZLE, completed: true },
|
||||||
|
SAMPLE_PUZZLE,
|
||||||
|
SAMPLE_PUZZLE,
|
||||||
|
SAMPLE_PUZZLE,
|
||||||
|
SAMPLE_PUZZLE,
|
||||||
|
SAMPLE_PUZZLE,
|
||||||
|
SAMPLE_PUZZLE,
|
||||||
|
SAMPLE_PUZZLE,
|
||||||
|
SAMPLE_PUZZLE,
|
||||||
|
SAMPLE_PUZZLE,
|
||||||
|
SAMPLE_PUZZLE,
|
||||||
|
SAMPLE_PUZZLE,
|
||||||
|
];
|
||||||
|
|
||||||
|
export class PuzzleMenuState extends TextualGameState {
|
||||||
|
constructor() {
|
||||||
|
super("PuzzleMenuState");
|
||||||
|
this.loading = false;
|
||||||
|
this.activeCategory = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
getStateHeaderTitle() {
|
||||||
|
return T.puzzleMenu.title;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Overrides the GameState implementation to provide our own html
|
||||||
|
*/
|
||||||
|
internalGetFullHtml() {
|
||||||
|
let headerHtml = `
|
||||||
|
<div class="headerBar">
|
||||||
|
<h1><button class="backButton"></button> ${this.getStateHeaderTitle()}</h1>
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
<button class="styledButton createPuzzle">+ ${T.puzzleMenu.createPuzzle}</button>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
return `
|
||||||
|
${headerHtml}
|
||||||
|
<div class="container">
|
||||||
|
${this.getInnerHTML()}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
getMainContentHTML() {
|
||||||
|
let html = `
|
||||||
|
|
||||||
|
|
||||||
|
<div class="categoryChooser">
|
||||||
|
${categories
|
||||||
|
.map(
|
||||||
|
category => `
|
||||||
|
<button data-category="${category}" class="styledButton category">${T.puzzleMenu.categories[category]}</button>
|
||||||
|
`
|
||||||
|
)
|
||||||
|
.join("")}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="puzzles" id="mainContainer">
|
||||||
|
<div class="puzzle"></div>
|
||||||
|
<div class="puzzle"></div>
|
||||||
|
<div class="puzzle"></div>
|
||||||
|
<div class="puzzle"></div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectCategory(category) {
|
||||||
|
if (category === this.activeCategory) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.loading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.loading = true;
|
||||||
|
this.activeCategory = category;
|
||||||
|
|
||||||
|
const activeCategory = this.htmlElement.querySelector(".active[data-category]");
|
||||||
|
if (activeCategory) {
|
||||||
|
activeCategory.classList.remove("active");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.htmlElement.querySelector(`[data-category="${category}"]`).classList.add("active");
|
||||||
|
|
||||||
|
const container = this.htmlElement.querySelector("#mainContainer");
|
||||||
|
while (container.firstChild) {
|
||||||
|
container.removeChild(container.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadingElement = document.createElement("div");
|
||||||
|
loadingElement.classList.add("loader");
|
||||||
|
loadingElement.innerText = T.global.loading + "...";
|
||||||
|
container.appendChild(loadingElement);
|
||||||
|
|
||||||
|
this.asyncChannel
|
||||||
|
.watch(this.getPuzzlesForCategory(category))
|
||||||
|
.then(
|
||||||
|
puzzles => this.renderPuzzles(puzzles),
|
||||||
|
error => {
|
||||||
|
this.dialogs.showWarning(
|
||||||
|
T.dialogs.puzzleLoadFailed.title,
|
||||||
|
T.dialogs.puzzleLoadFailed.desc + " " + error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(() => (this.loading = false));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {PuzzleMetadata[]} puzzles
|
||||||
|
*/
|
||||||
|
renderPuzzles(puzzles) {
|
||||||
|
const container = this.htmlElement.querySelector("#mainContainer");
|
||||||
|
while (container.firstChild) {
|
||||||
|
container.removeChild(container.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const puzzle of puzzles) {
|
||||||
|
const elem = document.createElement("div");
|
||||||
|
elem.classList.add("puzzle");
|
||||||
|
elem.classList.toggle("completed", puzzle.completed);
|
||||||
|
|
||||||
|
if (puzzle.title) {
|
||||||
|
const title = document.createElement("div");
|
||||||
|
title.classList.add("title");
|
||||||
|
title.innerText = puzzle.title;
|
||||||
|
elem.appendChild(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (puzzle.author) {
|
||||||
|
const author = document.createElement("div");
|
||||||
|
author.classList.add("author");
|
||||||
|
author.innerText = "by " + puzzle.author;
|
||||||
|
elem.appendChild(author);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (puzzle.upvotes) {
|
||||||
|
const upvotes = document.createElement("div");
|
||||||
|
upvotes.classList.add("upvotes");
|
||||||
|
upvotes.innerText = formatBigNumberFull(puzzle.upvotes);
|
||||||
|
elem.appendChild(upvotes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (puzzle.playcount) {
|
||||||
|
const playcount = document.createElement("div");
|
||||||
|
playcount.classList.add("playcount");
|
||||||
|
playcount.innerText = String(puzzle.playcount) + " plays";
|
||||||
|
elem.appendChild(playcount);
|
||||||
|
}
|
||||||
|
|
||||||
|
const definition = ShapeDefinition.fromShortKey(puzzle.shortKey);
|
||||||
|
const canvas = definition.generateAsCanvas(100 * this.app.getEffectiveUiScale());
|
||||||
|
|
||||||
|
const icon = document.createElement("div");
|
||||||
|
icon.classList.add("icon");
|
||||||
|
icon.appendChild(canvas);
|
||||||
|
elem.appendChild(icon);
|
||||||
|
|
||||||
|
container.appendChild(elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getPuzzlesForCategory(category) {
|
||||||
|
return new Promise(resolve => setTimeout(() => resolve(BUILTIN_PUZZLES), 100));
|
||||||
|
}
|
||||||
|
|
||||||
|
onEnter() {
|
||||||
|
this.selectCategory("levels");
|
||||||
|
|
||||||
|
for (const category of categories) {
|
||||||
|
const button = this.htmlElement.querySelector(`[data-category="${category}"]`);
|
||||||
|
this.trackClicks(button, () => this.selectCategory(category));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.trackClicks(this.htmlElement.querySelector("button.createPuzzle"), this.createNewPuzzle);
|
||||||
|
}
|
||||||
|
|
||||||
|
createNewPuzzle() {
|
||||||
|
const savegame = this.app.savegameMgr.createNewSavegame();
|
||||||
|
this.moveToState("InGameState", {
|
||||||
|
gameModeId: enumGameModeIds.puzzleEdit,
|
||||||
|
savegame,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -122,6 +122,14 @@ mainMenu:
|
|||||||
puzzleMenu:
|
puzzleMenu:
|
||||||
play: Play
|
play: Play
|
||||||
edit: Edit
|
edit: Edit
|
||||||
|
title: Puzzle Mode
|
||||||
|
createPuzzle: Create Puzzle
|
||||||
|
|
||||||
|
categories:
|
||||||
|
levels: Levels
|
||||||
|
new: New
|
||||||
|
topRated: Top Rated
|
||||||
|
myPuzzles: My Puzzles
|
||||||
|
|
||||||
dialogs:
|
dialogs:
|
||||||
buttons:
|
buttons:
|
||||||
@ -259,6 +267,11 @@ dialogs:
|
|||||||
title: Tutorial Available
|
title: Tutorial Available
|
||||||
desc: There is a tutorial video available for this level, but it is only available in English. Would you like to watch it?
|
desc: There is a tutorial video available for this level, but it is only available in English. Would you like to watch it?
|
||||||
|
|
||||||
|
puzzleLoadFailed:
|
||||||
|
title: Puzzles failed to load
|
||||||
|
desc: >-
|
||||||
|
Unfortunately the puzzles could not be loaded:
|
||||||
|
|
||||||
ingame:
|
ingame:
|
||||||
# This is shown in the top left corner and displays useful keybindings in
|
# This is shown in the top left corner and displays useful keybindings in
|
||||||
# every situation
|
# every situation
|
||||||
@ -486,10 +499,10 @@ ingame:
|
|||||||
modeMenu:
|
modeMenu:
|
||||||
puzzleEditMode:
|
puzzleEditMode:
|
||||||
back:
|
back:
|
||||||
title: Main Menu
|
title: Menu
|
||||||
next:
|
next:
|
||||||
title: Playtest
|
title: Playtest
|
||||||
desc: You will have to complete the puzzle before being able to publish it
|
desc: Required for publishing
|
||||||
puzzleEditTestMode:
|
puzzleEditTestMode:
|
||||||
back:
|
back:
|
||||||
title: Edit
|
title: Edit
|
||||||
@ -497,7 +510,7 @@ ingame:
|
|||||||
title: Publish
|
title: Publish
|
||||||
puzzlePlayMode:
|
puzzlePlayMode:
|
||||||
back:
|
back:
|
||||||
title: Puzzle Menu
|
title: Menu
|
||||||
next:
|
next:
|
||||||
title: Next
|
title: Next
|
||||||
|
|
||||||
|