mirror of
https://github.com/tobspr/shapez.io.git
synced 2025-06-13 13:04:03 +00:00
Puzzle mode, Refactor hud
This commit is contained in:
parent
36aaf7bfa5
commit
72530005cb
@ -1,54 +0,0 @@
|
||||
#ingame_HUD_ModeMenu {
|
||||
position: absolute;
|
||||
@include S(bottom, 10px);
|
||||
@include S(left, 10px);
|
||||
display: flex;
|
||||
|
||||
backdrop-filter: blur(D(1px));
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
backdrop-filter: blur(D(1px));
|
||||
padding: D(3px);
|
||||
|
||||
> button,
|
||||
> .button {
|
||||
@include PlainText;
|
||||
@include IncreasedClickArea(0px);
|
||||
background: green;
|
||||
@include S(width, 30px);
|
||||
@include S(height, 30px);
|
||||
|
||||
pointer-events: all;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: all 0.12s ease-in-out;
|
||||
transition-property: opacity, transform;
|
||||
|
||||
display: inline-flex;
|
||||
background: center center / 70% no-repeat;
|
||||
grid-row: 1;
|
||||
|
||||
&.pressed {
|
||||
transform: scale(0.9) !important;
|
||||
}
|
||||
|
||||
opacity: 0.7;
|
||||
&:hover {
|
||||
opacity: 0.9 !important;
|
||||
}
|
||||
|
||||
@include DarkThemeInvert;
|
||||
|
||||
&.settings {
|
||||
& {
|
||||
/* @load-async */
|
||||
background-image: uiResource("icons/settings_menu_settings.png");
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
opacity: 0.9;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
#ingame_HUD_ModeMenuBack {
|
||||
#ingame_HUD_PuzzleBackToMenu {
|
||||
position: absolute;
|
||||
@include S(top, 10px);
|
||||
@include S(left, 10px);
|
||||
@include S(left, 0px);
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
@ -1,9 +1,9 @@
|
||||
#ingame_HUD_PuzzleDLCLogo {
|
||||
position: absolute;
|
||||
@include S(width, 150px);
|
||||
@include S(width, 120px);
|
||||
@include S(height, 40px);
|
||||
@include S(left, 50px);
|
||||
@include S(top, 10px);
|
||||
@include S(left, 40px);
|
||||
@include S(top, 7px);
|
||||
|
||||
& {
|
||||
/* @load-async */
|
||||
|
@ -11,13 +11,17 @@
|
||||
|
||||
> span {
|
||||
@include S(margin-bottom, 10px);
|
||||
|
||||
strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ingame_HUD_PuzzleEditorTitle {
|
||||
position: absolute;
|
||||
|
||||
@include S(top, 23px);
|
||||
@include S(top, 18px);
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
text-transform: uppercase;
|
||||
|
@ -1,6 +1,6 @@
|
||||
#ingame_HUD_PuzzleReview {
|
||||
#ingame_HUD_PuzzleEditorReview {
|
||||
position: absolute;
|
||||
@include S(top, 15px);
|
||||
@include S(top, 17px);
|
||||
@include S(right, 10px);
|
||||
|
||||
display: flex;
|
@ -1,14 +1,15 @@
|
||||
#ingame_HUD_ModeSettings {
|
||||
#ingame_HUD_PuzzleEditorSettings {
|
||||
position: absolute;
|
||||
background: $ingameHudBg;
|
||||
@include S(padding, 10px);
|
||||
@include S(bottom, 50px);
|
||||
@include S(left, 15px);
|
||||
@include S(bottom, 60px);
|
||||
@include S(left, 10px);
|
||||
|
||||
@include SuperSmallText;
|
||||
color: #eee;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@include S(border-radius, $globalBorderRadius);
|
||||
|
||||
> .section {
|
||||
> label {
|
||||
@ -37,11 +38,15 @@
|
||||
@include IncreasedClickArea(0px);
|
||||
}
|
||||
|
||||
|
||||
.value {
|
||||
text-align: center;
|
||||
@include S(min-width, 15px);
|
||||
}
|
||||
}
|
||||
|
||||
> .buttons > button.trim {
|
||||
@include S(margin-top, 10px);
|
||||
@include SuperSmallText;
|
||||
}
|
||||
}
|
||||
}
|
19
src/css/ingame_hud/puzzle_play_metadata.scss
Normal file
19
src/css/ingame_hud/puzzle_play_metadata.scss
Normal file
@ -0,0 +1,19 @@
|
||||
#ingame_HUD_PuzzleEditorMetadata {
|
||||
position: absolute;
|
||||
|
||||
@include S(top, 70px);
|
||||
@include S(left, 10px);
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@include SuperDuperSmallText;
|
||||
@include S(width, 200px);
|
||||
|
||||
> span {
|
||||
@include S(margin-bottom, 10px);
|
||||
|
||||
strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
@ -56,12 +56,12 @@
|
||||
@import "ingame_hud/sandbox_controller";
|
||||
@import "ingame_hud/standalone_advantages";
|
||||
@import "ingame_hud/cat_memes";
|
||||
@import "ingame_hud/mode_menu_back";
|
||||
@import "ingame_hud/mode_menu_next";
|
||||
@import "ingame_hud/mode_menu";
|
||||
@import "ingame_hud/mode_settings";
|
||||
@import "ingame_hud/puzzle_back_to_menu";
|
||||
@import "ingame_hud/puzzle_editor_review";
|
||||
@import "ingame_hud/puzzle_dlc_logo";
|
||||
@import "ingame_hud/puzzle_editor_controls";
|
||||
@import "ingame_hud/puzzle_editor_settings";
|
||||
@import "ingame_hud/puzzle_play_metadata";
|
||||
|
||||
// prettier-ignore
|
||||
$elements:
|
||||
@ -79,12 +79,12 @@ ingame_HUD_PlacerVariants,
|
||||
ingame_HUD_PinnedShapes,
|
||||
ingame_HUD_GameMenu,
|
||||
ingame_HUD_KeybindingOverlay,
|
||||
ingame_HUD_ModeMenuBack,
|
||||
ingame_HUD_PuzzleReview,
|
||||
ingame_HUD_PuzzleBackToMenu,
|
||||
ingame_HUD_PuzzleEditorReview,
|
||||
ingame_HUD_PuzzleEditorControls,
|
||||
ingame_HUD_PuzzleEditorTitle,
|
||||
ingame_HUD_ModeMenu,
|
||||
ingame_HUD_ModeSettings,
|
||||
ingame_HUD_PuzzleEditorSettings,
|
||||
ingame_HUD_PuzzlePlayMetadata
|
||||
ingame_HUD_Notifications,
|
||||
ingame_HUD_DebugInfo,
|
||||
ingame_HUD_EntityDebugger,
|
||||
@ -127,8 +127,8 @@ body.uiHidden {
|
||||
#ingame_HUD_PlacementHints,
|
||||
#ingame_HUD_GameMenu,
|
||||
#ingame_HUD_PinnedShapes,
|
||||
#ingame_HUD_ModeMenuBack,
|
||||
#ingame_HUD_PuzzleReview,
|
||||
#ingame_HUD_PuzzleBackToMenu,
|
||||
#ingame_HUD_PuzzleEditorReview,
|
||||
#ingame_HUD_Notifications,
|
||||
#ingame_HUD_TutorialHints,
|
||||
#ingame_HUD_Waypoints,
|
||||
|
@ -74,6 +74,8 @@ export const globalConfig = {
|
||||
goalAcceptorMinimumDurationSeconds: G_IS_DEV ? 1 : 5,
|
||||
goalAcceptorsPerProducer: G_IS_DEV ? 4 : 4,
|
||||
puzzleModeSpeed: 3,
|
||||
puzzleMinBoundsSize: 2,
|
||||
puzzleMaxBoundsSize: 20,
|
||||
|
||||
buildingSpeeds: {
|
||||
cutter: 1 / 4,
|
||||
|
@ -44,6 +44,15 @@ export class Rectangle {
|
||||
return new Rectangle(left, top, right - left, bottom - top);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} width
|
||||
* @param {number} height
|
||||
*/
|
||||
static centered(width, height) {
|
||||
return new Rectangle(-Math.ceil(width / 2), -Math.ceil(height / 2), width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if a intersects b
|
||||
* @param {Rectangle} a
|
||||
@ -287,6 +296,15 @@ export class Rectangle {
|
||||
return Rectangle.fromTRBL(top, right, bottom, left);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the rectangle fully intersects the given rectangle
|
||||
* @param {Rectangle} rect
|
||||
*/
|
||||
intersectsFully(rect) {
|
||||
const intersection = this.getIntersection(rect);
|
||||
return intersection && Math.abs(intersection.w * intersection.h - rect.w * rect.h) < 0.001;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the union of this rectangle with another
|
||||
* @param {Rectangle} rect
|
||||
|
@ -66,6 +66,10 @@ export class MetaBalancerBuilding extends MetaBuilding {
|
||||
* @returns {Array<[string, string]>}
|
||||
*/
|
||||
getAdditionalStatistics(root, variant) {
|
||||
if (root.gameMode.throughputDoesNotMatter()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let speedMultiplier = 2;
|
||||
switch (variant) {
|
||||
case enumBalancerVariants.merger:
|
||||
|
@ -55,6 +55,9 @@ export class MetaBeltBuilding extends MetaBuilding {
|
||||
* @returns {Array<[string, string]>}
|
||||
*/
|
||||
getAdditionalStatistics(root, variant) {
|
||||
if (root.gameMode.throughputDoesNotMatter()) {
|
||||
return [];
|
||||
}
|
||||
const beltSpeed = root.hubGoals.getBeltBaseSpeed();
|
||||
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(beltSpeed)]];
|
||||
}
|
||||
|
@ -38,6 +38,9 @@ export class MetaCutterBuilding extends MetaBuilding {
|
||||
* @returns {Array<[string, string]>}
|
||||
*/
|
||||
getAdditionalStatistics(root, variant) {
|
||||
if (root.gameMode.throughputDoesNotMatter()) {
|
||||
return [];
|
||||
}
|
||||
const speed = root.hubGoals.getProcessorBaseSpeed(
|
||||
variant === enumCutterVariants.quad
|
||||
? enumItemProcessorTypes.cutterQuad
|
||||
|
@ -40,6 +40,9 @@ export class MetaFilterBuilding extends MetaBuilding {
|
||||
* @returns {Array<[string, string]>}
|
||||
*/
|
||||
getAdditionalStatistics(root, variant) {
|
||||
if (root.gameMode.throughputDoesNotMatter()) {
|
||||
return [];
|
||||
}
|
||||
const beltSpeed = root.hubGoals.getBeltBaseSpeed();
|
||||
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(beltSpeed)]];
|
||||
}
|
||||
|
@ -31,6 +31,9 @@ export class MetaMinerBuilding extends MetaBuilding {
|
||||
* @returns {Array<[string, string]>}
|
||||
*/
|
||||
getAdditionalStatistics(root, variant) {
|
||||
if (root.gameMode.throughputDoesNotMatter()) {
|
||||
return [];
|
||||
}
|
||||
const speed = root.hubGoals.getMinerBaseSpeed();
|
||||
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]];
|
||||
}
|
||||
|
@ -35,6 +35,9 @@ export class MetaMixerBuilding extends MetaBuilding {
|
||||
* @returns {Array<[string, string]>}
|
||||
*/
|
||||
getAdditionalStatistics(root, variant) {
|
||||
if (root.gameMode.throughputDoesNotMatter()) {
|
||||
return [];
|
||||
}
|
||||
const speed = root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.mixer);
|
||||
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]];
|
||||
}
|
||||
|
@ -46,6 +46,9 @@ export class MetaPainterBuilding extends MetaBuilding {
|
||||
* @returns {Array<[string, string]>}
|
||||
*/
|
||||
getAdditionalStatistics(root, variant) {
|
||||
if (root.gameMode.throughputDoesNotMatter()) {
|
||||
return [];
|
||||
}
|
||||
switch (variant) {
|
||||
case defaultBuildingVariant:
|
||||
case enumPainterVariants.mirrored: {
|
||||
|
@ -48,6 +48,9 @@ export class MetaRotaterBuilding extends MetaBuilding {
|
||||
* @returns {Array<[string, string]>}
|
||||
*/
|
||||
getAdditionalStatistics(root, variant) {
|
||||
if (root.gameMode.throughputDoesNotMatter()) {
|
||||
return [];
|
||||
}
|
||||
switch (variant) {
|
||||
case defaultBuildingVariant: {
|
||||
const speed = root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.rotater);
|
||||
|
@ -28,6 +28,9 @@ export class MetaStackerBuilding extends MetaBuilding {
|
||||
* @returns {Array<[string, string]>}
|
||||
*/
|
||||
getAdditionalStatistics(root, variant) {
|
||||
if (root.gameMode.throughputDoesNotMatter()) {
|
||||
return [];
|
||||
}
|
||||
const speed = root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.stacker);
|
||||
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]];
|
||||
}
|
||||
|
@ -72,13 +72,21 @@ export class MetaUndergroundBeltBuilding extends MetaBuilding {
|
||||
globalConfig.undergroundBeltMaxTilesByTier[enumUndergroundBeltVariantToTier[variant]];
|
||||
|
||||
const beltSpeed = root.hubGoals.getUndergroundBeltBaseSpeed();
|
||||
return [
|
||||
|
||||
/** @type {Array<[string, string]>} */
|
||||
const stats = [
|
||||
[
|
||||
T.ingame.buildingPlacement.infoTexts.range,
|
||||
T.ingame.buildingPlacement.infoTexts.tiles.replace("<x>", "" + rangeTiles),
|
||||
],
|
||||
[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(beltSpeed)],
|
||||
];
|
||||
|
||||
if (root.gameMode.throughputDoesNotMatter()) {
|
||||
return stats;
|
||||
}
|
||||
stats.push([T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(beltSpeed)]);
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -103,7 +103,7 @@ export class GameCore {
|
||||
root.keyMapper = new KeyActionMapper(root, this.root.gameState.inputReciever);
|
||||
|
||||
// Init game mode
|
||||
root.gameMode = GameMode.create(root, gameModeId);
|
||||
root.gameMode = GameMode.create(root, gameModeId, parentState.creationPayload.gameModeParameters);
|
||||
|
||||
// Needs to come first
|
||||
root.dynamicTickrate = new DynamicTickrate(root);
|
||||
@ -455,7 +455,9 @@ export class GameCore {
|
||||
systems.hub.draw(params);
|
||||
|
||||
// Green wires overlay
|
||||
root.hud.parts.wiresOverlay.draw(params);
|
||||
if (root.hud.parts.wiresOverlay) {
|
||||
root.hud.parts.wiresOverlay.draw(params);
|
||||
}
|
||||
|
||||
if (this.root.currentLayer === "wires") {
|
||||
// Static map entities
|
||||
|
@ -1,12 +1,13 @@
|
||||
/* typehints:start */
|
||||
import { GameRoot } from "./root";
|
||||
import { Rectangle } from "../core/rectangle";
|
||||
/* typehints:end */
|
||||
|
||||
import { Rectangle } from "../core/rectangle";
|
||||
import { gGameModeRegistry } from "../core/global_registries";
|
||||
import { types, BasicSerializableObject } from "../savegame/serialization";
|
||||
import { MetaBuilding } from "./meta_building";
|
||||
import { MetaItemProducerBuilding } from "./buildings/item_producer";
|
||||
import { BaseHUDPart } from "./hud/base_hud_part";
|
||||
|
||||
/** @enum {string} */
|
||||
export const enumGameModeIds = {
|
||||
@ -36,9 +37,10 @@ export class GameMode extends BasicSerializableObject {
|
||||
/**
|
||||
* @param {GameRoot} root
|
||||
* @param {string} [id=Regular]
|
||||
* @param {object|undefined} payload
|
||||
*/
|
||||
static create(root, id = enumGameModeIds.regular) {
|
||||
return new (gGameModeRegistry.findById(id))(root);
|
||||
static create(root, id = enumGameModeIds.regular, payload = undefined) {
|
||||
return new (gGameModeRegistry.findById(id))(root, payload);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -47,7 +49,11 @@ export class GameMode extends BasicSerializableObject {
|
||||
constructor(root) {
|
||||
super();
|
||||
this.root = root;
|
||||
this.hiddenHudParts = {};
|
||||
|
||||
/**
|
||||
* @type {Record<string, typeof BaseHUDPart>}
|
||||
*/
|
||||
this.additionalHudParts = {};
|
||||
|
||||
/** @type {typeof MetaBuilding[]} */
|
||||
this.hiddenBuildings = [MetaItemProducerBuilding];
|
||||
@ -78,14 +84,6 @@ export class GameMode extends BasicSerializableObject {
|
||||
return this.constructor.getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name - Class name of HUD Part
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isHudPartExcluded(name) {
|
||||
return this.hiddenHudParts[name] === false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {typeof MetaBuilding} building - Class name of building
|
||||
* @returns {boolean}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { globalConfig, IS_MOBILE } from "../../core/config";
|
||||
import { globalConfig } from "../../core/config";
|
||||
import { DrawParameters } from "../../core/draw_parameters";
|
||||
import { Signal } from "../../core/signal";
|
||||
import { KEYMAPPINGS } from "../key_action_mapper";
|
||||
@ -6,47 +6,16 @@ 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 { HUDBuildingPlacer } from "./parts/building_placer";
|
||||
import { HUDCatMemes } from "./parts/cat_memes";
|
||||
import { HUDColorBlindHelper } from "./parts/color_blind_helper";
|
||||
import { HUDConstantSignalEdit } from "./parts/constant_signal_edit";
|
||||
import { HUDChangesDebugger } from "./parts/debug_changes";
|
||||
import { HUDDebugInfo } from "./parts/debug_info";
|
||||
import { HUDEntityDebugger } from "./parts/entity_debugger";
|
||||
import { HUDGameMenu } from "./parts/game_menu";
|
||||
import { HUDInteractiveTutorial } from "./parts/interactive_tutorial";
|
||||
import { HUDKeybindingOverlay } from "./parts/keybinding_overlay";
|
||||
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 { HUDModalDialogs } from "./parts/modal_dialogs";
|
||||
import { HUDModeMenu } from "./parts/mode_menu";
|
||||
import { HUDModeMenuBack } from "./parts/mode_menu_back";
|
||||
import { HUDPuzzleReview } from "./parts/mode_puzzle_review";
|
||||
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 { HUDPuzzleEditorControls } from "./parts/puzzle_editor_controls";
|
||||
import { HUDSandboxController } from "./parts/sandbox_controller";
|
||||
import { HUDScreenshotExporter } from "./parts/screenshot_exporter";
|
||||
import { enumNotificationType } from "./parts/notifications";
|
||||
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";
|
||||
import { TrailerMaker } from "./trailer_maker";
|
||||
|
||||
export class GameHUD {
|
||||
@ -73,79 +42,30 @@ export class GameHUD {
|
||||
unlockNotificationFinished: /** @type {TypedSignal<[]>} */ (new Signal()),
|
||||
};
|
||||
|
||||
this.initParts({
|
||||
buildingsToolbar: HUDBuildingsToolbar,
|
||||
wiresToolbar: HUDWiresToolbar,
|
||||
blueprintPlacer: HUDBlueprintPlacer,
|
||||
buildingPlacer: HUDBuildingPlacer,
|
||||
unlockNotification: HUDUnlockNotification,
|
||||
gameMenu: HUDGameMenu,
|
||||
massSelector: HUDMassSelector,
|
||||
shop: HUDShop,
|
||||
statistics: HUDStatistics,
|
||||
waypoints: HUDWaypoints,
|
||||
wireInfo: HUDWireInfo,
|
||||
leverToggle: HUDLeverToggle,
|
||||
constantSignalEdit: HUDConstantSignalEdit,
|
||||
modeMenuBack: HUDModeMenuBack,
|
||||
PuzzleReview: HUDPuzzleReview,
|
||||
modeMenu: HUDModeMenu,
|
||||
modeSettings: HUDModeSettings,
|
||||
puzzleDlcLogo: HUDPuzzleDLCLogo,
|
||||
puzzleEditorControls: HUDPuzzleEditorControls,
|
||||
this.parts = {
|
||||
buildingsToolbar: new HUDBuildingsToolbar(this.root),
|
||||
buildingPlacer: new HUDBuildingPlacer(this.root),
|
||||
|
||||
// Must always exist
|
||||
pinnedShapes: HUDPinnedShapes,
|
||||
notifications: HUDNotifications,
|
||||
settingsMenu: HUDSettingsMenu,
|
||||
debugInfo: HUDDebugInfo,
|
||||
dialogs: HUDModalDialogs,
|
||||
screenshotExporter: HUDScreenshotExporter,
|
||||
shapeViewer: HUDShapeViewer,
|
||||
|
||||
wiresOverlay: HUDWiresOverlay,
|
||||
layerPreview: HUDLayerPreview,
|
||||
|
||||
minerHighlight: HUDMinerHighlight,
|
||||
tutorialVideoOffer: HUDTutorialVideoOffer,
|
||||
settingsMenu: new HUDSettingsMenu(this.root),
|
||||
debugInfo: new HUDDebugInfo(this.root),
|
||||
dialogs: new HUDModalDialogs(this.root),
|
||||
|
||||
// Typing hints
|
||||
/* typehints:start */
|
||||
/** @type {HUDChangesDebugger} */
|
||||
changesDebugger: null,
|
||||
/* typehints:end */
|
||||
});
|
||||
|
||||
if (!IS_MOBILE) {
|
||||
if (!this.root.gameMode.isHudPartExcluded(HUDKeybindingOverlay.name)) {
|
||||
this.parts.keybindingOverlay = new HUDKeybindingOverlay(this.root);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (G_IS_DEV && globalConfig.debug.enableEntityInspector) {
|
||||
this.parts.entityDebugger = new HUDEntityDebugger(this.root);
|
||||
}
|
||||
|
||||
if (this.root.app.restrictionMgr.getIsStandaloneMarketingActive()) {
|
||||
this.parts.watermark = new HUDWatermark(this.root);
|
||||
this.parts.standaloneAdvantages = new HUDStandaloneAdvantages(this.root);
|
||||
this.parts.catMemes = new HUDCatMemes(this.root);
|
||||
}
|
||||
|
||||
if (G_IS_DEV && globalConfig.debug.renderChanges) {
|
||||
this.parts.changesDebugger = new HUDChangesDebugger(this.root);
|
||||
}
|
||||
|
||||
if (this.root.app.settings.getAllSettings().offerHints) {
|
||||
if (!this.root.gameMode.isHudPartExcluded(HUDPartTutorialHints.name)) {
|
||||
this.parts.tutorialHints = new HUDPartTutorialHints(this.root);
|
||||
}
|
||||
|
||||
if (!this.root.gameMode.isHudPartExcluded(HUDInteractiveTutorial.name)) {
|
||||
this.parts.interactiveTutorial = new HUDInteractiveTutorial(this.root);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.root.app.settings.getAllSettings().vignette) {
|
||||
this.parts.vignetteOverlay = new HUDVignetteOverlay(this.root);
|
||||
}
|
||||
@ -154,12 +74,17 @@ export class GameHUD {
|
||||
this.parts.colorBlindHelper = new HUDColorBlindHelper(this.root);
|
||||
}
|
||||
|
||||
this.parts.sandboxController = new HUDSandboxController(this.root);
|
||||
|
||||
if (!G_IS_RELEASE && !G_IS_DEV) {
|
||||
this.parts.betaOverlay = new HUDBetaOverlay(this.root);
|
||||
}
|
||||
|
||||
const additionalParts = this.root.gameMode.additionalHudParts;
|
||||
console.log(additionalParts);
|
||||
for (const [partId, part] of Object.entries(additionalParts)) {
|
||||
this.parts[partId] = new part(this.root);
|
||||
}
|
||||
console.log(this.parts);
|
||||
|
||||
const frag = document.createDocumentFragment();
|
||||
for (const key in this.parts) {
|
||||
this.parts[key].createElements(frag);
|
||||
@ -180,21 +105,6 @@ export class GameHUD {
|
||||
/* dev:end*/
|
||||
}
|
||||
|
||||
/** @param {object} parts */
|
||||
initParts(parts) {
|
||||
this.parts = {};
|
||||
|
||||
for (let key in parts) {
|
||||
const Part = parts[key];
|
||||
|
||||
if (!Part || this.root.gameMode.isHudPartExcluded(Part.name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.parts[key] = new Part(this.root);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to close all overlays
|
||||
*/
|
||||
|
@ -275,11 +275,13 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic {
|
||||
const worldPosition = this.root.camera.screenToWorld(mousePosition);
|
||||
|
||||
// Draw peeker
|
||||
this.root.hud.parts.layerPreview.renderPreview(
|
||||
parameters,
|
||||
worldPosition,
|
||||
1 / this.root.camera.zoomLevel
|
||||
);
|
||||
if (this.root.hud.parts.layerPreview) {
|
||||
this.root.hud.parts.layerPreview.renderPreview(
|
||||
parameters,
|
||||
worldPosition,
|
||||
1 / this.root.camera.zoomLevel
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,17 +0,0 @@
|
||||
import { BaseHUDPart } from "../base_hud_part";
|
||||
import { makeDiv } from "../../../core/utils";
|
||||
|
||||
export class HUDModeMenu extends BaseHUDPart {
|
||||
createElements(parent) {
|
||||
this.element = makeDiv(parent, "ingame_HUD_ModeMenu");
|
||||
|
||||
this.settingsButton = makeDiv(this.element, null, ["button", "settings"]);
|
||||
this.trackClicks(this.settingsButton, this.openSettings);
|
||||
}
|
||||
|
||||
openSettings() {
|
||||
this.root.hud.parts.modeSettings.toggle();
|
||||
}
|
||||
|
||||
initialize() {}
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
import { makeDiv } from "../../../core/utils";
|
||||
import { BaseHUDPart } from "../base_hud_part";
|
||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
||||
|
||||
export class HUDModeSettings extends BaseHUDPart {
|
||||
createElements(parent) {
|
||||
this.element = makeDiv(parent, "ingame_HUD_ModeSettings");
|
||||
|
||||
const bind = (selector, handler) => this.trackClicks(this.element.querySelector(selector), handler);
|
||||
|
||||
// @fixme
|
||||
if (this.root.gameMode.getBuildableZones()) {
|
||||
this.zone = makeDiv(
|
||||
this.element,
|
||||
null,
|
||||
["section", "zone"],
|
||||
`
|
||||
<label>Zone</label>
|
||||
|
||||
<div class="buttons">
|
||||
<div class="zoneWidth plusMinus">
|
||||
<label>Width</label>
|
||||
<button class="styledButton minus">-</button>
|
||||
<span class="value"></span>
|
||||
<button class="styledButton plus">+</button>
|
||||
</div>
|
||||
|
||||
<div class="zoneHeight plusMinus">
|
||||
<label>Height</label>
|
||||
<button class="styledButton minus">-</button>
|
||||
<span class="value"></span>
|
||||
<button class="styledButton plus">+</button>
|
||||
</div>
|
||||
</div>`
|
||||
);
|
||||
|
||||
bind(".zoneWidth .minus", () => this.modifyZone(-1, 0));
|
||||
bind(".zoneWidth .plus", () => this.modifyZone(1, 0));
|
||||
bind(".zoneHeight .minus", () => this.modifyZone(0, -1));
|
||||
bind(".zoneHeight .plus", () => this.modifyZone(0, 1));
|
||||
}
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this.visible = false;
|
||||
this.domAttach = new DynamicDomAttach(this.root, this.element);
|
||||
this.updateZoneValues();
|
||||
}
|
||||
|
||||
modifyZone(width, height) {
|
||||
this.root.gameMode.adjustZone(width, height);
|
||||
this.updateZoneValues();
|
||||
}
|
||||
|
||||
updateZoneValues() {
|
||||
const zones = this.root.gameMode.getBuildableZones();
|
||||
if (!zones || zones.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const zone = zones[0];
|
||||
this.element.querySelector(".zoneWidth > .value").textContent = String(zone.w);
|
||||
this.element.querySelector(".zoneHeight > .value").textContent = String(zone.h);
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this.visible = !this.visible;
|
||||
}
|
||||
|
||||
update() {
|
||||
this.domAttach.update(this.visible);
|
||||
}
|
||||
}
|
@ -55,7 +55,7 @@ export class HUDPinnedShapes extends BaseHUDPart {
|
||||
*/
|
||||
deserialize(data) {
|
||||
if (!data || !data.shapes || !Array.isArray(data.shapes)) {
|
||||
return "Invalid pinned shapes data";
|
||||
return "Invalid pinned shapes data: " + JSON.stringify(data);
|
||||
}
|
||||
this.pinnedShapes = data.shapes;
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { makeDiv } from "../../../core/utils";
|
||||
import { BaseHUDPart } from "../base_hud_part";
|
||||
|
||||
export class HUDModeMenuBack extends BaseHUDPart {
|
||||
export class HUDPuzzleBackToMenu extends BaseHUDPart {
|
||||
createElements(parent) {
|
||||
const key = this.root.gameMode.getId();
|
||||
|
||||
this.element = makeDiv(parent, "ingame_HUD_ModeMenuBack");
|
||||
this.element = makeDiv(parent, "ingame_HUD_PuzzleBackToMenu");
|
||||
this.button = document.createElement("button");
|
||||
this.button.classList.add("button");
|
||||
this.element.appendChild(this.button);
|
@ -1,19 +1,17 @@
|
||||
import { makeDiv } from "../../../core/utils";
|
||||
import { T } from "../../../translations";
|
||||
import { BaseHUDPart } from "../base_hud_part";
|
||||
|
||||
export class HUDPuzzleEditorControls extends BaseHUDPart {
|
||||
createElements(parent) {
|
||||
this.element = makeDiv(parent, "ingame_HUD_PuzzleEditorControls");
|
||||
|
||||
this.element.innerHTML = `
|
||||
|
||||
<span>1. Build constant producers to generate resources.</span>
|
||||
<span>2. Build goal acceptors and deliver shapes to set the puzzle goals.</span>
|
||||
<span>3. Once you are done, press 'Playtest' to validate your puzzle.</span>
|
||||
`;
|
||||
this.element.innerHTML = T.ingame.puzzleEditorControls.instructions
|
||||
.map(text => `<span>${text}</span>`)
|
||||
.join("");
|
||||
|
||||
this.titleElement = makeDiv(parent, "ingame_HUD_PuzzleEditorTitle");
|
||||
this.titleElement.innerText = "Puzzle Editor";
|
||||
this.titleElement.innerText = T.ingame.puzzleEditorControls.title;
|
||||
}
|
||||
|
||||
initialize() {}
|
||||
|
@ -16,7 +16,7 @@ import { BaseHUDPart } from "../base_hud_part";
|
||||
const trim = require("trim");
|
||||
const logger = createLogger("puzzle-review");
|
||||
|
||||
export class HUDPuzzleReview extends BaseHUDPart {
|
||||
export class HUDPuzzleEditorReview extends BaseHUDPart {
|
||||
constructor(root) {
|
||||
super(root);
|
||||
|
||||
@ -27,7 +27,7 @@ export class HUDPuzzleReview extends BaseHUDPart {
|
||||
createElements(parent) {
|
||||
const key = this.root.gameMode.getId();
|
||||
|
||||
this.element = makeDiv(parent, "ingame_HUD_PuzzleReview");
|
||||
this.element = makeDiv(parent, "ingame_HUD_PuzzleEditorReview");
|
||||
this.button = document.createElement("button");
|
||||
this.button.classList.add("button");
|
||||
this.button.textContent = T.puzzleMenu.reviewPuzzle;
|
141
src/js/game/hud/parts/puzzle_editor_settings.js
Normal file
141
src/js/game/hud/parts/puzzle_editor_settings.js
Normal file
@ -0,0 +1,141 @@
|
||||
/* typehints:start */
|
||||
import { PuzzleGameMode } from "../../modes/puzzle";
|
||||
/* typehints:end */
|
||||
|
||||
import { globalConfig } from "../../../core/config";
|
||||
import { createLogger } from "../../../core/logging";
|
||||
import { Rectangle } from "../../../core/rectangle";
|
||||
import { makeDiv } from "../../../core/utils";
|
||||
import { T } from "../../../translations";
|
||||
import { StaticMapEntityComponent } from "../../components/static_map_entity";
|
||||
import { BaseHUDPart } from "../base_hud_part";
|
||||
|
||||
const logger = createLogger("puzzle-editor");
|
||||
|
||||
export class HUDPuzzleEditorSettings extends BaseHUDPart {
|
||||
createElements(parent) {
|
||||
this.element = makeDiv(parent, "ingame_HUD_PuzzleEditorSettings");
|
||||
|
||||
if (this.root.gameMode.getBuildableZones()) {
|
||||
const bind = (selector, handler) =>
|
||||
this.trackClicks(this.element.querySelector(selector), handler);
|
||||
this.zone = makeDiv(
|
||||
this.element,
|
||||
null,
|
||||
["section", "zone"],
|
||||
`
|
||||
<label>${T.ingame.puzzleEditorSettings.zoneTitle}</label>
|
||||
|
||||
<div class="buttons">
|
||||
<div class="zoneWidth plusMinus">
|
||||
<label>${T.ingame.puzzleEditorSettings.zoneWidth}</label>
|
||||
<button class="styledButton minus">-</button>
|
||||
<span class="value"></span>
|
||||
<button class="styledButton plus">+</button>
|
||||
</div>
|
||||
|
||||
<div class="zoneHeight plusMinus">
|
||||
<label>${T.ingame.puzzleEditorSettings.zoneHeight}</label>
|
||||
<button class="styledButton minus">-</button>
|
||||
<span class="value"></span>
|
||||
<button class="styledButton plus">+</button>
|
||||
</div>
|
||||
|
||||
<button class="styledButton trim">${T.ingame.puzzleEditorSettings.trimZone}</button>
|
||||
</div>`
|
||||
);
|
||||
|
||||
bind(".zoneWidth .minus", () => this.modifyZone(-1, 0));
|
||||
bind(".zoneWidth .plus", () => this.modifyZone(1, 0));
|
||||
bind(".zoneHeight .minus", () => this.modifyZone(0, -1));
|
||||
bind(".zoneHeight .plus", () => this.modifyZone(0, 1));
|
||||
bind("button.trim", this.trim);
|
||||
}
|
||||
}
|
||||
|
||||
trim() {
|
||||
const mode = /** @type {PuzzleGameMode} */ (this.root.gameMode);
|
||||
|
||||
let w = mode.zoneWidth;
|
||||
let h = mode.zoneHeight;
|
||||
if (this.anyBuildingOutsideZone(w, h)) {
|
||||
logger.error("Trim: Zone is already too small");
|
||||
return;
|
||||
}
|
||||
|
||||
logger.log("Zone trim: Starts at", w, h);
|
||||
|
||||
while (!this.anyBuildingOutsideZone(w - 1, h)) {
|
||||
--w;
|
||||
}
|
||||
|
||||
while (!this.anyBuildingOutsideZone(w, h - 1)) {
|
||||
--h;
|
||||
}
|
||||
|
||||
logger.log("Zone trim: After height pass at", w, h);
|
||||
if (this.anyBuildingOutsideZone(w, h)) {
|
||||
logger.error("Trim: Zone is too small *after* trim");
|
||||
return;
|
||||
}
|
||||
|
||||
mode.zoneWidth = w;
|
||||
mode.zoneHeight = h;
|
||||
this.updateZoneValues();
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this.visible = true;
|
||||
this.updateZoneValues();
|
||||
}
|
||||
|
||||
anyBuildingOutsideZone(width, height) {
|
||||
if (Math.min(width, height) < globalConfig.puzzleMinBoundsSize) {
|
||||
return true;
|
||||
}
|
||||
const newZone = Rectangle.centered(width, height);
|
||||
const entities = this.root.entityMgr.getAllWithComponent(StaticMapEntityComponent);
|
||||
|
||||
for (const entity of entities) {
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
const bounds = staticComp.getTileSpaceBounds();
|
||||
if (!newZone.intersectsFully(bounds)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
modifyZone(deltaW, deltaH) {
|
||||
const mode = /** @type {PuzzleGameMode} */ (this.root.gameMode);
|
||||
|
||||
const newWidth = mode.zoneWidth + deltaW;
|
||||
const newHeight = mode.zoneHeight + deltaH;
|
||||
|
||||
if (Math.min(newWidth, newHeight) < globalConfig.puzzleMinBoundsSize) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Math.max(newWidth, newHeight) > globalConfig.puzzleMaxBoundsSize) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.anyBuildingOutsideZone(newWidth, newHeight)) {
|
||||
this.root.hud.parts.dialogs.showWarning(
|
||||
T.dialogs.puzzleResizeBadBuildings.title,
|
||||
T.dialogs.puzzleResizeBadBuildings.desc
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
mode.zoneWidth = newWidth;
|
||||
mode.zoneHeight = newHeight;
|
||||
this.updateZoneValues();
|
||||
}
|
||||
|
||||
updateZoneValues() {
|
||||
const mode = /** @type {PuzzleGameMode} */ (this.root.gameMode);
|
||||
|
||||
this.element.querySelector(".zoneWidth > .value").textContent = String(mode.zoneWidth);
|
||||
this.element.querySelector(".zoneHeight > .value").textContent = String(mode.zoneHeight);
|
||||
}
|
||||
}
|
15
src/js/game/hud/parts/puzzle_play_metadata.js
Normal file
15
src/js/game/hud/parts/puzzle_play_metadata.js
Normal file
@ -0,0 +1,15 @@
|
||||
import { makeDiv } from "../../../core/utils";
|
||||
import { T } from "../../../translations";
|
||||
import { BaseHUDPart } from "../base_hud_part";
|
||||
|
||||
export class HUDPuzzlePlayMetadata extends BaseHUDPart {
|
||||
createElements(parent) {
|
||||
this.element = makeDiv(parent, "ingame_HUD_PuzzlePlayMetadata");
|
||||
|
||||
this.titleElement = makeDiv(parent, "ingame_HUD_PuzzleEditorTitle");
|
||||
// this.titleElement.innerText = T.ingame.PuzzlePlayMetadata.title;
|
||||
this.titleElement.innerText = "tobspr's first puzzle";
|
||||
}
|
||||
|
||||
initialize() {}
|
||||
}
|
@ -118,7 +118,9 @@ export class HUDSandboxController extends BaseHUDPart {
|
||||
// Clear all shapes of this level
|
||||
hubGoals.storedShapes[hubGoals.currentGoal.definition.getHash()] = 0;
|
||||
|
||||
this.root.hud.parts.pinnedShapes.rerenderFull();
|
||||
if (this.root.hud.parts.pinnedShapes) {
|
||||
this.root.hud.parts.pinnedShapes.rerenderFull();
|
||||
}
|
||||
|
||||
// Compute gained rewards
|
||||
hubGoals.gainedRewards = {};
|
||||
@ -149,19 +151,8 @@ export class HUDSandboxController extends BaseHUDPart {
|
||||
this.domAttach = new DynamicDomAttach(this.root, this.element);
|
||||
}
|
||||
|
||||
isAvailable() {
|
||||
if (queryParamOptions.sandboxMode || G_IS_DEV) {
|
||||
return true;
|
||||
}
|
||||
// @ts-ignore
|
||||
if (window.sandboxMode) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
toggle() {
|
||||
if (!this.visible && !this.isAvailable()) {
|
||||
if (!this.visible) {
|
||||
return;
|
||||
}
|
||||
this.visible = !this.visible;
|
||||
|
@ -5,13 +5,8 @@ import { GameRoot } from "../root";
|
||||
import { Rectangle } from "../../core/rectangle";
|
||||
import { types } from "../../savegame/serialization";
|
||||
import { enumGameModeTypes, GameMode } from "../game_mode";
|
||||
import { HUDGameMenu } from "../hud/parts/game_menu";
|
||||
import { HUDInteractiveTutorial } from "../hud/parts/interactive_tutorial";
|
||||
import { HUDKeybindingOverlay } from "../hud/parts/keybinding_overlay";
|
||||
import { HUDPartTutorialHints } from "../hud/parts/tutorial_hints";
|
||||
import { HUDPinnedShapes } from "../hud/parts/pinned_shapes";
|
||||
import { HUDWaypoints } from "../hud/parts/waypoints";
|
||||
import { HUDMassSelector } from "../hud/parts/mass_selector";
|
||||
import { HUDPuzzleBackToMenu } from "../hud/parts/puzzle_back_to_menu";
|
||||
import { HUDPuzzleDLCLogo } from "../hud/parts/puzzle_dlc_logo";
|
||||
|
||||
export class PuzzleGameMode extends GameMode {
|
||||
static getType() {
|
||||
@ -32,22 +27,13 @@ export class PuzzleGameMode extends GameMode {
|
||||
|
||||
const data = this.getSaveData();
|
||||
|
||||
this.hiddenHudParts = {
|
||||
[HUDGameMenu.name]: false,
|
||||
[HUDMassSelector.name]: false,
|
||||
[HUDInteractiveTutorial.name]: false,
|
||||
[HUDKeybindingOverlay.name]: false,
|
||||
[HUDPartTutorialHints.name]: false,
|
||||
[HUDPinnedShapes.name]: false,
|
||||
[HUDWaypoints.name]: false,
|
||||
this.additionalHudParts = {
|
||||
puzzleBackToMenu: HUDPuzzleBackToMenu,
|
||||
puzzleDlcLogo: HUDPuzzleDLCLogo,
|
||||
};
|
||||
|
||||
this.setDimensions(data.zoneWidth, data.zoneHeight);
|
||||
}
|
||||
|
||||
setDimensions(w = 16, h = 9) {
|
||||
this.zoneWidth = w < 2 ? 2 : w;
|
||||
this.zoneHeight = h < 2 ? 2 : h;
|
||||
this.zoneWidth = data.zoneWidth || 8;
|
||||
this.zoneHeight = data.zoneHeight || 6;
|
||||
}
|
||||
|
||||
getSaveData() {
|
||||
@ -58,16 +44,12 @@ export class PuzzleGameMode extends GameMode {
|
||||
return save.gameMode.data;
|
||||
}
|
||||
|
||||
createCenteredRectangle(width, height) {
|
||||
return new Rectangle(-Math.ceil(width / 2), -Math.ceil(height / 2), width, height);
|
||||
}
|
||||
|
||||
getCameraBounds() {
|
||||
return this.createCenteredRectangle(this.zoneWidth + 20, this.zoneHeight + 20);
|
||||
return Rectangle.centered(this.zoneWidth + 20, this.zoneHeight + 20);
|
||||
}
|
||||
|
||||
getBuildableZones() {
|
||||
return [this.createCenteredRectangle(this.zoneWidth, this.zoneHeight)];
|
||||
return [Rectangle.centered(this.zoneWidth, this.zoneHeight)];
|
||||
}
|
||||
|
||||
hasHub() {
|
||||
|
@ -19,6 +19,10 @@ import { MetaVirtualProcessorBuilding } from "../buildings/virtual_processor";
|
||||
import { MetaAnalyzerBuilding } from "../buildings/analyzer";
|
||||
import { MetaComparatorBuilding } from "../buildings/comparator";
|
||||
import { MetaTransistorBuilding } from "../buildings/transistor";
|
||||
import { HUDPuzzleEditorControls } from "../hud/parts/puzzle_editor_controls";
|
||||
import { HUDPuzzleEditorReview } from "../hud/parts/puzzle_editor_review";
|
||||
import { HUDPuzzleEditorSettings } from "../hud/parts/puzzle_editor_settings";
|
||||
import { HUDPuzzleBackToMenu } from "../hud/parts/puzzle_back_to_menu";
|
||||
|
||||
export class PuzzleEditGameMode extends PuzzleGameMode {
|
||||
static getId() {
|
||||
@ -33,8 +37,6 @@ export class PuzzleEditGameMode extends PuzzleGameMode {
|
||||
constructor(root) {
|
||||
super(root);
|
||||
|
||||
this.playtest = false;
|
||||
|
||||
this.hiddenBuildings = [
|
||||
MetaStorageBuilding,
|
||||
MetaReaderBuilding,
|
||||
@ -53,32 +55,10 @@ export class PuzzleEditGameMode extends PuzzleGameMode {
|
||||
MetaComparatorBuilding,
|
||||
MetaTransistorBuilding,
|
||||
];
|
||||
}
|
||||
|
||||
adjustZone(w = 0, h = 0) {
|
||||
// @todo notify user when zone cannot be shrunk
|
||||
if (this.zoneWidth + w <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.zoneHeight + h <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newZone = this.createCenteredRectangle(this.zoneWidth + w, this.zoneHeight + h);
|
||||
const entities = this.root.entityMgr.entities;
|
||||
|
||||
// @fixme find a better way to check this
|
||||
for (const entity of entities) {
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
const union = newZone.getUnion(staticComp.getTileSpaceBounds());
|
||||
if (!union.equalsEpsilon(newZone)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.zoneWidth = newZone.w;
|
||||
this.zoneHeight = newZone.h;
|
||||
this.additionalHudParts.puzzleEditorControls = HUDPuzzleEditorControls;
|
||||
this.additionalHudParts.puzzleEditorReview = HUDPuzzleEditorReview;
|
||||
this.additionalHudParts.puzzleEditorSettings = HUDPuzzleEditorSettings;
|
||||
}
|
||||
|
||||
getIsEditor() {
|
||||
|
@ -2,16 +2,65 @@
|
||||
import { GameRoot } from "../root";
|
||||
/* typehints:end */
|
||||
|
||||
import { PuzzleGameMode } from "./puzzle";
|
||||
import { enumGameModeIds } from "../game_mode";
|
||||
import { PuzzleGameMode } from "./puzzle";
|
||||
import { MetaStorageBuilding } from "../buildings/storage";
|
||||
import { MetaReaderBuilding } from "../buildings/reader";
|
||||
import { MetaFilterBuilding } from "../buildings/filter";
|
||||
import { MetaDisplayBuilding } from "../buildings/display";
|
||||
import { MetaLeverBuilding } from "../buildings/lever";
|
||||
import { MetaItemProducerBuilding } from "../buildings/item_producer";
|
||||
import { MetaMinerBuilding } from "../buildings/miner";
|
||||
import { MetaWireBuilding } from "../buildings/wire";
|
||||
import { MetaWireTunnelBuilding } from "../buildings/wire_tunnel";
|
||||
import { MetaConstantSignalBuilding } from "../buildings/constant_signal";
|
||||
import { MetaLogicGateBuilding } from "../buildings/logic_gate";
|
||||
import { MetaVirtualProcessorBuilding } from "../buildings/virtual_processor";
|
||||
import { MetaAnalyzerBuilding } from "../buildings/analyzer";
|
||||
import { MetaComparatorBuilding } from "../buildings/comparator";
|
||||
import { MetaTransistorBuilding } from "../buildings/transistor";
|
||||
import { MetaConstantProducerBuilding } from "../buildings/constant_producer";
|
||||
import { MetaGoalAcceptorBuilding } from "../buildings/goal_acceptor";
|
||||
import { HUDConstantSignalEdit } from "../hud/parts/constant_signal_edit";
|
||||
|
||||
export class PuzzlePlayGameMode extends PuzzleGameMode {
|
||||
static getId() {
|
||||
return enumGameModeIds.puzzlePlay;
|
||||
}
|
||||
|
||||
/** @param {GameRoot} root */
|
||||
constructor(root) {
|
||||
/**
|
||||
* @param {GameRoot} root
|
||||
* @param {object} payload
|
||||
* @param {import("../../savegame/savegame_typedefs").PuzzleFullData} payload.puzzle
|
||||
*/
|
||||
constructor(root, { puzzle }) {
|
||||
super(root);
|
||||
|
||||
this.hiddenBuildings = [
|
||||
MetaConstantProducerBuilding,
|
||||
MetaGoalAcceptorBuilding,
|
||||
|
||||
MetaStorageBuilding,
|
||||
MetaReaderBuilding,
|
||||
MetaFilterBuilding,
|
||||
MetaDisplayBuilding,
|
||||
MetaLeverBuilding,
|
||||
MetaItemProducerBuilding,
|
||||
MetaMinerBuilding,
|
||||
|
||||
MetaWireBuilding,
|
||||
MetaWireTunnelBuilding,
|
||||
MetaConstantSignalBuilding,
|
||||
MetaLogicGateBuilding,
|
||||
MetaVirtualProcessorBuilding,
|
||||
MetaAnalyzerBuilding,
|
||||
MetaComparatorBuilding,
|
||||
MetaTransistorBuilding,
|
||||
];
|
||||
|
||||
this.additionalHudParts.constantSignalEdit = HUDConstantSignalEdit;
|
||||
|
||||
console.log("playing puzzle:", puzzle);
|
||||
this.puzzle = puzzle;
|
||||
}
|
||||
}
|
||||
|
@ -5,15 +5,37 @@ import { GameRoot } from "../root";
|
||||
import { findNiceIntegerValue } from "../../core/utils";
|
||||
import { MetaConstantProducerBuilding } from "../buildings/constant_producer";
|
||||
import { MetaGoalAcceptorBuilding } from "../buildings/goal_acceptor";
|
||||
import { HUDModeMenuBack } from "../hud/parts/mode_menu_back";
|
||||
import { HUDPuzzleReview } from "../hud/parts/mode_puzzle_review";
|
||||
import { HUDModeMenu } from "../hud/parts/mode_menu";
|
||||
import { HUDModeSettings } from "../hud/parts/mode_settings";
|
||||
import { enumGameModeIds, enumGameModeTypes, GameMode } from "../game_mode";
|
||||
import { ShapeDefinition } from "../shape_definition";
|
||||
import { enumHubGoalRewards } from "../tutorial_goals";
|
||||
import { HUDPuzzleDLCLogo } from "../hud/parts/puzzle_dlc_logo";
|
||||
import { HUDPuzzleEditorControls } from "../hud/parts/puzzle_editor_controls";
|
||||
import { HUDWiresToolbar } from "../hud/parts/wires_toolbar";
|
||||
import { HUDBlueprintPlacer } from "../hud/parts/blueprint_placer";
|
||||
import { HUDUnlockNotification } from "../hud/parts/unlock_notification";
|
||||
import { HUDMassSelector } from "../hud/parts/mass_selector";
|
||||
import { HUDShop } from "../hud/parts/shop";
|
||||
import { HUDWaypoints } from "../hud/parts/waypoints";
|
||||
import { HUDStatistics } from "../hud/parts/statistics";
|
||||
import { HUDWireInfo } from "../hud/parts/wire_info";
|
||||
import { HUDLeverToggle } from "../hud/parts/lever_toggle";
|
||||
import { HUDPinnedShapes } from "../hud/parts/pinned_shapes";
|
||||
import { HUDNotifications } from "../hud/parts/notifications";
|
||||
import { HUDScreenshotExporter } from "../hud/parts/screenshot_exporter";
|
||||
import { HUDWiresOverlay } from "../hud/parts/wires_overlay";
|
||||
import { HUDShapeViewer } from "../hud/parts/shape_viewer";
|
||||
import { HUDLayerPreview } from "../hud/parts/layer_preview";
|
||||
import { HUDTutorialVideoOffer } from "../hud/parts/tutorial_video_offer";
|
||||
import { HUDMinerHighlight } from "../hud/parts/miner_highlight";
|
||||
import { HUDGameMenu } from "../hud/parts/game_menu";
|
||||
import { HUDConstantSignalEdit } from "../hud/parts/constant_signal_edit";
|
||||
import { IS_MOBILE } from "../../core/config";
|
||||
import { HUDKeybindingOverlay } from "../hud/parts/keybinding_overlay";
|
||||
import { HUDWatermark } from "../hud/parts/watermark";
|
||||
import { HUDStandaloneAdvantages } from "../hud/parts/standalone_advantages";
|
||||
import { HUDCatMemes } from "../hud/parts/cat_memes";
|
||||
import { HUDPartTutorialHints } from "../hud/parts/tutorial_hints";
|
||||
import { HUDInteractiveTutorial } from "../hud/parts/interactive_tutorial";
|
||||
import { HUDSandboxController } from "../hud/parts/sandbox_controller";
|
||||
import { queryParamOptions } from "../../core/query_parameters";
|
||||
|
||||
/** @typedef {{
|
||||
* shape: string,
|
||||
@ -517,15 +539,48 @@ export class RegularGameMode extends GameMode {
|
||||
constructor(root) {
|
||||
super(root);
|
||||
|
||||
this.hiddenHudParts = {
|
||||
[HUDModeMenuBack.name]: false,
|
||||
[HUDPuzzleReview.name]: false,
|
||||
[HUDModeMenu.name]: false,
|
||||
[HUDModeSettings.name]: false,
|
||||
[HUDPuzzleDLCLogo.name]: false,
|
||||
[HUDPuzzleEditorControls.name]: false,
|
||||
this.additionalHudParts = {
|
||||
wiresToolbar: HUDWiresToolbar,
|
||||
blueprintPlacer: HUDBlueprintPlacer,
|
||||
unlockNotification: HUDUnlockNotification,
|
||||
massSelector: HUDMassSelector,
|
||||
shop: HUDShop,
|
||||
statistics: HUDStatistics,
|
||||
waypoints: HUDWaypoints,
|
||||
wireInfo: HUDWireInfo,
|
||||
leverToggle: HUDLeverToggle,
|
||||
pinnedShapes: HUDPinnedShapes,
|
||||
notifications: HUDNotifications,
|
||||
screenshotExporter: HUDScreenshotExporter,
|
||||
wiresOverlay: HUDWiresOverlay,
|
||||
shapeViewer: HUDShapeViewer,
|
||||
layerPreview: HUDLayerPreview,
|
||||
minerHighlight: HUDMinerHighlight,
|
||||
tutorialVideoOffer: HUDTutorialVideoOffer,
|
||||
gameMenu: HUDGameMenu,
|
||||
constantSignalEdit: HUDConstantSignalEdit,
|
||||
};
|
||||
|
||||
if (!IS_MOBILE) {
|
||||
this.additionalHudParts.keybindingOverlay = HUDKeybindingOverlay;
|
||||
}
|
||||
|
||||
if (this.root.app.restrictionMgr.getIsStandaloneMarketingActive()) {
|
||||
this.additionalHudParts.watermark = HUDWatermark;
|
||||
this.additionalHudParts.standaloneAdvantages = HUDStandaloneAdvantages;
|
||||
this.additionalHudParts.catMemes = HUDCatMemes;
|
||||
}
|
||||
|
||||
if (this.root.app.settings.getAllSettings().offerHints) {
|
||||
this.additionalHudParts.tutorialHints = HUDPartTutorialHints;
|
||||
this.additionalHudParts.interactiveTutorial = HUDInteractiveTutorial;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
if (queryParamOptions.sandboxMode || window.sandboxMode || G_IS_DEV) {
|
||||
this.additionalHudParts.sandboxController = HUDSandboxController;
|
||||
}
|
||||
|
||||
this.hiddenBuildings = [MetaConstantProducerBuilding, MetaGoalAcceptorBuilding];
|
||||
}
|
||||
|
||||
|
@ -77,7 +77,7 @@ export class ConstantSignalSystem extends GameSystemWithFilter {
|
||||
);
|
||||
}
|
||||
|
||||
if (!this.root.gameMode.isHudPartExcluded(HUDPinnedShapes.name)) {
|
||||
if (this.root.hud.parts.pinnedShapes) {
|
||||
items.push(
|
||||
...this.root.hud.parts.pinnedShapes.pinnedShapes.map(key =>
|
||||
this.root.shapeDefinitionMgr.getShapeItemFromShortKey(key)
|
||||
|
@ -49,14 +49,9 @@ export class ZoneSystem extends GameSystem {
|
||||
transformed.y += tile.y;
|
||||
}
|
||||
|
||||
for (const zone of zones) {
|
||||
const intersection = zone.getIntersection(transformed);
|
||||
if (intersection && intersection.w * intersection.h === transformed.w * transformed.h) {
|
||||
return;
|
||||
}
|
||||
if (!zones.some(zone => zone.intersectsFully(transformed))) {
|
||||
return STOP_PROPAGATION;
|
||||
}
|
||||
|
||||
return STOP_PROPAGATION;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -84,8 +79,7 @@ export class ZoneSystem extends GameSystem {
|
||||
context.lineWidth = 2;
|
||||
context.strokeStyle = THEME.map.zone.borderSolid;
|
||||
context.beginPath();
|
||||
context.rect(zone.x, zone.y, zone.w, zone.h);
|
||||
|
||||
context.rect(zone.x - 1, zone.y - 1, zone.w + 2, zone.h + 2);
|
||||
context.stroke();
|
||||
|
||||
const outer = zone;
|
||||
|
@ -51,9 +51,7 @@
|
||||
},
|
||||
|
||||
"zone": {
|
||||
"background": "#fff",
|
||||
"border": "rgba(23, 192, 255, 0.1)",
|
||||
"borderSolid": "rgba(23, 192, 255, 0.7)",
|
||||
"borderSolid": "rgba(23, 192, 255, 1)",
|
||||
"outerColor": "rgba(240, 240, 255, 0.5)"
|
||||
}
|
||||
},
|
||||
|
@ -1,18 +1,23 @@
|
||||
/* typehints:start */
|
||||
import { GameRoot } from "../game/root";
|
||||
import { PuzzleGameMode } from "../game/modes/puzzle";
|
||||
/* typehints:end */
|
||||
import { enumConstantSignalType } from "../game/components/constant_signal";
|
||||
import { StaticMapEntityComponent } from "../game/components/static_map_entity";
|
||||
import { ShapeItem } from "../game/items/shape_item";
|
||||
import { GameRoot } from "../game/root";
|
||||
|
||||
export class PuzzleSerializer {
|
||||
/**
|
||||
* Serializes the game root into a dump
|
||||
* @param {GameRoot} root
|
||||
* @param {boolean=} sanityChecks Whether to check for validity
|
||||
* @returns {object}
|
||||
* @returns {import("./savegame_typedefs").PuzzleGameData}
|
||||
*/
|
||||
generateDumpFromGameRoot(root, sanityChecks = true) {
|
||||
generateDumpFromGameRoot(root) {
|
||||
console.log("serializing", root);
|
||||
|
||||
/**
|
||||
* @type {import("./savegame_typedefs").PuzzleGameData["buildings"]}
|
||||
*/
|
||||
let buildings = [];
|
||||
|
||||
for (const entity of root.entityMgr.getAllWithComponent(StaticMapEntityComponent)) {
|
||||
@ -51,9 +56,15 @@ export class PuzzleSerializer {
|
||||
}
|
||||
}
|
||||
|
||||
const mode = /** @type {PuzzleGameMode} */ (root.gameMode);
|
||||
|
||||
return {
|
||||
version: 1,
|
||||
buildings,
|
||||
bounds: root.gameMode.getBuildableZones()[0],
|
||||
bounds: {
|
||||
w: mode.zoneWidth,
|
||||
h: mode.zoneHeight,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -40,12 +40,8 @@ export class SavegameSerializer {
|
||||
hubGoals: root.hubGoals.serialize(),
|
||||
entities: this.internal.serializeEntityArray(root.entityMgr.entities),
|
||||
beltPaths: root.systemMgr.systems.belt.serializePaths(),
|
||||
pinnedShapes: root.gameMode.isHudPartExcluded(HUDPinnedShapes.name)
|
||||
? null
|
||||
: root.hud.parts.pinnedShapes.serialize(),
|
||||
waypoints: root.gameMode.isHudPartExcluded(HUDWaypoints.name)
|
||||
? null
|
||||
: root.hud.parts.waypoints.serialize(),
|
||||
pinnedShapes: root.hud.parts.pinnedShapes ? root.hud.parts.pinnedShapes.serialize() : null,
|
||||
waypoints: root.hud.parts.waypoints ? root.hud.parts.waypoints.serialize() : null,
|
||||
};
|
||||
|
||||
if (G_IS_DEV) {
|
||||
@ -142,11 +138,11 @@ export class SavegameSerializer {
|
||||
errorReason = errorReason || this.internal.deserializeEntityArray(root, savegame.entities);
|
||||
errorReason = errorReason || root.systemMgr.systems.belt.deserializePaths(savegame.beltPaths);
|
||||
|
||||
if (!root.gameMode.isHudPartExcluded(HUDPinnedShapes.name)) {
|
||||
if (root.hud.parts.pinnedShapes) {
|
||||
errorReason = errorReason || root.hud.parts.pinnedShapes.deserialize(savegame.pinnedShapes);
|
||||
}
|
||||
|
||||
if (!root.gameMode.isHudPartExcluded(HUDWaypoints.name)) {
|
||||
if (root.hud.parts.waypoints) {
|
||||
errorReason = errorReason || root.hud.parts.waypoints.deserialize(savegame.waypoints);
|
||||
}
|
||||
|
||||
|
@ -41,4 +41,46 @@
|
||||
* }} SavegamesData
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* shortKey: string;
|
||||
* upvotes: number;
|
||||
* playcount: number;
|
||||
* title: string;
|
||||
* author: string;
|
||||
* completed: boolean;
|
||||
* }} PuzzleMetadata
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* type: "emitter";
|
||||
* item: string;
|
||||
* pos: { x: number; y: number; r: number }
|
||||
* }} PuzzleGameBuildingConstantProducer
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* type: "goal";
|
||||
* item: string;
|
||||
* pos: { x: number; y: number; r: number }
|
||||
* }} PuzzleGameBuildingGoal
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* version: number;
|
||||
* bounds: { w: number; h: number; },
|
||||
* buildings: (PuzzleGameBuildingGoal | PuzzleGameBuildingConstantProducer)[]
|
||||
* }} PuzzleGameData
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* meta: PuzzleMetadata,
|
||||
* game: PuzzleGameData
|
||||
* }} PuzzleFullData
|
||||
*/
|
||||
|
||||
export default {};
|
||||
|
@ -45,6 +45,9 @@ export class GameCreationPayload {
|
||||
|
||||
/** @type {Savegame} */
|
||||
this.savegame;
|
||||
|
||||
/** @type {object|undefined} */
|
||||
this.gameModeParameters;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,17 +7,6 @@ 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,
|
||||
@ -26,6 +15,7 @@ const SAMPLE_PUZZLE = {
|
||||
author: "verylongsteamnamewhichbreaks",
|
||||
completed: false,
|
||||
};
|
||||
|
||||
const BUILTIN_PUZZLES = [
|
||||
{ ...SAMPLE_PUZZLE, completed: true },
|
||||
{ ...SAMPLE_PUZZLE, completed: true },
|
||||
@ -141,7 +131,7 @@ export class PuzzleMenuState extends TextualGameState {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {PuzzleMetadata[]} puzzles
|
||||
* @param {import("../savegame/savegame_typedefs").PuzzleMetadata[]} puzzles
|
||||
*/
|
||||
renderPuzzles(puzzles) {
|
||||
const container = this.htmlElement.querySelector("#mainContainer");
|
||||
@ -191,6 +181,8 @@ export class PuzzleMenuState extends TextualGameState {
|
||||
elem.appendChild(icon);
|
||||
|
||||
container.appendChild(elem);
|
||||
|
||||
this.trackClicks(elem, () => this.playPuzzle(puzzle));
|
||||
}
|
||||
}
|
||||
|
||||
@ -198,6 +190,49 @@ export class PuzzleMenuState extends TextualGameState {
|
||||
return new Promise(resolve => setTimeout(() => resolve(BUILTIN_PUZZLES), 100));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {import("../savegame/savegame_typedefs").PuzzleMetadata} puzzle
|
||||
*/
|
||||
playPuzzle(puzzle) {
|
||||
/**
|
||||
* @type {import("../savegame/savegame_typedefs").PuzzleGameData}
|
||||
*/
|
||||
const puzzleData = {
|
||||
version: 1,
|
||||
buildings: [
|
||||
{
|
||||
type: "emitter",
|
||||
item: "CuCuCuCu",
|
||||
pos: { x: 0, y: 0, r: 180 },
|
||||
},
|
||||
{
|
||||
type: "emitter",
|
||||
item: "red",
|
||||
pos: { x: 2, y: 0, r: 180 },
|
||||
},
|
||||
{
|
||||
type: "goal",
|
||||
item: "CrCrCrCr",
|
||||
pos: { x: 0, y: 4, r: 0 },
|
||||
},
|
||||
],
|
||||
bounds: { w: 10, h: 10 },
|
||||
};
|
||||
|
||||
const savegame = this.app.savegameMgr.createNewSavegame();
|
||||
this.moveToState("InGameState", {
|
||||
gameModeId: enumGameModeIds.puzzlePlay,
|
||||
gameModeParameters: {
|
||||
puzzle: {
|
||||
meta: puzzle,
|
||||
game: puzzleData,
|
||||
},
|
||||
},
|
||||
savegame,
|
||||
});
|
||||
}
|
||||
|
||||
onEnter() {
|
||||
this.selectCategory("levels");
|
||||
|
||||
@ -209,7 +244,8 @@ export class PuzzleMenuState extends TextualGameState {
|
||||
this.trackClicks(this.htmlElement.querySelector("button.createPuzzle"), this.createNewPuzzle);
|
||||
|
||||
if (G_IS_DEV && globalConfig.debug.testPuzzleMode) {
|
||||
this.createNewPuzzle();
|
||||
// this.createNewPuzzle();
|
||||
this.playPuzzle(SAMPLE_PUZZLE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -298,6 +298,10 @@ dialogs:
|
||||
|
||||
placeholderName: Puzzle Title
|
||||
|
||||
puzzleResizeBadBuildings:
|
||||
title: Resize not possible
|
||||
desc: You can't make the zone any smaller, because then some buildings would be outside the zone.
|
||||
|
||||
ingame:
|
||||
# This is shown in the top left corner and displays useful keybindings in
|
||||
# every situation
|
||||
@ -522,6 +526,22 @@ ingame:
|
||||
title: Support me
|
||||
desc: I develop the game in my spare time!
|
||||
|
||||
# puzzle mode
|
||||
puzzleEditorSettings:
|
||||
zoneTitle: Zone
|
||||
zoneWidth: Width
|
||||
zoneHeight: Height
|
||||
trimZone: Trim
|
||||
|
||||
puzzleEditorControls:
|
||||
title: Puzzle Creator
|
||||
instructions:
|
||||
- 1. Place <strong>Constant Producers</strong> to provide shapes and colors to the player
|
||||
- 2. Build one or more shapes you want the player to build later and deliver it to one or more <strong>Goal Acceptors</strong>
|
||||
- 3. Once a Goal Acceptor receives a shape for a certain amount of time, it <strong>saves it as a goal</strong> that the player must produce later (Indicated by the <strong>green badge</strong>).
|
||||
- 4. Once you click review, your puzzle will be validated and you can publish it.
|
||||
- 5. Upon release, <strong>all buildings will be removed</strong> except for the Producers and Goal Acceptors - That's the part that the player is supposed to figure out for themselves, after all :)
|
||||
|
||||
# All shop upgrades
|
||||
shopUpgrades:
|
||||
belt:
|
||||
@ -749,12 +769,12 @@ buildings:
|
||||
constant_producer:
|
||||
default:
|
||||
name: &constant_producer Constant Producer
|
||||
description: Outputs a shape, color or boolean (1 or 0) as specified.
|
||||
description: Constantly outputs a specified shape or color.
|
||||
|
||||
goal_acceptor:
|
||||
default:
|
||||
name: &goal_acceptor Goal Acceptor
|
||||
description: Accepts items and triggers a goal if the specified item and/or rate criteria are met.
|
||||
description: Deliver shapes to the goal acceptor to set them as a goal.
|
||||
|
||||
storyRewards:
|
||||
# Those are the rewards gained from completing the store
|
||||
|
Loading…
Reference in New Issue
Block a user