Allow exporting whole bases, closes #137

This commit is contained in:
tobspr 2020-06-13 10:57:29 +02:00
parent fca216b3c5
commit 7e745fd0ce
9 changed files with 133 additions and 16 deletions

View File

@ -10,6 +10,7 @@ import {
Math_atan2,
Math_sin,
Math_cos,
Math_ceil,
} from "./builtins";
const tileSize = globalConfig.tileSize;
@ -303,13 +304,21 @@ export class Vector {
}
/**
* Computes componentwise floor and return a new vector
* Computes componentwise floor and returns a new vector
* @returns {Vector}
*/
floor() {
return new Vector(Math_floor(this.x), Math_floor(this.y));
}
/**
* Computes componentwise ceil and returns a new vector
* @returns {Vector}
*/
ceil() {
return new Vector(Math_ceil(this.x), Math_ceil(this.y));
}
/**
* Computes componentwise round and return a new vector
* @returns {Vector}

View File

@ -409,7 +409,7 @@ export class GameCore {
}
if (G_IS_DEV) {
root.map.drawStaticEntities(params);
root.map.drawStaticEntityDebugOverlays(params);
}
// END OF GAME CONTENT

View File

@ -136,7 +136,7 @@ export class Entity extends BasicSerializableObject {
* Draws the entity, to override use @see Entity.drawImpl
* @param {DrawParameters} parameters
*/
draw(parameters) {
drawDebugOverlays(parameters) {
const context = parameters.context;
const staticComp = this.components.StaticMapEntity;

View File

@ -2,6 +2,10 @@
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 { HUDProcessingOverlay } from "./parts/processing_overlay";
@ -29,10 +33,7 @@ import { HUDModalDialogs } from "./parts/modal_dialogs";
import { HUDPartTutorialHints } from "./parts/tutorial_hints";
import { HUDWaypoints } from "./parts/waypoints";
import { HUDInteractiveTutorial } from "./parts/interactive_tutorial";
/* dev:start */
import { TrailerMaker } from "./trailer_maker";
/* dev:end */
import { HUDScreenshotExporter } from "./parts/screenshot_exporter";
export class GameHUD {
/**
@ -66,6 +67,7 @@ export class GameHUD {
// betaOverlay: new HUDBetaOverlay(this.root),
debugInfo: new HUDDebugInfo(this.root),
dialogs: new HUDModalDialogs(this.root),
screenshotExporter: new HUDScreenshotExporter(this.root),
};
this.signals = {

View File

@ -57,12 +57,6 @@ export class HUDKeybindingOverlay extends BaseHUDPart {
<label>${T.ingame.keybindingsOverlay.selectBuildings}</label>
</div>
<div class="binding noPlacementOnly">
<code class="keybinding">${getKeycode(KEYMAPPINGS.massSelect.pasteLastBlueprint)}</code>
<label>${T.ingame.keybindingsOverlay.pasteLastBlueprint}</label>
</div>
<div class="binding placementOnly">
<code class="keybinding leftMouse"></code>
<label>${T.ingame.keybindingsOverlay.placeBuilding}</label>

View File

@ -0,0 +1,105 @@
import { BaseHUDPart } from "../base_hud_part";
import { KEYMAPPINGS } from "../../key_action_mapper";
import { IS_DEMO, globalConfig } from "../../../core/config";
import { T } from "../../../translations";
import { createLogger } from "../../../core/logging";
import { StaticMapEntityComponent } from "../../components/static_map_entity";
import { Vector } from "../../../core/vector";
import { Math_max, Math_min } from "../../../core/builtins";
import { makeOffscreenBuffer } from "../../../core/buffer_utils";
import { DrawParameters } from "../../../core/draw_parameters";
import { Rectangle } from "../../../core/rectangle";
const logger = createLogger("screenshot_exporter");
export class HUDScreenshotExporter extends BaseHUDPart {
createElements() {}
initialize() {
this.root.keyMapper.getBinding(KEYMAPPINGS.ingame.exportScreenshot).add(this.startExport, this);
}
startExport() {
if (IS_DEMO) {
this.root.hud.parts.dialogs.showFeatureRestrictionInfo(T.demo.features.exportingBase);
return;
}
const { ok } = this.root.hud.parts.dialogs.showInfo(
T.dialogs.exportScreenshotWarning.title,
T.dialogs.exportScreenshotWarning.desc,
["cancel:good", "ok:bad"]
);
ok.add(this.doExport, this);
}
doExport() {
logger.log("Starting export ...");
// Find extends
const staticEntities = this.root.entityMgr.getAllWithComponent(StaticMapEntityComponent);
const minTile = new Vector(0, 0);
const maxTile = new Vector(0, 0);
for (let i = 0; i < staticEntities.length; ++i) {
const bounds = staticEntities[i].components.StaticMapEntity.getTileSpaceBounds();
minTile.x = Math_min(minTile.x, bounds.x);
minTile.y = Math_min(minTile.y, bounds.y);
maxTile.x = Math_max(maxTile.x, bounds.x + bounds.w);
maxTile.y = Math_max(maxTile.y, bounds.y + bounds.h);
}
const minChunk = minTile.divideScalar(globalConfig.mapChunkSize).floor();
const maxChunk = maxTile.divideScalar(globalConfig.mapChunkSize).ceil();
const dimensions = maxChunk.sub(minChunk);
logger.log("Dimensions:", dimensions);
const chunkSizePixels = 128;
const chunkScale = chunkSizePixels / (globalConfig.mapChunkSize * globalConfig.tileSize);
logger.log("Scale:", chunkScale);
logger.log("Allocating buffer, if the factory grew too big it will crash here");
const [canvas, context] = makeOffscreenBuffer(
dimensions.x * chunkSizePixels,
dimensions.y * chunkSizePixels,
{
smooth: true,
reusable: false,
label: "export-buffer",
}
);
logger.log("Got buffer, rendering now ...");
const visibleRect = new Rectangle(
minChunk.x * globalConfig.mapChunkSize * globalConfig.tileSize,
minChunk.y * globalConfig.mapChunkSize * globalConfig.tileSize,
dimensions.x * globalConfig.mapChunkSize * globalConfig.tileSize,
dimensions.y * globalConfig.mapChunkSize * globalConfig.tileSize
);
const parameters = new DrawParameters({
context,
visibleRect,
desiredAtlasScale: "1",
root: this.root,
zoomLevel: chunkScale,
});
context.scale(chunkScale, chunkScale);
context.translate(-visibleRect.x, -visibleRect.y);
// Render all relevant chunks
this.root.map.drawBackground(parameters);
this.root.map.drawForeground(parameters);
// Offer export
logger.log("Rendered buffer, exporting ...");
const image = canvas.toDataURL("image/png");
const link = document.createElement("a");
link.download = "base.png";
link.href = image;
link.click();
logger.log("Done!");
}
}

View File

@ -24,7 +24,8 @@ export const KEYMAPPINGS = {
menuOpenStats: { keyCode: key("G") },
toggleHud: { keyCode: 113 }, // F2
toggleFPSInfo: { keyCode: 115 }, // F1
exportScreenshot: { keyCode: 114 }, // F3
toggleFPSInfo: { keyCode: 115 }, // F4
},
navigation: {

View File

@ -64,7 +64,7 @@ export class MapView extends BaseMap {
* Draws all static entities like buildings etc.
* @param {DrawParameters} drawParameters
*/
drawStaticEntities(drawParameters) {
drawStaticEntityDebugOverlays(drawParameters) {
const cullRange = drawParameters.visibleRect.toTileCullRectangle();
const top = cullRange.top();
const right = cullRange.right();
@ -90,7 +90,7 @@ export class MapView extends BaseMap {
if (content) {
let isBorder = x <= left - 1 || x >= right + 1 || y <= top - 1 || y >= bottom + 1;
if (!isBorder) {
content.draw(drawParameters);
content.drawDebugOverlays(drawParameters);
}
}
}

View File

@ -260,6 +260,10 @@ dialogs:
markerDemoLimit:
desc: You can only create two custom markers in the demo. Get the standalone for unlimited markers!
exportScreenshotWarning:
title: Export screenshot
desc: You requested to export your base as a screenshot. Please note that this can be quite slow for a big base and even crash your game!
ingame:
# This is shown in the top left corner and displays useful keybindings in
# every situation
@ -698,6 +702,7 @@ keybindings:
toggleHud: Toggle HUD
toggleFPSInfo: Toggle FPS and Debug Info
exportScreenshot: Export whole Base as Image
belt: *belt
splitter: *splitter
underground_belt: *underground_belt
@ -739,5 +744,6 @@ demo:
importingGames: Importing savegames
oneGameLimit: Limited to one savegame
customizeKeybindings: Customizing Keybindings
exportingBase: Exporting whole Base as Image
settingNotAvailable: Not available in the demo.