1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-22 06:23:04 +01:00

Improve graphics plugin API

This commit is contained in:
Ted John
2021-02-02 23:48:46 +00:00
parent 081c1895ce
commit c1b0df5de6
5 changed files with 234 additions and 13 deletions

View File

@@ -70,6 +70,14 @@ declare global {
y: number;
}
/**
* Represents the width and height in pixels.
*/
interface ScreenSize {
width: number;
height: number;
}
/**
* A coordinate within the game.
* Each in-game tile is a size of 32x32.
@@ -2207,19 +2215,40 @@ declare global {
* API for drawing graphics.
*/
interface GraphicsContext {
colour: number;
colour: number | undefined;
secondaryColour: number | undefined;
ternaryColour: number | undefined;
stroke: number;
fill: number;
paletteId: number | undefined;
readonly width: number;
readonly height: number;
getImage(id: number): ImageInfo | undefined;
measureText(text: string): ScreenSize;
clear(): void;
clip(x: number, y: number, width: number, height: number): void;
box(x: number, y: number, width: number, height: number): void;
image(image: number, x: number, y: number): void;
image(id: number, x: number, y: number): void;
line(x1: number, y1: number, x2: number, y2: number): void;
rect(x: number, y: number, width: number, height: number): void;
text(text: string, x: number, y: number): void;
well(x: number, y: number, width: number, height: number): void;
}
interface ImageInfo {
readonly id: number;
readonly offset: ScreenCoordsXY;
readonly width: number;
readonly height: number;
readonly isBMP: boolean;
readonly isRLE: boolean;
readonly isPalette: boolean;
readonly noZoom: boolean;
readonly nextZoomId: number | undefined;
}
/**
* Listens for incoming connections.
* Based on node.js net.Server, see https://nodejs.org/api/net.html for more information.

View File

@@ -22,7 +22,10 @@ namespace OpenRCT2::Scripting
duk_context* _ctx{};
rct_drawpixelinfo _dpi{};
colour_t _colour{};
std::optional<colour_t> _colour{};
std::optional<colour_t> _secondaryColour{};
std::optional<colour_t> _ternaryColour{};
std::optional<uint8_t> _paletteId{};
uint8_t _stroke{};
uint8_t _fill{};
@@ -36,25 +39,80 @@ namespace OpenRCT2::Scripting
static void Register(duk_context* ctx)
{
dukglue_register_property(ctx, &ScGraphicsContext::colour_get, &ScGraphicsContext::colour_set, "colour");
dukglue_register_property(
ctx, &ScGraphicsContext::secondaryColour_get, &ScGraphicsContext::secondaryColour_set, "secondaryColour");
dukglue_register_property(
ctx, &ScGraphicsContext::ternaryColour_get, &ScGraphicsContext::ternaryColour_set, "ternaryColour");
dukglue_register_property(ctx, &ScGraphicsContext::paletteId_get, &ScGraphicsContext::paletteId_set, "paletteId");
dukglue_register_property(ctx, &ScGraphicsContext::fill_get, &ScGraphicsContext::fill_set, "fill");
dukglue_register_property(ctx, &ScGraphicsContext::stroke_get, &ScGraphicsContext::stroke_set, "stroke");
dukglue_register_property(ctx, &ScGraphicsContext::width_get, nullptr, "width");
dukglue_register_property(ctx, &ScGraphicsContext::height_get, nullptr, "height");
dukglue_register_method(ctx, &ScGraphicsContext::getImage, "getImage");
dukglue_register_method(ctx, &ScGraphicsContext::measureText, "measureText");
dukglue_register_method(ctx, &ScGraphicsContext::box, "box");
dukglue_register_method(ctx, &ScGraphicsContext::clear, "clear");
dukglue_register_method(ctx, &ScGraphicsContext::clip, "clip");
dukglue_register_method(ctx, &ScGraphicsContext::image, "image");
dukglue_register_method(ctx, &ScGraphicsContext::line, "line");
dukglue_register_method(ctx, &ScGraphicsContext::rect, "rect");
dukglue_register_method(ctx, &ScGraphicsContext::text, "text");
dukglue_register_method(ctx, &ScGraphicsContext::well, "well");
}
private:
colour_t colour_get() const
DukValue colour_get() const
{
return _colour;
return ToDuk(_ctx, _colour);
}
void colour_set(colour_t value)
void colour_set(DukValue value)
{
_colour = value;
if (value.type() == DukValue::NUMBER)
_colour = static_cast<colour_t>(value.as_int());
else
_colour = {};
}
DukValue secondaryColour_get() const
{
return ToDuk(_ctx, _secondaryColour);
}
void secondaryColour_set(DukValue value)
{
if (value.type() == DukValue::NUMBER)
_secondaryColour = static_cast<colour_t>(value.as_int());
else
_secondaryColour = {};
}
DukValue ternaryColour_get() const
{
return ToDuk(_ctx, _ternaryColour);
}
void ternaryColour_set(DukValue value)
{
if (value.type() == DukValue::NUMBER)
_ternaryColour = static_cast<colour_t>(value.as_int());
else
_ternaryColour = {};
}
DukValue paletteId_get() const
{
return ToDuk(_ctx, _paletteId);
}
void paletteId_set(DukValue value)
{
if (value.type() == DukValue::NUMBER)
_paletteId = static_cast<uint8_t>(value.as_int());
else
_paletteId = {};
}
uint8_t fill_get() const
@@ -77,14 +135,66 @@ namespace OpenRCT2::Scripting
_stroke = value;
}
int32_t width_get() const
{
return _dpi.width;
}
int32_t height_get() const
{
return _dpi.height;
}
DukValue getImage(uint32_t id)
{
auto* g1 = gfx_get_g1_element(id);
if (g1 == nullptr)
{
return ToDuk(_ctx, undefined);
}
else
{
DukObject obj(_ctx);
obj.Set("id", id);
obj.Set("offset", ToDuk<ScreenCoordsXY>(_ctx, { g1->x_offset, g1->y_offset }));
obj.Set("width", g1->width);
obj.Set("height", g1->height);
obj.Set("isBMP", (g1->flags & G1_FLAG_BMP) != 0);
obj.Set("isRLE", (g1->flags & G1_FLAG_RLE_COMPRESSION) != 0);
obj.Set("isPalette", (g1->flags & G1_FLAG_PALETTE) != 0);
obj.Set("noZoom", (g1->flags & G1_FLAG_NO_ZOOM_DRAW) != 0);
if (g1->flags & G1_FLAG_HAS_ZOOM_SPRITE)
{
obj.Set("nextZoomId", id - g1->zoomed_offset);
}
else
{
obj.Set("nextZoomId", undefined);
}
return obj.Take();
}
}
DukValue measureText(const std::string& text)
{
gCurrentFontSpriteBase = FONT_SPRITE_BASE_MEDIUM;
auto width = gfx_get_string_width(text);
auto height = string_get_height_raw(text.c_str());
return ToDuk<ScreenSize>(_ctx, { width, height });
}
void box(int32_t x, int32_t y, int32_t width, int32_t height)
{
gfx_fill_rect_inset(&_dpi, { x, y, x + width - 1, y + height - 1 }, _colour, 0);
gfx_fill_rect_inset(&_dpi, { x, y, x + width - 1, y + height - 1 }, _colour.value_or(0), 0);
}
void well(int32_t x, int32_t y, int32_t width, int32_t height)
{
gfx_fill_rect_inset(&_dpi, { x, y, x + width - 1, y + height - 1 }, _colour, INSET_RECT_FLAG_BORDER_INSET | INSET_RECT_FLAG_FILL_DONT_LIGHTEN);
gfx_fill_rect_inset(
&_dpi, { x, y, x + width - 1, y + height - 1 }, _colour.value_or(0),
INSET_RECT_FLAG_BORDER_INSET | INSET_RECT_FLAG_FILL_DONT_LIGHTEN);
}
void clear()
@@ -99,6 +209,28 @@ namespace OpenRCT2::Scripting
_dpi = newDpi;
}
void image(uint32_t id, int32_t x, int32_t y)
{
ImageId img;
img = img.WithIndex(id);
if (_paletteId)
{
img = img.WithRemap(*_paletteId);
}
else
{
if (_colour)
{
img = img.WithPrimary(*_colour);
}
if (_secondaryColour)
{
img = img.WithSecondary(*_secondaryColour);
}
}
gfx_draw_sprite(&_dpi, static_cast<int32_t>(img.ToUInt32()), { x, y }, _ternaryColour.value_or(0));
}
void line(int32_t x1, int32_t y1, int32_t x2, int32_t y2)
{
gfx_draw_line(&_dpi, { { x1, y1 }, { x2, y2 } }, _stroke);
@@ -106,7 +238,28 @@ namespace OpenRCT2::Scripting
void rect(int32_t x, int32_t y, int32_t width, int32_t height)
{
gfx_fill_rect(&_dpi, { x, y, x + width - 1, y + height - 1 }, _fill);
if (_stroke != 0)
{
line(x, y, x + width, y);
line(x + width - 1, y + 1, x + width - 1, y + height - 1);
line(x, y + height - 1, x + width, y + height - 1);
line(x, y + 1, x, y + height - 1);
x++;
y++;
width -= 2;
height -= 2;
}
if (_fill != 0)
{
gfx_fill_rect(&_dpi, { x, y, x + width - 1, y + height - 1 }, _fill);
}
}
void text(const std::string& text, int32_t x, int32_t y)
{
gCurrentFontSpriteBase = FONT_SPRITE_BASE_MEDIUM;
gfx_draw_string(&_dpi, text.c_str(), _colour.value_or(0), { x, y });
}
};
} // namespace OpenRCT2::Scripting

View File

@@ -356,7 +356,7 @@ void draw_string_centred_raw(rct_drawpixelinfo* dpi, const ScreenCoordsXY& coord
}
}
int32_t string_get_height_raw(char* buffer)
int32_t string_get_height_raw(std::string_view text)
{
uint16_t fontBase = gCurrentFontSpriteBase;
@@ -366,7 +366,7 @@ int32_t string_get_height_raw(char* buffer)
else if (fontBase == FONT_SPRITE_BASE_TINY)
height += 6;
FmtString fmt(buffer);
FmtString fmt(text);
for (const auto& token : fmt)
{
switch (token.kind)

View File

@@ -459,6 +459,13 @@ public:
return result;
}
constexpr ImageId WithRemap(uint8_t paletteId)
{
ImageId result = *this;
result._value = (_value & ~MASK_REMAP) | ((paletteId << SHIFT_REMAP) & MASK_REMAP) | FLAG_PRIMARY;
return result;
}
constexpr ImageId WithPrimary(colour_t colour)
{
ImageId result = *this;
@@ -757,7 +764,7 @@ int32_t gfx_wrap_string(char* buffer, int32_t width, int32_t* num_lines, int32_t
int32_t gfx_get_string_width(std::string_view text);
int32_t gfx_get_string_width_new_lined(std::string_view text);
int32_t gfx_get_string_width_no_formatting(std::string_view text);
int32_t string_get_height_raw(char* buffer);
int32_t string_get_height_raw(std::string_view text);
int32_t gfx_clip_string(char* buffer, int32_t width);
void shorten_path(utf8* buffer, size_t bufferSize, const utf8* path, int32_t availableWidth);
void ttf_draw_string(

View File

@@ -50,6 +50,11 @@ namespace OpenRCT2::Scripting
return value.type() == DukValue::BOOLEAN ? value.as_bool() : defaultValue;
}
enum class DukUndefined
{
};
constexpr DukUndefined undefined{};
/**
* Allows creation of an object on the duktape stack and setting properties on it before
* retrieving the DukValue instance of it.
@@ -88,6 +93,13 @@ namespace OpenRCT2::Scripting
duk_put_prop_string(_ctx, _idx, name);
}
void Set(const char* name, DukUndefined)
{
EnsureObjectPushed();
duk_push_undefined(_ctx);
duk_put_prop_string(_ctx, _idx, name);
}
void Set(const char* name, bool value)
{
EnsureObjectPushed();
@@ -277,12 +289,24 @@ namespace OpenRCT2::Scripting
return DukValue::take_from_stack(ctx);
}
template<> inline DukValue ToDuk(duk_context* ctx, const DukUndefined&)
{
duk_push_undefined(ctx);
return DukValue::take_from_stack(ctx);
}
template<> inline DukValue ToDuk(duk_context* ctx, const bool& value)
{
duk_push_boolean(ctx, value);
return DukValue::take_from_stack(ctx);
}
template<> inline DukValue ToDuk(duk_context* ctx, const uint8_t& value)
{
duk_push_int(ctx, value);
return DukValue::take_from_stack(ctx);
}
template<> inline DukValue ToDuk(duk_context* ctx, const int32_t& value)
{
duk_push_int(ctx, value);
@@ -401,6 +425,14 @@ namespace OpenRCT2::Scripting
return result;
}
template<> inline DukValue ToDuk(duk_context* ctx, const ScreenSize& value)
{
DukObject dukCoords(ctx);
dukCoords.Set("width", value.width);
dukCoords.Set("height", value.height);
return dukCoords.Take();
}
} // namespace OpenRCT2::Scripting
#endif