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:
parent
5b036d7f5b
commit
07f89c45a3
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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)) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user