1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2026-03-02 03:39:21 +00:00

Highlight connected miners, improve miner performance

This commit is contained in:
tobspr
2020-09-13 18:39:06 +02:00
parent 3529a5d77f
commit 0377c6d58f
9 changed files with 548 additions and 333 deletions

View File

@@ -1,6 +1,7 @@
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;
@@ -40,6 +41,13 @@ export class MinerComponent extends Component {
* @type {BaseItem}
*/
this.cachedMinedItem = null;
/**
* Which miner this miner ejects to, in case its a chainable one.
* If the value is false, it means there is no entity, and we don't have to re-check
* @type {Entity|null|false}
*/
this.cachedChainedMiner = null;
}
/**

View File

@@ -1,277 +1,281 @@
/* typehints:start */
import { GameRoot } from "../root";
/* typehints:end */
/* dev:start */
import { TrailerMaker } from "./trailer_maker";
/* dev:end */
import { Signal } from "../../core/signal";
import { DrawParameters } from "../../core/draw_parameters";
import { HUDBuildingsToolbar } from "./parts/buildings_toolbar";
import { HUDBuildingPlacer } from "./parts/building_placer";
import { HUDBlueprintPlacer } from "./parts/blueprint_placer";
import { HUDKeybindingOverlay } from "./parts/keybinding_overlay";
import { HUDUnlockNotification } from "./parts/unlock_notification";
import { HUDGameMenu } from "./parts/game_menu";
import { HUDShop } from "./parts/shop";
import { IS_MOBILE, globalConfig, IS_DEMO } from "../../core/config";
import { HUDMassSelector } from "./parts/mass_selector";
import { HUDVignetteOverlay } from "./parts/vignette_overlay";
import { HUDStatistics } from "./parts/statistics";
import { MetaBuilding } from "../meta_building";
import { HUDPinnedShapes } from "./parts/pinned_shapes";
import { ShapeDefinition } from "../shape_definition";
import { HUDNotifications, enumNotificationType } from "./parts/notifications";
import { HUDSettingsMenu } from "./parts/settings_menu";
import { HUDDebugInfo } from "./parts/debug_info";
import { HUDEntityDebugger } from "./parts/entity_debugger";
import { KEYMAPPINGS } from "../key_action_mapper";
import { HUDWatermark } from "./parts/watermark";
import { HUDModalDialogs } from "./parts/modal_dialogs";
import { HUDPartTutorialHints } from "./parts/tutorial_hints";
import { HUDWaypoints } from "./parts/waypoints";
import { HUDInteractiveTutorial } from "./parts/interactive_tutorial";
import { HUDScreenshotExporter } from "./parts/screenshot_exporter";
import { HUDColorBlindHelper } from "./parts/color_blind_helper";
import { HUDShapeViewer } from "./parts/shape_viewer";
import { HUDWiresOverlay } from "./parts/wires_overlay";
import { HUDChangesDebugger } from "./parts/debug_changes";
import { queryParamOptions } from "../../core/query_parameters";
import { HUDSandboxController } from "./parts/sandbox_controller";
import { HUDWiresToolbar } from "./parts/wires_toolbar";
import { HUDWireInfo } from "./parts/wire_info";
import { HUDLeverToggle } from "./parts/lever_toggle";
import { HUDLayerPreview } from "./parts/layer_preview";
export class GameHUD {
/**
* @param {GameRoot} root
*/
constructor(root) {
this.root = root;
}
/**
* Initializes the hud parts
*/
initialize() {
this.parts = {
buildingsToolbar: new HUDBuildingsToolbar(this.root),
wiresToolbar: new HUDWiresToolbar(this.root),
blueprintPlacer: new HUDBlueprintPlacer(this.root),
buildingPlacer: new HUDBuildingPlacer(this.root),
unlockNotification: new HUDUnlockNotification(this.root),
gameMenu: new HUDGameMenu(this.root),
massSelector: new HUDMassSelector(this.root),
shop: new HUDShop(this.root),
statistics: new HUDStatistics(this.root),
waypoints: new HUDWaypoints(this.root),
wireInfo: new HUDWireInfo(this.root),
leverToggle: new HUDLeverToggle(this.root),
// Must always exist
pinnedShapes: new HUDPinnedShapes(this.root),
notifications: new HUDNotifications(this.root),
settingsMenu: new HUDSettingsMenu(this.root),
// betaOverlay: new HUDBetaOverlay(this.root),
debugInfo: new HUDDebugInfo(this.root),
dialogs: new HUDModalDialogs(this.root),
screenshotExporter: new HUDScreenshotExporter(this.root),
shapeViewer: new HUDShapeViewer(this.root),
wiresOverlay: new HUDWiresOverlay(this.root),
layerPreview: new HUDLayerPreview(this.root),
// Typing hints
/* typehints:start */
/** @type {HUDChangesDebugger} */
changesDebugger: null,
/* typehints:end */
};
this.signals = {
buildingSelectedForPlacement: /** @type {TypedSignal<[MetaBuilding|null]>} */ (new Signal()),
selectedPlacementBuildingChanged: /** @type {TypedSignal<[MetaBuilding|null]>} */ (new Signal()),
shapePinRequested: /** @type {TypedSignal<[ShapeDefinition]>} */ (new Signal()),
shapeUnpinRequested: /** @type {TypedSignal<[string]>} */ (new Signal()),
notification: /** @type {TypedSignal<[string, enumNotificationType]>} */ (new Signal()),
buildingsSelectedForCopy: /** @type {TypedSignal<[Array<number>]>} */ (new Signal()),
pasteBlueprintRequested: /** @type {TypedSignal<[]>} */ (new Signal()),
viewShapeDetailsRequested: /** @type {TypedSignal<[ShapeDefinition]>} */ (new Signal()),
};
if (!IS_MOBILE) {
this.parts.keybindingOverlay = new HUDKeybindingOverlay(this.root);
}
if (G_IS_DEV && globalConfig.debug.enableEntityInspector) {
this.parts.entityDebugger = new HUDEntityDebugger(this.root);
}
if (IS_DEMO) {
this.parts.watermark = new HUDWatermark(this.root);
}
if (G_IS_DEV && globalConfig.debug.renderChanges) {
this.parts.changesDebugger = new HUDChangesDebugger(this.root);
}
if (this.root.app.settings.getAllSettings().offerHints) {
this.parts.tutorialHints = new HUDPartTutorialHints(this.root);
this.parts.interactiveTutorial = new HUDInteractiveTutorial(this.root);
}
if (this.root.app.settings.getAllSettings().vignette) {
this.parts.vignetteOverlay = new HUDVignetteOverlay(this.root);
}
if (this.root.app.settings.getAllSettings().enableColorBlindHelper) {
this.parts.colorBlindHelper = new HUDColorBlindHelper(this.root);
}
if (queryParamOptions.sandboxMode || G_IS_DEV) {
this.parts.sandboxController = new HUDSandboxController(this.root);
}
const frag = document.createDocumentFragment();
for (const key in this.parts) {
this.parts[key].createElements(frag);
}
document.body.appendChild(frag);
for (const key in this.parts) {
this.parts[key].initialize();
}
this.root.keyMapper.getBinding(KEYMAPPINGS.ingame.toggleHud).add(this.toggleUi, this);
/* dev:start */
if (G_IS_DEV && globalConfig.debug.renderForTrailer) {
this.trailerMaker = new TrailerMaker(this.root);
}
/* dev:end*/
}
/**
* Attempts to close all overlays
*/
closeAllOverlays() {
for (const key in this.parts) {
this.parts[key].close();
}
}
/**
* Returns true if the game logic should be paused
*/
shouldPauseGame() {
for (const key in this.parts) {
if (this.parts[key].shouldPauseGame()) {
return true;
}
}
return false;
}
/**
* Returns true if the rendering can be paused
*/
shouldPauseRendering() {
for (const key in this.parts) {
if (this.parts[key].shouldPauseRendering()) {
return true;
}
}
return false;
}
/**
* Returns true if the rendering can be paused
*/
hasBlockingOverlayOpen() {
if (this.root.camera.getIsMapOverlayActive()) {
return true;
}
for (const key in this.parts) {
if (this.parts[key].isBlockingOverlay()) {
return true;
}
}
return false;
}
/**
* Toggles the ui
*/
toggleUi() {
document.body.classList.toggle("uiHidden");
}
/**
* Updates all parts
*/
update() {
if (!this.root.gameInitialized) {
return;
}
for (const key in this.parts) {
this.parts[key].update();
}
/* dev:start */
if (this.trailerMaker) {
this.trailerMaker.update();
}
/* dev:end*/
}
/**
* Draws all parts
* @param {DrawParameters} parameters
*/
draw(parameters) {
const partsOrder = [
"massSelector",
"buildingPlacer",
"blueprintPlacer",
"colorBlindHelper",
"changesDebugger",
];
for (let i = 0; i < partsOrder.length; ++i) {
if (this.parts[partsOrder[i]]) {
this.parts[partsOrder[i]].draw(parameters);
}
}
}
/**
* Draws all part overlays
* @param {DrawParameters} parameters
*/
drawOverlays(parameters) {
const partsOrder = ["waypoints", "watermark", "wireInfo"];
for (let i = 0; i < partsOrder.length; ++i) {
if (this.parts[partsOrder[i]]) {
this.parts[partsOrder[i]].drawOverlays(parameters);
}
}
}
/**
* Cleans up everything
*/
cleanup() {
for (const key in this.parts) {
this.parts[key].cleanup();
}
for (const key in this.signals) {
this.signals[key].removeAll();
}
}
}
/* typehints:start */
import { GameRoot } from "../root";
/* typehints:end */
/* dev:start */
import { TrailerMaker } from "./trailer_maker";
/* dev:end */
import { Signal } from "../../core/signal";
import { DrawParameters } from "../../core/draw_parameters";
import { HUDBuildingsToolbar } from "./parts/buildings_toolbar";
import { HUDBuildingPlacer } from "./parts/building_placer";
import { HUDBlueprintPlacer } from "./parts/blueprint_placer";
import { HUDKeybindingOverlay } from "./parts/keybinding_overlay";
import { HUDUnlockNotification } from "./parts/unlock_notification";
import { HUDGameMenu } from "./parts/game_menu";
import { HUDShop } from "./parts/shop";
import { IS_MOBILE, globalConfig, IS_DEMO } from "../../core/config";
import { HUDMassSelector } from "./parts/mass_selector";
import { HUDVignetteOverlay } from "./parts/vignette_overlay";
import { HUDStatistics } from "./parts/statistics";
import { MetaBuilding } from "../meta_building";
import { HUDPinnedShapes } from "./parts/pinned_shapes";
import { ShapeDefinition } from "../shape_definition";
import { HUDNotifications, enumNotificationType } from "./parts/notifications";
import { HUDSettingsMenu } from "./parts/settings_menu";
import { HUDDebugInfo } from "./parts/debug_info";
import { HUDEntityDebugger } from "./parts/entity_debugger";
import { KEYMAPPINGS } from "../key_action_mapper";
import { HUDWatermark } from "./parts/watermark";
import { HUDModalDialogs } from "./parts/modal_dialogs";
import { HUDPartTutorialHints } from "./parts/tutorial_hints";
import { HUDWaypoints } from "./parts/waypoints";
import { HUDInteractiveTutorial } from "./parts/interactive_tutorial";
import { HUDScreenshotExporter } from "./parts/screenshot_exporter";
import { HUDColorBlindHelper } from "./parts/color_blind_helper";
import { HUDShapeViewer } from "./parts/shape_viewer";
import { HUDWiresOverlay } from "./parts/wires_overlay";
import { HUDChangesDebugger } from "./parts/debug_changes";
import { queryParamOptions } from "../../core/query_parameters";
import { HUDSandboxController } from "./parts/sandbox_controller";
import { HUDWiresToolbar } from "./parts/wires_toolbar";
import { HUDWireInfo } from "./parts/wire_info";
import { HUDLeverToggle } from "./parts/lever_toggle";
import { HUDLayerPreview } from "./parts/layer_preview";
import { HUDMinerHighlight } from "./parts/miner_highlight";
export class GameHUD {
/**
* @param {GameRoot} root
*/
constructor(root) {
this.root = root;
}
/**
* Initializes the hud parts
*/
initialize() {
this.parts = {
buildingsToolbar: new HUDBuildingsToolbar(this.root),
wiresToolbar: new HUDWiresToolbar(this.root),
blueprintPlacer: new HUDBlueprintPlacer(this.root),
buildingPlacer: new HUDBuildingPlacer(this.root),
unlockNotification: new HUDUnlockNotification(this.root),
gameMenu: new HUDGameMenu(this.root),
massSelector: new HUDMassSelector(this.root),
shop: new HUDShop(this.root),
statistics: new HUDStatistics(this.root),
waypoints: new HUDWaypoints(this.root),
wireInfo: new HUDWireInfo(this.root),
leverToggle: new HUDLeverToggle(this.root),
// Must always exist
pinnedShapes: new HUDPinnedShapes(this.root),
notifications: new HUDNotifications(this.root),
settingsMenu: new HUDSettingsMenu(this.root),
// betaOverlay: new HUDBetaOverlay(this.root),
debugInfo: new HUDDebugInfo(this.root),
dialogs: new HUDModalDialogs(this.root),
screenshotExporter: new HUDScreenshotExporter(this.root),
shapeViewer: new HUDShapeViewer(this.root),
wiresOverlay: new HUDWiresOverlay(this.root),
layerPreview: new HUDLayerPreview(this.root),
minerHighlight: new HUDMinerHighlight(this.root),
// Typing hints
/* typehints:start */
/** @type {HUDChangesDebugger} */
changesDebugger: null,
/* typehints:end */
};
this.signals = {
buildingSelectedForPlacement: /** @type {TypedSignal<[MetaBuilding|null]>} */ (new Signal()),
selectedPlacementBuildingChanged: /** @type {TypedSignal<[MetaBuilding|null]>} */ (new Signal()),
shapePinRequested: /** @type {TypedSignal<[ShapeDefinition]>} */ (new Signal()),
shapeUnpinRequested: /** @type {TypedSignal<[string]>} */ (new Signal()),
notification: /** @type {TypedSignal<[string, enumNotificationType]>} */ (new Signal()),
buildingsSelectedForCopy: /** @type {TypedSignal<[Array<number>]>} */ (new Signal()),
pasteBlueprintRequested: /** @type {TypedSignal<[]>} */ (new Signal()),
viewShapeDetailsRequested: /** @type {TypedSignal<[ShapeDefinition]>} */ (new Signal()),
};
if (!IS_MOBILE) {
this.parts.keybindingOverlay = new HUDKeybindingOverlay(this.root);
}
if (G_IS_DEV && globalConfig.debug.enableEntityInspector) {
this.parts.entityDebugger = new HUDEntityDebugger(this.root);
}
if (IS_DEMO) {
this.parts.watermark = new HUDWatermark(this.root);
}
if (G_IS_DEV && globalConfig.debug.renderChanges) {
this.parts.changesDebugger = new HUDChangesDebugger(this.root);
}
if (this.root.app.settings.getAllSettings().offerHints) {
this.parts.tutorialHints = new HUDPartTutorialHints(this.root);
this.parts.interactiveTutorial = new HUDInteractiveTutorial(this.root);
}
if (this.root.app.settings.getAllSettings().vignette) {
this.parts.vignetteOverlay = new HUDVignetteOverlay(this.root);
}
if (this.root.app.settings.getAllSettings().enableColorBlindHelper) {
this.parts.colorBlindHelper = new HUDColorBlindHelper(this.root);
}
if (queryParamOptions.sandboxMode || G_IS_DEV) {
this.parts.sandboxController = new HUDSandboxController(this.root);
}
const frag = document.createDocumentFragment();
for (const key in this.parts) {
this.parts[key].createElements(frag);
}
document.body.appendChild(frag);
for (const key in this.parts) {
this.parts[key].initialize();
}
this.root.keyMapper.getBinding(KEYMAPPINGS.ingame.toggleHud).add(this.toggleUi, this);
/* dev:start */
if (G_IS_DEV && globalConfig.debug.renderForTrailer) {
this.trailerMaker = new TrailerMaker(this.root);
}
/* dev:end*/
}
/**
* Attempts to close all overlays
*/
closeAllOverlays() {
for (const key in this.parts) {
this.parts[key].close();
}
}
/**
* Returns true if the game logic should be paused
*/
shouldPauseGame() {
for (const key in this.parts) {
if (this.parts[key].shouldPauseGame()) {
return true;
}
}
return false;
}
/**
* Returns true if the rendering can be paused
*/
shouldPauseRendering() {
for (const key in this.parts) {
if (this.parts[key].shouldPauseRendering()) {
return true;
}
}
return false;
}
/**
* Returns true if the rendering can be paused
*/
hasBlockingOverlayOpen() {
if (this.root.camera.getIsMapOverlayActive()) {
return true;
}
for (const key in this.parts) {
if (this.parts[key].isBlockingOverlay()) {
return true;
}
}
return false;
}
/**
* Toggles the ui
*/
toggleUi() {
document.body.classList.toggle("uiHidden");
}
/**
* Updates all parts
*/
update() {
if (!this.root.gameInitialized) {
return;
}
for (const key in this.parts) {
this.parts[key].update();
}
/* dev:start */
if (this.trailerMaker) {
this.trailerMaker.update();
}
/* dev:end*/
}
/**
* Draws all parts
* @param {DrawParameters} parameters
*/
draw(parameters) {
const partsOrder = [
"massSelector",
"buildingPlacer",
"blueprintPlacer",
"colorBlindHelper",
"changesDebugger",
"minerHighlight",
];
for (let i = 0; i < partsOrder.length; ++i) {
if (this.parts[partsOrder[i]]) {
this.parts[partsOrder[i]].draw(parameters);
}
}
}
/**
* Draws all part overlays
* @param {DrawParameters} parameters
*/
drawOverlays(parameters) {
const partsOrder = ["waypoints", "watermark", "wireInfo"];
for (let i = 0; i < partsOrder.length; ++i) {
if (this.parts[partsOrder[i]]) {
this.parts[partsOrder[i]].drawOverlays(parameters);
}
}
}
/**
* Cleans up everything
*/
cleanup() {
for (const key in this.parts) {
this.parts[key].cleanup();
}
for (const key in this.signals) {
this.signals[key].removeAll();
}
}
}

