Fix keys being stuck, show savegame levels in main menu

pull/33/head
tobspr 4 years ago
parent 2a4ee8e784
commit e0facaf788

@ -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,

@ -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,
})

@ -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")
),

@ -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")
),

@ -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)});

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

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

@ -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)",

@ -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,

@ -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,

@ -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;
}

@ -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),

@ -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();

@ -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

@ -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();
}

@ -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 = [];

@ -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
);
}

@ -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
}
/**

@ -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: "CbCbCbRb:CwCwCwCw",
required: 4000,
reward: enumHubGoalRewards.reward_blueprints,
},
// 13
{
shape: "RpRpRpRp:CwCwCwCw", // painting t3
required: 7000,
required: 9000,
reward: enumHubGoalRewards.reward_underground_belt_tier_2,
},
// 13
// 14
{
shape: "SrSrSrSr:CyCyCyCy", // unused
required: 7850,
required: 12000,
reward: enumHubGoalRewards.reward_storage,
},
// 14
// 15
{
shape: "SrSrSrSr:CyCyCyCy:SwSwSwSw", // belts t4 (two variants)
required: 8000,
required: 14000,
reward: enumHubGoalRewards.reward_cutter_quad,
},
// 15
// 16
{
shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", // miner t4 (two variants)
required: 9000,
required: 17000,
reward: enumHubGoalRewards.reward_painter_double,
},
// 16
// 17
{
shape: "WrRgWrRg:CwCrCwCr:SgSgSgSg", // processors t4 (two varinats)
required: 10000,
required: 30000,
reward: enumHubGoalRewards.reward_painter_quad,
},
// 17
// 18
{
shape: finalGameShape,
required: 50000,

@ -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: {

@ -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;

@ -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();
}

@ -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();
}

@ -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]);
}
}
}
}
}

@ -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
*/

@ -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];

@ -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");

@ -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

Loading…
Cancel
Save