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 { DrawParameters } from "../core/draw_parameters";
|
||||
import { gItemRegistry } from "../core/global_registries";
|
||||
import { createLogger } from "../core/logging";
|
||||
import { Rectangle } from "../core/rectangle";
|
||||
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 { Entity } from "./entity";
|
||||
import { GameRoot, enumLayer } from "./root";
|
||||
import { Rectangle } from "../core/rectangle";
|
||||
import { BasicSerializableObject, types } from "../savegame/serialization";
|
||||
import { gItemRegistry } from "../core/global_registries";
|
||||
import { GameRoot } from "./root";
|
||||
|
||||
const logger = createLogger("belt_path");
|
||||
|
||||
@ -125,14 +125,6 @@ export class BeltPath extends BasicSerializableObject {
|
||||
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
|
||||
* @param {BaseItem} item
|
||||
@ -167,7 +159,7 @@ export class BeltPath extends BasicSerializableObject {
|
||||
* @returns {BaseItem|null}
|
||||
*/
|
||||
findItemAtTile(tile) {
|
||||
// TODO: This breaks color blind mode otherwise
|
||||
// @TODO: This breaks color blind mode otherwise
|
||||
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() {
|
||||
this.ejectorComp = this.entityPath[this.entityPath.length - 1].components.ItemEjector;
|
||||
this.ejectorSlot = this.ejectorComp.slots[0];
|
||||
|
||||
for (let i = 0; i < this.entityPath.length; ++i) {
|
||||
const ejectorComp = this.entityPath[i].components.ItemEjector;
|
||||
const isLast = i === this.entityPath.length - 1;
|
||||
ejectorComp.enabled = isLast;
|
||||
|
||||
// Clear all slots of non-end entities
|
||||
if (!isLast) {
|
||||
for (let k = 0; k < ejectorComp.slots.length; ++k) {
|
||||
ejectorComp.slots[k].item = null;
|
||||
ejectorComp.slots[k].progress = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.layer = this.entityPath[0].layer;
|
||||
this.acceptorTarget = this.computeAcceptingEntityAndSlot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the belt system when the surroundings changed
|
||||
*/
|
||||
onSurroundingsChanged() {
|
||||
this.onPathChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the entity which accepts our items
|
||||
* @return {{ entity: Entity, slot: number }}
|
||||
*/
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
@ -251,11 +298,6 @@ export class BeltPath extends BasicSerializableObject {
|
||||
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);
|
||||
if (!followUp) {
|
||||
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
|
||||
if (this.spacingToFirstItem > this.totalLength + 0.005) {
|
||||
return fail(
|
||||
@ -370,14 +398,6 @@ export class BeltPath extends BasicSerializableObject {
|
||||
|
||||
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
|
||||
this.entityPath.push(entity);
|
||||
this.onPathChanged();
|
||||
@ -970,7 +990,7 @@ export class BeltPath extends BasicSerializableObject {
|
||||
beltSpeed *= 100;
|
||||
}
|
||||
|
||||
let minimumDistance = this.ejectorSlot.item ? this.getItemSpacing() : 0;
|
||||
let minimumDistance = 0;
|
||||
|
||||
// Try to reduce spacing
|
||||
let remainingAmount = beltSpeed;
|
||||
@ -994,10 +1014,17 @@ export class BeltPath extends BasicSerializableObject {
|
||||
minimumDistance = this.getItemSpacing();
|
||||
}
|
||||
|
||||
// Check if we have an item which is ready to be emitted
|
||||
const lastItem = this.items[this.items.length - 1];
|
||||
if (lastItem && lastItem[_nextDistance] === 0) {
|
||||
// Take over
|
||||
if (this.ejectorComp.tryEject(0, lastItem[_item])) {
|
||||
if (lastItem && lastItem[_nextDistance] === 0 && this.acceptorTarget) {
|
||||
// Pass over the item
|
||||
if (
|
||||
this.root.systemMgr.systems.itemEjector.tryPassOverItem(
|
||||
lastItem[_item],
|
||||
this.acceptorTarget.entity,
|
||||
this.acceptorTarget.slot
|
||||
)
|
||||
) {
|
||||
this.items.pop();
|
||||
}
|
||||
}
|
||||
@ -1134,7 +1161,7 @@ export class BeltPath extends BasicSerializableObject {
|
||||
const beltLength = beltComp.getEffectiveLengthTiles(this.layer);
|
||||
|
||||
// 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
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
assert(
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { MetaBuilding, defaultBuildingVariant } from "./meta_building";
|
||||
/* typehints:start */
|
||||
import { MetaBuilding } from "./meta_building";
|
||||
import { AtlasSprite } from "../core/sprites";
|
||||
/* typehints:end */
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
@ -22,13 +24,18 @@ export const gBuildingVariants = {
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} id
|
||||
* @param {*} meta
|
||||
* @param {*} variant
|
||||
* @param {*} rotationVariant
|
||||
* Registers a new variant
|
||||
* @param {number} id
|
||||
* @param {typeof MetaBuilding} meta
|
||||
* @param {string} variant
|
||||
* @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);
|
||||
gBuildingVariants[id] = {
|
||||
metaClass: meta,
|
||||
|
@ -53,34 +53,8 @@ export class MetaBeltBaseBuilding extends MetaBuilding {
|
||||
direction: enumDirection.top, // updated later
|
||||
})
|
||||
);
|
||||
// Make this entity replaceabel
|
||||
// Make this entity replaceable
|
||||
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) {
|
||||
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;
|
||||
|
||||
/** @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 {
|
||||
static getId() {
|
||||
return "Belt";
|
||||
@ -56,6 +90,27 @@ export class BeltComponent extends Component {
|
||||
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
|
||||
* 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 { Entity } from "../entity";
|
||||
import { enumLayer } from "../root";
|
||||
import { BeltPath } from "../belt_path";
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
@ -14,6 +15,7 @@ import { enumLayer } from "../root";
|
||||
* layer: enumLayer,
|
||||
* progress: number?,
|
||||
* cachedDestSlot?: import("./item_acceptor").ItemAcceptorLocatedSlot,
|
||||
* cachedBeltPath?: BeltPath,
|
||||
* cachedTargetEntity?: Entity
|
||||
* }} ItemEjectorSlot
|
||||
*/
|
||||
@ -24,7 +26,7 @@ export class ItemEjectorComponent extends Component {
|
||||
}
|
||||
|
||||
static getSchema() {
|
||||
// The cachedDestSlot, cachedTargetEntity, and cachedConnectedSlots fields
|
||||
// The cachedDestSlot, cachedTargetEntity fields
|
||||
// are not serialized.
|
||||
return {
|
||||
instantEject: types.bool,
|
||||
@ -73,9 +75,6 @@ export class ItemEjectorComponent extends Component {
|
||||
|
||||
this.setSlots(slots);
|
||||
|
||||
/** @type {ItemEjectorSlot[]} */
|
||||
this.cachedConnectedSlots = null;
|
||||
|
||||
/**
|
||||
* Whether this ejector slot is enabled
|
||||
*/
|
||||
|
@ -198,11 +198,31 @@ export class GameLogic {
|
||||
for (let i = 0; i < entities.length; ++i) {
|
||||
const entity = entities[i];
|
||||
|
||||
let ejectorSlots = [];
|
||||
let acceptorSlots = [];
|
||||
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
const itemEjector = entity.components.ItemEjector;
|
||||
const itemAcceptor = entity.components.ItemAcceptor;
|
||||
const beltComp = entity.components.Belt;
|
||||
|
||||
if (itemEjector) {
|
||||
for (let ejectorSlot = 0; ejectorSlot < itemEjector.slots.length; ++ejectorSlot) {
|
||||
const slot = itemEjector.slots[ejectorSlot];
|
||||
ejectorSlots = itemEjector.slots.slice();
|
||||
}
|
||||
|
||||
if (itemAcceptor) {
|
||||
acceptorSlots = itemAcceptor.slots.slice();
|
||||
}
|
||||
|
||||
if (beltComp) {
|
||||
const fakeEjectorSlot = beltComp.getFakeEjectorSlots();
|
||||
const fakeAcceptorSlot = beltComp.getFakeAcceptorSlot();
|
||||
ejectorSlots.push(fakeEjectorSlot);
|
||||
acceptorSlots.push(fakeAcceptorSlot);
|
||||
}
|
||||
|
||||
for (let ejectorSlot = 0; ejectorSlot < ejectorSlots.length; ++ejectorSlot) {
|
||||
const slot = ejectorSlots[ejectorSlot];
|
||||
if (slot.layer !== layer) {
|
||||
continue;
|
||||
}
|
||||
@ -218,12 +238,9 @@ export class GameLogic {
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const itemAcceptor = entity.components.ItemAcceptor;
|
||||
if (itemAcceptor) {
|
||||
for (let acceptorSlot = 0; acceptorSlot < itemAcceptor.slots.length; ++acceptorSlot) {
|
||||
const slot = itemAcceptor.slots[acceptorSlot];
|
||||
for (let acceptorSlot = 0; acceptorSlot < acceptorSlots.length; ++acceptorSlot) {
|
||||
const slot = acceptorSlots[acceptorSlot];
|
||||
if (slot.layer !== layer) {
|
||||
continue;
|
||||
}
|
||||
@ -247,7 +264,6 @@ export class GameLogic {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return { ejectors, acceptors };
|
||||
}
|
||||
}
|
||||
|
@ -137,6 +137,9 @@ export class BeltSystem extends GameSystemWithFilter {
|
||||
const originalRect = staticComp.getTileSpaceBounds();
|
||||
const affectedArea = originalRect.expandedInAllDirections(1);
|
||||
|
||||
/** @type {Set<BeltPath>} */
|
||||
const changedPaths = new Set();
|
||||
|
||||
for (let x = affectedArea.x; x < affectedArea.right(); ++x) {
|
||||
for (let y = affectedArea.y; y < affectedArea.bottom(); ++y) {
|
||||
if (originalRect.containsPoint(x, y)) {
|
||||
@ -189,9 +192,16 @@ export class BeltSystem extends GameSystemWithFilter {
|
||||
// Make sure the chunks know about the update
|
||||
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) {
|
||||
this.debug_verifyBeltPaths();
|
||||
@ -361,27 +371,13 @@ export class BeltSystem extends GameSystemWithFilter {
|
||||
const followUpBeltComp = followUpEntity.components.Belt;
|
||||
if (followUpBeltComp) {
|
||||
const followUpStatic = followUpEntity.components.StaticMapEntity;
|
||||
const followUpAcceptor = followUpEntity.components.ItemAcceptor;
|
||||
|
||||
// Check if the belt accepts items from our direction
|
||||
const acceptorSlots = followUpAcceptor.slots;
|
||||
for (let i = 0; i < acceptorSlots.length; ++i) {
|
||||
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) {
|
||||
const acceptedDirection = followUpStatic.localDirectionToWorld(enumDirection.top);
|
||||
if (acceptedDirection === followUpDirection) {
|
||||
return followUpEntity;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@ -405,24 +401,15 @@ export class BeltSystem extends GameSystemWithFilter {
|
||||
const supplyBeltComp = supplyEntity.components.Belt;
|
||||
if (supplyBeltComp) {
|
||||
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
|
||||
const ejectorSlots = supplyEjector.slots;
|
||||
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) {
|
||||
if (otherDirection === supplyDirection) {
|
||||
return supplyEntity;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
@ -2,8 +2,8 @@ import { globalConfig } from "../../core/config";
|
||||
import { DrawParameters } from "../../core/draw_parameters";
|
||||
import { createLogger } from "../../core/logging";
|
||||
import { Rectangle } from "../../core/rectangle";
|
||||
import { enumDirectionToVector, Vector } from "../../core/vector";
|
||||
import { BaseItem, enumItemType, enumItemTypeToLayer } from "../base_item";
|
||||
import { enumDirection, enumDirectionToVector, Vector } from "../../core/vector";
|
||||
import { BaseItem, enumItemTypeToLayer } from "../base_item";
|
||||
import { ItemEjectorComponent } from "../components/item_ejector";
|
||||
import { Entity } from "../entity";
|
||||
import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||
@ -120,15 +120,13 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
|
||||
const ejectorComp = entity.components.ItemEjector;
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
|
||||
// Clear the old cache.
|
||||
ejectorComp.cachedConnectedSlots = null;
|
||||
|
||||
for (let ejectorSlotIndex = 0; ejectorSlotIndex < ejectorComp.slots.length; ++ejectorSlotIndex) {
|
||||
const ejectorSlot = ejectorComp.slots[ejectorSlotIndex];
|
||||
for (let slotIndex = 0; slotIndex < ejectorComp.slots.length; ++slotIndex) {
|
||||
const ejectorSlot = ejectorComp.slots[slotIndex];
|
||||
|
||||
// Clear the old cache.
|
||||
ejectorSlot.cachedDestSlot = null;
|
||||
ejectorSlot.cachedTargetEntity = null;
|
||||
ejectorSlot.cachedBeltPath = null;
|
||||
|
||||
// Figure out where and into which direction we eject items
|
||||
const ejectSlotWsTile = staticComp.localTileToWorld(ejectorSlot.pos);
|
||||
@ -146,8 +144,21 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
|
||||
for (let i = 0; i < targetEntities.length; ++i) {
|
||||
const targetEntity = targetEntities[i];
|
||||
|
||||
const targetAcceptorComp = targetEntity.components.ItemAcceptor;
|
||||
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) {
|
||||
// Entity doesn't accept items
|
||||
continue;
|
||||
@ -164,13 +175,6 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
|
||||
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
|
||||
ejectorSlot.cachedTargetEntity = targetEntity;
|
||||
ejectorSlot.cachedDestSlot = matchingSlot;
|
||||
@ -199,11 +203,7 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!sourceEjectorComp.cachedConnectedSlots) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const slots = sourceEjectorComp.cachedConnectedSlots;
|
||||
const slots = sourceEjectorComp.slots;
|
||||
for (let j = 0; j < slots.length; ++j) {
|
||||
const sourceSlot = slots[j];
|
||||
const item = sourceSlot.item;
|
||||
@ -212,7 +212,6 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
|
||||
continue;
|
||||
}
|
||||
|
||||
const destSlot = sourceSlot.cachedDestSlot;
|
||||
const targetEntity = sourceSlot.cachedTargetEntity;
|
||||
|
||||
// Advance items on the slot
|
||||
@ -229,7 +228,22 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if we are ejecting to a belt path
|
||||
const destPath = sourceSlot.cachedBeltPath;
|
||||
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;
|
||||
}
|
||||
|
||||
// Check if the target acceptor can actually accept this item
|
||||
const destSlot = sourceSlot.cachedDestSlot;
|
||||
if (destSlot) {
|
||||
const targetAcceptorComp = targetEntity.components.ItemAcceptor;
|
||||
if (!targetAcceptorComp.canAcceptItem(destSlot.index, item)) {
|
||||
continue;
|
||||
@ -245,6 +259,7 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user