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

View File

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

View File

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

View File

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

View File

@ -163,7 +163,14 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
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
sourceSlot.progress += progressGrowth;
}
@ -179,8 +186,6 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
const extraProgress = sourceSlot.progress - maxProgress;
// Check if we are ejecting to a belt path
const destPath = sourceSlot.cachedBeltPath;
if (destPath) {
// Try passing the item over
if (destPath.tryAcceptItem(item, extraProgress)) {

View File

@ -24,7 +24,9 @@ export class ItemProducerSystem extends GameSystemWithFilter {
}
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 { DrawParameters } from "../../core/draw_parameters";
import { enumDirectionToVector } from "../../core/vector";
import { BaseItem } from "../base_item";
import { MinerComponent } from "../components/miner";
import { Entity } from "../entity";
import { GameSystemWithFilter } from "../game_system_with_filter";
@ -31,20 +30,18 @@ export class MinerSystem extends GameSystemWithFilter {
}
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) {
miningSpeed *= 100;
progressGrowth = targetProgress;
}
for (let i = 0; i < this.allEntities.length; ++i) {
const entity = this.allEntities[i];
const minerComp = entity.components.Miner;
// Reset everything on recompute
if (this.needsRecompute) {
minerComp.cachedChainedMiner = null;
}
// Check if miner is above an actual tile
if (!minerComp.cachedMinedItem) {
const staticComp = entity.components.StaticMapEntity;
@ -58,25 +55,60 @@ export class MinerSystem extends GameSystemWithFilter {
minerComp.cachedMinedItem = tileBelow;
}
// First, try to get rid of chained items
if (minerComp.itemChainBuffer.length > 0) {
if (this.tryPerformMinerEject(entity, minerComp.itemChainBuffer[0])) {
minerComp.itemChainBuffer.shift();
// Reset everything on recompute
if (this.needsRecompute) {
minerComp.cachedChainedMiner = null;
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;
}
}
//console.log(minerComp.progress);
const mineDuration = 1 / miningSpeed;
const timeSinceMine = this.root.time.now() - minerComp.lastMiningTime;
if (timeSinceMine > mineDuration) {
// Store how much we overflowed
const buffer = Math.min(timeSinceMine - mineDuration, this.root.dynamicTickrate.deltaSeconds);
if (minerComp.progress >= targetProgress) {
// We can try to eject
const extraProgress = minerComp.progress - targetProgress;
if (this.tryPerformMinerEject(entity, minerComp.cachedMinedItem)) {
const ejectorComp = entity.components.ItemEjector;
if (ejectorComp.tryEject(0, minerComp.cachedMinedItem, extraProgress)) {
// Analytics hook
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) {
const ejectComp = entity.components.ItemEjector;
const staticComp = entity.components.StaticMapEntity;
const minedItem = entity.components.Miner.cachedMinedItem;
const contentsBelow = this.root.map.getLowerLayerContentXY(staticComp.origin.x, staticComp.origin.y);
if (!contentsBelow) {
// 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
if (targetContents) {
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);
if (targetLowerLayer) {
return targetContents;
@ -121,39 +158,37 @@ export class MinerSystem extends GameSystemWithFilter {
}
/**
*
* Finds the target exit miner for a given 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 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
if (minerComp.chainable) {
const targetEntity = minerComp.cachedChainedMiner;
const ourPosition = entity.components.StaticMapEntity.origin;
// Check if the cache has to get recomputed
if (targetEntity === null) {
minerComp.cachedChainedMiner = this.findChainedMiner(entity);
}
// Check if we now have a target
if (targetEntity) {
const targetMinerComp = targetEntity.components.Miner;
if (targetMinerComp.tryAcceptChainedItem(item)) {
return true;
} else {
return false;
}
/** @type {Entity|null|false} */
let nextTarget = targetEntity;
while (nextTarget) {
targetEntity = nextTarget;
if (targetEntity.components.StaticMapEntity.origin == ourPosition) {
// we are in a loop, do nothing
targetEntity = null;
break;
}
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 (ejectComp.tryEject(0, item)) {
return true;
if (targetEntity) {
const targetMinerComp = targetEntity.components.Miner;
if (targetMinerComp.cachedMinedItem == minerComp.cachedMinedItem) {
// only chain the same items
return targetEntity;
}
}
return false;
}