Refactor filters to make them not stall if one output is blocked
This commit is contained in:
parent
518d9b9f6f
commit
26cd38b68c
|
@ -30,7 +30,7 @@
|
|||
transform: scale(0.9) !important;
|
||||
}
|
||||
|
||||
opacity: 0.5;
|
||||
opacity: 0.7;
|
||||
&:hover {
|
||||
opacity: 0.9 !important;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
import { enumDirection, Vector } from "../../core/vector";
|
||||
import { FilterComponent } from "../components/filter";
|
||||
import { ItemAcceptorComponent } from "../components/item_acceptor";
|
||||
import { ItemEjectorComponent } from "../components/item_ejector";
|
||||
import {
|
||||
enumItemProcessorRequirements,
|
||||
enumItemProcessorTypes,
|
||||
ItemProcessorComponent,
|
||||
} from "../components/item_processor";
|
||||
import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins";
|
||||
import { Entity } from "../entity";
|
||||
import { MetaBuilding } from "../meta_building";
|
||||
|
@ -79,12 +75,6 @@ export class MetaFilterBuilding extends MetaBuilding {
|
|||
})
|
||||
);
|
||||
|
||||
entity.addComponent(
|
||||
new ItemProcessorComponent({
|
||||
processorType: enumItemProcessorTypes.filter,
|
||||
inputsPerCharge: 1,
|
||||
processingRequirement: enumItemProcessorRequirements.filter,
|
||||
})
|
||||
);
|
||||
entity.addComponent(new FilterComponent());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import { LeverComponent } from "./components/lever";
|
|||
import { WireTunnelComponent } from "./components/wire_tunnel";
|
||||
import { DisplayComponent } from "./components/display";
|
||||
import { BeltReaderComponent } from "./components/belt_reader";
|
||||
import { FilterComponent } from "./components/filter";
|
||||
|
||||
export function initComponentRegistry() {
|
||||
gComponentRegistry.register(StaticMapEntityComponent);
|
||||
|
@ -37,6 +38,7 @@ export function initComponentRegistry() {
|
|||
gComponentRegistry.register(WireTunnelComponent);
|
||||
gComponentRegistry.register(DisplayComponent);
|
||||
gComponentRegistry.register(BeltReaderComponent);
|
||||
gComponentRegistry.register(FilterComponent);
|
||||
|
||||
// IMPORTANT ^^^^^ UPDATE ENTITY COMPONENT STORAGE AFTERWARDS
|
||||
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
import { types } from "../../savegame/serialization";
|
||||
import { BaseItem } from "../base_item";
|
||||
import { Component } from "../component";
|
||||
import { typeItemSingleton } from "../item_resolver";
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* item: BaseItem,
|
||||
* progress: number
|
||||
* }} PendingFilterItem
|
||||
*/
|
||||
|
||||
export class FilterComponent extends Component {
|
||||
static getId() {
|
||||
return "Filter";
|
||||
}
|
||||
|
||||
duplicateWithoutContents() {
|
||||
return new FilterComponent();
|
||||
}
|
||||
|
||||
static getSchema() {
|
||||
return {
|
||||
pendingItemsToLeaveThrough: types.array(
|
||||
types.structured({
|
||||
item: typeItemSingleton,
|
||||
progress: types.ufloat,
|
||||
})
|
||||
),
|
||||
|
||||
pendingItemsToReject: types.array(
|
||||
types.structured({
|
||||
item: typeItemSingleton,
|
||||
progress: types.ufloat,
|
||||
})
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
/**
|
||||
* Items in queue to leave through
|
||||
* @type {Array<PendingFilterItem>}
|
||||
*/
|
||||
this.pendingItemsToLeaveThrough = [];
|
||||
|
||||
/**
|
||||
* Items in queue to reject
|
||||
* @type {Array<PendingFilterItem>}
|
||||
*/
|
||||
this.pendingItemsToReject = [];
|
||||
}
|
||||
}
|
|
@ -24,7 +24,6 @@ export const enumItemProcessorTypes = {
|
|||
/** @enum {string} */
|
||||
export const enumItemProcessorRequirements = {
|
||||
painterQuad: "painterQuad",
|
||||
filter: "filter",
|
||||
};
|
||||
|
||||
/** @typedef {{
|
||||
|
|
|
@ -17,6 +17,7 @@ import { LeverComponent } from "./components/lever";
|
|||
import { WireTunnelComponent } from "./components/wire_tunnel";
|
||||
import { DisplayComponent } from "./components/display";
|
||||
import { BeltReaderComponent } from "./components/belt_reader";
|
||||
import { FilterComponent } from "./components/filter";
|
||||
/* typehints:end */
|
||||
|
||||
/**
|
||||
|
@ -81,6 +82,9 @@ export class EntityComponentStorage {
|
|||
/** @type {BeltReaderComponent} */
|
||||
this.BeltReader;
|
||||
|
||||
/** @type {FilterComponent} */
|
||||
this.Filter;
|
||||
|
||||
/* typehints:end */
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import { LeverSystem } from "./systems/lever";
|
|||
import { DisplaySystem } from "./systems/display";
|
||||
import { ItemProcessorOverlaysSystem } from "./systems/item_processor_overlays";
|
||||
import { BeltReaderSystem } from "./systems/belt_reader";
|
||||
import { FilterSystem } from "./systems/filter";
|
||||
|
||||
const logger = createLogger("game_system_manager");
|
||||
|
||||
|
@ -92,6 +93,9 @@ export class GameSystemManager {
|
|||
/** @type {BeltReaderSystem} */
|
||||
beltReader: null,
|
||||
|
||||
/** @type {FilterSystem} */
|
||||
filter: null,
|
||||
|
||||
/* typehints:end */
|
||||
};
|
||||
this.systemUpdateOrder = [];
|
||||
|
@ -124,6 +128,8 @@ export class GameSystemManager {
|
|||
|
||||
add("itemProcessor", ItemProcessorSystem);
|
||||
|
||||
add("filter", FilterSystem);
|
||||
|
||||
add("itemEjector", ItemEjectorSystem);
|
||||
|
||||
add("mapResources", MapResourcesSystem);
|
||||
|
|
|
@ -401,7 +401,6 @@ export class HubGoals extends BasicSerializableObject {
|
|||
return 1e30;
|
||||
case enumItemProcessorTypes.splitter:
|
||||
return globalConfig.beltSpeedItemsPerSecond * this.upgradeImprovements.belt * 2;
|
||||
case enumItemProcessorTypes.filter:
|
||||
case enumItemProcessorTypes.reader:
|
||||
return globalConfig.beltSpeedItemsPerSecond * this.upgradeImprovements.belt;
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@ export class DisplaySystem extends GameSystemWithFilter {
|
|||
const pinsComp = entity.components.WiredPins;
|
||||
const network = pinsComp.slots[0].linkedNetwork;
|
||||
|
||||
if (!network || !network.currentValue) {
|
||||
if (!network || !network.hasValue()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
import { globalConfig } from "../../core/config";
|
||||
import { BaseItem } from "../base_item";
|
||||
import { FilterComponent } from "../components/filter";
|
||||
import { Entity } from "../entity";
|
||||
import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||
import { BOOL_TRUE_SINGLETON } from "../items/boolean_item";
|
||||
|
||||
const MAX_ITEMS_IN_QUEUE = 2;
|
||||
|
||||
export class FilterSystem extends GameSystemWithFilter {
|
||||
constructor(root) {
|
||||
super(root, [FilterComponent]);
|
||||
}
|
||||
|
||||
update() {
|
||||
const progress =
|
||||
this.root.dynamicTickrate.deltaSeconds *
|
||||
this.root.hubGoals.getBeltBaseSpeed() *
|
||||
globalConfig.itemSpacingOnBelts;
|
||||
|
||||
const requiredProgress = 1 - progress;
|
||||
|
||||
for (let i = 0; i < this.allEntities.length; ++i) {
|
||||
const entity = this.allEntities[i];
|
||||
const filterComp = entity.components.Filter;
|
||||
const ejectorComp = entity.components.ItemEjector;
|
||||
|
||||
// Process payloads
|
||||
const slotsAndLists = [filterComp.pendingItemsToLeaveThrough, filterComp.pendingItemsToReject];
|
||||
for (let slotIndex = 0; slotIndex < slotsAndLists.length; ++slotIndex) {
|
||||
const pendingItems = slotsAndLists[slotIndex];
|
||||
|
||||
for (let j = 0; j < pendingItems.length; ++j) {
|
||||
const nextItem = pendingItems[j];
|
||||
// Advance next item
|
||||
nextItem.progress = Math.min(requiredProgress, nextItem.progress + progress);
|
||||
// Check if it's ready to eject
|
||||
if (nextItem.progress >= requiredProgress - 1e-5) {
|
||||
if (ejectorComp.tryEject(slotIndex, nextItem.item)) {
|
||||
pendingItems.shift();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Entity} entity
|
||||
* @param {number} slot
|
||||
* @param {BaseItem} item
|
||||
*/
|
||||
tryAcceptItem(entity, slot, item) {
|
||||
const network = entity.components.WiredPins.slots[0].linkedNetwork;
|
||||
if (!network || !network.hasValue()) {
|
||||
// Filter is not connected
|
||||
return false;
|
||||
}
|
||||
|
||||
const value = network.currentValue;
|
||||
const filterComp = entity.components.Filter;
|
||||
assert(filterComp, "entity is no filter");
|
||||
|
||||
// Figure out which list we have to check
|
||||
let listToCheck;
|
||||
if (value.equals(BOOL_TRUE_SINGLETON) || value.equals(item)) {
|
||||
listToCheck = filterComp.pendingItemsToLeaveThrough;
|
||||
} else {
|
||||
listToCheck = filterComp.pendingItemsToReject;
|
||||
}
|
||||
|
||||
if (listToCheck.length >= MAX_ITEMS_IN_QUEUE) {
|
||||
// Busy
|
||||
return false;
|
||||
}
|
||||
|
||||
// Actually accept item
|
||||
listToCheck.push({
|
||||
item,
|
||||
progress: 0.0,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -282,6 +282,15 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -58,7 +58,6 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
|
|||
[enumItemProcessorTypes.painterDouble]: this.process_PAINTER_DOUBLE,
|
||||
[enumItemProcessorTypes.painterQuad]: this.process_PAINTER_QUAD,
|
||||
[enumItemProcessorTypes.hub]: this.process_HUB,
|
||||
[enumItemProcessorTypes.filter]: this.process_FILTER,
|
||||
[enumItemProcessorTypes.reader]: this.process_READER,
|
||||
};
|
||||
|
||||
|
@ -162,24 +161,13 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
|
|||
|
||||
// Check the network value at the given slot
|
||||
const network = pinsComp.slots[slotIndex - 1].linkedNetwork;
|
||||
const slotIsEnabled = network && isTruthyItem(network.currentValue);
|
||||
const slotIsEnabled = network && network.hasValue() && isTruthyItem(network.currentValue);
|
||||
if (!slotIsEnabled) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case enumItemProcessorRequirements.filter: {
|
||||
const network = pinsComp.slots[0].linkedNetwork;
|
||||
if (!network || !network.currentValue) {
|
||||
// Item filter is not connected
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise, all good
|
||||
return true;
|
||||
}
|
||||
|
||||
// By default, everything is accepted
|
||||
default:
|
||||
return true;
|
||||
|
@ -222,9 +210,8 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
|
|||
// Check which slots are enabled
|
||||
for (let i = 0; i < 4; ++i) {
|
||||
// Extract the network value on the Nth pin
|
||||
const networkValue = pinsComp.slots[i].linkedNetwork
|
||||
? pinsComp.slots[i].linkedNetwork.currentValue
|
||||
: null;
|
||||
const network = pinsComp.slots[i].linkedNetwork;
|
||||
const networkValue = network && network.hasValue() ? network.currentValue : null;
|
||||
|
||||
// If there is no "1" on that slot, don't paint there
|
||||
if (!isTruthyItem(networkValue)) {
|
||||
|
@ -257,18 +244,6 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
|
|||
return true;
|
||||
}
|
||||
|
||||
// FILTER
|
||||
// Double check with linked network
|
||||
case enumItemProcessorRequirements.filter: {
|
||||
const network = entity.components.WiredPins.slots[0].linkedNetwork;
|
||||
if (!network || !network.currentValue) {
|
||||
// Item filter is not connected
|
||||
return false;
|
||||
}
|
||||
|
||||
return processorComp.inputSlots.length >= processorComp.inputsPerCharge;
|
||||
}
|
||||
|
||||
default:
|
||||
assertAlways(false, "Unknown requirement for " + processorComp.processingRequirement);
|
||||
}
|
||||
|
@ -553,38 +528,6 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ProcessorImplementationPayload} payload
|
||||
*/
|
||||
process_FILTER(payload) {
|
||||
const item = payload.itemsBySlot[0];
|
||||
|
||||
const network = payload.entity.components.WiredPins.slots[0].linkedNetwork;
|
||||
if (!network || !network.currentValue) {
|
||||
payload.outItems.push({
|
||||
item,
|
||||
requiredSlot: 1,
|
||||
doNotTrack: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const value = network.currentValue;
|
||||
if (value.equals(BOOL_TRUE_SINGLETON) || value.equals(item)) {
|
||||
payload.outItems.push({
|
||||
item,
|
||||
requiredSlot: 0,
|
||||
doNotTrack: true,
|
||||
});
|
||||
} else {
|
||||
payload.outItems.push({
|
||||
item,
|
||||
requiredSlot: 1,
|
||||
doNotTrack: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ProcessorImplementationPayload} payload
|
||||
*/
|
||||
|
|
|
@ -34,10 +34,10 @@ export class ItemProcessorOverlaysSystem extends GameSystem {
|
|||
for (let i = 0; i < contents.length; ++i) {
|
||||
const entity = contents[i];
|
||||
const processorComp = entity.components.ItemProcessor;
|
||||
if (!processorComp) {
|
||||
continue;
|
||||
}
|
||||
const filterComp = entity.components.Filter;
|
||||
|
||||
// Draw processor overlays
|
||||
if (processorComp) {
|
||||
const requirement = processorComp.processingRequirement;
|
||||
if (!requirement && processorComp.type !== enumItemProcessorTypes.reader) {
|
||||
continue;
|
||||
|
@ -46,7 +46,6 @@ export class ItemProcessorOverlaysSystem extends GameSystem {
|
|||
if (this.drawnUids.has(entity.uid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.drawnUids.add(entity.uid);
|
||||
|
||||
switch (requirement) {
|
||||
|
@ -54,16 +53,23 @@ export class ItemProcessorOverlaysSystem extends GameSystem {
|
|||
this.drawConnectedSlotRequirement(parameters, entity, { drawIfFalse: true });
|
||||
break;
|
||||
}
|
||||
case enumItemProcessorRequirements.filter: {
|
||||
this.drawConnectedSlotRequirement(parameters, entity, { drawIfFalse: false });
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (processorComp.type === enumItemProcessorTypes.reader) {
|
||||
this.drawReaderOverlays(parameters, entity);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw filter overlays
|
||||
else if (filterComp) {
|
||||
if (this.drawnUids.has(entity.uid)) {
|
||||
continue;
|
||||
}
|
||||
this.drawnUids.add(entity.uid);
|
||||
|
||||
this.drawConnectedSlotRequirement(parameters, entity, { drawIfFalse: false });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -111,7 +117,7 @@ export class ItemProcessorOverlaysSystem extends GameSystem {
|
|||
for (let i = 0; i < pinsComp.slots.length; ++i) {
|
||||
const slot = pinsComp.slots[i];
|
||||
const network = slot.linkedNetwork;
|
||||
if (network && network.currentValue) {
|
||||
if (network && network.hasValue()) {
|
||||
anySlotConnected = true;
|
||||
|
||||
if (isTruthyItem(network.currentValue) || !drawIfFalse) {
|
||||
|
|
|
@ -47,13 +47,13 @@ export class LogicGateSystem extends GameSystemWithFilter {
|
|||
if (slot.type !== enumPinSlotType.logicalAcceptor) {
|
||||
continue;
|
||||
}
|
||||
if (slot.linkedNetwork) {
|
||||
if (slot.linkedNetwork.valueConflict) {
|
||||
const network = slot.linkedNetwork;
|
||||
if (network) {
|
||||
if (network.valueConflict) {
|
||||
anyConflict = true;
|
||||
break;
|
||||
}
|
||||
|
||||
slotValues.push(slot.linkedNetwork.currentValue);
|
||||
slotValues.push(network.currentValue);
|
||||
} else {
|
||||
slotValues.push(null);
|
||||
}
|
||||
|
|
|
@ -79,6 +79,14 @@ export class WireNetwork {
|
|||
*/
|
||||
this.uid = ++networkUidCounter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this network currently has a value
|
||||
* @returns {boolean}
|
||||
*/
|
||||
hasValue() {
|
||||
return !!this.currentValue && !this.valueConflict;
|
||||
}
|
||||
}
|
||||
|
||||
export class WireSystem extends GameSystemWithFilter {
|
||||
|
|
Reference in New Issue