1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2025-12-14 02:31:51 +00:00

Item acceptor refactor part 1

This commit is contained in:
Sense101 2022-01-18 17:39:02 +00:00
parent 2ac34665b3
commit 4a1bfcda64
23 changed files with 493 additions and 484 deletions

View File

@ -132,14 +132,14 @@ export class BeltPath extends BasicSerializableObject {
tryAcceptItem(item) { tryAcceptItem(item) {
if (this.spacingToFirstItem >= globalConfig.itemSpacingOnBelts) { if (this.spacingToFirstItem >= globalConfig.itemSpacingOnBelts) {
// So, since we already need one tick to accept this item we will add this directly. // So, since we already need one tick to accept this item we will add this directly.
const beltProgressPerTick = const progressGrowth =
this.root.hubGoals.getBeltBaseSpeed() * this.root.hubGoals.getBeltBaseSpeed() *
this.root.dynamicTickrate.deltaSeconds * this.root.dynamicTickrate.deltaSeconds *
globalConfig.itemSpacingOnBelts; globalConfig.itemSpacingOnBelts;
// First, compute how much progress we can make *at max* // First, compute how much progress we can make *at max*
const maxProgress = Math.max(0, this.spacingToFirstItem - globalConfig.itemSpacingOnBelts); const maxProgress = Math.max(0, this.spacingToFirstItem - globalConfig.itemSpacingOnBelts);
const initialProgress = Math.min(maxProgress, beltProgressPerTick); const initialProgress = Math.min(maxProgress, progressGrowth);
this.items.unshift([this.spacingToFirstItem - initialProgress, item]); this.items.unshift([this.spacingToFirstItem - initialProgress, item]);
this.spacingToFirstItem = initialProgress; this.spacingToFirstItem = initialProgress;
@ -272,31 +272,10 @@ export class BeltPath extends BasicSerializableObject {
} }
const matchingSlotIndex = matchingSlot.index; const matchingSlotIndex = matchingSlot.index;
const passOver = this.computePassOverFunctionWithoutBelts(targetEntity, matchingSlotIndex);
if (!passOver) {
return;
}
const matchingDirection = enumInvertedDirections[ejectingDirection]; const matchingDirection = enumInvertedDirections[ejectingDirection];
const filter = matchingSlot.slot.filter;
return function (item, remainingProgress = 0.0) { return function (item, startProgress = 0.0) {
// Check if the acceptor has a filter if (targetAcceptorComp.tryAcceptItem(matchingSlotIndex, matchingDirection, item, startProgress)) {
if (filter && item._type !== filter) {
return false;
}
// Try to pass over
if (passOver(item, matchingSlotIndex)) {
// Trigger animation on the acceptor comp
if (noSimplifiedBelts) {
targetAcceptorComp.onItemAccepted(
matchingSlotIndex,
matchingDirection,
item,
remainingProgress
);
}
return true; return true;
} }
return false; return false;
@ -313,7 +292,7 @@ export class BeltPath extends BasicSerializableObject {
const systems = this.root.systemMgr.systems; const systems = this.root.systemMgr.systems;
const hubGoals = this.root.hubGoals; const hubGoals = this.root.hubGoals;
// NOTICE: THIS IS COPIED FROM THE ITEM EJECTOR SYSTEM FOR PEROFMANCE REASONS // NOTICE: THIS IS COPIED FROM THE ITEM EJECTOR SYSTEM FOR PERFORMANCE REASONS
const itemProcessorComp = entity.components.ItemProcessor; const itemProcessorComp = entity.components.ItemProcessor;
if (itemProcessorComp) { if (itemProcessorComp) {
@ -323,7 +302,7 @@ export class BeltPath extends BasicSerializableObject {
if (!systems.itemProcessor.checkRequirements(entity, item, matchingSlotIndex)) { if (!systems.itemProcessor.checkRequirements(entity, item, matchingSlotIndex)) {
return; return;
} }
return itemProcessorComp.tryTakeItem(item, matchingSlotIndex); //return itemProcessorComp.tryTakeItem(item, matchingSlotIndex);
}; };
} }

View File

@ -179,11 +179,11 @@ export class MetaBalancerBuilding extends MetaBuilding {
entity.components.ItemAcceptor.setSlots([ entity.components.ItemAcceptor.setSlots([
{ {
pos: new Vector(0, 0), pos: new Vector(0, 0),
directions: [enumDirection.bottom], direction: enumDirection.bottom,
}, },
{ {
pos: new Vector(1, 0), pos: new Vector(1, 0),
directions: [enumDirection.bottom], direction: enumDirection.bottom,
}, },
]); ]);
@ -204,15 +204,14 @@ export class MetaBalancerBuilding extends MetaBuilding {
entity.components.ItemAcceptor.setSlots([ entity.components.ItemAcceptor.setSlots([
{ {
pos: new Vector(0, 0), pos: new Vector(0, 0),
directions: [enumDirection.bottom], direction: enumDirection.bottom,
}, },
{ {
pos: new Vector(0, 0), pos: new Vector(0, 0),
directions: [ direction:
variant === enumBalancerVariants.mergerInverse variant === enumBalancerVariants.mergerInverse
? enumDirection.left ? enumDirection.left
: enumDirection.right, : enumDirection.right,
],
}, },
]); ]);
@ -231,7 +230,7 @@ export class MetaBalancerBuilding extends MetaBuilding {
entity.components.ItemAcceptor.setSlots([ entity.components.ItemAcceptor.setSlots([
{ {
pos: new Vector(0, 0), pos: new Vector(0, 0),
directions: [enumDirection.bottom], direction: enumDirection.bottom,
}, },
]); ]);

View File

@ -96,7 +96,7 @@ export class MetaCutterBuilding extends MetaBuilding {
slots: [ slots: [
{ {
pos: new Vector(0, 0), pos: new Vector(0, 0),
directions: [enumDirection.bottom], direction: enumDirection.bottom,
filter: "shape", filter: "shape",
}, },
], ],

View File

@ -78,7 +78,7 @@ export class MetaFilterBuilding extends MetaBuilding {
slots: [ slots: [
{ {
pos: new Vector(0, 0), pos: new Vector(0, 0),
directions: [enumDirection.bottom], direction: enumDirection.bottom,
}, },
], ],
}) })

View File

@ -45,7 +45,7 @@ export class MetaGoalAcceptorBuilding extends MetaBuilding {
slots: [ slots: [
{ {
pos: new Vector(0, 0), pos: new Vector(0, 0),
directions: [enumDirection.bottom], direction: enumDirection.bottom,
filter: "shape", filter: "shape",
}, },
], ],

View File

@ -1,6 +1,6 @@
import { enumDirection, Vector } from "../../core/vector"; import { enumDirection, Vector } from "../../core/vector";
import { HubComponent } from "../components/hub"; import { HubComponent } from "../components/hub";
import { ItemAcceptorComponent } from "../components/item_acceptor"; import { enumItemAcceptorTypes, ItemAcceptorComponent } from "../components/item_acceptor";
import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor"; import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor";
import { Entity } from "../entity"; import { Entity } from "../entity";
import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; import { defaultBuildingVariant, MetaBuilding } from "../meta_building";
@ -75,75 +75,96 @@ export class MetaHubBuilding extends MetaBuilding {
slots: [ slots: [
{ {
pos: new Vector(0, 0), pos: new Vector(0, 0),
directions: [enumDirection.top, enumDirection.left], direction: enumDirection.top,
filter: "shape",
},
{
pos: new Vector(0, 0),
direction: enumDirection.left,
filter: "shape", filter: "shape",
}, },
{ {
pos: new Vector(1, 0), pos: new Vector(1, 0),
directions: [enumDirection.top], direction: enumDirection.top,
filter: "shape", filter: "shape",
}, },
{ {
pos: new Vector(2, 0), pos: new Vector(2, 0),
directions: [enumDirection.top], direction: enumDirection.top,
filter: "shape", filter: "shape",
}, },
{ {
pos: new Vector(3, 0), pos: new Vector(3, 0),
directions: [enumDirection.top, enumDirection.right], direction: enumDirection.top,
filter: "shape",
},
{
pos: new Vector(3, 0),
direction: enumDirection.right,
filter: "shape", filter: "shape",
}, },
{ {
pos: new Vector(0, 3), pos: new Vector(0, 3),
directions: [enumDirection.bottom, enumDirection.left], direction: enumDirection.left,
filter: "shape",
},
{
pos: new Vector(0, 3),
direction: enumDirection.bottom,
filter: "shape", filter: "shape",
}, },
{ {
pos: new Vector(1, 3), pos: new Vector(1, 3),
directions: [enumDirection.bottom], direction: enumDirection.bottom,
filter: "shape", filter: "shape",
}, },
{ {
pos: new Vector(2, 3), pos: new Vector(2, 3),
directions: [enumDirection.bottom], direction: enumDirection.bottom,
filter: "shape", filter: "shape",
}, },
{ {
pos: new Vector(3, 3), pos: new Vector(3, 3),
directions: [enumDirection.bottom, enumDirection.right], direction: enumDirection.bottom,
filter: "shape",
},
{
pos: new Vector(3, 3),
direction: enumDirection.right,
filter: "shape", filter: "shape",
}, },
{ {
pos: new Vector(0, 1), pos: new Vector(0, 1),
directions: [enumDirection.left], direction: enumDirection.left,
filter: "shape", filter: "shape",
}, },
{ {
pos: new Vector(0, 2), pos: new Vector(0, 2),
directions: [enumDirection.left], direction: enumDirection.left,
filter: "shape", filter: "shape",
}, },
{ {
pos: new Vector(0, 3), pos: new Vector(0, 3),
directions: [enumDirection.left], direction: enumDirection.left,
filter: "shape", filter: "shape",
}, },
{ {
pos: new Vector(3, 1), pos: new Vector(3, 1),
directions: [enumDirection.right], direction: enumDirection.right,
filter: "shape", filter: "shape",
}, },
{ {
pos: new Vector(3, 2), pos: new Vector(3, 2),
directions: [enumDirection.right], direction: enumDirection.right,
filter: "shape", filter: "shape",
}, },
{ {
pos: new Vector(3, 3), pos: new Vector(3, 3),
directions: [enumDirection.right], direction: enumDirection.right,
filter: "shape", filter: "shape",
}, },
], ],
type: enumItemAcceptorTypes.hub,
}) })
); );
} }

