mirror of
https://github.com/tobspr/shapez.io.git
synced 2026-02-12 10:59:23 +00:00
Added a test mode for the puzzle editor, and added the ability to download and import puzzles to the editor
This commit is contained in:
parent
aea668d7e3
commit
2add9ba18f
@ -116,6 +116,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.puzzle-lock {
|
.puzzle-lock {
|
||||||
|
&.active {
|
||||||
& {
|
& {
|
||||||
/* @load-async */
|
/* @load-async */
|
||||||
background: uiResource("locked_building.png") center center / 90% no-repeat;
|
background: uiResource("locked_building.png") center center / 90% no-repeat;
|
||||||
@ -144,4 +145,5 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
39
src/css/ingame_hud/puzzle_editor_download.scss
Normal file
39
src/css/ingame_hud/puzzle_editor_download.scss
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#ingame_HUD_PuzzleEditorDownload {
|
||||||
|
position: absolute;
|
||||||
|
@include S(top, 35px);
|
||||||
|
left: 50%;
|
||||||
|
|
||||||
|
transform: translateX(-50%);
|
||||||
|
backdrop-filter: blur(D(1px));
|
||||||
|
padding: D(3px);
|
||||||
|
|
||||||
|
> .button {
|
||||||
|
@include PlainText;
|
||||||
|
pointer-events: all;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
color: #333438;
|
||||||
|
transition: all 0.12s ease-in-out;
|
||||||
|
transition-property: opacity, transform;
|
||||||
|
text-transform: uppercase;
|
||||||
|
@include PlainText;
|
||||||
|
@include S(width, 30px);
|
||||||
|
@include S(height, 30px);
|
||||||
|
|
||||||
|
@include DarkThemeInvert;
|
||||||
|
|
||||||
|
opacity: 1;
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.9 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.pressed {
|
||||||
|
transform: scale(0.95) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
& {
|
||||||
|
/* @load-async */
|
||||||
|
background: uiResource("icons/download.png") center center / D(15px) no-repeat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,7 +2,7 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
background: $ingameHudBg;
|
background: $ingameHudBg;
|
||||||
@include S(padding, 10px);
|
@include S(padding, 10px);
|
||||||
@include S(bottom, 60px);
|
@include S(bottom, 70px);
|
||||||
@include S(left, 10px);
|
@include S(left, 10px);
|
||||||
|
|
||||||
@include SuperSmallText;
|
@include SuperSmallText;
|
||||||
@ -44,7 +44,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> .buttons {
|
> .mainButtons {
|
||||||
|
&.disabled {
|
||||||
|
button {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
> .buttonBar {
|
> .buttonBar {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -57,15 +63,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
> .buildingsButton {
|
> .testToggle {
|
||||||
display: grid;
|
display: grid;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@include S(margin-top, 4px);
|
@include S(margin-top, 4px);
|
||||||
> button {
|
> button {
|
||||||
|
&.disabled {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
@include SuperSmallText;
|
@include SuperSmallText;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -65,6 +65,7 @@
|
|||||||
@import "ingame_hud/puzzle_play_settings";
|
@import "ingame_hud/puzzle_play_settings";
|
||||||
@import "ingame_hud/puzzle_play_metadata";
|
@import "ingame_hud/puzzle_play_metadata";
|
||||||
@import "ingame_hud/puzzle_complete_notification";
|
@import "ingame_hud/puzzle_complete_notification";
|
||||||
|
@import "ingame_hud/puzzle_editor_download";
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
$elements:
|
$elements:
|
||||||
@ -84,6 +85,7 @@ ingame_HUD_GameMenu,
|
|||||||
ingame_HUD_KeybindingOverlay,
|
ingame_HUD_KeybindingOverlay,
|
||||||
ingame_HUD_PuzzleBackToMenu,
|
ingame_HUD_PuzzleBackToMenu,
|
||||||
ingame_HUD_PuzzleEditorReview,
|
ingame_HUD_PuzzleEditorReview,
|
||||||
|
ingame_HUD_PuzzleEditorDownload,
|
||||||
ingame_HUD_PuzzleEditorControls,
|
ingame_HUD_PuzzleEditorControls,
|
||||||
ingame_HUD_PuzzleEditorTitle,
|
ingame_HUD_PuzzleEditorTitle,
|
||||||
ingame_HUD_PuzzleEditorSettings,
|
ingame_HUD_PuzzleEditorSettings,
|
||||||
@ -133,8 +135,11 @@ body.uiHidden {
|
|||||||
#ingame_HUD_PlacementHints,
|
#ingame_HUD_PlacementHints,
|
||||||
#ingame_HUD_GameMenu,
|
#ingame_HUD_GameMenu,
|
||||||
#ingame_HUD_PinnedShapes,
|
#ingame_HUD_PinnedShapes,
|
||||||
#ingame_HUD_PuzzleBackToMenu,
|
#ingame_HUD_PuzzleEditorSettings,
|
||||||
#ingame_HUD_PuzzleEditorReview,
|
#ingame_HUD_PuzzlePlaySettings,
|
||||||
|
#ingame_HUD_PuzzleEditorControls,
|
||||||
|
#ingame_HUD_PuzzleEditorDownload,
|
||||||
|
#ingame_HUD_PuzzlePlayMetadata,
|
||||||
#ingame_HUD_Notifications,
|
#ingame_HUD_Notifications,
|
||||||
#ingame_HUD_TutorialHints,
|
#ingame_HUD_TutorialHints,
|
||||||
#ingame_HUD_Waypoints,
|
#ingame_HUD_Waypoints,
|
||||||
|
|||||||
@ -8,9 +8,13 @@
|
|||||||
justify-self: start;
|
justify-self: start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
@include S(margin-right, 5px);
|
||||||
|
}
|
||||||
|
|
||||||
.createPuzzle {
|
.createPuzzle {
|
||||||
background-color: $colorGreenBright;
|
background-color: $colorGreenBright;
|
||||||
@include S(margin-left, 5px);
|
@include S(margin-right, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -33,6 +33,12 @@ export class HUDBaseToolbar extends BaseHUDPart {
|
|||||||
this.htmlElementId = htmlElementId;
|
this.htmlElementId = htmlElementId;
|
||||||
this.layer = layer;
|
this.layer = layer;
|
||||||
|
|
||||||
|
this.requiredBuildings = [
|
||||||
|
gMetaBuildingRegistry.findByClass(MetaConstantProducerBuilding),
|
||||||
|
gMetaBuildingRegistry.findByClass(MetaGoalAcceptorBuilding),
|
||||||
|
gMetaBuildingRegistry.findByClass(MetaBlockBuilding),
|
||||||
|
];
|
||||||
|
|
||||||
/** @type {Object.<string, {
|
/** @type {Object.<string, {
|
||||||
* metaBuilding: MetaBuilding,
|
* metaBuilding: MetaBuilding,
|
||||||
* unlocked: boolean,
|
* unlocked: boolean,
|
||||||
@ -60,12 +66,10 @@ export class HUDBaseToolbar extends BaseHUDPart {
|
|||||||
const filtered = [];
|
const filtered = [];
|
||||||
|
|
||||||
for (let i = 0; i < buildings.length; i++) {
|
for (let i = 0; i < buildings.length; i++) {
|
||||||
if (this.root.gameMode.isBuildingExcluded(buildings[i])) {
|
if (!this.root.gameMode.isBuildingExcluded(buildings[i])) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
filtered.push(buildings[i]);
|
filtered.push(buildings[i]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return filtered;
|
return filtered;
|
||||||
}
|
}
|
||||||
@ -119,13 +123,14 @@ export class HUDBaseToolbar extends BaseHUDPart {
|
|||||||
});
|
});
|
||||||
|
|
||||||
//lock icon for puzzle editor
|
//lock icon for puzzle editor
|
||||||
if (this.root.gameMode.getIsEditor() && !this.inRequiredBuildings(metaBuilding)) {
|
if (this.root.gameMode.getIsEditor()) {
|
||||||
const puzzleLock = makeDiv(itemContainer, null, ["puzzle-lock"]);
|
|
||||||
|
|
||||||
itemContainer.classList.toggle("editor", true);
|
itemContainer.classList.toggle("editor", true);
|
||||||
this.trackClicks(puzzleLock, () => this.toggleBuildingLock(metaBuilding), {
|
if (!this.inRequiredBuildings(metaBuilding)) {
|
||||||
clickSound: null,
|
const puzzleLock = makeDiv(itemContainer, null, ["puzzle-lock"]);
|
||||||
});
|
puzzleLock.classList.add("active");
|
||||||
|
|
||||||
|
this.trackClicks(puzzleLock, () => this.toggleBuildingLock(metaBuilding));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.buildingHandles[metaBuilding.id] = {
|
this.buildingHandles[metaBuilding.id] = {
|
||||||
@ -149,13 +154,15 @@ export class HUDBaseToolbar extends BaseHUDPart {
|
|||||||
});
|
});
|
||||||
this.lastSelectedIndex = 0;
|
this.lastSelectedIndex = 0;
|
||||||
actionMapper.getBinding(KEYMAPPINGS.placement.cycleBuildings).add(this.cycleBuildings, this);
|
actionMapper.getBinding(KEYMAPPINGS.placement.cycleBuildings).add(this.cycleBuildings, this);
|
||||||
|
|
||||||
|
this.switchingTestMode = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the toolbar
|
* Updates the toolbar
|
||||||
*/
|
*/
|
||||||
update() {
|
update() {
|
||||||
const visible = this.visibilityCondition();
|
const visible = this.visibilityCondition() && !this.switchingTestMode;
|
||||||
this.domAttach.update(visible);
|
this.domAttach.update(visible);
|
||||||
|
|
||||||
if (visible) {
|
if (visible) {
|
||||||
@ -253,9 +260,11 @@ export class HUDBaseToolbar extends BaseHUDPart {
|
|||||||
|
|
||||||
const handle = this.buildingHandles[metaBuilding.getId()];
|
const handle = this.buildingHandles[metaBuilding.getId()];
|
||||||
if (handle.puzzleLocked) {
|
if (handle.puzzleLocked) {
|
||||||
|
if (this.root.gameMode.getIsEditor()) {
|
||||||
handle.puzzleLocked = false;
|
handle.puzzleLocked = false;
|
||||||
handle.element.classList.toggle("unlocked", false);
|
handle.element.classList.toggle("unlocked", false);
|
||||||
this.root.soundProxy.playUiClick();
|
this.root.soundProxy.playUiClick();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,6 +282,24 @@ export class HUDBaseToolbar extends BaseHUDPart {
|
|||||||
this.onSelectedPlacementBuildingChanged(metaBuilding);
|
this.onSelectedPlacementBuildingChanged(metaBuilding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {boolean} testMode
|
||||||
|
*/
|
||||||
|
toggleTestMode(testMode) {
|
||||||
|
// toggle the puzzle lock buttons and the editor-only buildings
|
||||||
|
|
||||||
|
this.element.querySelectorAll(".building > .puzzle-lock").forEach(element => {
|
||||||
|
element.classList.toggle("active", !testMode);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (let i = 0; i < this.requiredBuildings.length; ++i) {
|
||||||
|
const metaBuilding = this.requiredBuildings[i];
|
||||||
|
const handle = this.buildingHandles[metaBuilding.getId()];
|
||||||
|
handle.puzzleLocked = testMode;
|
||||||
|
handle.element.classList.toggle("unlocked", !testMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {MetaBuilding} metaBuilding
|
* @param {MetaBuilding} metaBuilding
|
||||||
*/
|
*/
|
||||||
@ -290,7 +317,6 @@ export class HUDBaseToolbar extends BaseHUDPart {
|
|||||||
const handle = this.buildingHandles[metaBuilding.getId()];
|
const handle = this.buildingHandles[metaBuilding.getId()];
|
||||||
handle.puzzleLocked = !handle.puzzleLocked;
|
handle.puzzleLocked = !handle.puzzleLocked;
|
||||||
handle.element.classList.toggle("unlocked", !handle.puzzleLocked);
|
handle.element.classList.toggle("unlocked", !handle.puzzleLocked);
|
||||||
this.root.soundProxy.playUiClick();
|
|
||||||
|
|
||||||
const entityManager = this.root.entityMgr;
|
const entityManager = this.root.entityMgr;
|
||||||
for (const entity of entityManager.getAllWithComponent(StaticMapEntityComponent)) {
|
for (const entity of entityManager.getAllWithComponent(StaticMapEntityComponent)) {
|
||||||
@ -312,11 +338,6 @@ export class HUDBaseToolbar extends BaseHUDPart {
|
|||||||
* @param {MetaBuilding} metaBuilding
|
* @param {MetaBuilding} metaBuilding
|
||||||
*/
|
*/
|
||||||
inRequiredBuildings(metaBuilding) {
|
inRequiredBuildings(metaBuilding) {
|
||||||
const requiredBuildings = [
|
return this.requiredBuildings.includes(metaBuilding);
|
||||||
gMetaBuildingRegistry.findByClass(MetaConstantProducerBuilding),
|
|
||||||
gMetaBuildingRegistry.findByClass(MetaGoalAcceptorBuilding),
|
|
||||||
gMetaBuildingRegistry.findByClass(MetaBlockBuilding),
|
|
||||||
];
|
|
||||||
return requiredBuildings.includes(metaBuilding);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,6 +44,7 @@ export class HUDBlueprintPlacer extends BaseHUDPart {
|
|||||||
this.root.camera.movePreHandler.add(this.onMouseMove, this);
|
this.root.camera.movePreHandler.add(this.onMouseMove, this);
|
||||||
|
|
||||||
this.root.hud.signals.selectedPlacementBuildingChanged.add(this.abortPlacement, this);
|
this.root.hud.signals.selectedPlacementBuildingChanged.add(this.abortPlacement, this);
|
||||||
|
this.root.signals.testModeChanged.add(this.abortPlacement, this);
|
||||||
this.root.signals.editModeChanged.add(this.onEditModeChanged, this);
|
this.root.signals.editModeChanged.add(this.onEditModeChanged, this);
|
||||||
|
|
||||||
this.domAttach = new DynamicDomAttach(this.root, this.costDisplayParent);
|
this.domAttach = new DynamicDomAttach(this.root, this.costDisplayParent);
|
||||||
|
|||||||
@ -131,6 +131,7 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart {
|
|||||||
this.root.signals.storyGoalCompleted.add(() => this.currentMetaBuilding.set(null));
|
this.root.signals.storyGoalCompleted.add(() => this.currentMetaBuilding.set(null));
|
||||||
this.root.signals.upgradePurchased.add(() => this.signals.variantChanged.dispatch());
|
this.root.signals.upgradePurchased.add(() => this.signals.variantChanged.dispatch());
|
||||||
this.root.signals.editModeChanged.add(this.onEditModeChanged, this);
|
this.root.signals.editModeChanged.add(this.onEditModeChanged, this);
|
||||||
|
this.root.signals.testModeChanged.add(this.abortPlacement, this);
|
||||||
|
|
||||||
// MOUSE BINDINGS
|
// MOUSE BINDINGS
|
||||||
this.root.camera.downPreHandler.add(this.onMouseDown, this);
|
this.root.camera.downPreHandler.add(this.onMouseDown, this);
|
||||||
@ -385,8 +386,8 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart {
|
|||||||
const buildingCode = contents.components.StaticMapEntity.code;
|
const buildingCode = contents.components.StaticMapEntity.code;
|
||||||
const extracted = getBuildingDataFromCode(buildingCode);
|
const extracted = getBuildingDataFromCode(buildingCode);
|
||||||
|
|
||||||
// Disable pipetting the hub
|
// Disable pipetting a non removeable building
|
||||||
if (extracted.metaInstance.getId() === gMetaBuildingRegistry.findByClass(MetaHubBuilding).getId()) {
|
if (!extracted.metaInstance.getIsRemovable(this.root)) {
|
||||||
this.currentMetaBuilding.set(null);
|
this.currentMetaBuilding.set(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,6 +45,7 @@ export class HUDMassSelector extends BaseHUDPart {
|
|||||||
|
|
||||||
this.root.hud.signals.selectedPlacementBuildingChanged.add(this.clearSelection, this);
|
this.root.hud.signals.selectedPlacementBuildingChanged.add(this.clearSelection, this);
|
||||||
this.root.signals.editModeChanged.add(this.clearSelection, this);
|
this.root.signals.editModeChanged.add(this.clearSelection, this);
|
||||||
|
this.root.signals.testModeChanged.add(this.clearSelection, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
37
src/js/game/hud/parts/puzzle_editor_download.js
Normal file
37
src/js/game/hud/parts/puzzle_editor_download.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { ReadWriteProxy } from "../../../core/read_write_proxy";
|
||||||
|
import { generateFileDownload, makeDiv } from "../../../core/utils";
|
||||||
|
import { PuzzleSerializer } from "../../../savegame/puzzle_serializer";
|
||||||
|
import { T } from "../../../translations";
|
||||||
|
import { BaseHUDPart } from "../base_hud_part";
|
||||||
|
|
||||||
|
export class HUDPuzzleEditorDownload extends BaseHUDPart {
|
||||||
|
constructor(root) {
|
||||||
|
super(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
createElements(parent) {
|
||||||
|
this.element = makeDiv(parent, "ingame_HUD_PuzzleEditorDownload");
|
||||||
|
this.button = document.createElement("button");
|
||||||
|
this.button.classList.add("button");
|
||||||
|
this.element.appendChild(this.button);
|
||||||
|
|
||||||
|
this.trackClicks(this.button, () => {
|
||||||
|
const { ok } = this.root.hud.parts.dialogs.showWarning(
|
||||||
|
T.dialogs.puzzleDownload.title,
|
||||||
|
T.dialogs.puzzleDownload.desc,
|
||||||
|
["cancel", "ok:good:enter"]
|
||||||
|
);
|
||||||
|
ok.add(() => this.downloadPuzzle());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {}
|
||||||
|
|
||||||
|
downloadPuzzle() {
|
||||||
|
const serialized = new PuzzleSerializer().generateDumpFromGameRoot(this.root);
|
||||||
|
|
||||||
|
const data = ReadWriteProxy.serializeObject(serialized);
|
||||||
|
const filename = "puzzle.bin";
|
||||||
|
generateFileDownload(filename, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,18 +1,15 @@
|
|||||||
import { globalConfig } from "../../../core/config";
|
import { globalConfig } from "../../../core/config";
|
||||||
import { gMetaBuildingRegistry } from "../../../core/global_registries";
|
import { gMetaBuildingRegistry } from "../../../core/global_registries";
|
||||||
import { createLogger } from "../../../core/logging";
|
|
||||||
import { Rectangle } from "../../../core/rectangle";
|
import { Rectangle } from "../../../core/rectangle";
|
||||||
import { makeDiv } from "../../../core/utils";
|
import { makeDiv } from "../../../core/utils";
|
||||||
import { T } from "../../../translations";
|
import { T } from "../../../translations";
|
||||||
import { MetaBlockBuilding } from "../../buildings/block";
|
import { MetaBlockBuilding } from "../../buildings/block";
|
||||||
import { MetaConstantProducerBuilding } from "../../buildings/constant_producer";
|
import { MetaConstantProducerBuilding } from "../../buildings/constant_producer";
|
||||||
import { MetaGoalAcceptorBuilding } from "../../buildings/goal_acceptor";
|
|
||||||
import { StaticMapEntityComponent } from "../../components/static_map_entity";
|
import { StaticMapEntityComponent } from "../../components/static_map_entity";
|
||||||
|
import { Entity } from "../../entity";
|
||||||
import { PuzzleGameMode } from "../../modes/puzzle";
|
import { PuzzleGameMode } from "../../modes/puzzle";
|
||||||
import { BaseHUDPart } from "../base_hud_part";
|
import { BaseHUDPart } from "../base_hud_part";
|
||||||
|
|
||||||
const logger = createLogger("puzzle-editor");
|
|
||||||
|
|
||||||
export class HUDPuzzleEditorSettings extends BaseHUDPart {
|
export class HUDPuzzleEditorSettings extends BaseHUDPart {
|
||||||
createElements(parent) {
|
createElements(parent) {
|
||||||
this.element = makeDiv(parent, "ingame_HUD_PuzzleEditorSettings");
|
this.element = makeDiv(parent, "ingame_HUD_PuzzleEditorSettings");
|
||||||
@ -27,7 +24,7 @@ export class HUDPuzzleEditorSettings extends BaseHUDPart {
|
|||||||
`
|
`
|
||||||
<label>${T.ingame.puzzleEditorSettings.zoneTitle}</label>
|
<label>${T.ingame.puzzleEditorSettings.zoneTitle}</label>
|
||||||
|
|
||||||
<div class="buttons">
|
<div class="mainButtons">
|
||||||
<div class="zoneWidth plusMinus">
|
<div class="zoneWidth plusMinus">
|
||||||
<label>${T.ingame.puzzleEditorSettings.zoneWidth}</label>
|
<label>${T.ingame.puzzleEditorSettings.zoneWidth}</label>
|
||||||
<button class="styledButton minus">-</button>
|
<button class="styledButton minus">-</button>
|
||||||
@ -47,10 +44,10 @@ export class HUDPuzzleEditorSettings extends BaseHUDPart {
|
|||||||
<button class="styledButton clearItems">${T.ingame.puzzleEditorSettings.clearItems}</button>
|
<button class="styledButton clearItems">${T.ingame.puzzleEditorSettings.clearItems}</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="buildingsButton">
|
|
||||||
<button class="styledButton resetPuzzle">${T.ingame.puzzleEditorSettings.resetPuzzle}</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="testToggle">
|
||||||
|
<button class="styledButton testPuzzle">${T.ingame.puzzleEditorSettings.enableTestMode}</button>
|
||||||
</div>`
|
</div>`
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -60,7 +57,12 @@ export class HUDPuzzleEditorSettings extends BaseHUDPart {
|
|||||||
bind(".zoneHeight .plus", () => this.modifyZone(0, 1));
|
bind(".zoneHeight .plus", () => this.modifyZone(0, 1));
|
||||||
bind("button.trim", this.trim);
|
bind("button.trim", this.trim);
|
||||||
bind("button.clearItems", this.clearItems);
|
bind("button.clearItems", this.clearItems);
|
||||||
bind("button.resetPuzzle", this.resetPuzzle);
|
bind("button.testPuzzle", this.toggleTestMode);
|
||||||
|
|
||||||
|
this.testMode = false;
|
||||||
|
|
||||||
|
/** @type {Entity[]} */
|
||||||
|
this.storedSolution = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,27 +70,67 @@ export class HUDPuzzleEditorSettings extends BaseHUDPart {
|
|||||||
this.root.logic.clearAllBeltsAndItems();
|
this.root.logic.clearAllBeltsAndItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
resetPuzzle() {
|
toggleTestMode() {
|
||||||
for (const entity of this.root.entityMgr.getAllWithComponent(StaticMapEntityComponent)) {
|
this.testMode = !this.testMode;
|
||||||
const staticComp = entity.components.StaticMapEntity;
|
|
||||||
const goalComp = entity.components.GoalAcceptor;
|
|
||||||
|
|
||||||
|
this.element.querySelector(".mainButtons").classList.toggle("disabled", this.testMode);
|
||||||
|
const testButton = this.element.querySelector(".testToggle > .testPuzzle");
|
||||||
|
testButton.textContent = this.testMode
|
||||||
|
? T.ingame.puzzleEditorSettings.disableTestMode
|
||||||
|
: T.ingame.puzzleEditorSettings.enableTestMode;
|
||||||
|
|
||||||
|
testButton.classList.toggle("disabled", true);
|
||||||
|
|
||||||
|
const buildingsToolbar = this.root.hud.parts.buildingsToolbar;
|
||||||
|
buildingsToolbar.switchingTestMode = true;
|
||||||
|
this.root.signals.testModeChanged.dispatch(this.testMode);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
buildingsToolbar.switchingTestMode = false;
|
||||||
|
buildingsToolbar.toggleTestMode(this.testMode);
|
||||||
|
|
||||||
|
testButton.classList.toggle("disabled", false);
|
||||||
|
}, 140);
|
||||||
|
|
||||||
|
this.root.logic.performBulkOperation(() => {
|
||||||
|
for (const entity of this.root.entityMgr.getAllWithComponent(StaticMapEntityComponent)) {
|
||||||
|
if (this.testMode) {
|
||||||
|
this.storedSolution.push(entity.clone());
|
||||||
|
|
||||||
|
const metaBuilding = entity.components.StaticMapEntity.getMetaBuilding();
|
||||||
|
const goalComp = entity.components.GoalAcceptor;
|
||||||
if (goalComp) {
|
if (goalComp) {
|
||||||
goalComp.clear();
|
goalComp.clear();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
[MetaGoalAcceptorBuilding, MetaConstantProducerBuilding, MetaBlockBuilding]
|
[MetaConstantProducerBuilding, MetaBlockBuilding]
|
||||||
.map(metaClass => gMetaBuildingRegistry.findByClass(metaClass).id)
|
.map(metaClass => gMetaBuildingRegistry.findByClass(metaClass).id)
|
||||||
.includes(staticComp.getMetaBuilding().id)
|
.includes(metaBuilding.id)
|
||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.root.map.removeStaticEntity(entity);
|
this.root.map.removeStaticEntity(entity);
|
||||||
this.root.entityMgr.destroyEntity(entity);
|
this.root.entityMgr.destroyEntity(entity);
|
||||||
}
|
}
|
||||||
this.root.entityMgr.processDestroyList();
|
this.root.entityMgr.processDestroyList();
|
||||||
|
|
||||||
|
if (!this.testMode) {
|
||||||
|
for (const entity of this.storedSolution) {
|
||||||
|
const placedEntity = this.root.logic.tryPlaceEntity(entity);
|
||||||
|
|
||||||
|
for (const key in entity.components) {
|
||||||
|
/** @type {import("../../../core/global_registries").Component} */ (entity.components[
|
||||||
|
key
|
||||||
|
]).copyAdditionalStateTo(placedEntity.components[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.storedSolution = [];
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
trim() {
|
trim() {
|
||||||
|
|||||||
@ -116,6 +116,15 @@ export class GameLogic {
|
|||||||
rotationVariant,
|
rotationVariant,
|
||||||
variant,
|
variant,
|
||||||
});
|
});
|
||||||
|
return this.tryPlaceEntity(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to place the given entity
|
||||||
|
* @param {Entity} entity
|
||||||
|
* @returns {Entity}
|
||||||
|
*/
|
||||||
|
tryPlaceEntity(entity) {
|
||||||
if (this.checkCanPlaceEntity(entity)) {
|
if (this.checkCanPlaceEntity(entity)) {
|
||||||
this.freeEntityAreaBeforeBuild(entity);
|
this.freeEntityAreaBeforeBuild(entity);
|
||||||
this.root.map.placeStaticEntity(entity);
|
this.root.map.placeStaticEntity(entity);
|
||||||
|
|||||||
@ -22,6 +22,13 @@ import { MetaTransistorBuilding } from "../buildings/transistor";
|
|||||||
import { HUDPuzzleEditorControls } from "../hud/parts/puzzle_editor_controls";
|
import { HUDPuzzleEditorControls } from "../hud/parts/puzzle_editor_controls";
|
||||||
import { HUDPuzzleEditorReview } from "../hud/parts/puzzle_editor_review";
|
import { HUDPuzzleEditorReview } from "../hud/parts/puzzle_editor_review";
|
||||||
import { HUDPuzzleEditorSettings } from "../hud/parts/puzzle_editor_settings";
|
import { HUDPuzzleEditorSettings } from "../hud/parts/puzzle_editor_settings";
|
||||||
|
import { createLogger } from "../../core/logging";
|
||||||
|
import { PuzzleSerializer } from "../../savegame/puzzle_serializer";
|
||||||
|
import { T } from "../../translations";
|
||||||
|
import { gMetaBuildingRegistry } from "../../core/global_registries";
|
||||||
|
import { HUDPuzzleEditorDownload } from "../hud/parts/puzzle_editor_download";
|
||||||
|
|
||||||
|
const logger = createLogger("puzzle-edit");
|
||||||
|
|
||||||
export class PuzzleEditGameMode extends PuzzleGameMode {
|
export class PuzzleEditGameMode extends PuzzleGameMode {
|
||||||
static getId() {
|
static getId() {
|
||||||
@ -32,8 +39,13 @@ export class PuzzleEditGameMode extends PuzzleGameMode {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @param {GameRoot} root */
|
/**
|
||||||
constructor(root) {
|
* @param {GameRoot} root
|
||||||
|
* @param {object} payload
|
||||||
|
* @param {import("../../savegame/savegame_typedefs").PuzzleGameData} payload.gameData
|
||||||
|
* @param {boolean} payload.startInTestMode
|
||||||
|
*/
|
||||||
|
constructor(root, { gameData = null, startInTestMode = false }) {
|
||||||
super(root);
|
super(root);
|
||||||
|
|
||||||
this.hiddenBuildings = [
|
this.hiddenBuildings = [
|
||||||
@ -58,9 +70,62 @@ export class PuzzleEditGameMode extends PuzzleGameMode {
|
|||||||
this.additionalHudParts.puzzleEditorControls = HUDPuzzleEditorControls;
|
this.additionalHudParts.puzzleEditorControls = HUDPuzzleEditorControls;
|
||||||
this.additionalHudParts.puzzleEditorReview = HUDPuzzleEditorReview;
|
this.additionalHudParts.puzzleEditorReview = HUDPuzzleEditorReview;
|
||||||
this.additionalHudParts.puzzleEditorSettings = HUDPuzzleEditorSettings;
|
this.additionalHudParts.puzzleEditorSettings = HUDPuzzleEditorSettings;
|
||||||
|
this.additionalHudParts.puzzleEditorDownload = HUDPuzzleEditorDownload;
|
||||||
|
|
||||||
|
this.gameData = gameData;
|
||||||
|
|
||||||
|
if (gameData) {
|
||||||
|
root.signals.postLoadHook.add(() => this.loadPuzzle(gameData), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.startInTestMode = startInTestMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import("../../savegame/savegame_typedefs").PuzzleGameData} puzzle
|
||||||
|
*/
|
||||||
|
loadPuzzle(puzzle) {
|
||||||
|
let errorText;
|
||||||
|
logger.log("Loading puzzle", puzzle);
|
||||||
|
|
||||||
|
// set zone and add buildings
|
||||||
|
try {
|
||||||
|
this.zoneWidth = puzzle.bounds.w;
|
||||||
|
this.zoneHeight = puzzle.bounds.h;
|
||||||
|
errorText = new PuzzleSerializer().deserializePuzzle(this.root, puzzle);
|
||||||
|
} catch (ex) {
|
||||||
|
errorText = ex.message || ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorText) {
|
||||||
|
this.root.gameState.moveToState("PuzzleMenuState", {
|
||||||
|
error: {
|
||||||
|
title: T.dialogs.puzzleLoadError.title,
|
||||||
|
desc: T.dialogs.puzzleLoadError.desc + " " + errorText,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const toolbar = this.root.hud.parts.buildingsToolbar;
|
||||||
|
|
||||||
|
// lock excluded buildings
|
||||||
|
for (let i = 0; i < this.gameData.excludedBuildings.length; ++i) {
|
||||||
|
const id = this.gameData.excludedBuildings[i];
|
||||||
|
|
||||||
|
if (!gMetaBuildingRegistry.hasId(id)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
toolbar.toggleBuildingLock(gMetaBuildingRegistry.findById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.startInTestMode) {
|
||||||
|
this.root.hud.parts.puzzleEditorSettings.toggleTestMode();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getIsEditor() {
|
getIsEditor() {
|
||||||
return true;
|
/** @type {HUDPuzzleEditorSettings} */
|
||||||
|
const editSettings = this.root.hud.parts.puzzleEditorSettings;
|
||||||
|
return !editSettings.testMode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -192,6 +192,7 @@ export class GameRoot {
|
|||||||
|
|
||||||
// Puzzle mode
|
// Puzzle mode
|
||||||
puzzleComplete: /** @type {TypedSignal<[]>} */ (new Signal()),
|
puzzleComplete: /** @type {TypedSignal<[]>} */ (new Signal()),
|
||||||
|
testModeChanged: /** @type {TypedSignal<[Boolean]>} */ (new Signal()),
|
||||||
};
|
};
|
||||||
|
|
||||||
// RNG's
|
// RNG's
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { DrawParameters } from "../../core/draw_parameters";
|
|||||||
import { clamp, lerp } from "../../core/utils";
|
import { clamp, lerp } from "../../core/utils";
|
||||||
import { Vector } from "../../core/vector";
|
import { Vector } from "../../core/vector";
|
||||||
import { GoalAcceptorComponent } from "../components/goal_acceptor";
|
import { GoalAcceptorComponent } from "../components/goal_acceptor";
|
||||||
|
import { enumGameModeIds } from "../game_mode";
|
||||||
import { GameSystemWithFilter } from "../game_system_with_filter";
|
import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||||
import { MapChunk } from "../map_chunk";
|
import { MapChunk } from "../map_chunk";
|
||||||
import { GameRoot } from "../root";
|
import { GameRoot } from "../root";
|
||||||
@ -42,7 +43,7 @@ export class GoalAcceptorSystem extends GameSystemWithFilter {
|
|||||||
!this.puzzleCompleted &&
|
!this.puzzleCompleted &&
|
||||||
this.root.gameInitialized &&
|
this.root.gameInitialized &&
|
||||||
allAccepted &&
|
allAccepted &&
|
||||||
!this.root.gameMode.getIsEditor()
|
!(this.root.gameMode.getId() == enumGameModeIds.puzzleEdit)
|
||||||
) {
|
) {
|
||||||
this.root.signals.puzzleComplete.dispatch();
|
this.root.signals.puzzleComplete.dispatch();
|
||||||
this.puzzleCompleted = true;
|
this.puzzleCompleted = true;
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
import { createLogger } from "../core/logging";
|
import { createLogger } from "../core/logging";
|
||||||
import { DialogWithForm } from "../core/modal_dialog_elements";
|
import { DialogWithForm } from "../core/modal_dialog_elements";
|
||||||
import { FormElementInput } from "../core/modal_dialog_forms";
|
import { FormElementInput } from "../core/modal_dialog_forms";
|
||||||
|
import { ReadWriteProxy } from "../core/read_write_proxy";
|
||||||
import { TextualGameState } from "../core/textual_game_state";
|
import { TextualGameState } from "../core/textual_game_state";
|
||||||
import { formatBigNumberFull } from "../core/utils";
|
import { formatBigNumberFull, startFileChoose, waitNextFrame } from "../core/utils";
|
||||||
import { enumGameModeIds } from "../game/game_mode";
|
import { enumGameModeIds } from "../game/game_mode";
|
||||||
import { ShapeDefinition } from "../game/shape_definition";
|
import { ShapeDefinition } from "../game/shape_definition";
|
||||||
import { MUSIC } from "../platform/sound";
|
import { MUSIC } from "../platform/sound";
|
||||||
@ -42,6 +43,7 @@ export class PuzzleMenuState extends TextualGameState {
|
|||||||
<h1><button class="backButton"></button> ${this.getStateHeaderTitle()}</h1>
|
<h1><button class="backButton"></button> ${this.getStateHeaderTitle()}</h1>
|
||||||
|
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
|
<button class="styledButton importPuzzle">Import</button>
|
||||||
<button class="styledButton loadPuzzle">${T.puzzleMenu.loadPuzzle}</button>
|
<button class="styledButton loadPuzzle">${T.puzzleMenu.loadPuzzle}</button>
|
||||||
<button class="styledButton createPuzzle">+ ${T.puzzleMenu.createPuzzle}</button>
|
<button class="styledButton createPuzzle">+ ${T.puzzleMenu.createPuzzle}</button>
|
||||||
</div>
|
</div>
|
||||||
@ -388,6 +390,7 @@ export class PuzzleMenuState extends TextualGameState {
|
|||||||
|
|
||||||
this.trackClicks(this.htmlElement.querySelector("button.createPuzzle"), () => this.createNewPuzzle());
|
this.trackClicks(this.htmlElement.querySelector("button.createPuzzle"), () => this.createNewPuzzle());
|
||||||
this.trackClicks(this.htmlElement.querySelector("button.loadPuzzle"), () => this.loadPuzzle());
|
this.trackClicks(this.htmlElement.querySelector("button.loadPuzzle"), () => this.loadPuzzle());
|
||||||
|
this.trackClicks(this.htmlElement.querySelector("button.importPuzzle"), () => this.importPuzzle());
|
||||||
}
|
}
|
||||||
|
|
||||||
createEmptySavegame() {
|
createEmptySavegame() {
|
||||||
@ -454,7 +457,44 @@ export class PuzzleMenuState extends TextualGameState {
|
|||||||
const savegame = this.createEmptySavegame();
|
const savegame = this.createEmptySavegame();
|
||||||
this.moveToState("InGameState", {
|
this.moveToState("InGameState", {
|
||||||
gameModeId: enumGameModeIds.puzzleEdit,
|
gameModeId: enumGameModeIds.puzzleEdit,
|
||||||
|
gameModeParameters: {},
|
||||||
savegame,
|
savegame,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
importPuzzle() {
|
||||||
|
startFileChoose(".bin").then(file => {
|
||||||
|
if (file) {
|
||||||
|
const closeLoader = this.dialogs.showLoadingDialog("Importing Puzzle");
|
||||||
|
waitNextFrame().then(() => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.addEventListener("load", event => {
|
||||||
|
const fileContents = event.target.result.toString();
|
||||||
|
|
||||||
|
/** @type {import("../savegame/savegame_typedefs").PuzzleGameData} */
|
||||||
|
let gameData;
|
||||||
|
|
||||||
|
try {
|
||||||
|
gameData = ReadWriteProxy.deserializeObject(fileContents);
|
||||||
|
} catch (err) {
|
||||||
|
closeLoader();
|
||||||
|
this.dialogs.showWarning(T.global.error, String(err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const savegame = this.createEmptySavegame();
|
||||||
|
this.moveToState("InGameState", {
|
||||||
|
gameModeId: enumGameModeIds.puzzleEdit,
|
||||||
|
gameModeParameters: {
|
||||||
|
gameData,
|
||||||
|
startInTestMode: true,
|
||||||
|
},
|
||||||
|
savegame,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
reader.readAsText(file);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -400,6 +400,11 @@ dialogs:
|
|||||||
desc: >-
|
desc: >-
|
||||||
Are you sure you want to delete '<title>'? This can not be undone!
|
Are you sure you want to delete '<title>'? This can not be undone!
|
||||||
|
|
||||||
|
puzzleDownload:
|
||||||
|
title: Download Puzzle
|
||||||
|
desc: >-
|
||||||
|
Do you want to download this puzzle?
|
||||||
|
|
||||||
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
|
||||||
@ -634,6 +639,8 @@ ingame:
|
|||||||
clearItems: Clear Items
|
clearItems: Clear Items
|
||||||
clearBuildings: Clear Buildings
|
clearBuildings: Clear Buildings
|
||||||
resetPuzzle: Reset Puzzle
|
resetPuzzle: Reset Puzzle
|
||||||
|
enableTestMode: Enable Test Mode
|
||||||
|
disableTestMode: Disable Test Mode
|
||||||
share: Share
|
share: Share
|
||||||
report: Report
|
report: Report
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user