Allow exporting whole bases, closes #137
This commit is contained in:
parent
fca216b3c5
commit
7e745fd0ce
|
@ -10,6 +10,7 @@ import {
|
||||||
Math_atan2,
|
Math_atan2,
|
||||||
Math_sin,
|
Math_sin,
|
||||||
Math_cos,
|
Math_cos,
|
||||||
|
Math_ceil,
|
||||||
} from "./builtins";
|
} from "./builtins";
|
||||||
|
|
||||||
const tileSize = globalConfig.tileSize;
|
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}
|
* @returns {Vector}
|
||||||
*/
|
*/
|
||||||
floor() {
|
floor() {
|
||||||
return new Vector(Math_floor(this.x), Math_floor(this.y));
|
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
|
* Computes componentwise round and return a new vector
|
||||||
* @returns {Vector}
|
* @returns {Vector}
|
||||||
|
|
|
@ -409,7 +409,7 @@ export class GameCore {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (G_IS_DEV) {
|
if (G_IS_DEV) {
|
||||||
root.map.drawStaticEntities(params);
|
root.map.drawStaticEntityDebugOverlays(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
// END OF GAME CONTENT
|
// END OF GAME CONTENT
|
||||||
|
|
|
@ -136,7 +136,7 @@ export class Entity extends BasicSerializableObject {
|
||||||
* Draws the entity, to override use @see Entity.drawImpl
|
* Draws the entity, to override use @see Entity.drawImpl
|
||||||
* @param {DrawParameters} parameters
|
* @param {DrawParameters} parameters
|
||||||
*/
|
*/
|
||||||
draw(parameters) {
|
drawDebugOverlays(parameters) {
|
||||||
const context = parameters.context;
|
const context = parameters.context;
|
||||||
const staticComp = this.components.StaticMapEntity;
|
const staticComp = this.components.StaticMapEntity;
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
import { GameRoot } from "../root";
|
import { GameRoot } from "../root";
|
||||||
/* typehints:end */
|
/* typehints:end */
|
||||||
|
|
||||||
|
/* dev:start */
|
||||||
|
import { TrailerMaker } from "./trailer_maker";
|
||||||
|
/* dev:end */
|
||||||
|
|
||||||
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 { HUDProcessingOverlay } from "./parts/processing_overlay";
|
||||||
|
@ -29,10 +33,7 @@ import { HUDModalDialogs } from "./parts/modal_dialogs";
|
||||||
import { HUDPartTutorialHints } from "./parts/tutorial_hints";
|
import { HUDPartTutorialHints } from "./parts/tutorial_hints";
|
||||||
import { HUDWaypoints } from "./parts/waypoints";
|
import { HUDWaypoints } from "./parts/waypoints";
|
||||||
import { HUDInteractiveTutorial } from "./parts/interactive_tutorial";
|
import { HUDInteractiveTutorial } from "./parts/interactive_tutorial";
|
||||||
|
import { HUDScreenshotExporter } from "./parts/screenshot_exporter";
|
||||||
/* dev:start */
|
|
||||||
import { TrailerMaker } from "./trailer_maker";
|
|
||||||
/* dev:end */
|
|
||||||
|
|
||||||
export class GameHUD {
|
export class GameHUD {
|
||||||
/**
|
/**
|
||||||
|
@ -66,6 +67,7 @@ export class GameHUD {
|
||||||
// betaOverlay: new HUDBetaOverlay(this.root),
|
// betaOverlay: new HUDBetaOverlay(this.root),
|
||||||
debugInfo: new HUDDebugInfo(this.root),
|
debugInfo: new HUDDebugInfo(this.root),
|
||||||
dialogs: new HUDModalDialogs(this.root),
|
dialogs: new HUDModalDialogs(this.root),
|
||||||
|
screenshotExporter: new HUDScreenshotExporter(this.root),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.signals = {
|
this.signals = {
|
||||||
|
|
|
@ -57,12 +57,6 @@ export class HUDKeybindingOverlay extends BaseHUDPart {
|
||||||
<label>${T.ingame.keybindingsOverlay.selectBuildings}</label>
|
<label>${T.ingame.keybindingsOverlay.selectBuildings}</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="binding noPlacementOnly">
|
|
||||||
<code class="keybinding">${getKeycode(KEYMAPPINGS.massSelect.pasteLastBlueprint)}</code>
|
|
||||||
<label>${T.ingame.keybindingsOverlay.pasteLastBlueprint}</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="binding placementOnly">
|
<div class="binding placementOnly">
|
||||||
<code class="keybinding leftMouse"></code>
|
<code class="keybinding leftMouse"></code>
|
||||||
<label>${T.ingame.keybindingsOverlay.placeBuilding}</label>
|
<label>${T.ingame.keybindingsOverlay.placeBuilding}</label>
|
||||||
|
|
|
@ -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!");
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,7 +24,8 @@ export const KEYMAPPINGS = {
|
||||||
menuOpenStats: { keyCode: key("G") },
|
menuOpenStats: { keyCode: key("G") },
|
||||||
|
|
||||||
toggleHud: { keyCode: 113 }, // F2
|
toggleHud: { keyCode: 113 }, // F2
|
||||||
toggleFPSInfo: { keyCode: 115 }, // F1
|
exportScreenshot: { keyCode: 114 }, // F3
|
||||||
|
toggleFPSInfo: { keyCode: 115 }, // F4
|
||||||
},
|
},
|
||||||
|
|
||||||
navigation: {
|
navigation: {
|
||||||
|
|
|
@ -64,7 +64,7 @@ export class MapView extends BaseMap {
|
||||||
* Draws all static entities like buildings etc.
|
* Draws all static entities like buildings etc.
|
||||||
* @param {DrawParameters} drawParameters
|
* @param {DrawParameters} drawParameters
|
||||||
*/
|
*/
|
||||||
drawStaticEntities(drawParameters) {
|
drawStaticEntityDebugOverlays(drawParameters) {
|
||||||
const cullRange = drawParameters.visibleRect.toTileCullRectangle();
|
const cullRange = drawParameters.visibleRect.toTileCullRectangle();
|
||||||
const top = cullRange.top();
|
const top = cullRange.top();
|
||||||
const right = cullRange.right();
|
const right = cullRange.right();
|
||||||
|
@ -90,7 +90,7 @@ export class MapView extends BaseMap {
|
||||||
if (content) {
|
if (content) {
|
||||||
let isBorder = x <= left - 1 || x >= right + 1 || y <= top - 1 || y >= bottom + 1;
|
let isBorder = x <= left - 1 || x >= right + 1 || y <= top - 1 || y >= bottom + 1;
|
||||||
if (!isBorder) {
|
if (!isBorder) {
|
||||||
content.draw(drawParameters);
|
content.drawDebugOverlays(drawParameters);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -260,6 +260,10 @@ dialogs:
|
||||||
markerDemoLimit:
|
markerDemoLimit:
|
||||||
desc: You can only create two custom markers in the demo. Get the standalone for unlimited markers!
|
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:
|
ingame:
|
||||||
# This is shown in the top left corner and displays useful keybindings in
|
# This is shown in the top left corner and displays useful keybindings in
|
||||||
# every situation
|
# every situation
|
||||||
|
@ -698,6 +702,7 @@ keybindings:
|
||||||
|
|
||||||
toggleHud: Toggle HUD
|
toggleHud: Toggle HUD
|
||||||
toggleFPSInfo: Toggle FPS and Debug Info
|
toggleFPSInfo: Toggle FPS and Debug Info
|
||||||
|
exportScreenshot: Export whole Base as Image
|
||||||
belt: *belt
|
belt: *belt
|
||||||
splitter: *splitter
|
splitter: *splitter
|
||||||
underground_belt: *underground_belt
|
underground_belt: *underground_belt
|
||||||
|
@ -739,5 +744,6 @@ demo:
|
||||||
importingGames: Importing savegames
|
importingGames: Importing savegames
|
||||||
oneGameLimit: Limited to one savegame
|
oneGameLimit: Limited to one savegame
|
||||||
customizeKeybindings: Customizing Keybindings
|
customizeKeybindings: Customizing Keybindings
|
||||||
|
exportingBase: Exporting whole Base as Image
|
||||||
|
|
||||||
settingNotAvailable: Not available in the demo.
|
settingNotAvailable: Not available in the demo.
|
||||||
|
|
Reference in New Issue