mirror of
https://github.com/tobspr/shapez.io.git
synced 2024-10-27 20:34:29 +00:00
Implement saving and restoring belt paths
This commit is contained in:
parent
9a6029279d
commit
42c569d91f
@ -8,6 +8,8 @@ import { BaseItem } from "./base_item";
|
||||
import { Entity } from "./entity";
|
||||
import { GameRoot } from "./root";
|
||||
import { Rectangle } from "../core/rectangle";
|
||||
import { BasicSerializableObject, types } from "../savegame/serialization";
|
||||
import { gItemRegistry } from "../core/global_registries";
|
||||
|
||||
const logger = createLogger("belt_path");
|
||||
|
||||
@ -20,12 +22,76 @@ const DEBUG = G_IS_DEV && false;
|
||||
/**
|
||||
* Stores a path of belts, used for optimizing performance
|
||||
*/
|
||||
export class BeltPath {
|
||||
export class BeltPath extends BasicSerializableObject {
|
||||
static getId() {
|
||||
return "BeltPath";
|
||||
}
|
||||
|
||||
static getSchema() {
|
||||
return {
|
||||
entityPath: types.array(types.entity),
|
||||
items: types.array(types.pair(types.ufloat, types.obj(gItemRegistry))),
|
||||
spacingToFirstItem: types.ufloat,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a path from a serialized object
|
||||
* @param {GameRoot} root
|
||||
* @param {Object} data
|
||||
* @returns {BeltPath|string}
|
||||
*/
|
||||
static fromSerialized(root, data) {
|
||||
// Create fake object which looks like a belt path but skips the constructor
|
||||
const fakeObject = /** @type {BeltPath} */ (Object.create(BeltPath.prototype));
|
||||
fakeObject.root = root;
|
||||
|
||||
// Deserialize the data
|
||||
const errorCodeDeserialize = fakeObject.deserialize(data);
|
||||
if (errorCodeDeserialize) {
|
||||
return errorCodeDeserialize;
|
||||
}
|
||||
|
||||
// Compute other properties
|
||||
fakeObject.init(false);
|
||||
|
||||
return fakeObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the path by computing the properties which are not saved
|
||||
* @param {boolean} computeSpacing Whether to also compute the spacing
|
||||
*/
|
||||
init(computeSpacing = true) {
|
||||
// Find acceptor and ejector
|
||||
this.ejectorComp = this.entityPath[this.entityPath.length - 1].components.ItemEjector;
|
||||
this.ejectorSlot = this.ejectorComp.slots[0];
|
||||
this.initialBeltComponent = this.entityPath[0].components.Belt;
|
||||
|
||||
this.totalLength = this.computeTotalLength();
|
||||
|
||||
if (computeSpacing) {
|
||||
this.spacingToFirstItem = this.totalLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Current bounds of this path
|
||||
* @type {Rectangle}
|
||||
*/
|
||||
this.worldBounds = this.computeBounds();
|
||||
|
||||
// Connect the belts
|
||||
for (let i = 0; i < this.entityPath.length; ++i) {
|
||||
this.entityPath[i].components.Belt.assignedPath = this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GameRoot} root
|
||||
* @param {Array<Entity>} entityPath
|
||||
*/
|
||||
constructor(root, entityPath) {
|
||||
super();
|
||||
this.root = root;
|
||||
|
||||
assert(entityPath.length > 0, "invalid entity path");
|
||||
@ -42,25 +108,7 @@ export class BeltPath {
|
||||
* Stores the spacing to the first item
|
||||
*/
|
||||
|
||||
// Find acceptor and ejector
|
||||
|
||||
this.ejectorComp = this.entityPath[this.entityPath.length - 1].components.ItemEjector;
|
||||
this.ejectorSlot = this.ejectorComp.slots[0];
|
||||
this.initialBeltComponent = this.entityPath[0].components.Belt;
|
||||
|
||||
this.totalLength = this.computeTotalLength();
|
||||
this.spacingToFirstItem = this.totalLength;
|
||||
|
||||
/**
|
||||
* Current bounds of this path
|
||||
* @type {Rectangle}
|
||||
*/
|
||||
this.worldBounds = this.computeBounds();
|
||||
|
||||
// Connect the belts
|
||||
for (let i = 0; i < this.entityPath.length; ++i) {
|
||||
this.entityPath[i].components.Belt.assignedPath = this;
|
||||
}
|
||||
this.init();
|
||||
|
||||
this.debug_checkIntegrity("constructor");
|
||||
}
|
||||
@ -86,6 +134,16 @@ export class BeltPath {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* SLOW / Tries to find the item closest to the given tile
|
||||
* @param {Vector} tile
|
||||
* @returns {BaseItem|null}
|
||||
*/
|
||||
findItemAtTile(tile) {
|
||||
// TODO: This breaks color blind mode otherwise
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the tile bounds of the path
|
||||
* @returns {Rectangle}
|
||||
@ -113,7 +171,7 @@ export class BeltPath {
|
||||
* Checks if this path is valid
|
||||
*/
|
||||
debug_checkIntegrity(currentChange = "change") {
|
||||
if (!G_IS_DEV || !DEBUG) {
|
||||
if (!G_IS_DEV) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -126,7 +184,7 @@ export class BeltPath {
|
||||
|
||||
// Check for mismatching length
|
||||
const totalLength = this.computeTotalLength();
|
||||
if (!epsilonCompare(this.totalLength, totalLength)) {
|
||||
if (!epsilonCompare(this.totalLength, totalLength, 0.01)) {
|
||||
return this.debug_failIntegrity(
|
||||
currentChange,
|
||||
"Total length mismatch, stored =",
|
||||
@ -200,7 +258,7 @@ export class BeltPath {
|
||||
}
|
||||
|
||||
// Check distance if empty
|
||||
if (this.items.length === 0 && !epsilonCompare(this.spacingToFirstItem, this.totalLength)) {
|
||||
if (this.items.length === 0 && !epsilonCompare(this.spacingToFirstItem, this.totalLength, 0.01)) {
|
||||
return fail(
|
||||
currentChange,
|
||||
"Path is empty but spacing to first item (",
|
||||
@ -230,7 +288,7 @@ export class BeltPath {
|
||||
}
|
||||
|
||||
// Check the total sum matches
|
||||
if (!epsilonCompare(currentPos, this.totalLength)) {
|
||||
if (!epsilonCompare(currentPos, this.totalLength, 0.01)) {
|
||||
return fail(
|
||||
"total sum (",
|
||||
currentPos,
|
||||
|
@ -1,12 +1,9 @@
|
||||
import { Component } from "../component";
|
||||
import { Math_cos, Math_PI, Math_sin } from "../../core/builtins";
|
||||
import { enumDirection, Vector } from "../../core/vector";
|
||||
import { types } from "../../savegame/serialization";
|
||||
import { gItemRegistry } from "../../core/global_registries";
|
||||
import { BaseItem } from "../base_item";
|
||||
import { Vector, enumDirection } from "../../core/vector";
|
||||
import { Math_PI, Math_sin, Math_cos } from "../../core/builtins";
|
||||
import { globalConfig } from "../../core/config";
|
||||
import { Entity } from "../entity";
|
||||
import { BeltPath } from "../belt_path";
|
||||
import { Component } from "../component";
|
||||
import { Entity } from "../entity";
|
||||
|
||||
export const curvedBeltLength = /* Math_PI / 4 */ 0.78;
|
||||
|
||||
@ -19,7 +16,6 @@ export class BeltComponent extends Component {
|
||||
// The followUpCache field is not serialized.
|
||||
return {
|
||||
direction: types.string,
|
||||
sortedItems: types.array(types.pair(types.float, types.obj(gItemRegistry))),
|
||||
};
|
||||
}
|
||||
|
||||
@ -37,9 +33,6 @@ export class BeltComponent extends Component {
|
||||
|
||||
this.direction = direction;
|
||||
|
||||
/** @type {Array<[number, BaseItem]>} */
|
||||
this.sortedItems = [];
|
||||
|
||||
/** @type {Entity} */
|
||||
this.followUpCache = null;
|
||||
|
||||
@ -85,46 +78,4 @@ export class BeltComponent extends Component {
|
||||
return new Vector(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the belt can currently accept an item from the given direction
|
||||
*/
|
||||
canAcceptItem() {
|
||||
const firstItem = this.sortedItems[0];
|
||||
if (!firstItem) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return firstItem[0] > globalConfig.itemSpacingOnBelts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes a new item to the belt
|
||||
* @param {BaseItem} item
|
||||
*/
|
||||
takeItem(item, leftoverProgress = 0.0) {
|
||||
if (G_IS_DEV) {
|
||||
assert(
|
||||
this.sortedItems.length === 0 ||
|
||||
leftoverProgress <= this.sortedItems[0][0] - globalConfig.itemSpacingOnBelts + 0.001,
|
||||
"Invalid leftover: " +
|
||||
leftoverProgress +
|
||||
" items are " +
|
||||
this.sortedItems.map(item => item[0])
|
||||
);
|
||||
assert(leftoverProgress < 1.0, "Invalid leftover: " + leftoverProgress);
|
||||
}
|
||||
this.sortedItems.unshift([leftoverProgress, item]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns how much space there is to the first item
|
||||
*/
|
||||
getDistanceToFirstItemCenter() {
|
||||
const firstItem = this.sortedItems[0];
|
||||
if (!firstItem) {
|
||||
return 1;
|
||||
}
|
||||
return firstItem[0];
|
||||
}
|
||||
}
|
||||
|
@ -48,9 +48,9 @@ export class HUDColorBlindHelper extends BaseHUDPart {
|
||||
|
||||
// Check if the belt has a color item
|
||||
if (beltComp) {
|
||||
const firstItem = beltComp.sortedItems[0];
|
||||
if (firstItem && firstItem[1] instanceof ColorItem) {
|
||||
return firstItem[1].color;
|
||||
const item = beltComp.assignedPath.findItemAtTile(tile);
|
||||
if (item && item instanceof ColorItem) {
|
||||
return item.color;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,15 +51,51 @@ export class BeltSystem extends GameSystemWithFilter {
|
||||
this.root.signals.entityDestroyed.add(this.updateSurroundingBeltPlacement, this);
|
||||
this.root.signals.entityDestroyed.add(this.onEntityDestroyed, this);
|
||||
this.root.signals.entityAdded.add(this.onEntityAdded, this);
|
||||
this.root.signals.postLoadHook.add(this.computeBeltCache, this);
|
||||
|
||||
// /** @type {Rectangle} */
|
||||
// this.areaToRecompute = null;
|
||||
|
||||
/** @type {Array<BeltPath>} */
|
||||
this.beltPaths = [];
|
||||
}
|
||||
|
||||
this.recomputePaths = true;
|
||||
/**
|
||||
* Serializes all belt paths
|
||||
*/
|
||||
serializePaths() {
|
||||
let data = [];
|
||||
for (let i = 0; i < this.beltPaths.length; ++i) {
|
||||
data.push(this.beltPaths[i].serialize());
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes all belt paths
|
||||
* @param {Array<any>} data
|
||||
*/
|
||||
deserializePaths(data) {
|
||||
if (!Array.isArray(data)) {
|
||||
return "Belt paths are not an array: " + typeof data;
|
||||
}
|
||||
|
||||
for (let i = 0; i < data.length; ++i) {
|
||||
const path = BeltPath.fromSerialized(this.root, data[i]);
|
||||
if (!(path instanceof BeltPath)) {
|
||||
return "Failed to create path from belt data: " + path;
|
||||
}
|
||||
|
||||
this.beltPaths.push(path);
|
||||
}
|
||||
|
||||
if (this.beltPaths.length === 0) {
|
||||
logger.warn("Recomputing belt paths (most likely the savegame is old)");
|
||||
this.recomputeAllBeltPaths();
|
||||
} else {
|
||||
logger.warn("Restored", this.beltPaths.length, "belt paths");
|
||||
}
|
||||
|
||||
this.verifyBeltPaths();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -76,7 +112,6 @@ export class BeltSystem extends GameSystemWithFilter {
|
||||
return;
|
||||
}
|
||||
|
||||
// this.recomputePaths = true;
|
||||
/*
|
||||
const metaBelt = gMetaBuildingRegistry.findByClass(MetaBeltBaseBuilding);
|
||||
|
||||
@ -236,7 +271,7 @@ export class BeltSystem extends GameSystemWithFilter {
|
||||
* Verifies all belt paths
|
||||
*/
|
||||
verifyBeltPaths() {
|
||||
if (G_IS_DEV && false) {
|
||||
if (G_IS_DEV && true) {
|
||||
for (let i = 0; i < this.beltPaths.length; ++i) {
|
||||
this.beltPaths[i].debug_checkIntegrity("general-verify");
|
||||
}
|
||||
@ -327,58 +362,11 @@ export class BeltSystem extends GameSystemWithFilter {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recomputes the belt cache
|
||||
*/
|
||||
computeBeltCache() {
|
||||
this.recomputePaths = false;
|
||||
/*
|
||||
if (this.areaToRecompute) {
|
||||
logger.log("Updating belt cache by updating area:", this.areaToRecompute);
|
||||
|
||||
if (G_IS_DEV && globalConfig.debug.renderChanges) {
|
||||
this.root.hud.parts.changesDebugger.renderChange(
|
||||
"belt-area",
|
||||
this.areaToRecompute,
|
||||
"#00fff6"
|
||||
);
|
||||
}
|
||||
|
||||
for (let x = this.areaToRecompute.x; x < this.areaToRecompute.right(); ++x) {
|
||||
for (let y = this.areaToRecompute.y; y < this.areaToRecompute.bottom(); ++y) {
|
||||
const tile = this.root.map.getTileContentXY(x, y);
|
||||
if (tile && tile.components.Belt) {
|
||||
tile.components.Belt.followUpCache = this.findFollowUpEntity(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reset stale areas afterwards
|
||||
this.areaToRecompute = null;
|
||||
} else {
|
||||
logger.log("Doing full belt recompute");
|
||||
|
||||
if (G_IS_DEV && globalConfig.debug.renderChanges) {
|
||||
this.root.hud.parts.changesDebugger.renderChange(
|
||||
"",
|
||||
new Rectangle(-1000, -1000, 2000, 2000),
|
||||
"#00fff6"
|
||||
);
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.allEntities.length; ++i) {
|
||||
const entity = this.allEntities[i];
|
||||
entity.components.Belt.followUpCache = this.findFollowUpEntity(entity);
|
||||
}
|
||||
}
|
||||
*/
|
||||
this.computeBeltPaths();
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the belt path network
|
||||
*/
|
||||
computeBeltPaths() {
|
||||
recomputeAllBeltPaths() {
|
||||
logger.warn("Recomputing all belt paths");
|
||||
const visitedUids = new Set();
|
||||
|
||||
const result = [];
|
||||
@ -429,10 +417,6 @@ export class BeltSystem extends GameSystemWithFilter {
|
||||
}
|
||||
|
||||
update() {
|
||||
if (this.recomputePaths) {
|
||||
this.computeBeltCache();
|
||||
}
|
||||
|
||||
this.verifyBeltPaths();
|
||||
|
||||
for (let i = 0; i < this.beltPaths.length; ++i) {
|
||||
@ -440,123 +424,6 @@ export class BeltSystem extends GameSystemWithFilter {
|
||||
}
|
||||
|
||||
this.verifyBeltPaths();
|
||||
|
||||
return;
|
||||
/*
|
||||
|
||||
// Divide by item spacing on belts since we use throughput and not speed
|
||||
let beltSpeed =
|
||||
this.root.hubGoals.getBeltBaseSpeed() *
|
||||
this.root.dynamicTickrate.deltaSeconds *
|
||||
globalConfig.itemSpacingOnBelts;
|
||||
|
||||
if (G_IS_DEV && globalConfig.debug.instantBelts) {
|
||||
beltSpeed *= 100;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.allEntities.length; ++i) {
|
||||
const entity = this.allEntities[i];
|
||||
|
||||
const beltComp = entity.components.Belt;
|
||||
const items = beltComp.sortedItems;
|
||||
|
||||
if (items.length === 0) {
|
||||
// Fast out for performance
|
||||
continue;
|
||||
}
|
||||
|
||||
const ejectorComp = entity.components.ItemEjector;
|
||||
let maxProgress = 1;
|
||||
|
||||
// PERFORMANCE OPTIMIZATION
|
||||
// Original:
|
||||
// const isCurrentlyEjecting = ejectorComp.isAnySlotEjecting();
|
||||
// Replaced (Since belts always have just one slot):
|
||||
const ejectorSlot = ejectorComp.slots[0];
|
||||
const isCurrentlyEjecting = ejectorSlot.item;
|
||||
|
||||
// When ejecting, we can not go further than the item spacing since it
|
||||
// will be on the corner
|
||||
if (isCurrentlyEjecting) {
|
||||
maxProgress = 1 - globalConfig.itemSpacingOnBelts;
|
||||
} else {
|
||||
// Otherwise our progress depends on the follow up
|
||||
if (beltComp.followUpCache) {
|
||||
const spacingOnBelt = beltComp.followUpCache.components.Belt.getDistanceToFirstItemCenter();
|
||||
maxProgress = Math.min(2, 1 - globalConfig.itemSpacingOnBelts + spacingOnBelt);
|
||||
|
||||
// Useful check, but hurts performance
|
||||
// assert(maxProgress >= 0.0, "max progress < 0 (I) (" + maxProgress + ")");
|
||||
}
|
||||
}
|
||||
|
||||
let speedMultiplier = 1;
|
||||
if (beltComp.direction !== enumDirection.top) {
|
||||
// Curved belts are shorter, thus being quicker (Looks weird otherwise)
|
||||
speedMultiplier = SQRT_2;
|
||||
}
|
||||
|
||||
// How much offset we add when transferring to a new belt
|
||||
// This substracts one tick because the belt will be updated directly
|
||||
// afterwards anyways
|
||||
const takeoverOffset = 1.0 + beltSpeed * speedMultiplier;
|
||||
|
||||
// Not really nice. haven't found the reason for this yet.
|
||||
if (items.length > 2 / globalConfig.itemSpacingOnBelts) {
|
||||
beltComp.sortedItems = [];
|
||||
}
|
||||
|
||||
for (let itemIndex = items.length - 1; itemIndex >= 0; --itemIndex) {
|
||||
const progressAndItem = items[itemIndex];
|
||||
|
||||
progressAndItem[0] = Math.min(maxProgress, progressAndItem[0] + speedMultiplier * beltSpeed);
|
||||
assert(progressAndItem[0] >= 0, "Bad progress: " + progressAndItem[0]);
|
||||
|
||||
if (progressAndItem[0] >= 1.0) {
|
||||
if (beltComp.followUpCache) {
|
||||
const followUpBelt = beltComp.followUpCache.components.Belt;
|
||||
if (followUpBelt.canAcceptItem()) {
|
||||
followUpBelt.takeItem(
|
||||
progressAndItem[1],
|
||||
Math_max(0, progressAndItem[0] - takeoverOffset)
|
||||
);
|
||||
items.splice(itemIndex, 1);
|
||||
} else {
|
||||
// Well, we couldn't really take it to a follow up belt, keep it at
|
||||
// max progress
|
||||
progressAndItem[0] = 1.0;
|
||||
maxProgress = 1 - globalConfig.itemSpacingOnBelts;
|
||||
}
|
||||
} else {
|
||||
// Try to give this item to a new belt
|
||||
|
||||
// PERFORMANCE OPTIMIZATION
|
||||
|
||||
// Original:
|
||||
// const freeSlot = ejectorComp.getFirstFreeSlot();
|
||||
|
||||
// Replaced
|
||||
if (ejectorSlot.item) {
|
||||
// So, we don't have a free slot - damned!
|
||||
progressAndItem[0] = 1.0;
|
||||
maxProgress = 1 - globalConfig.itemSpacingOnBelts;
|
||||
} else {
|
||||
// We got a free slot, remove this item and keep it on the ejector slot
|
||||
if (!ejectorComp.tryEject(0, progressAndItem[1])) {
|
||||
assert(false, "Ejection failed");
|
||||
}
|
||||
items.splice(itemIndex, 1);
|
||||
|
||||
// NOTICE: Do not override max progress here at all, this leads to issues
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// We just moved this item forward, so determine the maximum progress of other items
|
||||
maxProgress = Math.max(0, progressAndItem[0] - globalConfig.itemSpacingOnBelts);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
@ -598,44 +465,6 @@ export class BeltSystem extends GameSystemWithFilter {
|
||||
1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DrawParameters} parameters
|
||||
* @param {Entity} entity
|
||||
*/
|
||||
drawEntityItems(parameters, entity) {
|
||||
/*
|
||||
const beltComp = entity.components.Belt;
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
|
||||
const items = beltComp.sortedItems;
|
||||
|
||||
if (items.length === 0) {
|
||||
// Fast out for performance
|
||||
return;
|
||||
}
|
||||
|
||||
if (!staticComp.shouldBeDrawn(parameters)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < items.length; ++i) {
|
||||
const itemAndProgress = items[i];
|
||||
|
||||
// Nice would be const [pos, item] = itemAndPos; but that gets polyfilled and is super slow then
|
||||
const progress = itemAndProgress[0];
|
||||
const item = itemAndProgress[1];
|
||||
|
||||
const position = staticComp.applyRotationToVector(beltComp.transformBeltToLocalSpace(progress));
|
||||
|
||||
item.draw(
|
||||
(staticComp.origin.x + position.x + 0.5) * globalConfig.tileSize,
|
||||
(staticComp.origin.y + position.y + 0.5) * globalConfig.tileSize,
|
||||
parameters
|
||||
);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the belt parameters
|
||||
* @param {DrawParameters} parameters
|
||||
|
@ -14,6 +14,7 @@ import { SavegameInterface_V1001 } from "./schemas/1001";
|
||||
import { SavegameInterface_V1002 } from "./schemas/1002";
|
||||
import { SavegameInterface_V1003 } from "./schemas/1003";
|
||||
import { SavegameInterface_V1004 } from "./schemas/1004";
|
||||
import { SavegameInterface_V1005 } from "./schemas/1005";
|
||||
|
||||
const logger = createLogger("savegame");
|
||||
|
||||
@ -45,7 +46,7 @@ export class Savegame extends ReadWriteProxy {
|
||||
* @returns {number}
|
||||
*/
|
||||
static getCurrentVersion() {
|
||||
return 1004;
|
||||
return 1005;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -104,6 +105,11 @@ export class Savegame extends ReadWriteProxy {
|
||||
data.version = 1004;
|
||||
}
|
||||
|
||||
if (data.version === 1004) {
|
||||
SavegameInterface_V1005.migrate1004to1005(data);
|
||||
data.version = 1005;
|
||||
}
|
||||
|
||||
return ExplainedResult.good();
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ import { SavegameInterface_V1001 } from "./schemas/1001";
|
||||
import { SavegameInterface_V1002 } from "./schemas/1002";
|
||||
import { SavegameInterface_V1003 } from "./schemas/1003";
|
||||
import { SavegameInterface_V1004 } from "./schemas/1004";
|
||||
import { SavegameInterface_V1005 } from "./schemas/1005";
|
||||
|
||||
/** @type {Object.<number, typeof BaseSavegameInterface>} */
|
||||
export const savegameInterfaces = {
|
||||
@ -13,6 +14,7 @@ export const savegameInterfaces = {
|
||||
1002: SavegameInterface_V1002,
|
||||
1003: SavegameInterface_V1003,
|
||||
1004: SavegameInterface_V1004,
|
||||
1005: SavegameInterface_V1005,
|
||||
};
|
||||
|
||||
const logger = createLogger("savegame_interface_registry");
|
||||
|
@ -40,6 +40,7 @@ export class SavegameSerializer {
|
||||
hubGoals: root.hubGoals.serialize(),
|
||||
pinnedShapes: root.hud.parts.pinnedShapes.serialize(),
|
||||
waypoints: root.hud.parts.waypoints.serialize(),
|
||||
beltPaths: root.systemMgr.systems.belt.serializePaths(),
|
||||
};
|
||||
|
||||
data.entities = this.internal.serializeEntityArray(root.entityMgr.entities);
|
||||
@ -140,6 +141,7 @@ export class SavegameSerializer {
|
||||
errorReason = errorReason || root.hud.parts.pinnedShapes.deserialize(savegame.pinnedShapes);
|
||||
errorReason = errorReason || root.hud.parts.waypoints.deserialize(savegame.waypoints);
|
||||
errorReason = errorReason || this.internal.deserializeEntityArray(root, savegame.entities);
|
||||
errorReason = errorReason || root.systemMgr.systems.belt.deserializePaths(savegame.beltPaths);
|
||||
|
||||
// Check for errors
|
||||
if (errorReason) {
|
||||
|
@ -5,6 +5,10 @@ import { Entity } from "../game/entity";
|
||||
* }} SavegameStats
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* camera: any,
|
||||
@ -14,7 +18,8 @@ import { Entity } from "../game/entity";
|
||||
* hubGoals: any,
|
||||
* pinnedShapes: any,
|
||||
* waypoints: any,
|
||||
* entities: Array<Entity>
|
||||
* entities: Array<Entity>,
|
||||
* beltPaths: Array<any>
|
||||
* }} SerializedGame
|
||||
*/
|
||||
|
||||
|
29
src/js/savegame/schemas/1005.js
Normal file
29
src/js/savegame/schemas/1005.js
Normal file
@ -0,0 +1,29 @@
|
||||
import { createLogger } from "../../core/logging.js";
|
||||
import { SavegameInterface_V1004 } from "./1004.js";
|
||||
|
||||
const schema = require("./1005.json");
|
||||
const logger = createLogger("savegame_interface/1005");
|
||||
|
||||
export class SavegameInterface_V1005 extends SavegameInterface_V1004 {
|
||||
getVersion() {
|
||||
return 1005;
|
||||
}
|
||||
|
||||
getSchemaUncached() {
|
||||
return schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import("../savegame_typedefs.js").SavegameData} data
|
||||
*/
|
||||
static migrate1004to1005(data) {
|
||||
logger.log("Migrating 1004 to 1005");
|
||||
const dump = data.dump;
|
||||
if (!dump) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// just reset belt paths for now
|
||||
dump.beltPaths = [];
|
||||
}
|
||||
}
|
5
src/js/savegame/schemas/1005.json
Normal file
5
src/js/savegame/schemas/1005.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"type": "object",
|
||||
"required": [],
|
||||
"additionalProperties": true
|
||||
}
|
Loading…
Reference in New Issue
Block a user