1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2025-06-13 13:04:03 +00:00

Clear duplicated code and add input requirements for acceptor

This commit is contained in:
Sense101 2022-02-08 14:39:43 +00:00
parent 3a99628670
commit 0cacbcd267
6 changed files with 115 additions and 58 deletions

View File

@ -263,16 +263,7 @@ export class BeltPath extends BasicSerializableObject {
const matchingSlotIndex = matchingSlot.index; const matchingSlotIndex = matchingSlot.index;
return function (item, startProgress = 0.0) { return function (item, startProgress = 0.0) {
const storageComp = targetEntity.components.Storage; if (targetAcceptorComp.tryAcceptItem(targetEntity, matchingSlotIndex, item, startProgress)) {
if (
storageComp &&
storageComp.tryAcceptItem(item) &&
targetAcceptorComp.tryAcceptItem(matchingSlotIndex, item, startProgress)
) {
// unique duplicated code for storage
return true;
}
if (targetAcceptorComp.tryAcceptItem(matchingSlotIndex, item, startProgress)) {
return true; return true;
} }
return false; return false;

View File

@ -1,7 +1,7 @@
import { formatItemsPerSecond } from "../../core/utils"; import { formatItemsPerSecond } 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 { enumInputRequirements, ItemAcceptorComponent } from "../components/item_acceptor";
import { ItemEjectorComponent } from "../components/item_ejector"; import { ItemEjectorComponent } from "../components/item_ejector";
import { import {
enumItemProcessorTypes, enumItemProcessorTypes,
@ -274,6 +274,7 @@ export class MetaPainterBuilding extends MetaBuilding {
filter: "color", filter: "color",
}, },
]); ]);
entity.components.ItemAcceptor.inputRequirement = enumInputRequirements.quadPainter;
entity.components.ItemEjector.setSlots([ entity.components.ItemEjector.setSlots([
{ pos: new Vector(0, 0), direction: enumDirection.top }, { pos: new Vector(0, 0), direction: enumDirection.top },

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 { enumInputRequirements, 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";
@ -81,6 +81,7 @@ export class MetaStorageBuilding extends MetaBuilding {
direction: enumDirection.bottom, direction: enumDirection.bottom,
}, },
], ],
inputRequirement: enumInputRequirements.storage,
}) })
); );

View File

