Fix keys being stuck, show savegame levels in main menu
This commit is contained in:
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: "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,
|
||||
|
|
|
@ -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
|
||||
|
|
Reference in New Issue