mirror of
https://github.com/tobspr/shapez.io.git
synced 2024-10-27 20:34:29 +00:00
Vastly improve belt performance
This commit is contained in:
parent
31eeeab8ce
commit
6f28aff78f
@ -1,14 +1,14 @@
|
|||||||
import { globalConfig } from "../core/config";
|
import { globalConfig } from "../core/config";
|
||||||
import { DrawParameters } from "../core/draw_parameters";
|
import { DrawParameters } from "../core/draw_parameters";
|
||||||
|
import { gItemRegistry } from "../core/global_registries";
|
||||||
import { createLogger } from "../core/logging";
|
import { createLogger } from "../core/logging";
|
||||||
|
import { Rectangle } from "../core/rectangle";
|
||||||
import { epsilonCompare, round4Digits } from "../core/utils";
|
import { epsilonCompare, round4Digits } from "../core/utils";
|
||||||
import { Vector } from "../core/vector";
|
import { enumDirection, enumDirectionToVector, Vector } from "../core/vector";
|
||||||
|
import { BasicSerializableObject, types } from "../savegame/serialization";
|
||||||
import { BaseItem } from "./base_item";
|
import { BaseItem } from "./base_item";
|
||||||
import { Entity } from "./entity";
|
import { Entity } from "./entity";
|
||||||
import { GameRoot, enumLayer } from "./root";
|
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");
|
const logger = createLogger("belt_path");
|
||||||
|
|
||||||
@ -125,14 +125,6 @@ export class BeltPath extends BasicSerializableObject {
|
|||||||
return globalConfig.beltItemSpacingByLayer[this.layer];
|
return globalConfig.beltItemSpacingByLayer[this.layer];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the layer of the this path
|
|
||||||
* @returns {enumLayer}
|
|
||||||
*/
|
|
||||||
getLayer() {
|
|
||||||
return this.entityPath[0].layer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tries to accept the item
|
* Tries to accept the item
|
||||||
* @param {BaseItem} item
|
* @param {BaseItem} item
|
||||||
@ -167,7 +159,7 @@ export class BeltPath extends BasicSerializableObject {
|
|||||||
* @returns {BaseItem|null}
|
* @returns {BaseItem|null}
|
||||||
*/
|
*/
|
||||||
findItemAtTile(tile) {
|
findItemAtTile(tile) {
|
||||||
// TODO: This breaks color blind mode otherwise
|
// @TODO: This breaks color blind mode otherwise
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,27 +178,82 @@ export class BeltPath extends BasicSerializableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates all ejectors on the path, so that only the last ejector
|
* Recomputes the layer of the path and the target acceptor
|
||||||
*/
|
*/
|
||||||
onPathChanged() {
|
onPathChanged() {
|
||||||
this.ejectorComp = this.entityPath[this.entityPath.length - 1].components.ItemEjector;
|
this.layer = this.entityPath[0].layer;
|
||||||
this.ejectorSlot = this.ejectorComp.slots[0];
|
this.acceptorTarget = this.computeAcceptingEntityAndSlot();
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = 0; i < this.entityPath.length; ++i) {
|
/**
|
||||||
const ejectorComp = this.entityPath[i].components.ItemEjector;
|
* Called by the belt system when the surroundings changed
|
||||||
const isLast = i === this.entityPath.length - 1;
|
*/
|
||||||
ejectorComp.enabled = isLast;
|
onSurroundingsChanged() {
|
||||||
|
this.onPathChanged();
|
||||||
|
}
|
||||||
|
|
||||||
// Clear all slots of non-end entities
|
/**
|
||||||
if (!isLast) {
|
* Finds the entity which accepts our items
|
||||||
for (let k = 0; k < ejectorComp.slots.length; ++k) {
|
* @return {{ entity: Entity, slot: number }}
|
||||||
ejectorComp.slots[k].item = null;
|
*/
|
||||||
ejectorComp.slots[k].progress = 0.0;
|
computeAcceptingEntityAndSlot() {
|
||||||
|
const lastEntity = this.entityPath[this.entityPath.length - 1];
|
||||||
|
const lastStatic = lastEntity.components.StaticMapEntity;
|
||||||
|
const lastBeltComp = lastEntity.components.Belt;
|
||||||
|
|
||||||
|
// Figure out where and into which direction we eject items
|
||||||
|
const ejectSlotWsTile = lastStatic.localTileToWorld(new Vector(0, 0));
|
||||||
|
const ejectSlotWsDirection = lastStatic.localDirectionToWorld(lastBeltComp.direction);
|
||||||
|
const ejectSlotWsDirectionVector = enumDirectionToVector[ejectSlotWsDirection];
|
||||||
|
const ejectSlotTargetWsTile = ejectSlotWsTile.add(ejectSlotWsDirectionVector);
|
||||||
|
|
||||||
|
// Try to find the given acceptor component to take the item
|
||||||
|
// Since there can be cross layer dependencies, check on all layers
|
||||||
|
const targetEntities = this.root.map.getLayersContentsMultipleXY(
|
||||||
|
ejectSlotTargetWsTile.x,
|
||||||
|
ejectSlotTargetWsTile.y
|
||||||
|
);
|
||||||
|
|
||||||
|
for (let i = 0; i < targetEntities.length; ++i) {
|
||||||
|
const targetEntity = targetEntities[i];
|
||||||
|
|
||||||
|
const targetStaticComp = targetEntity.components.StaticMapEntity;
|
||||||
|
const targetBeltComp = targetEntity.components.Belt;
|
||||||
|
|
||||||
|
// Check for belts (special case)
|
||||||
|
if (targetBeltComp) {
|
||||||
|
const beltAcceptingDirection = targetStaticComp.localDirectionToWorld(enumDirection.top);
|
||||||
|
if (ejectSlotWsDirection === beltAcceptingDirection) {
|
||||||
|
return {
|
||||||
|
entity: targetEntity,
|
||||||
|
slot: 0,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
this.layer = this.entityPath[0].layer;
|
// Check for item acceptors
|
||||||
|
const targetAcceptorComp = targetEntity.components.ItemAcceptor;
|
||||||
|
if (!targetAcceptorComp) {
|
||||||
|
// Entity doesn't accept items
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const matchingSlot = targetAcceptorComp.findMatchingSlot(
|
||||||
|
targetStaticComp.worldToLocalTile(ejectSlotTargetWsTile),
|
||||||
|
targetStaticComp.worldDirectionToLocal(ejectSlotWsDirection),
|
||||||
|
lastEntity.layer
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!matchingSlot) {
|
||||||
|
// No matching slot found
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
entity: targetEntity,
|
||||||
|
slot: matchingSlot.index,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Following code will be compiled out outside of dev versions
|
// Following code will be compiled out outside of dev versions
|
||||||
@ -251,11 +298,6 @@ export class BeltPath extends BasicSerializableObject {
|
|||||||
return fail("Reference to destroyed entity " + entity.uid);
|
return fail("Reference to destroyed entity " + entity.uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
const enabledState = i === this.entityPath.length - 1;
|
|
||||||
if (entity.components.ItemEjector.enabled !== enabledState) {
|
|
||||||
return fail("Item ejector enabled state is not synchronized (index =" + i + ")");
|
|
||||||
}
|
|
||||||
|
|
||||||
const followUp = this.root.systemMgr.systems.belt.findFollowUpEntity(entity);
|
const followUp = this.root.systemMgr.systems.belt.findFollowUpEntity(entity);
|
||||||
if (!followUp) {
|
if (!followUp) {
|
||||||
return fail(
|
return fail(
|
||||||
@ -283,20 +325,6 @@ export class BeltPath extends BasicSerializableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for right ejector component and slot
|
|
||||||
if (this.ejectorComp !== this.entityPath[this.entityPath.length - 1].components.ItemEjector) {
|
|
||||||
return fail("Stale ejectorComp handle");
|
|
||||||
}
|
|
||||||
if (this.ejectorSlot !== this.ejectorComp.slots[0]) {
|
|
||||||
return fail("Stale ejector slot handle");
|
|
||||||
}
|
|
||||||
if (!this.ejectorComp) {
|
|
||||||
return fail("Ejector comp not set");
|
|
||||||
}
|
|
||||||
if (!this.ejectorSlot) {
|
|
||||||
return fail("Ejector slot not set");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check spacing
|
// Check spacing
|
||||||
if (this.spacingToFirstItem > this.totalLength + 0.005) {
|
if (this.spacingToFirstItem > this.totalLength + 0.005) {
|
||||||
return fail(
|
return fail(
|
||||||
@ -370,14 +398,6 @@ export class BeltPath extends BasicSerializableObject {
|
|||||||
|
|
||||||
const beltComp = entity.components.Belt;
|
const beltComp = entity.components.Belt;
|
||||||
|
|
||||||
// If the last belt has something on its ejector, put that into the path first
|
|
||||||
const pendingItem = this.ejectorComp.takeSlotItem(0);
|
|
||||||
if (pendingItem) {
|
|
||||||
// Ok, so we have a pending item
|
|
||||||
DEBUG && logger.log("Taking pending item and putting it back on the path");
|
|
||||||
this.items.push([0, pendingItem]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append the entity
|
// Append the entity
|
||||||
this.entityPath.push(entity);
|
this.entityPath.push(entity);
|
||||||
this.onPathChanged();
|
this.onPathChanged();
|
||||||
@ -970,7 +990,7 @@ export class BeltPath extends BasicSerializableObject {
|
|||||||
beltSpeed *= 100;
|
beltSpeed *= 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
let minimumDistance = this.ejectorSlot.item ? this.getItemSpacing() : 0;
|
let minimumDistance = 0;
|
||||||
|
|
||||||
// Try to reduce spacing
|
// Try to reduce spacing
|
||||||
let remainingAmount = beltSpeed;
|
let remainingAmount = beltSpeed;
|
||||||
@ -994,10 +1014,17 @@ export class BeltPath extends BasicSerializableObject {
|
|||||||
minimumDistance = this.getItemSpacing();
|
minimumDistance = this.getItemSpacing();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if we have an item which is ready to be emitted
|
||||||
const lastItem = this.items[this.items.length - 1];
|
const lastItem = this.items[this.items.length - 1];
|
||||||
if (lastItem && lastItem[_nextDistance] === 0) {
|
if (lastItem && lastItem[_nextDistance] === 0 && this.acceptorTarget) {
|
||||||
// Take over
|
// Pass over the item
|
||||||
if (this.ejectorComp.tryEject(0, lastItem[_item])) {
|
if (
|
||||||
|
this.root.systemMgr.systems.itemEjector.tryPassOverItem(
|
||||||
|
lastItem[_item],
|
||||||
|
this.acceptorTarget.entity,
|
||||||
|
this.acceptorTarget.slot
|
||||||
|
)
|
||||||
|
) {
|
||||||
this.items.pop();
|
this.items.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1134,7 +1161,7 @@ export class BeltPath extends BasicSerializableObject {
|
|||||||
const beltLength = beltComp.getEffectiveLengthTiles(this.layer);
|
const beltLength = beltComp.getEffectiveLengthTiles(this.layer);
|
||||||
|
|
||||||
// Check if the current items are on the belt
|
// Check if the current items are on the belt
|
||||||
while (trackPos + beltLength >= currentItemPos) {
|
while (trackPos + beltLength >= currentItemPos - 1e-51) {
|
||||||
// Its on the belt, render it now
|
// Its on the belt, render it now
|
||||||
const staticComp = entity.components.StaticMapEntity;
|
const staticComp = entity.components.StaticMapEntity;
|
||||||
assert(
|
assert(
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import { MetaBuilding, defaultBuildingVariant } from "./meta_building";
|
/* typehints:start */
|
||||||
|
import { MetaBuilding } from "./meta_building";
|
||||||
import { AtlasSprite } from "../core/sprites";
|
import { AtlasSprite } from "../core/sprites";
|
||||||
|
/* typehints:end */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
@ -22,13 +24,18 @@ export const gBuildingVariants = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Registers a new variant
|
||||||
* @param {*} id
|
* @param {number} id
|
||||||
* @param {*} meta
|
* @param {typeof MetaBuilding} meta
|
||||||
* @param {*} variant
|
* @param {string} variant
|
||||||
* @param {*} rotationVariant
|
* @param {number} rotationVariant
|
||||||
*/
|
*/
|
||||||
export function registerBuildingVariant(id, meta, variant = defaultBuildingVariant, rotationVariant = 0) {
|
export function registerBuildingVariant(
|
||||||
|
id,
|
||||||
|
meta,
|
||||||
|
variant = "default" /* FIXME: Circular dependency, actually its defaultBuildingVariant */,
|
||||||
|
rotationVariant = 0
|
||||||
|
) {
|
||||||
assert(!gBuildingVariants[id], "Duplicate id: " + id);
|
assert(!gBuildingVariants[id], "Duplicate id: " + id);
|
||||||
gBuildingVariants[id] = {
|
gBuildingVariants[id] = {
|
||||||
metaClass: meta,
|
metaClass: meta,
|
||||||
|
@ -53,34 +53,8 @@ export class MetaBeltBaseBuilding extends MetaBuilding {
|
|||||||
direction: enumDirection.top, // updated later
|
direction: enumDirection.top, // updated later
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
// Make this entity replaceabel
|
// Make this entity replaceable
|
||||||
entity.addComponent(new ReplaceableMapEntityComponent());
|
entity.addComponent(new ReplaceableMapEntityComponent());
|
||||||
|
|
||||||
entity.addComponent(
|
|
||||||
new ItemAcceptorComponent({
|
|
||||||
slots: [
|
|
||||||
{
|
|
||||||
pos: new Vector(0, 0),
|
|
||||||
directions: [enumDirection.bottom],
|
|
||||||
layer: this.getLayer(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
animated: false,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
entity.addComponent(
|
|
||||||
new ItemEjectorComponent({
|
|
||||||
slots: [
|
|
||||||
{
|
|
||||||
pos: new Vector(0, 0),
|
|
||||||
direction: enumDirection.top, // updated later
|
|
||||||
layer: this.getLayer(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
instantEject: true,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -90,7 +64,6 @@ export class MetaBeltBaseBuilding extends MetaBuilding {
|
|||||||
*/
|
*/
|
||||||
updateVariants(entity, rotationVariant) {
|
updateVariants(entity, rotationVariant) {
|
||||||
entity.components.Belt.direction = arrayBeltVariantToRotation[rotationVariant];
|
entity.components.Belt.direction = arrayBeltVariantToRotation[rotationVariant];
|
||||||
entity.components.ItemEjector.slots[0].direction = arrayBeltVariantToRotation[rotationVariant];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -7,6 +7,40 @@ import { enumLayer } from "../root";
|
|||||||
|
|
||||||
export const curvedBeltLength = /* Math.PI / 4 */ 0.78;
|
export const curvedBeltLength = /* Math.PI / 4 */ 0.78;
|
||||||
|
|
||||||
|
/** @type {import("./item_acceptor").ItemAcceptorSlot} */
|
||||||
|
export const FAKE_BELT_ACCEPTOR_SLOT = {
|
||||||
|
pos: new Vector(0, 0),
|
||||||
|
directions: [enumDirection.bottom],
|
||||||
|
layer: enumLayer.regular,
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @type {Object<enumDirection, import("./item_ejector").ItemEjectorSlot>} */
|
||||||
|
export const FAKE_BELT_EJECTOR_SLOT_BY_DIRECTION = {
|
||||||
|
[enumDirection.top]: {
|
||||||
|
pos: new Vector(0, 0),
|
||||||
|
direction: enumDirection.top,
|
||||||
|
item: null,
|
||||||
|
layer: enumLayer.regular,
|
||||||
|
progress: 0,
|
||||||
|
},
|
||||||
|
|
||||||
|
[enumDirection.right]: {
|
||||||
|
pos: new Vector(0, 0),
|
||||||
|
direction: enumDirection.right,
|
||||||
|
item: null,
|
||||||
|
layer: enumLayer.regular,
|
||||||
|
progress: 0,
|
||||||
|
},
|
||||||
|
|
||||||
|
[enumDirection.left]: {
|
||||||
|
pos: new Vector(0, 0),
|
||||||
|
direction: enumDirection.left,
|
||||||
|
item: null,
|
||||||
|
layer: enumLayer.regular,
|
||||||
|
progress: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export class BeltComponent extends Component {
|
export class BeltComponent extends Component {
|
||||||
static getId() {
|
static getId() {
|
||||||
return "Belt";
|
return "Belt";
|
||||||
@ -56,6 +90,27 @@ export class BeltComponent extends Component {
|
|||||||
return this.direction === enumDirection.top ? 1.0 : curvedBeltLength;
|
return this.direction === enumDirection.top ? 1.0 : curvedBeltLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns fake acceptor slot used for matching
|
||||||
|
* @returns {import("./item_acceptor").ItemAcceptorSlot}
|
||||||
|
*/
|
||||||
|
getFakeAcceptorSlot() {
|
||||||
|
return FAKE_BELT_ACCEPTOR_SLOT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns fake acceptor slot used for matching
|
||||||
|
* @returns {import("./item_ejector").ItemEjectorSlot}
|
||||||
|
*/
|
||||||
|
getFakeEjectorSlots() {
|
||||||
|
assert(
|
||||||
|
FAKE_BELT_EJECTOR_SLOT_BY_DIRECTION[this.direction],
|
||||||
|
"Invalid belt direction: ",
|
||||||
|
this.direction
|
||||||
|
);
|
||||||
|
return FAKE_BELT_EJECTOR_SLOT_BY_DIRECTION[this.direction];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts from belt space (0 = start of belt ... 1 = end of belt) to the local
|
* Converts from belt space (0 = start of belt ... 1 = end of belt) to the local
|
||||||
* belt coordinates (-0.5|-0.5 to 0.5|0.5)
|
* belt coordinates (-0.5|-0.5 to 0.5|0.5)
|
||||||
|
@ -5,6 +5,7 @@ import { types } from "../../savegame/serialization";
|
|||||||
import { gItemRegistry } from "../../core/global_registries";
|
import { gItemRegistry } from "../../core/global_registries";
|
||||||
import { Entity } from "../entity";
|
import { Entity } from "../entity";
|
||||||
import { enumLayer } from "../root";
|
import { enumLayer } from "../root";
|
||||||
|
import { BeltPath } from "../belt_path";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
@ -14,6 +15,7 @@ import { enumLayer } from "../root";
|
|||||||
* layer: enumLayer,
|
* layer: enumLayer,
|
||||||
* progress: number?,
|
* progress: number?,
|
||||||
* cachedDestSlot?: import("./item_acceptor").ItemAcceptorLocatedSlot,
|
* cachedDestSlot?: import("./item_acceptor").ItemAcceptorLocatedSlot,
|
||||||
|
* cachedBeltPath?: BeltPath,
|
||||||
* cachedTargetEntity?: Entity
|
* cachedTargetEntity?: Entity
|
||||||
* }} ItemEjectorSlot
|
* }} ItemEjectorSlot
|
||||||
*/
|
*/
|
||||||
@ -24,7 +26,7 @@ export class ItemEjectorComponent extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static getSchema() {
|
static getSchema() {
|
||||||
// The cachedDestSlot, cachedTargetEntity, and cachedConnectedSlots fields
|
// The cachedDestSlot, cachedTargetEntity fields
|
||||||
// are not serialized.
|
// are not serialized.
|
||||||
return {
|
return {
|
||||||
instantEject: types.bool,
|
instantEject: types.bool,
|
||||||
@ -73,9 +75,6 @@ export class ItemEjectorComponent extends Component {
|
|||||||
|
|
||||||
this.setSlots(slots);
|
this.setSlots(slots);
|
||||||
|
|
||||||
/** @type {ItemEjectorSlot[]} */
|
|
||||||
this.cachedConnectedSlots = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether this ejector slot is enabled
|
* Whether this ejector slot is enabled
|
||||||
*/
|
*/
|
||||||
|
@ -198,50 +198,66 @@ export class GameLogic {
|
|||||||
for (let i = 0; i < entities.length; ++i) {
|
for (let i = 0; i < entities.length; ++i) {
|
||||||
const entity = entities[i];
|
const entity = entities[i];
|
||||||
|
|
||||||
|
let ejectorSlots = [];
|
||||||
|
let acceptorSlots = [];
|
||||||
|
|
||||||
const staticComp = entity.components.StaticMapEntity;
|
const staticComp = entity.components.StaticMapEntity;
|
||||||
const itemEjector = entity.components.ItemEjector;
|
const itemEjector = entity.components.ItemEjector;
|
||||||
|
const itemAcceptor = entity.components.ItemAcceptor;
|
||||||
|
const beltComp = entity.components.Belt;
|
||||||
|
|
||||||
if (itemEjector) {
|
if (itemEjector) {
|
||||||
for (let ejectorSlot = 0; ejectorSlot < itemEjector.slots.length; ++ejectorSlot) {
|
ejectorSlots = itemEjector.slots.slice();
|
||||||
const slot = itemEjector.slots[ejectorSlot];
|
}
|
||||||
if (slot.layer !== layer) {
|
|
||||||
continue;
|
if (itemAcceptor) {
|
||||||
}
|
acceptorSlots = itemAcceptor.slots.slice();
|
||||||
const wsTile = staticComp.localTileToWorld(slot.pos);
|
}
|
||||||
const wsDirection = staticComp.localDirectionToWorld(slot.direction);
|
|
||||||
const targetTile = wsTile.add(enumDirectionToVector[wsDirection]);
|
if (beltComp) {
|
||||||
if (targetTile.equals(tile)) {
|
const fakeEjectorSlot = beltComp.getFakeEjectorSlots();
|
||||||
ejectors.push({
|
const fakeAcceptorSlot = beltComp.getFakeAcceptorSlot();
|
||||||
entity,
|
ejectorSlots.push(fakeEjectorSlot);
|
||||||
slot,
|
acceptorSlots.push(fakeAcceptorSlot);
|
||||||
fromTile: wsTile,
|
}
|
||||||
toDirection: wsDirection,
|
|
||||||
});
|
for (let ejectorSlot = 0; ejectorSlot < ejectorSlots.length; ++ejectorSlot) {
|
||||||
}
|
const slot = ejectorSlots[ejectorSlot];
|
||||||
|
if (slot.layer !== layer) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const wsTile = staticComp.localTileToWorld(slot.pos);
|
||||||
|
const wsDirection = staticComp.localDirectionToWorld(slot.direction);
|
||||||
|
const targetTile = wsTile.add(enumDirectionToVector[wsDirection]);
|
||||||
|
if (targetTile.equals(tile)) {
|
||||||
|
ejectors.push({
|
||||||
|
entity,
|
||||||
|
slot,
|
||||||
|
fromTile: wsTile,
|
||||||
|
toDirection: wsDirection,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const itemAcceptor = entity.components.ItemAcceptor;
|
for (let acceptorSlot = 0; acceptorSlot < acceptorSlots.length; ++acceptorSlot) {
|
||||||
if (itemAcceptor) {
|
const slot = acceptorSlots[acceptorSlot];
|
||||||
for (let acceptorSlot = 0; acceptorSlot < itemAcceptor.slots.length; ++acceptorSlot) {
|
if (slot.layer !== layer) {
|
||||||
const slot = itemAcceptor.slots[acceptorSlot];
|
continue;
|
||||||
if (slot.layer !== layer) {
|
}
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const wsTile = staticComp.localTileToWorld(slot.pos);
|
const wsTile = staticComp.localTileToWorld(slot.pos);
|
||||||
for (let k = 0; k < slot.directions.length; ++k) {
|
for (let k = 0; k < slot.directions.length; ++k) {
|
||||||
const direction = slot.directions[k];
|
const direction = slot.directions[k];
|
||||||
const wsDirection = staticComp.localDirectionToWorld(direction);
|
const wsDirection = staticComp.localDirectionToWorld(direction);
|
||||||
|
|
||||||
const sourceTile = wsTile.add(enumDirectionToVector[wsDirection]);
|
const sourceTile = wsTile.add(enumDirectionToVector[wsDirection]);
|
||||||
if (sourceTile.equals(tile)) {
|
if (sourceTile.equals(tile)) {
|
||||||
acceptors.push({
|
acceptors.push({
|
||||||
entity,
|
entity,
|
||||||
slot,
|
slot,
|
||||||
toTile: wsTile,
|
toTile: wsTile,
|
||||||
fromDirection: wsDirection,
|
fromDirection: wsDirection,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,6 +137,9 @@ export class BeltSystem extends GameSystemWithFilter {
|
|||||||
const originalRect = staticComp.getTileSpaceBounds();
|
const originalRect = staticComp.getTileSpaceBounds();
|
||||||
const affectedArea = originalRect.expandedInAllDirections(1);
|
const affectedArea = originalRect.expandedInAllDirections(1);
|
||||||
|
|
||||||
|
/** @type {Set<BeltPath>} */
|
||||||
|
const changedPaths = new Set();
|
||||||
|
|
||||||
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)) {
|
||||||
@ -189,10 +192,17 @@ export class BeltSystem extends GameSystemWithFilter {
|
|||||||
// Make sure the chunks know about the update
|
// Make sure the chunks know about the update
|
||||||
this.root.signals.entityChanged.dispatch(targetEntity);
|
this.root.signals.entityChanged.dispatch(targetEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (targetBeltComp.assignedPath) {
|
||||||
|
changedPaths.add(targetBeltComp.assignedPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// notify all paths *afterwards* to avoid multi-updates
|
||||||
|
changedPaths.forEach(path => path.onSurroundingsChanged());
|
||||||
|
|
||||||
if (G_IS_DEV && globalConfig.debug.checkBeltPaths) {
|
if (G_IS_DEV && globalConfig.debug.checkBeltPaths) {
|
||||||
this.debug_verifyBeltPaths();
|
this.debug_verifyBeltPaths();
|
||||||
}
|
}
|
||||||
@ -361,24 +371,10 @@ export class BeltSystem extends GameSystemWithFilter {
|
|||||||
const followUpBeltComp = followUpEntity.components.Belt;
|
const followUpBeltComp = followUpEntity.components.Belt;
|
||||||
if (followUpBeltComp) {
|
if (followUpBeltComp) {
|
||||||
const followUpStatic = followUpEntity.components.StaticMapEntity;
|
const followUpStatic = followUpEntity.components.StaticMapEntity;
|
||||||
const followUpAcceptor = followUpEntity.components.ItemAcceptor;
|
|
||||||
|
|
||||||
// Check if the belt accepts items from our direction
|
const acceptedDirection = followUpStatic.localDirectionToWorld(enumDirection.top);
|
||||||
const acceptorSlots = followUpAcceptor.slots;
|
if (acceptedDirection === followUpDirection) {
|
||||||
for (let i = 0; i < acceptorSlots.length; ++i) {
|
return followUpEntity;
|
||||||
const slot = acceptorSlots[i];
|
|
||||||
|
|
||||||
// Make sure the acceptor slot is on the same layer
|
|
||||||
if (slot.layer !== entity.layer) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let k = 0; k < slot.directions.length; ++k) {
|
|
||||||
const localDirection = followUpStatic.localDirectionToWorld(slot.directions[k]);
|
|
||||||
if (enumInvertedDirections[localDirection] === followUpDirection) {
|
|
||||||
return followUpEntity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -405,21 +401,12 @@ export class BeltSystem extends GameSystemWithFilter {
|
|||||||
const supplyBeltComp = supplyEntity.components.Belt;
|
const supplyBeltComp = supplyEntity.components.Belt;
|
||||||
if (supplyBeltComp) {
|
if (supplyBeltComp) {
|
||||||
const supplyStatic = supplyEntity.components.StaticMapEntity;
|
const supplyStatic = supplyEntity.components.StaticMapEntity;
|
||||||
const supplyEjector = supplyEntity.components.ItemEjector;
|
const otherDirection = supplyStatic.localDirectionToWorld(
|
||||||
|
enumInvertedDirections[supplyBeltComp.direction]
|
||||||
|
);
|
||||||
|
|
||||||
// Check if the belt accepts items from our direction
|
if (otherDirection === supplyDirection) {
|
||||||
const ejectorSlots = supplyEjector.slots;
|
return supplyEntity;
|
||||||
for (let i = 0; i < ejectorSlots.length; ++i) {
|
|
||||||
const slot = ejectorSlots[i];
|
|
||||||
|
|
||||||
// Make sure the ejector slot is on the same layer
|
|
||||||
if (slot.layer !== entity.layer) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const localDirection = supplyStatic.localDirectionToWorld(slot.direction);
|
|
||||||
if (enumInvertedDirections[localDirection] === supplyDirection) {
|
|
||||||
return supplyEntity;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ import { globalConfig } from "../../core/config";
|
|||||||
import { DrawParameters } from "../../core/draw_parameters";
|
import { DrawParameters } from "../../core/draw_parameters";
|
||||||
import { createLogger } from "../../core/logging";
|
import { createLogger } from "../../core/logging";
|
||||||
import { Rectangle } from "../../core/rectangle";
|
import { Rectangle } from "../../core/rectangle";
|
||||||
import { enumDirectionToVector, Vector } from "../../core/vector";
|
import { enumDirection, enumDirectionToVector, Vector } from "../../core/vector";
|
||||||
import { BaseItem, enumItemType, enumItemTypeToLayer } from "../base_item";
|
import { BaseItem, enumItemTypeToLayer } from "../base_item";
|
||||||
import { ItemEjectorComponent } from "../components/item_ejector";
|
import { ItemEjectorComponent } from "../components/item_ejector";
|
||||||
import { Entity } from "../entity";
|
import { Entity } from "../entity";
|
||||||
import { GameSystemWithFilter } from "../game_system_with_filter";
|
import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||||
@ -120,15 +120,13 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
|
|||||||
const ejectorComp = entity.components.ItemEjector;
|
const ejectorComp = entity.components.ItemEjector;
|
||||||
const staticComp = entity.components.StaticMapEntity;
|
const staticComp = entity.components.StaticMapEntity;
|
||||||
|
|
||||||
// Clear the old cache.
|
for (let slotIndex = 0; slotIndex < ejectorComp.slots.length; ++slotIndex) {
|
||||||
ejectorComp.cachedConnectedSlots = null;
|
const ejectorSlot = ejectorComp.slots[slotIndex];
|
||||||
|
|
||||||
for (let ejectorSlotIndex = 0; ejectorSlotIndex < ejectorComp.slots.length; ++ejectorSlotIndex) {
|
|
||||||
const ejectorSlot = ejectorComp.slots[ejectorSlotIndex];
|
|
||||||
|
|
||||||
// Clear the old cache.
|
// Clear the old cache.
|
||||||
ejectorSlot.cachedDestSlot = null;
|
ejectorSlot.cachedDestSlot = null;
|
||||||
ejectorSlot.cachedTargetEntity = null;
|
ejectorSlot.cachedTargetEntity = null;
|
||||||
|
ejectorSlot.cachedBeltPath = null;
|
||||||
|
|
||||||
// Figure out where and into which direction we eject items
|
// Figure out where and into which direction we eject items
|
||||||
const ejectSlotWsTile = staticComp.localTileToWorld(ejectorSlot.pos);
|
const ejectSlotWsTile = staticComp.localTileToWorld(ejectorSlot.pos);
|
||||||
@ -146,8 +144,21 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
|
|||||||
for (let i = 0; i < targetEntities.length; ++i) {
|
for (let i = 0; i < targetEntities.length; ++i) {
|
||||||
const targetEntity = targetEntities[i];
|
const targetEntity = targetEntities[i];
|
||||||
|
|
||||||
const targetAcceptorComp = targetEntity.components.ItemAcceptor;
|
|
||||||
const targetStaticComp = targetEntity.components.StaticMapEntity;
|
const targetStaticComp = targetEntity.components.StaticMapEntity;
|
||||||
|
const targetBeltComp = targetEntity.components.Belt;
|
||||||
|
|
||||||
|
// Check for belts (special case)
|
||||||
|
if (targetBeltComp) {
|
||||||
|
const beltAcceptingDirection = targetStaticComp.localDirectionToWorld(enumDirection.top);
|
||||||
|
if (ejectSlotWsDirection === beltAcceptingDirection) {
|
||||||
|
ejectorSlot.cachedTargetEntity = targetEntity;
|
||||||
|
ejectorSlot.cachedBeltPath = targetBeltComp.assignedPath;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for item acceptors
|
||||||
|
const targetAcceptorComp = targetEntity.components.ItemAcceptor;
|
||||||
if (!targetAcceptorComp) {
|
if (!targetAcceptorComp) {
|
||||||
// Entity doesn't accept items
|
// Entity doesn't accept items
|
||||||
continue;
|
continue;
|
||||||
@ -164,13 +175,6 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ok we found a connection
|
|
||||||
if (ejectorComp.cachedConnectedSlots) {
|
|
||||||
ejectorComp.cachedConnectedSlots.push(ejectorSlot);
|
|
||||||
} else {
|
|
||||||
ejectorComp.cachedConnectedSlots = [ejectorSlot];
|
|
||||||
}
|
|
||||||
|
|
||||||
// A slot can always be connected to one other slot only
|
// A slot can always be connected to one other slot only
|
||||||
ejectorSlot.cachedTargetEntity = targetEntity;
|
ejectorSlot.cachedTargetEntity = targetEntity;
|
||||||
ejectorSlot.cachedDestSlot = matchingSlot;
|
ejectorSlot.cachedDestSlot = matchingSlot;
|
||||||
@ -199,11 +203,7 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sourceEjectorComp.cachedConnectedSlots) {
|
const slots = sourceEjectorComp.slots;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const slots = sourceEjectorComp.cachedConnectedSlots;
|
|
||||||
for (let j = 0; j < slots.length; ++j) {
|
for (let j = 0; j < slots.length; ++j) {
|
||||||
const sourceSlot = slots[j];
|
const sourceSlot = slots[j];
|
||||||
const item = sourceSlot.item;
|
const item = sourceSlot.item;
|
||||||
@ -212,7 +212,6 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const destSlot = sourceSlot.cachedDestSlot;
|
|
||||||
const targetEntity = sourceSlot.cachedTargetEntity;
|
const targetEntity = sourceSlot.cachedTargetEntity;
|
||||||
|
|
||||||
// Advance items on the slot
|
// Advance items on the slot
|
||||||
@ -229,18 +228,34 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the target acceptor can actually accept this item
|
// Check if we are ejecting to a belt path
|
||||||
const targetAcceptorComp = targetEntity.components.ItemAcceptor;
|
const destPath = sourceSlot.cachedBeltPath;
|
||||||
if (!targetAcceptorComp.canAcceptItem(destSlot.index, item)) {
|
if (destPath) {
|
||||||
|
// Try passing the item over
|
||||||
|
if (destPath.tryAcceptItem(item)) {
|
||||||
|
sourceSlot.item = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always stop here, since there can *either* be a belt path *or*
|
||||||
|
// a slot
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to hand over the item
|
// Check if the target acceptor can actually accept this item
|
||||||
if (this.tryPassOverItem(item, targetEntity, destSlot.index)) {
|
const destSlot = sourceSlot.cachedDestSlot;
|
||||||
// Handover successful, clear slot
|
if (destSlot) {
|
||||||
targetAcceptorComp.onItemAccepted(destSlot.index, destSlot.acceptedDirection, item);
|
const targetAcceptorComp = targetEntity.components.ItemAcceptor;
|
||||||
sourceSlot.item = null;
|
if (!targetAcceptorComp.canAcceptItem(destSlot.index, item)) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to hand over the item
|
||||||
|
if (this.tryPassOverItem(item, targetEntity, destSlot.index)) {
|
||||||
|
// Handover successful, clear slot
|
||||||
|
targetAcceptorComp.onItemAccepted(destSlot.index, destSlot.acceptedDirection, item);
|
||||||
|
sourceSlot.item = null;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user