mirror of
https://github.com/OpenRCT2/OpenRCT2.git
synced 2025-01-22 18:31:59 -05:00
[Plugin] Add API for taking captures and giant captures of the park
This commit is contained in:
parent
3d3dbde642
commit
45d32bec68
5 changed files with 202 additions and 2 deletions
43
distribution/openrct2.d.ts
vendored
43
distribution/openrct2.d.ts
vendored
|
@ -138,6 +138,13 @@ declare global {
|
|||
*/
|
||||
sharedStorage: Configuration;
|
||||
|
||||
/**
|
||||
* Render the current state of the map and save to disc.
|
||||
* Useful for server administration and timelapse creation.
|
||||
* @param options Options that control the capture and output file.
|
||||
*/
|
||||
captureImage(options: CaptureOptions): void;
|
||||
|
||||
/**
|
||||
* Gets the loaded object at the given index.
|
||||
* @param type The object type.
|
||||
|
@ -214,6 +221,42 @@ declare global {
|
|||
has(key: string): boolean;
|
||||
}
|
||||
|
||||
interface CaptureOptions {
|
||||
/**
|
||||
* A relative filename from the screenshot directory to save the capture as.
|
||||
* By default, the filename will be automatically generated using the system date and time.
|
||||
*/
|
||||
filename?: string;
|
||||
|
||||
/**
|
||||
* Width of the capture in pixels.
|
||||
* Do not set if you would like a giant screenshot.
|
||||
*/
|
||||
width?: number;
|
||||
|
||||
/**
|
||||
* Height of the capture in pixels.
|
||||
* Do not set if you would like a giant screenshot.
|
||||
*/
|
||||
height?: number;
|
||||
|
||||
/**
|
||||
* Map position to centre the view on in map units.
|
||||
* Do not set if you would like a giant screenshot.
|
||||
*/
|
||||
position?: CoordsXY;
|
||||
|
||||
/**
|
||||
* The zoom level, 0 is 1:1, 1 is 2:1, 2 is 4:1 etc.
|
||||
*/
|
||||
zoom: number;
|
||||
|
||||
/**
|
||||
* Rotation of the camera from 0 to 3.
|
||||
*/
|
||||
rotation: number;
|
||||
}
|
||||
|
||||
type ObjectType =
|
||||
"ride" |
|
||||
"small_scenery" |
|
||||
|
|
|
@ -35,7 +35,22 @@
|
|||
# include <filesystem>
|
||||
namespace fs = std::filesystem;
|
||||
#else
|
||||
# ifdef _WIN32
|
||||
# ifndef NOMINMAX
|
||||
# define NOMINMAX
|
||||
# endif
|
||||
# ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# endif
|
||||
# define BITMAP WIN32_BITMAP
|
||||
# endif
|
||||
# include <filesystem.hpp>
|
||||
# ifdef _WIN32
|
||||
# undef CreateDirectory
|
||||
# undef CreateWindow
|
||||
# undef GetMessage
|
||||
# undef BITMAP
|
||||
# endif
|
||||
namespace fs = ghc::filesystem;
|
||||
#endif
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (c) 2014-2019 OpenRCT2 developers
|
||||
* Copyright (c) 2014-2020 OpenRCT2 developers
|
||||
*
|
||||
* For a complete list of all authors, please refer to contributors.md
|
||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
||||
|
@ -758,3 +758,89 @@ int32_t cmdline_for_screenshot(const char** argv, int32_t argc, ScreenshotOption
|
|||
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
static bool IsPathChildOf(fs::path x, const fs::path& parent)
|
||||
{
|
||||
auto xp = x.parent_path();
|
||||
while (xp != x)
|
||||
{
|
||||
if (xp == parent)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
x = xp;
|
||||
xp = x.parent_path();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::string ResolveFilenameForCapture(const fs::path& filename)
|
||||
{
|
||||
if (filename.empty())
|
||||
{
|
||||
// Automatic filename
|
||||
auto path = screenshot_get_next_path();
|
||||
if (!path)
|
||||
{
|
||||
throw std::runtime_error("Unable to generate a filename for capture.");
|
||||
}
|
||||
return *path;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto screenshotDirectory = fs::u8path(screenshot_get_directory());
|
||||
auto screenshotPath = fs::absolute(screenshotDirectory / filename);
|
||||
|
||||
// Check the filename isn't attempting to leave the screenshot directory for security
|
||||
if (!IsPathChildOf(screenshotPath, screenshotDirectory))
|
||||
{
|
||||
throw std::runtime_error("Filename is not a child of the screenshot directory.");
|
||||
}
|
||||
|
||||
auto directory = screenshotPath.parent_path();
|
||||
if (!fs::is_directory(directory))
|
||||
{
|
||||
if (!fs::create_directory(directory, screenshotDirectory))
|
||||
{
|
||||
throw std::runtime_error("Unable to create directory.");
|
||||
}
|
||||
}
|
||||
|
||||
return screenshotPath.string();
|
||||
}
|
||||
}
|
||||
|
||||
void CaptureImage(const CaptureOptions& options)
|
||||
{
|
||||
rct_viewport viewport{};
|
||||
if (options.View)
|
||||
{
|
||||
viewport.width = options.View->Width;
|
||||
viewport.height = options.View->Height;
|
||||
viewport.view_width = viewport.width;
|
||||
viewport.view_height = viewport.height;
|
||||
|
||||
auto z = tile_element_height(options.View->Position);
|
||||
CoordsXYZ coords3d(options.View->Position, z);
|
||||
auto coords2d = translate_3d_to_2d_with_z(options.Rotation, coords3d);
|
||||
viewport.viewPos = { coords2d.x - ((viewport.view_width * options.Zoom) / 2),
|
||||
coords2d.y - ((viewport.view_height * options.Zoom) / 2) };
|
||||
viewport.zoom = options.Zoom;
|
||||
}
|
||||
else
|
||||
{
|
||||
viewport = GetGiantViewport(gMapSize, options.Rotation, options.Zoom);
|
||||
}
|
||||
|
||||
auto backupRotation = gCurrentRotation;
|
||||
gCurrentRotation = options.Rotation;
|
||||
|
||||
auto outputPath = ResolveFilenameForCapture(options.Filename);
|
||||
auto dpi = CreateDPI(viewport);
|
||||
RenderViewport(nullptr, viewport, dpi);
|
||||
auto renderedPalette = screenshot_get_rendered_palette();
|
||||
WriteDpiToFile(outputPath, &dpi, renderedPalette);
|
||||
ReleaseDPI(dpi);
|
||||
|
||||
gCurrentRotation = backupRotation;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (c) 2014-2019 OpenRCT2 developers
|
||||
* Copyright (c) 2014-2020 OpenRCT2 developers
|
||||
*
|
||||
* For a complete list of all authors, please refer to contributors.md
|
||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
||||
|
@ -10,7 +10,11 @@
|
|||
#pragma once
|
||||
|
||||
#include "../common.h"
|
||||
#include "../core/FileSystem.hpp"
|
||||
#include "../world/Location.hpp"
|
||||
#include "ZoomLevel.hpp"
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
struct rct_drawpixelinfo;
|
||||
|
@ -31,6 +35,21 @@ struct ScreenshotOptions
|
|||
bool transparent = false;
|
||||
};
|
||||
|
||||
struct CaptureView
|
||||
{
|
||||
int32_t Width{};
|
||||
int32_t Height{};
|
||||
CoordsXY Position;
|
||||
};
|
||||
|
||||
struct CaptureOptions
|
||||
{
|
||||
fs::path Filename;
|
||||
std::optional<CaptureView> View;
|
||||
ZoomLevel Zoom;
|
||||
uint8_t Rotation{};
|
||||
};
|
||||
|
||||
void screenshot_check();
|
||||
std::string screenshot_dump();
|
||||
std::string screenshot_dump_png(rct_drawpixelinfo* dpi);
|
||||
|
@ -39,3 +58,5 @@ std::string screenshot_dump_png_32bpp(int32_t width, int32_t height, const void*
|
|||
void screenshot_giant();
|
||||
int32_t cmdline_for_screenshot(const char** argv, int32_t argc, ScreenshotOptions* options);
|
||||
int32_t cmdline_for_gfxbench(const char** argv, int32_t argc);
|
||||
|
||||
void CaptureImage(const CaptureOptions& options);
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#ifdef ENABLE_SCRIPTING
|
||||
|
||||
# include "../actions/GameAction.h"
|
||||
# include "../interface/Screenshot.h"
|
||||
# include "../object/ObjectManager.h"
|
||||
# include "../scenario/Scenario.h"
|
||||
# include "Duktape.hpp"
|
||||
|
@ -51,6 +52,39 @@ namespace OpenRCT2::Scripting
|
|||
return std::make_shared<ScConfiguration>(scriptEngine.GetSharedStorage());
|
||||
}
|
||||
|
||||
void captureImage(const DukValue& options)
|
||||
{
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
try
|
||||
{
|
||||
CaptureOptions captureOptions;
|
||||
captureOptions.Filename = fs::u8path(AsOrDefault(options["filename"], ""));
|
||||
captureOptions.Rotation = options["rotation"].as_int() & 3;
|
||||
captureOptions.Zoom = ZoomLevel(options["zoom"].as_int());
|
||||
|
||||
auto dukPosition = options["position"];
|
||||
if (dukPosition.type() == DukValue::Type::OBJECT)
|
||||
{
|
||||
CaptureView view;
|
||||
view.Width = options["width"].as_int();
|
||||
view.Height = options["height"].as_int();
|
||||
view.Position.x = dukPosition["x"].as_int();
|
||||
view.Position.y = dukPosition["y"].as_int();
|
||||
captureOptions.View = view;
|
||||
}
|
||||
|
||||
CaptureImage(captureOptions);
|
||||
}
|
||||
catch (const DukException&)
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, "Invalid options.");
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
duk_error(ctx, DUK_ERR_ERROR, ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
static DukValue CreateScObject(duk_context* ctx, uint8_t type, int32_t index)
|
||||
{
|
||||
switch (type)
|
||||
|
@ -247,6 +281,7 @@ namespace OpenRCT2::Scripting
|
|||
{
|
||||
dukglue_register_property(ctx, &ScContext::configuration_get, nullptr, "configuration");
|
||||
dukglue_register_property(ctx, &ScContext::sharedStorage_get, nullptr, "sharedStorage");
|
||||
dukglue_register_method(ctx, &ScContext::captureImage, "captureImage");
|
||||
dukglue_register_method(ctx, &ScContext::getObject, "getObject");
|
||||
dukglue_register_method(ctx, &ScContext::getAllObjects, "getAllObjects");
|
||||
dukglue_register_method(ctx, &ScContext::getRandom, "getRandom");
|
||||
|
|
Loading…
Reference in a new issue