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