View File

@ -73,12 +73,12 @@ export class MetaMixerBuilding extends MetaBuilding {
slots: [ slots: [
{ {
pos: new Vector(0, 0), pos: new Vector(0, 0),
directions: [enumDirection.bottom], direction: enumDirection.bottom,
filter: "color", filter: "color",
}, },
{ {
pos: new Vector(1, 0), pos: new Vector(1, 0),
directions: [enumDirection.bottom], direction: enumDirection.bottom,
filter: "color", filter: "color",
}, },
], ],

View File

@ -128,12 +128,12 @@ export class MetaPainterBuilding extends MetaBuilding {
slots: [ slots: [
{ {
pos: new Vector(0, 0), pos: new Vector(0, 0),
directions: [enumDirection.left], direction: enumDirection.left,
filter: "shape", filter: "shape",
}, },
{ {
pos: new Vector(1, 0), pos: new Vector(1, 0),
directions: [enumDirection.top], direction: enumDirection.top,
filter: "color", filter: "color",
}, },
], ],
@ -160,14 +160,13 @@ export class MetaPainterBuilding extends MetaBuilding {
entity.components.ItemAcceptor.setSlots([ entity.components.ItemAcceptor.setSlots([
{ {
pos: new Vector(0, 0), pos: new Vector(0, 0),
directions: [enumDirection.left], direction: enumDirection.left,
filter: "shape", filter: "shape",
}, },
{ {
pos: new Vector(1, 0), pos: new Vector(1, 0),
directions: [ direction:
variant === defaultBuildingVariant ? enumDirection.top : enumDirection.bottom, variant === defaultBuildingVariant ? enumDirection.top : enumDirection.bottom,
],
filter: "color", filter: "color",
}, },
]); ]);
@ -193,17 +192,17 @@ export class MetaPainterBuilding extends MetaBuilding {
entity.components.ItemAcceptor.setSlots([ entity.components.ItemAcceptor.setSlots([
{ {
pos: new Vector(0, 0), pos: new Vector(0, 0),
directions: [enumDirection.left], direction: enumDirection.left,
filter: "shape", filter: "shape",
}, },
{ {
pos: new Vector(0, 1), pos: new Vector(0, 1),
directions: [enumDirection.left], direction: enumDirection.left,
filter: "shape", filter: "shape",
}, },
{ {
pos: new Vector(1, 0), pos: new Vector(1, 0),
directions: [enumDirection.top], direction: enumDirection.top,
filter: "color", filter: "color",
}, },
]); ]);
@ -251,27 +250,27 @@ export class MetaPainterBuilding extends MetaBuilding {
entity.components.ItemAcceptor.setSlots([ entity.components.ItemAcceptor.setSlots([
{ {
pos: new Vector(0, 0), pos: new Vector(0, 0),
directions: [enumDirection.left], direction: enumDirection.left,
filter: "shape", filter: "shape",
}, },
{ {
pos: new Vector(0, 0), pos: new Vector(0, 0),
directions: [enumDirection.bottom], direction: enumDirection.bottom,
filter: "color", filter: "color",
}, },
{ {
pos: new Vector(1, 0), pos: new Vector(1, 0),
directions: [enumDirection.bottom], direction: enumDirection.bottom,
filter: "color", filter: "color",
}, },
{ {
pos: new Vector(2, 0), pos: new Vector(2, 0),
directions: [enumDirection.bottom], direction: enumDirection.bottom,
filter: "color", filter: "color",
}, },
{ {
pos: new Vector(3, 0), pos: new Vector(3, 0),
directions: [enumDirection.bottom], direction: enumDirection.bottom,
filter: "color", filter: "color",
}, },
]); ]);

View File

@ -84,7 +84,7 @@ export class MetaReaderBuilding extends MetaBuilding {
slots: [ slots: [
{ {
pos: new Vector(0, 0), pos: new Vector(0, 0),
directions: [enumDirection.bottom], direction: enumDirection.bottom,
}, },
], ],
}) })

View File

@ -128,7 +128,7 @@ export class MetaRotaterBuilding extends MetaBuilding {
slots: [ slots: [
{ {
pos: new Vector(0, 0), pos: new Vector(0, 0),
directions: [enumDirection.bottom], direction: enumDirection.bottom,
filter: "shape", filter: "shape",
}, },
], ],

View File

@ -73,12 +73,12 @@ export class MetaStackerBuilding extends MetaBuilding {
slots: [ slots: [
{ {
pos: new Vector(0, 0), pos: new Vector(0, 0),
directions: [enumDirection.bottom], direction: enumDirection.bottom,
filter: "shape", filter: "shape",
}, },
{ {
pos: new Vector(1, 0), pos: new Vector(1, 0),
directions: [enumDirection.bottom], direction: enumDirection.bottom,
filter: "shape", filter: "shape",
}, },
], ],

View File

@ -1,7 +1,7 @@
import { formatBigNumber } from "../../core/utils"; import { formatBigNumber } from "../../core/utils";
import { enumDirection, Vector } from "../../core/vector"; import { enumDirection, Vector } from "../../core/vector";
import { T } from "../../translations"; import { T } from "../../translations";
import { ItemAcceptorComponent } from "../components/item_acceptor"; import { enumItemAcceptorTypes, ItemAcceptorComponent } from "../components/item_acceptor";
import { ItemEjectorComponent } from "../components/item_ejector"; import { ItemEjectorComponent } from "../components/item_ejector";
import { StorageComponent } from "../components/storage"; import { StorageComponent } from "../components/storage";
import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins";
@ -74,13 +74,14 @@ export class MetaStorageBuilding extends MetaBuilding {
slots: [ slots: [
{ {
pos: new Vector(0, 1), pos: new Vector(0, 1),
directions: [enumDirection.bottom], direction: enumDirection.bottom,
}, },
{ {
pos: new Vector(1, 1), pos: new Vector(1, 1),
directions: [enumDirection.bottom], direction: enumDirection.bottom,
}, },
], ],
type: enumItemAcceptorTypes.storage,
}) })
); );

