1 /*****************************************************************************
2  * Copyright (c) 2014-2021 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 #ifdef ENABLE_SCRIPTING
11 
12 #    include "ScTileElement.hpp"
13 
14 #    include "../../../Context.h"
15 #    include "../../../common.h"
16 #    include "../../../core/Guard.hpp"
17 #    include "../../../ride/Track.h"
18 #    include "../../../world/Footpath.h"
19 #    include "../../../world/Scenery.h"
20 #    include "../../../world/Sprite.h"
21 #    include "../../../world/Surface.h"
22 #    include "../../Duktape.hpp"
23 #    include "../../ScriptEngine.h"
24 
25 #    include <cstdio>
26 #    include <cstring>
27 #    include <utility>
28 
29 namespace OpenRCT2::Scripting
30 {
ScTileElement(const CoordsXY & coords,TileElement * element)31     ScTileElement::ScTileElement(const CoordsXY& coords, TileElement* element)
32         : _coords(coords)
33         , _element(element)
34     {
35     }
36 
type_get() const37     std::string ScTileElement::type_get() const
38     {
39         switch (_element->GetType())
40         {
41             case TILE_ELEMENT_TYPE_SURFACE:
42                 return "surface";
43             case TILE_ELEMENT_TYPE_PATH:
44                 return "footpath";
45             case TILE_ELEMENT_TYPE_TRACK:
46                 return "track";
47             case TILE_ELEMENT_TYPE_SMALL_SCENERY:
48                 return "small_scenery";
49             case TILE_ELEMENT_TYPE_ENTRANCE:
50                 return "entrance";
51             case TILE_ELEMENT_TYPE_WALL:
52                 return "wall";
53             case TILE_ELEMENT_TYPE_LARGE_SCENERY:
54                 return "large_scenery";
55             case TILE_ELEMENT_TYPE_BANNER:
56                 return "banner";
57             case TILE_ELEMENT_TYPE_CORRUPT:
58                 return "openrct2_corrupt_deprecated";
59             default:
60                 return "unknown";
61         }
62     }
63 
type_set(std::string value)64     void ScTileElement::type_set(std::string value)
65     {
66         if (value == "surface")
67             _element->type = TILE_ELEMENT_TYPE_SURFACE;
68         else if (value == "footpath")
69             _element->type = TILE_ELEMENT_TYPE_PATH;
70         else if (value == "track")
71             _element->type = TILE_ELEMENT_TYPE_TRACK;
72         else if (value == "small_scenery")
73             _element->type = TILE_ELEMENT_TYPE_SMALL_SCENERY;
74         else if (value == "entrance")
75             _element->type = TILE_ELEMENT_TYPE_ENTRANCE;
76         else if (value == "wall")
77             _element->type = TILE_ELEMENT_TYPE_WALL;
78         else if (value == "large_scenery")
79             _element->type = TILE_ELEMENT_TYPE_LARGE_SCENERY;
80         else if (value == "banner")
81             _element->type = TILE_ELEMENT_TYPE_BANNER;
82         else
83         {
84             if (value == "openrct2_corrupt_deprecated")
85                 std::puts(
86                     "Creation of new corrupt elements is deprecated. To hide elements, use the 'hidden' property instead.");
87             return;
88         }
89 
90         Invalidate();
91     }
92 
baseHeight_get() const93     uint8_t ScTileElement::baseHeight_get() const
94     {
95         return _element->base_height;
96     }
baseHeight_set(uint8_t newBaseHeight)97     void ScTileElement::baseHeight_set(uint8_t newBaseHeight)
98     {
99         ThrowIfGameStateNotMutable();
100         _element->base_height = newBaseHeight;
101         Invalidate();
102     }
103 
baseZ_get() const104     uint16_t ScTileElement::baseZ_get() const
105     {
106         return _element->GetBaseZ();
107     }
baseZ_set(uint16_t value)108     void ScTileElement::baseZ_set(uint16_t value)
109     {
110         ThrowIfGameStateNotMutable();
111         _element->SetBaseZ(value);
112         Invalidate();
113     }
114 
clearanceHeight_get() const115     uint8_t ScTileElement::clearanceHeight_get() const
116     {
117         return _element->clearance_height;
118     }
clearanceHeight_set(uint8_t newClearanceHeight)119     void ScTileElement::clearanceHeight_set(uint8_t newClearanceHeight)
120     {
121         ThrowIfGameStateNotMutable();
122         _element->clearance_height = newClearanceHeight;
123         Invalidate();
124     }
125 
clearanceZ_get() const126     uint16_t ScTileElement::clearanceZ_get() const
127     {
128         return _element->GetClearanceZ();
129     }
clearanceZ_set(uint16_t value)130     void ScTileElement::clearanceZ_set(uint16_t value)
131     {
132         ThrowIfGameStateNotMutable();
133         _element->SetClearanceZ(value);
134         Invalidate();
135     }
136 
slope_get() const137     DukValue ScTileElement::slope_get() const
138     {
139         auto ctx = GetContext()->GetScriptEngine().GetContext();
140         switch (_element->GetType())
141         {
142             case TILE_ELEMENT_TYPE_SURFACE:
143             {
144                 auto el = _element->AsSurface();
145                 duk_push_int(ctx, el->GetSlope());
146                 break;
147             }
148             case TILE_ELEMENT_TYPE_WALL:
149             {
150                 auto el = _element->AsWall();
151                 duk_push_int(ctx, el->GetSlope());
152                 break;
153             }
154             default:
155             {
156                 duk_push_null(ctx);
157                 break;
158             }
159         }
160         return DukValue::take_from_stack(ctx);
161     }
slope_set(uint8_t value)162     void ScTileElement::slope_set(uint8_t value)
163     {
164         ThrowIfGameStateNotMutable();
165         switch (_element->GetType())
166         {
167             case TILE_ELEMENT_TYPE_SURFACE:
168             {
169                 auto el = _element->AsSurface();
170                 el->SetSlope(value);
171                 Invalidate();
172                 break;
173             }
174             case TILE_ELEMENT_TYPE_WALL:
175             {
176                 auto el = _element->AsWall();
177                 el->SetSlope(value);
178                 Invalidate();
179                 break;
180             }
181         }
182     }
183 
waterHeight_get() const184     DukValue ScTileElement::waterHeight_get() const
185     {
186         auto ctx = GetContext()->GetScriptEngine().GetContext();
187         auto el = _element->AsSurface();
188         if (el != nullptr)
189             duk_push_int(ctx, el->GetWaterHeight());
190         else
191             duk_push_null(ctx);
192         return DukValue::take_from_stack(ctx);
193     }
waterHeight_set(int32_t value)194     void ScTileElement::waterHeight_set(int32_t value)
195     {
196         ThrowIfGameStateNotMutable();
197         auto el = _element->AsSurface();
198         if (el != nullptr)
199         {
200             el->SetWaterHeight(value);
201             Invalidate();
202         }
203     }
204 
surfaceStyle_get() const205     DukValue ScTileElement::surfaceStyle_get() const
206     {
207         auto ctx = GetContext()->GetScriptEngine().GetContext();
208         auto el = _element->AsSurface();
209         if (el != nullptr)
210             duk_push_int(ctx, el->GetSurfaceStyle());
211         else
212             duk_push_null(ctx);
213         return DukValue::take_from_stack(ctx);
214     }
surfaceStyle_set(uint32_t value)215     void ScTileElement::surfaceStyle_set(uint32_t value)
216     {
217         ThrowIfGameStateNotMutable();
218         auto el = _element->AsSurface();
219         if (el != nullptr)
220         {
221             el->SetSurfaceStyle(value);
222             Invalidate();
223         }
224     }
225 
edgeStyle_get() const226     DukValue ScTileElement::edgeStyle_get() const
227     {
228         auto ctx = GetContext()->GetScriptEngine().GetContext();
229         auto el = _element->AsSurface();
230         if (el != nullptr)
231             duk_push_int(ctx, el->GetEdgeStyle());
232         else
233             duk_push_null(ctx);
234         return DukValue::take_from_stack(ctx);
235     }
edgeStyle_set(uint32_t value)236     void ScTileElement::edgeStyle_set(uint32_t value)
237     {
238         ThrowIfGameStateNotMutable();
239         auto el = _element->AsSurface();
240         if (el != nullptr)
241         {
242             el->SetEdgeStyle(value);
243             Invalidate();
244         }
245     }
246 
grassLength_get() const247     DukValue ScTileElement::grassLength_get() const
248     {
249         auto ctx = GetContext()->GetScriptEngine().GetContext();
250         auto el = _element->AsSurface();
251         if (el != nullptr)
252             duk_push_int(ctx, el->GetGrassLength());
253         else
254             duk_push_null(ctx);
255         return DukValue::take_from_stack(ctx);
256     }
grassLength_set(uint8_t value)257     void ScTileElement::grassLength_set(uint8_t value)
258     {
259         ThrowIfGameStateNotMutable();
260         auto el = _element->AsSurface();
261         if (el != nullptr)
262         {
263             // TODO: Give warning when value > GRASS_LENGTH_CLUMPS_2
264             el->SetGrassLengthAndInvalidate(value, _coords);
265             Invalidate();
266         }
267     }
268 
hasOwnership_get() const269     DukValue ScTileElement::hasOwnership_get() const
270     {
271         auto ctx = GetContext()->GetScriptEngine().GetContext();
272         auto el = _element->AsSurface();
273         if (el != nullptr)
274             duk_push_boolean(ctx, el->GetOwnership() & OWNERSHIP_OWNED);
275         else
276             duk_push_null(ctx);
277         return DukValue::take_from_stack(ctx);
278     }
279 
hasConstructionRights_get()280     DukValue ScTileElement::hasConstructionRights_get()
281     {
282         auto ctx = GetContext()->GetScriptEngine().GetContext();
283         auto el = _element->AsSurface();
284         if (el != nullptr)
285         {
286             auto ownership = el->GetOwnership();
287             duk_push_boolean(ctx, (ownership & OWNERSHIP_OWNED) || (ownership & OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED));
288         }
289         else
290             duk_push_null(ctx);
291         return DukValue::take_from_stack(ctx);
292     }
293 
ownership_get() const294     DukValue ScTileElement::ownership_get() const
295     {
296         auto ctx = GetContext()->GetScriptEngine().GetContext();
297         auto el = _element->AsSurface();
298         if (el != nullptr)
299             duk_push_int(ctx, el->GetOwnership());
300         else
301             duk_push_null(ctx);
302         return DukValue::take_from_stack(ctx);
303     }
ownership_set(uint8_t value)304     void ScTileElement::ownership_set(uint8_t value)
305     {
306         ThrowIfGameStateNotMutable();
307         auto el = _element->AsSurface();
308         if (el != nullptr)
309         {
310             el->SetOwnership(value);
311             Invalidate();
312         }
313     }
314 
parkFences_get() const315     DukValue ScTileElement::parkFences_get() const
316     {
317         auto ctx = GetContext()->GetScriptEngine().GetContext();
318         auto el = _element->AsSurface();
319         if (el != nullptr)
320             duk_push_int(ctx, el->GetParkFences());
321         else
322             duk_push_null(ctx);
323         return DukValue::take_from_stack(ctx);
324     }
parkFences_set(uint8_t value)325     void ScTileElement::parkFences_set(uint8_t value)
326     {
327         ThrowIfGameStateNotMutable();
328         auto el = _element->AsSurface();
329         if (el != nullptr)
330         {
331             el->SetParkFences(value);
332             Invalidate();
333         }
334     }
335 
trackType_get() const336     DukValue ScTileElement::trackType_get() const
337     {
338         auto ctx = GetContext()->GetScriptEngine().GetContext();
339         auto el = _element->AsTrack();
340         if (el != nullptr)
341             duk_push_int(ctx, el->GetTrackType());
342         else
343             duk_push_null(ctx);
344         return DukValue::take_from_stack(ctx);
345     }
trackType_set(uint8_t value)346     void ScTileElement::trackType_set(uint8_t value)
347     {
348         ThrowIfGameStateNotMutable();
349         auto el = _element->AsTrack();
350         if (el != nullptr)
351         {
352             el->SetTrackType(value);
353             Invalidate();
354         }
355     }
356 
sequence_get() const357     DukValue ScTileElement::sequence_get() const
358     {
359         auto ctx = GetContext()->GetScriptEngine().GetContext();
360         switch (_element->GetType())
361         {
362             case TILE_ELEMENT_TYPE_LARGE_SCENERY:
363             {
364                 auto el = _element->AsLargeScenery();
365                 duk_push_int(ctx, el->GetSequenceIndex());
366                 break;
367             }
368             case TILE_ELEMENT_TYPE_TRACK:
369             {
370                 auto el = _element->AsTrack();
371                 if (get_ride(el->GetRideIndex())->type != RIDE_TYPE_MAZE)
372                     duk_push_int(ctx, el->GetSequenceIndex());
373                 else
374                     duk_push_null(ctx);
375                 break;
376             }
377             case TILE_ELEMENT_TYPE_ENTRANCE:
378             {
379                 auto el = _element->AsEntrance();
380                 duk_push_int(ctx, el->GetSequenceIndex());
381                 break;
382             }
383             default:
384             {
385                 duk_push_null(ctx);
386                 break;
387             }
388         }
389         return DukValue::take_from_stack(ctx);
390     }
sequence_set(uint8_t value)391     void ScTileElement::sequence_set(uint8_t value)
392     {
393         ThrowIfGameStateNotMutable();
394         switch (_element->GetType())
395         {
396             case TILE_ELEMENT_TYPE_LARGE_SCENERY:
397             {
398                 auto el = _element->AsLargeScenery();
399                 el->SetSequenceIndex(value);
400                 Invalidate();
401                 break;
402             }
403             case TILE_ELEMENT_TYPE_TRACK:
404             {
405                 auto el = _element->AsTrack();
406                 if (get_ride(el->GetRideIndex())->type != RIDE_TYPE_MAZE)
407                 {
408                     el->SetSequenceIndex(value);
409                     Invalidate();
410                 }
411                 break;
412             }
413             case TILE_ELEMENT_TYPE_ENTRANCE:
414             {
415                 auto el = _element->AsEntrance();
416                 el->SetSequenceIndex(value);
417                 Invalidate();
418                 break;
419             }
420         }
421     }
422 
ride_get() const423     DukValue ScTileElement::ride_get() const
424     {
425         auto ctx = GetContext()->GetScriptEngine().GetContext();
426         switch (_element->GetType())
427         {
428             case TILE_ELEMENT_TYPE_PATH:
429             {
430                 auto el = _element->AsPath();
431                 if (el->IsQueue() && el->GetRideIndex() != RIDE_ID_NULL)
432                     duk_push_int(ctx, EnumValue(el->GetRideIndex()));
433                 else
434                     duk_push_null(ctx);
435                 break;
436             }
437             case TILE_ELEMENT_TYPE_TRACK:
438             {
439                 auto el = _element->AsTrack();
440                 duk_push_int(ctx, EnumValue(el->GetRideIndex()));
441                 break;
442             }
443             case TILE_ELEMENT_TYPE_ENTRANCE:
444             {
445                 auto el = _element->AsEntrance();
446                 duk_push_int(ctx, EnumValue(el->GetRideIndex()));
447                 break;
448             }
449             default:
450             {
451                 duk_push_null(ctx);
452                 break;
453             }
454         }
455         return DukValue::take_from_stack(ctx);
456     }
ride_set(int32_t value)457     void ScTileElement::ride_set(int32_t value)
458     {
459         ThrowIfGameStateNotMutable();
460         switch (_element->GetType())
461         {
462             case TILE_ELEMENT_TYPE_PATH:
463             {
464                 auto el = _element->AsPath();
465                 if (!el->HasAddition())
466                 {
467                     el->SetRideIndex(static_cast<ride_id_t>(value));
468                     Invalidate();
469                 }
470                 break;
471             }
472             case TILE_ELEMENT_TYPE_TRACK:
473             {
474                 auto el = _element->AsTrack();
475                 el->SetRideIndex(static_cast<ride_id_t>(value));
476                 Invalidate();
477                 break;
478             }
479             case TILE_ELEMENT_TYPE_ENTRANCE:
480             {
481                 auto el = _element->AsEntrance();
482                 el->SetRideIndex(static_cast<ride_id_t>(value));
483                 Invalidate();
484                 break;
485             }
486         }
487     }
488 
station_get() const489     DukValue ScTileElement::station_get() const
490     {
491         auto ctx = GetContext()->GetScriptEngine().GetContext();
492         switch (_element->GetType())
493         {
494             case TILE_ELEMENT_TYPE_PATH:
495             {
496                 auto el = _element->AsPath();
497                 if (el->IsQueue() && el->GetRideIndex() != RIDE_ID_NULL)
498                     duk_push_int(ctx, el->GetStationIndex());
499                 else
500                     duk_push_null(ctx);
501                 break;
502             }
503             case TILE_ELEMENT_TYPE_TRACK:
504             {
505                 auto el = _element->AsTrack();
506                 if (el->IsStation())
507                     duk_push_int(ctx, el->GetStationIndex());
508                 else
509                     duk_push_null(ctx);
510                 break;
511             }
512             case TILE_ELEMENT_TYPE_ENTRANCE:
513             {
514                 auto el = _element->AsEntrance();
515                 duk_push_int(ctx, el->GetStationIndex());
516                 break;
517             }
518             default:
519             {
520                 duk_push_null(ctx);
521                 break;
522             }
523         }
524         return DukValue::take_from_stack(ctx);
525     }
station_set(uint8_t value)526     void ScTileElement::station_set(uint8_t value)
527     {
528         ThrowIfGameStateNotMutable();
529         switch (_element->GetType())
530         {
531             case TILE_ELEMENT_TYPE_PATH:
532             {
533                 auto el = _element->AsPath();
534                 el->SetStationIndex(value);
535                 Invalidate();
536                 break;
537             }
538             case TILE_ELEMENT_TYPE_TRACK:
539             {
540                 auto el = _element->AsTrack();
541                 el->SetStationIndex(value);
542                 Invalidate();
543                 break;
544             }
545             case TILE_ELEMENT_TYPE_ENTRANCE:
546             {
547                 auto el = _element->AsEntrance();
548                 el->SetStationIndex(value);
549                 Invalidate();
550                 break;
551             }
552         }
553     }
554 
hasChainLift_get() const555     DukValue ScTileElement::hasChainLift_get() const
556     {
557         auto ctx = GetContext()->GetScriptEngine().GetContext();
558         auto el = _element->AsTrack();
559         if (el != nullptr)
560             duk_push_boolean(ctx, el->HasChain());
561         else
562             duk_push_null(ctx);
563         return DukValue::take_from_stack(ctx);
564     }
hasChainLift_set(bool value)565     void ScTileElement::hasChainLift_set(bool value)
566     {
567         ThrowIfGameStateNotMutable();
568         auto el = _element->AsTrack();
569         if (el != nullptr)
570         {
571             el->SetHasChain(value);
572             Invalidate();
573         }
574     }
575 
mazeEntry_get() const576     DukValue ScTileElement::mazeEntry_get() const
577     {
578         auto ctx = GetContext()->GetScriptEngine().GetContext();
579         auto el = _element->AsTrack();
580         if (el != nullptr && get_ride(el->GetRideIndex())->type == RIDE_TYPE_MAZE)
581             duk_push_int(ctx, el->GetMazeEntry());
582         else
583             duk_push_null(ctx);
584         return DukValue::take_from_stack(ctx);
585     }
mazeEntry_set(uint16_t value)586     void ScTileElement::mazeEntry_set(uint16_t value)
587     {
588         ThrowIfGameStateNotMutable();
589         auto el = _element->AsTrack();
590         if (el != nullptr)
591             if (get_ride(el->GetRideIndex())->type == RIDE_TYPE_MAZE)
592             {
593                 el->SetMazeEntry(value);
594                 Invalidate();
595             }
596     }
597 
colourScheme_get() const598     DukValue ScTileElement::colourScheme_get() const
599     {
600         auto ctx = GetContext()->GetScriptEngine().GetContext();
601         auto el = _element->AsTrack();
602         if (el != nullptr && get_ride(el->GetRideIndex())->type != RIDE_TYPE_MAZE)
603             duk_push_int(ctx, el->GetColourScheme());
604         else
605             duk_push_null(ctx);
606         return DukValue::take_from_stack(ctx);
607     }
colourScheme_set(uint8_t value)608     void ScTileElement::colourScheme_set(uint8_t value)
609     {
610         ThrowIfGameStateNotMutable();
611         auto el = _element->AsTrack();
612         if (el != nullptr)
613             if (get_ride(el->GetRideIndex())->type != RIDE_TYPE_MAZE)
614             {
615                 el->SetColourScheme(value);
616                 Invalidate();
617             }
618     }
619 
seatRotation_get() const620     DukValue ScTileElement::seatRotation_get() const
621     {
622         auto ctx = GetContext()->GetScriptEngine().GetContext();
623         auto el = _element->AsTrack();
624         if (el != nullptr && get_ride(el->GetRideIndex())->type != RIDE_TYPE_MAZE)
625             duk_push_int(ctx, el->GetSeatRotation());
626         else
627             duk_push_null(ctx);
628         return DukValue::take_from_stack(ctx);
629     }
seatRotation_set(uint8_t value)630     void ScTileElement::seatRotation_set(uint8_t value)
631     {
632         ThrowIfGameStateNotMutable();
633         auto el = _element->AsTrack();
634         if (el != nullptr)
635             if (get_ride(el->GetRideIndex())->type != RIDE_TYPE_MAZE)
636             {
637                 el->SetSeatRotation(value);
638                 Invalidate();
639             }
640     }
641 
brakeBoosterSpeed_get() const642     DukValue ScTileElement::brakeBoosterSpeed_get() const
643     {
644         auto ctx = GetContext()->GetScriptEngine().GetContext();
645         auto el = _element->AsTrack();
646         if (el != nullptr && TrackTypeHasSpeedSetting(el->GetTrackType()))
647             duk_push_int(ctx, el->GetBrakeBoosterSpeed());
648         else
649             duk_push_null(ctx);
650         return DukValue::take_from_stack(ctx);
651     }
brakeBoosterSpeed_set(uint8_t value)652     void ScTileElement::brakeBoosterSpeed_set(uint8_t value)
653     {
654         ThrowIfGameStateNotMutable();
655         auto el = _element->AsTrack();
656         if (el != nullptr)
657             if (TrackTypeHasSpeedSetting(el->GetTrackType()))
658             {
659                 el->SetBrakeBoosterSpeed(value);
660                 Invalidate();
661             }
662     }
663 
isInverted_get() const664     DukValue ScTileElement::isInverted_get() const
665     {
666         auto ctx = GetContext()->GetScriptEngine().GetContext();
667         auto el = _element->AsTrack();
668         if (el != nullptr)
669             duk_push_boolean(ctx, el->IsInverted());
670         else
671             duk_push_null(ctx);
672         return DukValue::take_from_stack(ctx);
673     }
isInverted_set(bool value)674     void ScTileElement::isInverted_set(bool value)
675     {
676         ThrowIfGameStateNotMutable();
677         auto el = _element->AsTrack();
678         if (el != nullptr)
679         {
680             el->SetInverted(value);
681             Invalidate();
682         }
683     }
684 
hasCableLift_get() const685     DukValue ScTileElement::hasCableLift_get() const
686     {
687         auto ctx = GetContext()->GetScriptEngine().GetContext();
688         auto el = _element->AsTrack();
689         if (el != nullptr)
690             duk_push_boolean(ctx, el->HasCableLift());
691         else
692             duk_push_null(ctx);
693         return DukValue::take_from_stack(ctx);
694     }
hasCableLift_set(bool value)695     void ScTileElement::hasCableLift_set(bool value)
696     {
697         ThrowIfGameStateNotMutable();
698         auto el = _element->AsTrack();
699         if (el != nullptr)
700         {
701             el->SetHasCableLift(value);
702             Invalidate();
703         }
704     }
705 
object_get() const706     DukValue ScTileElement::object_get() const
707     {
708         auto ctx = GetContext()->GetScriptEngine().GetContext();
709         switch (_element->GetType())
710         {
711             case TILE_ELEMENT_TYPE_PATH:
712             {
713                 auto el = _element->AsPath();
714                 duk_push_int(ctx, el->GetLegacyPathEntryIndex());
715                 break;
716             }
717             case TILE_ELEMENT_TYPE_SMALL_SCENERY:
718             {
719                 auto el = _element->AsSmallScenery();
720                 duk_push_int(ctx, el->GetEntryIndex());
721                 break;
722             }
723             case TILE_ELEMENT_TYPE_LARGE_SCENERY:
724             {
725                 auto el = _element->AsLargeScenery();
726                 duk_push_int(ctx, el->GetEntryIndex());
727                 break;
728             }
729             case TILE_ELEMENT_TYPE_WALL:
730             {
731                 auto el = _element->AsWall();
732                 duk_push_int(ctx, el->GetEntryIndex());
733                 break;
734             }
735             case TILE_ELEMENT_TYPE_ENTRANCE:
736             {
737                 auto el = _element->AsEntrance();
738                 duk_push_int(ctx, el->GetEntranceType());
739                 break;
740             }
741             default:
742             {
743                 duk_push_null(ctx);
744                 break;
745             }
746         }
747         return DukValue::take_from_stack(ctx);
748     }
object_set(uint32_t value)749     void ScTileElement::object_set(uint32_t value)
750     {
751         ThrowIfGameStateNotMutable();
752         switch (_element->GetType())
753         {
754             case TILE_ELEMENT_TYPE_PATH:
755             {
756                 auto el = _element->AsPath();
757                 el->SetLegacyPathEntryIndex(value & 0xFF);
758                 Invalidate();
759                 break;
760             }
761             case TILE_ELEMENT_TYPE_SMALL_SCENERY:
762             {
763                 auto el = _element->AsSmallScenery();
764                 el->SetEntryIndex(value & 0xFF);
765                 Invalidate();
766                 break;
767             }
768             case TILE_ELEMENT_TYPE_LARGE_SCENERY:
769             {
770                 auto el = _element->AsLargeScenery();
771                 el->SetEntryIndex(value);
772                 Invalidate();
773                 break;
774             }
775             case TILE_ELEMENT_TYPE_WALL:
776             {
777                 auto el = _element->AsWall();
778                 el->SetEntryIndex(value & 0xFFFF);
779                 Invalidate();
780                 break;
781             }
782             case TILE_ELEMENT_TYPE_ENTRANCE:
783             {
784                 auto el = _element->AsEntrance();
785                 el->SetEntranceType(value & 0xFF);
786                 Invalidate();
787                 break;
788             }
789         }
790     }
791 
isHidden_get() const792     bool ScTileElement::isHidden_get() const
793     {
794         // TODO: Simply return the 'hidden' field once corrupt elements are superseded.
795         const TileElement* element = map_get_first_element_at(_coords);
796         bool previousElementWasUsefulCorrupt = false;
797         do
798         {
799             if (element == _element)
800                 return previousElementWasUsefulCorrupt;
801 
802             if (element->GetType() == TILE_ELEMENT_TYPE_CORRUPT)
803                 previousElementWasUsefulCorrupt = !previousElementWasUsefulCorrupt;
804             else
805                 previousElementWasUsefulCorrupt = false;
806         } while (!(element++)->IsLastForTile());
807 
808         Guard::Assert(false);
809         return false;
810     }
isHidden_set(bool hide)811     void ScTileElement::isHidden_set(bool hide)
812     {
813         // TODO: Simply update the 'hidden' field once corrupt elements are superseded.
814         ThrowIfGameStateNotMutable();
815         const bool isHidden = isHidden_get();
816         if (hide == isHidden)
817             return;
818 
819         if (hide)
820         {
821             // Get index of our current element (has to be done now before inserting the corrupt element)
822             const auto elementIndex = _element - map_get_first_element_at(_coords);
823 
824             // Insert corrupt element at the end of the list for this tile
825             // Note: Z = MAX_ELEMENT_HEIGHT to guarantee this
826             TileElement* insertedElement = tile_element_insert(
827                 { _coords, MAX_ELEMENT_HEIGHT * COORDS_Z_STEP }, 0, TileElementType::Corrupt);
828             if (insertedElement == nullptr)
829             {
830                 // TODO: Show error
831                 return;
832             }
833 
834             // Since inserting a new element may move the tile elements in memory, we have to update the local pointer
835             _element = map_get_first_element_at(_coords) + elementIndex;
836 
837             // Move the corrupt element down in the list until it's right under our element
838             while (insertedElement > _element)
839             {
840                 std::swap<TileElement>(*insertedElement, *(insertedElement - 1));
841                 insertedElement--;
842 
843                 // Un-swap the last-for-tile flag
844                 if (insertedElement->IsLastForTile())
845                 {
846                     insertedElement->SetLastForTile(false);
847                     (insertedElement + 1)->SetLastForTile(true);
848                 }
849             }
850 
851             // Now the corrupt element took the hidden element's place, increment it by one
852             _element++;
853 
854             // Update base and clearance heights of inserted corrupt element to match the element to hide
855             insertedElement->base_height = insertedElement->clearance_height = _element->base_height;
856         }
857         else
858         {
859             TileElement* const elementToRemove = _element - 1;
860             Guard::Assert(elementToRemove->GetType() == TILE_ELEMENT_TYPE_CORRUPT);
861             tile_element_remove(elementToRemove);
862             _element--;
863         }
864 
865         Invalidate();
866     }
867 
age_get() const868     DukValue ScTileElement::age_get() const
869     {
870         auto ctx = GetContext()->GetScriptEngine().GetContext();
871         auto el = _element->AsSmallScenery();
872         if (el != nullptr)
873             duk_push_int(ctx, el->GetAge());
874         else
875             duk_push_null(ctx);
876         return DukValue::take_from_stack(ctx);
877     }
age_set(uint8_t value)878     void ScTileElement::age_set(uint8_t value)
879     {
880         ThrowIfGameStateNotMutable();
881         auto el = _element->AsSmallScenery();
882         if (el != nullptr)
883         {
884             el->SetAge(value);
885             Invalidate();
886         }
887     }
888 
quadrant_get() const889     DukValue ScTileElement::quadrant_get() const
890     {
891         auto ctx = GetContext()->GetScriptEngine().GetContext();
892         auto el = _element->AsSmallScenery();
893         if (el != nullptr)
894             duk_push_int(ctx, el->GetSceneryQuadrant());
895         else
896             duk_push_null(ctx);
897         return DukValue::take_from_stack(ctx);
898     }
quadrant_set(uint8_t value)899     void ScTileElement::quadrant_set(uint8_t value)
900     {
901         ThrowIfGameStateNotMutable();
902         auto el = _element->AsSmallScenery();
903         if (el != nullptr)
904         {
905             el->SetSceneryQuadrant(value);
906             Invalidate();
907         }
908     }
909 
occupiedQuadrants_get() const910     uint8_t ScTileElement::occupiedQuadrants_get() const
911     {
912         return _element->GetOccupiedQuadrants();
913     }
occupiedQuadrants_set(uint8_t value)914     void ScTileElement::occupiedQuadrants_set(uint8_t value)
915     {
916         ThrowIfGameStateNotMutable();
917         _element->SetOccupiedQuadrants(value);
918         Invalidate();
919     }
920 
isGhost_get() const921     bool ScTileElement::isGhost_get() const
922     {
923         return _element->IsGhost();
924     }
isGhost_set(bool value)925     void ScTileElement::isGhost_set(bool value)
926     {
927         ThrowIfGameStateNotMutable();
928         _element->SetGhost(value);
929         Invalidate();
930     }
931 
primaryColour_get() const932     DukValue ScTileElement::primaryColour_get() const
933     {
934         auto ctx = GetContext()->GetScriptEngine().GetContext();
935         switch (_element->GetType())
936         {
937             case TILE_ELEMENT_TYPE_SMALL_SCENERY:
938             {
939                 auto el = _element->AsSmallScenery();
940                 duk_push_int(ctx, el->GetPrimaryColour());
941                 break;
942             }
943             case TILE_ELEMENT_TYPE_LARGE_SCENERY:
944             {
945                 auto el = _element->AsLargeScenery();
946                 duk_push_int(ctx, el->GetPrimaryColour());
947                 break;
948             }
949             case TILE_ELEMENT_TYPE_WALL:
950             {
951                 auto el = _element->AsWall();
952                 duk_push_int(ctx, el->GetPrimaryColour());
953                 break;
954             }
955             default:
956             {
957                 duk_push_null(ctx);
958                 break;
959             }
960         }
961         return DukValue::take_from_stack(ctx);
962     }
primaryColour_set(uint8_t value)963     void ScTileElement::primaryColour_set(uint8_t value)
964     {
965         ThrowIfGameStateNotMutable();
966         switch (_element->GetType())
967         {
968             case TILE_ELEMENT_TYPE_SMALL_SCENERY:
969             {
970                 auto el = _element->AsSmallScenery();
971                 el->SetPrimaryColour(value);
972                 Invalidate();
973                 break;
974             }
975             case TILE_ELEMENT_TYPE_LARGE_SCENERY:
976             {
977                 auto el = _element->AsLargeScenery();
978                 el->SetPrimaryColour(value);
979                 Invalidate();
980                 break;
981             }
982             case TILE_ELEMENT_TYPE_WALL:
983             {
984                 auto el = _element->AsWall();
985                 el->SetPrimaryColour(value);
986                 Invalidate();
987                 break;
988             }
989         }
990     }
991 
secondaryColour_get() const992     DukValue ScTileElement::secondaryColour_get() const
993     {
994         auto ctx = GetContext()->GetScriptEngine().GetContext();
995         switch (_element->GetType())
996         {
997             case TILE_ELEMENT_TYPE_SMALL_SCENERY:
998             {
999                 auto el = _element->AsSmallScenery();
1000                 duk_push_int(ctx, el->GetSecondaryColour());
1001                 break;
1002             }
1003             case TILE_ELEMENT_TYPE_LARGE_SCENERY:
1004             {
1005                 auto el = _element->AsLargeScenery();
1006                 duk_push_int(ctx, el->GetSecondaryColour());
1007                 break;
1008             }
1009             case TILE_ELEMENT_TYPE_WALL:
1010             {
1011                 auto el = _element->AsWall();
1012                 duk_push_int(ctx, el->GetSecondaryColour());
1013                 break;
1014             }
1015             default:
1016             {
1017                 duk_push_null(ctx);
1018                 break;
1019             }
1020         }
1021         return DukValue::take_from_stack(ctx);
1022     }
secondaryColour_set(uint8_t value)1023     void ScTileElement::secondaryColour_set(uint8_t value)
1024     {
1025         ThrowIfGameStateNotMutable();
1026         switch (_element->GetType())
1027         {
1028             case TILE_ELEMENT_TYPE_SMALL_SCENERY:
1029             {
1030                 auto el = _element->AsSmallScenery();
1031                 el->SetSecondaryColour(value);
1032                 Invalidate();
1033                 break;
1034             }
1035             case TILE_ELEMENT_TYPE_LARGE_SCENERY:
1036             {
1037                 auto el = _element->AsLargeScenery();
1038                 el->SetSecondaryColour(value);
1039                 Invalidate();
1040                 break;
1041             }
1042             case TILE_ELEMENT_TYPE_WALL:
1043             {
1044                 auto el = _element->AsWall();
1045                 el->SetSecondaryColour(value);
1046                 Invalidate();
1047                 break;
1048             }
1049         }
1050     }
1051 
tertiaryColour_get() const1052     DukValue ScTileElement::tertiaryColour_get() const
1053     {
1054         auto ctx = GetContext()->GetScriptEngine().GetContext();
1055         auto el = _element->AsWall();
1056         if (el != nullptr)
1057             duk_push_int(ctx, el->GetTertiaryColour());
1058         else
1059             duk_push_null(ctx);
1060         return DukValue::take_from_stack(ctx);
1061     }
tertiaryColour_set(uint8_t value)1062     void ScTileElement::tertiaryColour_set(uint8_t value)
1063     {
1064         ThrowIfGameStateNotMutable();
1065         auto el = _element->AsWall();
1066         if (el != nullptr)
1067         {
1068             el->SetTertiaryColour(value);
1069             Invalidate();
1070         }
1071     }
1072 
bannerIndex_get() const1073     DukValue ScTileElement::bannerIndex_get() const
1074     {
1075         auto ctx = GetContext()->GetScriptEngine().GetContext();
1076         BannerIndex idx = _element->GetBannerIndex();
1077         if (idx == BANNER_INDEX_NULL)
1078             duk_push_null(ctx);
1079         else
1080             duk_push_int(ctx, idx);
1081         return DukValue::take_from_stack(ctx);
1082     }
bannerIndex_set(uint16_t value)1083     void ScTileElement::bannerIndex_set(uint16_t value)
1084     {
1085         ThrowIfGameStateNotMutable();
1086         switch (_element->GetType())
1087         {
1088             case TILE_ELEMENT_TYPE_LARGE_SCENERY:
1089             {
1090                 auto el = _element->AsLargeScenery();
1091                 el->SetBannerIndex(value);
1092                 Invalidate();
1093                 break;
1094             }
1095             case TILE_ELEMENT_TYPE_WALL:
1096             {
1097                 auto el = _element->AsWall();
1098                 el->SetBannerIndex(value);
1099                 Invalidate();
1100                 break;
1101             }
1102             case TILE_ELEMENT_TYPE_BANNER:
1103             {
1104                 auto el = _element->AsBanner();
1105                 el->SetIndex(value);
1106                 Invalidate();
1107                 break;
1108             }
1109         }
1110     }
1111 
1112     // Deprecated in favor of seperate 'edges' and 'corners' properties,
1113     // left here to maintain compatibility with older plugins.
1114     /** @deprecated */
edgesAndCorners_get() const1115     uint8_t ScTileElement::edgesAndCorners_get() const
1116     {
1117         auto el = _element->AsPath();
1118         return el != nullptr ? el->GetEdgesAndCorners() : 0;
1119     }
1120     /** @deprecated */
edgesAndCorners_set(uint8_t value)1121     void ScTileElement::edgesAndCorners_set(uint8_t value)
1122     {
1123         ThrowIfGameStateNotMutable();
1124         auto el = _element->AsPath();
1125         if (el != nullptr)
1126         {
1127             el->SetEdgesAndCorners(value);
1128             Invalidate();
1129         }
1130     }
1131 
edges_get() const1132     DukValue ScTileElement::edges_get() const
1133     {
1134         auto ctx = GetContext()->GetScriptEngine().GetContext();
1135         auto el = _element->AsPath();
1136         if (el != nullptr)
1137             duk_push_int(ctx, el->GetEdges());
1138         else
1139             duk_push_null(ctx);
1140         return DukValue::take_from_stack(ctx);
1141     }
edges_set(uint8_t value)1142     void ScTileElement::edges_set(uint8_t value)
1143     {
1144         ThrowIfGameStateNotMutable();
1145         auto el = _element->AsPath();
1146         if (el != nullptr)
1147         {
1148             el->SetEdges(value);
1149             Invalidate();
1150         }
1151     }
1152 
corners_get() const1153     DukValue ScTileElement::corners_get() const
1154     {
1155         auto ctx = GetContext()->GetScriptEngine().GetContext();
1156         auto el = _element->AsPath();
1157         if (el != nullptr)
1158             duk_push_int(ctx, el->GetCorners());
1159         else
1160             duk_push_null(ctx);
1161         return DukValue::take_from_stack(ctx);
1162     }
corners_set(uint8_t value)1163     void ScTileElement::corners_set(uint8_t value)
1164     {
1165         ThrowIfGameStateNotMutable();
1166         auto el = _element->AsPath();
1167         if (el != nullptr)
1168         {
1169             el->SetCorners(value);
1170             Invalidate();
1171         }
1172     }
1173 
slopeDirection_get() const1174     DukValue ScTileElement::slopeDirection_get() const
1175     {
1176         auto ctx = GetContext()->GetScriptEngine().GetContext();
1177         auto el = _element->AsPath();
1178         if (el != nullptr && el->IsSloped())
1179             duk_push_int(ctx, el->GetSlopeDirection());
1180         else
1181             duk_push_null(ctx);
1182         return DukValue::take_from_stack(ctx);
1183     }
slopeDirection_set(const DukValue & value)1184     void ScTileElement::slopeDirection_set(const DukValue& value)
1185     {
1186         ThrowIfGameStateNotMutable();
1187         auto el = _element->AsPath();
1188         if (el != nullptr)
1189         {
1190             if (value.type() == DukValue::Type::NUMBER)
1191             {
1192                 el->SetSloped(true);
1193                 el->SetSlopeDirection(value.as_int());
1194             }
1195             else
1196             {
1197                 el->SetSloped(false);
1198                 el->SetSlopeDirection(0);
1199             }
1200             Invalidate();
1201         }
1202     }
1203 
isQueue_get() const1204     DukValue ScTileElement::isQueue_get() const
1205     {
1206         auto ctx = GetContext()->GetScriptEngine().GetContext();
1207         auto el = _element->AsPath();
1208         if (el != nullptr)
1209             duk_push_boolean(ctx, el->IsQueue());
1210         else
1211             duk_push_null(ctx);
1212         return DukValue::take_from_stack(ctx);
1213     }
isQueue_set(bool value)1214     void ScTileElement::isQueue_set(bool value)
1215     {
1216         ThrowIfGameStateNotMutable();
1217         auto el = _element->AsPath();
1218         if (el != nullptr)
1219         {
1220             el->SetIsQueue(value);
1221             Invalidate();
1222         }
1223     }
1224 
queueBannerDirection_get() const1225     DukValue ScTileElement::queueBannerDirection_get() const
1226     {
1227         auto ctx = GetContext()->GetScriptEngine().GetContext();
1228         auto el = _element->AsPath();
1229         if (el != nullptr && el->HasQueueBanner())
1230             duk_push_int(ctx, el->GetQueueBannerDirection());
1231         else
1232             duk_push_null(ctx);
1233         return DukValue::take_from_stack(ctx);
1234     }
queueBannerDirection_set(const DukValue & value)1235     void ScTileElement::queueBannerDirection_set(const DukValue& value)
1236     {
1237         ThrowIfGameStateNotMutable();
1238         auto el = _element->AsPath();
1239         if (el != nullptr)
1240         {
1241             if (value.type() == DukValue::Type::NUMBER)
1242             {
1243                 el->SetHasQueueBanner(true);
1244                 el->SetQueueBannerDirection(value.as_int());
1245             }
1246             else
1247             {
1248                 el->SetHasQueueBanner(false);
1249                 el->SetQueueBannerDirection(0);
1250             }
1251             Invalidate();
1252         }
1253     }
1254 
isBlockedByVehicle_get() const1255     DukValue ScTileElement::isBlockedByVehicle_get() const
1256     {
1257         auto ctx = GetContext()->GetScriptEngine().GetContext();
1258         auto el = _element->AsPath();
1259         if (el != nullptr)
1260             duk_push_boolean(ctx, el->IsBlockedByVehicle());
1261         else
1262             duk_push_null(ctx);
1263         return DukValue::take_from_stack(ctx);
1264     }
isBlockedByVehicle_set(bool value)1265     void ScTileElement::isBlockedByVehicle_set(bool value)
1266     {
1267         ThrowIfGameStateNotMutable();
1268         auto el = _element->AsPath();
1269         if (el != nullptr)
1270         {
1271             el->SetIsBlockedByVehicle(value);
1272             Invalidate();
1273         }
1274     }
1275 
isWide_get() const1276     DukValue ScTileElement::isWide_get() const
1277     {
1278         auto ctx = GetContext()->GetScriptEngine().GetContext();
1279         auto el = _element->AsPath();
1280         if (el != nullptr)
1281             duk_push_boolean(ctx, el->IsWide());
1282         else
1283             duk_push_null(ctx);
1284         return DukValue::take_from_stack(ctx);
1285     }
isWide_set(bool value)1286     void ScTileElement::isWide_set(bool value)
1287     {
1288         ThrowIfGameStateNotMutable();
1289         auto el = _element->AsPath();
1290         if (el != nullptr)
1291         {
1292             el->SetWide(value);
1293             Invalidate();
1294         }
1295     }
1296 
addition_get() const1297     DukValue ScTileElement::addition_get() const
1298     {
1299         auto ctx = GetContext()->GetScriptEngine().GetContext();
1300         auto el = _element->AsPath();
1301         if (el != nullptr && el->HasAddition())
1302             duk_push_int(ctx, el->GetAddition() - 1);
1303         else
1304             duk_push_null(ctx);
1305         return DukValue::take_from_stack(ctx);
1306     }
addition_set(const DukValue & value)1307     void ScTileElement::addition_set(const DukValue& value)
1308     {
1309         ThrowIfGameStateNotMutable();
1310         auto el = _element->AsPath();
1311         if (el != nullptr)
1312         {
1313             if (value.type() == DukValue::Type::NUMBER)
1314             {
1315                 auto addition = value.as_int();
1316                 if (addition >= 0 && addition <= 254)
1317                 {
1318                     el->SetAddition(addition + 1);
1319                 }
1320             }
1321             else
1322             {
1323                 el->SetAddition(0);
1324             }
1325             Invalidate();
1326         }
1327     }
1328 
additionStatus_get() const1329     DukValue ScTileElement::additionStatus_get() const
1330     {
1331         auto ctx = GetContext()->GetScriptEngine().GetContext();
1332         auto el = _element->AsPath();
1333         if (el != nullptr && el->HasAddition())
1334             duk_push_int(ctx, el->GetAdditionStatus());
1335         else
1336             duk_push_null(ctx);
1337         return DukValue::take_from_stack(ctx);
1338     }
additionStatus_set(uint8_t value)1339     void ScTileElement::additionStatus_set(uint8_t value)
1340     {
1341         ThrowIfGameStateNotMutable();
1342         auto el = _element->AsPath();
1343         if (el != nullptr)
1344             if (el->HasAddition())
1345             {
1346                 el->SetAdditionStatus(value);
1347                 Invalidate();
1348             }
1349     }
1350 
isAdditionBroken_get() const1351     DukValue ScTileElement::isAdditionBroken_get() const
1352     {
1353         auto ctx = GetContext()->GetScriptEngine().GetContext();
1354         auto el = _element->AsPath();
1355         if (el != nullptr && el->HasAddition())
1356             duk_push_boolean(ctx, el->IsBroken());
1357         else
1358             duk_push_null(ctx);
1359         return DukValue::take_from_stack(ctx);
1360     }
isAdditionBroken_set(bool value)1361     void ScTileElement::isAdditionBroken_set(bool value)
1362     {
1363         ThrowIfGameStateNotMutable();
1364         auto el = _element->AsPath();
1365         if (el != nullptr)
1366         {
1367             el->SetIsBroken(value);
1368             Invalidate();
1369         }
1370     }
1371 
isAdditionGhost_get() const1372     DukValue ScTileElement::isAdditionGhost_get() const
1373     {
1374         auto ctx = GetContext()->GetScriptEngine().GetContext();
1375         auto el = _element->AsPath();
1376         if (el != nullptr && el->HasAddition())
1377             duk_push_boolean(ctx, el->AdditionIsGhost());
1378         else
1379             duk_push_null(ctx);
1380         return DukValue::take_from_stack(ctx);
1381     }
isAdditionGhost_set(bool value)1382     void ScTileElement::isAdditionGhost_set(bool value)
1383     {
1384         ThrowIfGameStateNotMutable();
1385         auto el = _element->AsPath();
1386         if (el != nullptr)
1387         {
1388             el->SetAdditionIsGhost(value);
1389             Invalidate();
1390         }
1391     }
1392 
footpathObject_get() const1393     DukValue ScTileElement::footpathObject_get() const
1394     {
1395         auto ctx = GetContext()->GetScriptEngine().GetContext();
1396         auto el = _element->AsEntrance();
1397         if (el != nullptr)
1398             duk_push_int(ctx, el->GetLegacyPathEntryIndex());
1399         else
1400             duk_push_null(ctx);
1401         return DukValue::take_from_stack(ctx);
1402     }
footpathObject_set(uint8_t value)1403     void ScTileElement::footpathObject_set(uint8_t value)
1404     {
1405         ThrowIfGameStateNotMutable();
1406         auto el = _element->AsEntrance();
1407         if (el != nullptr)
1408         {
1409             el->SetLegacyPathEntryIndex(value);
1410             Invalidate();
1411         }
1412     }
1413 
direction_get() const1414     DukValue ScTileElement::direction_get() const
1415     {
1416         auto ctx = GetContext()->GetScriptEngine().GetContext();
1417         switch (_element->GetType())
1418         {
1419             case TILE_ELEMENT_TYPE_BANNER:
1420             {
1421                 auto el = _element->AsBanner();
1422                 duk_push_int(ctx, el->GetPosition());
1423                 break;
1424             }
1425             case TILE_ELEMENT_TYPE_PATH:
1426             case TILE_ELEMENT_TYPE_SURFACE:
1427             {
1428                 duk_push_null(ctx);
1429                 break;
1430             }
1431             default:
1432             {
1433                 duk_push_int(ctx, _element->GetDirection());
1434                 break;
1435             }
1436         }
1437         return DukValue::take_from_stack(ctx);
1438     }
direction_set(uint8_t value)1439     void ScTileElement::direction_set(uint8_t value)
1440     {
1441         ThrowIfGameStateNotMutable();
1442         switch (_element->GetType())
1443         {
1444             case TILE_ELEMENT_TYPE_BANNER:
1445             {
1446                 auto el = _element->AsBanner();
1447                 el->SetPosition(value);
1448                 Invalidate();
1449                 break;
1450             }
1451             case TILE_ELEMENT_TYPE_PATH:
1452             case TILE_ELEMENT_TYPE_SURFACE:
1453             {
1454                 break;
1455             }
1456             default:
1457             {
1458                 _element->SetDirection(value);
1459                 Invalidate();
1460             }
1461         }
1462     }
1463 
Invalidate()1464     void ScTileElement::Invalidate()
1465     {
1466         map_invalidate_tile_full(_coords);
1467     }
1468 
Register(duk_context * ctx)1469     void ScTileElement::Register(duk_context* ctx)
1470     {
1471         // All
1472         dukglue_register_property(ctx, &ScTileElement::type_get, &ScTileElement::type_set, "type");
1473         dukglue_register_property(ctx, &ScTileElement::baseHeight_get, &ScTileElement::baseHeight_set, "baseHeight");
1474         dukglue_register_property(ctx, &ScTileElement::baseZ_get, &ScTileElement::baseZ_set, "baseZ");
1475         dukglue_register_property(
1476             ctx, &ScTileElement::clearanceHeight_get, &ScTileElement::clearanceHeight_set, "clearanceHeight");
1477         dukglue_register_property(ctx, &ScTileElement::clearanceZ_get, &ScTileElement::clearanceZ_set, "clearanceZ");
1478         dukglue_register_property(
1479             ctx, &ScTileElement::occupiedQuadrants_get, &ScTileElement::occupiedQuadrants_set, "occupiedQuadrants");
1480         dukglue_register_property(ctx, &ScTileElement::isGhost_get, &ScTileElement::isGhost_set, "isGhost");
1481         dukglue_register_property(ctx, &ScTileElement::isHidden_get, &ScTileElement::isHidden_set, "isHidden");
1482 
1483         // Track | Small Scenery | Wall | Entrance | Large Scenery | Banner
1484         dukglue_register_property(ctx, &ScTileElement::direction_get, &ScTileElement::direction_set, "direction");
1485 
1486         // Path | Small Scenery | Wall | Entrance | Large Scenery
1487         dukglue_register_property(ctx, &ScTileElement::object_get, &ScTileElement::object_set, "object");
1488 
1489         // Small Scenery | Wall | Large Scenery
1490         dukglue_register_property(ctx, &ScTileElement::primaryColour_get, &ScTileElement::primaryColour_set, "primaryColour");
1491         dukglue_register_property(
1492             ctx, &ScTileElement::secondaryColour_get, &ScTileElement::secondaryColour_set, "secondaryColour");
1493 
1494         // Wall | Large Scenery | Banner
1495         dukglue_register_property(ctx, &ScTileElement::bannerIndex_get, &ScTileElement::bannerIndex_set, "bannerIndex");
1496 
1497         // Path | Track | Entrance
1498         dukglue_register_property(ctx, &ScTileElement::ride_get, &ScTileElement::ride_set, "ride");
1499         dukglue_register_property(ctx, &ScTileElement::station_get, &ScTileElement::station_set, "station");
1500 
1501         // Track | Entrance | Large Scenery
1502         dukglue_register_property(ctx, &ScTileElement::sequence_get, &ScTileElement::sequence_set, "sequence");
1503 
1504         // Surface | Wall
1505         dukglue_register_property(ctx, &ScTileElement::slope_get, &ScTileElement::slope_set, "slope");
1506 
1507         // Surface only
1508         dukglue_register_property(ctx, &ScTileElement::waterHeight_get, &ScTileElement::waterHeight_set, "waterHeight");
1509         dukglue_register_property(ctx, &ScTileElement::surfaceStyle_get, &ScTileElement::surfaceStyle_set, "surfaceStyle");
1510         dukglue_register_property(ctx, &ScTileElement::edgeStyle_get, &ScTileElement::edgeStyle_set, "edgeStyle");
1511         dukglue_register_property(ctx, &ScTileElement::grassLength_get, &ScTileElement::grassLength_set, "grassLength");
1512         dukglue_register_property(ctx, &ScTileElement::hasOwnership_get, nullptr, "hasOwnership");
1513         dukglue_register_property(ctx, &ScTileElement::hasConstructionRights_get, nullptr, "hasConstructionRights");
1514         dukglue_register_property(ctx, &ScTileElement::ownership_get, &ScTileElement::ownership_set, "ownership");
1515         dukglue_register_property(ctx, &ScTileElement::parkFences_get, &ScTileElement::parkFences_set, "parkFences");
1516 
1517         // Footpath only
1518         dukglue_register_property(
1519             ctx, &ScTileElement::edgesAndCorners_get, &ScTileElement::edgesAndCorners_set, "edgesAndCorners");
1520         dukglue_register_property(ctx, &ScTileElement::edges_get, &ScTileElement::edges_set, "edges");
1521         dukglue_register_property(ctx, &ScTileElement::corners_get, &ScTileElement::corners_set, "corners");
1522         dukglue_register_property(
1523             ctx, &ScTileElement::slopeDirection_get, &ScTileElement::slopeDirection_set, "slopeDirection");
1524         dukglue_register_property(ctx, &ScTileElement::isQueue_get, &ScTileElement::isQueue_set, "isQueue");
1525         dukglue_register_property(
1526             ctx, &ScTileElement::queueBannerDirection_get, &ScTileElement::queueBannerDirection_set, "queueBannerDirection");
1527         dukglue_register_property(ctx, &ScTileElement::queueBannerDirection_get, &ScTileElement::edges_set, "test");
1528 
1529         dukglue_register_property(
1530             ctx, &ScTileElement::isBlockedByVehicle_get, &ScTileElement::isBlockedByVehicle_set, "isBlockedByVehicle");
1531         dukglue_register_property(ctx, &ScTileElement::isWide_get, &ScTileElement::isWide_set, "isWide");
1532 
1533         dukglue_register_property(ctx, &ScTileElement::addition_get, &ScTileElement::addition_set, "addition");
1534         dukglue_register_property(
1535             ctx, &ScTileElement::additionStatus_get, &ScTileElement::additionStatus_set, "additionStatus");
1536         dukglue_register_property(
1537             ctx, &ScTileElement::isAdditionBroken_get, &ScTileElement::isAdditionBroken_set, "isAdditionBroken");
1538         dukglue_register_property(
1539             ctx, &ScTileElement::isAdditionGhost_get, &ScTileElement::isAdditionGhost_set, "isAdditionGhost");
1540 
1541         // Track only
1542         dukglue_register_property(ctx, &ScTileElement::trackType_get, &ScTileElement::trackType_set, "trackType");
1543         dukglue_register_property(ctx, &ScTileElement::mazeEntry_get, &ScTileElement::mazeEntry_set, "mazeEntry");
1544         dukglue_register_property(ctx, &ScTileElement::colourScheme_get, &ScTileElement::colourScheme_set, "colourScheme");
1545         dukglue_register_property(ctx, &ScTileElement::seatRotation_get, &ScTileElement::seatRotation_set, "seatRotation");
1546         dukglue_register_property(
1547             ctx, &ScTileElement::brakeBoosterSpeed_get, &ScTileElement::brakeBoosterSpeed_set, "brakeBoosterSpeed");
1548         dukglue_register_property(ctx, &ScTileElement::hasChainLift_get, &ScTileElement::hasChainLift_set, "hasChainLift");
1549         dukglue_register_property(ctx, &ScTileElement::isInverted_get, &ScTileElement::isInverted_set, "isInverted");
1550         dukglue_register_property(ctx, &ScTileElement::hasCableLift_get, &ScTileElement::hasCableLift_set, "hasCableLift");
1551 
1552         // Small Scenery only
1553         dukglue_register_property(ctx, &ScTileElement::age_get, &ScTileElement::age_set, "age");
1554         dukglue_register_property(ctx, &ScTileElement::quadrant_get, &ScTileElement::quadrant_set, "quadrant");
1555 
1556         // Wall only
1557         dukglue_register_property(
1558             ctx, &ScTileElement::tertiaryColour_get, &ScTileElement::tertiaryColour_set, "tertiaryColour");
1559 
1560         // Entrance only
1561         dukglue_register_property(
1562             ctx, &ScTileElement::footpathObject_get, &ScTileElement::footpathObject_set, "footpathObject");
1563     }
1564 
1565 } // namespace OpenRCT2::Scripting
1566 
1567 #endif
1568