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;
|
position: absolute;
|
||||||
@include S(top, 10px);
|
@include S(top, 10px);
|
||||||
@include S(left, 10px);
|
@include S(left, 0px);
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
@ -1,9 +1,9 @@
|
|||||||
#ingame_HUD_PuzzleDLCLogo {
|
#ingame_HUD_PuzzleDLCLogo {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@include S(width, 150px);
|
@include S(width, 120px);
|
||||||
@include S(height, 40px);
|
@include S(height, 40px);
|
||||||
@include S(left, 50px);
|
@include S(left, 40px);
|
||||||
@include S(top, 10px);
|
@include S(top, 7px);
|
||||||
|
|
||||||
& {
|
& {
|
||||||
/* @load-async */
|
/* @load-async */
|
||||||
|
@ -11,13 +11,17 @@
|
|||||||
|
|
||||||
> span {
|
> span {
|
||||||
@include S(margin-bottom, 10px);
|
@include S(margin-bottom, 10px);
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ingame_HUD_PuzzleEditorTitle {
|
#ingame_HUD_PuzzleEditorTitle {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
||||||
@include S(top, 23px);
|
@include S(top, 18px);
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#ingame_HUD_PuzzleReview {
|
#ingame_HUD_PuzzleEditorReview {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@include S(top, 15px);
|
@include S(top, 17px);
|
||||||
@include S(right, 10px);
|
@include S(right, 10px);
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
@ -1,14 +1,15 @@
|
|||||||
#ingame_HUD_ModeSettings {
|
#ingame_HUD_PuzzleEditorSettings {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background: $ingameHudBg;
|
background: $ingameHudBg;
|
||||||
@include S(padding, 10px);
|
@include S(padding, 10px);
|
||||||
@include S(bottom, 50px);
|
@include S(bottom, 60px);
|
||||||
@include S(left, 15px);
|
@include S(left, 10px);
|
||||||
|
|
||||||
@include SuperSmallText;
|
@include SuperSmallText;
|
||||||
color: #eee;
|
color: #eee;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@include S(border-radius, $globalBorderRadius);
|
||||||
|
|
||||||
> .section {
|
> .section {
|
||||||
> label {
|
> label {
|
||||||
@ -37,11 +38,15 @@
|
|||||||
@include IncreasedClickArea(0px);
|
@include IncreasedClickArea(0px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.value {
|
.value {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@include S(min-width, 15px);
|
@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/sandbox_controller";
|
||||||
@import "ingame_hud/standalone_advantages";
|
@import "ingame_hud/standalone_advantages";
|
||||||
@import "ingame_hud/cat_memes";
|
@import "ingame_hud/cat_memes";
|
||||||
@import "ingame_hud/mode_menu_back";
|
@import "ingame_hud/puzzle_back_to_menu";
|
||||||
@import "ingame_hud/mode_menu_next";
|
@import "ingame_hud/puzzle_editor_review";
|
||||||
@import "ingame_hud/mode_menu";
|
|
||||||
@import "ingame_hud/mode_settings";
|
|
||||||
@import "ingame_hud/puzzle_dlc_logo";
|
@import "ingame_hud/puzzle_dlc_logo";
|
||||||
@import "ingame_hud/puzzle_editor_controls";
|
@import "ingame_hud/puzzle_editor_controls";
|
||||||
|
@import "ingame_hud/puzzle_editor_settings";
|
||||||
|
@import "ingame_hud/puzzle_play_metadata";
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
$elements:
|
$elements:
|
||||||
@ -79,12 +79,12 @@ ingame_HUD_PlacerVariants,
|
|||||||
ingame_HUD_PinnedShapes,
|
ingame_HUD_PinnedShapes,
|
||||||
ingame_HUD_GameMenu,
|
ingame_HUD_GameMenu,
|
||||||
ingame_HUD_KeybindingOverlay,
|
ingame_HUD_KeybindingOverlay,
|
||||||
ingame_HUD_ModeMenuBack,
|
ingame_HUD_PuzzleBackToMenu,
|
||||||
ingame_HUD_PuzzleReview,
|
ingame_HUD_PuzzleEditorReview,
|
||||||
ingame_HUD_PuzzleEditorControls,
|
ingame_HUD_PuzzleEditorControls,
|
||||||
ingame_HUD_PuzzleEditorTitle,
|
ingame_HUD_PuzzleEditorTitle,
|
||||||
ingame_HUD_ModeMenu,
|
ingame_HUD_PuzzleEditorSettings,
|
||||||
ingame_HUD_ModeSettings,
|
ingame_HUD_PuzzlePlayMetadata
|
||||||
ingame_HUD_Notifications,
|
ingame_HUD_Notifications,
|
||||||
ingame_HUD_DebugInfo,
|
ingame_HUD_DebugInfo,
|
||||||
ingame_HUD_EntityDebugger,
|
ingame_HUD_EntityDebugger,
|
||||||
@ -127,8 +127,8 @@ body.uiHidden {
|
|||||||
#ingame_HUD_PlacementHints,
|
#ingame_HUD_PlacementHints,
|
||||||
#ingame_HUD_GameMenu,
|
#ingame_HUD_GameMenu,
|
||||||
#ingame_HUD_PinnedShapes,
|
#ingame_HUD_PinnedShapes,
|
||||||
#ingame_HUD_ModeMenuBack,
|
#ingame_HUD_PuzzleBackToMenu,
|
||||||
#ingame_HUD_PuzzleReview,
|
#ingame_HUD_PuzzleEditorReview,
|
||||||
#ingame_HUD_Notifications,
|
#ingame_HUD_Notifications,
|
||||||
#ingame_HUD_TutorialHints,
|
#ingame_HUD_TutorialHints,
|
||||||
#ingame_HUD_Waypoints,
|
#ingame_HUD_Waypoints,
|
||||||
|
@ -74,6 +74,8 @@ export const globalConfig = {
|
|||||||
goalAcceptorMinimumDurationSeconds: G_IS_DEV ? 1 : 5,
|
goalAcceptorMinimumDurationSeconds: G_IS_DEV ? 1 : 5,
|
||||||
goalAcceptorsPerProducer: G_IS_DEV ? 4 : 4,
|
goalAcceptorsPerProducer: G_IS_DEV ? 4 : 4,
|
||||||
puzzleModeSpeed: 3,
|
puzzleModeSpeed: 3,
|
||||||
|
puzzleMinBoundsSize: 2,
|
||||||
|
puzzleMaxBoundsSize: 20,
|
||||||
|
|
||||||
buildingSpeeds: {
|
buildingSpeeds: {
|
||||||
cutter: 1 / 4,
|
cutter: 1 / 4,
|
||||||
|
@ -44,6 +44,15 @@ export class Rectangle {
|
|||||||
return new Rectangle(left, top, right - left, bottom - top);
|
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
|
* Returns if a intersects b
|
||||||
* @param {Rectangle} a
|
* @param {Rectangle} a
|
||||||
@ -287,6 +296,15 @@ export class Rectangle {
|
|||||||
return Rectangle.fromTRBL(top, right, bottom, left);
|
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
|
* Returns the union of this rectangle with another
|
||||||
* @param {Rectangle} rect
|
* @param {Rectangle} rect
|
||||||
|
@ -66,6 +66,10 @@ export class MetaBalancerBuilding extends MetaBuilding {
|
|||||||
* @returns {Array<[string, string]>}
|
* @returns {Array<[string, string]>}
|
||||||
*/
|
*/
|
||||||
getAdditionalStatistics(root, variant) {
|
getAdditionalStatistics(root, variant) {
|
||||||
|
if (root.gameMode.throughputDoesNotMatter()) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
let speedMultiplier = 2;
|
let speedMultiplier = 2;
|
||||||
switch (variant) {
|
switch (variant) {
|
||||||
case enumBalancerVariants.merger:
|
case enumBalancerVariants.merger:
|
||||||
|
@ -55,6 +55,9 @@ export class MetaBeltBuilding extends MetaBuilding {
|
|||||||
* @returns {Array<[string, string]>}
|
* @returns {Array<[string, string]>}
|
||||||
*/
|
*/
|
||||||
getAdditionalStatistics(root, variant) {
|
getAdditionalStatistics(root, variant) {
|
||||||
|
if (root.gameMode.throughputDoesNotMatter()) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
const beltSpeed = root.hubGoals.getBeltBaseSpeed();
|
const beltSpeed = root.hubGoals.getBeltBaseSpeed();
|
||||||
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(beltSpeed)]];
|
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(beltSpeed)]];
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,9 @@ export class MetaCutterBuilding extends MetaBuilding {
|
|||||||
* @returns {Array<[string, string]>}
|
* @returns {Array<[string, string]>}
|
||||||
*/
|
*/
|
||||||
getAdditionalStatistics(root, variant) {
|
getAdditionalStatistics(root, variant) {
|
||||||
|
if (root.gameMode.throughputDoesNotMatter()) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
const speed = root.hubGoals.getProcessorBaseSpeed(
|
const speed = root.hubGoals.getProcessorBaseSpeed(
|
||||||
variant === enumCutterVariants.quad
|
variant === enumCutterVariants.quad
|
||||||
? enumItemProcessorTypes.cutterQuad
|
? enumItemProcessorTypes.cutterQuad
|
||||||
|
@ -40,6 +40,9 @@ export class MetaFilterBuilding extends MetaBuilding {
|
|||||||
* @returns {Array<[string, string]>}
|
* @returns {Array<[string, string]>}
|
||||||
*/
|
*/
|
||||||
getAdditionalStatistics(root, variant) {
|
getAdditionalStatistics(root, variant) {
|
||||||
|
if (root.gameMode.throughputDoesNotMatter()) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
const beltSpeed = root.hubGoals.getBeltBaseSpeed();
|
const beltSpeed = root.hubGoals.getBeltBaseSpeed();
|
||||||
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(beltSpeed)]];
|
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(beltSpeed)]];
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,9 @@ export class MetaMinerBuilding extends MetaBuilding {
|
|||||||
* @returns {Array<[string, string]>}
|
* @returns {Array<[string, string]>}
|
||||||
*/
|
*/
|
||||||
getAdditionalStatistics(root, variant) {
|
getAdditionalStatistics(root, variant) {
|
||||||
|
if (root.gameMode.throughputDoesNotMatter()) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
const speed = root.hubGoals.getMinerBaseSpeed();
|
const speed = root.hubGoals.getMinerBaseSpeed();
|
||||||
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]];
|
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]];
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,9 @@ export class MetaMixerBuilding extends MetaBuilding {
|
|||||||
* @returns {Array<[string, string]>}
|
* @returns {Array<[string, string]>}
|
||||||
*/
|
*/
|
||||||
getAdditionalStatistics(root, variant) {
|
getAdditionalStatistics(root, variant) {
|
||||||
|
if (root.gameMode.throughputDoesNotMatter()) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
const speed = root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.mixer);
|
const speed = root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.mixer);
|
||||||
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]];
|
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]];
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,9 @@ export class MetaPainterBuilding extends MetaBuilding {
|
|||||||
* @returns {Array<[string, string]>}
|
* @returns {Array<[string, string]>}
|
||||||
*/
|
*/
|
||||||
getAdditionalStatistics(root, variant) {
|
getAdditionalStatistics(root, variant) {
|
||||||
|
if (root.gameMode.throughputDoesNotMatter()) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
switch (variant) {
|
switch (variant) {
|
||||||
case defaultBuildingVariant:
|
case defaultBuildingVariant:
|
||||||
case enumPainterVariants.mirrored: {
|
case enumPainterVariants.mirrored: {
|
||||||
|
@ -48,6 +48,9 @@ export class MetaRotaterBuilding extends MetaBuilding {
|
|||||||
* @returns {Array<[string, string]>}
|
* @returns {Array<[string, string]>}
|
||||||
*/
|
*/
|
||||||
getAdditionalStatistics(root, variant) {
|
getAdditionalStatistics(root, variant) {
|
||||||
|
if (root.gameMode.throughputDoesNotMatter()) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
switch (variant) {
|
switch (variant) {
|
||||||
case defaultBuildingVariant: {
|
case defaultBuildingVariant: {
|
||||||
const speed = root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.rotater);
|
const speed = root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.rotater);
|
||||||
|
@ -28,6 +28,9 @@ export class MetaStackerBuilding extends MetaBuilding {
|
|||||||
* @returns {Array<[string, string]>}
|
* @returns {Array<[string, string]>}
|
||||||
*/
|
*/
|
||||||
getAdditionalStatistics(root, variant) {
|
getAdditionalStatistics(root, variant) {
|
||||||
|
if (root.gameMode.throughputDoesNotMatter()) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
const speed = root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.stacker);
|
const speed = root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.stacker);
|
||||||
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]];
|
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]];
|
||||||
}
|
}
|
||||||
|
@ -72,13 +72,21 @@ export class MetaUndergroundBeltBuilding extends MetaBuilding {
|
|||||||
globalConfig.undergroundBeltMaxTilesByTier[enumUndergroundBeltVariantToTier[variant]];
|
globalConfig.undergroundBeltMaxTilesByTier[enumUndergroundBeltVariantToTier[variant]];
|
||||||
|
|
||||||
const beltSpeed = root.hubGoals.getUndergroundBeltBaseSpeed();
|
const beltSpeed = root.hubGoals.getUndergroundBeltBaseSpeed();
|
||||||
return [
|
|
||||||
|
/** @type {Array<[string, string]>} */
|
||||||
|
const stats = [
|
||||||
[
|
[
|
||||||
T.ingame.buildingPlacement.infoTexts.range,
|
T.ingame.buildingPlacement.infoTexts.range,
|
||||||
T.ingame.buildingPlacement.infoTexts.tiles.replace("<x>", "" + rangeTiles),
|
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);
|
root.keyMapper = new KeyActionMapper(root, this.root.gameState.inputReciever);
|
||||||
|
|
||||||
// Init game mode
|
// Init game mode
|
||||||
root.gameMode = GameMode.create(root, gameModeId);
|
root.gameMode = GameMode.create(root, gameModeId, parentState.creationPayload.gameModeParameters);
|
||||||
|
|
||||||
// Needs to come first
|
// Needs to come first
|
||||||
root.dynamicTickrate = new DynamicTickrate(root);
|
root.dynamicTickrate = new DynamicTickrate(root);
|
||||||
@ -455,7 +455,9 @@ export class GameCore {
|
|||||||
systems.hub.draw(params);
|
systems.hub.draw(params);
|
||||||
|
|
||||||
// Green wires overlay
|
// Green wires overlay
|
||||||
|
if (root.hud.parts.wiresOverlay) {
|
||||||
root.hud.parts.wiresOverlay.draw(params);
|
root.hud.parts.wiresOverlay.draw(params);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.root.currentLayer === "wires") {
|
if (this.root.currentLayer === "wires") {
|
||||||
// Static map entities
|
// Static map entities
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
/* typehints:start */
|
/* typehints:start */
|
||||||
import { GameRoot } from "./root";
|
import { GameRoot } from "./root";
|
||||||
import { Rectangle } from "../core/rectangle";
|
|
||||||
/* typehints:end */
|
/* typehints:end */
|
||||||
|
|
||||||
|
import { Rectangle } from "../core/rectangle";
|
||||||
import { gGameModeRegistry } from "../core/global_registries";
|
import { gGameModeRegistry } from "../core/global_registries";
|
||||||
import { types, BasicSerializableObject } from "../savegame/serialization";
|
import { types, BasicSerializableObject } from "../savegame/serialization";
|
||||||
import { MetaBuilding } from "./meta_building";
|
import { MetaBuilding } from "./meta_building";
|
||||||
import { MetaItemProducerBuilding } from "./buildings/item_producer";
|
import { MetaItemProducerBuilding } from "./buildings/item_producer";
|
||||||
|
import { BaseHUDPart } from "./hud/base_hud_part";
|
||||||
|
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
export const enumGameModeIds = {
|
export const enumGameModeIds = {
|
||||||
@ -36,9 +37,10 @@ export class GameMode extends BasicSerializableObject {
|
|||||||
/**
|
/**
|
||||||
* @param {GameRoot} root
|
* @param {GameRoot} root
|
||||||
* @param {string} [id=Regular]
|
* @param {string} [id=Regular]
|
||||||
|
* @param {object|undefined} payload
|
||||||
*/
|
*/
|
||||||
static create(root, id = enumGameModeIds.regular) {
|
static create(root, id = enumGameModeIds.regular, payload = undefined) {
|
||||||
return new (gGameModeRegistry.findById(id))(root);
|
return new (gGameModeRegistry.findById(id))(root, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,7 +49,11 @@ export class GameMode extends BasicSerializableObject {
|
|||||||
constructor(root) {
|
constructor(root) {
|
||||||
super();
|
super();
|
||||||
this.root = root;
|
this.root = root;
|
||||||
this.hiddenHudParts = {};
|
|
||||||
|
/**
|
||||||
|
* @type {Record<string, typeof BaseHUDPart>}
|
||||||
|
*/
|
||||||
|
this.additionalHudParts = {};
|
||||||
|
|
||||||
/** @type {typeof MetaBuilding[]} */
|
/** @type {typeof MetaBuilding[]} */
|
||||||
this.hiddenBuildings = [MetaItemProducerBuilding];
|
this.hiddenBuildings = [MetaItemProducerBuilding];
|
||||||
@ -78,14 +84,6 @@ export class GameMode extends BasicSerializableObject {
|
|||||||
return this.constructor.getType();
|
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
|
* @param {typeof MetaBuilding} building - Class name of building
|
||||||
* @returns {boolean}
|
* @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 { DrawParameters } from "../../core/draw_parameters";
|
||||||
import { Signal } from "../../core/signal";
|
import { Signal } from "../../core/signal";
|
||||||
import { KEYMAPPINGS } from "../key_action_mapper";
|
import { KEYMAPPINGS } from "../key_action_mapper";
|
||||||
@ -6,47 +6,16 @@ import { MetaBuilding } from "../meta_building";
|
|||||||
import { GameRoot } from "../root";
|
import { GameRoot } from "../root";
|
||||||
import { ShapeDefinition } from "../shape_definition";
|
import { ShapeDefinition } from "../shape_definition";
|
||||||
import { HUDBetaOverlay } from "./parts/beta_overlay";
|
import { HUDBetaOverlay } from "./parts/beta_overlay";
|
||||||
import { HUDBlueprintPlacer } from "./parts/blueprint_placer";
|
|
||||||
import { HUDBuildingsToolbar } from "./parts/buildings_toolbar";
|
import { HUDBuildingsToolbar } from "./parts/buildings_toolbar";
|
||||||
import { HUDBuildingPlacer } from "./parts/building_placer";
|
import { HUDBuildingPlacer } from "./parts/building_placer";
|
||||||
import { HUDCatMemes } from "./parts/cat_memes";
|
|
||||||
import { HUDColorBlindHelper } from "./parts/color_blind_helper";
|
import { HUDColorBlindHelper } from "./parts/color_blind_helper";
|
||||||
import { HUDConstantSignalEdit } from "./parts/constant_signal_edit";
|
|
||||||
import { HUDChangesDebugger } from "./parts/debug_changes";
|
import { HUDChangesDebugger } from "./parts/debug_changes";
|
||||||
import { HUDDebugInfo } from "./parts/debug_info";
|
import { HUDDebugInfo } from "./parts/debug_info";
|
||||||
import { HUDEntityDebugger } from "./parts/entity_debugger";
|
import { HUDEntityDebugger } from "./parts/entity_debugger";
|
||||||
import { 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 { HUDModalDialogs } from "./parts/modal_dialogs";
|
||||||
import { HUDModeMenu } from "./parts/mode_menu";
|
import { enumNotificationType } from "./parts/notifications";
|
||||||
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 { HUDSettingsMenu } from "./parts/settings_menu";
|
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 { 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";
|
import { TrailerMaker } from "./trailer_maker";
|
||||||
|
|
||||||
export class GameHUD {
|
export class GameHUD {
|
||||||
@ -73,79 +42,30 @@ export class GameHUD {
|
|||||||
unlockNotificationFinished: /** @type {TypedSignal<[]>} */ (new Signal()),
|
unlockNotificationFinished: /** @type {TypedSignal<[]>} */ (new Signal()),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.initParts({
|
this.parts = {
|
||||||
buildingsToolbar: HUDBuildingsToolbar,
|
buildingsToolbar: new HUDBuildingsToolbar(this.root),
|
||||||
wiresToolbar: HUDWiresToolbar,
|
buildingPlacer: new HUDBuildingPlacer(this.root),
|
||||||
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,
|
|
||||||
|
|
||||||
// Must always exist
|
// Must always exist
|
||||||
pinnedShapes: HUDPinnedShapes,
|
settingsMenu: new HUDSettingsMenu(this.root),
|
||||||
notifications: HUDNotifications,
|
debugInfo: new HUDDebugInfo(this.root),
|
||||||
settingsMenu: HUDSettingsMenu,
|
dialogs: new HUDModalDialogs(this.root),
|
||||||
debugInfo: HUDDebugInfo,
|
|
||||||
dialogs: HUDModalDialogs,
|
|
||||||
screenshotExporter: HUDScreenshotExporter,
|
|
||||||
shapeViewer: HUDShapeViewer,
|
|
||||||
|
|
||||||
wiresOverlay: HUDWiresOverlay,
|
|
||||||
layerPreview: HUDLayerPreview,
|
|
||||||
|
|
||||||
minerHighlight: HUDMinerHighlight,
|
|
||||||
tutorialVideoOffer: HUDTutorialVideoOffer,
|
|
||||||
|
|
||||||
// Typing hints
|
// Typing hints
|
||||||
/* typehints:start */
|
/* typehints:start */
|
||||||
/** @type {HUDChangesDebugger} */
|
/** @type {HUDChangesDebugger} */
|
||||||
changesDebugger: null,
|
changesDebugger: null,
|
||||||
/* typehints:end */
|
/* 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) {
|
if (G_IS_DEV && globalConfig.debug.enableEntityInspector) {
|
||||||
this.parts.entityDebugger = new HUDEntityDebugger(this.root);
|
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) {
|
if (G_IS_DEV && globalConfig.debug.renderChanges) {
|
||||||
this.parts.changesDebugger = new HUDChangesDebugger(this.root);
|
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) {
|
if (this.root.app.settings.getAllSettings().vignette) {
|
||||||
this.parts.vignetteOverlay = new HUDVignetteOverlay(this.root);
|
this.parts.vignetteOverlay = new HUDVignetteOverlay(this.root);
|
||||||
}
|
}
|
||||||
@ -154,12 +74,17 @@ export class GameHUD {
|
|||||||
this.parts.colorBlindHelper = new HUDColorBlindHelper(this.root);
|
this.parts.colorBlindHelper = new HUDColorBlindHelper(this.root);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.parts.sandboxController = new HUDSandboxController(this.root);
|
|
||||||
|
|
||||||
if (!G_IS_RELEASE && !G_IS_DEV) {
|
if (!G_IS_RELEASE && !G_IS_DEV) {
|
||||||
this.parts.betaOverlay = new HUDBetaOverlay(this.root);
|
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();
|
const frag = document.createDocumentFragment();
|
||||||
for (const key in this.parts) {
|
for (const key in this.parts) {
|
||||||
this.parts[key].createElements(frag);
|
this.parts[key].createElements(frag);
|
||||||
@ -180,21 +105,6 @@ export class GameHUD {
|
|||||||
/* dev:end*/
|
/* 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
|
* Attempts to close all overlays
|
||||||
*/
|
*/
|
||||||
|
@ -275,12 +275,14 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic {
|
|||||||
const worldPosition = this.root.camera.screenToWorld(mousePosition);
|
const worldPosition = this.root.camera.screenToWorld(mousePosition);
|
||||||
|
|
||||||
// Draw peeker
|
// Draw peeker
|
||||||
|
if (this.root.hud.parts.layerPreview) {
|
||||||
this.root.hud.parts.layerPreview.renderPreview(
|
this.root.hud.parts.layerPreview.renderPreview(
|
||||||
parameters,
|
parameters,
|
||||||
worldPosition,
|
worldPosition,
|
||||||
1 / this.root.camera.zoomLevel
|
1 / this.root.camera.zoomLevel
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {DrawParameters} parameters
|
* @param {DrawParameters} parameters
|
||||||
|
@ -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) {
|
deserialize(data) {
|
||||||
if (!data || !data.shapes || !Array.isArray(data.shapes)) {
|
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;
|
this.pinnedShapes = data.shapes;
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { makeDiv } from "../../../core/utils";
|
import { makeDiv } from "../../../core/utils";
|
||||||
import { BaseHUDPart } from "../base_hud_part";
|
import { BaseHUDPart } from "../base_hud_part";
|
||||||
|
|
||||||
export class HUDModeMenuBack extends BaseHUDPart {
|
export class HUDPuzzleBackToMenu extends BaseHUDPart {
|
||||||
createElements(parent) {
|
createElements(parent) {
|
||||||
const key = this.root.gameMode.getId();
|
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 = document.createElement("button");
|
||||||
this.button.classList.add("button");
|
this.button.classList.add("button");
|
||||||
this.element.appendChild(this.button);
|
this.element.appendChild(this.button);
|
@ -1,19 +1,17 @@
|
|||||||
import { makeDiv } from "../../../core/utils";
|
import { makeDiv } from "../../../core/utils";
|
||||||
|
import { T } from "../../../translations";
|
||||||
import { BaseHUDPart } from "../base_hud_part";
|
import { BaseHUDPart } from "../base_hud_part";
|
||||||
|
|
||||||
export class HUDPuzzleEditorControls extends BaseHUDPart {
|
export class HUDPuzzleEditorControls extends BaseHUDPart {
|
||||||
createElements(parent) {
|
createElements(parent) {
|
||||||
this.element = makeDiv(parent, "ingame_HUD_PuzzleEditorControls");
|
this.element = makeDiv(parent, "ingame_HUD_PuzzleEditorControls");
|
||||||
|
|
||||||
this.element.innerHTML = `
|
this.element.innerHTML = T.ingame.puzzleEditorControls.instructions
|
||||||
|
.map(text => `<span>${text}</span>`)
|
||||||
<span>1. Build constant producers to generate resources.</span>
|
.join("");
|
||||||
<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.titleElement = makeDiv(parent, "ingame_HUD_PuzzleEditorTitle");
|
this.titleElement = makeDiv(parent, "ingame_HUD_PuzzleEditorTitle");
|
||||||
this.titleElement.innerText = "Puzzle Editor";
|
this.titleElement.innerText = T.ingame.puzzleEditorControls.title;
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize() {}
|
initialize() {}
|
||||||
|
@ -16,7 +16,7 @@ import { BaseHUDPart } from "../base_hud_part";
|
|||||||
const trim = require("trim");
|
const trim = require("trim");
|
||||||
const logger = createLogger("puzzle-review");
|
const logger = createLogger("puzzle-review");
|
||||||
|
|
||||||
export class HUDPuzzleReview extends BaseHUDPart {
|
export class HUDPuzzleEditorReview extends BaseHUDPart {
|
||||||
constructor(root) {
|
constructor(root) {
|
||||||
super(root);
|
super(root);
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ export class HUDPuzzleReview extends BaseHUDPart {
|
|||||||
createElements(parent) {
|
createElements(parent) {
|
||||||
const key = this.root.gameMode.getId();
|
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 = document.createElement("button");
|
||||||
this.button.classList.add("button");
|
this.button.classList.add("button");
|
||||||
this.button.textContent = T.puzzleMenu.reviewPuzzle;
|
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
|
// Clear all shapes of this level
|
||||||
hubGoals.storedShapes[hubGoals.currentGoal.definition.getHash()] = 0;
|
hubGoals.storedShapes[hubGoals.currentGoal.definition.getHash()] = 0;
|
||||||
|
|
||||||
|
if (this.root.hud.parts.pinnedShapes) {
|
||||||
this.root.hud.parts.pinnedShapes.rerenderFull();
|
this.root.hud.parts.pinnedShapes.rerenderFull();
|
||||||
|
}
|
||||||
|
|
||||||
// Compute gained rewards
|
// Compute gained rewards
|
||||||
hubGoals.gainedRewards = {};
|
hubGoals.gainedRewards = {};
|
||||||
@ -149,19 +151,8 @@ export class HUDSandboxController extends BaseHUDPart {
|
|||||||
this.domAttach = new DynamicDomAttach(this.root, this.element);
|
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() {
|
toggle() {
|
||||||
if (!this.visible && !this.isAvailable()) {
|
if (!this.visible) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.visible = !this.visible;
|
this.visible = !this.visible;
|
||||||
|
@ -5,13 +5,8 @@ import { GameRoot } from "../root";
|
|||||||
import { Rectangle } from "../../core/rectangle";
|
import { Rectangle } from "../../core/rectangle";
|
||||||
import { types } from "../../savegame/serialization";
|
import { types } from "../../savegame/serialization";
|
||||||
import { enumGameModeTypes, GameMode } from "../game_mode";
|
import { enumGameModeTypes, GameMode } from "../game_mode";
|
||||||
import { HUDGameMenu } from "../hud/parts/game_menu";
|
import { HUDPuzzleBackToMenu } from "../hud/parts/puzzle_back_to_menu";
|
||||||
import { HUDInteractiveTutorial } from "../hud/parts/interactive_tutorial";
|
import { HUDPuzzleDLCLogo } from "../hud/parts/puzzle_dlc_logo";
|
||||||
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";
|
|
||||||
|
|
||||||
export class PuzzleGameMode extends GameMode {
|
export class PuzzleGameMode extends GameMode {
|
||||||
static getType() {
|
static getType() {
|
||||||
@ -32,22 +27,13 @@ export class PuzzleGameMode extends GameMode {
|
|||||||
|
|
||||||
const data = this.getSaveData();
|
const data = this.getSaveData();
|
||||||
|
|
||||||
this.hiddenHudParts = {
|
this.additionalHudParts = {
|
||||||
[HUDGameMenu.name]: false,
|
puzzleBackToMenu: HUDPuzzleBackToMenu,
|
||||||
[HUDMassSelector.name]: false,
|
puzzleDlcLogo: HUDPuzzleDLCLogo,
|
||||||
[HUDInteractiveTutorial.name]: false,
|
|
||||||
[HUDKeybindingOverlay.name]: false,
|
|
||||||
[HUDPartTutorialHints.name]: false,
|
|
||||||
[HUDPinnedShapes.name]: false,
|
|
||||||
[HUDWaypoints.name]: false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.setDimensions(data.zoneWidth, data.zoneHeight);
|
this.zoneWidth = data.zoneWidth || 8;
|
||||||
}
|
this.zoneHeight = data.zoneHeight || 6;
|
||||||
|
|
||||||
setDimensions(w = 16, h = 9) {
|
|
||||||
this.zoneWidth = w < 2 ? 2 : w;
|
|
||||||
this.zoneHeight = h < 2 ? 2 : h;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getSaveData() {
|
getSaveData() {
|
||||||
@ -58,16 +44,12 @@ export class PuzzleGameMode extends GameMode {
|
|||||||
return save.gameMode.data;
|
return save.gameMode.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
createCenteredRectangle(width, height) {
|
|
||||||
return new Rectangle(-Math.ceil(width / 2), -Math.ceil(height / 2), width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
getCameraBounds() {
|
getCameraBounds() {
|
||||||
return this.createCenteredRectangle(this.zoneWidth + 20, this.zoneHeight + 20);
|
return Rectangle.centered(this.zoneWidth + 20, this.zoneHeight + 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
getBuildableZones() {
|
getBuildableZones() {
|
||||||
return [this.createCenteredRectangle(this.zoneWidth, this.zoneHeight)];
|
return [Rectangle.centered(this.zoneWidth, this.zoneHeight)];
|
||||||
}
|
}
|
||||||
|
|
||||||
hasHub() {
|
hasHub() {
|
||||||
|
@ -19,6 +19,10 @@ import { MetaVirtualProcessorBuilding } from "../buildings/virtual_processor";
|
|||||||
import { MetaAnalyzerBuilding } from "../buildings/analyzer";
|
import { MetaAnalyzerBuilding } from "../buildings/analyzer";
|
||||||
import { MetaComparatorBuilding } from "../buildings/comparator";
|
import { MetaComparatorBuilding } from "../buildings/comparator";
|
||||||
import { MetaTransistorBuilding } from "../buildings/transistor";
|
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 {
|
export class PuzzleEditGameMode extends PuzzleGameMode {
|
||||||
static getId() {
|
static getId() {
|
||||||
@ -33,8 +37,6 @@ export class PuzzleEditGameMode extends PuzzleGameMode {
|
|||||||
constructor(root) {
|
constructor(root) {
|
||||||
super(root);
|
super(root);
|
||||||
|
|
||||||
this.playtest = false;
|
|
||||||
|
|
||||||
this.hiddenBuildings = [
|
this.hiddenBuildings = [
|
||||||
MetaStorageBuilding,
|
MetaStorageBuilding,
|
||||||
MetaReaderBuilding,
|
MetaReaderBuilding,
|
||||||
@ -53,32 +55,10 @@ export class PuzzleEditGameMode extends PuzzleGameMode {
|
|||||||
MetaComparatorBuilding,
|
MetaComparatorBuilding,
|
||||||
MetaTransistorBuilding,
|
MetaTransistorBuilding,
|
||||||
];
|
];
|
||||||
}
|
|
||||||
|
|
||||||
adjustZone(w = 0, h = 0) {
|
this.additionalHudParts.puzzleEditorControls = HUDPuzzleEditorControls;
|
||||||
// @todo notify user when zone cannot be shrunk
|
this.additionalHudParts.puzzleEditorReview = HUDPuzzleEditorReview;
|
||||||
if (this.zoneWidth + w <= 0) {
|
this.additionalHudParts.puzzleEditorSettings = HUDPuzzleEditorSettings;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getIsEditor() {
|
getIsEditor() {
|
||||||
|
@ -2,16 +2,65 @@
|
|||||||
import { GameRoot } from "../root";
|
import { GameRoot } from "../root";
|
||||||
/* typehints:end */
|
/* typehints:end */
|
||||||
|
|
||||||
import { PuzzleGameMode } from "./puzzle";
|
|
||||||
import { enumGameModeIds } from "../game_mode";
|
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 {
|
export class PuzzlePlayGameMode extends PuzzleGameMode {
|
||||||
static getId() {
|
static getId() {
|
||||||
return enumGameModeIds.puzzlePlay;
|
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);
|
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 { findNiceIntegerValue } from "../../core/utils";
|
||||||
import { MetaConstantProducerBuilding } from "../buildings/constant_producer";
|
import { MetaConstantProducerBuilding } from "../buildings/constant_producer";
|
||||||
import { MetaGoalAcceptorBuilding } from "../buildings/goal_acceptor";
|
import { MetaGoalAcceptorBuilding } from "../buildings/goal_acceptor";
|
||||||
import { 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 { enumGameModeIds, enumGameModeTypes, GameMode } from "../game_mode";
|
||||||
import { ShapeDefinition } from "../shape_definition";
|
import { ShapeDefinition } from "../shape_definition";
|
||||||
import { enumHubGoalRewards } from "../tutorial_goals";
|
import { enumHubGoalRewards } from "../tutorial_goals";
|
||||||
import { HUDPuzzleDLCLogo } from "../hud/parts/puzzle_dlc_logo";
|
import { HUDWiresToolbar } from "../hud/parts/wires_toolbar";
|
||||||
import { HUDPuzzleEditorControls } from "../hud/parts/puzzle_editor_controls";
|
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 {{
|
/** @typedef {{
|
||||||
* shape: string,
|
* shape: string,
|
||||||
@ -517,15 +539,48 @@ export class RegularGameMode extends GameMode {
|
|||||||
constructor(root) {
|
constructor(root) {
|
||||||
super(root);
|
super(root);
|
||||||
|
|
||||||
this.hiddenHudParts = {
|
this.additionalHudParts = {
|
||||||
[HUDModeMenuBack.name]: false,
|
wiresToolbar: HUDWiresToolbar,
|
||||||
[HUDPuzzleReview.name]: false,
|
blueprintPlacer: HUDBlueprintPlacer,
|
||||||
[HUDModeMenu.name]: false,
|
unlockNotification: HUDUnlockNotification,
|
||||||
[HUDModeSettings.name]: false,
|
massSelector: HUDMassSelector,
|
||||||
[HUDPuzzleDLCLogo.name]: false,
|
shop: HUDShop,
|
||||||
[HUDPuzzleEditorControls.name]: false,
|
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];
|
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(
|
items.push(
|
||||||
...this.root.hud.parts.pinnedShapes.pinnedShapes.map(key =>
|
...this.root.hud.parts.pinnedShapes.pinnedShapes.map(key =>
|
||||||
this.root.shapeDefinitionMgr.getShapeItemFromShortKey(key)
|
this.root.shapeDefinitionMgr.getShapeItemFromShortKey(key)
|
||||||
|
@ -49,15 +49,10 @@ export class ZoneSystem extends GameSystem {
|
|||||||
transformed.y += tile.y;
|
transformed.y += tile.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const zone of zones) {
|
if (!zones.some(zone => zone.intersectsFully(transformed))) {
|
||||||
const intersection = zone.getIntersection(transformed);
|
|
||||||
if (intersection && intersection.w * intersection.h === transformed.w * transformed.h) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return STOP_PROPAGATION;
|
return STOP_PROPAGATION;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws the zone
|
* Draws the zone
|
||||||
@ -84,8 +79,7 @@ export class ZoneSystem extends GameSystem {
|
|||||||
context.lineWidth = 2;
|
context.lineWidth = 2;
|
||||||
context.strokeStyle = THEME.map.zone.borderSolid;
|
context.strokeStyle = THEME.map.zone.borderSolid;
|
||||||
context.beginPath();
|
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();
|
context.stroke();
|
||||||
|
|
||||||
const outer = zone;
|
const outer = zone;
|
||||||
|
@ -51,9 +51,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
"zone": {
|
"zone": {
|
||||||
"background": "#fff",
|
"borderSolid": "rgba(23, 192, 255, 1)",
|
||||||
"border": "rgba(23, 192, 255, 0.1)",
|
|
||||||
"borderSolid": "rgba(23, 192, 255, 0.7)",
|
|
||||||
"outerColor": "rgba(240, 240, 255, 0.5)"
|
"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 { enumConstantSignalType } from "../game/components/constant_signal";
|
||||||
import { StaticMapEntityComponent } from "../game/components/static_map_entity";
|
import { StaticMapEntityComponent } from "../game/components/static_map_entity";
|
||||||
import { ShapeItem } from "../game/items/shape_item";
|
import { ShapeItem } from "../game/items/shape_item";
|
||||||
import { GameRoot } from "../game/root";
|
|
||||||
|
|
||||||
export class PuzzleSerializer {
|
export class PuzzleSerializer {
|
||||||
/**
|
/**
|
||||||
* Serializes the game root into a dump
|
* Serializes the game root into a dump
|
||||||
* @param {GameRoot} root
|
* @param {GameRoot} root
|
||||||
* @param {boolean=} sanityChecks Whether to check for validity
|
* @returns {import("./savegame_typedefs").PuzzleGameData}
|
||||||
* @returns {object}
|
|
||||||
*/
|
*/
|
||||||
generateDumpFromGameRoot(root, sanityChecks = true) {
|
generateDumpFromGameRoot(root) {
|
||||||
console.log("serializing", root);
|
console.log("serializing", root);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {import("./savegame_typedefs").PuzzleGameData["buildings"]}
|
||||||
|
*/
|
||||||
let buildings = [];
|
let buildings = [];
|
||||||
|
|
||||||
for (const entity of root.entityMgr.getAllWithComponent(StaticMapEntityComponent)) {
|
for (const entity of root.entityMgr.getAllWithComponent(StaticMapEntityComponent)) {
|
||||||
@ -51,9 +56,15 @@ export class PuzzleSerializer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mode = /** @type {PuzzleGameMode} */ (root.gameMode);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
version: 1,
|
||||||
buildings,
|
buildings,
|
||||||
bounds: root.gameMode.getBuildableZones()[0],
|
bounds: {
|
||||||
|
w: mode.zoneWidth,
|
||||||
|
h: mode.zoneHeight,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,12 +40,8 @@ export class SavegameSerializer {
|
|||||||
hubGoals: root.hubGoals.serialize(),
|
hubGoals: root.hubGoals.serialize(),
|
||||||
entities: this.internal.serializeEntityArray(root.entityMgr.entities),
|
entities: this.internal.serializeEntityArray(root.entityMgr.entities),
|
||||||
beltPaths: root.systemMgr.systems.belt.serializePaths(),
|
beltPaths: root.systemMgr.systems.belt.serializePaths(),
|
||||||
pinnedShapes: root.gameMode.isHudPartExcluded(HUDPinnedShapes.name)
|
pinnedShapes: root.hud.parts.pinnedShapes ? root.hud.parts.pinnedShapes.serialize() : null,
|
||||||
? null
|
waypoints: root.hud.parts.waypoints ? root.hud.parts.waypoints.serialize() : null,
|
||||||
: root.hud.parts.pinnedShapes.serialize(),
|
|
||||||
waypoints: root.gameMode.isHudPartExcluded(HUDWaypoints.name)
|
|
||||||
? null
|
|
||||||
: root.hud.parts.waypoints.serialize(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (G_IS_DEV) {
|
if (G_IS_DEV) {
|
||||||
@ -142,11 +138,11 @@ export class SavegameSerializer {
|
|||||||
errorReason = errorReason || this.internal.deserializeEntityArray(root, savegame.entities);
|
errorReason = errorReason || this.internal.deserializeEntityArray(root, savegame.entities);
|
||||||
errorReason = errorReason || root.systemMgr.systems.belt.deserializePaths(savegame.beltPaths);
|
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);
|
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);
|
errorReason = errorReason || root.hud.parts.waypoints.deserialize(savegame.waypoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,4 +41,46 @@
|
|||||||
* }} SavegamesData
|
* }} 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 {};
|
export default {};
|
||||||
|
@ -45,6 +45,9 @@ export class GameCreationPayload {
|
|||||||
|
|
||||||
/** @type {Savegame} */
|
/** @type {Savegame} */
|
||||||
this.savegame;
|
this.savegame;
|
||||||
|
|
||||||
|
/** @type {object|undefined} */
|
||||||
|
this.gameModeParameters;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,17 +7,6 @@ import { T } from "../translations";
|
|||||||
|
|
||||||
const categories = ["levels", "new", "topRated", "myPuzzles"];
|
const categories = ["levels", "new", "topRated", "myPuzzles"];
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {{
|
|
||||||
* shortKey: string;
|
|
||||||
* upvotes: number;
|
|
||||||
* playcount: number;
|
|
||||||
* title: string;
|
|
||||||
* author: string;
|
|
||||||
* completed: boolean;
|
|
||||||
* }} PuzzleMetadata
|
|
||||||
*/
|
|
||||||
|
|
||||||
const SAMPLE_PUZZLE = {
|
const SAMPLE_PUZZLE = {
|
||||||
shortKey: "CuCuCuCu",
|
shortKey: "CuCuCuCu",
|
||||||
upvotes: 10000,
|
upvotes: 10000,
|
||||||
@ -26,6 +15,7 @@ const SAMPLE_PUZZLE = {
|
|||||||
author: "verylongsteamnamewhichbreaks",
|
author: "verylongsteamnamewhichbreaks",
|
||||||
completed: false,
|
completed: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const BUILTIN_PUZZLES = [
|
const BUILTIN_PUZZLES = [
|
||||||
{ ...SAMPLE_PUZZLE, completed: true },
|
{ ...SAMPLE_PUZZLE, completed: true },
|
||||||
{ ...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) {
|
renderPuzzles(puzzles) {
|
||||||
const container = this.htmlElement.querySelector("#mainContainer");
|
const container = this.htmlElement.querySelector("#mainContainer");
|
||||||
@ -191,6 +181,8 @@ export class PuzzleMenuState extends TextualGameState {
|
|||||||
elem.appendChild(icon);
|
elem.appendChild(icon);
|
||||||
|
|
||||||
container.appendChild(elem);
|
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));
|
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() {
|
onEnter() {
|
||||||
this.selectCategory("levels");
|
this.selectCategory("levels");
|
||||||
|
|
||||||
@ -209,7 +244,8 @@ export class PuzzleMenuState extends TextualGameState {
|
|||||||
this.trackClicks(this.htmlElement.querySelector("button.createPuzzle"), this.createNewPuzzle);
|
this.trackClicks(this.htmlElement.querySelector("button.createPuzzle"), this.createNewPuzzle);
|
||||||
|
|
||||||
if (G_IS_DEV && globalConfig.debug.testPuzzleMode) {
|
if (G_IS_DEV && globalConfig.debug.testPuzzleMode) {
|
||||||
this.createNewPuzzle();
|
// this.createNewPuzzle();
|
||||||
|
this.playPuzzle(SAMPLE_PUZZLE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,6 +298,10 @@ dialogs:
|
|||||||
|
|
||||||
placeholderName: Puzzle Title
|
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:
|
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
|
||||||
@ -522,6 +526,22 @@ ingame:
|
|||||||
title: Support me
|
title: Support me
|
||||||
desc: I develop the game in my spare time!
|
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
|
# All shop upgrades
|
||||||
shopUpgrades:
|
shopUpgrades:
|
||||||
belt:
|
belt:
|
||||||
@ -749,12 +769,12 @@ buildings:
|
|||||||
constant_producer:
|
constant_producer:
|
||||||
default:
|
default:
|
||||||
name: &constant_producer Constant Producer
|
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:
|
goal_acceptor:
|
||||||
default:
|
default:
|
||||||
name: &goal_acceptor Goal Acceptor
|
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:
|
storyRewards:
|
||||||
# Those are the rewards gained from completing the store
|
# Those are the rewards gained from completing the store
|
||||||
|
Loading…
Reference in New Issue
Block a user