View File

@ -74,15 +74,10 @@ export class MetaTrashBuilding extends MetaBuilding {
entity.addComponent( entity.addComponent(
new ItemAcceptorComponent({ new ItemAcceptorComponent({
slots: [ slots: [
{ { pos: new Vector(0, 0), direction: enumDirection.top },
pos: new Vector(0, 0), { pos: new Vector(0, 0), direction: enumDirection.left },
directions: [ { pos: new Vector(0, 0), direction: enumDirection.right },
enumDirection.top, { pos: new Vector(0, 0), direction: enumDirection.bottom },
enumDirection.right,
enumDirection.bottom,
enumDirection.left,
],
},
], ],
}) })
); );

View File

@ -1,6 +1,6 @@
import { Loader } from "../../core/loader"; import { Loader } from "../../core/loader";
import { enumDirection, Vector, enumAngleToDirection, enumDirectionToVector } from "../../core/vector"; import { enumDirection, Vector, enumAngleToDirection, enumDirectionToVector } from "../../core/vector";
import { ItemAcceptorComponent } from "../components/item_acceptor"; import { enumItemAcceptorTypes, ItemAcceptorComponent } from "../components/item_acceptor";
import { ItemEjectorComponent } from "../components/item_ejector"; import { ItemEjectorComponent } from "../components/item_ejector";
import { enumUndergroundBeltMode, UndergroundBeltComponent } from "../components/underground_belt"; import { enumUndergroundBeltMode, UndergroundBeltComponent } from "../components/underground_belt";
import { Entity } from "../entity"; import { Entity } from "../entity";
@ -195,6 +195,7 @@ export class MetaUndergroundBeltBuilding extends MetaBuilding {
entity.addComponent( entity.addComponent(
new ItemAcceptorComponent({ new ItemAcceptorComponent({
slots: [], slots: [],
type: enumItemAcceptorTypes.undergroundBelt,
}) })
); );
} }
@ -277,7 +278,7 @@ export class MetaUndergroundBeltBuilding extends MetaBuilding {
entity.components.ItemAcceptor.setSlots([ entity.components.ItemAcceptor.setSlots([
{ {
pos: new Vector(0, 0), pos: new Vector(0, 0),
directions: [enumDirection.bottom], direction: enumDirection.bottom,
}, },
]); ]);
return; return;

View File

@ -8,7 +8,7 @@ export const curvedBeltLength = /* Math.PI / 4 */ 0.78;
/** @type {import("./item_acceptor").ItemAcceptorSlot} */ /** @type {import("./item_acceptor").ItemAcceptorSlot} */
export const FAKE_BELT_ACCEPTOR_SLOT = { export const FAKE_BELT_ACCEPTOR_SLOT = {
pos: new Vector(0, 0), pos: new Vector(0, 0),
directions: [enumDirection.bottom], direction: enumDirection.bottom,
}; };
/** @type {Object<enumDirection, import("./item_ejector").ItemEjectorSlot>} */ /** @type {Object<enumDirection, import("./item_ejector").ItemEjectorSlot>} */

View File

@ -1,27 +1,52 @@
import { enumDirection, enumInvertedDirections, Vector } from "../../core/vector"; import { enumDirection, enumInvertedDirections, Vector } from "../../core/vector";
import { types } from "../../savegame/serialization";
import { BaseItem } from "../base_item"; import { BaseItem } from "../base_item";
import { Component } from "../component"; import { Component } from "../component";
import { Entity } from "../entity";
/** @typedef {{ import { GameRoot } from "../root";
* pos: Vector,
* directions: enumDirection[],
* filter?: ItemType
* }} ItemAcceptorSlot */
/** /**
* @typedef {{
* pos: Vector,
* direction: enumDirection,
* filter?: ItemType
* }} ItemAcceptorSlot
*
* Contains information about a slot plus its location * Contains information about a slot plus its location
* @typedef {{ * @typedef {{
* slot: ItemAcceptorSlot, * slot: ItemAcceptorSlot,
* index: number, * index: number,
* acceptedDirection: enumDirection * acceptedDirection: enumDirection
* }} ItemAcceptorLocatedSlot */ * }} ItemAcceptorLocatedSlot
*
/** @typedef {{ * @typedef {{
* pos: Vector, * pos: Vector,
* directions: enumDirection[], * direction: enumDirection,
* filter?: ItemType * filter?: ItemType
* }} ItemAcceptorSlotConfig */ * }} ItemAcceptorSlotConfig
*
* @typedef {Map<number, {
* item: BaseItem,
* animProgress: number,
* direction: enumDirection
* }>} ItemAcceptorInput
*
* @typedef {{
* root: GameRoot,
* entity: Entity,
* item: BaseItem,
* slotIndex: number,
* extraProgress: number
* }} InputCompletedArgs
*/
/** @enum {string} */
export const enumItemAcceptorTypes = {
itemProcessor: "itemProcessor",
hub: "hub",
storage: "storage",
trash: "trash",
undergroundBelt: "undergroundBelt",
};
export class ItemAcceptorComponent extends Component { export class ItemAcceptorComponent extends Component {
static getId() { static getId() {
@ -32,25 +57,15 @@ export class ItemAcceptorComponent extends Component {
* *
* @param {object} param0 * @param {object} param0
* @param {Array<ItemAcceptorSlotConfig>} param0.slots The slots from which we accept items * @param {Array<ItemAcceptorSlotConfig>} param0.slots The slots from which we accept items
* @param {enumItemAcceptorTypes=} param0.type Function that gets called when the input of an item is completed
*/ */
constructor({ slots = [] }) { constructor({ slots = [], type = enumItemAcceptorTypes.itemProcessor }) {
super(); super();
/** @type {ItemAcceptorInput} */
this.currentInputs = new Map(); // @SENSETODO does this need to be saved?
this.type = type;
this.setSlots(slots); this.setSlots(slots);
this.clear();
}
clear() {
/**
* Fixes belt animations
* @type {Array<{
* item: BaseItem,
* slotIndex: number,
* animProgress: number,
* direction: enumDirection
* }>}
*/
this.itemConsumptionAnimations = [];
} }
/** /**
@ -64,7 +79,7 @@ export class ItemAcceptorComponent extends Component {
const slot = slots[i]; const slot = slots[i];
this.slots.push({ this.slots.push({
pos: slot.pos, pos: slot.pos,
directions: slot.directions, direction: slot.direction,
// Which type of item to accept (shape | color | all) @see ItemType // Which type of item to accept (shape | color | all) @see ItemType
filter: slot.filter, filter: slot.filter,
@ -73,31 +88,28 @@ export class ItemAcceptorComponent extends Component {
} }
/** /**
* Returns if this acceptor can accept a new item at slot N * Called when trying to input a new item
*
* NOTICE: The belt path ignores this for performance reasons and does his own check
* @param {number} slotIndex
* @param {BaseItem=} item
*/
canAcceptItem(slotIndex, item) {
const slot = this.slots[slotIndex];
return !slot.filter || slot.filter === item.getItemType();
}
/**
* Called when an item has been accepted so that
* @param {number} slotIndex * @param {number} slotIndex
* @param {enumDirection} direction * @param {enumDirection} direction
* @param {BaseItem} item * @param {BaseItem} item
* @param {number} remainingProgress World space remaining progress, can be set to set the start position of the item * @param {number} startProgress World space remaining progress, can be set to set the start position of the item
* @returns {boolean} if the input was succesful
*/ */
onItemAccepted(slotIndex, direction, item, remainingProgress = 0.0) { tryAcceptItem(slotIndex, direction, item, startProgress = 0.0) {
this.itemConsumptionAnimations.push({ const slot = this.slots[slotIndex];
//@SENSETODO see if this works for buildings like hub
if (this.currentInputs.has(slotIndex) || (slot.filter && slot.filter != item.getItemType())) {
return false;
}
this.currentInputs.set(slotIndex, {
item, item,
slotIndex,
direction, direction,
animProgress: Math.min(1, remainingProgress * 2), animProgress: Math.min(1, startProgress),
}); });
return true;
} }
/** /**
@ -121,16 +133,12 @@ export class ItemAcceptorComponent extends Component {
continue; continue;
} }
// Check if the acceptor slot accepts items from our direction if (desiredDirection === slot.direction) {
for (let i = 0; i < slot.directions.length; ++i) { return {
// const localDirection = targetStaticComp.localDirectionToWorld(slot.directions[l]); slot,
if (desiredDirection === slot.directions[i]) { index: slotIndex,
return { acceptedDirection: desiredDirection,
slot, };
index: slotIndex,
acceptedDirection: desiredDirection,
};
}
} }
} }

View File

@ -70,7 +70,7 @@ export class ItemEjectorComponent extends Component {
direction: slot.direction, direction: slot.direction,
item: null, item: null,
lastItem: null, lastItem: null,
progress: 0, progress: 0, //@SENSETODO needs to be started off with extra time
cachedDestSlot: null, cachedDestSlot: null,
cachedTargetEntity: null, cachedTargetEntity: null,
}); });
@ -127,28 +127,16 @@ export class ItemEjectorComponent extends Component {
* Tries to eject a given item * Tries to eject a given item
* @param {number} slotIndex * @param {number} slotIndex
* @param {BaseItem} item * @param {BaseItem} item
* @param {number} startingProgress
* @returns {boolean} * @returns {boolean}
*/ */
tryEject(slotIndex, item) { tryEject(slotIndex, item, startingProgress = 0.0) {
if (!this.canEjectOnSlot(slotIndex)) { if (!this.canEjectOnSlot(slotIndex)) {
return false; return false;
} }
this.slots[slotIndex].item = item; this.slots[slotIndex].item = item;
this.slots[slotIndex].lastItem = item; this.slots[slotIndex].lastItem = item;
this.slots[slotIndex].progress = 0; this.slots[slotIndex].progress = startingProgress;
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;
}
} }

View File

@ -27,16 +27,23 @@ export const enumItemProcessorRequirements = {
painterQuad: "painterQuad", painterQuad: "painterQuad",
}; };
/** @typedef {{ /**
* @typedef {{
* item: BaseItem, * item: BaseItem,
* requiredSlot?: number, * requiredSlot?: number,
* preferredSlot?: number * preferredSlot?: number
* }} EjectorItemToEject */ * }} EjectorItemToEject
*
/** @typedef {{ * @typedef {{
* remainingTime: number, * remainingTime: number,
* items: Array<EjectorItemToEject>, * items: Array<EjectorItemToEject>,
* }} EjectorCharge */ * }} EjectorCharge
*
* @typedef {{
* item: BaseItem
* extraProgress: number
* }} ItemProcessorInput
*/
export class ItemProcessorComponent extends Component { export class ItemProcessorComponent extends Component {
static getId() { static getId() {
@ -75,9 +82,9 @@ export class ItemProcessorComponent extends Component {
/** /**
* Our current inputs * Our current inputs
* @type {Map<number, BaseItem>} * @type {Map<number, ItemProcessorInput>}
*/ */
this.inputSlots = new Map(); this.inputs = new Map();
this.clear(); this.clear();
} }
@ -88,13 +95,7 @@ export class ItemProcessorComponent extends Component {
// sure the outputs always match // sure the outputs always match
this.nextOutputSlot = 0; this.nextOutputSlot = 0;
this.inputSlots.clear(); this.inputs.clear();
/**
* Current input count
* @type {number}
*/
this.inputCount = 0;
/** /**
* What we are currently processing, empty if we don't produce anything rn * What we are currently processing, empty if we don't produce anything rn
@ -110,30 +111,4 @@ export class ItemProcessorComponent extends Component {
*/ */
this.bonusTime = 0; this.bonusTime = 0;
} }
/**
* Tries to take the item
* @param {BaseItem} item
* @param {number} sourceSlot
*/
tryTakeItem(item, sourceSlot) {
if (
this.type === enumItemProcessorTypes.hub ||
this.type === enumItemProcessorTypes.trash ||
this.type === enumItemProcessorTypes.goal
) {
// Hub has special logic .. not really nice but efficient.
this.inputSlots.set(this.inputCount, item);
this.inputCount++;
return true;
}
// Check that we only take one item per slot
if (this.inputSlots.has(sourceSlot)) {
return false;
}
this.inputSlots.set(sourceSlot, item);
this.inputCount++;
return true;
}
} }