View File

@@ -0,0 +1,170 @@
import { globalConfig } from "../../../core/config";
import { formatItemsPerSecond, round2Digits } from "../../../core/utils";
import { Vector } from "../../../core/vector";
import { T } from "../../../translations";
import { Entity } from "../../entity";
import { THEME } from "../../theme";
import { BaseHUDPart } from "../base_hud_part";
export class HUDMinerHighlight extends BaseHUDPart {
initialize() {}
/**
*
* @param {import("../../../core/draw_utils").DrawParameters} parameters
*/
draw(parameters) {
const mousePos = this.root.app.mousePosition;
if (!mousePos) {
// Mouse pos not ready
return;
}
if (this.root.currentLayer !== "regular") {
// Not within the regular layer
return;
}
if (this.root.camera.getIsMapOverlayActive()) {
// Not within the map overlay
return;
}
const worldPos = this.root.camera.screenToWorld(mousePos);
const hoveredTile = worldPos.toTileSpace();
const contents = this.root.map.getTileContent(hoveredTile, "regular");
if (!contents) {
// Empty tile
return;
}
const minerComp = contents.components.Miner;
if (!minerComp || !minerComp.chainable) {
// Not a chainable miner
return;
}
parameters.context.fillStyle = THEME.map.connectedMiners.overlay;
const connectedEntities = this.findConnectedMiners(contents);
for (let i = 0; i < connectedEntities.length; ++i) {
const entity = connectedEntities[i];
const staticComp = entity.components.StaticMapEntity;
parameters.context.beginRoundedRect(
staticComp.origin.x * globalConfig.tileSize + 5,
staticComp.origin.y * globalConfig.tileSize + 5,
globalConfig.tileSize - 10,
globalConfig.tileSize - 10,
3
);
parameters.context.fill();
}
const throughput = round2Digits(connectedEntities.length * this.root.hubGoals.getMinerBaseSpeed());
const maxThroughput = this.root.hubGoals.getBeltBaseSpeed();
const screenPos = this.root.camera.screenToWorld(mousePos);
const scale = (1 / this.root.camera.zoomLevel) * this.root.app.getEffectiveUiScale();
const isCapped = throughput > maxThroughput;
// Background
parameters.context.fillStyle = THEME.map.connectedMiners.background;
parameters.context.beginRoundedRect(
screenPos.x + 5 * scale,
screenPos.y - 3 * scale,
(isCapped ? 100 : 65) * scale,
(isCapped ? 45 : 30) * scale,
2
);
parameters.context.fill();
// Throughput
parameters.context.fillStyle = THEME.map.connectedMiners.textColor;
parameters.context.font = "bold " + scale * 10 + "px GameFont";
parameters.context.fillText(
formatItemsPerSecond(throughput),
screenPos.x + 10 * scale,
screenPos.y + 10 * scale
);
// Amount of miners
parameters.context.globalAlpha = 0.6;
parameters.context.font = "bold " + scale * 8 + "px GameFont";
parameters.context.fillText(
connectedEntities.length === 1
? T.ingame.connectedMiners.one_miner
: T.ingame.connectedMiners.n_miners.replace("<amount>", String(connectedEntities.length)),
screenPos.x + 10 * scale,
screenPos.y + 22 * scale
);
parameters.context.globalAlpha = 1;
if (isCapped) {
parameters.context.fillStyle = THEME.map.connectedMiners.textColorCapped;
parameters.context.fillText(
T.ingame.connectedMiners.limited_items.replace(
"<max_throughput>",
formatItemsPerSecond(maxThroughput)
),
screenPos.x + 10 * scale,
screenPos.y + 34 * scale
);
}
}
/**
* Finds all connected miners to the given entity
* @param {Entity} entity
* @param {Set<number>} seenUids Which entities have already been processed
* @returns {Array<Entity>} The connected miners
*/
findConnectedMiners(entity, seenUids = new Set()) {
let results = [];
const origin = entity.components.StaticMapEntity.origin;
if (!seenUids.has(entity.uid)) {
seenUids.add(entity.uid);
results.push(entity);
}
// Check for the miner which we connect to
const connectedMiner = this.root.systemMgr.systems.miner.findChainedMiner(entity);
if (connectedMiner && !seenUids.has(connectedMiner.uid)) {
results.push(connectedMiner);
seenUids.add(connectedMiner.uid);
results.push(...this.findConnectedMiners(connectedMiner, seenUids));
}
// Search within a 1x1 grid - this assumes miners are always 1x1
for (let dx = -1; dx <= 1; ++dx) {
for (let dy = -1; dy <= 1; ++dy) {
const contents = this.root.map.getTileContent(
new Vector(origin.x + dx, origin.y + dy),
"regular"
);
if (contents) {
const minerComp = contents.components.Miner;
if (minerComp && minerComp.chainable) {
// Found a miner connected to this entity
if (!seenUids.has(contents.uid)) {
if (this.root.systemMgr.systems.miner.findChainedMiner(contents) === entity) {
results.push(contents);
seenUids.add(contents.uid);
results.push(...this.findConnectedMiners(contents, seenUids));
}
}
}
}
}
}
return results;
}
}

