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) {
if (this.spacingToFirstItem >= globalConfig.itemSpacingOnBelts) {
// 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.dynamicTickrate.deltaSeconds *
globalConfig.itemSpacingOnBelts;
// First, compute how much progress we can make *at max*
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.spacingToFirstItem = initialProgress;
@ -272,31 +272,10 @@ export class BeltPath extends BasicSerializableObject {
}
const matchingSlotIndex = matchingSlot.index;
const passOver = this.computePassOverFunctionWithoutBelts(targetEntity, matchingSlotIndex);
if (!passOver) {
return;
}
const matchingDirection = enumInvertedDirections[ejectingDirection];
const filter = matchingSlot.slot.filter;
return function (item, remainingProgress = 0.0) {
// Check if the acceptor has a filter
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 function (item, startProgress = 0.0) {
if (targetAcceptorComp.tryAcceptItem(matchingSlotIndex, matchingDirection, item, startProgress)) {
return true;
}
return false;
@ -313,7 +292,7 @@ export class BeltPath extends BasicSerializableObject {
const systems = this.root.systemMgr.systems;
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;
if (itemProcessorComp) {
@ -323,7 +302,7 @@ export class BeltPath extends BasicSerializableObject {
if (!systems.itemProcessor.checkRequirements(entity, item, matchingSlotIndex)) {
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([
{
pos: new Vector(0, 0),
directions: [enumDirection.bottom],
direction: enumDirection.bottom,
},
{
pos: new Vector(1, 0),
directions: [enumDirection.bottom],
direction: enumDirection.bottom,
},
]);
@ -204,15 +204,14 @@ export class MetaBalancerBuilding extends MetaBuilding {
entity.components.ItemAcceptor.setSlots([
{
pos: new Vector(0, 0),
directions: [enumDirection.bottom],
direction: enumDirection.bottom,
},
{
pos: new Vector(0, 0),
directions: [
direction:
variant === enumBalancerVariants.mergerInverse
? enumDirection.left
: enumDirection.right,
],
},
]);
@ -231,7 +230,7 @@ export class MetaBalancerBuilding extends MetaBuilding {
entity.components.ItemAcceptor.setSlots([
{
pos: new Vector(0, 0),
directions: [enumDirection.bottom],
direction: enumDirection.bottom,
},
]);

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
import { enumDirection, Vector } from "../../core/vector";
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 { Entity } from "../entity";
import { defaultBuildingVariant, MetaBuilding } from "../meta_building";
@ -75,75 +75,96 @@ export class MetaHubBuilding extends MetaBuilding {
slots: [
{
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",
},
{
pos: new Vector(1, 0),
directions: [enumDirection.top],
direction: enumDirection.top,
filter: "shape",
},
{
pos: new Vector(2, 0),
directions: [enumDirection.top],
direction: enumDirection.top,
filter: "shape",
},
{
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",
},
{
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",
},
{
pos: new Vector(1, 3),
directions: [enumDirection.bottom],
direction: enumDirection.bottom,
filter: "shape",
},
{
pos: new Vector(2, 3),
directions: [enumDirection.bottom],
direction: enumDirection.bottom,
filter: "shape",
},
{
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",
},
{
pos: new Vector(0, 1),
directions: [enumDirection.left],
direction: enumDirection.left,
filter: "shape",
},
{
pos: new Vector(0, 2),
directions: [enumDirection.left],
direction: enumDirection.left,
filter: "shape",
},
{
pos: new Vector(0, 3),
directions: [enumDirection.left],
direction: enumDirection.left,
filter: "shape",
},
{
pos: new Vector(3, 1),
directions: [enumDirection.right],
direction: enumDirection.right,
filter: "shape",
},
{
pos: new Vector(3, 2),
directions: [enumDirection.right],
direction: enumDirection.right,
filter: "shape",
},
{
pos: new Vector(3, 3),
directions: [enumDirection.right],
direction: enumDirection.right,
filter: "shape",
},
],
type: enumItemAcceptorTypes.hub,
})
);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,7 +8,7 @@ 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],
direction: enumDirection.bottom,
};
/** @type {Object<enumDirection, import("./item_ejector").ItemEjectorSlot>} */

View File

@ -1,27 +1,52 @@
import { enumDirection, enumInvertedDirections, Vector } from "../../core/vector";
import { types } from "../../savegame/serialization";
import { BaseItem } from "../base_item";
import { Component } from "../component";
/** @typedef {{
* pos: Vector,
* directions: enumDirection[],
* filter?: ItemType
* }} ItemAcceptorSlot */
import { Entity } from "../entity";
import { GameRoot } from "../root";
/**
* @typedef {{
* pos: Vector,
* direction: enumDirection,
* filter?: ItemType
* }} ItemAcceptorSlot
*
* Contains information about a slot plus its location
* @typedef {{
* slot: ItemAcceptorSlot,
* index: number,
* acceptedDirection: enumDirection
* }} ItemAcceptorLocatedSlot */
/** @typedef {{
* }} ItemAcceptorLocatedSlot
*
* @typedef {{
* pos: Vector,
* directions: enumDirection[],
* direction: enumDirection,
* 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 {
static getId() {
@ -32,25 +57,15 @@ export class ItemAcceptorComponent extends Component {
*
* @param {object} param0
* @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();
/** @type {ItemAcceptorInput} */
this.currentInputs = new Map(); // @SENSETODO does this need to be saved?
this.type = type;
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];
this.slots.push({
pos: slot.pos,
directions: slot.directions,
direction: slot.direction,
// Which type of item to accept (shape | color | all) @see ItemType
filter: slot.filter,
@ -73,31 +88,28 @@ export class ItemAcceptorComponent extends Component {
}
/**
* Returns if this acceptor can accept a new item at slot N
*
* 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
* Called when trying to input a new item
* @param {number} slotIndex
* @param {enumDirection} direction
* @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) {
this.itemConsumptionAnimations.push({
tryAcceptItem(slotIndex, direction, item, startProgress = 0.0) {
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,
slotIndex,
direction,
animProgress: Math.min(1, remainingProgress * 2),
animProgress: Math.min(1, startProgress),
});
return true;
}
/**
@ -121,10 +133,7 @@ export class ItemAcceptorComponent extends Component {
continue;
}
// Check if the acceptor slot accepts items from our direction
for (let i = 0; i < slot.directions.length; ++i) {
// const localDirection = targetStaticComp.localDirectionToWorld(slot.directions[l]);
if (desiredDirection === slot.directions[i]) {
if (desiredDirection === slot.direction) {
return {
slot,
index: slotIndex,
@ -132,7 +141,6 @@ export class ItemAcceptorComponent extends Component {
};
}
}
}
return null;
}

View File

@ -70,7 +70,7 @@ export class ItemEjectorComponent extends Component {
direction: slot.direction,
item: null,
lastItem: null,
progress: 0,
progress: 0, //@SENSETODO needs to be started off with extra time
cachedDestSlot: null,
cachedTargetEntity: null,
});
@ -127,28 +127,16 @@ export class ItemEjectorComponent extends Component {
* Tries to eject a given item
* @param {number} slotIndex
* @param {BaseItem} item
* @param {number} startingProgress
* @returns {boolean}
*/
tryEject(slotIndex, item) {
tryEject(slotIndex, item, startingProgress = 0.0) {
if (!this.canEjectOnSlot(slotIndex)) {
return false;
}
this.slots[slotIndex].item = item;
this.slots[slotIndex].lastItem = item;
this.slots[slotIndex].progress = 0;
this.slots[slotIndex].progress = startingProgress;
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",
};
/** @typedef {{
/**
* @typedef {{
* item: BaseItem,
* requiredSlot?: number,
* preferredSlot?: number
* }} EjectorItemToEject */
/** @typedef {{
* }} EjectorItemToEject
*
* @typedef {{
* remainingTime: number,
* items: Array<EjectorItemToEject>,
* }} EjectorCharge */
* }} EjectorCharge
*
* @typedef {{
* item: BaseItem
* extraProgress: number
* }} ItemProcessorInput
*/
export class ItemProcessorComponent extends Component {
static getId() {
@ -75,9 +82,9 @@ export class ItemProcessorComponent extends Component {
/**
* Our current inputs
* @type {Map<number, BaseItem>}
* @type {Map<number, ItemProcessorInput>}
*/
this.inputSlots = new Map();
this.inputs = new Map();
this.clear();
}
@ -88,13 +95,7 @@ export class ItemProcessorComponent extends Component {
// sure the outputs always match
this.nextOutputSlot = 0;
this.inputSlots.clear();
/**
* Current input count
* @type {number}
*/
this.inputCount = 0;
this.inputs.clear();
/**
* 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;
}
/**
* 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,13 +500,7 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic {
const acceptorSlotWsTile = staticComp.localTileToWorld(slot.pos);
const acceptorSlotWsPos = acceptorSlotWsTile.toWorldSpaceCenterOfTile();
// Go over all slots
for (
let acceptorDirectionIndex = 0;
acceptorDirectionIndex < slot.directions.length;
++acceptorDirectionIndex
) {
const direction = slot.directions[acceptorDirectionIndex];
const direction = slot.direction;
const worldDirection = staticComp.localDirectionToWorld(direction);
// Figure out which tile ejects to this slot
@ -559,7 +553,6 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic {
});
parameters.context.globalAlpha = 1;
}
}
// Go over all slots
for (let ejectorSlotIndex = 0; ejectorSlotIndex < ejectorSlots.length; ++ejectorSlotIndex) {

View File

@ -431,11 +431,10 @@ export class GameLogic {
}
}
for (let acceptorSlot = 0; acceptorSlot < acceptorSlots.length; ++acceptorSlot) {
const slot = acceptorSlots[acceptorSlot];
for (let i = 0; i < acceptorSlots.length; ++i) {
const slot = acceptorSlots[i];
const wsTile = staticComp.localTileToWorld(slot.pos);
for (let k = 0; k < slot.directions.length; ++k) {
const direction = slot.directions[k];
const direction = slot.direction;
const wsDirection = staticComp.localDirectionToWorld(direction);
const sourceTile = wsTile.add(enumDirectionToVector[wsDirection]);
@ -451,7 +450,6 @@ export class GameLogic {
}
}
}
}
return { ejectors, acceptors };
}

View File

@ -1,58 +1,67 @@
import { globalConfig } from "../../core/config";
import { DrawParameters } from "../../core/draw_parameters";
import { fastArrayDelete } from "../../core/utils";
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 { ShapeItem } from "../items/shape_item";
import { MapChunkView } from "../map_chunk_view";
export class ItemAcceptorSystem extends GameSystemWithFilter {
constructor(root) {
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() {
if (this.root.app.settings.getAllSettings().simplifiedBelts) {
// Disabled in potato mode
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 because its only a half tile - (same code as ejector)
const progressGrowth =
2 *
this.root.dynamicTickrate.deltaSeconds *
this.root.hubGoals.getBeltBaseSpeed() *
globalConfig.itemSpacingOnBelts * // * 2 because its only a half tile
numTicks;
// Reset accumulated ticks
this.accumulatedTicksWhileInMapOverview = 0;
globalConfig.itemSpacingOnBelts;
for (let i = 0; i < this.allEntities.length; ++i) {
const entity = this.allEntities[i];
const aceptorComp = entity.components.ItemAcceptor;
const animations = aceptorComp.itemConsumptionAnimations;
const acceptorComp = entity.components.ItemAcceptor;
const inputs = acceptorComp.currentInputs;
// Process item consumption animations to avoid items popping from the belts
for (let animIndex = 0; animIndex < animations.length; ++animIndex) {
const anim = animations[animIndex];
anim.animProgress += progress;
if (anim.animProgress > 1) {
fastArrayDelete(animations, animIndex);
animIndex -= 1;
}
}
inputs.forEach((values, index) => {
if (values.animProgress >= 1) return; // items which are inputted already
values.animProgress += progressGrowth;
if (values.animProgress < 1) return;
/** @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;
for (let animIndex = 0; animIndex < acceptorComp.itemConsumptionAnimations.length; ++animIndex) {
const { item, slotIndex, animProgress, direction } = acceptorComp.itemConsumptionAnimations[
animIndex
];
acceptorComp.currentInputs.forEach((values, index) => {
const { item, animProgress, direction } = values;
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);
if (!chunk.tileSpaceRectangle.containsPoint(realSlotPos.x, realSlotPos.y)) {
// Not within this chunk
continue;
return;
}
const fadeOutDirection = enumDirectionToVector[staticComp.localDirectionToWorld(direction)];
@ -100,7 +108,49 @@ export class ItemAcceptorSystem extends GameSystemWithFilter {
parameters,
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,167 +139,74 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
update() {
this.staleAreaDetector.update();
// Precompute effective belt speed
let progressGrowth = 2 * this.root.dynamicTickrate.deltaSeconds;
if (G_IS_DEV && globalConfig.debug.instantBelts) {
progressGrowth = 1;
}
// * 2 because its only a half tile - (same code as acceptor)
const progressGrowth =
2 *
this.root.dynamicTickrate.deltaSeconds *
this.root.hubGoals.getBeltBaseSpeed() *
globalConfig.itemSpacingOnBelts;
// Go over all cache entries
for (let i = 0; i < this.allEntities.length; ++i) {
const sourceEntity = this.allEntities[i];
const sourceEjectorComp = sourceEntity.components.ItemEjector;
const entity = this.allEntities[i];
const ejectorComp = entity.components.ItemEjector;
const slots = ejectorComp.slots;
const slots = sourceEjectorComp.slots;
for (let j = 0; j < slots.length; ++j) {
const sourceSlot = slots[j];
const item = sourceSlot.item;
const slot = slots[j];
const item = slot.item;
if (!item) {
// No item available to be ejected
// No output in progress
continue;
}
// Advance items on the slot
sourceSlot.progress = Math.min(
1,
sourceSlot.progress +
progressGrowth *
this.root.hubGoals.getBeltBaseSpeed() *
globalConfig.itemSpacingOnBelts
);
// @SENSETODO do we really want to cap it at one, or should the excess get passed on?
slot.progress = Math.min(1, slot.progress + progressGrowth);
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
if (sourceSlot.progress < 1.0) {
if (slot.progress < 1) {
continue;
}
// Check if we are ejecting to a belt path
const destPath = sourceSlot.cachedBeltPath;
const destPath = slot.cachedBeltPath;
if (destPath) {
// Try passing the item over
if (destPath.tryAcceptItem(item)) {
sourceSlot.item = null;
slot.item = null;
}
// Always stop here, since there can *either* be a belt path *or*
// a slot
// Always stop here, since there can *either* be a belt path *or* an acceptor
continue;
}
// Check if the target acceptor can actually accept this item
const destEntity = sourceSlot.cachedTargetEntity;
const destSlot = sourceSlot.cachedDestSlot;
if (destSlot) {
const destEntity = slot.cachedTargetEntity;
const destSlot = slot.cachedDestSlot;
if (destEntity && destSlot) {
const targetAcceptorComp = destEntity.components.ItemAcceptor;
if (!targetAcceptorComp.canAcceptItem(destSlot.index, item)) {
continue;
}
// Try to hand over the item
if (this.tryPassOverItem(item, destEntity, destSlot.index)) {
// Handover successful, clear slot
if (!this.root.app.settings.getAllSettings().simplifiedBelts) {
targetAcceptorComp.onItemAccepted(
const extraProgress = slot.progress - 1;
if (
targetAcceptorComp.tryAcceptItem(
destSlot.index,
destSlot.acceptedDirection,
item
);
}
sourceSlot.item = null;
continue;
}
}
}
}
}
/**
*
* @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()
extraProgress
)
) {
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;
// Handover successful, clear slot
slot.item = null;
}
}
return false;
//@SENSETODO deal with other buildings - acceptor code on them needs to be different!
}
}
}
/**
@ -390,11 +297,6 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
progress = Math.min(maxProgress, progress);
}
// Skip if the item would barely be visible
if (progress < 0.05) {
continue;
}
const realPosition = staticComp.localTileToWorld(slot.pos);
if (!chunk.tileSpaceRectangle.containsPoint(realPosition.x, realPosition.y)) {
// 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
*/
//@SENSETODO not sure if this is true anymore
const MAX_QUEUED_CHARGES = 2;
/**
@ -22,6 +23,7 @@ const MAX_QUEUED_CHARGES = 2;
*
* @typedef {{
* item: BaseItem,
* extraProgress?: number,
* preferredSlot?: number,
* requiredSlot?: number,
* doNotTrack?: boolean
@ -32,8 +34,7 @@ const MAX_QUEUED_CHARGES = 2;
* Type of a processor implementation
* @typedef {{
* entity: Entity,
* items: Map<number, BaseItem>,
* inputCount: number,
* inputs: Map<number, import("../components/item_processor").ItemProcessorInput>,
* outItems: Array<ProducedItem>
* }} 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 (itemsToEject.length === 0) {
processorComp.ongoingCharges.shift();
@ -195,7 +198,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
// DEFAULT
// By default, we can start processing once all inputs are there
case null: {
return processorComp.inputCount >= processorComp.inputsPerCharge;
return processorComp.inputs.size >= processorComp.inputsPerCharge;
}
// QUAD PAINTER
@ -204,7 +207,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
const pinsComp = entity.components.WiredPins;
// 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) {
return false;
}
@ -233,7 +236,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
// Check if all colors of the enabled slots are there
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,
// it is not possible to paint, but if there is nothing we can ignore it
for (let j = 0; j < 4; ++j) {
@ -260,8 +263,8 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
startNewCharge(entity) {
const processorComp = entity.components.ItemProcessor;
// First, take items
const items = processorComp.inputSlots;
// First, take inputs
const inputs = processorComp.inputs;
/** @type {Array<ProducedItem>} */
const outItems = [];
@ -273,9 +276,8 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
// Call implementation
handler({
entity,
items,
inputs: inputs,
outItems,
inputCount: processorComp.inputCount,
});
// Track produced items
@ -298,8 +300,8 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
remainingTime: timeToProcess,
});
processorComp.inputSlots.clear();
processorComp.inputCount = 0;
processorComp.inputs.clear(); //@SENSETODO temp, will completely replace processorcomp.inputs with acceptor inputs later
entity.components.ItemAcceptor.currentInputs.clear();
}
/**
@ -314,12 +316,12 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
const processorComp = payload.entity.components.ItemProcessor;
for (let i = 0; i < 2; ++i) {
const item = payload.items.get(i);
if (!item) {
const input = payload.inputs.get(i);
if (!input || !input.item) {
continue;
}
payload.outItems.push({
item,
item: input.item,
preferredSlot: processorComp.nextOutputSlot++ % availableSlots,
doNotTrack: true,
});
@ -331,7 +333,8 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
* @param {ProcessorImplementationPayload} 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");
const inputDefinition = inputItem.definition;
@ -357,7 +360,8 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
* @param {ProcessorImplementationPayload} 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");
const inputDefinition = inputItem.definition;
@ -383,7 +387,8 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
* @param {ProcessorImplementationPayload} 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");
const inputDefinition = inputItem.definition;
@ -397,7 +402,8 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
* @param {ProcessorImplementationPayload} 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");
const inputDefinition = inputItem.definition;
@ -411,7 +417,8 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
* @param {ProcessorImplementationPayload} 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");
const inputDefinition = inputItem.definition;
@ -425,8 +432,10 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
* @param {ProcessorImplementationPayload} payload
*/
process_STACKER(payload) {
const lowerItem = /** @type {ShapeItem} */ (payload.items.get(0));
const upperItem = /** @type {ShapeItem} */ (payload.items.get(1));
const lowerInput = payload.inputs.get(0);
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(upperItem instanceof ShapeItem, "Input for upper stack is not a shape");
@ -451,9 +460,11 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
* @param {ProcessorImplementationPayload} payload
*/
process_MIXER(payload) {
const input1 = payload.inputs.get(0);
const input2 = payload.inputs.get(1);
// Find both colors and combine them
const item1 = /** @type {ColorItem} */ (payload.items.get(0));
const item2 = /** @type {ColorItem} */ (payload.items.get(1));
const item1 = /** @type {ColorItem} */ (input1.item);
const item2 = /** @type {ColorItem} */ (input2.item);
assert(item1 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
*/
process_PAINTER(payload) {
const shapeItem = /** @type {ShapeItem} */ (payload.items.get(0));
const colorItem = /** @type {ColorItem} */ (payload.items.get(1));
const input1 = payload.inputs.get(0);
const input2 = payload.inputs.get(1);
const shapeItem = /** @type {ShapeItem} */ (input1.item);
const colorItem = /** @type {ColorItem} */ (input2.item);
const colorizedDefinition = this.root.shapeDefinitionMgr.shapeActionPaintWith(
shapeItem.definition,
@ -492,9 +505,9 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
* @param {ProcessorImplementationPayload} payload
*/
process_PAINTER_DOUBLE(payload) {
const shapeItem1 = /** @type {ShapeItem} */ (payload.items.get(0));
const shapeItem2 = /** @type {ShapeItem} */ (payload.items.get(1));
const colorItem = /** @type {ColorItem} */ (payload.items.get(2));
const shapeItem1 = /** @type {ShapeItem} */ (payload.inputs.get(0).item);
const shapeItem2 = /** @type {ShapeItem} */ (payload.inputs.get(1).item);
const colorItem = /** @type {ColorItem} */ (payload.inputs.get(2).item);
assert(shapeItem1 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
*/
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");
/** @type {Array<enumColors>} */
const colors = [null, null, null, null];
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) {
colors[i] = colorItem.color;
}
@ -549,7 +563,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
*/
process_READER(payload) {
// Pass through the item
const item = payload.items.get(0);
const item = payload.inputs.get(0).item;
payload.outItems.push({
item,
doNotTrack: true,
@ -565,17 +579,17 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
* @param {ProcessorImplementationPayload} payload
*/
process_HUB(payload) {
const hubComponent = payload.entity.components.Hub;
assert(hubComponent, "Hub item processor has no hub component");
// Hardcoded
for (let i = 0; i < payload.inputCount; ++i) {
const item = /** @type {ShapeItem} */ (payload.items.get(i));
if (!item) {
continue;
}
this.root.hubGoals.handleDefinitionDelivered(item.definition);
}
//const input = payload.inputs.get(i);
//const hubComponent = payload.entity.components.Hub;
//assert(hubComponent, "Hub item processor has no hub component");
//// Hardcoded
//for (let i = 0; i < payload.inputCount; ++i) {
// const item = /** @type {ShapeItem} */ (payload.inputs.get(i));
// if (!item) {
// continue;
// }
// this.root.hubGoals.handleDefinitionDelivered(item.definition);
//}
}
/**
@ -583,7 +597,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
*/
process_GOAL(payload) {
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();
if (goalComp.item && !item.equals(goalComp.item)) {