mirror of
https://github.com/tobspr/shapez.io.git
synced 2025-06-13 13:04:03 +00:00
Puzzle mode, part 3
This commit is contained in:
parent
f64714ec25
commit
b732db58b9
Binary file not shown.
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 14 KiB |
@ -67,6 +67,14 @@
|
|||||||
* {
|
* {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.text {
|
||||||
|
text-transform: uppercase;
|
||||||
|
@include S(margin-bottom, 10px);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> .dialogInner {
|
> .dialogInner {
|
||||||
@ -168,6 +176,11 @@
|
|||||||
|
|
||||||
&.errored {
|
&.errored {
|
||||||
background-color: rgb(250, 206, 206);
|
background-color: rgb(250, 206, 206);
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: #fff;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#ingame_HUD_ModeMenuNext {
|
#ingame_HUD_PuzzleReview {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@include S(top, 15px);
|
@include S(top, 15px);
|
||||||
@include S(right, 10px);
|
@include S(right, 10px);
|
||||||
|
@ -80,7 +80,7 @@ ingame_HUD_PinnedShapes,
|
|||||||
ingame_HUD_GameMenu,
|
ingame_HUD_GameMenu,
|
||||||
ingame_HUD_KeybindingOverlay,
|
ingame_HUD_KeybindingOverlay,
|
||||||
ingame_HUD_ModeMenuBack,
|
ingame_HUD_ModeMenuBack,
|
||||||
ingame_HUD_ModeMenuNext,
|
ingame_HUD_PuzzleReview,
|
||||||
ingame_HUD_PuzzleEditorControls,
|
ingame_HUD_PuzzleEditorControls,
|
||||||
ingame_HUD_PuzzleEditorTitle,
|
ingame_HUD_PuzzleEditorTitle,
|
||||||
ingame_HUD_ModeMenu,
|
ingame_HUD_ModeMenu,
|
||||||
@ -128,7 +128,7 @@ body.uiHidden {
|
|||||||
#ingame_HUD_GameMenu,
|
#ingame_HUD_GameMenu,
|
||||||
#ingame_HUD_PinnedShapes,
|
#ingame_HUD_PinnedShapes,
|
||||||
#ingame_HUD_ModeMenuBack,
|
#ingame_HUD_ModeMenuBack,
|
||||||
#ingame_HUD_ModeMenuNext,
|
#ingame_HUD_PuzzleReview,
|
||||||
#ingame_HUD_Notifications,
|
#ingame_HUD_Notifications,
|
||||||
#ingame_HUD_TutorialHints,
|
#ingame_HUD_TutorialHints,
|
||||||
#ingame_HUD_Waypoints,
|
#ingame_HUD_Waypoints,
|
||||||
|
@ -71,6 +71,10 @@ export const globalConfig = {
|
|||||||
|
|
||||||
readerAnalyzeIntervalSeconds: 10,
|
readerAnalyzeIntervalSeconds: 10,
|
||||||
|
|
||||||
|
goalAcceptorMinimumDurationSeconds: G_IS_DEV ? 1 : 5,
|
||||||
|
goalAcceptorsPerProducer: G_IS_DEV ? 4 : 4,
|
||||||
|
puzzleModeSpeed: 3,
|
||||||
|
|
||||||
buildingSpeeds: {
|
buildingSpeeds: {
|
||||||
cutter: 1 / 4,
|
cutter: 1 / 4,
|
||||||
cutterQuad: 1 / 4,
|
cutterQuad: 1 / 4,
|
||||||
|
@ -267,7 +267,7 @@ export class Dialog {
|
|||||||
* Dialog which simply shows a loading spinner
|
* Dialog which simply shows a loading spinner
|
||||||
*/
|
*/
|
||||||
export class DialogLoading extends Dialog {
|
export class DialogLoading extends Dialog {
|
||||||
constructor(app) {
|
constructor(app, text = "") {
|
||||||
super({
|
super({
|
||||||
app,
|
app,
|
||||||
title: "",
|
title: "",
|
||||||
@ -279,6 +279,8 @@ export class DialogLoading extends Dialog {
|
|||||||
// Loading dialog can not get closed with back button
|
// Loading dialog can not get closed with back button
|
||||||
this.inputReciever.backButton.removeAll();
|
this.inputReciever.backButton.removeAll();
|
||||||
this.inputReciever.context = "dialog-loading";
|
this.inputReciever.context = "dialog-loading";
|
||||||
|
|
||||||
|
this.text = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
createElement() {
|
createElement() {
|
||||||
@ -287,6 +289,13 @@ export class DialogLoading extends Dialog {
|
|||||||
elem.classList.add("loadingDialog");
|
elem.classList.add("loadingDialog");
|
||||||
this.element = elem;
|
this.element = elem;
|
||||||
|
|
||||||
|
if (this.text) {
|
||||||
|
const text = document.createElement("div");
|
||||||
|
text.classList.add("text");
|
||||||
|
text.innerText = this.text;
|
||||||
|
elem.appendChild(text);
|
||||||
|
}
|
||||||
|
|
||||||
const loader = document.createElement("div");
|
const loader = document.createElement("div");
|
||||||
loader.classList.add("prefab_LoadingTextWithAnim");
|
loader.classList.add("prefab_LoadingTextWithAnim");
|
||||||
loader.classList.add("loadingIndicator");
|
loader.classList.add("loadingIndicator");
|
||||||
@ -444,7 +453,7 @@ export class DialogWithForm extends Dialog {
|
|||||||
for (let i = 0; i < this.formElements.length; ++i) {
|
for (let i = 0; i < this.formElements.length; ++i) {
|
||||||
const elem = this.formElements[i];
|
const elem = this.formElements[i];
|
||||||
elem.bindEvents(div, this.clickDetectors);
|
elem.bindEvents(div, this.clickDetectors);
|
||||||
elem.valueChosen.add(this.closeRequested.dispatch, this.closeRequested);
|
// elem.valueChosen.add(this.closeRequested.dispatch, this.closeRequested);
|
||||||
elem.valueChosen.add(this.valueChosen.dispatch, this.valueChosen);
|
elem.valueChosen.add(this.valueChosen.dispatch, this.valueChosen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,6 +117,11 @@ export class FormElementInput extends FormElement {
|
|||||||
return this.element.value;
|
return this.element.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setValue(value) {
|
||||||
|
this.element.value = value;
|
||||||
|
this.updateErrorState();
|
||||||
|
}
|
||||||
|
|
||||||
focus() {
|
focus() {
|
||||||
this.element.focus();
|
this.element.focus();
|
||||||
}
|
}
|
||||||
|
@ -98,6 +98,18 @@ export class MetaBalancerBuilding extends MetaBuilding {
|
|||||||
available.push(enumBalancerVariants.splitter, enumBalancerVariants.splitterInverse);
|
available.push(enumBalancerVariants.splitter, enumBalancerVariants.splitterInverse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (root.gameMode.getIsDeterministic()) {
|
||||||
|
// mergers are not deterministic
|
||||||
|
available = available.filter(
|
||||||
|
v =>
|
||||||
|
![
|
||||||
|
enumBalancerVariants.merger,
|
||||||
|
enumBalancerVariants.mergerInverse,
|
||||||
|
defaultBuildingVariant,
|
||||||
|
].includes(v)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return available;
|
return available;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ export class MetaGoalAcceptorBuilding extends MetaBuilding {
|
|||||||
slots: [
|
slots: [
|
||||||
{
|
{
|
||||||
pos: new Vector(0, 0),
|
pos: new Vector(0, 0),
|
||||||
directions: [enumDirection.top],
|
directions: [enumDirection.bottom],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
@ -41,12 +41,6 @@ export class MetaGoalAcceptorBuilding extends MetaBuilding {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
entity.addComponent(
|
|
||||||
new BeltReaderComponent({
|
|
||||||
type: enumBeltReaderType.wireless,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
entity.addComponent(new GoalAcceptorComponent({}));
|
entity.addComponent(new GoalAcceptorComponent({}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -393,11 +393,11 @@ export class Camera extends BasicSerializableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getMaximumZoom() {
|
getMaximumZoom() {
|
||||||
return this.root.gameMode.getMaximumZoom() * this.root.app.platformWrapper.getScreenScale();
|
return this.root.gameMode.getMaximumZoom();
|
||||||
}
|
}
|
||||||
|
|
||||||
getMinimumZoom() {
|
getMinimumZoom() {
|
||||||
return this.root.gameMode.getMinimumZoom() * this.root.app.platformWrapper.getScreenScale();
|
return this.root.gameMode.getMinimumZoom();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,11 +1,19 @@
|
|||||||
|
import { globalConfig } from "../../core/config";
|
||||||
import { BaseItem } from "../base_item";
|
import { BaseItem } from "../base_item";
|
||||||
import { Component } from "../component";
|
import { Component } from "../component";
|
||||||
|
import { typeItemSingleton } from "../item_resolver";
|
||||||
|
|
||||||
export class GoalAcceptorComponent extends Component {
|
export class GoalAcceptorComponent extends Component {
|
||||||
static getId() {
|
static getId() {
|
||||||
return "GoalAcceptor";
|
return "GoalAcceptor";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getSchema() {
|
||||||
|
return {
|
||||||
|
item: typeItemSingleton,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {object} param0
|
* @param {object} param0
|
||||||
* @param {BaseItem=} param0.item
|
* @param {BaseItem=} param0.item
|
||||||
@ -13,8 +21,25 @@ export class GoalAcceptorComponent extends Component {
|
|||||||
*/
|
*/
|
||||||
constructor({ item = null, rate = null }) {
|
constructor({ item = null, rate = null }) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
// ths item to produce
|
||||||
|
/** @type {BaseItem | undefined} */
|
||||||
this.item = item;
|
this.item = item;
|
||||||
|
|
||||||
this.achieved = false;
|
// the last items we delivered
|
||||||
|
/** @type {{ item: BaseItem; time: number; }[]} */
|
||||||
|
this.deliveryHistory = [];
|
||||||
|
|
||||||
|
// Used for animations
|
||||||
|
this.displayPercentage = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getRequiredDeliveryHistorySize() {
|
||||||
|
return (
|
||||||
|
(globalConfig.puzzleModeSpeed *
|
||||||
|
globalConfig.goalAcceptorMinimumDurationSeconds *
|
||||||
|
globalConfig.beltSpeedItemsPerSecond) /
|
||||||
|
globalConfig.goalAcceptorsPerProducer
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,12 +102,12 @@ export class GameCore {
|
|||||||
// This isn't nice, but we need it right here
|
// This isn't nice, but we need it right here
|
||||||
root.keyMapper = new KeyActionMapper(root, this.root.gameState.inputReciever);
|
root.keyMapper = new KeyActionMapper(root, this.root.gameState.inputReciever);
|
||||||
|
|
||||||
// Needs to come first
|
|
||||||
root.dynamicTickrate = new DynamicTickrate(root);
|
|
||||||
|
|
||||||
// Init game mode
|
// Init game mode
|
||||||
root.gameMode = GameMode.create(root, gameModeId);
|
root.gameMode = GameMode.create(root, gameModeId);
|
||||||
|
|
||||||
|
// Needs to come first
|
||||||
|
root.dynamicTickrate = new DynamicTickrate(root);
|
||||||
|
|
||||||
// Init classes
|
// Init classes
|
||||||
root.camera = new Camera(root);
|
root.camera = new Camera(root);
|
||||||
root.map = new MapView(root);
|
root.map = new MapView(root);
|
||||||
|
@ -23,10 +23,16 @@ export class DynamicTickrate {
|
|||||||
|
|
||||||
this.averageFps = 60;
|
this.averageFps = 60;
|
||||||
|
|
||||||
this.setTickRate(this.root.app.settings.getDesiredFps());
|
const fixedRate = this.root.gameMode.getFixedTickrate();
|
||||||
|
if (fixedRate) {
|
||||||
|
logger.log("Setting fixed tickrate of", fixedRate);
|
||||||
|
this.setTickRate(fixedRate);
|
||||||
|
} else {
|
||||||
|
this.setTickRate(this.root.app.settings.getDesiredFps());
|
||||||
|
|
||||||
if (G_IS_DEV && globalConfig.debug.renderForTrailer) {
|
if (G_IS_DEV && globalConfig.debug.renderForTrailer) {
|
||||||
this.setTickRate(300);
|
this.setTickRate(300);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,9 +105,7 @@ export class DynamicTickrate {
|
|||||||
|
|
||||||
this.averageTickDuration = average;
|
this.averageTickDuration = average;
|
||||||
|
|
||||||
const desiredFps = this.root.app.settings.getDesiredFps();
|
// Disabled for now: Dynamically adjusting tick rate
|
||||||
|
|
||||||
// Disabled for now: Dynamicall adjusting tick rate
|
|
||||||
// if (this.averageFps > desiredFps * 0.9) {
|
// if (this.averageFps > desiredFps * 0.9) {
|
||||||
// // if (average < maxTickDuration) {
|
// // if (average < maxTickDuration) {
|
||||||
// this.increaseTickRate();
|
// this.increaseTickRate();
|
||||||
|
@ -172,6 +172,21 @@ export class GameMode extends BasicSerializableObject {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @returns {boolean} */
|
||||||
|
getIsDeterministic() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @returns {boolean} */
|
||||||
|
getIsEditor() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @returns {number | undefined} */
|
||||||
|
getFixedTickrate() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/** @returns {string} */
|
/** @returns {string} */
|
||||||
getBlueprintShapeKey() {
|
getBlueprintShapeKey() {
|
||||||
return "CbCbCbRb:CwCwCwCw";
|
return "CbCbCbRb:CwCwCwCw";
|
||||||
|
@ -184,10 +184,6 @@ export class HubGoals extends BasicSerializableObject {
|
|||||||
* @param {string} upgradeId
|
* @param {string} upgradeId
|
||||||
*/
|
*/
|
||||||
getUpgradeLevel(upgradeId) {
|
getUpgradeLevel(upgradeId) {
|
||||||
if (this.root.gameMode.throughputDoesNotMatter()) {
|
|
||||||
return 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.upgradeLevels[upgradeId] || 0;
|
return this.upgradeLevels[upgradeId] || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -481,7 +477,7 @@ export class HubGoals extends BasicSerializableObject {
|
|||||||
*/
|
*/
|
||||||
getBeltBaseSpeed() {
|
getBeltBaseSpeed() {
|
||||||
if (this.root.gameMode.throughputDoesNotMatter()) {
|
if (this.root.gameMode.throughputDoesNotMatter()) {
|
||||||
return globalConfig.beltSpeedItemsPerSecond * 5;
|
return globalConfig.beltSpeedItemsPerSecond * globalConfig.puzzleModeSpeed;
|
||||||
}
|
}
|
||||||
return globalConfig.beltSpeedItemsPerSecond * this.upgradeImprovements.belt;
|
return globalConfig.beltSpeedItemsPerSecond * this.upgradeImprovements.belt;
|
||||||
}
|
}
|
||||||
@ -492,7 +488,7 @@ export class HubGoals extends BasicSerializableObject {
|
|||||||
*/
|
*/
|
||||||
getUndergroundBeltBaseSpeed() {
|
getUndergroundBeltBaseSpeed() {
|
||||||
if (this.root.gameMode.throughputDoesNotMatter()) {
|
if (this.root.gameMode.throughputDoesNotMatter()) {
|
||||||
return globalConfig.beltSpeedItemsPerSecond * 5;
|
return globalConfig.beltSpeedItemsPerSecond * globalConfig.puzzleModeSpeed;
|
||||||
}
|
}
|
||||||
return globalConfig.beltSpeedItemsPerSecond * this.upgradeImprovements.belt;
|
return globalConfig.beltSpeedItemsPerSecond * this.upgradeImprovements.belt;
|
||||||
}
|
}
|
||||||
@ -503,7 +499,7 @@ export class HubGoals extends BasicSerializableObject {
|
|||||||
*/
|
*/
|
||||||
getMinerBaseSpeed() {
|
getMinerBaseSpeed() {
|
||||||
if (this.root.gameMode.throughputDoesNotMatter()) {
|
if (this.root.gameMode.throughputDoesNotMatter()) {
|
||||||
return globalConfig.minerSpeedItemsPerSecond * 5;
|
return globalConfig.minerSpeedItemsPerSecond * globalConfig.puzzleModeSpeed;
|
||||||
}
|
}
|
||||||
return globalConfig.minerSpeedItemsPerSecond * this.upgradeImprovements.miner;
|
return globalConfig.minerSpeedItemsPerSecond * this.upgradeImprovements.miner;
|
||||||
}
|
}
|
||||||
@ -515,7 +511,7 @@ export class HubGoals extends BasicSerializableObject {
|
|||||||
*/
|
*/
|
||||||
getProcessorBaseSpeed(processorType) {
|
getProcessorBaseSpeed(processorType) {
|
||||||
if (this.root.gameMode.throughputDoesNotMatter()) {
|
if (this.root.gameMode.throughputDoesNotMatter()) {
|
||||||
return 10;
|
return globalConfig.beltSpeedItemsPerSecond * globalConfig.puzzleModeSpeed * 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (processorType) {
|
switch (processorType) {
|
||||||
|
@ -25,7 +25,7 @@ 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 { HUDModeMenu } from "./parts/mode_menu";
|
||||||
import { HUDModeMenuBack } from "./parts/mode_menu_back";
|
import { HUDModeMenuBack } from "./parts/mode_menu_back";
|
||||||
import { HUDModeMenuNext } from "./parts/mode_menu_next";
|
import { HUDPuzzleReview } from "./parts/mode_puzzle_review";
|
||||||
import { HUDModeSettings } from "./parts/mode_settings";
|
import { HUDModeSettings } from "./parts/mode_settings";
|
||||||
import { enumNotificationType, HUDNotifications } from "./parts/notifications";
|
import { enumNotificationType, HUDNotifications } from "./parts/notifications";
|
||||||
import { HUDPinnedShapes } from "./parts/pinned_shapes";
|
import { HUDPinnedShapes } from "./parts/pinned_shapes";
|
||||||
@ -88,7 +88,7 @@ export class GameHUD {
|
|||||||
leverToggle: HUDLeverToggle,
|
leverToggle: HUDLeverToggle,
|
||||||
constantSignalEdit: HUDConstantSignalEdit,
|
constantSignalEdit: HUDConstantSignalEdit,
|
||||||
modeMenuBack: HUDModeMenuBack,
|
modeMenuBack: HUDModeMenuBack,
|
||||||
modeMenuNext: HUDModeMenuNext,
|
PuzzleReview: HUDPuzzleReview,
|
||||||
modeMenu: HUDModeMenu,
|
modeMenu: HUDModeMenu,
|
||||||
modeSettings: HUDModeSettings,
|
modeSettings: HUDModeSettings,
|
||||||
puzzleDlcLogo: HUDPuzzleDLCLogo,
|
puzzleDlcLogo: HUDPuzzleDLCLogo,
|
||||||
|
@ -29,11 +29,14 @@ export class HUDModalDialogs extends BaseHUDPart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
shouldPauseRendering() {
|
shouldPauseRendering() {
|
||||||
return this.dialogStack.length > 0;
|
// return this.dialogStack.length > 0;
|
||||||
|
// @todo: Check if change this affects anything
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldPauseGame() {
|
shouldPauseGame() {
|
||||||
return this.shouldPauseRendering();
|
// @todo: Check if this change affects anything
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
createElements(parent) {
|
createElements(parent) {
|
||||||
@ -139,8 +142,8 @@ export class HUDModalDialogs extends BaseHUDPart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns method to be called when laoding finishd
|
// Returns method to be called when laoding finishd
|
||||||
showLoadingDialog() {
|
showLoadingDialog(text = "") {
|
||||||
const dialog = new DialogLoading(this.app);
|
const dialog = new DialogLoading(this.app, text);
|
||||||
this.internalShowDialog(dialog);
|
this.internalShowDialog(dialog);
|
||||||
return this.closeDialog.bind(this, dialog);
|
return this.closeDialog.bind(this, dialog);
|
||||||
}
|
}
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
import { BaseHUDPart } from "../base_hud_part";
|
|
||||||
import { makeDiv } from "../../../core/utils";
|
|
||||||
import { T } from "../../../translations";
|
|
||||||
|
|
||||||
export class HUDModeMenuNext extends BaseHUDPart {
|
|
||||||
createElements(parent) {
|
|
||||||
const key = this.root.gameMode.getId();
|
|
||||||
|
|
||||||
this.element = makeDiv(parent, "ingame_HUD_ModeMenuNext");
|
|
||||||
this.button = document.createElement("button");
|
|
||||||
this.button.classList.add("button");
|
|
||||||
this.button.textContent = T.ingame.modeMenu[key].next.title;
|
|
||||||
this.element.appendChild(this.button);
|
|
||||||
|
|
||||||
this.content = makeDiv(this.element, null, ["content"], T.ingame.modeMenu[key].next.desc);
|
|
||||||
|
|
||||||
this.trackClicks(this.button, this.next);
|
|
||||||
}
|
|
||||||
|
|
||||||
initialize() {}
|
|
||||||
|
|
||||||
next() {}
|
|
||||||
}
|
|
167
src/js/game/hud/parts/mode_puzzle_review.js
Normal file
167
src/js/game/hud/parts/mode_puzzle_review.js
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
import { globalConfig, THIRDPARTY_URLS } from "../../../core/config";
|
||||||
|
import { createLogger } from "../../../core/logging";
|
||||||
|
import { DialogWithForm } from "../../../core/modal_dialog_elements";
|
||||||
|
import { FormElementInput, FormElementItemChooser } from "../../../core/modal_dialog_forms";
|
||||||
|
import { fillInLinkIntoTranslation, makeDiv } from "../../../core/utils";
|
||||||
|
import { PuzzleSerializer } from "../../../savegame/puzzle_serializer";
|
||||||
|
import { T } from "../../../translations";
|
||||||
|
import { ConstantSignalComponent } from "../../components/constant_signal";
|
||||||
|
import { GoalAcceptorComponent } from "../../components/goal_acceptor";
|
||||||
|
import { ShapeItem } from "../../items/shape_item";
|
||||||
|
import { ShapeDefinition } from "../../shape_definition";
|
||||||
|
import { BaseHUDPart } from "../base_hud_part";
|
||||||
|
|
||||||
|
const trim = require("trim");
|
||||||
|
const logger = createLogger("puzzle-review");
|
||||||
|
|
||||||
|
export class HUDPuzzleReview extends BaseHUDPart {
|
||||||
|
constructor(root) {
|
||||||
|
super(root);
|
||||||
|
|
||||||
|
this.validationEndsIn = null;
|
||||||
|
this.callOnceValidationEnded = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
createElements(parent) {
|
||||||
|
const key = this.root.gameMode.getId();
|
||||||
|
|
||||||
|
this.element = makeDiv(parent, "ingame_HUD_PuzzleReview");
|
||||||
|
this.button = document.createElement("button");
|
||||||
|
this.button.classList.add("button");
|
||||||
|
this.button.textContent = T.puzzleMenu.reviewPuzzle;
|
||||||
|
this.element.appendChild(this.button);
|
||||||
|
|
||||||
|
this.trackClicks(this.button, this.startReview);
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {}
|
||||||
|
|
||||||
|
startReview() {
|
||||||
|
const validationError = this.validatePuzzle();
|
||||||
|
if (validationError) {
|
||||||
|
this.root.hud.parts.dialogs.showWarning(T.puzzleMenu.validation.title, validationError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeLoading = this.root.hud.parts.dialogs.showLoadingDialog(T.puzzleMenu.validtingPuzzle);
|
||||||
|
this.validationEndsIn = this.root.time.now() + globalConfig.goalAcceptorMinimumDurationSeconds;
|
||||||
|
this.callOnceValidationEnded = () => {
|
||||||
|
closeLoading();
|
||||||
|
const validationError = this.validatePuzzle();
|
||||||
|
if (validationError) {
|
||||||
|
this.root.hud.parts.dialogs.showWarning(T.puzzleMenu.validation.title, validationError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.startSubmit();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
startSubmit() {
|
||||||
|
const regex = /^[a-zA-Z0-9_\- ]{1,20}$/;
|
||||||
|
const nameInput = new FormElementInput({
|
||||||
|
id: "nameInput",
|
||||||
|
label: T.dialogs.submitPuzzle.descName,
|
||||||
|
placeholder: T.dialogs.submitPuzzle.placeholderName,
|
||||||
|
defaultValue: "",
|
||||||
|
validator: val => val.match(regex) && trim(val).length > 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
let items = new Set();
|
||||||
|
const acceptors = this.root.entityMgr.getAllWithComponent(GoalAcceptorComponent);
|
||||||
|
for (const acceptor of acceptors) {
|
||||||
|
const item = acceptor.components.GoalAcceptor.item;
|
||||||
|
if (item.getItemType() === "shape") {
|
||||||
|
items.add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (items.size < 8) {
|
||||||
|
// add some randoms
|
||||||
|
const item = this.root.hubGoals.computeFreeplayShape(Math.round(10 + Math.random() * 10000));
|
||||||
|
items.add(new ShapeItem(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemInput = new FormElementItemChooser({
|
||||||
|
id: "signalItem",
|
||||||
|
label: fillInLinkIntoTranslation(T.dialogs.submitPuzzle.descIcon, THIRDPARTY_URLS.shapeViewer),
|
||||||
|
items: Array.from(items),
|
||||||
|
});
|
||||||
|
|
||||||
|
const shapeKeyInput = new FormElementInput({
|
||||||
|
id: "shapeKeyInput",
|
||||||
|
label: null,
|
||||||
|
placeholder: "CuCuCuCu",
|
||||||
|
defaultValue: "",
|
||||||
|
validator: val => ShapeDefinition.isValidShortKey(trim(val)),
|
||||||
|
});
|
||||||
|
|
||||||
|
const dialog = new DialogWithForm({
|
||||||
|
app: this.root.app,
|
||||||
|
title: T.dialogs.submitPuzzle.title,
|
||||||
|
desc: "",
|
||||||
|
formElements: [nameInput, itemInput, shapeKeyInput],
|
||||||
|
buttons: ["cancel:bad:escape", "ok:good:enter"],
|
||||||
|
});
|
||||||
|
|
||||||
|
itemInput.valueChosen.add(value => {
|
||||||
|
shapeKeyInput.setValue(value.definition.getHash());
|
||||||
|
});
|
||||||
|
|
||||||
|
this.root.hud.parts.dialogs.internalShowDialog(dialog);
|
||||||
|
|
||||||
|
dialog.buttonSignals.ok.add(() => {
|
||||||
|
const title = trim(nameInput.getValue());
|
||||||
|
const shortKey = trim(shapeKeyInput.getValue());
|
||||||
|
this.doSubmitPuzzle(title, shortKey);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
doSubmitPuzzle(title, shortKey) {
|
||||||
|
const serialized = new PuzzleSerializer().generateDumpFromGameRoot(this.root);
|
||||||
|
|
||||||
|
logger.log("Submitting puzzle, title=", title, "shortKey=", shortKey);
|
||||||
|
logger.log("Serialized data:", serialized);
|
||||||
|
|
||||||
|
const closeLoading = this.root.hud.parts.dialogs.showLoadingDialog(T.puzzleMenu.submittingPuzzle);
|
||||||
|
|
||||||
|
// @todo
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
if (
|
||||||
|
this.validationEndsIn &&
|
||||||
|
this.validationEndsIn < this.root.time.now() &&
|
||||||
|
this.callOnceValidationEnded
|
||||||
|
) {
|
||||||
|
const callMethod = this.callOnceValidationEnded;
|
||||||
|
this.callOnceValidationEnded = null;
|
||||||
|
callMethod();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validatePuzzle() {
|
||||||
|
// Check there is at least one constant producer and goal acceptor
|
||||||
|
const producers = this.root.entityMgr.getAllWithComponent(ConstantSignalComponent);
|
||||||
|
const acceptors = this.root.entityMgr.getAllWithComponent(GoalAcceptorComponent);
|
||||||
|
|
||||||
|
if (producers.length === 0) {
|
||||||
|
return T.puzzleMenu.validation.noProducers;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (acceptors.length === 0) {
|
||||||
|
return T.puzzleMenu.validation.noGoalAcceptors;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if all acceptors satisfy the constraints
|
||||||
|
for (const acceptor of acceptors) {
|
||||||
|
const goalComp = acceptor.components.GoalAcceptor;
|
||||||
|
if (!goalComp.item) {
|
||||||
|
return T.puzzleMenu.validation.goalAcceptorNoItem;
|
||||||
|
}
|
||||||
|
const required = goalComp.getRequiredDeliveryHistorySize();
|
||||||
|
if (goalComp.deliveryHistory.length < required) {
|
||||||
|
return T.puzzleMenu.validation.goalAcceptorRateNotMet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,9 +8,8 @@ export class HUDPuzzleEditorControls extends BaseHUDPart {
|
|||||||
this.element.innerHTML = `
|
this.element.innerHTML = `
|
||||||
|
|
||||||
<span>1. Build constant producers to generate resources.</span>
|
<span>1. Build constant producers to generate resources.</span>
|
||||||
<span>2. Build goal acceptors the capture shapes.</span>
|
<span>2. Build goal acceptors and deliver shapes to set the puzzle goals.</span>
|
||||||
<span>3. Produce your desired shape(s) within the puzzle area and deliver it to the goal acceptors, which will capture it.</span>
|
<span>3. Once you are done, press 'Playtest' to validate your puzzle.</span>
|
||||||
<span>4. 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");
|
||||||
|
@ -77,6 +77,7 @@ export class MapChunkView extends MapChunk {
|
|||||||
systems.display.drawChunk(parameters, this);
|
systems.display.drawChunk(parameters, this);
|
||||||
systems.storage.drawChunk(parameters, this);
|
systems.storage.drawChunk(parameters, this);
|
||||||
systems.constantProducer.drawChunk(parameters, this);
|
systems.constantProducer.drawChunk(parameters, this);
|
||||||
|
systems.goalAcceptor.drawChunk(parameters, this);
|
||||||
systems.itemProcessorOverlays.drawChunk(parameters, this);
|
systems.itemProcessorOverlays.drawChunk(parameters, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,6 +82,10 @@ export class PuzzleGameMode extends GameMode {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getMaximumZoom() {
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
getIsSaveable() {
|
getIsSaveable() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -98,6 +102,14 @@ export class PuzzleGameMode extends GameMode {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getIsDeterministic() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
getFixedTickrate() {
|
||||||
|
return 300;
|
||||||
|
}
|
||||||
|
|
||||||
/** @returns {boolean} */
|
/** @returns {boolean} */
|
||||||
getIsFreeplayAvailable() {
|
getIsFreeplayAvailable() {
|
||||||
return true;
|
return true;
|
||||||
|
@ -66,4 +66,8 @@ export class PuzzleEditGameMode extends PuzzleGameMode {
|
|||||||
|
|
||||||
this.zone = this.createCenteredRectangle(this.zoneWidth, this.zoneHeight);
|
this.zone = this.createCenteredRectangle(this.zoneWidth, this.zoneHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getIsEditor() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,11 @@
|
|||||||
import { GameRoot } from "../root";
|
import { GameRoot } from "../root";
|
||||||
/* typehints:end */
|
/* typehints:end */
|
||||||
|
|
||||||
import { queryParamOptions } from "../../core/query_parameters";
|
|
||||||
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 { MetaItemProducerBuilding } from "../buildings/item_producer";
|
|
||||||
import { HUDModeMenuBack } from "../hud/parts/mode_menu_back";
|
import { HUDModeMenuBack } from "../hud/parts/mode_menu_back";
|
||||||
import { HUDModeMenuNext } from "../hud/parts/mode_menu_next";
|
import { HUDPuzzleReview } from "../hud/parts/mode_puzzle_review";
|
||||||
import { HUDModeMenu } from "../hud/parts/mode_menu";
|
import { HUDModeMenu } from "../hud/parts/mode_menu";
|
||||||
import { HUDModeSettings } from "../hud/parts/mode_settings";
|
import { HUDModeSettings } from "../hud/parts/mode_settings";
|
||||||
import { enumGameModeIds, enumGameModeTypes, GameMode } from "../game_mode";
|
import { enumGameModeIds, enumGameModeTypes, GameMode } from "../game_mode";
|
||||||
@ -520,7 +518,7 @@ export class RegularGameMode extends GameMode {
|
|||||||
|
|
||||||
this.hiddenHurtParts = {
|
this.hiddenHurtParts = {
|
||||||
[HUDModeMenuBack.name]: false,
|
[HUDModeMenuBack.name]: false,
|
||||||
[HUDModeMenuNext.name]: false,
|
[HUDPuzzleReview.name]: false,
|
||||||
[HUDModeMenu.name]: false,
|
[HUDModeMenu.name]: false,
|
||||||
[HUDModeSettings.name]: false,
|
[HUDModeSettings.name]: false,
|
||||||
[HUDPuzzleDLCLogo.name]: false,
|
[HUDPuzzleDLCLogo.name]: false,
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
/* typehints:start */
|
|
||||||
/* typehints:end */
|
|
||||||
import { globalConfig } from "../../core/config";
|
import { globalConfig } from "../../core/config";
|
||||||
import { DrawParameters } from "../../core/draw_parameters";
|
import { DrawParameters } from "../../core/draw_parameters";
|
||||||
|
import { Vector } from "../../core/vector";
|
||||||
import { ConstantSignalComponent } from "../components/constant_signal";
|
import { ConstantSignalComponent } from "../components/constant_signal";
|
||||||
import { ItemProducerComponent } from "../components/item_producer";
|
import { ItemProducerComponent } from "../components/item_producer";
|
||||||
import { GameSystemWithFilter } from "../game_system_with_filter";
|
import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||||
@ -43,19 +42,25 @@ export class ConstantProducerSystem extends GameSystemWithFilter {
|
|||||||
const signalComp = contents[i].components.ConstantSignal;
|
const signalComp = contents[i].components.ConstantSignal;
|
||||||
|
|
||||||
if (!producerComp || !producerComp.isWireless() || !signalComp || !signalComp.isWireless()) {
|
if (!producerComp || !producerComp.isWireless() || !signalComp || !signalComp.isWireless()) {
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const staticComp = contents[i].components.StaticMapEntity;
|
const staticComp = contents[i].components.StaticMapEntity;
|
||||||
const item = signalComp.signal;
|
const item = signalComp.signal;
|
||||||
|
|
||||||
if (!item) {
|
if (!item) {
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Better looking overlay
|
|
||||||
const center = staticComp.getTileSpaceBounds().getCenter().toWorldSpace();
|
const center = staticComp.getTileSpaceBounds().getCenter().toWorldSpace();
|
||||||
item.drawItemCenteredClipped(center.x, center.y + 1, parameters, globalConfig.tileSize * 0.65);
|
|
||||||
|
const localOffset = new Vector(0, 1).rotateFastMultipleOf90(staticComp.rotation);
|
||||||
|
item.drawItemCenteredClipped(
|
||||||
|
center.x + localOffset.x,
|
||||||
|
center.y + localOffset.y,
|
||||||
|
parameters,
|
||||||
|
globalConfig.tileSize * 0.65
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ export class ConstantSignalSystem extends GameSystemWithFilter {
|
|||||||
label: fillInLinkIntoTranslation(T.dialogs.editSignal.descShortKey, THIRDPARTY_URLS.shapeViewer),
|
label: fillInLinkIntoTranslation(T.dialogs.editSignal.descShortKey, THIRDPARTY_URLS.shapeViewer),
|
||||||
placeholder: "",
|
placeholder: "",
|
||||||
defaultValue: "",
|
defaultValue: "",
|
||||||
validator: val => this.parseSignalCode(val),
|
validator: val => this.parseSignalCode(entity.components.ConstantSignal.type, val),
|
||||||
});
|
});
|
||||||
|
|
||||||
const items = [
|
const items = [
|
||||||
@ -93,7 +93,7 @@ export class ConstantSignalSystem extends GameSystemWithFilter {
|
|||||||
|
|
||||||
const dialog = new DialogWithForm({
|
const dialog = new DialogWithForm({
|
||||||
app: this.root.app,
|
app: this.root.app,
|
||||||
title: T.dialogs.editSignal.title,
|
title: T.dialogs.editConstantProducer.title,
|
||||||
desc: T.dialogs.editSignal.descItems,
|
desc: T.dialogs.editSignal.descItems,
|
||||||
formElements: [itemInput, signalValueInput],
|
formElements: [itemInput, signalValueInput],
|
||||||
buttons: ["cancel:bad:escape", "ok:good:enter"],
|
buttons: ["cancel:bad:escape", "ok:good:enter"],
|
||||||
@ -123,12 +123,20 @@ export class ConstantSignalSystem extends GameSystemWithFilter {
|
|||||||
if (itemInput.chosenItem) {
|
if (itemInput.chosenItem) {
|
||||||
constantComp.signal = itemInput.chosenItem;
|
constantComp.signal = itemInput.chosenItem;
|
||||||
} else {
|
} else {
|
||||||
constantComp.signal = this.parseSignalCode(signalValueInput.getValue());
|
constantComp.signal = this.parseSignalCode(
|
||||||
|
entity.components.ConstantSignal.type,
|
||||||
|
signalValueInput.getValue()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
dialog.buttonSignals.ok.add(closeHandler);
|
dialog.buttonSignals.ok.add(() => {
|
||||||
dialog.valueChosen.add(closeHandler);
|
closeHandler();
|
||||||
|
});
|
||||||
|
dialog.valueChosen.add(() => {
|
||||||
|
dialog.closeRequested.dispatch();
|
||||||
|
closeHandler();
|
||||||
|
});
|
||||||
|
|
||||||
// When cancelled, destroy the entity again
|
// When cancelled, destroy the entity again
|
||||||
if (deleteOnCancel) {
|
if (deleteOnCancel) {
|
||||||
@ -157,10 +165,11 @@ export class ConstantSignalSystem extends GameSystemWithFilter {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Tries to parse a signal code
|
* Tries to parse a signal code
|
||||||
|
* @param {string} type
|
||||||
* @param {string} code
|
* @param {string} code
|
||||||
* @returns {BaseItem}
|
* @returns {BaseItem}
|
||||||
*/
|
*/
|
||||||
parseSignalCode(code) {
|
parseSignalCode(type, code) {
|
||||||
if (!this.root || !this.root.shapeDefinitionMgr) {
|
if (!this.root || !this.root.shapeDefinitionMgr) {
|
||||||
// Stale reference
|
// Stale reference
|
||||||
return null;
|
return null;
|
||||||
@ -172,12 +181,15 @@ export class ConstantSignalSystem extends GameSystemWithFilter {
|
|||||||
if (enumColors[codeLower]) {
|
if (enumColors[codeLower]) {
|
||||||
return COLOR_ITEM_SINGLETONS[codeLower];
|
return COLOR_ITEM_SINGLETONS[codeLower];
|
||||||
}
|
}
|
||||||
if (code === "1" || codeLower === "true") {
|
|
||||||
return BOOL_TRUE_SINGLETON;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (code === "0" || codeLower === "false") {
|
if (type === enumConstantSignalType.wired) {
|
||||||
return BOOL_FALSE_SINGLETON;
|
if (code === "1" || codeLower === "true") {
|
||||||
|
return BOOL_TRUE_SINGLETON;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code === "0" || codeLower === "false") {
|
||||||
|
return BOOL_FALSE_SINGLETON;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ShapeDefinition.isValidShortKey(code)) {
|
if (ShapeDefinition.isValidShortKey(code)) {
|
||||||
|
@ -1,111 +1,114 @@
|
|||||||
/* typehints:start */
|
import { globalConfig } from "../../core/config";
|
||||||
import { GameRoot } from "../root";
|
import { DrawParameters } from "../../core/draw_parameters";
|
||||||
/* typehints:end */
|
import { clamp, lerp } from "../../core/utils";
|
||||||
|
import { Vector } from "../../core/vector";
|
||||||
import { THIRDPARTY_URLS, globalConfig } from "../../core/config";
|
|
||||||
import { DialogWithForm } from "../../core/modal_dialog_elements";
|
|
||||||
import { FormElementInput, FormElementItemChooser } from "../../core/modal_dialog_forms";
|
|
||||||
import { fillInLinkIntoTranslation } from "../../core/utils";
|
|
||||||
import { T } from "../../translations";
|
|
||||||
import { GoalAcceptorComponent } from "../components/goal_acceptor";
|
import { GoalAcceptorComponent } from "../components/goal_acceptor";
|
||||||
import { GameSystemWithFilter } from "../game_system_with_filter";
|
import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||||
// import { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON } from "../items/boolean_item";
|
import { MapChunk } from "../map_chunk";
|
||||||
// import { COLOR_ITEM_SINGLETONS } from "../items/color_item";
|
import { GameRoot } from "../root";
|
||||||
|
|
||||||
export class GoalAcceptorSystem extends GameSystemWithFilter {
|
export class GoalAcceptorSystem extends GameSystemWithFilter {
|
||||||
/** @param {GameRoot} root */
|
/** @param {GameRoot} root */
|
||||||
constructor(root) {
|
constructor(root) {
|
||||||
super(root, [GoalAcceptorComponent]);
|
super(root, [GoalAcceptorComponent]);
|
||||||
|
|
||||||
this.root.signals.entityManuallyPlaced.add(this.editGoal, this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
|
const now = this.root.time.now();
|
||||||
|
|
||||||
for (let i = 0; i < this.allEntities.length; ++i) {
|
for (let i = 0; i < this.allEntities.length; ++i) {
|
||||||
const entity = this.allEntities[i];
|
const entity = this.allEntities[i];
|
||||||
const goalComp = entity.components.GoalAcceptor;
|
const goalComp = entity.components.GoalAcceptor;
|
||||||
const readerComp = entity.components.BeltReader;
|
|
||||||
|
|
||||||
// Check against goals (set on placement)
|
// filter the ones which are no longer active, or which are not the same
|
||||||
|
goalComp.deliveryHistory = goalComp.deliveryHistory.filter(
|
||||||
|
d =>
|
||||||
|
now - d.time < globalConfig.goalAcceptorMinimumDurationSeconds && d.item === goalComp.item
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if goal criteria has been met for all goals
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {DrawParameters} parameters
|
||||||
|
* @param {MapChunk} chunk
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
drawChunk(parameters, chunk) {
|
drawChunk(parameters, chunk) {
|
||||||
/*
|
const contents = chunk.containedEntitiesByLayer.regular;
|
||||||
*const contents = chunk.containedEntitiesByLayer.regular;
|
for (let i = 0; i < contents.length; ++i) {
|
||||||
*for (let i = 0; i < contents.length; ++i) {}
|
const goalComp = contents[i].components.GoalAcceptor;
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
editGoal(entity) {
|
if (!goalComp) {
|
||||||
if (!entity.components.GoalAcceptor) {
|
continue;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const uid = entity.uid;
|
|
||||||
const goalComp = entity.components.GoalAcceptor;
|
|
||||||
|
|
||||||
const itemInput = new FormElementInput({
|
|
||||||
id: "goalItemInput",
|
|
||||||
label: fillInLinkIntoTranslation(T.dialogs.editGoalAcceptor.desc, THIRDPARTY_URLS.shapeViewer),
|
|
||||||
placeholder: "CuCuCuCu",
|
|
||||||
defaultValue: "CuCuCuCu",
|
|
||||||
validator: val => this.parseItem(val),
|
|
||||||
});
|
|
||||||
|
|
||||||
const dialog = new DialogWithForm({
|
|
||||||
app: this.root.app,
|
|
||||||
title: T.dialogs.editGoalAcceptor.title,
|
|
||||||
desc: "",
|
|
||||||
formElements: [itemInput],
|
|
||||||
buttons: ["cancel:bad:escape", "ok:good:enter"],
|
|
||||||
closeButton: false,
|
|
||||||
});
|
|
||||||
this.root.hud.parts.dialogs.internalShowDialog(dialog);
|
|
||||||
|
|
||||||
const closeHandler = () => {
|
|
||||||
if (this.isEntityStale(uid)) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
goalComp.item = this.parseItem(itemInput.getValue());
|
const staticComp = contents[i].components.StaticMapEntity;
|
||||||
};
|
const item = goalComp.item;
|
||||||
|
|
||||||
dialog.buttonSignals.ok.add(closeHandler);
|
const requiredItemsForSuccess = goalComp.getRequiredDeliveryHistorySize();
|
||||||
dialog.buttonSignals.cancel.add(() => {
|
const percentage = clamp(goalComp.deliveryHistory.length / requiredItemsForSuccess, 0, 1);
|
||||||
if (this.isEntityStale(uid)) {
|
|
||||||
return;
|
const center = staticComp.getTileSpaceBounds().getCenter().toWorldSpace();
|
||||||
|
if (item) {
|
||||||
|
const localOffset = new Vector(0, -1.8).rotateFastMultipleOf90(staticComp.rotation);
|
||||||
|
item.drawItemCenteredClipped(
|
||||||
|
center.x + localOffset.x,
|
||||||
|
center.y + localOffset.y,
|
||||||
|
parameters,
|
||||||
|
globalConfig.tileSize * 0.65
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.root.logic.tryDeleteBuilding(entity);
|
const isValid = item && goalComp.deliveryHistory.length >= requiredItemsForSuccess;
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
parseRate(value) {
|
parameters.context.translate(center.x, center.y);
|
||||||
return Number(value);
|
parameters.context.rotate((staticComp.rotation / 180) * Math.PI);
|
||||||
}
|
|
||||||
|
|
||||||
parseItem(value) {
|
parameters.context.lineWidth = 1;
|
||||||
return this.root.systemMgr.systems.constantSignal.parseSignalCode(value);
|
parameters.context.fillStyle = "#8de255";
|
||||||
}
|
parameters.context.strokeStyle = "#64666e";
|
||||||
|
parameters.context.lineCap = "round";
|
||||||
|
|
||||||
isEntityStale(uid) {
|
// progress arc
|
||||||
if (!this.root || !this.root.entityMgr) {
|
|
||||||
return true;
|
goalComp.displayPercentage = lerp(goalComp.displayPercentage, percentage, 0.3);
|
||||||
|
|
||||||
|
const startAngle = Math.PI * 0.595;
|
||||||
|
const maxAngle = Math.PI * 1.82;
|
||||||
|
parameters.context.beginPath();
|
||||||
|
parameters.context.arc(
|
||||||
|
0.25,
|
||||||
|
-1.5,
|
||||||
|
11.6,
|
||||||
|
startAngle,
|
||||||
|
startAngle + goalComp.displayPercentage * maxAngle,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
parameters.context.arc(
|
||||||
|
0.25,
|
||||||
|
-1.5,
|
||||||
|
15.5,
|
||||||
|
startAngle + goalComp.displayPercentage * maxAngle,
|
||||||
|
startAngle,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
parameters.context.closePath();
|
||||||
|
parameters.context.fill();
|
||||||
|
parameters.context.stroke();
|
||||||
|
parameters.context.lineCap = "butt";
|
||||||
|
|
||||||
|
// LED indicator
|
||||||
|
|
||||||
|
parameters.context.lineWidth = 1;
|
||||||
|
parameters.context.strokeStyle = "#64666e";
|
||||||
|
parameters.context.fillStyle = isValid ? "#8de255" : "#e2555f";
|
||||||
|
parameters.context.beginCircle(10, 11.8, 3);
|
||||||
|
parameters.context.fill();
|
||||||
|
parameters.context.stroke();
|
||||||
|
|
||||||
|
parameters.context.rotate((-staticComp.rotation / 180) * Math.PI);
|
||||||
|
parameters.context.translate(-center.x, -center.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
const entity = this.root.entityMgr.findByUid(uid, false);
|
|
||||||
if (!entity) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const goalComp = entity.components.GoalAcceptor;
|
|
||||||
if (!goalComp) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -568,8 +568,18 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
|
|||||||
* @param {ProcessorImplementationPayload} payload
|
* @param {ProcessorImplementationPayload} payload
|
||||||
*/
|
*/
|
||||||
process_GOAL(payload) {
|
process_GOAL(payload) {
|
||||||
const readerComp = payload.entity.components.BeltReader;
|
const goalComp = payload.entity.components.GoalAcceptor;
|
||||||
readerComp.lastItemTimes.push(this.root.time.now());
|
if (this.root.gameMode.getIsEditor()) {
|
||||||
readerComp.lastItem = payload.items[payload.items.length - 1].item;
|
// while playing in editor, assign the item
|
||||||
|
goalComp.item = payload.items[0].item;
|
||||||
|
}
|
||||||
|
|
||||||
|
const now = this.root.time.now();
|
||||||
|
|
||||||
|
// push our new entry
|
||||||
|
goalComp.deliveryHistory.push({
|
||||||
|
item: payload.items[0].item,
|
||||||
|
time: now,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
18
src/js/savegame/puzzle_serializer.js
Normal file
18
src/js/savegame/puzzle_serializer.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
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}
|
||||||
|
*/
|
||||||
|
generateDumpFromGameRoot(root, sanityChecks = true) {
|
||||||
|
console.log("serializing", root);
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: "puzzle",
|
||||||
|
contents: "foo",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -124,6 +124,9 @@ puzzleMenu:
|
|||||||
edit: Edit
|
edit: Edit
|
||||||
title: Puzzle Mode
|
title: Puzzle Mode
|
||||||
createPuzzle: Create Puzzle
|
createPuzzle: Create Puzzle
|
||||||
|
reviewPuzzle: Review & Publish
|
||||||
|
validtingPuzzle: Validating Puzzle
|
||||||
|
submittingPuzzle: Submitting Puzzle
|
||||||
|
|
||||||
categories:
|
categories:
|
||||||
levels: Levels
|
levels: Levels
|
||||||
@ -131,6 +134,14 @@ puzzleMenu:
|
|||||||
topRated: Top Rated
|
topRated: Top Rated
|
||||||
myPuzzles: My Puzzles
|
myPuzzles: My Puzzles
|
||||||
|
|
||||||
|
validation:
|
||||||
|
title: Invalid Puzzle
|
||||||
|
noProducers: Please place a Constant Producer!
|
||||||
|
noGoalAcceptors: Please place a Goal Acceptor!
|
||||||
|
goalAcceptorNoItem: >-
|
||||||
|
One or more Goal Acceptors have not yet assigned an item. Deliver a shape to them to set a goal.
|
||||||
|
goalAcceptorRateNotMet: >-
|
||||||
|
One or more Goal Acceptors are not getting enough items. Make sure that the indicators are green for all acceptors.
|
||||||
dialogs:
|
dialogs:
|
||||||
buttons:
|
buttons:
|
||||||
ok: OK
|
ok: OK
|
||||||
@ -248,9 +259,8 @@ dialogs:
|
|||||||
Choose a pre-defined item:
|
Choose a pre-defined item:
|
||||||
descShortKey: ... or enter the <strong>short key</strong> of a shape (Which you can generate <link>here</link>)
|
descShortKey: ... or enter the <strong>short key</strong> of a shape (Which you can generate <link>here</link>)
|
||||||
|
|
||||||
editGoalAcceptor:
|
editConstantProducer:
|
||||||
title: Set Goal
|
title: Set Item
|
||||||
desc: Enter the <strong>short key</strong> of a shape (Which you can generate <link>here</link>). The goal will count as completed once 1 item /s is delivered.
|
|
||||||
|
|
||||||
markerDemoLimit:
|
markerDemoLimit:
|
||||||
desc: You can only create two custom markers in the demo. Get the standalone for unlimited markers!
|
desc: You can only create two custom markers in the demo. Get the standalone for unlimited markers!
|
||||||
@ -276,6 +286,15 @@ dialogs:
|
|||||||
desc: >-
|
desc: >-
|
||||||
Unfortunately the puzzles could not be loaded:
|
Unfortunately the puzzles could not be loaded:
|
||||||
|
|
||||||
|
submitPuzzle:
|
||||||
|
title: Submit Puzzle
|
||||||
|
descName: >-
|
||||||
|
Give your puzzle a name:
|
||||||
|
descIcon: >-
|
||||||
|
Please enter a unique short key, which will be shown as the icon of your puzzle (You can generate them <link>here</link>, or choose one of the randomly suggested shapes below):
|
||||||
|
|
||||||
|
placeholderName: Puzzle Title
|
||||||
|
|
||||||
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
|
||||||
@ -500,24 +519,6 @@ 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!
|
||||||
|
|
||||||
modeMenu:
|
|
||||||
puzzleEditMode:
|
|
||||||
back:
|
|
||||||
title: Menu
|
|
||||||
next:
|
|
||||||
title: Playtest
|
|
||||||
desc: Required for publishing
|
|
||||||
puzzleEditTestMode:
|
|
||||||
back:
|
|
||||||
title: Edit
|
|
||||||
next:
|
|
||||||
title: Publish
|
|
||||||
puzzlePlayMode:
|
|
||||||
back:
|
|
||||||
title: Menu
|
|
||||||
next:
|
|
||||||
title: Next
|
|
||||||
|
|
||||||
# All shop upgrades
|
# All shop upgrades
|
||||||
shopUpgrades:
|
shopUpgrades:
|
||||||
belt:
|
belt:
|
||||||
|
Loading…
Reference in New Issue
Block a user