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

Fix miner and item producer, and refactor chained miners

This commit is contained in:
Sense101 2022-02-06 15:42:07 +00:00
parent 5b036d7f5b
commit 07f89c45a3
7 changed files with 130 additions and 93 deletions

View File

@ -2,9 +2,13 @@ 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 { typeItemSingleton } from "../item_resolver";
const chainBufferSize = 6; /**
* @typedef {{
* item: BaseItem,
* extraProgress?: number,
* }} MinerItem
*/
export class MinerComponent extends Component { export class MinerComponent extends Component {
static getId() { static getId() {
@ -14,17 +18,17 @@ export class MinerComponent extends Component {
static getSchema() { static getSchema() {
// cachedMinedItem is not serialized. // cachedMinedItem is not serialized.
return { return {
lastMiningTime: types.ufloat, progress: types.ufloat,
itemChainBuffer: types.array(typeItemSingleton),
}; };
} }
constructor({ chainable = false }) { constructor({ chainable = false }) {
super(); super();
this.lastMiningTime = 0; this.progress = 0;
this.chainable = chainable; this.chainable = chainable;
/** /**
* The item we are mining beneath us
* @type {BaseItem} * @type {BaseItem}
*/ */
this.cachedMinedItem = null; this.cachedMinedItem = null;
@ -35,30 +39,11 @@ export class MinerComponent extends Component {
* @type {Entity|null|false} * @type {Entity|null|false}
*/ */
this.cachedChainedMiner = null; this.cachedChainedMiner = null;
this.clear();
}
clear() {
/** /**
* Stores items from other miners which were chained to this * The miner at the end of the chain, which actually ejects the items
* miner. * If the value is false, it means there is no entity, and we don't have to re-check
* @type {Array<BaseItem>} * @type {Entity|null|false}
*/ */
this.itemChainBuffer = []; this.cachedExitMiner = null;
}
/**
*
* @param {BaseItem} item
*/
tryAcceptChainedItem(item) {
if (this.itemChainBuffer.length > chainBufferSize) {
// Well, this one is full
return false;
}
this.itemChainBuffer.push(item);
return true;
} }
} }

View File

@ -150,12 +150,12 @@ export class GameSystemManager {
add("belt", BeltSystem); add("belt", BeltSystem);
add("miner", MinerSystem);
add("storage", StorageSystem); add("storage", StorageSystem);
add("itemEjector", ItemEjectorSystem); add("itemEjector", ItemEjectorSystem);
add("miner", MinerSystem);
add("undergroundBelt", UndergroundBeltSystem); add("undergroundBelt", UndergroundBeltSystem);
add("itemProcessor", ItemProcessorSystem); add("itemProcessor", ItemProcessorSystem);

View File

@ -303,17 +303,14 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic {
const mouseTile = worldPos.toTileSpace(); const mouseTile = worldPos.toTileSpace();
// Compute best rotation variant // Compute best rotation variant
const { const { rotation, rotationVariant, connectedEntities } =
rotation, metaBuilding.computeOptimalDirectionAndRotationVariantAtTile({
rotationVariant, root: this.root,
connectedEntities, tile: mouseTile,
} = metaBuilding.computeOptimalDirectionAndRotationVariantAtTile({ rotation: this.currentBaseRotation,
root: this.root, variant: this.currentVariant.get(),
tile: mouseTile, layer: metaBuilding.getLayer(),
rotation: this.currentBaseRotation, });
variant: this.currentVariant.get(),
layer: metaBuilding.getLayer(),
});
// Check if there are connected entities // Check if there are connected entities
if (connectedEntities) { if (connectedEntities) {
@ -657,8 +654,16 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic {
// Connected to a belt // Connected to a belt
isConnected = true; isConnected = true;
} else if (minerComp && minerComp.chainable && destMiner && destMiner.chainable) { } else if (minerComp && minerComp.chainable && destMiner && destMiner.chainable) {
// Chainable miners connected to eachother const worldTile = staticComp.localTileToWorld(slot.pos);
isConnected = true; if (
this.root.map.getLowerLayerContentXY(worldTile.x, worldTile.y) ==
destMiner.cachedMinedItem
) {
// Chainable miners connected to eachother
isConnected = true;
} else {
isBlocked = true;
}
} else { } else {
// This one is blocked // This one is blocked
isBlocked = true; isBlocked = true;

View File

@ -134,6 +134,7 @@ export class HUDMinerHighlight extends BaseHUDPart {
findConnectedMiners(entity, seenUids = new Set()) { findConnectedMiners(entity, seenUids = new Set()) {
let results = []; let results = [];
const origin = entity.components.StaticMapEntity.origin; const origin = entity.components.StaticMapEntity.origin;
const originMinerComp = entity.components.Miner;
if (!seenUids.has(entity.uid)) { if (!seenUids.has(entity.uid)) {
seenUids.add(entity.uid); seenUids.add(entity.uid);
@ -157,7 +158,11 @@ export class HUDMinerHighlight extends BaseHUDPart {
); );
if (contents) { if (contents) {
const minerComp = contents.components.Miner; const minerComp = contents.components.Miner;
if (minerComp && minerComp.chainable) { if (
minerComp &&
minerComp.chainable &&
originMinerComp.cachedMinedItem == minerComp.cachedMinedItem
) {
// Found a miner connected to this entity // Found a miner connected to this entity
if (!seenUids.has(contents.uid)) { if (!seenUids.has(contents.uid)) {
if (this.root.systemMgr.systems.miner.findChainedMiner(contents) === entity) { if (this.root.systemMgr.systems.miner.findChainedMiner(contents) === entity) {

View File

@ -163,7 +163,14 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
continue; continue;
} }
if (sourceSlot.progress < maxProgress) { // Limit progress here as well
let progressLimit = maxProgress;
const destPath = sourceSlot.cachedBeltPath;
if (destPath) {
progressLimit += destPath.spacingToFirstItem - globalConfig.itemSpacingOnBelts;
}
if (sourceSlot.progress < progressLimit) {
// Advance items on the slot // Advance items on the slot
sourceSlot.progress += progressGrowth; sourceSlot.progress += progressGrowth;
} }
@ -179,8 +186,6 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
const extraProgress = sourceSlot.progress - maxProgress; const extraProgress = sourceSlot.progress - maxProgress;
// Check if we are ejecting to a belt path
const destPath = sourceSlot.cachedBeltPath;
if (destPath) { if (destPath) {
// Try passing the item over // Try passing the item over
if (destPath.tryAcceptItem(item, extraProgress)) { if (destPath.tryAcceptItem(item, extraProgress)) {

View File

@ -24,7 +24,9 @@ export class ItemProducerSystem extends GameSystemWithFilter {
} }
this.item = network.currentValue; this.item = network.currentValue;
ejectorComp.tryEject(0, this.item);
// Basically start ejecting at the exit of the ejector. Hacky, but who cares. It works, and its not in the base game :)
ejectorComp.tryEject(0, this.item, 0.5);
} }
} }
} }

View File

@ -1,7 +1,6 @@
import { globalConfig } from "../../core/config"; import { globalConfig } from "../../core/config";
import { DrawParameters } from "../../core/draw_parameters"; import { DrawParameters } from "../../core/draw_parameters";
import { enumDirectionToVector } from "../../core/vector"; import { enumDirectionToVector } from "../../core/vector";
import { BaseItem } from "../base_item";
import { MinerComponent } from "../components/miner"; import { MinerComponent } from "../components/miner";
import { Entity } from "../entity"; import { Entity } from "../entity";
import { GameSystemWithFilter } from "../game_system_with_filter"; import { GameSystemWithFilter } from "../game_system_with_filter";
@ -31,20 +30,18 @@ export class MinerSystem extends GameSystemWithFilter {
} }
update() { update() {
let miningSpeed = this.root.hubGoals.getMinerBaseSpeed(); let progressGrowth = this.root.dynamicTickrate.deltaSeconds * this.root.hubGoals.getMinerBaseSpeed();
const targetProgress = 1;
if (G_IS_DEV && globalConfig.debug.instantMiners) { if (G_IS_DEV && globalConfig.debug.instantMiners) {
miningSpeed *= 100; progressGrowth = targetProgress;
} }
for (let i = 0; i < this.allEntities.length; ++i) { for (let i = 0; i < this.allEntities.length; ++i) {
const entity = this.allEntities[i]; const entity = this.allEntities[i];
const minerComp = entity.components.Miner; const minerComp = entity.components.Miner;
// Reset everything on recompute
if (this.needsRecompute) {
minerComp.cachedChainedMiner = null;
}
// Check if miner is above an actual tile // Check if miner is above an actual tile
if (!minerComp.cachedMinedItem) { if (!minerComp.cachedMinedItem) {
const staticComp = entity.components.StaticMapEntity; const staticComp = entity.components.StaticMapEntity;
@ -58,25 +55,60 @@ export class MinerSystem extends GameSystemWithFilter {
minerComp.cachedMinedItem = tileBelow; minerComp.cachedMinedItem = tileBelow;
} }
// First, try to get rid of chained items // Reset everything on recompute
if (minerComp.itemChainBuffer.length > 0) { if (this.needsRecompute) {
if (this.tryPerformMinerEject(entity, minerComp.itemChainBuffer[0])) { minerComp.cachedChainedMiner = null;
minerComp.itemChainBuffer.shift(); minerComp.cachedExitMiner = null;
}
// Check if we are a chained miner
if (minerComp.chainable) {
if (!minerComp.cachedChainedMiner) {
minerComp.cachedChainedMiner = this.findChainedMiner(entity);
}
// don't calculate on the same tick as recompute, or so miners wont have caches yet
if (minerComp.cachedChainedMiner && !minerComp.cachedExitMiner && !this.needsRecompute) {
minerComp.cachedExitMiner = this.findExitMiner(entity);
}
// Check if we now have a target at the end of the chain - if so, that's what we will progress
const exitEntity = minerComp.cachedExitMiner;
if (exitEntity) {
const exitMinerComp = exitEntity.components.Miner;
exitMinerComp.progress += progressGrowth;
continue; continue;
} }
} }
//console.log(minerComp.progress);
const mineDuration = 1 / miningSpeed; if (minerComp.progress >= targetProgress) {
const timeSinceMine = this.root.time.now() - minerComp.lastMiningTime; // We can try to eject
if (timeSinceMine > mineDuration) { const extraProgress = minerComp.progress - targetProgress;
// Store how much we overflowed
const buffer = Math.min(timeSinceMine - mineDuration, this.root.dynamicTickrate.deltaSeconds);
if (this.tryPerformMinerEject(entity, minerComp.cachedMinedItem)) { const ejectorComp = entity.components.ItemEjector;
if (ejectorComp.tryEject(0, minerComp.cachedMinedItem, extraProgress)) {
// Analytics hook // Analytics hook
this.root.signals.itemProduced.dispatch(minerComp.cachedMinedItem); this.root.signals.itemProduced.dispatch(minerComp.cachedMinedItem);
// Store mining time
minerComp.lastMiningTime = this.root.time.now() - buffer; minerComp.progress -= targetProgress;
}
}
if (minerComp.progress < targetProgress) {
minerComp.progress += progressGrowth;
}
if (minerComp.progress >= targetProgress) {
// We can try to eject
const extraProgress = minerComp.progress - targetProgress;
const ejectorComp = entity.components.ItemEjector;
if (ejectorComp.tryEject(0, minerComp.cachedMinedItem, extraProgress)) {
// Analytics hook
this.root.signals.itemProduced.dispatch(minerComp.cachedMinedItem);
minerComp.progress -= targetProgress;
} }
} }
} }
@ -93,6 +125,7 @@ export class MinerSystem extends GameSystemWithFilter {
findChainedMiner(entity) { findChainedMiner(entity) {
const ejectComp = entity.components.ItemEjector; const ejectComp = entity.components.ItemEjector;
const staticComp = entity.components.StaticMapEntity; const staticComp = entity.components.StaticMapEntity;
const minedItem = entity.components.Miner.cachedMinedItem;
const contentsBelow = this.root.map.getLowerLayerContentXY(staticComp.origin.x, staticComp.origin.y); const contentsBelow = this.root.map.getLowerLayerContentXY(staticComp.origin.x, staticComp.origin.y);
if (!contentsBelow) { if (!contentsBelow) {
// This miner has no contents // This miner has no contents
@ -109,7 +142,11 @@ export class MinerSystem extends GameSystemWithFilter {
// Check if we are connected to another miner and thus do not eject directly // Check if we are connected to another miner and thus do not eject directly
if (targetContents) { if (targetContents) {
const targetMinerComp = targetContents.components.Miner; const targetMinerComp = targetContents.components.Miner;
if (targetMinerComp && targetMinerComp.chainable) { if (
targetMinerComp &&
targetMinerComp.chainable &&
targetMinerComp.cachedMinedItem == minedItem
) {
const targetLowerLayer = this.root.map.getLowerLayerContentXY(targetTile.x, targetTile.y); const targetLowerLayer = this.root.map.getLowerLayerContentXY(targetTile.x, targetTile.y);
if (targetLowerLayer) { if (targetLowerLayer) {
return targetContents; return targetContents;
@ -121,39 +158,37 @@ export class MinerSystem extends GameSystemWithFilter {
} }
/** /**
* * Finds the target exit miner for a given entity
* @param {Entity} entity * @param {Entity} entity
* @param {BaseItem} item * @returns {Entity|false} The exit miner entity or null if not found
*/ */
tryPerformMinerEject(entity, item) { findExitMiner(entity) {
const minerComp = entity.components.Miner; const minerComp = entity.components.Miner;
const ejectComp = entity.components.ItemEjector; // Recompute exit miner if we are not at the front
let targetEntity = minerComp.cachedChainedMiner;
// Check if we are a chained miner const ourPosition = entity.components.StaticMapEntity.origin;
if (minerComp.chainable) {
const targetEntity = minerComp.cachedChainedMiner;
// Check if the cache has to get recomputed /** @type {Entity|null|false} */
if (targetEntity === null) { let nextTarget = targetEntity;
minerComp.cachedChainedMiner = this.findChainedMiner(entity); while (nextTarget) {
} targetEntity = nextTarget;
if (targetEntity.components.StaticMapEntity.origin == ourPosition) {
// Check if we now have a target // we are in a loop, do nothing
if (targetEntity) { targetEntity = null;
const targetMinerComp = targetEntity.components.Miner; break;
if (targetMinerComp.tryAcceptChainedItem(item)) {
return true;
} else {
return false;
}
} }
const targetMinerComp = targetEntity.components.Miner;
nextTarget = targetMinerComp.cachedChainedMiner;
} }
// Seems we are a regular miner or at the end of a row, try actually ejecting if (targetEntity) {
if (ejectComp.tryEject(0, item)) { const targetMinerComp = targetEntity.components.Miner;
return true; if (targetMinerComp.cachedMinedItem == minerComp.cachedMinedItem) {
// only chain the same items
return targetEntity;
}
} }
return false; return false;
} }