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/entities/Tileset.h"
19 #include "solarus/graphics/SpriteAnimation.h"
20 #include "solarus/graphics/SpriteAnimationDirection.h"
21 #include "solarus/graphics/Surface.h"
22 #include <sstream>
23 
24 namespace Solarus {
25 
26 /**
27  * \brief Constructor.
28  * \param image_file_name The image from which the frames are extracted.
29  * \param directions The image sequence of each direction.
30  * \param frame_delay Delay in millisecond between two frames for this sprite animation
31  * (or 0 to make no animation, for example when you have only one frame).
32  * \param loop_on_frame Frame to loop on after the last frame (or -1 to make no loop).
33  */
SpriteAnimation(const std::string & image_file_name,const std::vector<SpriteAnimationDirection> & directions,uint32_t frame_delay,int loop_on_frame)34 SpriteAnimation::SpriteAnimation(
35     const std::string& image_file_name,
36     const std::vector<SpriteAnimationDirection>& directions,
37     uint32_t frame_delay,
38     int loop_on_frame):
39 
40   src_image(nullptr),
41   src_image_is_tileset(image_file_name == "tileset"),
42   directions(directions),
43   frame_delay(frame_delay),
44   loop_on_frame(loop_on_frame),
45   should_enable_pixel_collisions(false) {
46 
47   if (!src_image_is_tileset) {
48     src_image = Surface::create(image_file_name);
49     if (src_image == nullptr) {
50       Debug::error(std::string("Cannot load sprite image '" + image_file_name + "'"));
51     };
52   }
53 }
54 
55 /**
56  * \brief When the sprite is displayed on a map, sets the tileset.
57  *
58  * This function must be called if this sprite image depends on the map's tileset.
59  *
60  * \param tileset The tileset.
61  */
set_tileset(const Tileset & tileset)62 void SpriteAnimation::set_tileset(const Tileset& tileset) {
63 
64   if (!src_image_is_tileset) {
65     // Nothing to do when the tileset changes.
66     return;
67   }
68 
69   src_image = tileset.get_entities_image();
70   if (src_image == nullptr) {
71     std::string file_name = std::string("tilesets/") + tileset.get_id() + ".entities.png";
72     Debug::error(std::string("Missing sprites image for tileset '") + tileset.get_id() + "': " + file_name);
73   }
74 
75   if (should_enable_pixel_collisions) {
76     disable_pixel_collisions();  // To force creating the images again.
77     do_enable_pixel_collisions();
78   }
79 }
80 
81 /**
82  * \brief Returns the number of directions of this animation.
83  * \return The number of directions.
84  */
get_nb_directions() const85 int SpriteAnimation::get_nb_directions() const {
86   return directions.size();
87 }
88 
89 /**
90  * \brief Returns the delay between two frames for this sprite animation.
91  * \return the frame delay in milliseconds
92  */
get_frame_delay() const93 uint32_t SpriteAnimation::get_frame_delay() const {
94   return frame_delay;
95 }
96 
97 /**
98  * \brief Returns whether this animation loops on a frame.
99  * \return true if this animation loops
100  */
is_looping() const101 bool SpriteAnimation::is_looping() const {
102   return loop_on_frame != -1;
103 }
104 
105 /**
106  * \brief Returns the next frame of the current frame.
107  * \param current_direction the current direction
108  * \param current_frame the current frame
109  * \return the next frame of the current frame in this direction
110  * (or -1 if the animation is over)
111  */
get_next_frame(int current_direction,int current_frame) const112 int SpriteAnimation::get_next_frame(
113     int current_direction, int current_frame) const {
114 
115   if (current_direction < 0
116       || current_direction >= get_nb_directions()) {
117     std::ostringstream oss;
118     oss << "Invalid sprite direction '" << current_direction
119         << "': this sprite has " << get_nb_directions()
120         << " direction(s)";
121     Debug::die(oss.str());
122   }
123 
124   int next_frame = current_frame + 1;
125 
126   if (next_frame == directions[current_direction].get_nb_frames()) {
127     // If we are on the last frame
128     // we loop on the appropriate frame
129     // or -1 if there is no loop.
130     next_frame = loop_on_frame;
131   }
132 
133   return next_frame;
134 }
135 
136 /**
137  * \brief Draws a specific frame of this animation on a surface.
138  * \param dst_surface the surface on which the sprite will be drawn
139  * \param dst_position coordinates on the destination surface
140  * (the origin point will be drawn at this position)
141  * \param current_direction the direction to show
142  * \param current_frame the frame to show in this direction
143  * \param infos draw infos bundle
144  */
draw(Surface & dst_surface,const Point & dst_position,int current_direction,int current_frame,const DrawInfos & infos) const145 void SpriteAnimation::draw(Surface& dst_surface,
146     const Point& dst_position, int current_direction, int current_frame, const DrawInfos &infos) const {
147 
148   if (src_image == nullptr) {
149     return;
150   }
151 
152   if (current_direction < 0
153       || current_direction >= get_nb_directions()) {
154     std::ostringstream oss;
155     oss << "Invalid sprite direction "
156         << current_direction << ": this sprite has " << get_nb_directions()
157         << " direction(s)";
158     Debug::die(oss.str());
159   }
160   directions[current_direction].draw(dst_surface, dst_position,
161       current_frame, *src_image, infos);
162 }
163 
164 /**
165  * \brief Enables the pixel-perfect collision detection for this animation.
166  */
enable_pixel_collisions()167 void SpriteAnimation::enable_pixel_collisions() {
168 
169   if (src_image != nullptr) {
170     do_enable_pixel_collisions();
171   }
172   else {
173     // Wait for the source image to be available before analyzing its pixels.
174     should_enable_pixel_collisions = true;
175   }
176 }
177 
178 /**
179  * \brief Internal function that enables the pixel-perfect collision detection for this animation now.
180  */
do_enable_pixel_collisions()181 void SpriteAnimation::do_enable_pixel_collisions() {
182 
183   if (src_image == nullptr) {
184     return;
185   }
186 
187   for (SpriteAnimationDirection& direction: directions) {
188     direction.enable_pixel_collisions(*src_image);
189   }
190 }
191 
192 /**
193  * \brief Disables the pixel-perfect collision detection for this animation.
194  */
disable_pixel_collisions()195 void SpriteAnimation::disable_pixel_collisions() {
196 
197   if (src_image == nullptr) {
198     return;
199   }
200 
201   for (SpriteAnimationDirection& direction: directions) {
202     direction.disable_pixel_collisions();
203   }
204 }
205 
206 /**
207  * \brief Returns whether the pixel-perfect collisions are enabled for this animations.
208  * \return true if the pixel-perfect collisions are enabled
209  */
are_pixel_collisions_enabled() const210 bool SpriteAnimation::are_pixel_collisions_enabled() const {
211 
212   if (should_enable_pixel_collisions) {
213     return true;
214   }
215 
216   if (directions.empty()) {
217     return false;
218   }
219 
220   return directions[0].are_pixel_collisions_enabled();
221 }
222 
223 }
224 
225