@ -3,6 +3,7 @@ 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"; import { Entity } from "../entity";
import { isTruthyItem } from "../items/boolean_item";
import { typeItemSingleton } from "../item_resolver"; import { typeItemSingleton } from "../item_resolver";
import { GameRoot } from "../root"; import { GameRoot } from "../root";
@ -49,6 +50,12 @@ import { GameRoot } from "../root";
* }} InputCompletedArgs * }} InputCompletedArgs
*/ */
/** @enum {string} */
export const enumInputRequirements = {
quadPainter: "quadPainter",
storage: "storage",
};
export class ItemAcceptorComponent extends Component { export class ItemAcceptorComponent extends Component {
static getId() { static getId() {
return "ItemAcceptor"; return "ItemAcceptor";
@ -78,18 +85,26 @@ 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 {number=} param0.maxSlotInputs The maximum amount of items one slot can accept before it is full * @param {number=} param0.maxSlotInputs The maximum amount of items one slot can accept before it is full
* @param {string|null=} param0.inputRequirement The requirement to accept items
*/ */
constructor({ slots = [], maxSlotInputs = 2 }) { constructor({ slots = [], maxSlotInputs = 2, inputRequirement = null }) {
super(); super();
this.setSlots(slots);
this.inputRequirement = inputRequirement;
// setting this to 1 will cause throughput issues at very high speeds
this.maxSlotInputs = maxSlotInputs;
this.clear();
}
clear() {
/** @type {ItemAcceptorInputs} */ /** @type {ItemAcceptorInputs} */
this.inputs = []; this.inputs = [];
/** @type {ItemAcceptorCompletedInputs} */ /** @type {ItemAcceptorCompletedInputs} */
this.completedInputs = []; this.completedInputs = [];
this.setSlots(slots);
// setting this to 1 will cause throughput issues at very high speeds
this.maxSlotInputs = maxSlotInputs;
} }
/** /**
@ -111,16 +126,74 @@ export class ItemAcceptorComponent extends Component {
} }
} }
/**
*
* @param {Entity} entity
* @param {BaseItem} item
* @param {number} slotIndex
* @returns
*/
canAcceptItem(entity, item, slotIndex) {
const slot = this.slots[slotIndex];
// make sure there is a slot and we match the filter
if (slot && !(slot.filter && slot.filter != item.getItemType())) {
switch (this.inputRequirement) {
case null: {
return true;
}
case enumInputRequirements.quadPainter: {
const pinsComp = entity.components.WiredPins;
if (slotIndex === 0) {
// Always accept the shape
return true;
}
// Check the network value at the given slot
const network = pinsComp.slots[slotIndex - 1].linkedNetwork;
const slotIsEnabled = network && network.hasValue() && isTruthyItem(network.currentValue);
if (!slotIsEnabled) {
return false;
}
return true;
}
case enumInputRequirements.storage: {
const storageComp = entity.components.Storage;
if (storageComp.storedCount >= storageComp.maximumStorage) {
return false;
}
const itemType = item.getItemType();
if (storageComp.storedItem && itemType !== storageComp.storedItem.getItemType()) {
return false;
}
// set the item straight away - this way different kinds of items can't be inq the acceptor
storageComp.storedItem = item;
storageComp.storedCount++;
return true;
}
default: {
assertAlways(false, "Input requirement is not recognised: " + slot.filter);
break;
}
}
}
return false;
}
/** /**
* Called when trying to input a new item * Called when trying to input a new item
* @param {Entity} entity
* @param {number} slotIndex * @param {number} slotIndex
* @param {BaseItem} item * @param {BaseItem} item
* @param {number} startProgress 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 * @returns {boolean} if the input was succesful
*/ */
tryAcceptItem(slotIndex, item, startProgress = 0.0) { tryAcceptItem(entity, slotIndex, item, startProgress = 0.0) {
const slot = this.slots[slotIndex]; // make sure we have space to actually accept
let existingInputs = 0; let existingInputs = 0;
for (let i = 0; i < this.inputs.length; i++) { for (let i = 0; i < this.inputs.length; i++) {
if (this.inputs[i].slotIndex == slotIndex) { if (this.inputs[i].slotIndex == slotIndex) {
@ -136,8 +209,7 @@ export class ItemAcceptorComponent extends Component {
if (existingInputs >= this.maxSlotInputs) { if (existingInputs >= this.maxSlotInputs) {
return false; return false;
} }
if (!this.canAcceptItem(entity, item, slotIndex)) {
if (slot.filter && slot.filter != item.getItemType()) {
return false; return false;
} }

View File

@ -202,17 +202,7 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
const destSlot = sourceSlot.cachedDestSlot; const destSlot = sourceSlot.cachedDestSlot;
if (destEntity && destSlot) { if (destEntity && destSlot) {
const targetAcceptorComp = destEntity.components.ItemAcceptor; const targetAcceptorComp = destEntity.components.ItemAcceptor;
const storageComp = destEntity.components.Storage; if (targetAcceptorComp.tryAcceptItem(destEntity, destSlot.index, item, extraProgress)) {
if (
storageComp &&
storageComp.tryAcceptItem(item) &&
targetAcceptorComp.tryAcceptItem(destSlot.index, item, extraProgress)
) {
// unique duplicated code for storage - hacky :(
sourceSlot.item = null;
return;
}
if (targetAcceptorComp.tryAcceptItem(destSlot.index, item, extraProgress)) {
// Handover successful, clear slot // Handover successful, clear slot
sourceSlot.item = null; sourceSlot.item = null;
} }

View File

@ -146,7 +146,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
} }
} }
// requirements are no longer needed as items will always be accepted, only the next method is. // input requirements are now handled in the item acceptor, which also fits better with what the acceptor is supposed to do
/** /**
* Checks whether it's possible to process something * Checks whether it's possible to process something
@ -160,21 +160,35 @@ 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 acceptorComp.completedInputs.length >= processorComp.inputsPerCharge; // Since each slot might have more than one input, don't check each slot more than once
let usedSlots = [];
for (let i = 0; i < acceptorComp.completedInputs.length; i++) {
const index = acceptorComp.completedInputs[i].slotIndex;
if (!usedSlots.includes(index)) {
usedSlots.push(index);
}
}
return usedSlots.length >= processorComp.inputsPerCharge;
} }
// QUAD PAINTER // QUAD PAINTER
// For the quad painter, it might be possible to start processing earlier // For the quad painter, it might be possible to start processing earlier
case enumItemProcessorRequirements.painterQuad: { case enumItemProcessorRequirements.painterQuad: {
const pinsComp = entity.components.WiredPins; const pinsComp = entity.components.WiredPins;
const inputs = acceptorComp.completedInputs;
const input = acceptorComp.completedInputs[0]; // split inputs efficiently
if (!input) { let items = new Map();
return false; for (let i = 0; i < inputs.length; i++) {
const input = inputs[i];
if (!items.get(input.slotIndex)) {
items.set(input.slotIndex, input.item);
}
} }
// 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} */ (input.item); const shapeItem = /** @type {ShapeItem} */ (items.get(0));
if (!shapeItem) { if (!shapeItem) {
return false; return false;
} }
@ -203,10 +217,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 ( if (slotStatus[i] && !items.get(1 + i)) {
slotStatus[i] &&
!acceptorComp.completedInputs.find(input => input.slotIndex == i + 1) // @TODO this is slow
) {
// 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) {
@ -217,7 +228,6 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
} }
} }
} }
return true; return true;
} }
@ -243,8 +253,12 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
for (let i = 0; i < inputs.length; i++) { for (let i = 0; i < inputs.length; i++) {
const input = inputs[i]; const input = inputs[i];
if (!items.get(input.slotIndex)) {
items.set(input.slotIndex, input.item); items.set(input.slotIndex, input.item);
extraProgress = Math.max(extraProgress, input.extraProgress); extraProgress = Math.max(extraProgress, input.extraProgress);
inputs.splice(i, 1);
i--;
}
} }
/** @type {Array<ProducedItem>} */ /** @type {Array<ProducedItem>} */
@ -283,18 +297,6 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
items: outItems, items: outItems,
remainingTime: timeToProcess, remainingTime: timeToProcess,
}; };
// only remove one item from each slot - we don't want to delete extra items!
let usedSlots = [];
for (let i = 0; i < acceptorComp.completedInputs.length; i++) {
const index = acceptorComp.completedInputs[i].slotIndex;
if (!usedSlots.includes(index)) {
usedSlots.push(index);
acceptorComp.completedInputs.splice(i, 1);
i--;
}
}
} }
/** /**