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