1 /*****************************************************************************
2  * Copyright (c) 2014-2020 OpenRCT2 developers
3  *
4  * For a complete list of all authors, please refer to contributors.md
5  * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
6  *
7  * OpenRCT2 is licensed under the GNU General Public License version 3.
8  *****************************************************************************/
9 
10 #pragma once
11 
12 #ifdef ENABLE_SCRIPTING
13 
14 #    include <openrct2/drawing/Drawing.h>
15 #    include <openrct2/scripting/Duktape.hpp>
16 
17 namespace OpenRCT2::Scripting
18 {
19     class ScGraphicsContext
20     {
21     private:
22         duk_context* _ctx{};
23         rct_drawpixelinfo _dpi{};
24 
25         std::optional<colour_t> _colour{};
26         std::optional<colour_t> _secondaryColour{};
27         std::optional<colour_t> _ternaryColour{};
28         std::optional<uint8_t> _paletteId{};
29         uint8_t _stroke{};
30         uint8_t _fill{};
31 
32     public:
ScGraphicsContext(duk_context * ctx,const rct_drawpixelinfo & dpi)33         ScGraphicsContext(duk_context* ctx, const rct_drawpixelinfo& dpi)
34             : _ctx(ctx)
35             , _dpi(dpi)
36         {
37         }
38 
Register(duk_context * ctx)39         static void Register(duk_context* ctx)
40         {
41             dukglue_register_property(ctx, &ScGraphicsContext::colour_get, &ScGraphicsContext::colour_set, "colour");
42             dukglue_register_property(
43                 ctx, &ScGraphicsContext::secondaryColour_get, &ScGraphicsContext::secondaryColour_set, "secondaryColour");
44             dukglue_register_property(
45                 ctx, &ScGraphicsContext::ternaryColour_get, &ScGraphicsContext::ternaryColour_set, "ternaryColour");
46             dukglue_register_property(ctx, &ScGraphicsContext::paletteId_get, &ScGraphicsContext::paletteId_set, "paletteId");
47             dukglue_register_property(ctx, &ScGraphicsContext::fill_get, &ScGraphicsContext::fill_set, "fill");
48             dukglue_register_property(ctx, &ScGraphicsContext::stroke_get, &ScGraphicsContext::stroke_set, "stroke");
49             dukglue_register_property(ctx, &ScGraphicsContext::width_get, nullptr, "width");
50             dukglue_register_property(ctx, &ScGraphicsContext::height_get, nullptr, "height");
51 
52             dukglue_register_method(ctx, &ScGraphicsContext::getImage, "getImage");
53             dukglue_register_method(ctx, &ScGraphicsContext::measureText, "measureText");
54 
55             dukglue_register_method(ctx, &ScGraphicsContext::box, "box");
56             dukglue_register_method(ctx, &ScGraphicsContext::clear, "clear");
57             dukglue_register_method(ctx, &ScGraphicsContext::clip, "clip");
58             dukglue_register_method(ctx, &ScGraphicsContext::image, "image");
59             dukglue_register_method(ctx, &ScGraphicsContext::line, "line");
60             dukglue_register_method(ctx, &ScGraphicsContext::rect, "rect");
61             dukglue_register_method(ctx, &ScGraphicsContext::text, "text");
62             dukglue_register_method(ctx, &ScGraphicsContext::well, "well");
63         }
64 
65     private:
colour_get() const66         DukValue colour_get() const
67         {
68             return ToDuk(_ctx, _colour);
69         }
70 
colour_set(DukValue value)71         void colour_set(DukValue value)
72         {
73             if (value.type() == DukValue::NUMBER)
74                 _colour = static_cast<colour_t>(value.as_int());
75             else
76                 _colour = {};
77         }
78 
secondaryColour_get() const79         DukValue secondaryColour_get() const
80         {
81             return ToDuk(_ctx, _secondaryColour);
82         }
83 
secondaryColour_set(DukValue value)84         void secondaryColour_set(DukValue value)
85         {
86             if (value.type() == DukValue::NUMBER)
87                 _secondaryColour = static_cast<colour_t>(value.as_int());
88             else
89                 _secondaryColour = {};
90         }
91 
ternaryColour_get() const92         DukValue ternaryColour_get() const
93         {
94             return ToDuk(_ctx, _ternaryColour);
95         }
96 
ternaryColour_set(DukValue value)97         void ternaryColour_set(DukValue value)
98         {
99             if (value.type() == DukValue::NUMBER)
100                 _ternaryColour = static_cast<colour_t>(value.as_int());
101             else
102                 _ternaryColour = {};
103         }
104 
paletteId_get() const105         DukValue paletteId_get() const
106         {
107             return ToDuk(_ctx, _paletteId);
108         }
109 
paletteId_set(DukValue value)110         void paletteId_set(DukValue value)
111         {
112             if (value.type() == DukValue::NUMBER)
113                 _paletteId = static_cast<uint8_t>(value.as_int());
114             else
115                 _paletteId = {};
116         }
117 
fill_get() const118         uint8_t fill_get() const
119         {
120             return _fill;
121         }
122 
fill_set(uint8_t value)123         void fill_set(uint8_t value)
124         {
125             _fill = value;
126         }
127 
stroke_get() const128         uint8_t stroke_get() const
129         {
130             return _stroke;
131         }
132 
stroke_set(uint8_t value)133         void stroke_set(uint8_t value)
134         {
135             _stroke = value;
136         }
137 
width_get() const138         int32_t width_get() const
139         {
140             return _dpi.width;
141         }
142 
height_get() const143         int32_t height_get() const
144         {
145             return _dpi.height;
146         }
147 
getImage(uint32_t id)148         DukValue getImage(uint32_t id)
149         {
150             auto* g1 = gfx_get_g1_element(id);
151             if (g1 == nullptr)
152             {
153                 return ToDuk(_ctx, undefined);
154             }
155 
156             DukObject obj(_ctx);
157             obj.Set("id", id);
158             obj.Set("offset", ToDuk<ScreenCoordsXY>(_ctx, { g1->x_offset, g1->y_offset }));
159             obj.Set("width", g1->width);
160             obj.Set("height", g1->height);
161 
162             obj.Set("isBMP", (g1->flags & G1_FLAG_BMP) != 0);
163             obj.Set("isRLE", (g1->flags & G1_FLAG_RLE_COMPRESSION) != 0);
164             obj.Set("isPalette", (g1->flags & G1_FLAG_PALETTE) != 0);
165             obj.Set("noZoom", (g1->flags & G1_FLAG_NO_ZOOM_DRAW) != 0);
166 
167             if (g1->flags & G1_FLAG_HAS_ZOOM_SPRITE)
168             {
169                 obj.Set("nextZoomId", id - g1->zoomed_offset);
170             }
171             else
172             {
173                 obj.Set("nextZoomId", undefined);
174             }
175             return obj.Take();
176         }
177 
measureText(const std::string & text)178         DukValue measureText(const std::string& text)
179         {
180             auto width = gfx_get_string_width(text, FontSpriteBase::MEDIUM);
181             auto height = string_get_height_raw(text.c_str(), FontSpriteBase::MEDIUM);
182             return ToDuk<ScreenSize>(_ctx, { width, height });
183         }
184 
box(int32_t x,int32_t y,int32_t width,int32_t height)185         void box(int32_t x, int32_t y, int32_t width, int32_t height)
186         {
187             gfx_fill_rect_inset(&_dpi, { x, y, x + width - 1, y + height - 1 }, _colour.value_or(0), 0);
188         }
189 
well(int32_t x,int32_t y,int32_t width,int32_t height)190         void well(int32_t x, int32_t y, int32_t width, int32_t height)
191         {
192             gfx_fill_rect_inset(
193                 &_dpi, { x, y, x + width - 1, y + height - 1 }, _colour.value_or(0),
194                 INSET_RECT_FLAG_BORDER_INSET | INSET_RECT_FLAG_FILL_DONT_LIGHTEN);
195         }
196 
clear()197         void clear()
198         {
199             gfx_clear(&_dpi, _fill);
200         }
201 
clip(int32_t x,int32_t y,int32_t width,int32_t height)202         void clip(int32_t x, int32_t y, int32_t width, int32_t height)
203         {
204             rct_drawpixelinfo newDpi;
205             clip_drawpixelinfo(&newDpi, &_dpi, { x, y }, width, height);
206             _dpi = newDpi;
207         }
208 
image(uint32_t id,int32_t x,int32_t y)209         void image(uint32_t id, int32_t x, int32_t y)
210         {
211             ImageId img;
212             img = img.WithIndex(id);
213             if (_paletteId)
214             {
215                 img = img.WithRemap(*_paletteId);
216             }
217             else
218             {
219                 if (_colour)
220                 {
221                     img = img.WithPrimary(*_colour);
222                 }
223                 if (_secondaryColour)
224                 {
225                     img = img.WithSecondary(*_secondaryColour);
226                 }
227             }
228 
229             gfx_draw_sprite(&_dpi, img.WithTertiary(_ternaryColour.value_or(0)), { x, y });
230         }
231 
line(int32_t x1,int32_t y1,int32_t x2,int32_t y2)232         void line(int32_t x1, int32_t y1, int32_t x2, int32_t y2)
233         {
234             gfx_draw_line(&_dpi, { { x1, y1 }, { x2, y2 } }, _stroke);
235         }
236 
rect(int32_t x,int32_t y,int32_t width,int32_t height)237         void rect(int32_t x, int32_t y, int32_t width, int32_t height)
238         {
239             if (_stroke != 0)
240             {
241                 line(x, y, x + width, y);
242                 line(x + width - 1, y + 1, x + width - 1, y + height - 1);
243                 line(x, y + height - 1, x + width, y + height - 1);
244                 line(x, y + 1, x, y + height - 1);
245 
246                 x++;
247                 y++;
248                 width -= 2;
249                 height -= 2;
250             }
251             if (_fill != 0)
252             {
253                 gfx_fill_rect(&_dpi, { x, y, x + width - 1, y + height - 1 }, _fill);
254             }
255         }
256 
text(const std::string & text,int32_t x,int32_t y)257         void text(const std::string& text, int32_t x, int32_t y)
258         {
259             gfx_draw_string(&_dpi, { x, y }, text.c_str(), { _colour.value_or(0) });
260         }
261     };
262 } // namespace OpenRCT2::Scripting
263 
264 #endif
265