Fix keys being stuck, show savegame levels in main menu

This commit is contained in:
tobspr 2020-05-28 14:53:11 +02:00
parent 2a4ee8e784
commit e0facaf788
28 changed files with 175 additions and 138 deletions

View File

@ -292,7 +292,7 @@ function gulptasksHTML($, gulp, buildFolder, browserSync) {
});
gulp.task("html.prod", () => {
return buildHtml("https://api.shapez.io", {
return buildHtml("https://analytics.shapez.io", {
analytics: true,
});
});
@ -315,7 +315,7 @@ function gulptasksHTML($, gulp, buildFolder, browserSync) {
});
gulp.task("html.standalone-prod", () => {
return buildHtml("https://api.shapez.io", {
return buildHtml("https://analytics.shapez.io", {
analytics: false,
standalone: true,
enableCachebust: false,

View File

@ -46,7 +46,6 @@ function gulptasksJS($, gulp, buildFolder, browserSync) {
requireUncached("./webpack.production.config.js")({
enableAssert: true,
environment: "staging",
apiEndpoint: "https://api-staging.shapez.io/v1",
es6: false,
})
)
@ -63,7 +62,6 @@ function gulptasksJS($, gulp, buildFolder, browserSync) {
requireUncached("./webpack.production.config.js")({
enableAssert: true,
environment: "staging",
apiEndpoint: "https://api-staging.shapez.io/v1",
es6: true,
})
)
@ -81,7 +79,6 @@ function gulptasksJS($, gulp, buildFolder, browserSync) {
requireUncached("./webpack.production.config.js")({
enableAssert: false,
environment: "prod",
apiEndpoint: "https://api.shapez.io/v1",
es6: false,
})
)
@ -100,7 +97,6 @@ function gulptasksJS($, gulp, buildFolder, browserSync) {
enableAssert: false,
environment: "prod",
es6: true,
apiEndpoint: "https://api.shapez.io/v1",
})
)
)
@ -148,7 +144,6 @@ function gulptasksJS($, gulp, buildFolder, browserSync) {
requireUncached("./webpack.production.config.js")({
enableAssert: true,
environment: "staging",
apiEndpoint: "https://api-staging.shapez.io/v1",
es6: true,
standalone: true,
})
@ -165,7 +160,6 @@ function gulptasksJS($, gulp, buildFolder, browserSync) {
requireUncached("./webpack.production.config.js")({
enableAssert: false,
environment: "prod",
apiEndpoint: "https://api.shapez.io/v1",
es6: true,
standalone: true,
})

View File

@ -32,9 +32,6 @@ module.exports = ({ watch = false, standalone = false }) => {
"window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));",
G_HAVE_ASSERT: "true",
G_APP_ENVIRONMENT: JSON.stringify("dev"),
G_API_ENDPOINT: JSON.stringify(
lzString.compressToEncodedURIComponent("http://localhost:5005/v1")
),
G_TRACKING_ENDPOINT: JSON.stringify(
lzString.compressToEncodedURIComponent("http://localhost:10005/v1")
),

View File

@ -13,7 +13,6 @@ const UnusedFilesPlugin = require("unused-files-webpack-plugin").UnusedFilesWebp
module.exports = ({
enableAssert = false,
apiEndpoint,
environment,
es6 = false,
standalone = false,
@ -30,7 +29,6 @@ module.exports = ({
G_IS_STANDALONE: standalone ? "true" : "false",
G_IS_BROWSER: isBrowser ? "true" : "false",
G_IS_MOBILE_APP: mobileApp ? "true" : "false",
G_API_ENDPOINT: JSON.stringify(lzString.compressToEncodedURIComponent(apiEndpoint)),
G_TRACKING_ENDPOINT: JSON.stringify(
lzString.compressToEncodedURIComponent("https://tracking.shapez.io/v1")
),

View File

@ -11,7 +11,7 @@
border-bottom-width: 0;
transition: transform 0.12s ease-in-out;
background: rgba(mix(#ddd, $colorBlueBright, 80%), 0.89);
background: rgba(mix(#ddd, $colorBlueBright, 80%), 0.69);
&:not(.visible) {
transform: translateX(-50%) translateY(#{D(100px)});

View File

@ -12,7 +12,7 @@
.keybinding {
vertical-align: middle;
@include S(margin, 0, 4px);
@include S(margin, 0, 1px);
position: relative;
top: unset;
left: unset;

View File

@ -235,7 +235,7 @@
opacity: 0.5;
}
.updateTime {
.level {
grid-column: 1 / 2;
grid-row: 1 / 2;
@include PlainText;

View File

@ -3,12 +3,15 @@ export const CHANGELOG = [
version: "1.1.0",
date: "unreleased",
entries: [
"BLUEPRINTS!",
"BLUEPRINTS! They are unlocked at level 11.",
"Savegame levels are now shown in the main menu. For existing games, save them again to make the level show up.",
"Allow holding SHIFT to rotate counter clockwise",
"Allow changing all keybindings, including CTRL, ALT and SHIFT (by Dimava)",
"Added confirmation when deleting more than 500 buildings at a time",
"Added background to toolbar to increase contrast",
"Further decrease requirements of first levels",
"Pinned shapes now are saved",
"Fix keys being stuck when opening a dialog",
"Allow placing extractors anywhere again, but they don't work at all if not placed on a resource",
"Fix cycling through keybindings selecting locked buildings as well (by Dimava)",
"There is now a github action, checking all pull requests with eslint. (by mrHedgehog)",

View File

@ -83,7 +83,7 @@ export const globalConfig = {
debug: {
/* dev:start */
fastGameEnter: true,
// fastGameEnter: true,
// noArtificialDelays: true,
// disableSavegameWrite: true,
// showEntityBounds: true,
@ -100,7 +100,7 @@ export const globalConfig = {
// testClipping: true,
// framePausesBetweenTicks: 40,
// testTranslations: true,
enableEntityInspector: true,
// enableEntityInspector: true,
// testAds: true,
// disableMapOverview: true,
disableTutorialHints: true,

View File

@ -23,6 +23,11 @@ export class InputDistributor {
/** @type {Array<function(any) : boolean>} */
this.filters = [];
/**
* All keys which are currently down
*/
this.keysDown = new Set();
this.bindToEvents();
}
@ -173,6 +178,7 @@ export class InputDistributor {
*/
handleBlur() {
this.forwardToReceiver("pageBlur", {});
this.keysDown.clear();
}
/**
@ -180,19 +186,24 @@ export class InputDistributor {
*/
handleKeydown(event) {
if (
// TAB
event.keyCode === 9 ||
// F1 - F10
(event.keyCode >= 112 && event.keyCode < 122)
event.keyCode === 9 || // TAB
event.keyCode === 16 || // SHIFT
event.keyCode === 17 || // CTRL
event.keyCode === 18 || // ALT
(event.keyCode >= 112 && event.keyCode < 122) // F1 - F10
) {
event.preventDefault();
}
const isInitial = !this.keysDown.has(event.keyCode);
this.keysDown.add(event.keyCode);
if (
this.forwardToReceiver("keydown", {
keyCode: event.keyCode,
shift: event.shiftKey,
alt: event.altKey,
initial: isInitial,
event,
}) === STOP_PROPAGATION
) {
@ -212,6 +223,8 @@ export class InputDistributor {
* @param {KeyboardEvent} event
*/
handleKeyup(event) {
this.keysDown.delete(event.keyCode);
this.forwardToReceiver("keyup", {
keyCode: event.keyCode,
shift: event.shiftKey,

View File

@ -873,19 +873,19 @@ export class Camera extends BasicSerializableObject {
let forceY = 0;
const actionMapper = this.root.keyMapper;
if (actionMapper.getBinding(KEYMAPPINGS.ingame.mapMoveUp).currentlyDown) {
if (actionMapper.getBinding(KEYMAPPINGS.ingame.mapMoveUp).isCurrentlyPressed()) {
forceY -= 1;
}
if (actionMapper.getBinding(KEYMAPPINGS.ingame.mapMoveDown).currentlyDown) {
if (actionMapper.getBinding(KEYMAPPINGS.ingame.mapMoveDown).isCurrentlyPressed()) {
forceY += 1;
}
if (actionMapper.getBinding(KEYMAPPINGS.ingame.mapMoveLeft).currentlyDown) {
if (actionMapper.getBinding(KEYMAPPINGS.ingame.mapMoveLeft).isCurrentlyPressed()) {
forceX -= 1;
}
if (actionMapper.getBinding(KEYMAPPINGS.ingame.mapMoveRight).currentlyDown) {
if (actionMapper.getBinding(KEYMAPPINGS.ingame.mapMoveRight).isCurrentlyPressed()) {
forceX += 1;
}

View File

@ -59,6 +59,7 @@ export class GameHUD {
vignetteOverlay: new HUDVignetteOverlay(this.root),
// Must always exist
pinnedShapes: new HUDPinnedShapes(this.root),
notifications: new HUDNotifications(this.root),

View File

@ -59,7 +59,7 @@ export class HUDBlueprintPlacer extends BaseHUDPart {
const tile = worldPos.toTileSpace();
if (blueprint.tryPlace(this.root, tile)) {
// This actually feels weird
// if (!this.root.keyMapper.getBinding(KEYMAPPINGS.placementModifiers.placeMultiple).currentlyDown) {
// if (!this.root.keyMapper.getBinding(KEYMAPPINGS.placementModifiers.placeMultiple).isCurrentlyPressed()) {
// this.currentBlueprint.set(null);
// }
}
@ -84,7 +84,11 @@ export class HUDBlueprintPlacer extends BaseHUDPart {
rotateBlueprint() {
if (this.currentBlueprint.get()) {
if (this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateInverseModifier).currentlyDown) {
if (
this.root.keyMapper
.getBinding(KEYMAPPINGS.placement.rotateInverseModifier)
.isCurrentlyPressed()
) {
this.currentBlueprint.get().rotateCcw();
} else {
this.currentBlueprint.get().rotateCw();

View File

@ -161,9 +161,9 @@ export class HUDBuildingPlacer extends BaseHUDPart {
if (
metaBuilding &&
metaBuilding.getRotateAutomaticallyWhilePlacing(this.currentVariant.get()) &&
!this.root.keyMapper.getBinding(
KEYMAPPINGS.placementModifiers.placementDisableAutoOrientation
).currentlyDown
!this.root.keyMapper
.getBinding(KEYMAPPINGS.placementModifiers.placementDisableAutoOrientation)
.isCurrentlyPressed()
) {
const delta = newPos.sub(oldPos);
const angleDeg = Math_degrees(delta.angle());
@ -171,8 +171,9 @@ export class HUDBuildingPlacer extends BaseHUDPart {
// Holding alt inverts the placement
if (
this.root.keyMapper.getBinding(KEYMAPPINGS.placementModifiers.placeInverse)
.currentlyDown
this.root.keyMapper
.getBinding(KEYMAPPINGS.placementModifiers.placeInverse)
.isCurrentlyPressed()
) {
this.currentBaseRotation = (180 + this.currentBaseRotation) % 360;
}
@ -394,7 +395,11 @@ export class HUDBuildingPlacer extends BaseHUDPart {
tryRotate() {
const selectedBuilding = this.currentMetaBuilding.get();
if (selectedBuilding) {
if (this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateInverseModifier).currentlyDown) {
if (
this.root.keyMapper
.getBinding(KEYMAPPINGS.placement.rotateInverseModifier)
.isCurrentlyPressed()
) {
this.currentBaseRotation = (this.currentBaseRotation + 270) % 360;
} else {
this.currentBaseRotation = (this.currentBaseRotation + 90) % 360;
@ -479,16 +484,18 @@ export class HUDBuildingPlacer extends BaseHUDPart {
if (
metaBuilding.getFlipOrientationAfterPlacement() &&
!this.root.keyMapper.getBinding(
KEYMAPPINGS.placementModifiers.placementDisableAutoOrientation
).currentlyDown
!this.root.keyMapper
.getBinding(KEYMAPPINGS.placementModifiers.placementDisableAutoOrientation)
.isCurrentlyPressed()
) {
this.currentBaseRotation = (180 + this.currentBaseRotation) % 360;
}
if (
!metaBuilding.getStayInPlacementMode() &&
!this.root.keyMapper.getBinding(KEYMAPPINGS.placementModifiers.placeMultiple).currentlyDown &&
!this.root.keyMapper
.getBinding(KEYMAPPINGS.placementModifiers.placeMultiple)
.isCurrentlyPressed() &&
!this.root.app.settings.getAllSettings().alwaysMultiplace
) {
// Stop placement

View File

@ -12,6 +12,7 @@ import { enumMouseButton } from "../../camera";
import { T } from "../../../translations";
import { KEYMAPPINGS } from "../../key_action_mapper";
import { THEME } from "../../theme";
import { enumHubGoalRewards } from "../../tutorial_goals";
const logger = createLogger("hud/mass_selector");
@ -30,9 +31,9 @@ export class HUDMassSelector extends BaseHUDPart {
"ingame_HUD_MassSelector",
[],
T.ingame.massSelect.infoText
.replace("<keyDelete>", removalKeybinding)
.replace("<keyCopy>", copyKeybinding)
.replace("<keyCancel>", abortKeybinding)
.replace("<keyDelete>", `<code class='keybinding'>${removalKeybinding}</code>`)
.replace("<keyCopy>", `<code class='keybinding'>${copyKeybinding}</code>`)
.replace("<keyCancel>", `<code class='keybinding'>${abortKeybinding}</code>`)
);
}
@ -107,6 +108,13 @@ export class HUDMassSelector extends BaseHUDPart {
startCopy() {
if (this.selectedUids.size > 0) {
if (!this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_blueprints)) {
this.root.hud.parts.dialogs.showInfo(
T.dialogs.blueprintsNotUnlocked.title,
T.dialogs.blueprintsNotUnlocked.desc
);
return;
}
this.root.hud.signals.buildingsSelectedForCopy.dispatch(Array.from(this.selectedUids));
this.selectedUids = new Set();
this.root.soundProxy.playUiClick();
@ -121,7 +129,7 @@ export class HUDMassSelector extends BaseHUDPart {
* @param {enumMouseButton} mouseButton
*/
onMouseDown(pos, mouseButton) {
if (!this.root.keyMapper.getBinding(KEYMAPPINGS.massSelect.massSelectStart).currentlyDown) {
if (!this.root.keyMapper.getBinding(KEYMAPPINGS.massSelect.massSelectStart).isCurrentlyPressed()) {
return;
}
@ -129,7 +137,11 @@ export class HUDMassSelector extends BaseHUDPart {
return;
}
if (!this.root.keyMapper.getBinding(KEYMAPPINGS.massSelect.massSelectSelectMultiple).currentlyDown) {
if (
!this.root.keyMapper
.getBinding(KEYMAPPINGS.massSelect.massSelectSelectMultiple)
.isCurrentlyPressed()
) {
// Start new selection
this.selectedUids = new Set();
}

View File

@ -9,6 +9,19 @@ export class HUDPinnedShapes extends BaseHUDPart {
this.element = makeDiv(parent, "ingame_HUD_PinnedShapes", []);
}
serialize() {
return {
shapes: this.pinnedShapes,
};
}
deserialize(data) {
if (!data || !data.shapes || !Array.isArray(data.shapes)) {
return "Invalid pinned shapes data";
}
this.pinnedShapes = data.shapes;
}
initialize() {
/** @type {Array<{ key: string, goal: number }>} */
this.pinnedShapes = [];

View File

@ -89,9 +89,10 @@ export class HUDUnlockNotification extends BaseHUDPart {
clearTimeout(this.buttonShowTimeout);
}
this.element.querySelector("button.close").classList.remove("unlocked");
this.buttonShowTimeout = setTimeout(
() => this.element.querySelector("button.close").classList.add("unlocked"),
G_IS_DEV ? 1000 : 10000
10000
);
}

View File

@ -235,12 +235,17 @@ export class Keybinding {
this.builtin = builtin;
this.repeated = repeated;
this.currentlyDown = false;
this.signal = new Signal();
this.toggled = new Signal();
}
/**
* Returns whether this binding is currently pressed
*/
isCurrentlyPressed() {
return this.app.inputMgr.keysDown.has(this.keyCode);
}
/**
* Adds an event listener
* @param {function() : void} receiver
@ -350,7 +355,6 @@ export class KeyActionMapper {
for (const key in this.keybindings) {
/** @type {Keybinding} */
const binding = this.keybindings[key];
binding.currentlyDown = false;
}
}
@ -360,17 +364,16 @@ export class KeyActionMapper {
* @param {number} param0.keyCode
* @param {boolean} param0.shift
* @param {boolean} param0.alt
* @param {boolean=} param0.initial
*/
handleKeydown({ keyCode, shift, alt }) {
handleKeydown({ keyCode, shift, alt, initial }) {
let stop = false;
// Find mapping
for (const key in this.keybindings) {
/** @type {Keybinding} */
const binding = this.keybindings[key];
if (binding.keyCode === keyCode && (!binding.currentlyDown || binding.repeated)) {
binding.currentlyDown = true;
if (binding.keyCode === keyCode && (initial || binding.repeated)) {
/** @type {Signal} */
const signal = this.keybindings[key].signal;
if (signal.dispatch() === STOP_PROPAGATION) {
@ -392,13 +395,7 @@ export class KeyActionMapper {
* @param {boolean} param0.alt
*/
handleKeyup({ keyCode, shift, alt }) {
for (const key in this.keybindings) {
/** @type {Keybinding} */
const binding = this.keybindings[key];
if (binding.keyCode === keyCode) {
binding.currentlyDown = false;
}
}
// Empty
}
/**

View File

@ -23,6 +23,7 @@ export const enumHubGoalRewards = {
reward_painter_quad: "reward_painter_quad",
reward_storage: "reward_storage",
reward_blueprints: "reward_blueprints",
reward_freeplay: "reward_freeplay",
no_reward: "no_reward",
@ -65,14 +66,14 @@ export const tutorialGoals = [
// Rotater
{
shape: "Cu----Cu", // belts t2
required: 300,
required: 200,
reward: enumHubGoalRewards.reward_tunnel,
},
// 6
{
shape: "Cu------", // miners t2
required: 500,
required: 400,
reward: enumHubGoalRewards.reward_painter,
},
@ -80,14 +81,14 @@ export const tutorialGoals = [
// Painter
{
shape: "CrCrCrCr", // unused
required: 1000,
required: 800,
reward: enumHubGoalRewards.reward_rotater_ccw,
},
// 8
{
shape: "RbRb----", // painter t2
required: 1500,
required: 1250,
reward: enumHubGoalRewards.reward_mixer,
},
@ -95,7 +96,7 @@ export const tutorialGoals = [
// Mixing (purple)
{
shape: "CpCpCpCp", // belts t3
required: 2500,
required: 1750,
reward: enumHubGoalRewards.reward_splitter_compact,
},
@ -103,7 +104,7 @@ export const tutorialGoals = [
// Star shape + cyan
{
shape: "ScScScSc", // miners t3
required: 5000,
required: 2250,
reward: enumHubGoalRewards.reward_stacker,
},
@ -111,46 +112,54 @@ export const tutorialGoals = [
// Stacker
{
shape: "CgScScCg", // processors t3
required: 6000,
required: 3000,
reward: enumHubGoalRewards.reward_miner_chainable,
},
// 12
// Blueprints
{
shape: "RpRpRpRp:CwCwCwCw", // painting t3
required: 7000,
reward: enumHubGoalRewards.reward_underground_belt_tier_2,
shape: "CbCbCbRb:CwCwCwCw",
required: 4000,
reward: enumHubGoalRewards.reward_blueprints,
},
// 13
{
shape: "SrSrSrSr:CyCyCyCy", // unused
required: 7850,
reward: enumHubGoalRewards.reward_storage,
shape: "RpRpRpRp:CwCwCwCw", // painting t3
required: 9000,
reward: enumHubGoalRewards.reward_underground_belt_tier_2,
},
// 14
{
shape: "SrSrSrSr:CyCyCyCy:SwSwSwSw", // belts t4 (two variants)
required: 8000,
reward: enumHubGoalRewards.reward_cutter_quad,
shape: "SrSrSrSr:CyCyCyCy", // unused
required: 12000,
reward: enumHubGoalRewards.reward_storage,
},
// 15
{
shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", // miner t4 (two variants)
required: 9000,
reward: enumHubGoalRewards.reward_painter_double,
shape: "SrSrSrSr:CyCyCyCy:SwSwSwSw", // belts t4 (two variants)
required: 14000,
reward: enumHubGoalRewards.reward_cutter_quad,
},
// 16
{
shape: "WrRgWrRg:CwCrCwCr:SgSgSgSg", // processors t4 (two varinats)
required: 10000,
reward: enumHubGoalRewards.reward_painter_quad,
shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", // miner t4 (two variants)
required: 17000,
reward: enumHubGoalRewards.reward_painter_double,
},
// 17
{
shape: "WrRgWrRg:CwCrCwCr:SgSgSgSg", // processors t4 (two varinats)
required: 30000,
reward: enumHubGoalRewards.reward_painter_quad,
},
// 18
{
shape: finalGameShape,
required: 50000,

View File

@ -2,6 +2,7 @@ import { findNiceIntegerValue } from "../core/utils";
import { ShapeDefinition } from "./shape_definition";
export const finalGameShape = "RuCw--Cw:----Ru--";
export const blueprintShape = "CbCbCbRb:CwCwCwCw";
export const UPGRADES = {
belt: {

1
src/js/globals.d.ts vendored
View File

@ -7,7 +7,6 @@ declare function assertAlways(condition: boolean | object | string, ...errorMess
declare const abstract: void;
declare const G_API_ENDPOINT: string;
declare const G_APP_ENVIRONMENT: string;
declare const G_HAVE_ASSERT: boolean;
declare const G_BUILD_TIME: number;

View File

@ -63,9 +63,7 @@ export class Savegame extends ReadWriteProxy {
return {
version: this.getCurrentVersion(),
dump: null,
stats: {
buildingsPlaced: 0,
},
stats: {},
lastUpdate: Date.now(),
};
}
@ -79,7 +77,6 @@ export class Savegame extends ReadWriteProxy {
return ExplainedResult.bad("Can not migrate savegame, too old");
}
console.log("TODO: Migrate from", data.version);
if (data.version === 1000) {
SavegameInterface_V1001.migrate1000to1001(data);
data.version = 1001;
@ -222,6 +219,12 @@ export class Savegame extends ReadWriteProxy {
saveMetadata() {
this.metaDataRef.lastUpdate = new Date().getTime();
this.metaDataRef.version = this.getCurrentVersion();
if (!this.hasGameDump()) {
this.metaDataRef.level = 0;
} else {
this.metaDataRef.level = this.currentData.dump.hubGoals.level;
}
return this.app.savegameMgr.writeAsync();
}

View File

@ -19,7 +19,8 @@ export const enumLocalSavegameStatus = {
* @typedef {{
* lastUpdate: number,
* version: number,
* internalId: string
* internalId: string,
* level: number
* }} SavegameMetadata
*
* @typedef {{
@ -48,7 +49,7 @@ export class SavegameManager extends ReadWriteProxy {
}
getCurrentVersion() {
return 1000;
return 1001;
}
/**
@ -68,6 +69,13 @@ export class SavegameManager extends ReadWriteProxy {
* @param {SavegamesData} data
*/
migrate(data) {
if (data.version < 1001) {
data.savegames.forEach(savegame => {
savegame.level = 0;
});
data.version = 1001;
}
return ExplainedResult.good();
}

View File

@ -38,6 +38,7 @@ export class SavegameSerializer {
map: root.map.serialize(),
entityMgr: root.entityMgr.serialize(),
hubGoals: root.hubGoals.serialize(),
pinnedShapes: root.hud.parts.pinnedShapes.serialize(),
};
data.entities = this.internal.serializeEntityArray(root.entityMgr.entities);
@ -118,7 +119,7 @@ export class SavegameSerializer {
/**
* Tries to load the savegame from a given dump
* @param {SerializedGame} savegame
* @param {import("./savegame_typedefs").SerializedGame} savegame
* @param {GameRoot} root
* @returns {ExplainedResult}
*/
@ -135,6 +136,7 @@ export class SavegameSerializer {
errorReason = errorReason || root.camera.deserialize(savegame.camera);
errorReason = errorReason || root.map.deserialize(savegame.map);
errorReason = errorReason || root.hubGoals.deserialize(savegame.hubGoals);
errorReason = errorReason || root.hud.parts.pinnedShapes.deserialize(savegame.pinnedShapes);
errorReason = errorReason || this.internal.deserializeEntityArray(root, savegame.entities);
// Check for errors
@ -144,47 +146,4 @@ export class SavegameSerializer {
return ExplainedResult.good();
}
/////////// MIGRATION HELPERS ///////////
/**
* Performs a function on each component (useful to add / remove / alter properties for migration)
* @param {SerializedGame} savegame
* @param {typeof Component} componentHandle
* @param {function} modifier
*/
migration_migrateComponent(savegame, componentHandle, modifier) {
const targetId = componentHandle.getId();
for (const entityListId in savegame.entities) {
for (let i = 0; i < savegame.entities[entityListId].length; ++i) {
const list = savegame.entities[entityListId][i];
for (let k = 0; k < list.length; ++k) {
const entity = list[k];
const components = entity.components;
if (components[targetId]) {
modifier(components[targetId]);
}
}
}
}
}
/**
* Performs an operation on each object which is a PooledObject (usually Projectiles). Useful to
* perform migrations
* @param {Array<any>} pools
* @param {string} targetClassKey
* @param {function} modifier
*/
migration_migrateGenericObjectPool(pools, targetClassKey, modifier) {
for (let i = 0; i < pools.length; ++i) {
const pool = pools[i];
if (pool.key === targetClassKey) {
const entries = pool.data.entries;
for (const uid in entries) {
modifier(entries[uid]);
}
}
}
}
}

View File

@ -1,11 +1,10 @@
import { Entity } from "../game/entity";
/**
* @typedef {{
* buildingsPlaced: number
* }} SavegameStats
*/
import { Entity } from "../game/entity";
/**
* @typedef {{
* camera: any,
@ -13,6 +12,7 @@ import { Entity } from "../game/entity";
* entityMgr: any,
* map: any,
* hubGoals: any,
* pinnedShapes: any,
* entities: Array<Entity>
* }} SerializedGame
*/
@ -22,6 +22,6 @@ import { Entity } from "../game/entity";
* version: number,
* dump: SerializedGame,
* stats: SavegameStats,
* lastUpdate: number
* lastUpdate: number,
* }} SavegameData
*/

View File

@ -24,6 +24,10 @@ export class SavegameInterface_V1001 extends SavegameInterface_V1000 {
return true;
}
dump.pinnedShapes = {
shapes: [],
};
const entities = dump.entities;
for (let i = 0; i < entities.length; ++i) {
const entity = entities[i];

View File

@ -283,8 +283,10 @@ export class MainMenuState extends GameState {
makeDiv(
elem,
null,
["updateTime"],
formatSecondsToTimeAgo((new Date().getTime() - games[i].lastUpdate) / 1000.0)
["level"],
games[i].level
? T.mainMenu.savegameLevel.replace("<x>", "" + games[i].level)
: T.mainMenu.savegameLevelUnknown
);
const deleteButton = document.createElement("button");

View File

@ -78,6 +78,9 @@ mainMenu:
browserWarning: >-
Sorry, but the game is known to run slow on your browser! Get the standalone version or download chrome for the full experience.
savegameLevel: Level <x>
savegameLevelUnknown: Unknown Level
dialogs:
buttons:
ok: OK
@ -177,6 +180,11 @@ dialogs:
desc: >-
You are deleting a lot of buildings (<count> to be exact)! Are you sure you want to do this?
blueprintsNotUnlocked:
title: Not unlocked yet
desc: >-
Blueprints have not been unlocked yet! Complete more levels to unlock them.
ingame:
# This is shown in the top left corner and displays useful keybindings in
# every situation
@ -447,6 +455,10 @@ storyRewards:
title: Freeplay
desc: You did it! You unlocked the <strong>free-play mode</strong>! This means that shapes are now randomly generated! (No worries, more content is planned for the standalone!)
reward_blueprints:
title: Blueprints
desc: You can now <strong>copy and paste</strong> parts of your factory! Select an area (Hold ctrl, then drag), then press 'C' to copy it.<br><br>Pasting it is not free, you need to produce blueprint shapes to afford it! (Those you just delivered).
# Special reward, which is shown when there is no reward actually
no_reward:
title: Next level