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

@ -17,6 +17,7 @@ export const CHANGELOG = [
"Added a button to the statistics dialog to disable the sorting (by squeek502)",
"Tier 2 tunnels are now 9 tiles wide, so the gap between is 8 tiles (double the tier 1 range)",
"Updated and added new translations (Thanks to all contributors!)",
"Show connected chained miners on hover",
"Added setting to be able to delete buildings while placing (inspired by hexy)",
"You can now adjust the sound and music volumes! (inspired by Yoshie2000)",
"Some hud elements now have reduced opacity when hovering, so you can see through (inspired by mvb005)",

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)"
}
},

View File

@ -426,6 +426,12 @@ ingame:
1_3_expand: >-
This is <strong>NOT</strong> an idle game! Build more extractors and belts to finish the goal quicker.<br><br>Tip: Hold <strong>SHIFT</strong> to place multiple extractors, and use <strong>R</strong> to rotate them.
# Connected miners
connectedMiners:
one_miner: 1 Miner
n_miners: <amount> Miners
limited_items: Limited to <max_throughput>
# All shop upgrades
shopUpgrades:
belt: