1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2025-12-13 10:11:50 +00:00

Item acceptor refactor part two - completed functionality

This commit is contained in:
Sense101 2022-01-19 00:16:59 +00:00
parent 4a1bfcda64
commit 4a14727fd3
10 changed files with 74 additions and 177 deletions

View File

@ -128,8 +128,9 @@ export class BeltPath extends BasicSerializableObject {
/**
* Tries to accept the item
* @param {BaseItem} item
* @param {number} startProgress
*/
tryAcceptItem(item) {
tryAcceptItem(item, startProgress = 0) {
if (this.spacingToFirstItem >= globalConfig.itemSpacingOnBelts) {
// So, since we already need one tick to accept this item we will add this directly.
const progressGrowth =
@ -139,7 +140,7 @@ export class BeltPath extends BasicSerializableObject {
// First, compute how much progress we can make *at max*
const maxProgress = Math.max(0, this.spacingToFirstItem - globalConfig.itemSpacingOnBelts);
const initialProgress = Math.min(maxProgress, progressGrowth);
const initialProgress = Math.min(maxProgress, startProgress);
this.items.unshift([this.spacingToFirstItem - initialProgress, item]);
this.spacingToFirstItem = initialProgress;

View File

@ -47,25 +47,6 @@ export class MetaTrashBuilding extends MetaBuilding {
return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_cutter_and_trash);
}
addAchievementReceiver(entity) {
if (!entity.root) {
return;
}
const itemProcessor = entity.components.ItemProcessor;
const tryTakeItem = itemProcessor.tryTakeItem.bind(itemProcessor);
itemProcessor.tryTakeItem = () => {
const taken = tryTakeItem(...arguments);
if (taken) {
entity.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.trash1000, 1);
}
return taken;
};
}
/**
* Creates the entity at the given location
* @param {Entity} entity
@ -88,7 +69,5 @@ export class MetaTrashBuilding extends MetaBuilding {
processorType: enumItemProcessorTypes.trash,
})
);
this.addAchievementReceiver(entity);
}
}

View File

@ -28,7 +28,12 @@ import { GameRoot } from "../root";
* item: BaseItem,
* animProgress: number,
* direction: enumDirection
* }>} ItemAcceptorInput
* }>} ItemAcceptorInputs
*
* @typedef {Map<number, {
* item: BaseItem,
* extraProgress: number
* }>} ItemAcceptorCompletedInputs
*
* @typedef {{
* root: GameRoot,
@ -62,8 +67,10 @@ export class ItemAcceptorComponent extends Component {
constructor({ slots = [], type = enumItemAcceptorTypes.itemProcessor }) {
super();
/** @type {ItemAcceptorInput} */
this.currentInputs = new Map(); // @SENSETODO does this need to be saved?
/** @type {ItemAcceptorInputs} */
this.inputs = new Map();
/** @type {ItemAcceptorCompletedInputs} */
this.completedInputs = new Map(); // @SENSETODO does this need to be saved?
this.type = type;
this.setSlots(slots);
}
@ -100,11 +107,11 @@ export class ItemAcceptorComponent extends Component {
//@SENSETODO see if this works for buildings like hub
if (this.currentInputs.has(slotIndex) || (slot.filter && slot.filter != item.getItemType())) {
if (this.completedInputs.has(slotIndex) || (slot.filter && slot.filter != item.getItemType())) {
return false;
}
this.currentInputs.set(slotIndex, {
this.inputs.set(slotIndex, {
item,
direction,
animProgress: Math.min(1, startProgress),

View File

@ -134,6 +134,7 @@ export class ItemEjectorComponent extends Component {
if (!this.canEjectOnSlot(slotIndex)) {
return false;
}
if (startingProgress > 0) console.log("starting with progress: " + startingProgress);
this.slots[slotIndex].item = item;
this.slots[slotIndex].lastItem = item;
this.slots[slotIndex].progress = startingProgress;

View File

@ -30,6 +30,7 @@ export const enumItemProcessorRequirements = {
/**
* @typedef {{
* item: BaseItem,
* extraProgress?: number,
* requiredSlot?: number,
* preferredSlot?: number
* }} EjectorItemToEject
@ -84,7 +85,6 @@ export class ItemProcessorComponent extends Component {
* Our current inputs
* @type {Map<number, ItemProcessorInput>}
*/
this.inputs = new Map();
this.clear();
}
@ -95,8 +95,6 @@ export class ItemProcessorComponent extends Component {
// sure the outputs always match
this.nextOutputSlot = 0;
this.inputs.clear();
/**
* What we are currently processing, empty if we don't produce anything rn
* requiredSlot: Item *must* be ejected on this slot

View File

@ -145,7 +145,7 @@ export class GameSystemManager {
// Order is important!
// IMPORTANT: Item acceptor must be before the belt, because it may not tick after the belt
// has put in the item into the acceptor animation, otherwise its off
// has put in the item into the acceptor animqation, otherwise its off
add("itemAcceptor", ItemAcceptorSystem);
add("belt", BeltSystem);
@ -156,14 +156,14 @@ export class GameSystemManager {
add("storage", StorageSystem);
add("itemEjector", ItemEjectorSystem);
add("itemProcessor", ItemProcessorSystem);
add("filter", FilterSystem);
add("itemProducer", ItemProducerSystem);
add("itemEjector", ItemEjectorSystem);
if (this.root.gameMode.hasResources()) {
add("mapResources", MapResourcesSystem);
}

View File

@ -113,12 +113,10 @@ export class BeltUnderlaysSystem extends GameSystemWithFilter {
continue;
}
// Step 2: Check if any of the directions matches
for (let j = 0; j < slot.directions.length; ++j) {
const slotDirection = staticComp.localDirectionToWorld(slot.directions[j]);
if (slotDirection === fromDirection) {
return true;
}
// Step 2: Check if the direction matches
const slotDirection = staticComp.localDirectionToWorld(slot.direction);
if (slotDirection === fromDirection) {
return true;
}
}
}

View File

@ -1,6 +1,7 @@
import { globalConfig } from "../../core/config";
import { DrawParameters } from "../../core/draw_parameters";
import { enumDirectionToVector } from "../../core/vector";
import { ACHIEVEMENTS } from "../../platform/achievement_provider";
import {
enumItemAcceptorTypes,
ItemAcceptorComponent,
@ -40,15 +41,19 @@ export class ItemAcceptorSystem extends GameSystemWithFilter {
for (let i = 0; i < this.allEntities.length; ++i) {
const entity = this.allEntities[i];
const acceptorComp = entity.components.ItemAcceptor;
const inputs = acceptorComp.currentInputs;
const inputs = acceptorComp.inputs;
inputs.forEach((values, index) => {
if (values.animProgress >= 1) return; // items which are inputted already
values.animProgress += progressGrowth;
if (values.animProgress < 1) return;
inputs.delete(index);
acceptorComp.completedInputs.set(index, {
item: values.item,
extraProgress: values.animProgress - 1,
}); // will be handled on the SAME frame due to processor being afterwards
/** @type {function(InputCompletedArgs) : string} */
const handler = this.handlers[acceptorComp.type];
assert(handler, "No handler for acceptor type defined: " + acceptorComp.type);
@ -84,9 +89,8 @@ export class ItemAcceptorSystem extends GameSystemWithFilter {
}
const staticComp = entity.components.StaticMapEntity;
acceptorComp.currentInputs.forEach((values, index) => {
acceptorComp.inputs.forEach((values, index) => {
const { item, animProgress, direction } = values;
if (animProgress >= 1) return; // items which are inputted already
const slotData = acceptorComp.slots[index];
const realSlotPos = staticComp.localTileToWorld(slotData.pos);
@ -115,17 +119,7 @@ export class ItemAcceptorSystem extends GameSystemWithFilter {
/**
* @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
}
input_ITEMPROCESSOR(args) {}
//@SENSETODO this isn't set up like it should be yet
/**
@ -138,7 +132,7 @@ export class ItemAcceptorSystem extends GameSystemWithFilter {
this.root.hubGoals.handleDefinitionDelivered(item.definition);
const acceptorComp = args.entity.components.ItemAcceptor;
acceptorComp.currentInputs.delete(args.slotIndex);
acceptorComp.inputs.delete(args.slotIndex);
}
/**
@ -147,7 +141,8 @@ export class ItemAcceptorSystem extends GameSystemWithFilter {
input_TRASH(args) {
// just remove the item
const acceptorComp = args.entity.components.ItemAcceptor;
acceptorComp.currentInputs.delete(args.slotIndex);
acceptorComp.inputs.delete(args.slotIndex);
args.entity.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.trash1000, 1);
}
//storage

View File

@ -160,24 +160,25 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
continue;
}
// Advance items on the slot
// @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 (slot.progress < 1) {
// Advance items on the slot
slot.progress += progressGrowth;
if (G_IS_DEV && globalConfig.debug.disableEjectorProcessing) {
slot.progress = 1;
if (G_IS_DEV && globalConfig.debug.disableEjectorProcessing) {
slot.progress = 1;
}
}
// Check if we are still in the process of ejecting, can't proceed then
if (slot.progress < 1) {
continue;
}
if (slot.progress < 1) continue;
const extraProgress = slot.progress - 1;
// Check if we are ejecting to a belt path
const destPath = slot.cachedBeltPath;
if (destPath) {
// Try passing the item over
if (destPath.tryAcceptItem(item)) {
// Try passing the item over - extraProgress / 2 because the progress there is for double the distance
if (destPath.tryAcceptItem(item, extraProgress / 2)) {
slot.item = null;
}
@ -190,7 +191,6 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
const destSlot = slot.cachedDestSlot;
if (destEntity && destSlot) {
const targetAcceptorComp = destEntity.components.ItemAcceptor;
const extraProgress = slot.progress - 1;
if (
targetAcceptorComp.tryAcceptItem(
destSlot.index,
@ -245,7 +245,7 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
}
// Limit the progress to the maximum available space on the next belt (also see #1000)
let progress = slot.progress;
let progress = Math.min(1, slot.progress);
const nextBeltPath = slot.cachedBeltPath;
if (nextBeltPath) {
/*
@ -321,90 +321,4 @@ 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

@ -34,7 +34,7 @@ const MAX_QUEUED_CHARGES = 2;
* Type of a processor implementation
* @typedef {{
* entity: Entity,
* inputs: Map<number, import("../components/item_processor").ItemProcessorInput>,
* inputs: import("../components/item_acceptor").ItemAcceptorCompletedInputs,
* outItems: Array<ProducedItem>
* }} ProcessorImplementationPayload
*/
@ -83,8 +83,14 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
const processorComp = entity.components.ItemProcessor;
const ejectorComp = entity.components.ItemEjector;
const currentCharge = processorComp.ongoingCharges[0];
// Check if we have an empty queue and can start a new charge - do this first so we don't waste a tick
if (processorComp.ongoingCharges.length < MAX_QUEUED_CHARGES) {
if (this.canProcess(entity)) {
this.startNewCharge(entity);
}
}
const currentCharge = processorComp.ongoingCharges[0];
if (currentCharge) {
// Process next charge
if (currentCharge.remainingTime > 0.0) {
@ -101,7 +107,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
// Go over all items and try to eject them
for (let j = 0; j < itemsToEject.length; ++j) {
const { item, requiredSlot, preferredSlot } = itemsToEject[j];
const { item, requiredSlot, preferredSlot, extraProgress = 0 } = itemsToEject[j];
assert(ejectorComp, "To eject items, the building needs to have an ejector");
@ -125,7 +131,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
if (slot !== null) {
// Alright, we can actually eject
if (!ejectorComp.tryEject(slot, item)) {
if (!ejectorComp.tryEject(slot, item, extraProgress)) {
assert(false, "Failed to eject");
} else {
itemsToEject.splice(j, 1);
@ -142,13 +148,6 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
}
}
}
// Check if we have an empty queue and can start a new charge
if (processorComp.ongoingCharges.length < MAX_QUEUED_CHARGES) {
if (this.canProcess(entity)) {
this.startNewCharge(entity);
}
}
}
}
@ -192,13 +191,14 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
* @param {Entity} entity
*/
canProcess(entity) {
const acceptorComp = entity.components.ItemAcceptor;
const processorComp = entity.components.ItemProcessor;
switch (processorComp.processingRequirement) {
// DEFAULT
// By default, we can start processing once all inputs are there
case null: {
return processorComp.inputs.size >= processorComp.inputsPerCharge;
return acceptorComp.completedInputs.size >= processorComp.inputsPerCharge;
}
// QUAD PAINTER
@ -207,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.inputs.get(0).item);
const shapeItem = /** @type {ShapeItem} */ (acceptorComp.inputs.get(0).item);
if (!shapeItem) {
return false;
}
@ -236,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.inputs.get(1 + i)) {
if (slotStatus[i] && !acceptorComp.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) {
@ -261,12 +261,13 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
* @param {Entity} entity
*/
startNewCharge(entity) {
const acceptorComp = entity.components.ItemAcceptor;
const processorComp = entity.components.ItemProcessor;
// First, take inputs
const inputs = processorComp.inputs;
// First, take inputs - but only ones that are completed
const inputs = acceptorComp.completedInputs;
console.log("processor added charge");
/** @type {Array<ProducedItem>} */
const outItems = [];
/** @type {function(ProcessorImplementationPayload) : void} */
@ -276,7 +277,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
// Call implementation
handler({
entity,
inputs: inputs,
inputs,
outItems,
});
@ -289,7 +290,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
// Queue Charge
const baseSpeed = this.root.hubGoals.getProcessorBaseSpeed(processorComp.type);
const originalTime = 1 / baseSpeed;
const originalTime = 0;
const bonusTimeToApply = Math.min(originalTime, processorComp.bonusTime);
const timeToProcess = originalTime - bonusTimeToApply;
@ -300,8 +301,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
remainingTime: timeToProcess,
});
processorComp.inputs.clear(); //@SENSETODO temp, will completely replace processorcomp.inputs with acceptor inputs later
entity.components.ItemAcceptor.currentInputs.clear();
acceptorComp.completedInputs.clear();
}
/**
@ -322,6 +322,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
}
payload.outItems.push({
item: input.item,
extraProgress: input.extraProgress,
preferredSlot: processorComp.nextOutputSlot++ % availableSlots,
doNotTrack: true,
});
@ -351,6 +352,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
payload.outItems.push({
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(definition),
extraProgress: input.extraProgress,
requiredSlot: i,
});
}
@ -378,6 +380,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
payload.outItems.push({
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(definition),
extraProgress: input.extraProgress,
requiredSlot: i,
});
}
@ -395,6 +398,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateCW(inputDefinition);
payload.outItems.push({
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition),
extraProgress: input.extraProgress,
});
}