Add setting to show chunk borders

pull/609/head
tobspr 4 years ago
parent 49ea6fc381
commit bca379ee89

@ -18,6 +18,7 @@ export const CHANGELOG = [
"Updated and added new translations (Thanks to all contributors!)",
"Added setting to be able to delete buildings while placing (inspired by hexy)",
"Mark pinned shapes in statistics dialog and show them first (inspired by davidburhans)",
"Added setting to show chunk borders",
"Quad painters have been reworked! They now are integrated with the wires, and only paint the shape when the value is 1 (inspired by dengr1605)",
"There are now compact 1x1 splitters available to be unlocked!",
"Allow editing waypoints (by isaisstillalive)",

@ -9,7 +9,8 @@ import { DrawParameters } from "../core/draw_parameters";
import { gMetaBuildingRegistry } from "../core/global_registries";
import { createLogger } from "../core/logging";
import { Rectangle } from "../core/rectangle";
import { randomInt, round2Digits, round3Digits } from "../core/utils";
import { ORIGINAL_SPRITE_SCALE } from "../core/sprites";
import { lerp, randomInt, round2Digits } from "../core/utils";
import { Vector } from "../core/vector";
import { Savegame } from "../savegame/savegame";
import { SavegameSerializer } from "../savegame/savegame_serializer";
@ -30,7 +31,6 @@ import { GameRoot } from "./root";
import { ShapeDefinitionManager } from "./shape_definition_manager";
import { SoundProxy } from "./sound_proxy";
import { GameTime } from "./time/game_time";
import { ORIGINAL_SPRITE_SCALE } from "../core/sprites";
const logger = createLogger("ingame/core");
@ -61,6 +61,12 @@ export class GameCore {
// Cached
this.boundInternalTick = this.updateLogic.bind(this);
/**
* Opacity of the overview alpha
* @TODO Doesn't belong here
*/
this.overlayAlpha = 0;
}
/**
@ -385,10 +391,10 @@ export class GameCore {
// Main rendering order
// -----
if (this.root.camera.getIsMapOverlayActive()) {
// Map overview
root.map.drawOverlay(params);
} else {
const desiredOverlayAlpha = this.root.camera.getIsMapOverlayActive() ? 1 : 0;
this.overlayAlpha = lerp(this.overlayAlpha, desiredOverlayAlpha, 0.25);
if (this.overlayAlpha < 0.99) {
// Background (grid, resources, etc)
root.map.drawBackground(params);
@ -410,6 +416,13 @@ export class GameCore {
}
}
if (this.overlayAlpha > 0.01) {
// Map overview
context.globalAlpha = this.overlayAlpha;
root.map.drawOverlay(params);
context.globalAlpha = 1;
}
if (G_IS_DEV) {
root.map.drawStaticEntityDebugOverlays(params);
}

@ -1,96 +1,92 @@
import { makeOffscreenBuffer } from "../../../core/buffer_utils";
import { globalConfig } from "../../../core/config";
import { DrawParameters } from "../../../core/draw_parameters";
import { KEYMAPPINGS } from "../../key_action_mapper";
import { THEME } from "../../theme";
import { BaseHUDPart } from "../base_hud_part";
import { Loader } from "../../../core/loader";
import { lerp } from "../../../core/utils";
const wiresBackgroundDpi = 4;
export class HUDWiresOverlay extends BaseHUDPart {
createElements(parent) {}
initialize() {
// Probably not the best location, but the one which makes most sense
this.root.keyMapper.getBinding(KEYMAPPINGS.ingame.switchLayers).add(this.switchLayers, this);
this.generateTilePattern();
this.currentAlpha = 0.0;
}
/**
* Switches between layers
*/
switchLayers() {
if (this.root.currentLayer === "regular") {
this.root.currentLayer = "wires";
} else {
this.root.currentLayer = "regular";
}
this.root.signals.editModeChanged.dispatch(this.root.currentLayer);
}
/**
* Generates the background pattern for the wires overlay
*/
generateTilePattern() {
const overlayTile = Loader.getSprite("sprites/wires/overlay_tile.png");
const dims = globalConfig.tileSize * wiresBackgroundDpi;
const [canvas, context] = makeOffscreenBuffer(dims, dims, {
smooth: false,
reusable: false,
label: "wires-tile-pattern",
});
context.clearRect(0, 0, dims, dims);
overlayTile.draw(context, 0, 0, dims, dims);
this.tilePatternCanvas = canvas;
}
update() {
const desiredAlpha = this.root.currentLayer === "wires" ? 1.0 : 0.0;
this.currentAlpha = lerp(this.currentAlpha, desiredAlpha, 0.12);
}
/**
*
* @param {DrawParameters} parameters
*/
draw(parameters) {
if (this.currentAlpha < 0.02) {
return;
}
if (this.root.camera.getIsMapOverlayActive()) {
return;
}
if (!this.cachedPatternBackground) {
this.cachedPatternBackground = parameters.context.createPattern(this.tilePatternCanvas, "repeat");
}
const bounds = parameters.visibleRect;
parameters.context.globalAlpha = this.currentAlpha;
const scaleFactor = 1 / wiresBackgroundDpi;
parameters.context.globalCompositeOperation = "overlay";
parameters.context.fillStyle = "rgba(50, 200, 150, 1)";
parameters.context.fillRect(bounds.x, bounds.y, bounds.w, bounds.h);
parameters.context.globalCompositeOperation = "source-over";
parameters.context.scale(scaleFactor, scaleFactor);
parameters.context.fillStyle = this.cachedPatternBackground;
parameters.context.fillRect(
bounds.x / scaleFactor,
bounds.y / scaleFactor,
bounds.w / scaleFactor,
bounds.h / scaleFactor
);
parameters.context.scale(1 / scaleFactor, 1 / scaleFactor);
parameters.context.globalAlpha = 1;
}
}
import { makeOffscreenBuffer } from "../../../core/buffer_utils";
import { globalConfig } from "../../../core/config";
import { DrawParameters } from "../../../core/draw_parameters";
import { KEYMAPPINGS } from "../../key_action_mapper";
import { THEME } from "../../theme";
import { BaseHUDPart } from "../base_hud_part";
import { Loader } from "../../../core/loader";
import { lerp } from "../../../core/utils";
const wiresBackgroundDpi = 4;
export class HUDWiresOverlay extends BaseHUDPart {
createElements(parent) {}
initialize() {
// Probably not the best location, but the one which makes most sense
this.root.keyMapper.getBinding(KEYMAPPINGS.ingame.switchLayers).add(this.switchLayers, this);
this.generateTilePattern();
this.currentAlpha = 0.0;
}
/**
* Switches between layers
*/
switchLayers() {
if (this.root.currentLayer === "regular") {
this.root.currentLayer = "wires";
} else {
this.root.currentLayer = "regular";
}
this.root.signals.editModeChanged.dispatch(this.root.currentLayer);
}
/**
* Generates the background pattern for the wires overlay
*/
generateTilePattern() {
const overlayTile = Loader.getSprite("sprites/wires/overlay_tile.png");
const dims = globalConfig.tileSize * wiresBackgroundDpi;
const [canvas, context] = makeOffscreenBuffer(dims, dims, {
smooth: false,
reusable: false,
label: "wires-tile-pattern",
});
context.clearRect(0, 0, dims, dims);
overlayTile.draw(context, 0, 0, dims, dims);
this.tilePatternCanvas = canvas;
}
update() {
const desiredAlpha = this.root.currentLayer === "wires" ? 1.0 : 0.0;
this.currentAlpha = lerp(this.currentAlpha, desiredAlpha, 0.12);
}
/**
*
* @param {DrawParameters} parameters
*/
draw(parameters) {
if (this.currentAlpha < 0.02) {
return;
}
if (!this.cachedPatternBackground) {
this.cachedPatternBackground = parameters.context.createPattern(this.tilePatternCanvas, "repeat");
}
const bounds = parameters.visibleRect;
parameters.context.globalAlpha = this.currentAlpha;
const scaleFactor = 1 / wiresBackgroundDpi;
parameters.context.globalCompositeOperation = "overlay";
parameters.context.fillStyle = "rgba(50, 200, 150, 1)";
parameters.context.fillRect(bounds.x, bounds.y, bounds.w, bounds.h);
parameters.context.globalCompositeOperation = "source-over";
parameters.context.scale(scaleFactor, scaleFactor);
parameters.context.fillStyle = this.cachedPatternBackground;
parameters.context.fillRect(
bounds.x / scaleFactor,
bounds.y / scaleFactor,
bounds.w / scaleFactor,
bounds.h / scaleFactor
);
parameters.context.scale(1 / scaleFactor, 1 / scaleFactor);
parameters.context.globalAlpha = 1;
}
}