View File

@@ -212,43 +212,5 @@ export class MapView extends BaseMap {
}
this.drawVisibleChunks(parameters, MapChunkView.prototype.drawBackgroundLayer);
if (G_IS_DEV && globalConfig.debug.showChunkBorders) {
const cullRange = parameters.visibleRect.toTileCullRectangle();
const top = cullRange.top();
const right = cullRange.right();
const bottom = cullRange.bottom();
const left = cullRange.left();
const border = 1;
const minY = top - border;
const maxY = bottom + border;
const minX = left - border;
const maxX = right + border - 1;
const chunkStartX = Math.floor(minX / globalConfig.mapChunkSize);
const chunkStartY = Math.floor(minY / globalConfig.mapChunkSize);
const chunkEndX = Math.ceil(maxX / globalConfig.mapChunkSize);
const chunkEndY = Math.ceil(maxY / globalConfig.mapChunkSize);
for (let chunkX = chunkStartX; chunkX <= chunkEndX; ++chunkX) {
for (let chunkY = chunkStartY; chunkY <= chunkEndY; ++chunkY) {
parameters.context.fillStyle = "#ffaaaa";
parameters.context.fillRect(
chunkX * globalConfig.mapChunkWorldSize,
chunkY * globalConfig.mapChunkWorldSize,
globalConfig.mapChunkWorldSize,
3
);
parameters.context.fillRect(
chunkX * globalConfig.mapChunkWorldSize,
chunkY * globalConfig.mapChunkWorldSize,
3,
globalConfig.mapChunkWorldSize
);
}
}
}
}
}

View File

@@ -10,6 +10,24 @@ import { MapChunkView } from "../map_chunk_view";
export class MinerSystem extends GameSystemWithFilter {
constructor(root) {
super(root, [MinerComponent]);
this.needsRecompute = true;
this.root.signals.entityAdded.add(this.onEntityChanged, this);
this.root.signals.entityChanged.add(this.onEntityChanged, this);
this.root.signals.entityDestroyed.add(this.onEntityChanged, this);
}
/**
* Called whenever an entity got changed
* @param {Entity} entity
*/
onEntityChanged(entity) {
const minerComp = entity.components.Miner;
if (minerComp && minerComp.chainable) {
// Miner component, need to recompute
this.needsRecompute = true;
}
}
update() {
@@ -20,11 +38,15 @@ export class MinerSystem extends GameSystemWithFilter {
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
const minerComp = entity.components.Miner;
if (!minerComp.cachedMinedItem) {
const staticComp = entity.components.StaticMapEntity;
const tileBelow = this.root.map.getLowerLayerContentXY(
@@ -59,6 +81,36 @@ export class MinerSystem extends GameSystemWithFilter {
}
}
}
// After this frame we are done
this.needsRecompute = false;
}
/**
* Finds the target chained miner for a given entity
* @param {Entity} entity
* @returns {Entity|false} The chained entity or null if not found
*/
findChainedMiner(entity) {
const ejectComp = entity.components.ItemEjector;
const staticComp = entity.components.StaticMapEntity;
const ejectingSlot = ejectComp.slots[0];
const ejectingPos = staticComp.localTileToWorld(ejectingSlot.pos);
const ejectingDirection = staticComp.localDirectionToWorld(ejectingSlot.direction);
const targetTile = ejectingPos.add(enumDirectionToVector[ejectingDirection]);
const targetContents = this.root.map.getTileContent(targetTile, "regular");
// 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) {
return targetContents;
}
}
return false;
}
/**
@@ -69,26 +121,23 @@ export class MinerSystem extends GameSystemWithFilter {
tryPerformMinerEject(entity, item) {
const minerComp = entity.components.Miner;
const ejectComp = entity.components.ItemEjector;
const staticComp = entity.components.StaticMapEntity;
// Check if we are a chained miner
if (minerComp.chainable) {
const ejectingSlot = ejectComp.slots[0];
const ejectingPos = staticComp.localTileToWorld(ejectingSlot.pos);
const ejectingDirection = staticComp.localDirectionToWorld(ejectingSlot.direction);
const targetEntity = minerComp.cachedChainedMiner;
const targetTile = ejectingPos.add(enumDirectionToVector[ejectingDirection]);
const targetContents = this.root.map.getTileContent(targetTile, "regular");
// Check if the cache has to get recomputed
if (targetEntity === null) {
minerComp.cachedChainedMiner = this.findChainedMiner(entity);
}
// Check if we are connected to another miner and thus do not eject directly
if (targetContents) {
const targetMinerComp = targetContents.components.Miner;
if (targetMinerComp) {
if (targetMinerComp.tryAcceptChainedItem(item)) {
return true;
} else {
return false;
}
// Check if we now have a target
if (targetEntity) {
const targetMinerComp = targetEntity.components.Miner;
if (targetMinerComp.tryAcceptChainedItem(item)) {
return true;
} else {
return false;
}
}
}
@@ -97,6 +146,7 @@ export class MinerSystem extends GameSystemWithFilter {
if (ejectComp.tryEject(0, item)) {
return true;
}
return false;
}

View File

@@ -39,6 +39,13 @@
"overlayColor": "rgba(97, 161, 152, 0.75)",
"previewColor": "rgb(97, 161, 152, 0.5)",
"highlightColor": "rgba(0, 0, 255, 0.5)"
},
"connectedMiners": {
"overlay": "rgba(40, 50, 60, 0.5)",
"textColor": "#fff",
"textColorCapped": "#ef5072",
"background": "rgba(40, 50, 60, 0.8)"
}
},

View File

@@ -39,7 +39,14 @@
"wires": {
"overlayColor": "rgba(97, 161, 152, 0.75)",
"previewColor": "rgb(97, 161, 152, 0.4)",
"highlightColor": "rgba(72, 137, 255, 0.8)"
"highlightColor": "rgba(72, 137, 255, 1)"
},
"connectedMiners": {
"overlay": "rgba(40, 50, 60, 0.5)",
"textColor": "#fff",
"textColorCapped": "#ef5072",
"background": "rgba(40, 50, 60, 0.8)"
}
},