View File

@ -500,65 +500,58 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic {
const acceptorSlotWsTile = staticComp.localTileToWorld(slot.pos); const acceptorSlotWsTile = staticComp.localTileToWorld(slot.pos);
const acceptorSlotWsPos = acceptorSlotWsTile.toWorldSpaceCenterOfTile(); const acceptorSlotWsPos = acceptorSlotWsTile.toWorldSpaceCenterOfTile();
// Go over all slots const direction = slot.direction;
for ( const worldDirection = staticComp.localDirectionToWorld(direction);
let acceptorDirectionIndex = 0;
acceptorDirectionIndex < slot.directions.length;
++acceptorDirectionIndex
) {
const direction = slot.directions[acceptorDirectionIndex];
const worldDirection = staticComp.localDirectionToWorld(direction);
// Figure out which tile ejects to this slot // Figure out which tile ejects to this slot
const sourceTile = acceptorSlotWsTile.add(enumDirectionToVector[worldDirection]); const sourceTile = acceptorSlotWsTile.add(enumDirectionToVector[worldDirection]);
let isBlocked = false; let isBlocked = false;
let isConnected = false; let isConnected = false;
// Find all entities which are on that tile // Find all entities which are on that tile
const sourceEntities = this.root.map.getLayersContentsMultipleXY(sourceTile.x, sourceTile.y); const sourceEntities = this.root.map.getLayersContentsMultipleXY(sourceTile.x, sourceTile.y);
// Check for every entity: // Check for every entity:
for (let i = 0; i < sourceEntities.length; ++i) { for (let i = 0; i < sourceEntities.length; ++i) {
const sourceEntity = sourceEntities[i]; const sourceEntity = sourceEntities[i];
const sourceEjector = sourceEntity.components.ItemEjector; const sourceEjector = sourceEntity.components.ItemEjector;
const sourceBeltComp = sourceEntity.components.Belt; const sourceBeltComp = sourceEntity.components.Belt;
const sourceStaticComp = sourceEntity.components.StaticMapEntity; const sourceStaticComp = sourceEntity.components.StaticMapEntity;
const ejectorAcceptLocalTile = sourceStaticComp.worldToLocalTile(acceptorSlotWsTile); const ejectorAcceptLocalTile = sourceStaticComp.worldToLocalTile(acceptorSlotWsTile);
// If this entity is on the same layer as the slot - if so, it can either be // If this entity is on the same layer as the slot - if so, it can either be
// connected, or it can not be connected and thus block the input // connected, or it can not be connected and thus block the input
if (sourceEjector && sourceEjector.anySlotEjectsToLocalTile(ejectorAcceptLocalTile)) { if (sourceEjector && sourceEjector.anySlotEjectsToLocalTile(ejectorAcceptLocalTile)) {
// This one is connected, all good // This one is connected, all good
isConnected = true; isConnected = true;
} else if ( } else if (
sourceBeltComp && sourceBeltComp &&
sourceStaticComp.localDirectionToWorld(sourceBeltComp.direction) === sourceStaticComp.localDirectionToWorld(sourceBeltComp.direction) ===
enumInvertedDirections[worldDirection] enumInvertedDirections[worldDirection]
) { ) {
// Belt connected // Belt connected
isConnected = true; isConnected = true;
} else { } else {
// This one is blocked // This one is blocked
isBlocked = true; isBlocked = true;
}
} }
const alpha = isConnected || isBlocked ? 1.0 : 0.3;
const sprite = isBlocked ? badArrowSprite : goodArrowSprite;
parameters.context.globalAlpha = alpha;
drawRotatedSprite({
parameters,
sprite,
x: acceptorSlotWsPos.x,
y: acceptorSlotWsPos.y,
angle: Math.radians(enumDirectionToAngle[enumInvertedDirections[worldDirection]]),
size: 13,
offsetY: offsetShift + 13,
});
parameters.context.globalAlpha = 1;
} }
const alpha = isConnected || isBlocked ? 1.0 : 0.3;
const sprite = isBlocked ? badArrowSprite : goodArrowSprite;
parameters.context.globalAlpha = alpha;
drawRotatedSprite({
parameters,
sprite,
x: acceptorSlotWsPos.x,
y: acceptorSlotWsPos.y,
angle: Math.radians(enumDirectionToAngle[enumInvertedDirections[worldDirection]]),
size: 13,
offsetY: offsetShift + 13,
});
parameters.context.globalAlpha = 1;
} }
// Go over all slots // Go over all slots

