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 { 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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) {
|
||||||
|
@ -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)) {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user