Add multiple performance settings

This commit is contained in:
tobspr 2020-08-14 09:38:48 +02:00
parent 9e76606674
commit 9085f32ec3
16 changed files with 241 additions and 255 deletions

View File

@ -12,6 +12,7 @@ export const CHANGELOG = [
"Fix rare crash regarding the buildings toolbar (by isaisstillalive)", "Fix rare crash regarding the buildings toolbar (by isaisstillalive)",
"Fixed some phrases (by EnderDoom77)", "Fixed some phrases (by EnderDoom77)",
"Zoom towards mouse cursor (by Dimava)", "Zoom towards mouse cursor (by Dimava)",
"Added multiple settings to optimize the performance",
"Updated the soundtrack again, it is now 40 minutes in total!", "Updated the soundtrack again, it is now 40 minutes in total!",
"Updated and added new translations (Thanks to all contributors!)", "Updated and added new translations (Thanks to all contributors!)",
"Allow editing waypoints (by isaisstillalive)", "Allow editing waypoints (by isaisstillalive)",

View File

@ -46,6 +46,7 @@ export const globalConfig = {
// Map // Map
mapChunkSize: 16, mapChunkSize: 16,
mapChunkOverviewMinZoom: 0.9, mapChunkOverviewMinZoom: 0.9,
mapChunkWorldSize: null, // COMPUTED
// Belt speeds // Belt speeds
// NOTICE: Update webpack.production.config too! // NOTICE: Update webpack.production.config too!
@ -110,6 +111,8 @@ export const IS_MOBILE = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
// Automatic calculations // Automatic calculations
globalConfig.minerSpeedItemsPerSecond = globalConfig.beltSpeedItemsPerSecond / 5; globalConfig.minerSpeedItemsPerSecond = globalConfig.beltSpeedItemsPerSecond / 5;
globalConfig.mapChunkWorldSize = globalConfig.mapChunkSize * globalConfig.tileSize;
// Dynamic calculations // Dynamic calculations
if (globalConfig.debug.disableMapOverview) { if (globalConfig.debug.disableMapOverview) {
globalConfig.mapChunkOverviewMinZoom = 0; globalConfig.mapChunkOverviewMinZoom = 0;

View File

@ -1,8 +1,6 @@
/* typehints:start */ /* typehints:start */
import { InGameState } from "../states/ingame";
import { Application } from "../application"; import { Application } from "../application";
/* typehints:end */ /* typehints:end */
import { BufferMaintainer } from "../core/buffer_maintainer"; import { BufferMaintainer } from "../core/buffer_maintainer";
import { disableImageSmoothing, enableImageSmoothing, registerCanvas } from "../core/buffer_utils"; import { disableImageSmoothing, enableImageSmoothing, registerCanvas } from "../core/buffer_utils";
import { globalConfig } from "../core/config"; import { globalConfig } from "../core/config";
@ -10,12 +8,16 @@ import { getDeviceDPI, resizeHighDPICanvas } from "../core/dpi_manager";
import { DrawParameters } from "../core/draw_parameters"; import { DrawParameters } from "../core/draw_parameters";
import { gMetaBuildingRegistry } from "../core/global_registries"; import { gMetaBuildingRegistry } from "../core/global_registries";
import { createLogger } from "../core/logging"; import { createLogger } from "../core/logging";
import { Rectangle } from "../core/rectangle";
import { randomInt } from "../core/utils";
import { Vector } from "../core/vector"; import { Vector } from "../core/vector";
import { Savegame } from "../savegame/savegame"; import { Savegame } from "../savegame/savegame";
import { SavegameSerializer } from "../savegame/savegame_serializer"; import { SavegameSerializer } from "../savegame/savegame_serializer";
import { InGameState } from "../states/ingame";
import { AutomaticSave } from "./automatic_save"; import { AutomaticSave } from "./automatic_save";
import { MetaHubBuilding } from "./buildings/hub"; import { MetaHubBuilding } from "./buildings/hub";
import { Camera } from "./camera"; import { Camera } from "./camera";
import { DynamicTickrate } from "./dynamic_tickrate";
import { EntityManager } from "./entity_manager"; import { EntityManager } from "./entity_manager";
import { GameSystemManager } from "./game_system_manager"; import { GameSystemManager } from "./game_system_manager";
import { HubGoals } from "./hub_goals"; import { HubGoals } from "./hub_goals";
@ -23,15 +25,12 @@ import { GameHUD } from "./hud/hud";
import { KeyActionMapper } from "./key_action_mapper"; import { KeyActionMapper } from "./key_action_mapper";
import { GameLogic } from "./logic"; import { GameLogic } from "./logic";
import { MapView } from "./map_view"; import { MapView } from "./map_view";
import { GameRoot, enumLayer } from "./root"; import { defaultBuildingVariant } from "./meta_building";
import { ProductionAnalytics } from "./production_analytics";
import { enumLayer, GameRoot } from "./root";
import { ShapeDefinitionManager } from "./shape_definition_manager"; import { ShapeDefinitionManager } from "./shape_definition_manager";
import { SoundProxy } from "./sound_proxy"; import { SoundProxy } from "./sound_proxy";
import { GameTime } from "./time/game_time"; import { GameTime } from "./time/game_time";
import { ProductionAnalytics } from "./production_analytics";
import { randomInt } from "../core/utils";
import { defaultBuildingVariant } from "./meta_building";
import { DynamicTickrate } from "./dynamic_tickrate";
import { Rectangle } from "../core/rectangle";
const logger = createLogger("ingame/core"); const logger = createLogger("ingame/core");
@ -233,10 +232,6 @@ export class GameCore {
tick(deltaMs) { tick(deltaMs) {
const root = this.root; const root = this.root;
if (root.hud.parts.processingOverlay.hasTasks() || root.hud.parts.processingOverlay.isRunning()) {
return true;
}
// Extract current real time // Extract current real time
root.time.updateRealtimeNow(); root.time.updateRealtimeNow();
@ -326,14 +321,6 @@ export class GameCore {
const root = this.root; const root = this.root;
const systems = root.systemMgr.systems; const systems = root.systemMgr.systems;
const taskRunner = root.hud.parts.processingOverlay;
if (taskRunner.hasTasks()) {
if (!taskRunner.isRunning()) {
taskRunner.process();
}
return;
}
this.root.dynamicTickrate.onFrameRendered(); this.root.dynamicTickrate.onFrameRendered();
if (!this.shouldRender()) { if (!this.shouldRender()) {
@ -357,15 +344,16 @@ export class GameCore {
// Compute optimal zoom level and atlas scale // Compute optimal zoom level and atlas scale
const zoomLevel = root.camera.zoomLevel; const zoomLevel = root.camera.zoomLevel;
const lowQuality = root.app.settings.getAllSettings().lowQualityTextures;
const effectiveZoomLevel = const effectiveZoomLevel =
(zoomLevel / globalConfig.assetsDpi) * getDeviceDPI() * globalConfig.assetsSharpness; (zoomLevel / globalConfig.assetsDpi) * getDeviceDPI() * globalConfig.assetsSharpness;
let desiredAtlasScale = "0.1"; let desiredAtlasScale = "0.1";
if (effectiveZoomLevel > 0.75) { if (effectiveZoomLevel > 0.75 && !lowQuality) {
desiredAtlasScale = "1"; desiredAtlasScale = "1";
} else if (effectiveZoomLevel > 0.5) { } else if (effectiveZoomLevel > 0.5 && !lowQuality) {
desiredAtlasScale = "0.75"; desiredAtlasScale = "0.75";
} else if (effectiveZoomLevel > 0.25) { } else if (effectiveZoomLevel > 0.25 && !lowQuality) {
desiredAtlasScale = "0.5"; desiredAtlasScale = "0.5";
} else if (effectiveZoomLevel > 0.1) { } else if (effectiveZoomLevel > 0.1) {
desiredAtlasScale = "0.25"; desiredAtlasScale = "0.25";
@ -380,7 +368,7 @@ export class GameCore {
root: root, root: root,
}); });
if (G_IS_DEV && (globalConfig.debug.testCulling || globalConfig.debug.hideFog)) { if (G_IS_DEV && globalConfig.debug.testCulling) {
context.clearRect(0, 0, root.gameWidth, root.gameHeight); context.clearRect(0, 0, root.gameWidth, root.gameHeight);
} }

View File

@ -8,7 +8,6 @@ import { TrailerMaker } from "./trailer_maker";
import { Signal } from "../../core/signal"; import { Signal } from "../../core/signal";
import { DrawParameters } from "../../core/draw_parameters"; import { DrawParameters } from "../../core/draw_parameters";
import { HUDProcessingOverlay } from "./parts/processing_overlay";
import { HUDBuildingsToolbar } from "./parts/buildings_toolbar"; import { HUDBuildingsToolbar } from "./parts/buildings_toolbar";
import { HUDBuildingPlacer } from "./parts/building_placer"; import { HUDBuildingPlacer } from "./parts/building_placer";
import { HUDBlueprintPlacer } from "./parts/blueprint_placer"; import { HUDBlueprintPlacer } from "./parts/blueprint_placer";
@ -57,7 +56,6 @@ export class GameHUD {
*/ */
initialize() { initialize() {
this.parts = { this.parts = {
processingOverlay: new HUDProcessingOverlay(this.root),
buildingsToolbar: new HUDBuildingsToolbar(this.root), buildingsToolbar: new HUDBuildingsToolbar(this.root),
wiresToolbar: new HUDWiresToolbar(this.root), wiresToolbar: new HUDWiresToolbar(this.root),
blueprintPlacer: new HUDBlueprintPlacer(this.root), blueprintPlacer: new HUDBlueprintPlacer(this.root),

View File

@ -1,112 +0,0 @@
import { DynamicDomAttach } from "../dynamic_dom_attach";
import { BaseHUDPart } from "../base_hud_part";
import { makeDiv } from "../../../core/utils";
import { Signal } from "../../../core/signal";
import { InputReceiver } from "../../../core/input_receiver";
import { createLogger } from "../../../core/logging";
const logger = createLogger("hud/processing_overlay");
export class HUDProcessingOverlay extends BaseHUDPart {
constructor(root) {
super(root);
this.tasks = [];
this.computeTimeout = null;
this.root.signals.performAsync.add(this.queueTask, this);
this.allTasksFinished = new Signal();
this.inputReceiver = new InputReceiver("processing-overlay");
this.root.signals.aboutToDestruct.add(() =>
this.root.app.inputMgr.destroyReceiver(this.inputReceiver)
);
}
createElements(parent) {
this.element = makeDiv(
parent,
"rg_HUD_ProcessingOverlay",
["hudElement"],
`
<span class="prefab_LoadingTextWithAnim">
Computing
</span>
`
);
}
initialize() {
this.domWatcher = new DynamicDomAttach(this.root, this.element, {
timeToKeepSeconds: 0,
});
}
queueTask(task, name) {
if (!this.root.gameInitialized) {
// Tasks before the game started can be done directlry
task();
return;
}
task.__name = name;
this.tasks.push(task);
}
hasTasks() {
return this.tasks.length > 0;
}
isRunning() {
return this.computeTimeout !== null;
}
processSync() {
const now = performance.now();
while (this.tasks.length > 0) {
const workload = this.tasks[0];
workload.call();
this.tasks.shift();
}
const duration = performance.now() - now;
if (duration > 100) {
logger.log("Tasks done slow (SYNC!) within", (performance.now() - now).toFixed(2), "ms");
}
}
process() {
this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReceiver);
this.domWatcher.update(true);
if (this.tasks.length === 0) {
logger.warn("No tasks but still called process");
return;
}
if (this.computeTimeout) {
assert(false, "Double compute queued");
clearTimeout(this.computeTimeout);
}
this.computeTimeout = setTimeout(() => {
const now = performance.now();
while (this.tasks.length > 0) {
const workload = this.tasks[0];
workload.call();
this.tasks.shift();
}
const duration = performance.now() - now;
if (duration > 100) {
logger.log("Tasks done slow within", (performance.now() - now).toFixed(2), "ms");
}
this.domWatcher.update(false);
this.root.app.inputMgr.makeSureDetached(this.inputReceiver);
clearTimeout(this.computeTimeout);
this.computeTimeout = null;
this.allTasksFinished.dispatch();
});
}
}

View File

@ -63,7 +63,7 @@ export class HUDScreenshotExporter extends BaseHUDPart {
} }
logger.log("ChunkSizePixels:", chunkSizePixels); logger.log("ChunkSizePixels:", chunkSizePixels);
const chunkScale = chunkSizePixels / (globalConfig.mapChunkSize * globalConfig.tileSize); const chunkScale = chunkSizePixels / globalConfig.mapChunkWorldSize;
logger.log("Scale:", chunkScale); logger.log("Scale:", chunkScale);
logger.log("Allocating buffer, if the factory grew too big it will crash here"); logger.log("Allocating buffer, if the factory grew too big it will crash here");
@ -79,10 +79,10 @@ export class HUDScreenshotExporter extends BaseHUDPart {
logger.log("Got buffer, rendering now ..."); logger.log("Got buffer, rendering now ...");
const visibleRect = new Rectangle( const visibleRect = new Rectangle(
minChunk.x * globalConfig.mapChunkSize * globalConfig.tileSize, minChunk.x * globalConfig.mapChunkWorldSize,
minChunk.y * globalConfig.mapChunkSize * globalConfig.tileSize, minChunk.y * globalConfig.mapChunkWorldSize,
dimensions.x * globalConfig.mapChunkSize * globalConfig.tileSize, dimensions.x * globalConfig.mapChunkWorldSize,
dimensions.y * globalConfig.mapChunkSize * globalConfig.tileSize dimensions.y * globalConfig.mapChunkWorldSize
); );
const parameters = new DrawParameters({ const parameters = new DrawParameters({
context, context,

View File

@ -1,7 +1,12 @@
import { GameRoot, enumLayer } from "./root"; import { GameRoot, enumLayer } from "./root";
import { globalConfig } from "../core/config"; import { globalConfig } from "../core/config";
import { createLogger } from "../core/logging"; import { createLogger } from "../core/logging";
import { clamp, fastArrayDeleteValueIfContained, make2DUndefinedArray } from "../core/utils"; import {
clamp,
fastArrayDeleteValueIfContained,
make2DUndefinedArray,
fastArrayDeleteValue,
} from "../core/utils";
import { Vector } from "../core/vector"; import { Vector } from "../core/vector";
import { BaseItem } from "./base_item"; import { BaseItem } from "./base_item";
import { enumColors } from "./colors"; import { enumColors } from "./colors";
@ -39,6 +44,15 @@ export class MapChunk {
/** @type {Array<Entity>} */ /** @type {Array<Entity>} */
this.containedEntities = []; this.containedEntities = [];
/**
* Which entities this chunk contains, sorted by layer
* @type {Object<enumLayer, Array<Entity>>}
*/
this.containedEntitiesByLayer = {
[enumLayer.regular]: [],
[enumLayer.wires]: [],
};
/** /**
* Store which patches we have so we can render them in the overview * Store which patches we have so we can render them in the overview
* @type {Array<{pos: Vector, item: BaseItem, size: number }>} * @type {Array<{pos: Vector, item: BaseItem, size: number }>}
@ -403,8 +417,9 @@ export class MapChunk {
assert(contents === null || !oldContents, "Tile already used: " + tileX + " / " + tileY); assert(contents === null || !oldContents, "Tile already used: " + tileX + " / " + tileY);
if (oldContents) { if (oldContents) {
// Remove from list // Remove from list (the old contents must be reigstered)
fastArrayDeleteValueIfContained(this.containedEntities, oldContents); fastArrayDeleteValueIfContained(this.containedEntities, oldContents);
fastArrayDeleteValueIfContained(this.containedEntitiesByLayer[layer], oldContents);
} }
if (layer === enumLayer.regular) { if (layer === enumLayer.regular) {
@ -417,6 +432,10 @@ export class MapChunk {
if (this.containedEntities.indexOf(contents) < 0) { if (this.containedEntities.indexOf(contents) < 0) {
this.containedEntities.push(contents); this.containedEntities.push(contents);
} }
if (this.containedEntitiesByLayer[layer].indexOf(contents) < 0) {
this.containedEntitiesByLayer[layer].push(contents);
}
} }
} }
} }

View File

@ -69,13 +69,14 @@ export class MapChunkView extends MapChunk {
redrawMethod: this.generateOverlayBuffer.bind(this), redrawMethod: this.generateOverlayBuffer.bind(this),
}); });
const dims = globalConfig.mapChunkSize * globalConfig.tileSize; const dims = globalConfig.mapChunkWorldSize;
// Draw chunk "pixel" art
parameters.context.imageSmoothingEnabled = false; parameters.context.imageSmoothingEnabled = false;
parameters.context.drawImage(sprite, this.x * dims, this.y * dims, dims, dims); parameters.context.drawImage(sprite, this.x * dims, this.y * dims, dims, dims);
parameters.context.imageSmoothingEnabled = true; parameters.context.imageSmoothingEnabled = true;
// Draw patch items
if (this.root.currentLayer === enumLayer.regular) { if (this.root.currentLayer === enumLayer.regular) {
for (let i = 0; i < this.patches.length; ++i) { for (let i = 0; i < this.patches.length; ++i) {
const patch = this.patches[i]; const patch = this.patches[i];

View File

@ -195,17 +195,19 @@ export class MapView extends BaseMap {
); );
} }
const dpi = this.backgroundCacheDPI; if (!this.root.app.settings.getAllSettings().disableTileGrid) {
parameters.context.scale(1 / dpi, 1 / dpi); const dpi = this.backgroundCacheDPI;
parameters.context.scale(1 / dpi, 1 / dpi);
parameters.context.fillStyle = this.cachedBackgroundPattern; parameters.context.fillStyle = this.cachedBackgroundPattern;
parameters.context.fillRect( parameters.context.fillRect(
parameters.visibleRect.x * dpi, parameters.visibleRect.x * dpi,
parameters.visibleRect.y * dpi, parameters.visibleRect.y * dpi,
parameters.visibleRect.w * dpi, parameters.visibleRect.w * dpi,
parameters.visibleRect.h * dpi parameters.visibleRect.h * dpi
); );
parameters.context.scale(dpi, dpi); parameters.context.scale(dpi, dpi);
}
this.drawVisibleChunks(parameters, MapChunkView.prototype.drawBackgroundLayer); this.drawVisibleChunks(parameters, MapChunkView.prototype.drawBackgroundLayer);
@ -233,16 +235,16 @@ export class MapView extends BaseMap {
for (let chunkY = chunkStartY; chunkY <= chunkEndY; ++chunkY) { for (let chunkY = chunkStartY; chunkY <= chunkEndY; ++chunkY) {
parameters.context.fillStyle = "#ffaaaa"; parameters.context.fillStyle = "#ffaaaa";
parameters.context.fillRect( parameters.context.fillRect(
chunkX * globalConfig.mapChunkSize * globalConfig.tileSize, chunkX * globalConfig.mapChunkWorldSize,
chunkY * globalConfig.mapChunkSize * globalConfig.tileSize, chunkY * globalConfig.mapChunkWorldSize,
globalConfig.mapChunkSize * globalConfig.tileSize, globalConfig.mapChunkWorldSize,
3 3
); );
parameters.context.fillRect( parameters.context.fillRect(
chunkX * globalConfig.mapChunkSize * globalConfig.tileSize, chunkX * globalConfig.mapChunkWorldSize,
chunkY * globalConfig.mapChunkSize * globalConfig.tileSize, chunkY * globalConfig.mapChunkWorldSize,
3, 3,
globalConfig.mapChunkSize * globalConfig.tileSize globalConfig.mapChunkWorldSize
); );
} }
} }

View File

@ -162,9 +162,6 @@ export class GameRoot {
// Called right after game is initialized // Called right after game is initialized
postLoadHook: /** @type {TypedSignal<[]>} */ (new Signal()), postLoadHook: /** @type {TypedSignal<[]>} */ (new Signal()),
// Can be used to trigger an async task
performAsync: /** @type {TypedSignal<[function]>} */ (new Signal()),
shapeDelivered: /** @type {TypedSignal<[ShapeDefinition]>} */ (new Signal()), shapeDelivered: /** @type {TypedSignal<[ShapeDefinition]>} */ (new Signal()),
itemProduced: /** @type {TypedSignal<[BaseItem]>} */ (new Signal()), itemProduced: /** @type {TypedSignal<[BaseItem]>} */ (new Signal()),

View File

@ -4,6 +4,7 @@ import { BOOL_TRUE_SINGLETON, BOOL_FALSE_SINGLETON } from "../items/boolean_item
import { MapChunkView } from "../map_chunk_view"; import { MapChunkView } from "../map_chunk_view";
import { globalConfig } from "../../core/config"; import { globalConfig } from "../../core/config";
import { Loader } from "../../core/loader"; import { Loader } from "../../core/loader";
import { enumLayer } from "../root";
export class LeverSystem extends GameSystemWithFilter { export class LeverSystem extends GameSystemWithFilter {
constructor(root) { constructor(root) {
@ -31,23 +32,19 @@ export class LeverSystem extends GameSystemWithFilter {
* @param {MapChunkView} chunk * @param {MapChunkView} chunk
*/ */
drawChunk(parameters, chunk) { drawChunk(parameters, chunk) {
const contents = chunk.contents; const contents = chunk.containedEntitiesByLayer[enumLayer.regular];
for (let y = 0; y < globalConfig.mapChunkSize; ++y) { for (let i = 0; i < contents.length; ++i) {
for (let x = 0; x < globalConfig.mapChunkSize; ++x) { const entity = contents[i];
const entity = contents[x][y]; if (entity && entity.components.Lever) {
const sprite = entity.components.Lever.toggled ? this.spriteOn : this.spriteOff;
if (entity && entity.components.Lever) { const origin = entity.components.StaticMapEntity.origin;
const sprite = entity.components.Lever.toggled ? this.spriteOn : this.spriteOff; sprite.drawCached(
parameters,
const origin = entity.components.StaticMapEntity.origin; origin.x * globalConfig.tileSize,
sprite.drawCached( origin.y * globalConfig.tileSize,
parameters, globalConfig.tileSize,
origin.x * globalConfig.tileSize, globalConfig.tileSize
origin.y * globalConfig.tileSize, );
globalConfig.tileSize,
globalConfig.tileSize
);
}
} }
} }
} }

View File

@ -1,7 +1,8 @@
import { GameSystem } from "../game_system";
import { DrawParameters } from "../../core/draw_parameters";
import { globalConfig } from "../../core/config"; import { globalConfig } from "../../core/config";
import { DrawParameters } from "../../core/draw_parameters";
import { GameSystem } from "../game_system";
import { MapChunkView } from "../map_chunk_view"; import { MapChunkView } from "../map_chunk_view";
import { THEME } from "../theme";
export class MapResourcesSystem extends GameSystem { export class MapResourcesSystem extends GameSystem {
/** /**
@ -10,39 +11,106 @@ export class MapResourcesSystem extends GameSystem {
* @param {MapChunkView} chunk * @param {MapChunkView} chunk
*/ */
drawChunk(parameters, chunk) { drawChunk(parameters, chunk) {
const basicChunkBackground = this.root.buffers.getForKey({
key: "chunkres",
subKey: chunk.renderKey,
w: globalConfig.mapChunkSize,
h: globalConfig.mapChunkSize,
dpi: 1,
redrawMethod: this.generateChunkBackground.bind(this, chunk),
});
parameters.context.imageSmoothingEnabled = false;
parameters.context.drawImage(
basicChunkBackground,
chunk.tileX * globalConfig.tileSize,
chunk.tileY * globalConfig.tileSize,
globalConfig.mapChunkWorldSize,
globalConfig.mapChunkWorldSize
);
parameters.context.imageSmoothingEnabled = true;
parameters.context.globalAlpha = 0.5; parameters.context.globalAlpha = 0.5;
const layer = chunk.lowerLayer; if (this.root.app.settings.getAllSettings().lowQualityMapResources) {
for (let x = 0; x < globalConfig.mapChunkSize; ++x) { // LOW QUALITY: Draw patch items only
const row = layer[x]; for (let i = 0; i < chunk.patches.length; ++i) {
const worldX = (chunk.tileX + x) * globalConfig.tileSize; const patch = chunk.patches[i];
for (let y = 0; y < globalConfig.mapChunkSize; ++y) {
const lowerItem = row[y];
if (lowerItem) {
const worldY = (chunk.tileY + y) * globalConfig.tileSize;
if ( patch.item.draw(
!parameters.visibleRect.containsRect4Params( chunk.x * globalConfig.mapChunkWorldSize + patch.pos.x * globalConfig.tileSize,
worldX, chunk.y * globalConfig.mapChunkWorldSize + patch.pos.y * globalConfig.tileSize,
worldY, parameters,
globalConfig.tileSize, Math.min(80, 40 / parameters.zoomLevel)
globalConfig.tileSize );
) }
) { } else {
// Clipped // HIGH QUALITY: Draw all items
continue; 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;
if (
!parameters.visibleRect.containsRect4Params(
worldX,
worldY,
globalConfig.tileSize,
globalConfig.tileSize
)
) {
// Clipped
continue;
}
// parameters.context.fillStyle = lowerItem.getBackgroundColorAsResource();
// parameters.context.fillRect(worldX, worldY, globalConfig.tileSize, globalConfig.tileSize);
lowerItem.draw(
worldX + globalConfig.halfTileSize,
worldY + globalConfig.halfTileSize,
parameters
);
} }
parameters.context.fillStyle = lowerItem.getBackgroundColorAsResource();
parameters.context.fillRect(worldX, worldY, globalConfig.tileSize, globalConfig.tileSize);
lowerItem.draw(
worldX + globalConfig.halfTileSize,
worldY + globalConfig.halfTileSize,
parameters
);
} }
} }
} }
parameters.context.globalAlpha = 1; 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);
}
}
}
}
} }

View File

@ -153,11 +153,6 @@ export class GameTime extends BasicSerializableObject {
); );
break; break;
} }
// If we queued async tasks, perform them next frame and do not update anymore
if (this.root.hud.parts.processingOverlay.hasTasks()) {
break;
}
} }
} }

View File

@ -19,6 +19,7 @@ const logger = createLogger("application_settings");
export const enumCategories = { export const enumCategories = {
general: "general", general: "general",
userInterface: "userInterface", userInterface: "userInterface",
performance: "performance",
advanced: "advanced", advanced: "advanced",
}; };
@ -147,20 +148,6 @@ export const allApplicationSettings = [
(app, id) => app.updateAfterUiScaleChanged(), (app, id) => app.updateAfterUiScaleChanged(),
}), }),
new BoolSetting(
"fullscreen",
enumCategories.general,
/**
* @param {Application} app
*/
(app, value) => {
if (app.platformWrapper.getSupportsFullscreen()) {
app.platformWrapper.setFullscreen(value);
}
},
!IS_DEMO
),
new BoolSetting( new BoolSetting(
"soundsMuted", "soundsMuted",
enumCategories.general, enumCategories.general,
@ -178,6 +165,20 @@ export const allApplicationSettings = [
(app, value) => app.sound.setMusicMuted(value) (app, value) => app.sound.setMusicMuted(value)
), ),
new BoolSetting(
"fullscreen",
enumCategories.general,
/**
* @param {Application} app
*/
(app, value) => {
if (app.platformWrapper.getSupportsFullscreen()) {
app.platformWrapper.setFullscreen(value);
}
},
!IS_DEMO
),
new BoolSetting( new BoolSetting(
"enableColorBlindHelper", "enableColorBlindHelper",
enumCategories.general, enumCategories.general,
@ -187,7 +188,6 @@ export const allApplicationSettings = [
(app, value) => null (app, value) => null
), ),
// GAME
new BoolSetting("offerHints", enumCategories.userInterface, (app, value) => {}), new BoolSetting("offerHints", enumCategories.userInterface, (app, value) => {}),
new EnumSetting("theme", { new EnumSetting("theme", {
@ -220,16 +220,6 @@ export const allApplicationSettings = [
(app, id) => null, (app, id) => null,
}), }),
new EnumSetting("refreshRate", {
options: ["60", "75", "100", "120", "144", "165", "250", G_IS_DEV ? "10" : "500"],
valueGetter: rate => rate,
textGetter: rate => rate + " Hz",
category: enumCategories.advanced,
restartRequired: false,
changeCb: (app, id) => {},
enabled: !IS_DEMO,
}),
new EnumSetting("scrollWheelSensitivity", { new EnumSetting("scrollWheelSensitivity", {
options: scrollWheelSensitivities.sort((a, b) => a.scale - b.scale), options: scrollWheelSensitivities.sort((a, b) => a.scale - b.scale),
valueGetter: scale => scale.id, valueGetter: scale => scale.id,
@ -258,6 +248,20 @@ export const allApplicationSettings = [
new BoolSetting("compactBuildingInfo", enumCategories.userInterface, (app, value) => {}), new BoolSetting("compactBuildingInfo", enumCategories.userInterface, (app, value) => {}),
new BoolSetting("disableCutDeleteWarnings", enumCategories.advanced, (app, value) => {}), new BoolSetting("disableCutDeleteWarnings", enumCategories.advanced, (app, value) => {}),
new BoolSetting("rotationByBuilding", enumCategories.advanced, (app, value) => {}), new BoolSetting("rotationByBuilding", enumCategories.advanced, (app, value) => {}),
new EnumSetting("refreshRate", {
options: ["60", "75", "100", "120", "144", "165", "250", "500"],
valueGetter: rate => rate,
textGetter: rate => rate + " Hz",
category: enumCategories.performance,
restartRequired: false,
changeCb: (app, id) => {},
enabled: !IS_DEMO,
}),
new BoolSetting("lowQualityMapResources", enumCategories.performance, (app, value) => {}),
new BoolSetting("disableTileGrid", enumCategories.performance, (app, value) => {}),
new BoolSetting("lowQualityTextures", enumCategories.performance, (app, value) => {}),
]; ];
export function getApplicationSettingById(id) { export function getApplicationSettingById(id) {
@ -288,6 +292,10 @@ class SettingsStorage {
this.enableColorBlindHelper = false; this.enableColorBlindHelper = false;
this.lowQualityMapResources = false;
this.disableTileGrid = false;
this.lowQualityTextures = false;
/** /**
* @type {Object.<string, number>} * @type {Object.<string, number>}
*/ */
@ -487,7 +495,7 @@ export class ApplicationSettings extends ReadWriteProxy {
} }
getCurrentVersion() { getCurrentVersion() {
return 18; return 21;
} }
/** @param {{settings: SettingsStorage, version: number}} data */ /** @param {{settings: SettingsStorage, version: number}} data */
@ -565,6 +573,21 @@ export class ApplicationSettings extends ReadWriteProxy {
data.version = 18; data.version = 18;
} }
if (data.version < 19) {
data.settings.lowQualityMapResources = false;
data.version = 19;
}
if (data.version < 20) {
data.settings.disableTileGrid = false;
data.version = 20;
}
if (data.version < 21) {
data.settings.lowQualityTextures = false;
data.version = 21;
}
return ExplainedResult.good(); return ExplainedResult.good();
} }
} }

View File

@ -381,17 +381,6 @@ export class InGameState extends GameState {
} }
} }
// // Check if we can show an ad
// // DISABLED
// if (this.stage === stages.s10_gameRunning && !this.core.root.hud.parts.processingOverlay.hasTasks()) {
// if (this.app.isRenderable() && this.app.adProvider.getCanShowVideoAd()) {
// this.saveThenGoToState("WatchAdState", {
// nextStateId: "RunningGameState",
// nextStatePayload: this.creationPayload,
// });
// }
// }
if (this.stage === stages.s10_gameRunning) { if (this.stage === stages.s10_gameRunning) {
this.core.tick(dt); this.core.tick(dt);
} }

View File

@ -670,6 +670,7 @@ settings:
general: General general: General
userInterface: User Interface userInterface: User Interface
advanced: Advanced advanced: Advanced
performance: Performance
versionBadges: versionBadges:
dev: Development dev: Development
@ -759,9 +760,9 @@ settings:
light: Light light: Light
refreshRate: refreshRate:
title: Simulation Target title: Tick Rate
description: >- description: >-
If you have a 144hz monitor, change the refresh rate here so the game will properly simulate at higher refresh rates. This might actually decrease the FPS if your computer is too slow. The game will automatically adjust the tickrate to be between this target tickrate and half of it. For example, with a tickrate of 60hz, the game will try to stay at 60hz, and if your computer can't handle it it will go down until it eventually reaches 30hz.
alwaysMultiplace: alwaysMultiplace:
title: Multiplace title: Multiplace
@ -798,6 +799,22 @@ settings:
description: >- description: >-
Disables the warning dialogs brought up when cutting/deleting more than 100 entities. Disables the warning dialogs brought up when cutting/deleting more than 100 entities.
lowQualityMapResources:
title: Low Quality Map Resources
description: >-
Simplifies the rendering of resources on the map when zoomed in to improve performance.
It even looks cleaner, so be sure to try it out!
disableTileGrid:
title: Disable Grid
description: >-
Disabling the tile grid can help with the performance. This also makes the game look cleaner!
lowQualityTextures:
title: Low quality textures (Ugly)
description: >-
Uses low quality textures to save performance. This will make the game look very ugly!
keybindings: keybindings:
title: Keybindings title: Keybindings
hint: >- hint: >-