@ -133,6 +133,12 @@ export class MapChunkView extends MapChunk {
: THEME.map.chunkOverview.empty;
context.fillRect(0, 0, w, h);
if (this.root.app.settings.getAllSettings().displayChunkBorders) {
context.fillStyle = THEME.map.chunkBorders;
context.fillRect(0, 0, w, 1);
context.fillRect(0, 1, 1, h);
}
for (let x = 0; x < globalConfig.mapChunkSize; ++x) {
const lowerArray = this.lowerLayer[x];
const upperArray = this.contents[x];

@ -196,6 +196,7 @@ export class MapView extends BaseMap {
);
}
// Render tile grid
if (!this.root.app.settings.getAllSettings().disableTileGrid) {
const dpi = this.backgroundCacheDPI;
parameters.context.scale(1 / dpi, 1 / dpi);

@ -1,107 +1,113 @@
import { globalConfig } from "../../core/config";
import { DrawParameters } from "../../core/draw_parameters";
import { GameSystem } from "../game_system";
import { MapChunkView } from "../map_chunk_view";
import { THEME } from "../theme";
import { drawSpriteClipped } from "../../core/draw_utils";
export class MapResourcesSystem extends GameSystem {
/**
* Draws the map resources
* @param {DrawParameters} parameters
* @param {MapChunkView} chunk
*/
drawChunk(parameters, chunk) {
const basicChunkBackground = this.root.buffers.getForKey({
key: "mapresourcebg",
subKey: chunk.renderKey,
w: globalConfig.mapChunkSize,
h: globalConfig.mapChunkSize,
dpi: 1,
redrawMethod: this.generateChunkBackground.bind(this, chunk),
});
parameters.context.imageSmoothingEnabled = false;
drawSpriteClipped({
parameters,
sprite: basicChunkBackground,
x: chunk.tileX * globalConfig.tileSize,
y: chunk.tileY * globalConfig.tileSize,
w: globalConfig.mapChunkWorldSize,
h: globalConfig.mapChunkWorldSize,
originalW: globalConfig.mapChunkSize,
originalH: globalConfig.mapChunkSize,
});
parameters.context.imageSmoothingEnabled = true;
parameters.context.globalAlpha = 0.5;
if (this.root.app.settings.getAllSettings().lowQualityMapResources) {
// LOW QUALITY: Draw patch items only
for (let i = 0; i < chunk.patches.length; ++i) {
const patch = chunk.patches[i];
const destX = chunk.x * globalConfig.mapChunkWorldSize + patch.pos.x * globalConfig.tileSize;
const destY = chunk.y * globalConfig.mapChunkWorldSize + patch.pos.y * globalConfig.tileSize;
const diameter = Math.min(80, 40 / parameters.zoomLevel);
patch.item.drawItemCenteredClipped(destX, destY, parameters, diameter);
}
} else {
// HIGH QUALITY: Draw all items
const layer = chunk.lowerLayer;
for (let x = 0; x < globalConfig.mapChunkSize; ++x) {
const row = layer[x];
const worldX = (chunk.tileX + x) * globalConfig.tileSize;
for (let y = 0; y < globalConfig.mapChunkSize; ++y) {
const lowerItem = row[y];
if (lowerItem) {
const worldY = (chunk.tileY + y) * globalConfig.tileSize;
const destX = worldX + globalConfig.halfTileSize;
const destY = worldY + globalConfig.halfTileSize;
lowerItem.drawItemCenteredClipped(
destX,
destY,
parameters,
globalConfig.defaultItemDiameter
);
}
}
}
}
parameters.context.globalAlpha = 1;
}
/**
*
* @param {MapChunkView} chunk
* @param {HTMLCanvasElement} canvas
* @param {CanvasRenderingContext2D} context
* @param {number} w
* @param {number} h
* @param {number} dpi
*/
generateChunkBackground(chunk, canvas, context, w, h, dpi) {
if (this.root.app.settings.getAllSettings().disableTileGrid) {
// The map doesn't draw a background, so we have to
context.fillStyle = THEME.map.background;
context.fillRect(0, 0, w, h);
} else {
context.clearRect(0, 0, w, h);
}
context.globalAlpha = 0.5;
const layer = chunk.lowerLayer;
for (let x = 0; x < globalConfig.mapChunkSize; ++x) {
const row = layer[x];
for (let y = 0; y < globalConfig.mapChunkSize; ++y) {
const item = row[y];
if (item) {
context.fillStyle = item.getBackgroundColorAsResource();
context.fillRect(x, y, 1, 1);
}
}
}
}
}
import { globalConfig } from "../../core/config";
import { DrawParameters } from "../../core/draw_parameters";
import { GameSystem } from "../game_system";
import { MapChunkView } from "../map_chunk_view";
import { THEME } from "../theme";
import { drawSpriteClipped } from "../../core/draw_utils";
export class MapResourcesSystem extends GameSystem {
/**
* Draws the map resources
* @param {DrawParameters} parameters
* @param {MapChunkView} chunk
*/
drawChunk(parameters, chunk) {
const basicChunkBackground = this.root.buffers.getForKey({
key: "mapresourcebg",
subKey: chunk.renderKey,
w: globalConfig.mapChunkSize,
h: globalConfig.mapChunkSize,
dpi: 1,
redrawMethod: this.generateChunkBackground.bind(this, chunk),
});
parameters.context.imageSmoothingEnabled = false;
drawSpriteClipped({
parameters,
sprite: basicChunkBackground,
x: chunk.tileX * globalConfig.tileSize,
y: chunk.tileY * globalConfig.tileSize,
w: globalConfig.mapChunkWorldSize,
h: globalConfig.mapChunkWorldSize,
originalW: globalConfig.mapChunkSize,
originalH: globalConfig.mapChunkSize,
});
parameters.context.imageSmoothingEnabled = true;
parameters.context.globalAlpha = 0.5;
if (this.root.app.settings.getAllSettings().lowQualityMapResources) {
// LOW QUALITY: Draw patch items only
for (let i = 0; i < chunk.patches.length; ++i) {
const patch = chunk.patches[i];
const destX = chunk.x * globalConfig.mapChunkWorldSize + patch.pos.x * globalConfig.tileSize;
const destY = chunk.y * globalConfig.mapChunkWorldSize + patch.pos.y * globalConfig.tileSize;
const diameter = Math.min(80, 40 / parameters.zoomLevel);
patch.item.drawItemCenteredClipped(destX, destY, parameters, diameter);
}
} else {
// HIGH QUALITY: Draw all items
const layer = chunk.lowerLayer;
for (let x = 0; x < globalConfig.mapChunkSize; ++x) {
const row = layer[x];
const worldX = (chunk.tileX + x) * globalConfig.tileSize;
for (let y = 0; y < globalConfig.mapChunkSize; ++y) {
const lowerItem = row[y];
if (lowerItem) {
const worldY = (chunk.tileY + y) * globalConfig.tileSize;
const destX = worldX + globalConfig.halfTileSize;
const destY = worldY + globalConfig.halfTileSize;
lowerItem.drawItemCenteredClipped(
destX,
destY,
parameters,
globalConfig.defaultItemDiameter
);
}
}
}
}
parameters.context.globalAlpha = 1;
}
/**
*
* @param {MapChunkView} chunk
* @param {HTMLCanvasElement} canvas
* @param {CanvasRenderingContext2D} context
* @param {number} w
* @param {number} h
* @param {number} dpi
*/
generateChunkBackground(chunk, canvas, context, w, h, dpi) {
if (this.root.app.settings.getAllSettings().disableTileGrid) {
// The map doesn't draw a background, so we have to
context.fillStyle = THEME.map.background;
context.fillRect(0, 0, w, h);
} else {
context.clearRect(0, 0, w, h);
}
context.globalAlpha = 0.5;
const layer = chunk.lowerLayer;
for (let x = 0; x < globalConfig.mapChunkSize; ++x) {
const row = layer[x];
for (let y = 0; y < globalConfig.mapChunkSize; ++y) {
const item = row[y];
if (item) {
context.fillStyle = item.getBackgroundColorAsResource();
context.fillRect(x, y, 1, 1);
}
}
}
if (this.root.app.settings.getAllSettings().displayChunkBorders) {
context.fillStyle = THEME.map.chunkBorders;
context.fillRect(0, 0, w, 1);
context.fillRect(0, 1, 1, h);
}
}
}

@ -1,48 +1,50 @@
{
"uiStyle": "dark",
"map": {
"background": "#2e2f37",
"grid": "rgba(255, 255, 255, 0.02)",
"gridLineWidth": 0.5,
"selectionOverlay": "rgba(74, 163, 223, 0.7)",
"selectionOutline": "rgba(74, 163, 223, 0.5)",
"selectionBackground": "rgba(74, 163, 223, 0.2)",
"directionLock": {
"regular": {
"color": "rgb(74, 237, 134)",
"background": "rgba(74, 237, 134, 0.2)"
},
"wires": {
"color": "rgb(209, 107, 203)",
"background": "rgba(209, 107, 203, 0.2)"
}
},
"colorBlindPickerTile": "rgba(255, 255, 255, 0.5)",
"resources": {
"shape": "#3d3f4a",
"red": "#4a3d3f",
"green": "#3e4a3d",
"blue": "#35384a"
},
"chunkOverview": {
"empty": "#444856",
"filled": "#646b7d"
},
"wires": {
"overlayColor": "rgba(97, 161, 152, 0.75)",
"previewColor": "rgb(97, 161, 152, 0.5)",
"highlightColor": "rgba(0, 0, 255, 0.5)"
}
},
"items": {
"outline": "#111418",
"outlineWidth": 0.75,
"circleBackground": "rgba(20, 30, 40, 0.3)"
}
}
{
"uiStyle": "dark",
"map": {
"background": "#2e2f37",
"grid": "rgba(255, 255, 255, 0.02)",
"gridLineWidth": 0.5,
"selectionOverlay": "rgba(74, 163, 223, 0.7)",
"selectionOutline": "rgba(74, 163, 223, 0.5)",
"selectionBackground": "rgba(74, 163, 223, 0.2)",
"chunkBorders": "rgba(127, 190, 255, 0.04)",
"directionLock": {
"regular": {
"color": "rgb(74, 237, 134)",
"background": "rgba(74, 237, 134, 0.2)"
},
"wires": {
"color": "rgb(209, 107, 203)",
"background": "rgba(209, 107, 203, 0.2)"
}
},
"colorBlindPickerTile": "rgba(255, 255, 255, 0.5)",
"resources": {
"shape": "#3d3f4a",
"red": "#4a3d3f",
"green": "#3e4a3d",
"blue": "#35384a"
},
"chunkOverview": {
"empty": "#444856",
"filled": "#646b7d"
},
"wires": {
"overlayColor": "rgba(97, 161, 152, 0.75)",
"previewColor": "rgb(97, 161, 152, 0.5)",
"highlightColor": "rgba(0, 0, 255, 0.5)"
}
},
"items": {
"outline": "#111418",
"outlineWidth": 0.75,
"circleBackground": "rgba(20, 30, 40, 0.3)"
}
}

@ -1,49 +1,51 @@
{
"uiStyle": "light",
"map": {
"background": "#fff",
"grid": "#fafafa",
"gridLineWidth": 1,
"selectionOverlay": "rgba(74, 163, 223, 0.7)",
"selectionOutline": "rgba(74, 163, 223, 0.5)",
"selectionBackground": "rgba(74, 163, 223, 0.2)",
"directionLock": {
"regular": {
"color": "rgb(74, 237, 134)",
"background": "rgba(74, 237, 134, 0.2)"
},
"wires": {
"color": "rgb(209, 107, 203)",
"background": "rgba(209, 107, 203, 0.2)"
}
},
"colorBlindPickerTile": "rgba(50, 50, 50, 0.4)",
"resources": {
"shape": "#eaebec",
"red": "#ffbfc1",
"green": "#cbffc4",
"blue": "#bfdaff"
},
"chunkOverview": {
"empty": "#a6afbb",
"filled": "#c5ccd6"
},
"wires": {
"overlayColor": "rgba(97, 161, 152, 0.75)",
"previewColor": "rgb(97, 161, 152, 0.4)",
"highlightColor": "rgba(72, 137, 255, 0.8)"
}
},
"items": {
"outline": "#55575a",
"outlineWidth": 0.75,
"circleBackground": "rgba(40, 50, 65, 0.1)"
}
}
{
"uiStyle": "light",
"map": {
"background": "#fff",
"grid": "#fafafa",
"gridLineWidth": 1,
"selectionOverlay": "rgba(74, 163, 223, 0.7)",
"selectionOutline": "rgba(74, 163, 223, 0.5)",
"selectionBackground": "rgba(74, 163, 223, 0.2)",
"chunkBorders": "rgba(0, 30, 50, 0.03)",
"directionLock": {
"regular": {
"color": "rgb(74, 237, 134)",
"background": "rgba(74, 237, 134, 0.2)"
},
"wires": {
"color": "rgb(209, 107, 203)",
"background": "rgba(209, 107, 203, 0.2)"
}
},
"colorBlindPickerTile": "rgba(50, 50, 50, 0.4)",
"resources": {
"shape": "#eaebec",
"red": "#ffbfc1",
"green": "#cbffc4",
"blue": "#bfdaff"
},
"chunkOverview": {
"empty": "#a6afbb",
"filled": "#c5ccd6"
},
"wires": {
"overlayColor": "rgba(97, 161, 152, 0.75)",
"previewColor": "rgb(97, 161, 152, 0.4)",
"highlightColor": "rgba(72, 137, 255, 0.8)"
}
},
"items": {
"outline": "#55575a",
"outlineWidth": 0.75,
"circleBackground": "rgba(40, 50, 65, 0.1)"
}
}

@ -261,6 +261,7 @@ export const allApplicationSettings = [
new BoolSetting("compactBuildingInfo", enumCategories.userInterface, (app, value) => {}),
new BoolSetting("disableCutDeleteWarnings", enumCategories.advanced, (app, value) => {}),
new BoolSetting("rotationByBuilding", enumCategories.advanced, (app, value) => {}),
new BoolSetting("displayChunkBorders", enumCategories.advanced, (app, value) => {}),
new EnumSetting("refreshRate", {
options: refreshRateOptions,
@ -303,6 +304,7 @@ class SettingsStorage {
this.disableCutDeleteWarnings = false;
this.rotationByBuilding = true;
this.clearCursorOnDeleteWhilePlacing = true;
this.displayChunkBorders = false;
this.enableColorBlindHelper = false;
@ -509,7 +511,7 @@ export class ApplicationSettings extends ReadWriteProxy {
}
getCurrentVersion() {
return 22;
return 23;
}
/** @param {{settings: SettingsStorage, version: number}} data */
@ -607,6 +609,11 @@ export class ApplicationSettings extends ReadWriteProxy {
data.version = 22;
}
if (data.version < 23) {
data.settings.displayChunkBorders = false;
data.version = 23;
}
return ExplainedResult.good();
}
}

@ -571,7 +571,7 @@ buildings:
lever:
default:
name: &lever Button
name: &lever Switch
description: Can be toggled to emit 1 / 0
logic_gate:
@ -870,6 +870,11 @@ settings:
description: >-
Uses low quality textures to save performance. This will make the game look very ugly!
displayChunkBorders:
title: Display Chunk Borders
description: >-
The game is divided into chunks of 16x16 tiles, if this setting is enabled the borders of each chunk are displayed.
keybindings:
title: Keybindings
hint: >-

Loading…
Cancel
Save