View File

@ -431,22 +431,20 @@ export class GameLogic {
} }
} }
for (let acceptorSlot = 0; acceptorSlot < acceptorSlots.length; ++acceptorSlot) { for (let i = 0; i < acceptorSlots.length; ++i) {
const slot = acceptorSlots[acceptorSlot]; const slot = acceptorSlots[i];
const wsTile = staticComp.localTileToWorld(slot.pos); const wsTile = staticComp.localTileToWorld(slot.pos);
for (let k = 0; k < slot.directions.length; ++k) { const direction = slot.direction;
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,
}); });
}
} }
} }
} }

View File

@ -1,58 +1,67 @@
import { globalConfig } from "../../core/config"; import { globalConfig } from "../../core/config";
import { DrawParameters } from "../../core/draw_parameters"; import { DrawParameters } from "../../core/draw_parameters";
import { fastArrayDelete } from "../../core/utils";
import { enumDirectionToVector } from "../../core/vector"; import { enumDirectionToVector } from "../../core/vector";
import { ItemAcceptorComponent } from "../components/item_acceptor"; import {
enumItemAcceptorTypes,
ItemAcceptorComponent,
InputCompletedArgs,
} from "../components/item_acceptor";
import { GameSystemWithFilter } from "../game_system_with_filter"; import { GameSystemWithFilter } from "../game_system_with_filter";
import { ShapeItem } from "../items/shape_item";
import { MapChunkView } from "../map_chunk_view"; import { MapChunkView } from "../map_chunk_view";
export class ItemAcceptorSystem extends GameSystemWithFilter { export class ItemAcceptorSystem extends GameSystemWithFilter {
constructor(root) { constructor(root) {
super(root, [ItemAcceptorComponent]); super(root, [ItemAcceptorComponent]);
// Well ... it's better to be verbose I guess? /**
this.accumulatedTicksWhileInMapOverview = 0; * @type {Object<enumItemAcceptorTypes, function(InputCompletedArgs) : string>}
*/
this.handlers = {
[enumItemAcceptorTypes.itemProcessor]: this.input_ITEMPROCESSOR,
[enumItemAcceptorTypes.hub]: this.input_HUB,
[enumItemAcceptorTypes.trash]: this.input_TRASH,
};
// Bind all handlers
for (const key in this.handlers) {
this.handlers[key] = this.handlers[key].bind(this);
}
} }
update() { update() {
if (this.root.app.settings.getAllSettings().simplifiedBelts) { // * 2 because its only a half tile - (same code as ejector)
// Disabled in potato mode const progressGrowth =
return;
}
// This system doesn't render anything while in map overview,
// so simply accumulate ticks
if (this.root.camera.getIsMapOverlayActive()) {
++this.accumulatedTicksWhileInMapOverview;
return;
}
// Compute how much ticks we missed
const numTicks = 1 + this.accumulatedTicksWhileInMapOverview;
const progress =
this.root.dynamicTickrate.deltaSeconds *
2 * 2 *
this.root.dynamicTickrate.deltaSeconds *
this.root.hubGoals.getBeltBaseSpeed() * this.root.hubGoals.getBeltBaseSpeed() *
globalConfig.itemSpacingOnBelts * // * 2 because its only a half tile globalConfig.itemSpacingOnBelts;
numTicks;
// Reset accumulated ticks
this.accumulatedTicksWhileInMapOverview = 0;
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];
const aceptorComp = entity.components.ItemAcceptor; const acceptorComp = entity.components.ItemAcceptor;
const animations = aceptorComp.itemConsumptionAnimations; const inputs = acceptorComp.currentInputs;
// Process item consumption animations to avoid items popping from the belts inputs.forEach((values, index) => {
for (let animIndex = 0; animIndex < animations.length; ++animIndex) { if (values.animProgress >= 1) return; // items which are inputted already
const anim = animations[animIndex];
anim.animProgress += progress; values.animProgress += progressGrowth;
if (anim.animProgress > 1) {
fastArrayDelete(animations, animIndex); if (values.animProgress < 1) return;
animIndex -= 1;
} /** @type {function(InputCompletedArgs) : string} */
} const handler = this.handlers[acceptorComp.type];
assert(handler, "No handler for acceptor type defined: " + acceptorComp.type);
// Call implementation
handler({
root: this.root,
entity,
item: values.item,
slotIndex: index,
extraProgress: values.animProgress - 1,
});
});
} }
} }
@ -75,17 +84,16 @@ export class ItemAcceptorSystem extends GameSystemWithFilter {
} }
const staticComp = entity.components.StaticMapEntity; const staticComp = entity.components.StaticMapEntity;
for (let animIndex = 0; animIndex < acceptorComp.itemConsumptionAnimations.length; ++animIndex) { acceptorComp.currentInputs.forEach((values, index) => {
const { item, slotIndex, animProgress, direction } = acceptorComp.itemConsumptionAnimations[ const { item, animProgress, direction } = values;
animIndex if (animProgress >= 1) return; // items which are inputted already
];
const slotData = acceptorComp.slots[slotIndex]; const slotData = acceptorComp.slots[index];
const realSlotPos = staticComp.localTileToWorld(slotData.pos); const realSlotPos = staticComp.localTileToWorld(slotData.pos);
if (!chunk.tileSpaceRectangle.containsPoint(realSlotPos.x, realSlotPos.y)) { if (!chunk.tileSpaceRectangle.containsPoint(realSlotPos.x, realSlotPos.y)) {
// Not within this chunk // Not within this chunk
continue; return;
} }
const fadeOutDirection = enumDirectionToVector[staticComp.localDirectionToWorld(direction)]; const fadeOutDirection = enumDirectionToVector[staticComp.localDirectionToWorld(direction)];
@ -100,7 +108,49 @@ export class ItemAcceptorSystem extends GameSystemWithFilter {
parameters, parameters,
globalConfig.defaultItemDiameter globalConfig.defaultItemDiameter
); );
} });
} }
} }
/**
* @param {InputCompletedArgs} args
*/
input_ITEMPROCESSOR(args) {
const entity = args.entity;
const itemProcessorComp = entity.components.ItemProcessor;
assert(itemProcessorComp, "No item processor to input item to");
itemProcessorComp.inputs.set(args.slotIndex, {
item: args.item,
extraProgress: args.extraProgress,
}); // in the future the item processor will not need a list of items
}
//@SENSETODO this isn't set up like it should be yet
/**
* @param {InputCompletedArgs} args
*/
input_HUB(args) {
const item = /** @type {ShapeItem} */ (args.item);
assert(item instanceof ShapeItem, "Input for hub is not a shape");
this.root.hubGoals.handleDefinitionDelivered(item.definition);
const acceptorComp = args.entity.components.ItemAcceptor;
acceptorComp.currentInputs.delete(args.slotIndex);
}
/**
* @param {InputCompletedArgs} args
*/
input_TRASH(args) {
// just remove the item
const acceptorComp = args.entity.components.ItemAcceptor;
acceptorComp.currentInputs.delete(args.slotIndex);
}
//storage
//underground belt
} }

View File

@ -139,169 +139,76 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
update() { update() {
this.staleAreaDetector.update(); this.staleAreaDetector.update();
// Precompute effective belt speed // * 2 because its only a half tile - (same code as acceptor)
let progressGrowth = 2 * this.root.dynamicTickrate.deltaSeconds; const progressGrowth =
2 *
if (G_IS_DEV && globalConfig.debug.instantBelts) { this.root.dynamicTickrate.deltaSeconds *
progressGrowth = 1; this.root.hubGoals.getBeltBaseSpeed() *
} globalConfig.itemSpacingOnBelts;
// Go over all cache entries // Go over all cache entries
for (let i = 0; i < this.allEntities.length; ++i) { for (let i = 0; i < this.allEntities.length; ++i) {
const sourceEntity = this.allEntities[i]; const entity = this.allEntities[i];
const sourceEjectorComp = sourceEntity.components.ItemEjector; const ejectorComp = entity.components.ItemEjector;
const slots = ejectorComp.slots;
const slots = sourceEjectorComp.slots;
for (let j = 0; j < slots.length; ++j) { for (let j = 0; j < slots.length; ++j) {
const sourceSlot = slots[j]; const slot = slots[j];
const item = sourceSlot.item; const item = slot.item;
if (!item) { if (!item) {
// No item available to be ejected // No output in progress
continue; continue;
} }
// Advance items on the slot // Advance items on the slot
sourceSlot.progress = Math.min( // @SENSETODO do we really want to cap it at one, or should the excess get passed on?
1, slot.progress = Math.min(1, slot.progress + progressGrowth);
sourceSlot.progress +
progressGrowth *
this.root.hubGoals.getBeltBaseSpeed() *
globalConfig.itemSpacingOnBelts
);
if (G_IS_DEV && globalConfig.debug.disableEjectorProcessing) { if (G_IS_DEV && globalConfig.debug.disableEjectorProcessing) {
sourceSlot.progress = 1.0; slot.progress = 1;
} }
// Check if we are still in the process of ejecting, can't proceed then // Check if we are still in the process of ejecting, can't proceed then
if (sourceSlot.progress < 1.0) { if (slot.progress < 1) {
continue; continue;
} }
// Check if we are ejecting to a belt path // Check if we are ejecting to a belt path
const destPath = sourceSlot.cachedBeltPath; const destPath = slot.cachedBeltPath;
if (destPath) { if (destPath) {
// Try passing the item over // Try passing the item over
if (destPath.tryAcceptItem(item)) { if (destPath.tryAcceptItem(item)) {
sourceSlot.item = null; slot.item = null;
} }
// Always stop here, since there can *either* be a belt path *or* // Always stop here, since there can *either* be a belt path *or* an acceptor
// a slot
continue; continue;
} }
// Check if the target acceptor can actually accept this item // Check if the target acceptor can actually accept this item
const destEntity = sourceSlot.cachedTargetEntity; const destEntity = slot.cachedTargetEntity;
const destSlot = sourceSlot.cachedDestSlot; const destSlot = slot.cachedDestSlot;
if (destSlot) { if (destEntity && destSlot) {
const targetAcceptorComp = destEntity.components.ItemAcceptor; const targetAcceptorComp = destEntity.components.ItemAcceptor;
if (!targetAcceptorComp.canAcceptItem(destSlot.index, item)) { const extraProgress = slot.progress - 1;
continue; if (
} targetAcceptorComp.tryAcceptItem(
destSlot.index,
// Try to hand over the item destSlot.acceptedDirection,
if (this.tryPassOverItem(item, destEntity, destSlot.index)) { item,
extraProgress
)
) {
// Handover successful, clear slot // Handover successful, clear slot
if (!this.root.app.settings.getAllSettings().simplifiedBelts) { slot.item = null;
targetAcceptorComp.onItemAccepted(
destSlot.index,
destSlot.acceptedDirection,
item
);
}
sourceSlot.item = null;
continue;
} }
} }
//@SENSETODO deal with other buildings - acceptor code on them needs to be different!
} }
} }
} }
/**
*
* @param {BaseItem} item
* @param {Entity} receiver
* @param {number} slotIndex
*/
tryPassOverItem(item, receiver, slotIndex) {
// Try figuring out how what to do with the item
// @TODO: Kinda hacky. How to solve this properly? Don't want to go through inheritance hell.
const beltComp = receiver.components.Belt;
if (beltComp) {
const path = beltComp.assignedPath;
assert(path, "belt has no path");
if (path.tryAcceptItem(item)) {
return true;
}
// Belt can have nothing else
return false;
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
// NOTICE ! THIS CODE IS DUPLICATED IN THE BELT PATH FOR PERFORMANCE REASONS
//
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
const itemProcessorComp = receiver.components.ItemProcessor;
if (itemProcessorComp) {
// Check for potential filters
if (!this.root.systemMgr.systems.itemProcessor.checkRequirements(receiver, item, slotIndex)) {
return false;
}
// Its an item processor ..
if (itemProcessorComp.tryTakeItem(item, slotIndex)) {
return true;
}
// Item processor can have nothing else
return false;
}
const undergroundBeltComp = receiver.components.UndergroundBelt;
if (undergroundBeltComp) {
// Its an underground belt. yay.
if (
undergroundBeltComp.tryAcceptExternalItem(
item,
this.root.hubGoals.getUndergroundBeltBaseSpeed()
)
) {
return true;
}
// Underground belt can have nothing else
return false;
}
const storageComp = receiver.components.Storage;
if (storageComp) {
// It's a storage
if (storageComp.canAcceptItem(item)) {
storageComp.takeItem(item);
return true;
}
// Storage can't have anything else
return false;
}
const filterComp = receiver.components.Filter;
if (filterComp) {
// It's a filter! Unfortunately the filter has to know a lot about it's
// surrounding state and components, so it can't be within the component itself.
if (this.root.systemMgr.systems.filter.tryAcceptItem(receiver, slotIndex, item)) {
return true;
}
}
return false;
}
/** /**
* @param {DrawParameters} parameters * @param {DrawParameters} parameters
* @param {MapChunkView} chunk * @param {MapChunkView} chunk
@ -390,11 +297,6 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
progress = Math.min(maxProgress, progress); progress = Math.min(maxProgress, progress);
} }
// Skip if the item would barely be visible
if (progress < 0.05) {
continue;
}
const realPosition = staticComp.localTileToWorld(slot.pos); const realPosition = staticComp.localTileToWorld(slot.pos);
if (!chunk.tileSpaceRectangle.containsPoint(realPosition.x, realPosition.y)) { if (!chunk.tileSpaceRectangle.containsPoint(realPosition.x, realPosition.y)) {
// Not within this chunk // Not within this chunk
@ -419,4 +321,90 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
} }
} }
} }
/////////////////////////////////////////////////////// OBSOLETE
/**
*
* @param {BaseItem} item
* @param {Entity} receiver
* @param {number} slotIndex
*/
tryPassOverItem(item, receiver, slotIndex) {
// Try figuring out how what to do with the item
// @TODO: Kinda hacky. How to solve this properly? Don't want to go through inheritance hell.
const beltComp = receiver.components.Belt;
if (beltComp) {
const path = beltComp.assignedPath;
assert(path, "belt has no path");
if (path.tryAcceptItem(item)) {
return true;
}
// Belt can have nothing else
return false;
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
// NOTICE ! THIS CODE IS DUPLICATED IN THE BELT PATH FOR PERFORMANCE REASONS
//
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
const itemProcessorComp = receiver.components.ItemProcessor;
if (itemProcessorComp) {
// Check for potential filters
if (!this.root.systemMgr.systems.itemProcessor.checkRequirements(receiver, item, slotIndex)) {
return false;
}
// Its an item processor ..
//if (itemProcessorComp.tryTakeItem(item, slotIndex)) {
// return true;
//}
// Item processor can have nothing else
return false;
}
const undergroundBeltComp = receiver.components.UndergroundBelt;
if (undergroundBeltComp) {
// Its an underground belt. yay.
if (
undergroundBeltComp.tryAcceptExternalItem(
item,
this.root.hubGoals.getUndergroundBeltBaseSpeed()
)
) {
return true;
}
// Underground belt can have nothing else
return false;
}
const storageComp = receiver.components.Storage;
if (storageComp) {
// It's a storage
if (storageComp.canAcceptItem(item)) {
storageComp.takeItem(item);
return true;
}
// Storage can't have anything else
return false;
}
const filterComp = receiver.components.Filter;
if (filterComp) {
// It's a filter! Unfortunately the filter has to know a lot about it's
// surrounding state and components, so it can't be within the component itself.
if (this.root.systemMgr.systems.filter.tryAcceptItem(receiver, slotIndex, item)) {
return true;
}
}
return false;
}
} }

View File

@ -15,6 +15,7 @@ import { ShapeItem } from "../items/shape_item";
/** /**
* We need to allow queuing charges, otherwise the throughput will stall * We need to allow queuing charges, otherwise the throughput will stall
*/ */
//@SENSETODO not sure if this is true anymore
const MAX_QUEUED_CHARGES = 2; const MAX_QUEUED_CHARGES = 2;
/** /**
@ -22,6 +23,7 @@ const MAX_QUEUED_CHARGES = 2;
* *
* @typedef {{ * @typedef {{
* item: BaseItem, * item: BaseItem,
* extraProgress?: number,
* preferredSlot?: number, * preferredSlot?: number,
* requiredSlot?: number, * requiredSlot?: number,
* doNotTrack?: boolean * doNotTrack?: boolean
@ -32,8 +34,7 @@ const MAX_QUEUED_CHARGES = 2;
* Type of a processor implementation * Type of a processor implementation
* @typedef {{ * @typedef {{
* entity: Entity, * entity: Entity,
* items: Map<number, BaseItem>, * inputs: Map<number, import("../components/item_processor").ItemProcessorInput>,
* inputCount: number,
* outItems: Array<ProducedItem> * outItems: Array<ProducedItem>
* }} ProcessorImplementationPayload * }} ProcessorImplementationPayload
*/ */
@ -133,6 +134,8 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
} }
} }
//@SENSETODO add code in here that handles queued outputs -rearrange where this is too
// If the charge was entirely emptied to the outputs, start the next charge // If the charge was entirely emptied to the outputs, start the next charge
if (itemsToEject.length === 0) { if (itemsToEject.length === 0) {
processorComp.ongoingCharges.shift(); processorComp.ongoingCharges.shift();
@ -195,7 +198,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
// DEFAULT // DEFAULT
// By default, we can start processing once all inputs are there // By default, we can start processing once all inputs are there
case null: { case null: {
return processorComp.inputCount >= processorComp.inputsPerCharge; return processorComp.inputs.size >= processorComp.inputsPerCharge;
} }
// QUAD PAINTER // QUAD PAINTER
@ -204,7 +207,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
const pinsComp = entity.components.WiredPins; const pinsComp = entity.components.WiredPins;
// First slot is the shape, so if it's not there we can't do anything // First slot is the shape, so if it's not there we can't do anything
const shapeItem = /** @type {ShapeItem} */ (processorComp.inputSlots.get(0)); const shapeItem = /** @type {ShapeItem} */ (processorComp.inputs.get(0).item);
if (!shapeItem) { if (!shapeItem) {
return false; return false;
} }
@ -233,7 +236,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
// Check if all colors of the enabled slots are there // Check if all colors of the enabled slots are there
for (let i = 0; i < slotStatus.length; ++i) { for (let i = 0; i < slotStatus.length; ++i) {
if (slotStatus[i] && !processorComp.inputSlots.get(1 + i)) { if (slotStatus[i] && !processorComp.inputs.get(1 + i)) {
// A slot which is enabled wasn't enabled. Make sure if there is anything on the quadrant, // A slot which is enabled wasn't enabled. Make sure if there is anything on the quadrant,
// it is not possible to paint, but if there is nothing we can ignore it // it is not possible to paint, but if there is nothing we can ignore it
for (let j = 0; j < 4; ++j) { for (let j = 0; j < 4; ++j) {
@ -260,8 +263,8 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
startNewCharge(entity) { startNewCharge(entity) {
const processorComp = entity.components.ItemProcessor; const processorComp = entity.components.ItemProcessor;
// First, take items // First, take inputs
const items = processorComp.inputSlots; const inputs = processorComp.inputs;
/** @type {Array<ProducedItem>} */ /** @type {Array<ProducedItem>} */
const outItems = []; const outItems = [];
@ -273,9 +276,8 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
// Call implementation // Call implementation
handler({ handler({
entity, entity,
items, inputs: inputs,
outItems, outItems,
inputCount: processorComp.inputCount,
}); });
// Track produced items // Track produced items
@ -298,8 +300,8 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
remainingTime: timeToProcess, remainingTime: timeToProcess,
}); });
processorComp.inputSlots.clear(); processorComp.inputs.clear(); //@SENSETODO temp, will completely replace processorcomp.inputs with acceptor inputs later
processorComp.inputCount = 0; entity.components.ItemAcceptor.currentInputs.clear();
} }
/** /**
@ -314,12 +316,12 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
const processorComp = payload.entity.components.ItemProcessor; const processorComp = payload.entity.components.ItemProcessor;
for (let i = 0; i < 2; ++i) { for (let i = 0; i < 2; ++i) {
const item = payload.items.get(i); const input = payload.inputs.get(i);
if (!item) { if (!input || !input.item) {
continue; continue;
} }
payload.outItems.push({ payload.outItems.push({
item, item: input.item,
preferredSlot: processorComp.nextOutputSlot++ % availableSlots, preferredSlot: processorComp.nextOutputSlot++ % availableSlots,
doNotTrack: true, doNotTrack: true,
}); });
@ -331,7 +333,8 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
* @param {ProcessorImplementationPayload} payload * @param {ProcessorImplementationPayload} payload
*/ */
process_CUTTER(payload) { process_CUTTER(payload) {
const inputItem = /** @type {ShapeItem} */ (payload.items.get(0)); const input = payload.inputs.get(0);
const inputItem = /** @type {ShapeItem} */ (input.item);
assert(inputItem instanceof ShapeItem, "Input for cut is not a shape"); assert(inputItem instanceof ShapeItem, "Input for cut is not a shape");
const inputDefinition = inputItem.definition; const inputDefinition = inputItem.definition;
@ -357,7 +360,8 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
* @param {ProcessorImplementationPayload} payload * @param {ProcessorImplementationPayload} payload
*/ */
process_CUTTER_QUAD(payload) { process_CUTTER_QUAD(payload) {
const inputItem = /** @type {ShapeItem} */ (payload.items.get(0)); const input = payload.inputs.get(0);
const inputItem = /** @type {ShapeItem} */ (input.item);
assert(inputItem instanceof ShapeItem, "Input for cut is not a shape"); assert(inputItem instanceof ShapeItem, "Input for cut is not a shape");
const inputDefinition = inputItem.definition; const inputDefinition = inputItem.definition;
@ -383,7 +387,8 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
* @param {ProcessorImplementationPayload} payload * @param {ProcessorImplementationPayload} payload
*/ */
process_ROTATER(payload) { process_ROTATER(payload) {
const inputItem = /** @type {ShapeItem} */ (payload.items.get(0)); const input = payload.inputs.get(0);
const inputItem = /** @type {ShapeItem} */ (input.item);
assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape"); assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape");
const inputDefinition = inputItem.definition; const inputDefinition = inputItem.definition;
@ -397,7 +402,8 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
* @param {ProcessorImplementationPayload} payload * @param {ProcessorImplementationPayload} payload
*/ */
process_ROTATER_CCW(payload) { process_ROTATER_CCW(payload) {
const inputItem = /** @type {ShapeItem} */ (payload.items.get(0)); const input = payload.inputs.get(0);
const inputItem = /** @type {ShapeItem} */ (input.item);
assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape"); assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape");
const inputDefinition = inputItem.definition; const inputDefinition = inputItem.definition;
@ -411,7 +417,8 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
* @param {ProcessorImplementationPayload} payload * @param {ProcessorImplementationPayload} payload
*/ */
process_ROTATER_180(payload) { process_ROTATER_180(payload) {
const inputItem = /** @type {ShapeItem} */ (payload.items.get(0)); const input = payload.inputs.get(0);
const inputItem = /** @type {ShapeItem} */ (input.item);
assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape"); assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape");
const inputDefinition = inputItem.definition; const inputDefinition = inputItem.definition;
@ -425,8 +432,10 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
* @param {ProcessorImplementationPayload} payload * @param {ProcessorImplementationPayload} payload
*/ */
process_STACKER(payload) { process_STACKER(payload) {
const lowerItem = /** @type {ShapeItem} */ (payload.items.get(0)); const lowerInput = payload.inputs.get(0);
const upperItem = /** @type {ShapeItem} */ (payload.items.get(1)); const upperInput = payload.inputs.get(0);
const lowerItem = /** @type {ShapeItem} */ (lowerInput.item);
const upperItem = /** @type {ShapeItem} */ (upperInput.item);
assert(lowerItem instanceof ShapeItem, "Input for lower stack is not a shape"); assert(lowerItem instanceof ShapeItem, "Input for lower stack is not a shape");
assert(upperItem instanceof ShapeItem, "Input for upper stack is not a shape"); assert(upperItem instanceof ShapeItem, "Input for upper stack is not a shape");
@ -451,9 +460,11 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
* @param {ProcessorImplementationPayload} payload * @param {ProcessorImplementationPayload} payload
*/ */
process_MIXER(payload) { process_MIXER(payload) {
const input1 = payload.inputs.get(0);
const input2 = payload.inputs.get(1);
// Find both colors and combine them // Find both colors and combine them
const item1 = /** @type {ColorItem} */ (payload.items.get(0)); const item1 = /** @type {ColorItem} */ (input1.item);
const item2 = /** @type {ColorItem} */ (payload.items.get(1)); const item2 = /** @type {ColorItem} */ (input2.item);
assert(item1 instanceof ColorItem, "Input for color mixer is not a color"); assert(item1 instanceof ColorItem, "Input for color mixer is not a color");
assert(item2 instanceof ColorItem, "Input for color mixer is not a color"); assert(item2 instanceof ColorItem, "Input for color mixer is not a color");
@ -475,8 +486,10 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
* @param {ProcessorImplementationPayload} payload * @param {ProcessorImplementationPayload} payload
*/ */
process_PAINTER(payload) { process_PAINTER(payload) {
const shapeItem = /** @type {ShapeItem} */ (payload.items.get(0)); const input1 = payload.inputs.get(0);
const colorItem = /** @type {ColorItem} */ (payload.items.get(1)); const input2 = payload.inputs.get(1);
const shapeItem = /** @type {ShapeItem} */ (input1.item);
const colorItem = /** @type {ColorItem} */ (input2.item);
const colorizedDefinition = this.root.shapeDefinitionMgr.shapeActionPaintWith( const colorizedDefinition = this.root.shapeDefinitionMgr.shapeActionPaintWith(
shapeItem.definition, shapeItem.definition,
@ -492,9 +505,9 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
* @param {ProcessorImplementationPayload} payload * @param {ProcessorImplementationPayload} payload
*/ */
process_PAINTER_DOUBLE(payload) { process_PAINTER_DOUBLE(payload) {
const shapeItem1 = /** @type {ShapeItem} */ (payload.items.get(0)); const shapeItem1 = /** @type {ShapeItem} */ (payload.inputs.get(0).item);
const shapeItem2 = /** @type {ShapeItem} */ (payload.items.get(1)); const shapeItem2 = /** @type {ShapeItem} */ (payload.inputs.get(1).item);
const colorItem = /** @type {ColorItem} */ (payload.items.get(2)); const colorItem = /** @type {ColorItem} */ (payload.inputs.get(2).item);
assert(shapeItem1 instanceof ShapeItem, "Input for painter is not a shape"); assert(shapeItem1 instanceof ShapeItem, "Input for painter is not a shape");
assert(shapeItem2 instanceof ShapeItem, "Input for painter is not a shape"); assert(shapeItem2 instanceof ShapeItem, "Input for painter is not a shape");
@ -522,13 +535,14 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
* @param {ProcessorImplementationPayload} payload * @param {ProcessorImplementationPayload} payload
*/ */
process_PAINTER_QUAD(payload) { process_PAINTER_QUAD(payload) {
const shapeItem = /** @type {ShapeItem} */ (payload.items.get(0)); const input = payload.inputs.get(0);
const shapeItem = /** @type {ShapeItem} */ (input.item);
assert(shapeItem instanceof ShapeItem, "Input for painter is not a shape"); assert(shapeItem instanceof ShapeItem, "Input for painter is not a shape");
/** @type {Array<enumColors>} */ /** @type {Array<enumColors>} */
const colors = [null, null, null, null]; const colors = [null, null, null, null];
for (let i = 0; i < 4; ++i) { for (let i = 0; i < 4; ++i) {
const colorItem = /** @type {ColorItem} */ (payload.items.get(i + 1)); const colorItem = /** @type {ColorItem} */ (payload.inputs.get(i + 1).item);
if (colorItem) { if (colorItem) {
colors[i] = colorItem.color; colors[i] = colorItem.color;
} }
@ -549,7 +563,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
*/ */
process_READER(payload) { process_READER(payload) {
// Pass through the item // Pass through the item
const item = payload.items.get(0); const item = payload.inputs.get(0).item;
payload.outItems.push({ payload.outItems.push({
item, item,
doNotTrack: true, doNotTrack: true,
@ -565,17 +579,17 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
* @param {ProcessorImplementationPayload} payload * @param {ProcessorImplementationPayload} payload
*/ */
process_HUB(payload) { process_HUB(payload) {
const hubComponent = payload.entity.components.Hub; //const input = payload.inputs.get(i);
assert(hubComponent, "Hub item processor has no hub component"); //const hubComponent = payload.entity.components.Hub;
//assert(hubComponent, "Hub item processor has no hub component");
// Hardcoded //// Hardcoded
for (let i = 0; i < payload.inputCount; ++i) { //for (let i = 0; i < payload.inputCount; ++i) {
const item = /** @type {ShapeItem} */ (payload.items.get(i)); // const item = /** @type {ShapeItem} */ (payload.inputs.get(i));
if (!item) { // if (!item) {
continue; // continue;
} // }
this.root.hubGoals.handleDefinitionDelivered(item.definition); // this.root.hubGoals.handleDefinitionDelivered(item.definition);
} //}
} }
/** /**
@ -583,7 +597,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
*/ */
process_GOAL(payload) { process_GOAL(payload) {
const goalComp = payload.entity.components.GoalAcceptor; const goalComp = payload.entity.components.GoalAcceptor;
const item = payload.items.get(0); const item = payload.inputs.get(0).item;
const now = this.root.time.now(); const now = this.root.time.now();
if (goalComp.item && !item.equals(goalComp.item)) { if (goalComp.item && !item.equals(goalComp.item)) {