mirror of
https://github.com/tobspr/shapez.io.git
synced 2024-10-27 20:34:29 +00:00
Merge branch 'master' of https://github.com/tobspr/shapez.io
This commit is contained in:
commit
8f867016de
@ -1,8 +1,9 @@
|
|||||||
export const CHANGELOG = [
|
export const CHANGELOG = [
|
||||||
{
|
{
|
||||||
version: "1.1.18",
|
version: "1.1.18",
|
||||||
date: "24.06.2020",
|
date: "26.06.2020",
|
||||||
entries: [
|
entries: [
|
||||||
|
"Huge belt performance improvements - up to 50% increase",
|
||||||
"Preparations for the wires update",
|
"Preparations for the wires update",
|
||||||
"Update belt placement performance on huge factories (by Phlosioneer)",
|
"Update belt placement performance on huge factories (by Phlosioneer)",
|
||||||
"Allow clicking on variants to select them",
|
"Allow clicking on variants to select them",
|
||||||
|
@ -51,7 +51,7 @@ export const globalConfig = {
|
|||||||
// Belt speeds
|
// Belt speeds
|
||||||
// NOTICE: Update webpack.production.config too!
|
// NOTICE: Update webpack.production.config too!
|
||||||
beltSpeedItemsPerSecond: 2,
|
beltSpeedItemsPerSecond: 2,
|
||||||
itemSpacingOnBelts: 0.8,
|
itemSpacingOnBelts: 0.63,
|
||||||
minerSpeedItemsPerSecond: 0, // COMPUTED
|
minerSpeedItemsPerSecond: 0, // COMPUTED
|
||||||
|
|
||||||
undergroundBeltMaxTilesByTier: [5, 8],
|
undergroundBeltMaxTilesByTier: [5, 8],
|
||||||
|
@ -19,7 +19,7 @@ export class FormElement {
|
|||||||
abstract;
|
abstract;
|
||||||
}
|
}
|
||||||
|
|
||||||
focus(parent) {}
|
focus() {}
|
||||||
|
|
||||||
isValid() {
|
isValid() {
|
||||||
return true;
|
return true;
|
||||||
|
@ -72,6 +72,7 @@ function objectPolyfills() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!Object.entries) {
|
if (!Object.entries) {
|
||||||
|
// @ts-ignore
|
||||||
Object.entries = function entries(O) {
|
Object.entries = function entries(O) {
|
||||||
return reduce(
|
return reduce(
|
||||||
keys(O),
|
keys(O),
|
||||||
|
@ -405,8 +405,6 @@ export function findNiceValue(num) {
|
|||||||
return Math_round(niceValue * 100) / 100;
|
return Math_round(niceValue * 100) / 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.fn = findNiceValue;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds a nice integer value
|
* Finds a nice integer value
|
||||||
* @see findNiceValue
|
* @see findNiceValue
|
||||||
|
1066
src/js/game/belt_path.js
Normal file
1066
src/js/game/belt_path.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,13 +1,13 @@
|
|||||||
import { DrawParameters } from "../../../core/draw_parameters";
|
import { DrawParameters } from "../core/draw_parameters";
|
||||||
import { Loader } from "../../../core/loader";
|
import { Loader } from "../core/loader";
|
||||||
import { createLogger } from "../../../core/logging";
|
import { createLogger } from "../core/logging";
|
||||||
import { Vector } from "../../../core/vector";
|
import { Vector } from "../core/vector";
|
||||||
import { Entity } from "../../entity";
|
import { Entity } from "./entity";
|
||||||
import { GameRoot } from "../../root";
|
import { GameRoot } from "./root";
|
||||||
import { findNiceIntegerValue } from "../../../core/utils";
|
import { findNiceIntegerValue } from "../core/utils";
|
||||||
import { Math_pow } from "../../../core/builtins";
|
import { Math_pow } from "../core/builtins";
|
||||||
import { blueprintShape } from "../../upgrades";
|
import { blueprintShape } from "./upgrades";
|
||||||
import { globalConfig } from "../../../core/config";
|
import { globalConfig } from "../core/config";
|
||||||
|
|
||||||
const logger = createLogger("blueprint");
|
const logger = createLogger("blueprint");
|
||||||
|
|
||||||
@ -176,7 +176,6 @@ export class Blueprint {
|
|||||||
tryPlace(root, tile) {
|
tryPlace(root, tile) {
|
||||||
return root.logic.performBulkOperation(() => {
|
return root.logic.performBulkOperation(() => {
|
||||||
let anyPlaced = false;
|
let anyPlaced = false;
|
||||||
const beltsToRegisterLater = [];
|
|
||||||
for (let i = 0; i < this.entities.length; ++i) {
|
for (let i = 0; i < this.entities.length; ++i) {
|
||||||
let placeable = true;
|
let placeable = true;
|
||||||
const entity = this.entities[i];
|
const entity = this.entities[i];
|
||||||
@ -217,21 +216,10 @@ export class Blueprint {
|
|||||||
|
|
||||||
root.map.placeStaticEntity(clone);
|
root.map.placeStaticEntity(clone);
|
||||||
|
|
||||||
// Registering a belt immediately triggers a recalculation of surrounding belt
|
root.entityMgr.registerEntity(clone);
|
||||||
// directions, which is no good when not all belts have been placed. To resolve
|
|
||||||
// this, only register belts after all entities have been placed.
|
|
||||||
if (!clone.components.Belt) {
|
|
||||||
root.entityMgr.registerEntity(clone);
|
|
||||||
} else {
|
|
||||||
beltsToRegisterLater.push(clone);
|
|
||||||
}
|
|
||||||
anyPlaced = true;
|
anyPlaced = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < beltsToRegisterLater.length; i++) {
|
|
||||||
root.entityMgr.registerEntity(beltsToRegisterLater[i]);
|
|
||||||
}
|
|
||||||
return anyPlaced;
|
return anyPlaced;
|
||||||
});
|
});
|
||||||
}
|
}
|
@ -1,12 +1,12 @@
|
|||||||
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 { types } from "../../savegame/serialization";
|
||||||
import { gItemRegistry } from "../../core/global_registries";
|
import { BeltPath } from "../belt_path";
|
||||||
import { BaseItem } from "../base_item";
|
import { Component } from "../component";
|
||||||
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 { Entity } from "../entity";
|
||||||
|
|
||||||
|
export const curvedBeltLength = /* Math_PI / 4 */ 0.78;
|
||||||
|
|
||||||
export class BeltComponent extends Component {
|
export class BeltComponent extends Component {
|
||||||
static getId() {
|
static getId() {
|
||||||
return "Belt";
|
return "Belt";
|
||||||
@ -16,7 +16,6 @@ export class BeltComponent extends Component {
|
|||||||
// The followUpCache field is not serialized.
|
// The followUpCache field is not serialized.
|
||||||
return {
|
return {
|
||||||
direction: types.string,
|
direction: types.string,
|
||||||
sortedItems: types.array(types.pair(types.float, types.obj(gItemRegistry))),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,11 +33,22 @@ export class BeltComponent extends Component {
|
|||||||
|
|
||||||
this.direction = direction;
|
this.direction = direction;
|
||||||
|
|
||||||
/** @type {Array<[number, BaseItem]>} */
|
|
||||||
this.sortedItems = [];
|
|
||||||
|
|
||||||
/** @type {Entity} */
|
/** @type {Entity} */
|
||||||
this.followUpCache = null;
|
this.followUpCache = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The path this belt is contained in, not serialized
|
||||||
|
* @type {BeltPath}
|
||||||
|
*/
|
||||||
|
this.assignedPath = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the effective length of this belt in tile space
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
getEffectiveLengthTiles() {
|
||||||
|
return this.direction === enumDirection.top ? 1.0 : curvedBeltLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -50,14 +60,17 @@ export class BeltComponent extends Component {
|
|||||||
transformBeltToLocalSpace(progress) {
|
transformBeltToLocalSpace(progress) {
|
||||||
switch (this.direction) {
|
switch (this.direction) {
|
||||||
case enumDirection.top:
|
case enumDirection.top:
|
||||||
|
assert(progress <= 1.02, "Invalid progress: " + progress);
|
||||||
return new Vector(0, 0.5 - progress);
|
return new Vector(0, 0.5 - progress);
|
||||||
|
|
||||||
case enumDirection.right: {
|
case enumDirection.right: {
|
||||||
const arcProgress = progress * 0.5 * Math_PI;
|
assert(progress <= curvedBeltLength + 0.02, "Invalid progress 2: " + progress);
|
||||||
|
const arcProgress = (progress / curvedBeltLength) * 0.5 * Math_PI;
|
||||||
return new Vector(0.5 - 0.5 * Math_cos(arcProgress), 0.5 - 0.5 * Math_sin(arcProgress));
|
return new Vector(0.5 - 0.5 * Math_cos(arcProgress), 0.5 - 0.5 * Math_sin(arcProgress));
|
||||||
}
|
}
|
||||||
case enumDirection.left: {
|
case enumDirection.left: {
|
||||||
const arcProgress = progress * 0.5 * Math_PI;
|
assert(progress <= curvedBeltLength + 0.02, "Invalid progress 3: " + progress);
|
||||||
|
const arcProgress = (progress / curvedBeltLength) * 0.5 * Math_PI;
|
||||||
return new Vector(-0.5 + 0.5 * Math_cos(arcProgress), 0.5 - 0.5 * Math_sin(arcProgress));
|
return new Vector(-0.5 + 0.5 * Math_cos(arcProgress), 0.5 - 0.5 * Math_sin(arcProgress));
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -65,46 +78,4 @@ export class BeltComponent extends Component {
|
|||||||
return new Vector(0, 0);
|
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];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -194,4 +194,17 @@ export class ItemEjectorComponent extends Component {
|
|||||||
this.slots[slotIndex].progress = this.instantEject ? 1 : 0;
|
this.slots[slotIndex].progress = this.instantEject ? 1 : 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the given slot and returns the item it had
|
||||||
|
* @param {number} slotIndex
|
||||||
|
* @returns {BaseItem|null}
|
||||||
|
*/
|
||||||
|
takeSlotItem(slotIndex) {
|
||||||
|
const slot = this.slots[slotIndex];
|
||||||
|
const item = slot.item;
|
||||||
|
slot.item = null;
|
||||||
|
slot.progress = 0.0;
|
||||||
|
return item;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -418,6 +418,10 @@ export class GameCore {
|
|||||||
root.map.drawStaticEntityDebugOverlays(params);
|
root.map.drawStaticEntityDebugOverlays(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (G_IS_DEV && globalConfig.debug.renderBeltPaths) {
|
||||||
|
systems.belt.drawBeltPathDebug(params);
|
||||||
|
}
|
||||||
|
|
||||||
// END OF GAME CONTENT
|
// END OF GAME CONTENT
|
||||||
// -----
|
// -----
|
||||||
|
|
||||||
|
@ -74,8 +74,11 @@ export class GameHUD {
|
|||||||
shapeViewer: new HUDShapeViewer(this.root),
|
shapeViewer: new HUDShapeViewer(this.root),
|
||||||
wiresOverlay: new HUDWiresOverlay(this.root),
|
wiresOverlay: new HUDWiresOverlay(this.root),
|
||||||
|
|
||||||
|
// Typing hints
|
||||||
|
/* typehints:start */
|
||||||
/** @type {HUDChangesDebugger} */
|
/** @type {HUDChangesDebugger} */
|
||||||
changesDebugger: null,
|
changesDebugger: null,
|
||||||
|
/* typehints:end */
|
||||||
};
|
};
|
||||||
|
|
||||||
this.signals = {
|
this.signals = {
|
||||||
|
@ -9,7 +9,7 @@ import { KEYMAPPINGS } from "../../key_action_mapper";
|
|||||||
import { blueprintShape } from "../../upgrades";
|
import { blueprintShape } from "../../upgrades";
|
||||||
import { BaseHUDPart } from "../base_hud_part";
|
import { BaseHUDPart } from "../base_hud_part";
|
||||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
||||||
import { Blueprint } from "./blueprint";
|
import { Blueprint } from "../../blueprint";
|
||||||
import { SOUNDS } from "../../../platform/sound";
|
import { SOUNDS } from "../../../platform/sound";
|
||||||
|
|
||||||
export class HUDBlueprintPlacer extends BaseHUDPart {
|
export class HUDBlueprintPlacer extends BaseHUDPart {
|
||||||
|
@ -48,9 +48,9 @@ export class HUDColorBlindHelper extends BaseHUDPart {
|
|||||||
|
|
||||||
// Check if the belt has a color item
|
// Check if the belt has a color item
|
||||||
if (beltComp) {
|
if (beltComp) {
|
||||||
const firstItem = beltComp.sortedItems[0];
|
const item = beltComp.assignedPath.findItemAtTile(tile);
|
||||||
if (firstItem && firstItem[1] instanceof ColorItem) {
|
if (item && item instanceof ColorItem) {
|
||||||
return firstItem[1].color;
|
return item.color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ export class HUDChangesDebugger extends BaseHUDPart {
|
|||||||
* @param {string} fillColor Color to display (Hex)
|
* @param {string} fillColor Color to display (Hex)
|
||||||
* @param {number=} timeToDisplay How long to display the change
|
* @param {number=} timeToDisplay How long to display the change
|
||||||
*/
|
*/
|
||||||
renderChange(label, area, fillColor, timeToDisplay = 2) {
|
renderChange(label, area, fillColor, timeToDisplay = 0.3) {
|
||||||
this.changes.push({
|
this.changes.push({
|
||||||
label,
|
label,
|
||||||
area: area.clone(),
|
area: area.clone(),
|
||||||
|
@ -196,7 +196,7 @@ export class GameLogic {
|
|||||||
* @param {function} operation
|
* @param {function} operation
|
||||||
*/
|
*/
|
||||||
performBulkOperation(operation) {
|
performBulkOperation(operation) {
|
||||||
logger.log("Running bulk operation ...");
|
logger.warn("Running bulk operation ...");
|
||||||
assert(!this.root.bulkOperationRunning, "Can not run two bulk operations twice");
|
assert(!this.root.bulkOperationRunning, "Can not run two bulk operations twice");
|
||||||
this.root.bulkOperationRunning = true;
|
this.root.bulkOperationRunning = true;
|
||||||
const now = performanceNow();
|
const now = performanceNow();
|
||||||
@ -227,6 +227,7 @@ export class GameLogic {
|
|||||||
}
|
}
|
||||||
this.root.map.removeStaticEntity(building);
|
this.root.map.removeStaticEntity(building);
|
||||||
this.root.entityMgr.destroyEntity(building);
|
this.root.entityMgr.destroyEntity(building);
|
||||||
|
this.root.entityMgr.processDestroyList();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,21 +1,18 @@
|
|||||||
import { Math_sqrt, Math_max } from "../../core/builtins";
|
import { Math_sqrt } from "../../core/builtins";
|
||||||
import { globalConfig } from "../../core/config";
|
import { globalConfig } from "../../core/config";
|
||||||
import { DrawParameters } from "../../core/draw_parameters";
|
import { DrawParameters } from "../../core/draw_parameters";
|
||||||
import { gMetaBuildingRegistry } from "../../core/global_registries";
|
|
||||||
import { Loader } from "../../core/loader";
|
import { Loader } from "../../core/loader";
|
||||||
import { createLogger } from "../../core/logging";
|
import { createLogger } from "../../core/logging";
|
||||||
import { Rectangle } from "../../core/rectangle";
|
|
||||||
import { AtlasSprite } from "../../core/sprites";
|
import { AtlasSprite } from "../../core/sprites";
|
||||||
import { enumDirection, enumDirectionToVector, enumInvertedDirections, Vector } from "../../core/vector";
|
import { enumDirection, enumDirectionToVector, enumInvertedDirections } from "../../core/vector";
|
||||||
import { MetaBeltBaseBuilding } from "../buildings/belt_base";
|
import { BeltPath } from "../belt_path";
|
||||||
import { BeltComponent } from "../components/belt";
|
import { BeltComponent } from "../components/belt";
|
||||||
import { Entity } from "../entity";
|
import { Entity } from "../entity";
|
||||||
import { GameSystemWithFilter } from "../game_system_with_filter";
|
import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||||
import { MapChunkView } from "../map_chunk_view";
|
import { MapChunkView } from "../map_chunk_view";
|
||||||
import { defaultBuildingVariant } from "../meta_building";
|
import { fastArrayDeleteValue } from "../../core/utils";
|
||||||
|
|
||||||
export const BELT_ANIM_COUNT = 28;
|
export const BELT_ANIM_COUNT = 28;
|
||||||
const SQRT_2 = Math_sqrt(2);
|
|
||||||
|
|
||||||
const logger = createLogger("belt");
|
const logger = createLogger("belt");
|
||||||
|
|
||||||
@ -52,10 +49,53 @@ export class BeltSystem extends GameSystemWithFilter {
|
|||||||
|
|
||||||
this.root.signals.entityAdded.add(this.updateSurroundingBeltPlacement, this);
|
this.root.signals.entityAdded.add(this.updateSurroundingBeltPlacement, this);
|
||||||
this.root.signals.entityDestroyed.add(this.updateSurroundingBeltPlacement, this);
|
this.root.signals.entityDestroyed.add(this.updateSurroundingBeltPlacement, this);
|
||||||
this.root.signals.postLoadHook.add(this.computeBeltCache, this);
|
this.root.signals.entityDestroyed.add(this.onEntityDestroyed, this);
|
||||||
|
this.root.signals.entityAdded.add(this.onEntityAdded, this);
|
||||||
|
|
||||||
/** @type {Rectangle} */
|
// /** @type {Rectangle} */
|
||||||
this.areaToRecompute = null;
|
// this.areaToRecompute = null;
|
||||||
|
|
||||||
|
/** @type {Array<BeltPath>} */
|
||||||
|
this.beltPaths = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -72,10 +112,7 @@ export class BeltSystem extends GameSystemWithFilter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entity.components.Belt) {
|
/*
|
||||||
this.cacheNeedsUpdate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const metaBelt = gMetaBuildingRegistry.findByClass(MetaBeltBaseBuilding);
|
const metaBelt = gMetaBuildingRegistry.findByClass(MetaBeltBaseBuilding);
|
||||||
|
|
||||||
// Compute affected area
|
// Compute affected area
|
||||||
@ -85,6 +122,8 @@ export class BeltSystem extends GameSystemWithFilter {
|
|||||||
// Store if anything got changed, if so we need to queue a recompute
|
// Store if anything got changed, if so we need to queue a recompute
|
||||||
let anythingChanged = false;
|
let anythingChanged = false;
|
||||||
|
|
||||||
|
anythingChanged = true; // TODO / FIXME
|
||||||
|
|
||||||
for (let x = affectedArea.x; x < affectedArea.right(); ++x) {
|
for (let x = affectedArea.x; x < affectedArea.right(); ++x) {
|
||||||
for (let y = affectedArea.y; y < affectedArea.bottom(); ++y) {
|
for (let y = affectedArea.y; y < affectedArea.bottom(); ++y) {
|
||||||
if (!originalRect.containsPoint(x, y)) {
|
if (!originalRect.containsPoint(x, y)) {
|
||||||
@ -121,10 +160,133 @@ export class BeltSystem extends GameSystemWithFilter {
|
|||||||
logger.log("Queuing recompute:", this.areaToRecompute);
|
logger.log("Queuing recompute:", this.areaToRecompute);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME
|
||||||
|
this.areaToRecompute = new Rectangle(-1000, -1000, 2000, 2000);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an entity got destroyed
|
||||||
|
* @param {Entity} entity
|
||||||
|
*/
|
||||||
|
onEntityDestroyed(entity) {
|
||||||
|
if (!this.root.gameInitialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!entity.components.Belt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const assignedPath = entity.components.Belt.assignedPath;
|
||||||
|
assert(assignedPath, "Entity has no belt path assigned");
|
||||||
|
this.deleteEntityFromPath(assignedPath, entity);
|
||||||
|
this.verifyBeltPaths();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to delete the belt from its current path
|
||||||
|
* @param {BeltPath} path
|
||||||
|
* @param {Entity} entity
|
||||||
|
*/
|
||||||
|
deleteEntityFromPath(path, entity) {
|
||||||
|
if (path.entityPath.length === 1) {
|
||||||
|
// This is a single entity path, easy to do, simply erase whole path
|
||||||
|
fastArrayDeleteValue(this.beltPaths, path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notice: Since there might be circular references, it is important to check
|
||||||
|
// which role the entity has
|
||||||
|
if (path.isStartEntity(entity)) {
|
||||||
|
// We tried to delete the start
|
||||||
|
path.deleteEntityOnStart(entity);
|
||||||
|
} else if (path.isEndEntity(entity)) {
|
||||||
|
// We tried to delete the end
|
||||||
|
path.deleteEntityOnEnd(entity);
|
||||||
|
} else {
|
||||||
|
// We tried to delete something inbetween
|
||||||
|
const newPath = path.deleteEntityOnPathSplitIntoTwo(entity);
|
||||||
|
this.beltPaths.push(newPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an entity got added
|
||||||
|
* @param {Entity} entity
|
||||||
|
*/
|
||||||
|
onEntityAdded(entity) {
|
||||||
|
if (!this.root.gameInitialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!entity.components.Belt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fromEntity = this.findSupplyingEntity(entity);
|
||||||
|
const toEntity = this.findFollowUpEntity(entity);
|
||||||
|
|
||||||
|
// Check if we can add the entity to the previous path
|
||||||
|
if (fromEntity) {
|
||||||
|
const fromPath = fromEntity.components.Belt.assignedPath;
|
||||||
|
fromPath.extendOnEnd(entity);
|
||||||
|
|
||||||
|
// Check if we now can extend the current path by the next path
|
||||||
|
if (toEntity) {
|
||||||
|
const toPath = toEntity.components.Belt.assignedPath;
|
||||||
|
|
||||||
|
if (fromPath === toPath) {
|
||||||
|
// This is a circular dependency -> Ignore
|
||||||
|
} else {
|
||||||
|
fromPath.extendByPath(toPath);
|
||||||
|
|
||||||
|
// Delete now obsolete path
|
||||||
|
fastArrayDeleteValue(this.beltPaths, toPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (toEntity) {
|
||||||
|
// Prepend it to the other path
|
||||||
|
const toPath = toEntity.components.Belt.assignedPath;
|
||||||
|
toPath.extendOnBeginning(entity);
|
||||||
|
} else {
|
||||||
|
// This is an empty belt path
|
||||||
|
const path = new BeltPath(this.root, [entity]);
|
||||||
|
this.beltPaths.push(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.verifyBeltPaths();
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(parameters) {
|
draw(parameters) {
|
||||||
this.forEachMatchingEntityOnScreen(parameters, this.drawEntityItems.bind(this));
|
for (let i = 0; i < this.beltPaths.length; ++i) {
|
||||||
|
this.beltPaths[i].draw(parameters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies all belt paths
|
||||||
|
*/
|
||||||
|
verifyBeltPaths() {
|
||||||
|
if (G_IS_DEV && true) {
|
||||||
|
for (let i = 0; i < this.beltPaths.length; ++i) {
|
||||||
|
this.beltPaths[i].debug_checkIntegrity("general-verify");
|
||||||
|
}
|
||||||
|
|
||||||
|
const belts = this.root.entityMgr.getAllWithComponent(BeltComponent);
|
||||||
|
for (let i = 0; i < belts.length; ++i) {
|
||||||
|
const path = belts[i].components.Belt.assignedPath;
|
||||||
|
if (!path) {
|
||||||
|
throw new Error("Belt has no path: " + belts[i].uid);
|
||||||
|
}
|
||||||
|
if (this.beltPaths.indexOf(path) < 0) {
|
||||||
|
throw new Error("Path of entity not contained: " + belts[i].uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -166,166 +328,102 @@ export class BeltSystem extends GameSystemWithFilter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recomputes the belt cache
|
* Finds the supplying belt for a given belt. Used for building the dependencies
|
||||||
|
* @param {Entity} entity
|
||||||
*/
|
*/
|
||||||
computeBeltCache() {
|
findSupplyingEntity(entity) {
|
||||||
if (this.areaToRecompute) {
|
const staticComp = entity.components.StaticMapEntity;
|
||||||
logger.log("Updating belt cache by updating area:", this.areaToRecompute);
|
|
||||||
|
|
||||||
if (G_IS_DEV && globalConfig.debug.renderChanges) {
|
const supplyDirection = staticComp.localDirectionToWorld(enumDirection.bottom);
|
||||||
this.root.hud.parts.changesDebugger.renderChange(
|
const supplyVector = enumDirectionToVector[supplyDirection];
|
||||||
"belt-area",
|
|
||||||
this.areaToRecompute,
|
|
||||||
"#00fff6"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let x = this.areaToRecompute.x; x < this.areaToRecompute.right(); ++x) {
|
const supplyTile = staticComp.origin.add(supplyVector);
|
||||||
for (let y = this.areaToRecompute.y; y < this.areaToRecompute.bottom(); ++y) {
|
const supplyEntity = this.root.map.getTileContent(supplyTile);
|
||||||
const tile = this.root.map.getTileContentXY(x, y);
|
|
||||||
if (tile && tile.components.Belt) {
|
// Check if theres a belt at the tile we point to
|
||||||
tile.components.Belt.followUpCache = this.findFollowUpEntity(tile);
|
if (supplyEntity) {
|
||||||
|
const supplyBeltComp = supplyEntity.components.Belt;
|
||||||
|
if (supplyBeltComp) {
|
||||||
|
const supplyStatic = supplyEntity.components.StaticMapEntity;
|
||||||
|
const supplyEjector = supplyEntity.components.ItemEjector;
|
||||||
|
|
||||||
|
// Check if the belt accepts items from our direction
|
||||||
|
const ejectorSlots = supplyEjector.slots;
|
||||||
|
for (let i = 0; i < ejectorSlots.length; ++i) {
|
||||||
|
const slot = ejectorSlots[i];
|
||||||
|
const localDirection = supplyStatic.localDirectionToWorld(slot.direction);
|
||||||
|
if (enumInvertedDirections[localDirection] === supplyDirection) {
|
||||||
|
return supplyEntity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
/**
|
||||||
if (this.areaToRecompute) {
|
* Computes the belt path network
|
||||||
this.computeBeltCache();
|
*/
|
||||||
}
|
recomputeAllBeltPaths() {
|
||||||
|
logger.warn("Recomputing all belt paths");
|
||||||
|
const visitedUids = new Set();
|
||||||
|
|
||||||
// Divide by item spacing on belts since we use throughput and not speed
|
const result = [];
|
||||||
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) {
|
for (let i = 0; i < this.allEntities.length; ++i) {
|
||||||
const entity = this.allEntities[i];
|
const entity = this.allEntities[i];
|
||||||
|
if (visitedUids.has(entity.uid)) {
|
||||||
const beltComp = entity.components.Belt;
|
|
||||||
const items = beltComp.sortedItems;
|
|
||||||
|
|
||||||
if (items.length === 0) {
|
|
||||||
// Fast out for performance
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ejectorComp = entity.components.ItemEjector;
|
// Mark entity as visited
|
||||||
let maxProgress = 1;
|
visitedUids.add(entity.uid);
|
||||||
|
|
||||||
/* PERFORMANCE OPTIMIZATION */
|
// Compute path, start with entity and find precedors / successors
|
||||||
// Original:
|
const path = [entity];
|
||||||
// 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
|
let maxIter = 9999;
|
||||||
// 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
|
// Find precedors
|
||||||
// assert(maxProgress >= 0.0, "max progress < 0 (I) (" + maxProgress + ")");
|
let prevEntity = this.findSupplyingEntity(entity);
|
||||||
|
while (prevEntity && --maxIter > 0) {
|
||||||
|
if (visitedUids.has(prevEntity.uid)) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
path.unshift(prevEntity);
|
||||||
|
visitedUids.add(prevEntity.uid);
|
||||||
|
prevEntity = this.findSupplyingEntity(prevEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
let speedMultiplier = 1;
|
// Find succedors
|
||||||
if (beltComp.direction !== enumDirection.top) {
|
let nextEntity = this.findFollowUpEntity(entity);
|
||||||
// Curved belts are shorter, thus being quicker (Looks weird otherwise)
|
while (nextEntity && --maxIter > 0) {
|
||||||
speedMultiplier = SQRT_2;
|
if (visitedUids.has(nextEntity.uid)) {
|
||||||
}
|
break;
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
path.push(nextEntity);
|
||||||
|
visitedUids.add(nextEntity.uid);
|
||||||
|
nextEntity = this.findFollowUpEntity(nextEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(maxIter > 1, "Ran out of iterations");
|
||||||
|
result.push(new BeltPath(this.root, path));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.log("Found", this.beltPaths.length, "belt paths");
|
||||||
|
this.beltPaths = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
this.verifyBeltPaths();
|
||||||
|
|
||||||
|
for (let i = 0; i < this.beltPaths.length; ++i) {
|
||||||
|
this.beltPaths[i].update();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.verifyBeltPaths();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -368,38 +466,12 @@ export class BeltSystem extends GameSystemWithFilter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Draws the belt parameters
|
||||||
* @param {DrawParameters} parameters
|
* @param {DrawParameters} parameters
|
||||||
* @param {Entity} entity
|
|
||||||
*/
|
*/
|
||||||
drawEntityItems(parameters, entity) {
|
drawBeltPathDebug(parameters) {
|
||||||
const beltComp = entity.components.Belt;
|
for (let i = 0; i < this.beltPaths.length; ++i) {
|
||||||
const staticComp = entity.components.StaticMapEntity;
|
this.beltPaths[i].drawDebug(parameters);
|
||||||
|
|
||||||
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
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -239,9 +239,9 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
|
|||||||
|
|
||||||
const beltComp = receiver.components.Belt;
|
const beltComp = receiver.components.Belt;
|
||||||
if (beltComp) {
|
if (beltComp) {
|
||||||
// Ayy, its a belt!
|
const path = beltComp.assignedPath;
|
||||||
if (beltComp.canAcceptItem()) {
|
assert(path, "belt has no path");
|
||||||
beltComp.takeItem(item);
|
if (path.tryAcceptItem(item)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import { SavegameInterface_V1001 } from "./schemas/1001";
|
|||||||
import { SavegameInterface_V1002 } from "./schemas/1002";
|
import { SavegameInterface_V1002 } from "./schemas/1002";
|
||||||
import { SavegameInterface_V1003 } from "./schemas/1003";
|
import { SavegameInterface_V1003 } from "./schemas/1003";
|
||||||
import { SavegameInterface_V1004 } from "./schemas/1004";
|
import { SavegameInterface_V1004 } from "./schemas/1004";
|
||||||
|
import { SavegameInterface_V1005 } from "./schemas/1005";
|
||||||
|
|
||||||
const logger = createLogger("savegame");
|
const logger = createLogger("savegame");
|
||||||
|
|
||||||
@ -45,7 +46,7 @@ export class Savegame extends ReadWriteProxy {
|
|||||||
* @returns {number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
static getCurrentVersion() {
|
static getCurrentVersion() {
|
||||||
return 1004;
|
return 1005;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -104,6 +105,11 @@ export class Savegame extends ReadWriteProxy {
|
|||||||
data.version = 1004;
|
data.version = 1004;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data.version === 1004) {
|
||||||
|
SavegameInterface_V1005.migrate1004to1005(data);
|
||||||
|
data.version = 1005;
|
||||||
|
}
|
||||||
|
|
||||||
return ExplainedResult.good();
|
return ExplainedResult.good();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import { SavegameInterface_V1001 } from "./schemas/1001";
|
|||||||
import { SavegameInterface_V1002 } from "./schemas/1002";
|
import { SavegameInterface_V1002 } from "./schemas/1002";
|
||||||
import { SavegameInterface_V1003 } from "./schemas/1003";
|
import { SavegameInterface_V1003 } from "./schemas/1003";
|
||||||
import { SavegameInterface_V1004 } from "./schemas/1004";
|
import { SavegameInterface_V1004 } from "./schemas/1004";
|
||||||
|
import { SavegameInterface_V1005 } from "./schemas/1005";
|
||||||
|
|
||||||
/** @type {Object.<number, typeof BaseSavegameInterface>} */
|
/** @type {Object.<number, typeof BaseSavegameInterface>} */
|
||||||
export const savegameInterfaces = {
|
export const savegameInterfaces = {
|
||||||
@ -13,6 +14,7 @@ export const savegameInterfaces = {
|
|||||||
1002: SavegameInterface_V1002,
|
1002: SavegameInterface_V1002,
|
||||||
1003: SavegameInterface_V1003,
|
1003: SavegameInterface_V1003,
|
||||||
1004: SavegameInterface_V1004,
|
1004: SavegameInterface_V1004,
|
||||||
|
1005: SavegameInterface_V1005,
|
||||||
};
|
};
|
||||||
|
|
||||||
const logger = createLogger("savegame_interface_registry");
|
const logger = createLogger("savegame_interface_registry");
|
||||||
|
@ -40,6 +40,7 @@ export class SavegameSerializer {
|
|||||||
hubGoals: root.hubGoals.serialize(),
|
hubGoals: root.hubGoals.serialize(),
|
||||||
pinnedShapes: root.hud.parts.pinnedShapes.serialize(),
|
pinnedShapes: root.hud.parts.pinnedShapes.serialize(),
|
||||||
waypoints: root.hud.parts.waypoints.serialize(),
|
waypoints: root.hud.parts.waypoints.serialize(),
|
||||||
|
beltPaths: root.systemMgr.systems.belt.serializePaths(),
|
||||||
};
|
};
|
||||||
|
|
||||||
data.entities = this.internal.serializeEntityArray(root.entityMgr.entities);
|
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.pinnedShapes.deserialize(savegame.pinnedShapes);
|
||||||
errorReason = errorReason || root.hud.parts.waypoints.deserialize(savegame.waypoints);
|
errorReason = errorReason || root.hud.parts.waypoints.deserialize(savegame.waypoints);
|
||||||
errorReason = errorReason || this.internal.deserializeEntityArray(root, savegame.entities);
|
errorReason = errorReason || this.internal.deserializeEntityArray(root, savegame.entities);
|
||||||
|
errorReason = errorReason || root.systemMgr.systems.belt.deserializePaths(savegame.beltPaths);
|
||||||
|
|
||||||
// Check for errors
|
// Check for errors
|
||||||
if (errorReason) {
|
if (errorReason) {
|
||||||
|
@ -5,6 +5,10 @@ import { Entity } from "../game/entity";
|
|||||||
* }} SavegameStats
|
* }} SavegameStats
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* camera: any,
|
* camera: any,
|
||||||
@ -14,7 +18,8 @@ import { Entity } from "../game/entity";
|
|||||||
* hubGoals: any,
|
* hubGoals: any,
|
||||||
* pinnedShapes: any,
|
* pinnedShapes: any,
|
||||||
* waypoints: any,
|
* waypoints: any,
|
||||||
* entities: Array<Entity>
|
* entities: Array<Entity>,
|
||||||
|
* beltPaths: Array<any>
|
||||||
* }} SerializedGame
|
* }} 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