1 /*
2  * Copyright (C) 2006-2019 Christopho, Solarus - http://www.solarus-games.org
3  *
4  * Solarus is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * Solarus is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 #include "solarus/core/Debug.h"
18 #include "solarus/core/Game.h"
19 #include "solarus/core/Map.h"
20 #include "solarus/core/PixelBits.h"
21 #include "solarus/core/Size.h"
22 #include "solarus/core/System.h"
23 #include "solarus/graphics/Color.h"
24 #include "solarus/graphics/Sprite.h"
25 #include "solarus/graphics/SpriteAnimation.h"
26 #include "solarus/graphics/SpriteAnimationDirection.h"
27 #include "solarus/graphics/SpriteAnimationSet.h"
28 #include "solarus/graphics/Surface.h"
29 #include "solarus/graphics/Shader.h"
30 #include "solarus/lua/LuaContext.h"
31 #include "solarus/lua/LuaTools.h"
32 #include "solarus/movements/Movement.h"
33 #include <lua.hpp>
34 #include <limits>
35 #include <memory>
36 #include <sstream>
37 #include <iostream>
38 
39 namespace Solarus {
40 
41 std::map<std::string, SpriteAnimationSet*> Sprite::all_animation_sets;
42 
43 /**
44  * \brief Initializes the sprites system.
45  */
initialize()46 void Sprite::initialize() {
47 }
48 
49 /**
50  * \brief Uninitializes the sprites system.
51  */
quit()52 void Sprite::quit() {
53 
54   // delete the animations loaded
55   for (auto& kvp: all_animation_sets) {
56     delete kvp.second;
57   }
58   all_animation_sets.clear();
59 }
60 
61 /**
62  * \brief Returns the sprite animation set corresponding to the specified id.
63  *
64  * The animation set may be created if it is new, or just retrieved from
65  * memory if it way already used before.
66  *
67  * \param id id of the animation set
68  * \return the corresponding animation set
69  */
get_animation_set(const std::string & id)70 SpriteAnimationSet& Sprite::get_animation_set(const std::string& id) {
71 
72   SpriteAnimationSet* animation_set = nullptr;
73   auto it = all_animation_sets.find(id);
74   if (it != all_animation_sets.end()) {
75     animation_set = it->second;
76   }
77   else {
78     animation_set = new SpriteAnimationSet(id);
79     all_animation_sets[id] = animation_set;
80   }
81 
82   Debug::check_assertion(animation_set != nullptr, "No animation set");
83 
84   return *animation_set;
85 }
86 
87 /**
88  * \brief Creates a sprite with the specified animation set.
89  * \param id name of an animation set
90  */
Sprite(const std::string & id)91 Sprite::Sprite(const std::string& id):
92   Drawable(),
93   animation_set_id(id),
94   animation_set(get_animation_set(id)),
95   current_animation(nullptr),
96   current_direction(0),
97   current_frame(-1),
98   frame_changed(false),
99   frame_delay(0),
100   next_frame_date(0),
101   ignore_suspend(false),
102   paused(false),
103   finished(false),
104   synchronize_to(nullptr),
105   blink_delay(0),
106   blink_is_sprite_visible(true),
107   blink_next_change_date(0),
108   finished_callback_ref() {
109 
110   set_current_animation(animation_set.get_default_animation());
111 }
112 
113 /**
114  * \brief Returns the id of the animation set of this sprite.
115  * \return the animation set id of this sprite
116  */
get_animation_set_id() const117 const std::string& Sprite::get_animation_set_id() const {
118   return animation_set_id;
119 }
120 
121 /**
122  * \brief Returns the animation set of this sprite.
123  *
124  * If several sprites have the same animation set, they share the same instance of animation set
125  * and the same pointer is returned here.
126  *
127  * \return the animation set of this sprite
128  */
get_animation_set() const129 const SpriteAnimationSet& Sprite::get_animation_set() const {
130   return animation_set;
131 }
132 
133 /**
134  * \brief When the sprite is drawn on a map, sets the tileset.
135  *
136  * This function must be called if this sprite image depends on the map's tileset.
137  *
138  * \param tileset The tileset.
139  */
set_tileset(const Tileset & tileset)140 void Sprite::set_tileset(const Tileset& tileset) {
141   animation_set.set_tileset(tileset);
142 }
143 
144 /**
145  * \brief Enables the pixel-perfect collision detection for the animation set of this sprite.
146  *
147  * All sprites that use the same animation set as this one will be affected.
148  */
enable_pixel_collisions()149 void Sprite::enable_pixel_collisions() {
150   animation_set.enable_pixel_collisions();
151 }
152 
153 /**
154  * \brief Returns whether the pixel-perfect collisions are enabled for the animation set of this sprite.
155  * \return true if the pixel-perfect collisions are enabled
156  */
are_pixel_collisions_enabled() const157 bool Sprite::are_pixel_collisions_enabled() const {
158   return animation_set.are_pixel_collisions_enabled();
159 }
160 
161 /**
162  * \brief Returns the size of the current frame.
163  * \return The size of the current frame.
164  */
get_size() const165 Size Sprite::get_size() const {
166 
167   if (current_animation == nullptr) {
168     return Size();
169   }
170   return current_animation->get_direction(current_direction).get_size();
171 }
172 
173 /**
174  * \brief Returns the maximum frame size of the animation set of this sprite.
175  * \return The maximum frame size.
176  */
get_max_size() const177 const Size& Sprite::get_max_size() const {
178   return animation_set.get_max_size();
179 }
180 
181 /**
182  * \brief Returns the origin point of a frame for the current animation and
183  * the current direction.
184  * \return The origin point of a frame.
185  */
get_origin() const186 Point Sprite::get_origin() const {
187 
188   if (current_animation == nullptr) {
189     return Point();
190   }
191 
192   return current_animation->get_direction(current_direction).get_origin();
193 }
194 
195 /**
196  * \brief Returns a rectangle big enough to contain a frame of any animation
197  * and direction.
198  *
199  * The rectangle is anchored to the origin point of the sprite,
200  * so it has has negative top and left coordinates.
201  *
202  * \return The maximum frame size.
203  */
get_max_bounding_box() const204 const Rectangle& Sprite::get_max_bounding_box() const {
205 
206   return animation_set.get_max_bounding_box();
207 }
208 
209 /**
210  * \brief Returns the frame delay of the current animation.
211  *
212  * A value of 0 (only for 1-frame animations) means that the
213  * animation must continue to be drawn: in this case,
214  * is_animation_finished() always returns false.
215  *
216  * \return The delay between two frames for the current animation
217  * in milliseconds.
218  */
get_frame_delay() const219 uint32_t Sprite::get_frame_delay() const {
220   return frame_delay;
221 }
222 
223 /**
224  * \brief Sets the frame delay of the current animation.
225  *
226  * A value of 0 (only for 1-frame animations) means that the
227  * animation will continue to be drawn.
228  *
229  * \param frame_delay The delay between two frames for the current animation
230  * in milliseconds.
231  */
set_frame_delay(uint32_t frame_delay)232 void Sprite::set_frame_delay(uint32_t frame_delay) {
233   this->frame_delay = frame_delay;
234 }
235 
236 /**
237  * \brief Returns the date if the next frame change.
238  * \return The next frame date or 0 if there is no frame change scheduled.
239  */
get_next_frame_date() const240 uint32_t Sprite::get_next_frame_date() const {
241   return next_frame_date;
242 }
243 
244 
245 /**
246  * \brief Returns the next frame of the current frame.
247  * \return The next frame of the current frame, or -1 if the animation is
248  * finished.
249  */
get_next_frame() const250 int Sprite::get_next_frame() const {
251 
252   if (current_animation == nullptr) {
253     return -1;
254   }
255 
256   return current_animation->get_next_frame(current_direction, current_frame);
257 }
258 
259 /**
260  * \brief Returns the current animation of the sprite.
261  * \return the name of the current animation of the sprite
262  */
get_current_animation() const263 const std::string& Sprite::get_current_animation() const {
264   return current_animation_name;
265 }
266 
267 /**
268  * \brief Sets the current animation of the sprite.
269  *
270  * If the sprite is already playing another animation, this animation is interrupted.
271  * If the sprite is already playing the same animation, nothing is done.
272  *
273  * \param animation_name name of the new animation of the sprite
274  */
set_current_animation(const std::string & animation_name)275 void Sprite::set_current_animation(const std::string& animation_name) {
276 
277   if (animation_name != this->current_animation_name || !is_animation_started()) {
278 
279     this->current_animation_name = animation_name;
280     if (animation_set.has_animation(animation_name)) {
281       this->current_animation = &animation_set.get_animation(animation_name);
282       set_frame_delay(current_animation->get_frame_delay());
283     }
284     else {
285       this->current_animation = nullptr;
286     }
287 
288     int old_direction = this->current_direction;
289     if (current_direction < 0
290         || current_direction >= get_nb_directions()) {
291       current_direction = 0;
292     }
293 
294     set_current_frame(0, false);
295     set_finished_callback(ScopedLuaRef());
296 
297     LuaContext* lua_context = get_lua_context();
298     if (lua_context != nullptr) {
299       lua_context->sprite_on_animation_changed(*this, current_animation_name);
300       if (current_direction != old_direction) {
301         lua_context->sprite_on_direction_changed(*this, current_animation_name, current_direction);
302       }
303       lua_context->sprite_on_frame_changed(*this, current_animation_name, 0);
304     }
305   }
306 }
307 
308 /**
309  * \brief Returns whether this sprite has an animation with the specified name.
310  * \param animation_name an animation name
311  * \return true if this animation exists
312  */
has_animation(const std::string & animation_name) const313 bool Sprite::has_animation(const std::string& animation_name) const {
314   return animation_set.has_animation(animation_name);
315 }
316 
317 /**
318  * \brief Returns the number of directions in the current animation of this
319  * sprite.
320  * \return The number of directions.
321  */
get_nb_directions() const322 int Sprite::get_nb_directions() const {
323 
324   if (current_animation == nullptr) {
325     return 0;
326   }
327   return current_animation->get_nb_directions();
328 }
329 
330 /**
331  * \brief Returns the current direction of the sprite's animation.
332  * \return the current direction
333  */
get_current_direction() const334 int Sprite::get_current_direction() const {
335   return current_direction;
336 }
337 
338 /**
339  * \brief Sets the current direction of the sprite's animation and restarts the animation.
340  *
341  * If the specified direction is the current direction, nothing is done.
342  *
343  * \param current_direction the current direction
344  */
set_current_direction(int current_direction)345 void Sprite::set_current_direction(int current_direction) {
346 
347   if (current_direction != this->current_direction) {
348 
349     if (current_direction < 0
350         || current_direction >= get_nb_directions()) {
351       std::ostringstream oss;
352       oss << "Illegal direction " << current_direction
353           << " for sprite '" << get_animation_set_id()
354           << "' in animation '" << current_animation_name << "'";
355       Debug::error(oss.str());
356       return;
357     }
358 
359     this->current_direction = current_direction;
360 
361     set_current_frame(0, false);
362 
363     LuaContext* lua_context = get_lua_context();
364     if (lua_context != nullptr) {
365       lua_context->sprite_on_direction_changed(*this, current_animation_name, current_direction);
366       lua_context->sprite_on_frame_changed(*this, current_animation_name, 0);
367     }
368   }
369 }
370 
371 /**
372  * \brief Returns the number of frames in the current direction of the current
373  * animation of this sprite.
374  * \return The number of frames.
375  */
get_nb_frames() const376 int Sprite::get_nb_frames() const {
377 
378   if (current_animation == nullptr) {
379     return 0;
380   }
381   return current_animation->get_direction(current_direction).get_nb_frames();
382 }
383 
384 /**
385  * \brief Returns the current frame of the sprite's animation.
386  * \return the current frame
387  */
get_current_frame() const388 int Sprite::get_current_frame() const {
389   return current_frame;
390 }
391 
392 /**
393  * \brief Sets the current frame of the sprite's animation.
394  *
395  * If the animation was finished, it is restarted.
396  * If the animation is suspended, it remains suspended
397  * but the specified frame is drawn.
398  *
399  * \param current_frame The current frame.
400  * \param notify_script \c true to notify the Lua sprite if any.
401  * Don't set notify_script to \c false unless you do the notification
402  * yourself later.
403  */
set_current_frame(int current_frame,bool notify_script)404 void Sprite::set_current_frame(int current_frame, bool notify_script) {
405 
406   finished = false;
407   next_frame_date = System::now() + get_frame_delay();
408 
409   if (current_frame != this->current_frame) {
410     this->current_frame = current_frame;
411     set_frame_changed(true);
412 
413     if (notify_script) {
414       LuaContext* lua_context = get_lua_context();
415       if (lua_context != nullptr) {
416         lua_context->sprite_on_frame_changed(
417             *this, current_animation_name, current_frame);
418       }
419     }
420   }
421 }
422 
423 /**
424  * \brief Returns the rectangle of the current frame.
425  * \return The current frame's rectangle.
426  */
get_current_frame_rectangle() const427 Rectangle Sprite::get_current_frame_rectangle() const {
428 
429   if (current_animation == nullptr) {
430     return Rectangle();
431   }
432 
433   return current_animation->get_direction(current_direction).get_frame(current_frame);
434 }
435 
436 /**
437  * \brief Returns whether the frame of this sprite has just changed.
438  * \return true if the frame of this sprite has just changed.
439  */
has_frame_changed() const440 bool Sprite::has_frame_changed() const {
441   return frame_changed;
442 }
443 
444 /**
445  * \brief Sets whether the frame has just changed.
446  * \param frame_changed true if the frame has just changed.
447  */
set_frame_changed(bool frame_changed)448 void Sprite::set_frame_changed(bool frame_changed) {
449 
450   this->frame_changed = frame_changed;
451 }
452 
453 /**
454  * \brief Makes this sprite always synchronized with another one as soon as
455  * they have the same animation name.
456  * \param other the sprite to synchronize to, or nullptr to stop any previous synchronization
457  */
set_synchronized_to(const SpritePtr & other)458 void Sprite::set_synchronized_to(const SpritePtr& other) {
459   this->synchronize_to = other;
460 }
461 
462 /**
463  * \brief Returns true if the animation is started.
464  *
465  * It can be suspended.
466  *
467  * \return true if the animation is started, false otherwise
468  */
is_animation_started() const469 bool Sprite::is_animation_started() const {
470   return !is_animation_finished();
471 }
472 
473 /**
474  * \brief Starts the animation.
475  */
start_animation()476 void Sprite::start_animation() {
477   restart_animation();
478 }
479 
480 /**
481  * \brief Restarts the animation.
482  */
restart_animation()483 void Sprite::restart_animation() {
484   set_current_frame(0);
485   set_paused(false);
486 }
487 
488 /**
489  * \brief Stops the animation.
490  */
stop_animation()491 void Sprite::stop_animation() {
492   finished = true;
493 }
494 
495 /**
496  * \brief Suspends or resumes the animation.
497  *
498  * Nothing is done if the parameter specified does not change.
499  *
500  * \param suspended true to suspend the animation, false to resume it
501  */
set_suspended(bool suspended)502 void Sprite::set_suspended(bool suspended) {
503 
504   if (suspended != is_suspended() &&
505       !ignore_suspend) {
506 
507     Drawable::set_suspended(suspended);
508 
509     // compte next_frame_date if the animation is being resumed
510     if (!suspended) {
511       uint32_t now = System::now();
512       next_frame_date = now + get_frame_delay();
513       blink_next_change_date = now;
514     }
515     else {
516       blink_is_sprite_visible = true;
517     }
518   }
519 }
520 
521 /**
522  * \brief Returns whether this sprite keeps playing when the game is suspended.
523  * \return \c true if the sprite continues its animation even when the game is
524  * suspended.
525  */
get_ignore_suspend() const526 bool Sprite::get_ignore_suspend() const {
527   return ignore_suspend;
528 }
529 
530 /**
531  * \brief Sets whether this sprite should keep playing its animation when the game is suspended.
532  *
533  * This will ignore subsequent calls to set_suspended().
534  *
535  * \param ignore_suspend true to make the sprite continue its animation even
536  * when the game is suspended
537  */
set_ignore_suspend(bool ignore_suspend)538 void Sprite::set_ignore_suspend(bool ignore_suspend) {
539   set_suspended(false);
540   this->ignore_suspend = ignore_suspend;
541 }
542 
543 /**
544  * \brief Returns true if the animation is paused.
545  * \return true if the animation is paused, false otherwise
546  */
is_paused() const547 bool Sprite::is_paused() const {
548   return paused;
549 }
550 
551 /**
552  * \brief Pauses or resumes the animation.
553  *
554  * Nothing is done if the parameter specified does not change.
555  *
556  * \param paused true to pause the animation, false to resume it
557  */
set_paused(bool paused)558 void Sprite::set_paused(bool paused) {
559 
560   if (paused != this->paused) {
561     this->paused = paused;
562 
563     // compte next_frame_date if the animation is being resumed
564     if (!paused) {
565       uint32_t now = System::now();
566       next_frame_date = now + get_frame_delay();
567       blink_next_change_date = now;
568     }
569     else {
570       blink_is_sprite_visible = true;
571     }
572   }
573 }
574 
575 /**
576  * \brief Returns true if the animation is looping.
577  * \return true if the animation is looping
578  */
is_animation_looping() const579 bool Sprite::is_animation_looping() const {
580 
581   if (current_animation == nullptr) {
582     return false;
583   }
584   return current_animation->is_looping();
585 }
586 
587 /**
588  * \brief Returns true if the animation is finished.
589  *
590  * The animation is finished after the last frame is reached
591  * and if the frame delay is not zero (a frame delay
592  * of zero should be used only for 1-frame animations).
593  *
594  * \return true if the animation is finished
595  */
is_animation_finished() const596 bool Sprite::is_animation_finished() const {
597   return finished;
598 }
599 
600 /**
601  * \brief Returns true if the last frame is reached.
602  * \return true if the last frame is reached
603  */
is_last_frame_reached() const604 bool Sprite::is_last_frame_reached() const {
605 
606   return get_current_frame() == get_nb_frames() - 1;
607 }
608 
609 /**
610  * \brief Returns whether the sprite is blinking.
611  * \return true if the sprite is blinking
612  */
is_blinking() const613 bool Sprite::is_blinking() const {
614   return blink_delay != 0;
615 }
616 
617 /**
618  * \brief Sets the blink delay of this sprite.
619  * \param blink_delay Blink delay of the sprite in milliseconds,
620  * or zero to stop blinking.
621  */
set_blinking(uint32_t blink_delay)622 void Sprite::set_blinking(uint32_t blink_delay) {
623   this->blink_delay = blink_delay;
624 
625   if (blink_delay > 0) {
626     blink_is_sprite_visible = false;
627     blink_next_change_date = System::now();
628   }
629 }
630 
631 /**
632  * \brief Tests whether this sprite's pixels are overlapping another sprite.
633  * \param other Another sprite.
634  * \param x1 X coordinate of this sprite's origin point on the map,
635  * before applying its current movement if any.
636  * \param y1 Y coordinate of this sprite's origin point on the map,
637  * before applying its current movement if any.
638  * \param x2 X coordinate of the other sprite's origin point on the map,
639  * before applying its current movement if any.
640  * \param y2 Y coordinate of the other sprite's origin point on the map,
641  * before applying its current movement if any.
642  * \return \c true if the sprites are overlapping.
643  */
test_collision(const Sprite & other,int x1,int y1,int x2,int y2) const644 bool Sprite::test_collision(const Sprite& other, int x1, int y1, int x2, int y2) const {
645 
646   if (current_animation == nullptr || other.current_animation == nullptr) {
647     return false;
648   }
649 
650   if (!is_animation_started() || !other.is_animation_started()) {
651     // The animation is not running.
652     return false;
653   }
654 
655   if (!are_pixel_collisions_enabled()) {
656     Debug::error(std::string("Pixel-precise collisions are not enabled for sprite '") +
657                  get_animation_set_id() + "'");
658     return false;
659   }
660 
661   if (!other.are_pixel_collisions_enabled()) {
662     Debug::error(std::string("Pixel-precise collisions are not enabled for sprite '") +
663                  other.get_animation_set_id() + "'");
664     return false;
665   }
666 
667   const SpriteAnimationDirection& direction1 = current_animation->get_direction(current_direction);
668   const Point& origin1 = direction1.get_origin();
669   Point location1 = { x1 - origin1.x, y1 - origin1.y };
670   location1 += get_xy();
671   const PixelBits& pixel_bits1 = direction1.get_pixel_bits(current_frame);
672 
673   const SpriteAnimationDirection& direction2 = other.current_animation->get_direction(other.current_direction);
674   const Point& origin2 = direction2.get_origin();
675   Point location2 = { x2 - origin2.x, y2 - origin2.y };
676   location2 += other.get_xy();
677   const PixelBits& pixel_bits2 = direction2.get_pixel_bits(other.current_frame);
678 
679   return pixel_bits1.test_collision(pixel_bits2,
680                                     Transform(location1,get_origin(),get_scale(),get_rotation()),
681                                     Transform(location2,other.get_origin(),other.get_scale(),other.get_rotation()));
682 }
683 
684 /**
685  * \brief Checks whether the frame has to be changed.
686  *
687  * If the frame changes, next_frame_date is updated.
688  */
update()689 void Sprite::update() {
690 
691   Drawable::update();
692 
693   if (is_suspended() || paused) {
694     return;
695   }
696 
697   LuaContext* lua_context = get_lua_context();
698 
699   frame_changed = false;
700   uint32_t now = System::now();
701 
702   // Update the current frame.
703   if (synchronize_to == nullptr
704       || current_animation_name != synchronize_to->get_current_animation()
705       || synchronize_to->get_current_direction() > get_nb_directions()
706       || synchronize_to->get_current_frame() > get_nb_frames()) {
707 
708     // Update frames normally (with time).
709     while (!finished &&
710         !is_suspended() &&
711         !paused &&
712         get_frame_delay() > 0 &&
713         now >= next_frame_date
714     ) {
715       int next_frame = get_next_frame();
716 
717       // Test if the animation is finished.
718       if (next_frame == -1) {
719         finished = true;
720         notify_finished();
721       }
722       else {
723         current_frame = next_frame;
724         uint32_t old_next_frame_date = next_frame_date;
725         next_frame_date += get_frame_delay();
726         if (next_frame_date < old_next_frame_date) {
727           // Overflow. Can happen with data files generated with the editor
728           // before 1.4 (frame_delay is too big).
729           next_frame_date = std::numeric_limits<uint32_t>::max();
730         }
731       }
732       set_frame_changed(true);
733 
734       if (lua_context != nullptr) {
735         lua_context->sprite_on_frame_changed(*this, current_animation_name, current_frame);
736       }
737     }
738   }
739   else {
740     // Take the same frame as the other sprite.
741     if (synchronize_to->is_animation_finished()) {
742       finished = true;
743       notify_finished();
744     }
745     else {
746       int other_frame = synchronize_to->get_current_frame();
747       if (other_frame != current_frame) {
748         current_frame = other_frame;
749         next_frame_date = now + get_frame_delay();
750         set_frame_changed(true);
751 
752         if (lua_context != nullptr) {
753           lua_context->sprite_on_frame_changed(*this, current_animation_name, current_frame);
754         }
755       }
756     }
757   }
758 
759   // Update the special effects.
760   if (is_blinking()) {
761     // The sprite is blinking.
762 
763     while (now >= blink_next_change_date) {
764       blink_is_sprite_visible = !blink_is_sprite_visible;
765       blink_next_change_date += blink_delay;
766     }
767   }
768 }
769 
770 /**
771  * @brief Sprite::draw_intermediate
772  * @param region
773  * @param dst_surface
774  * @param dst_position
775  */
776 /*void Sprite::draw_intermediate() const {
777     get_intermediate_surface().clear();
778     current_animation->draw(
779         get_intermediate_surface(),
780         get_origin(),
781         current_direction,
782         current_frame);
783 }*/
784 
785 /**
786  * \brief Draws the sprite on a surface, with its current animation,
787  * direction and frame.
788  * \param dst_surface The destination surface.
789  * \param infos draw infos, region is ignored in this case
790  */
raw_draw(Surface & dst_surface,const DrawInfos & infos) const791 void Sprite::raw_draw(Surface& dst_surface,const DrawInfos& infos) const {
792 
793   if (current_animation == nullptr) {
794     return;
795   }
796 
797   if (!is_animation_finished()
798       && (blink_delay == 0 || blink_is_sprite_visible)) {
799 
800     current_animation->draw(
801           dst_surface,
802           infos.dst_position,
803           current_direction,
804           current_frame,
805           infos);
806   }
807 }
808 
809 /**
810  * @brief compute region of the spritesheet to draw given crop region
811  * @param region
812  * @return clipped region
813  */
clamp_region(const Rectangle & region) const814 Rectangle Sprite::clamp_region(const Rectangle& region) const {
815   Rectangle src_position(region);
816   const Point& origin = get_origin();
817   src_position.add_xy(origin);
818   const Size& frame_size = get_size();
819   if (src_position.get_x() < 0) {
820     src_position.set_width(src_position.get_width() + src_position.get_x());
821     src_position.set_x(0);
822   }
823   if (src_position.get_x() + src_position.get_width() > frame_size.width) {
824     src_position.set_width(frame_size.width - src_position.get_x());
825   }
826   if (src_position.get_y() < 0) {
827     src_position.set_height(src_position.get_height() + src_position.get_y());
828     src_position.set_y(0);
829   }
830   if (src_position.get_y() + src_position.get_height() > frame_size.height) {
831     src_position.set_height(frame_size.height - src_position.get_y());
832   }
833   return src_position;
834 }
835 
836 /**
837  * \brief Draws a subrectangle of the current frame of this sprite.
838  * \param region The subrectangle to draw, relative to the origin point.
839  * It may be bigger than the frame: in this case it will be clipped.
840  * \param dst_surface The destination surface.
841  * \param dst_position Coordinates on the destination surface.
842  * The origin point of the sprite will appear at these coordinates.
843  */
raw_draw_region(Surface & dst_surface,const DrawInfos & infos) const844 void Sprite::raw_draw_region(Surface& dst_surface, const DrawInfos& infos) const {
845   if (current_animation == nullptr) {
846     return;
847   }
848 
849   if (!is_animation_finished()
850       && (blink_delay == 0 || blink_is_sprite_visible)) {
851 
852     //draw_intermediate();
853 
854     // If the region is bigger than the current frame, clip it.
855     // Otherwise, more than the current frame could be visible.
856     Rectangle src_position = clamp_region(infos.region);
857 
858 
859     struct CropProxy : DrawProxy {
860       CropProxy(const Rectangle& r, const Point& p) : crop_region(r), origin(p) {}
861       void draw(Surface& dst_surface, const Surface& src_surface, const DrawInfos& infos) const override {
862         //Compute area to keep in sprite sheet
863         Rectangle crop_win = Rectangle(crop_region.get_xy()+infos.region.get_xy(),crop_region.get_bottom_right()+infos.region.get_xy());
864 
865         //Adapt destination
866         Point dest = infos.dst_position+crop_region.get_xy();
867 
868         //Use given Proxy to draw result
869         //infos.proxy.draw(dst_surface,src_surface,infos);
870         infos.proxy.draw(dst_surface,src_surface,DrawInfos(infos,crop_win&infos.region,dest));
871       }
872       const Rectangle& crop_region;
873       const Point origin;
874     };
875 
876     //Instantiate crop proxy with current draw parameters
877     CropProxy cropProxy{
878       src_position,
879       get_origin()
880     };
881 
882     current_animation->draw(
883           dst_surface,
884           infos.dst_position,
885           current_direction,
886           current_frame,
887           DrawInfos(infos,DrawProxyChain<2>(DrawProxyChain<2>::Proxies{{cropProxy,infos.proxy}})));
888   }
889 }
890 
get_region() const891 Rectangle Sprite::get_region() const {
892   return Rectangle(-get_origin(),get_size());
893 }
894 
895 /**
896  * \brief Draws a transition effect on this drawable object.
897  * \param transition The transition effect to apply.
898  */
899 /*void Sprite::draw_transition(Transition& transition) {
900   //TODO reinvent that
901   //transition.draw(get_intermediate_surface());
902 }*/
903 
904 /**
905  * \brief Returns the surface where transitions on this drawable object
906  * are applied.
907  * \return The surface for transitions.
908  */
909 /*Surface& Sprite::get_transition_surface() {
910   //TODO meh
911   return get_intermediate_surface();
912 }*/
913 
914 /**
915  * \brief Returns the intermediate surface used for transitions and other
916  * effects for this sprite.
917  *
918  * Creates this intermediate surface if it does not exist yet.
919  *
920  * \return The intermediate surface of this sprite.
921  */
get_intermediate_surface() const922 Surface& Sprite::get_intermediate_surface() const {
923 
924   if (intermediate_surface == nullptr) {
925     intermediate_surface = Surface::create(get_max_size(),true);
926   }
927   return *intermediate_surface;
928 }
929 
930 /**
931  * \brief Returns the Lua registry ref to what to do when the current
932  * animation finishes.
933  * \return A Lua ref to a function or string (the name of an animation),
934  * or an empty ref.
935  */
get_finished_callback() const936 const ScopedLuaRef& Sprite::get_finished_callback() const {
937   return finished_callback_ref;
938 }
939 
940 /**
941  * \brief Sets what to do when the current animation finishes.
942  * \param finished_callback_ref A Lua ref to a function or string
943  * (the name of an animation), or an empty ref.
944  */
set_finished_callback(const ScopedLuaRef & finished_callback_ref)945 void Sprite::set_finished_callback(const ScopedLuaRef& finished_callback_ref) {
946 
947   if (!finished_callback_ref.is_empty()) {
948     Debug::check_assertion(get_lua_context() != nullptr, "Undefined Lua context");
949   }
950 
951   this->finished_callback_ref = finished_callback_ref;
952 }
953 
954 /**
955  * \brief Returns the name identifying this type in Lua.
956  * \return the name identifying this type in Lua
957  */
get_lua_type_name() const958 const std::string& Sprite::get_lua_type_name() const {
959   return LuaContext::sprite_module_name;
960 }
961 
962 /**
963  * \brief Performs appropriate notifications when the current animation finishes.
964  */
notify_finished()965 void Sprite::notify_finished() {
966 
967   LuaContext* lua_context = get_lua_context();
968   if (lua_context != nullptr) {
969     lua_State* l = lua_context->get_internal_state();
970 
971     // Sprite callback.
972     if (!finished_callback_ref.is_empty()) {
973       // The callback may be a function or a string.
974       finished_callback_ref.push(l);
975       finished_callback_ref.clear();
976       if (lua_isstring(l, -1)) {
977         // Name of a next animation to set.
978         std::string animation = lua_tostring(l, -1);
979         lua_pop(l, 1);
980         set_current_animation(animation);
981       }
982       else {
983         // Function to call.
984         LuaTools::call_function(l, 0, 0, "sprite callback");
985       }
986     }
987 
988     // Sprite event.
989     lua_context->sprite_on_animation_finished(*this, current_animation_name);
990   }
991 
992 }
993 
994 }
995 
996