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 "../interface/Window.h"
15 
16 #    include <memory>
17 #    include <openrct2/Context.h>
18 #    include <openrct2/common.h>
19 #    include <openrct2/interface/Viewport.h>
20 #    include <openrct2/scripting/Duktape.hpp>
21 #    include <openrct2/scripting/ScriptEngine.h>
22 #    include <openrct2/world/Map.h>
23 
24 namespace OpenRCT2::Scripting
25 {
26     class ScViewport
27     {
28     private:
29         rct_windowclass _class{};
30         rct_windownumber _number{};
31 
32     public:
ScViewport(rct_windowclass c,rct_windownumber n=0)33         ScViewport(rct_windowclass c, rct_windownumber n = 0)
34             : _class(c)
35             , _number(n)
36         {
37         }
38 
39     private:
left_get() const40         int32_t left_get() const
41         {
42             auto viewport = GetViewport();
43             if (viewport != nullptr)
44             {
45                 return viewport->viewPos.x;
46             }
47             return 0;
48         }
left_set(int32_t value)49         void left_set(int32_t value)
50         {
51             auto viewport = GetViewport();
52             if (viewport != nullptr)
53             {
54                 SetViewLeftTop(value, viewport->viewPos.y);
55             }
56         }
57 
top_get() const58         int32_t top_get() const
59         {
60             auto viewport = GetViewport();
61             if (viewport != nullptr)
62             {
63                 return viewport->viewPos.y;
64             }
65             return 0;
66         }
top_set(int32_t value)67         void top_set(int32_t value)
68         {
69             auto viewport = GetViewport();
70             if (viewport != nullptr)
71             {
72                 SetViewLeftTop(viewport->viewPos.x, value);
73             }
74         }
75 
right_get() const76         int32_t right_get() const
77         {
78             auto viewport = GetViewport();
79             if (viewport != nullptr)
80             {
81                 return viewport->viewPos.x + viewport->view_width;
82             }
83             return 0;
84         }
right_set(int32_t value)85         void right_set(int32_t value)
86         {
87             auto viewport = GetViewport();
88             if (viewport != nullptr)
89             {
90                 SetViewLeftTop(value - viewport->view_width, viewport->viewPos.y);
91             }
92         }
93 
bottom_get() const94         int32_t bottom_get() const
95         {
96             auto viewport = GetViewport();
97             if (viewport != nullptr)
98             {
99                 return viewport->viewPos.y + viewport->view_height;
100             }
101             return 0;
102         }
bottom_set(int32_t value)103         void bottom_set(int32_t value)
104         {
105             auto viewport = GetViewport();
106             if (viewport != nullptr)
107             {
108                 SetViewLeftTop(viewport->viewPos.x, value - viewport->view_height);
109             }
110         }
111 
rotation_get() const112         int32_t rotation_get() const
113         {
114             return get_current_rotation();
115         }
rotation_set(int32_t value)116         void rotation_set(int32_t value)
117         {
118             if (value >= 0 && value < 4)
119             {
120                 auto w = GetWindow();
121                 if (w != nullptr)
122                 {
123                     while (get_current_rotation() != value)
124                     {
125                         window_rotate_camera(w, 1);
126                     }
127                 }
128             }
129         }
130 
zoom_get() const131         int32_t zoom_get() const
132         {
133             auto viewport = GetViewport();
134             if (viewport != nullptr)
135             {
136                 return static_cast<int8_t>(viewport->zoom);
137             }
138             return 0;
139         }
zoom_set(int32_t value)140         void zoom_set(int32_t value)
141         {
142             auto w = GetWindow();
143             if (w != nullptr)
144             {
145                 window_zoom_set(w, value, false);
146             }
147         }
148 
visibilityFlags_get() const149         uint32_t visibilityFlags_get() const
150         {
151             auto viewport = GetViewport();
152             if (viewport != nullptr)
153             {
154                 return viewport->flags;
155             }
156             return 0;
157         }
visibilityFlags_set(uint32_t value)158         void visibilityFlags_set(uint32_t value)
159         {
160             auto w = GetWindow();
161             if (w != nullptr)
162             {
163                 auto viewport = w->viewport;
164                 if (viewport != nullptr)
165                 {
166                     if (viewport->flags != value)
167                     {
168                         viewport->flags = value;
169                         w->Invalidate();
170                     }
171                 }
172             }
173         }
174 
getCentrePosition() const175         DukValue getCentrePosition() const
176         {
177             auto viewport = GetViewport();
178             if (viewport != nullptr)
179             {
180                 auto centre = viewport->viewPos + ScreenCoordsXY{ viewport->view_width / 2, viewport->view_height / 2 };
181                 auto coords = viewport_coord_to_map_coord(centre, 24);
182 
183                 auto ctx = GetContext()->GetScriptEngine().GetContext();
184                 auto obj = duk_push_object(ctx);
185                 duk_push_number(ctx, coords.x);
186                 duk_put_prop_string(ctx, obj, "x");
187                 duk_push_number(ctx, coords.y);
188                 duk_put_prop_string(ctx, obj, "y");
189                 return DukValue::take_from_stack(ctx);
190             }
191             return {};
192         }
193 
moveTo(DukValue position)194         void moveTo(DukValue position)
195         {
196             auto w = GetWindow();
197             if (w != nullptr)
198             {
199                 auto viewport = w->viewport;
200                 if (viewport != nullptr)
201                 {
202                     auto coords = GetCoordsFromObject(position);
203                     if (coords)
204                     {
205                         auto screenCoords = translate_3d_to_2d_with_z(get_current_rotation(), *coords);
206                         auto left = screenCoords.x - (viewport->view_width / 2);
207                         auto top = screenCoords.y - (viewport->view_height / 2);
208                         SetViewLeftTop(left, top);
209                     }
210                 }
211             }
212         }
213 
scrollTo(DukValue position)214         void scrollTo(DukValue position)
215         {
216             auto w = GetWindow();
217             if (w != nullptr)
218             {
219                 auto coords = GetCoordsFromObject(position);
220                 if (coords)
221                 {
222                     window_scroll_to_location(w, *coords);
223                 }
224             }
225         }
226 
227     public:
Register(duk_context * ctx)228         static void Register(duk_context* ctx)
229         {
230             dukglue_register_property(ctx, &ScViewport::left_get, &ScViewport::left_set, "left");
231             dukglue_register_property(ctx, &ScViewport::top_get, &ScViewport::top_set, "top");
232             dukglue_register_property(ctx, &ScViewport::right_get, &ScViewport::right_set, "right");
233             dukglue_register_property(ctx, &ScViewport::bottom_get, &ScViewport::bottom_set, "bottom");
234             dukglue_register_property(ctx, &ScViewport::rotation_get, &ScViewport::rotation_set, "rotation");
235             dukglue_register_property(ctx, &ScViewport::zoom_get, &ScViewport::zoom_set, "zoom");
236             dukglue_register_property(
237                 ctx, &ScViewport::visibilityFlags_get, &ScViewport::visibilityFlags_set, "visibilityFlags");
238             dukglue_register_method(ctx, &ScViewport::getCentrePosition, "getCentrePosition");
239             dukglue_register_method(ctx, &ScViewport::moveTo, "moveTo");
240             dukglue_register_method(ctx, &ScViewport::scrollTo, "scrollTo");
241         }
242 
243     private:
GetWindow() const244         rct_window* GetWindow() const
245         {
246             if (_class == WC_MAIN_WINDOW)
247                 return window_get_main();
248 
249             return window_find_by_number(_class, _number);
250         }
251 
GetViewport() const252         rct_viewport* GetViewport() const
253         {
254             auto w = GetWindow();
255             if (w != nullptr)
256             {
257                 return w->viewport;
258             }
259             return nullptr;
260         }
261 
SetViewLeftTop(int32_t left,int32_t top)262         void SetViewLeftTop(int32_t left, int32_t top)
263         {
264             auto w = GetWindow();
265             if (w != nullptr)
266             {
267                 auto viewport = w->viewport;
268                 if (viewport != nullptr)
269                 {
270                     viewport->viewPos.x = left;
271                     viewport->viewPos.y = top;
272                     viewport->flags &= ~WF_SCROLLING_TO_LOCATION;
273                     w->savedViewPos.x = viewport->viewPos.x;
274                     w->savedViewPos.y = viewport->viewPos.y;
275                     viewport->Invalidate();
276                 }
277             }
278         }
279 
GetCoordsFromObject(DukValue position) const280         std::optional<CoordsXYZ> GetCoordsFromObject(DukValue position) const
281         {
282             if (position.type() == DukValue::Type::OBJECT)
283             {
284                 auto dukX = position["x"];
285                 auto dukY = position["y"];
286                 auto dukZ = position["z"];
287                 if (dukX.type() == DukValue::Type::NUMBER && dukY.type() == DukValue::Type::NUMBER)
288                 {
289                     auto x = dukX.as_int();
290                     auto y = dukY.as_int();
291                     if (dukZ.type() == DukValue::Type::NUMBER)
292                     {
293                         return CoordsXYZ(x, y, dukZ.as_int());
294                     }
295 
296                     auto z = tile_element_height(CoordsXY(x, y));
297                     return CoordsXYZ(x, y, z);
298                 }
299             }
300             return std::nullopt;
301         }
302     };
303 } // namespace OpenRCT2::Scripting
304 
305 #endif
306