1 /*
2 Copyright (c) 2010 Peter "Corsix" Cawley
3 
4 Permission is hereby granted, free of charge, to any person obtaining a copy of
5 this software and associated documentation files (the "Software"), to deal in
6 the Software without restriction, including without limitation the rights to
7 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 of the Software, and to permit persons to whom the Software is furnished to do
9 so, subject to the following conditions:
10 
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
13 
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 SOFTWARE.
21 */
22 
23 #include "th_gfx.h"
24 #include "th_lua_internal.h"
25 #include "th_map.h"
26 
27 namespace {
28 
29 /* this variable is used to determine the layer of the animation, it should be
30   rewritten at some
31   point so that the it is passed as an argument in the function l_anim_set_tile
32 */
33 int last_layer = 2;
34 
l_anims_new(lua_State * L)35 int l_anims_new(lua_State* L) {
36   luaT_stdnew<animation_manager>(L, luaT_environindex, true);
37   return 1;
38 }
39 
l_anims_set_spritesheet(lua_State * L)40 int l_anims_set_spritesheet(lua_State* L) {
41   animation_manager* pAnims = luaT_testuserdata<animation_manager>(L);
42   sprite_sheet* pSheet = luaT_testuserdata<sprite_sheet>(L, 2);
43   lua_settop(L, 2);
44 
45   pAnims->set_sprite_sheet(pSheet);
46   luaT_setenvfield(L, 1, "sprites");
47   return 1;
48 }
49 
50 //! Set the video target for the sprites.
51 /*!
52     setCanvas(<video-surface>)
53  */
l_anims_set_canvas(lua_State * L)54 int l_anims_set_canvas(lua_State* L) {
55   animation_manager* pAnims = luaT_testuserdata<animation_manager>(L);
56   render_target* pCanvas = luaT_testuserdata<render_target>(L, 2);
57   lua_settop(L, 2);
58 
59   pAnims->set_canvas(pCanvas);
60   luaT_setenvfield(L, 1, "target");
61   return 1;
62 }
63 
l_anims_load(lua_State * L)64 int l_anims_load(lua_State* L) {
65   animation_manager* pAnims = luaT_testuserdata<animation_manager>(L);
66   size_t iStartDataLength, iFrameDataLength, iListDataLength,
67       iElementDataLength;
68   const uint8_t* pStartData = luaT_checkfile(L, 2, &iStartDataLength);
69   const uint8_t* pFrameData = luaT_checkfile(L, 3, &iFrameDataLength);
70   const uint8_t* pListData = luaT_checkfile(L, 4, &iListDataLength);
71   const uint8_t* pElementData = luaT_checkfile(L, 5, &iElementDataLength);
72 
73   if (pAnims->load_from_th_file(pStartData, iStartDataLength, pFrameData,
74                                 iFrameDataLength, pListData, iListDataLength,
75                                 pElementData, iElementDataLength)) {
76     lua_pushboolean(L, 1);
77   } else {
78     lua_pushboolean(L, 0);
79   }
80 
81   return 1;
82 }
83 
84 //! Load custom animations.
85 /*!
86     loadCustom(<data-of-an-animation-file>) -> true/false
87  */
l_anims_loadcustom(lua_State * L)88 int l_anims_loadcustom(lua_State* L) {
89   animation_manager* pAnims = luaT_testuserdata<animation_manager>(L);
90   size_t iDataLength;
91   const uint8_t* pData = luaT_checkfile(L, 2, &iDataLength);
92 
93   if (pAnims->load_custom_animations(pData, iDataLength)) {
94     lua_pushboolean(L, 1);
95   } else {
96     lua_pushboolean(L, 0);
97   }
98 
99   return 1;
100 }
101 
102 //! Lua interface for getting a set of animations by name and tile size (one for
103 //! each view direction, 'nil' if no animation is available for a direction).
104 /*!
105     getAnimations(<tile-size>, <animation-name>) -> (<anim-north>, <anim-east>,
106    <anim-south>, <anim-west>)
107  */
l_anims_getanims(lua_State * L)108 int l_anims_getanims(lua_State* L) {
109   animation_manager* pAnims = luaT_testuserdata<animation_manager>(L);
110   int iTileSize = static_cast<int>(luaL_checkinteger(L, 2));
111   const char* pName = luaL_checkstring(L, 3);
112 
113   const animation_start_frames& oFrames =
114       pAnims->get_named_animations(pName, iTileSize);
115   if (oFrames.north < 0) {
116     lua_pushnil(L);
117   } else {
118     lua_pushnumber(L, static_cast<double>(oFrames.north));
119   }
120   if (oFrames.east < 0) {
121     lua_pushnil(L);
122   } else {
123     lua_pushnumber(L, static_cast<double>(oFrames.east));
124   }
125   if (oFrames.south < 0) {
126     lua_pushnil(L);
127   } else {
128     lua_pushnumber(L, static_cast<double>(oFrames.south));
129   }
130   if (oFrames.west < 0) {
131     lua_pushnil(L);
132   } else {
133     lua_pushnumber(L, static_cast<double>(oFrames.west));
134   }
135   return 4;
136 }
137 
l_anims_getfirst(lua_State * L)138 int l_anims_getfirst(lua_State* L) {
139   animation_manager* pAnims = luaT_testuserdata<animation_manager>(L);
140   int iAnim = static_cast<int>(luaL_checkinteger(L, 2));
141 
142   lua_pushinteger(L, pAnims->get_first_frame(iAnim));
143   return 1;
144 }
145 
l_anims_getnext(lua_State * L)146 int l_anims_getnext(lua_State* L) {
147   animation_manager* pAnims = luaT_testuserdata<animation_manager>(L);
148   int iFrame = static_cast<int>(luaL_checkinteger(L, 2));
149 
150   lua_pushinteger(L, pAnims->get_next_frame(iFrame));
151   return 1;
152 }
153 
l_anims_set_alt_pal(lua_State * L)154 int l_anims_set_alt_pal(lua_State* L) {
155   animation_manager* pAnims = luaT_testuserdata<animation_manager>(L);
156   size_t iAnimation = luaL_checkinteger(L, 2);
157   size_t iPalLen;
158   const uint8_t* pPal = luaT_checkfile(L, 3, &iPalLen);
159   if (iPalLen != 256) {
160     return luaL_argerror(L, 3, "GhostPalette string is not a valid palette");
161   }
162   uint32_t iAlt32 = static_cast<uint32_t>(luaL_checkinteger(L, 4));
163 
164   pAnims->set_animation_alt_palette_map(iAnimation, pPal, iAlt32);
165 
166   lua_getfenv(L, 1);
167   lua_insert(L, 2);
168   lua_settop(L, 4);
169   lua_settable(L, 2);
170   lua_settop(L, 1);
171   return 1;
172 }
173 
l_anims_set_marker(lua_State * L)174 int l_anims_set_marker(lua_State* L) {
175   animation_manager* pAnims = luaT_testuserdata<animation_manager>(L);
176   lua_pushboolean(
177       L, pAnims->set_frame_marker(luaL_checkinteger(L, 2),
178                                   static_cast<int>(luaL_checkinteger(L, 3)),
179                                   static_cast<int>(luaL_checkinteger(L, 4)))
180              ? 1
181              : 0);
182   return 1;
183 }
184 
l_anims_set_secondary_marker(lua_State * L)185 int l_anims_set_secondary_marker(lua_State* L) {
186   animation_manager* pAnims = luaT_testuserdata<animation_manager>(L);
187   lua_pushboolean(
188       L, pAnims->set_frame_secondary_marker(
189              luaL_checkinteger(L, 2), static_cast<int>(luaL_checkinteger(L, 3)),
190              static_cast<int>(luaL_checkinteger(L, 4)))
191              ? 1
192              : 0);
193   return 1;
194 }
195 
l_anims_draw(lua_State * L)196 int l_anims_draw(lua_State* L) {
197   animation_manager* pAnims = luaT_testuserdata<animation_manager>(L);
198   render_target* pCanvas = luaT_testuserdata<render_target>(L, 2);
199   size_t iFrame = luaL_checkinteger(L, 3);
200   layers* pLayers = luaT_testuserdata<layers>(L, 4, luaT_upvalueindex(2));
201   int iX = static_cast<int>(luaL_checkinteger(L, 5));
202   int iY = static_cast<int>(luaL_checkinteger(L, 6));
203   int iFlags = static_cast<int>(luaL_optinteger(L, 7, 0));
204 
205   pAnims->draw_frame(pCanvas, iFrame, *pLayers, iX, iY, iFlags);
206 
207   lua_settop(L, 1);
208   return 1;
209 }
210 
211 template <typename T>
l_anim_new(lua_State * L)212 int l_anim_new(lua_State* L) {
213   T* pAnimation = luaT_stdnew<T>(L, luaT_environindex, true);
214   lua_rawgeti(L, luaT_environindex, 2);
215   lua_pushlightuserdata(L, pAnimation);
216   lua_pushvalue(L, -3);
217   lua_rawset(L, -3);
218   lua_pop(L, 1);
219   return 1;
220 }
221 
222 template <typename T>
l_anim_persist(lua_State * L)223 int l_anim_persist(lua_State* L) {
224   T* pAnimation;
225   if (lua_gettop(L) == 3) {
226     pAnimation = luaT_testuserdata<T>(L, 1, luaT_environindex, false);
227     luaT_rotate(L, 1, -1);
228   } else {
229     // Fast __persist call
230     pAnimation = (T*)lua_touserdata(L, -1);
231   }
232   lua_persist_writer* pWriter = (lua_persist_writer*)lua_touserdata(L, 1);
233 
234   pAnimation->persist(pWriter);
235   lua_rawgeti(L, luaT_environindex, 1);
236   lua_pushlightuserdata(L, pAnimation);
237   lua_gettable(L, -2);
238   pWriter->write_stack_object(-1);
239   lua_pop(L, 2);
240   return 0;
241 }
242 
243 template <typename T>
l_anim_pre_depersist(lua_State * L)244 int l_anim_pre_depersist(lua_State* L) {
245   // Note that anims and the map have nice reference cycles between them
246   // and hence we cannot be sure which is depersisted first. To ensure that
247   // things work nicely, we initialise all the fields of a THAnimation as
248   // soon as possible, thus preventing issues like an anim -> map -> anim
249   // reference chain whereby l_anim_depersist is called after l_map_depersist
250   // (as anim references map in its environment table) causing the prev
251   // field to be set during map depersistence, then cleared to nullptr by the
252   // constructor during l_anim_depersist.
253   T* pAnimation = luaT_testuserdata<T>(L);
254   new (pAnimation) T;  // Call constructor
255   return 0;
256 }
257 
258 template <typename T>
l_anim_depersist(lua_State * L)259 int l_anim_depersist(lua_State* L) {
260   T* pAnimation = luaT_testuserdata<T>(L);
261   lua_settop(L, 2);
262   lua_insert(L, 1);
263   lua_persist_reader* pReader = (lua_persist_reader*)lua_touserdata(L, 1);
264 
265   lua_rawgeti(L, luaT_environindex, 2);
266   lua_pushlightuserdata(L, pAnimation);
267   lua_pushvalue(L, 2);
268   lua_settable(L, -3);
269   lua_pop(L, 1);
270   pAnimation->depersist(pReader);
271   lua_rawgeti(L, luaT_environindex, 1);
272   lua_pushlightuserdata(L, pAnimation);
273   if (!pReader->read_stack_object()) return 0;
274   lua_settable(L, -3);
275   lua_pop(L, 1);
276   return 0;
277 }
278 
l_anim_set_hitresult(lua_State * L)279 int l_anim_set_hitresult(lua_State* L) {
280   luaL_checktype(L, 1, LUA_TUSERDATA);
281   lua_settop(L, 2);
282   lua_rawgeti(L, luaT_environindex, 1);
283   lua_pushlightuserdata(L, lua_touserdata(L, 1));
284   lua_pushvalue(L, 2);
285   lua_settable(L, 3);
286   lua_settop(L, 1);
287   return 1;
288 }
289 
l_anim_set_frame(lua_State * L)290 int l_anim_set_frame(lua_State* L) {
291   animation* pAnimation = luaT_testuserdata<animation>(L);
292   pAnimation->set_frame(luaL_checkinteger(L, 2));
293   lua_settop(L, 1);
294   return 1;
295 }
296 
l_anim_get_frame(lua_State * L)297 int l_anim_get_frame(lua_State* L) {
298   animation* pAnimation = luaT_testuserdata<animation>(L);
299   lua_pushinteger(L, pAnimation->get_frame());
300   return 1;
301 }
302 
l_anim_set_crop(lua_State * L)303 int l_anim_set_crop(lua_State* L) {
304   animation* pAnimation = luaT_testuserdata<animation>(L);
305   pAnimation->set_crop_column(static_cast<int>(luaL_checkinteger(L, 2)));
306   lua_settop(L, 1);
307   return 1;
308 }
309 
l_anim_get_crop(lua_State * L)310 int l_anim_get_crop(lua_State* L) {
311   animation* pAnimation = luaT_testuserdata<animation>(L);
312   lua_pushinteger(L, pAnimation->get_crop_column());
313   return 1;
314 }
315 
l_anim_set_anim(lua_State * L)316 int l_anim_set_anim(lua_State* L) {
317   animation* pAnimation = luaT_testuserdata<animation>(L);
318   animation_manager* pManager = luaT_testuserdata<animation_manager>(L, 2);
319   lua_Integer iAnim = luaL_checkinteger(L, 3);
320   if (iAnim < 0 ||
321       iAnim >= static_cast<lua_Integer>(pManager->get_animation_count()))
322     luaL_argerror(L, 3, "Animation index out of bounds");
323 
324   if (lua_isnoneornil(L, 4)) {
325     pAnimation->set_flags(0);
326   } else {
327     pAnimation->set_flags(static_cast<uint32_t>(luaL_checkinteger(L, 4)));
328   }
329 
330   pAnimation->set_animation(pManager, static_cast<size_t>(iAnim));
331   lua_settop(L, 2);
332   luaT_setenvfield(L, 1, "animator");
333   lua_pushnil(L);
334   luaT_setenvfield(L, 1, "morph_target");
335 
336   return 1;
337 }
338 
l_anim_set_morph(lua_State * L)339 int l_anim_set_morph(lua_State* L) {
340   animation* pAnimation = luaT_testuserdata<animation>(L);
341   animation* pMorphTarget =
342       luaT_testuserdata<animation>(L, 2, luaT_environindex);
343 
344   int iDurationFactor = 1;
345   if (!lua_isnoneornil(L, 3) && luaL_checkinteger(L, 3) > 0) {
346     iDurationFactor = static_cast<int>(luaL_checkinteger(L, 3));
347   }
348 
349   pAnimation->set_morph_target(pMorphTarget, iDurationFactor);
350   lua_settop(L, 2);
351   luaT_setenvfield(L, 1, "morph_target");
352 
353   return 1;
354 }
355 
l_anim_set_drawable_layer(lua_State * L)356 int l_anim_set_drawable_layer(lua_State* L) {
357   last_layer = static_cast<int>(luaL_checkinteger(L, 2));
358   return 1;
359 }
360 
l_anim_get_anim(lua_State * L)361 int l_anim_get_anim(lua_State* L) {
362   animation* pAnimation = luaT_testuserdata<animation>(L);
363   lua_pushinteger(L, pAnimation->get_animation());
364 
365   return 1;
366 }
367 
368 template <typename T>
l_anim_set_tile(lua_State * L)369 int l_anim_set_tile(lua_State* L) {
370   T* pAnimation = luaT_testuserdata<T>(L);
371   if (lua_isnoneornil(L, 2)) {
372     pAnimation->remove_from_tile();
373     lua_pushnil(L);
374     luaT_setenvfield(L, 1, "map");
375     lua_settop(L, 1);
376   } else {
377     level_map* pMap = luaT_testuserdata<level_map>(L, 2);
378     map_tile* pNode =
379         pMap->get_tile(static_cast<int>(luaL_checkinteger(L, 3) - 1),
380                        static_cast<int>(luaL_checkinteger(L, 4) - 1));
381     if (pNode) {
382       pAnimation->attach_to_tile(pNode, last_layer);
383     } else {
384       luaL_argerror(L, 3,
385                     lua_pushfstring(L,
386                                     "Map index out of bounds (" LUA_NUMBER_FMT
387                                     "," LUA_NUMBER_FMT ")",
388                                     lua_tonumber(L, 3), lua_tonumber(L, 4)));
389     }
390 
391     lua_settop(L, 2);
392     luaT_setenvfield(L, 1, "map");
393   }
394 
395   return 1;
396 }
397 
l_anim_get_tile(lua_State * L)398 int l_anim_get_tile(lua_State* L) {
399   animation* pAnimation = luaT_testuserdata<animation>(L);
400   lua_settop(L, 1);
401   lua_getfenv(L, 1);
402   lua_getfield(L, 2, "map");
403   lua_replace(L, 2);
404   if (lua_isnil(L, 2)) {
405     return 0;
406   }
407   level_map* pMap = (level_map*)lua_touserdata(L, 2);
408   const link_list* pListNode = pAnimation->get_previous();
409   while (pListNode->prev) {
410     pListNode = pListNode->prev;
411   }
412   // Casting pListNode to a map_tile* is slightly dubious, but it should
413   // work. If on the normal list, then pListNode will be a map_tile*, and
414   // all is fine. However, if on the early list, pListNode will be pointing
415   // to a member of a map_tile, so we're relying on pointer arithmetic
416   // being a subtract and integer divide by sizeof(map_tile) to yield the
417   // correct map_tile.
418   const map_tile* pRootNode = pMap->get_tile_unchecked(0, 0);
419   uintptr_t iDiff = reinterpret_cast<const char*>(pListNode) -
420                     reinterpret_cast<const char*>(pRootNode);
421   int iIndex = (int)(iDiff / sizeof(map_tile));
422   int iY = iIndex / pMap->get_width();
423   int iX = iIndex - (iY * pMap->get_width());
424   lua_pushinteger(L, iX + 1);
425   lua_pushinteger(L, iY + 1);
426   return 3;  // map, x, y
427 }
428 
l_anim_set_parent(lua_State * L)429 int l_anim_set_parent(lua_State* L) {
430   animation* pAnimation = luaT_testuserdata<animation>(L);
431   animation* pParent =
432       luaT_testuserdata<animation>(L, 2, luaT_environindex, false);
433   pAnimation->set_parent(pParent);
434   lua_settop(L, 1);
435   return 1;
436 }
437 
438 template <typename T>
l_anim_set_flag(lua_State * L)439 int l_anim_set_flag(lua_State* L) {
440   T* pAnimation = luaT_testuserdata<T>(L);
441   pAnimation->set_flags(static_cast<uint32_t>(luaL_checkinteger(L, 2)));
442 
443   lua_settop(L, 1);
444   return 1;
445 }
446 
447 template <typename T>
l_anim_set_flag_partial(lua_State * L)448 int l_anim_set_flag_partial(lua_State* L) {
449   T* pAnimation = luaT_testuserdata<T>(L);
450   uint32_t iFlags = static_cast<uint32_t>(luaL_checkinteger(L, 2));
451   if (lua_isnone(L, 3) || lua_toboolean(L, 3)) {
452     pAnimation->set_flags(pAnimation->get_flags() | iFlags);
453   } else {
454     pAnimation->set_flags(pAnimation->get_flags() & ~iFlags);
455   }
456   lua_settop(L, 1);
457   return 1;
458 }
459 
460 template <typename T>
l_anim_make_visible(lua_State * L)461 int l_anim_make_visible(lua_State* L) {
462   T* pAnimation = luaT_testuserdata<T>(L);
463   pAnimation->set_flags(pAnimation->get_flags() &
464                         ~static_cast<uint32_t>(thdf_alpha_50 | thdf_alpha_75));
465 
466   lua_settop(L, 1);
467   return 1;
468 }
469 
470 template <typename T>
l_anim_make_invisible(lua_State * L)471 int l_anim_make_invisible(lua_State* L) {
472   T* pAnimation = luaT_testuserdata<T>(L);
473   pAnimation->set_flags(pAnimation->get_flags() |
474                         static_cast<uint32_t>(thdf_alpha_50 | thdf_alpha_75));
475 
476   lua_settop(L, 1);
477   return 1;
478 }
479 
480 template <typename T>
l_anim_get_flag(lua_State * L)481 int l_anim_get_flag(lua_State* L) {
482   T* pAnimation = luaT_testuserdata<T>(L);
483   lua_pushinteger(L, pAnimation->get_flags());
484 
485   return 1;
486 }
487 
488 template <typename T>
l_anim_set_position(lua_State * L)489 int l_anim_set_position(lua_State* L) {
490   T* pAnimation = luaT_testuserdata<T>(L);
491 
492   pAnimation->set_position(static_cast<int>(luaL_checkinteger(L, 2)),
493                            static_cast<int>(luaL_checkinteger(L, 3)));
494 
495   lua_settop(L, 1);
496   return 1;
497 }
498 
l_anim_get_position(lua_State * L)499 int l_anim_get_position(lua_State* L) {
500   animation* pAnimation = luaT_testuserdata<animation>(L);
501 
502   lua_pushinteger(L, pAnimation->get_x());
503   lua_pushinteger(L, pAnimation->get_y());
504 
505   return 2;
506 }
507 
508 template <typename T>
l_anim_set_speed(lua_State * L)509 int l_anim_set_speed(lua_State* L) {
510   T* pAnimation = luaT_testuserdata<T>(L);
511 
512   pAnimation->set_speed(static_cast<int>(luaL_optinteger(L, 2, 0)),
513                         static_cast<int>(luaL_optinteger(L, 3, 0)));
514 
515   lua_settop(L, 1);
516   return 1;
517 }
518 
519 template <typename T>
l_anim_set_layer(lua_State * L)520 int l_anim_set_layer(lua_State* L) {
521   T* pAnimation = luaT_testuserdata<T>(L);
522 
523   pAnimation->set_layer(static_cast<int>(luaL_checkinteger(L, 2)),
524                         static_cast<int>(luaL_optinteger(L, 3, 0)));
525 
526   lua_settop(L, 1);
527   return 1;
528 }
529 
l_anim_set_layers_from(lua_State * L)530 int l_anim_set_layers_from(lua_State* L) {
531   animation* pAnimation = luaT_testuserdata<animation>(L);
532   const animation* pAnimationSrc =
533       luaT_testuserdata<animation>(L, 2, luaT_environindex);
534 
535   pAnimation->set_layers_from(pAnimationSrc);
536 
537   lua_settop(L, 1);
538   return 1;
539 }
540 
l_anim_set_tag(lua_State * L)541 int l_anim_set_tag(lua_State* L) {
542   luaT_testuserdata<animation>(L);
543   lua_settop(L, 2);
544   luaT_setenvfield(L, 1, "tag");
545   return 1;
546 }
547 
l_anim_get_tag(lua_State * L)548 int l_anim_get_tag(lua_State* L) {
549   luaT_testuserdata<animation>(L);
550   lua_settop(L, 1);
551   lua_getfenv(L, 1);
552   lua_getfield(L, 2, "tag");
553   return 1;
554 }
555 
l_anim_get_marker(lua_State * L)556 int l_anim_get_marker(lua_State* L) {
557   animation* pAnimation = luaT_testuserdata<animation>(L);
558   int iX = 0;
559   int iY = 0;
560   pAnimation->get_marker(&iX, &iY);
561   lua_pushinteger(L, iX);
562   lua_pushinteger(L, iY);
563   return 2;
564 }
565 
l_anim_get_secondary_marker(lua_State * L)566 int l_anim_get_secondary_marker(lua_State* L) {
567   animation* pAnimation = luaT_testuserdata<animation>(L);
568   int iX = 0;
569   int iY = 0;
570   pAnimation->get_secondary_marker(&iX, &iY);
571   lua_pushinteger(L, iX);
572   lua_pushinteger(L, iY);
573   return 2;
574 }
575 
576 template <typename T>
l_anim_tick(lua_State * L)577 int l_anim_tick(lua_State* L) {
578   T* pAnimation = luaT_testuserdata<T>(L);
579   pAnimation->tick();
580   lua_settop(L, 1);
581   return 1;
582 }
583 
584 template <typename T>
l_anim_draw(lua_State * L)585 int l_anim_draw(lua_State* L) {
586   T* pAnimation = luaT_testuserdata<T>(L);
587   render_target* pCanvas = luaT_testuserdata<render_target>(L, 2);
588   pAnimation->draw(pCanvas, static_cast<int>(luaL_checkinteger(L, 3)),
589                    static_cast<int>(luaL_checkinteger(L, 4)));
590   lua_settop(L, 1);
591   return 1;
592 }
593 
l_srl_set_sheet(lua_State * L)594 int l_srl_set_sheet(lua_State* L) {
595   sprite_render_list* pSrl = luaT_testuserdata<sprite_render_list>(L);
596   sprite_sheet* pSheet = luaT_testuserdata<sprite_sheet>(L, 2);
597   pSrl->set_sheet(pSheet);
598 
599   lua_settop(L, 2);
600   luaT_setenvfield(L, 1, "sheet");
601   return 1;
602 }
603 
l_srl_append(lua_State * L)604 int l_srl_append(lua_State* L) {
605   sprite_render_list* pSrl = luaT_testuserdata<sprite_render_list>(L);
606   pSrl->append_sprite(luaL_checkinteger(L, 2),
607                       static_cast<int>(luaL_checkinteger(L, 3)),
608                       static_cast<int>(luaL_checkinteger(L, 4)));
609   lua_settop(L, 1);
610   return 1;
611 }
612 
l_srl_set_lifetime(lua_State * L)613 int l_srl_set_lifetime(lua_State* L) {
614   sprite_render_list* pSrl = luaT_testuserdata<sprite_render_list>(L);
615   pSrl->set_lifetime(static_cast<int>(luaL_checkinteger(L, 2)));
616   lua_settop(L, 1);
617   return 1;
618 }
619 
l_srl_is_dead(lua_State * L)620 int l_srl_is_dead(lua_State* L) {
621   sprite_render_list* pSrl = luaT_testuserdata<sprite_render_list>(L);
622   lua_pushboolean(L, pSrl->is_dead() ? 1 : 0);
623   return 1;
624 }
625 
626 }  // namespace
627 
lua_register_anims(const lua_register_state * pState)628 void lua_register_anims(const lua_register_state* pState) {
629   // Anims
630   {
631     lua_class_binding<animation_manager> lcb(pState, "anims", l_anims_new,
632                                              lua_metatable::anims);
633     lcb.add_function(l_anims_load, "load");
634     lcb.add_function(l_anims_loadcustom, "loadCustom");
635     lcb.add_function(l_anims_set_spritesheet, "setSheet", lua_metatable::sheet);
636     lcb.add_function(l_anims_set_canvas, "setCanvas", lua_metatable::surface);
637     lcb.add_function(l_anims_getanims, "getAnimations");
638     lcb.add_function(l_anims_getfirst, "getFirstFrame");
639     lcb.add_function(l_anims_getnext, "getNextFrame");
640     lcb.add_function(l_anims_set_alt_pal, "setAnimationGhostPalette");
641     lcb.add_function(l_anims_set_marker, "setFrameMarker");
642     lcb.add_function(l_anims_set_secondary_marker, "setFrameSecondaryMarker");
643     lcb.add_function(l_anims_draw, "draw", lua_metatable::surface,
644                      lua_metatable::layers);
645     lcb.add_constant("Alt32_GreyScale", thdf_alt32_grey_scale);
646     lcb.add_constant("Alt32_BlueRedSwap", thdf_alt32_blue_red_swap);
647   }
648 
649   // Weak table at AnimMetatable[1] for light UD -> object lookup
650   // For hitTest / setHitTestResult
651   lua_newtable(pState->L);
652   lua_createtable(pState->L, 0, 1);
653   lua_pushliteral(pState->L, "v");
654   lua_setfield(pState->L, -2, "__mode");
655   lua_setmetatable(pState->L, -2);
656   lua_rawseti(pState->L,
657               pState->metatables[static_cast<size_t>(lua_metatable::anim)], 1);
658 
659   // Weak table at AnimMetatable[2] for light UD -> full UD lookup
660   // For persisting Map
661   lua_newtable(pState->L);
662   lua_createtable(pState->L, 0, 1);
663   lua_pushliteral(pState->L, "v");
664   lua_setfield(pState->L, -2, "__mode");
665   lua_setmetatable(pState->L, -2);
666   lua_rawseti(pState->L,
667               pState->metatables[static_cast<size_t>(lua_metatable::anim)], 2);
668 
669   // Anim
670   {
671     lua_class_binding<animation> lcb(pState, "animation", l_anim_new<animation>,
672                                      lua_metatable::anim);
673     lcb.add_metamethod(l_anim_persist<animation>, "persist");
674     lcb.add_metamethod(l_anim_pre_depersist<animation>, "pre_depersist");
675     lcb.add_metamethod(l_anim_depersist<animation>, "depersist");
676     lcb.add_function(l_anim_set_anim, "setAnimation", lua_metatable::anims);
677     lcb.add_function(l_anim_set_crop, "setCrop");
678     lcb.add_function(l_anim_get_crop, "getCrop");
679     lcb.add_function(l_anim_set_morph, "setMorph");
680     lcb.add_function(l_anim_set_frame, "setFrame");
681     lcb.add_function(l_anim_get_frame, "getFrame");
682     lcb.add_function(l_anim_get_anim, "getAnimation");
683     lcb.add_function(l_anim_set_tile<animation>, "setTile", lua_metatable::map);
684     lcb.add_function(l_anim_get_tile, "getTile");
685     lcb.add_function(l_anim_set_parent, "setParent");
686     lcb.add_function(l_anim_set_flag<animation>, "setFlag");
687     lcb.add_function(l_anim_set_flag_partial<animation>, "setPartialFlag");
688     lcb.add_function(l_anim_get_flag<animation>, "getFlag");
689     lcb.add_function(l_anim_make_visible<animation>, "makeVisible");
690     lcb.add_function(l_anim_make_invisible<animation>, "makeInvisible");
691     lcb.add_function(l_anim_set_tag, "setTag");
692     lcb.add_function(l_anim_get_tag, "getTag");
693     lcb.add_function(l_anim_set_position<animation>, "setPosition");
694     lcb.add_function(l_anim_get_position, "getPosition");
695     lcb.add_function(l_anim_set_speed<animation>, "setSpeed");
696     lcb.add_function(l_anim_set_layer<animation>, "setLayer");
697     lcb.add_function(l_anim_set_layers_from, "setLayersFrom");
698     lcb.add_function(l_anim_set_hitresult, "setHitTestResult");
699     lcb.add_function(l_anim_get_marker, "getMarker");
700     lcb.add_function(l_anim_get_secondary_marker, "getSecondaryMarker");
701     lcb.add_function(l_anim_tick<animation>, "tick");
702     lcb.add_function(l_anim_draw<animation>, "draw", lua_metatable::surface);
703     lcb.add_function(l_anim_set_drawable_layer, "setDrawingLayer");
704   }
705 
706   // Duplicate AnimMetatable[1,2] to SpriteListMetatable[1,2]
707   lua_rawgeti(pState->L,
708               pState->metatables[static_cast<size_t>(lua_metatable::anim)], 1);
709   lua_rawseti(
710       pState->L,
711       pState->metatables[static_cast<size_t>(lua_metatable::sprite_list)], 1);
712   lua_rawgeti(pState->L,
713               pState->metatables[static_cast<size_t>(lua_metatable::anim)], 2);
714   lua_rawseti(
715       pState->L,
716       pState->metatables[static_cast<size_t>(lua_metatable::sprite_list)], 2);
717 
718   // SpriteList
719   {
720     lua_class_binding<sprite_render_list> lcb(pState, "spriteList",
721                                               l_anim_new<sprite_render_list>,
722                                               lua_metatable::sprite_list);
723     lcb.add_metamethod(l_anim_persist<sprite_render_list>, "persist");
724     lcb.add_metamethod(l_anim_pre_depersist<sprite_render_list>,
725                        "pre_depersist");
726     lcb.add_metamethod(l_anim_depersist<sprite_render_list>, "depersist");
727     lcb.add_function(l_srl_set_sheet, "setSheet", lua_metatable::sheet);
728     lcb.add_function(l_srl_append, "append");
729     lcb.add_function(l_srl_set_lifetime, "setLifetime");
730     lcb.add_function(l_srl_is_dead, "isDead");
731     lcb.add_function(l_anim_set_tile<sprite_render_list>, "setTile",
732                      lua_metatable::map);
733     lcb.add_function(l_anim_set_flag<sprite_render_list>, "setFlag");
734     lcb.add_function(l_anim_set_flag_partial<sprite_render_list>,
735                      "setPartialFlag");
736     lcb.add_function(l_anim_get_flag<sprite_render_list>, "getFlag");
737     lcb.add_function(l_anim_make_visible<sprite_render_list>, "makeVisible");
738     lcb.add_function(l_anim_make_invisible<sprite_render_list>,
739                      "makeInvisible");
740     lcb.add_function(l_anim_set_position<sprite_render_list>, "setPosition");
741     lcb.add_function(l_anim_set_speed<sprite_render_list>, "setSpeed");
742     lcb.add_function(l_anim_set_layer<sprite_render_list>, "setLayer");
743     lcb.add_function(l_anim_tick<sprite_render_list>, "tick");
744     lcb.add_function(l_anim_draw<sprite_render_list>, "draw",
745                      lua_metatable::surface);
746   }
747 }
748