82639166ed99f5b72abfe25a9f9c1e337f6bb186
[supertux.git] / lib / special / sprite.cpp
1 //  $Id$
2 //
3 //  SuperTux
4 //  Copyright (C) 2004 Ingo Ruhnke <grumbel@gmx.de>
5 //
6 //  This program is free software; you can redistribute it and/or
7 //  modify it under the terms of the GNU General Public License
8 //  as published by the Free Software Foundation; either version 2
9 //  of the License, or (at your option) any later version.
10 //
11 //  This program is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //  GNU General Public License for more details.
15 //
16 //  You should have received a copy of the GNU General Public License
17 //  along with this program; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19
20 #include <iostream>
21 #include <cmath>
22
23 #include "../app/globals.h"
24 #include "../app/setup.h"
25 #include "../special/sprite.h"
26 #include "../video/drawing_context.h"
27
28 using namespace SuperTux;
29
30 Sprite::Sprite(lisp_object_t* cur)
31 {
32   for(; !lisp_nil_p(cur); cur = lisp_cdr(cur))
33     {
34     std::string token = lisp_symbol(lisp_car(lisp_car(cur)));
35     lisp_object_t* data = lisp_car(lisp_cdr(lisp_car(cur)));
36     LispReader reader(lisp_cdr(lisp_car(cur)));
37
38     if(token == "name")
39       name = lisp_string(data);
40     else if(token == "action")
41       parse_action(reader);
42     else
43       std::cerr << "Warning: Unknown sprite field: " << token << std::endl;
44     }
45
46   if(name.empty())
47     Termination::abort("Error: Sprite wihtout name.", "");
48   if(actions.empty())
49     Termination::abort("Error: Sprite wihtout actions.", "");
50 }
51
52 Sprite::~Sprite()
53 {
54   for(Actions::iterator i_act = actions.begin(); i_act != actions.end(); ++i_act)
55     {
56     for(std::vector<Surface*>::iterator i_sur = i_act->second->surfaces.begin();
57         i_sur != i_act->second->surfaces.end(); ++i_sur)
58       {
59       if(!i_act->second->mirror)
60         delete *i_sur;
61       }
62     delete i_act->second;
63     }
64 }
65
66 void
67 Sprite::parse_action(LispReader& lispreader)
68 {
69   action = new Action;
70
71   init_defaults(action);
72
73   if(!lispreader.read_string("name", action->name))
74     if(!actions.empty())
75       Termination::abort("Error: If there are more than one action, they need names!", "");
76   lispreader.read_int("x-offset", action->x_offset);
77   lispreader.read_int("y-offset", action->y_offset);
78   lispreader.read_int("z-order", action->z_order);
79   lispreader.read_float("fps",     action->fps);
80
81   /* TODO: add a top filter entry */
82   std::vector <int> mask_color;
83   lispreader.read_int_vector("apply-mask", mask_color);
84   if(mask_color.size() == 4)
85     {
86     for(std::vector<Surface*>::iterator i = action->surfaces.begin();
87         i < action->surfaces.end(); i++)
88       {
89         (*i)->apply_mask(Color(mask_color));
90       }
91     }
92
93   action->mirror = false;
94   std::string mirror_action;
95   lispreader.read_string("mirror-action", mirror_action);
96   if(!mirror_action.empty())
97     {
98     action->mirror = true;
99     Action* act_tmp = get_action(mirror_action);
100     if(act_tmp == NULL)
101       std::cerr << "Warning: Could not mirror action. Action not found\n"
102                    "Mirror actions must be defined after the real one!\n";
103     else
104       action->surfaces = act_tmp->surfaces;
105     }
106
107   // Load images
108   if(!action->mirror)
109     {
110     std::vector<std::string> images;
111     if(!lispreader.read_string_vector("images", images))
112       Termination::abort("Sprite contains no images: ", action->name);
113
114     for(std::vector<std::string>::size_type i = 0; i < images.size(); i++)
115       {
116       action->surfaces.push_back(
117           new Surface(datadir + "/images/" + images[i], true));
118       }
119     }
120   actions[action->name] = action;
121 }
122
123 /*void Sprite::parse_filter(LispReader& lispreader)
124 {
125
126 }*/
127
128 void
129 Sprite::init_defaults(Action* act)
130 {
131   act->x_offset = 0;
132   act->y_offset = 0;
133   act->z_order = 0;
134   act->fps = 10;
135
136   start_animation(-1);
137 }
138
139 void
140 Sprite::set_action(std::string act)
141 {
142 if(!next_action.empty() && animation_loops > 0)
143   {
144   next_action = act;
145   return;
146   }
147 Actions::iterator i = actions.find(act);
148 if(i == actions.end())
149   {
150   std::cerr << "Warning: Action '" << act << "' not found on Sprite '" << name << "'\n";
151   return;
152   }
153 action = i->second;
154 }
155
156 Sprite::Action*
157 Sprite::get_action(std::string act)
158 {
159 Actions::iterator i = actions.find(act);
160 if(i == actions.end())
161   {
162   std::cerr << "Warning: Action '" << act << "' not found on Sprite '" << name << "'\n";
163   return NULL;
164   }
165 return i->second;
166 }
167
168 void
169 Sprite::start_animation(int loops)
170 {
171 reset();
172 animation_loops = loops;
173 }
174
175 void
176 Sprite::reset()
177 {
178 frame = 0;
179 last_tick = SDL_GetTicks();
180 animation_reversed = false;
181 next_action.clear();
182 }
183
184 bool
185 Sprite::check_animation()
186 {
187 return animation_loops;
188 }
189
190 void
191 Sprite::reverse_animation(bool reverse)
192 {
193 animation_reversed = reverse;
194
195 if(animation_reversed)
196   frame = get_frames()-1;
197 else
198   frame = 0;
199 }
200
201 void
202 Sprite::update()
203 {
204 if(animation_loops == 0)
205   {
206   if(frame >= get_frames() || frame < 0)
207     frame = 0;
208   return;
209   }
210
211 float frame_inc = (action->fps/1000.0) * (SDL_GetTicks() - last_tick);
212 last_tick = SDL_GetTicks();
213
214 if(animation_reversed)
215   frame -= frame_inc;
216 else
217   frame += frame_inc;
218
219 if(animation_reversed)
220   {
221   if(frame < 0 || frame >= (float)get_frames())
222     {  // last case can happen when not used reverse_animation()
223     float excedent = frame - 0;
224     frame = get_frames() - 1;
225     if(animation_loops > 0)
226       {
227       animation_loops--;
228       if(animation_loops == 0 && !next_action.empty())
229         {
230         set_action(next_action);
231         start_animation(-1);
232         }
233       }
234
235     if(fabsf(excedent) < get_frames())
236       frame += excedent;
237     }
238   }
239 else
240   {
241   if(frame >= (float)get_frames())
242     {
243     float excedent = frame - get_frames();
244     frame = 0;
245     if(animation_loops > 0)
246       {
247       animation_loops--;
248       if(animation_loops == 0 && !next_action.empty())
249         {
250         set_action(next_action);
251         start_animation(-1);
252         }
253       }
254
255     if(excedent < get_frames())
256       frame += excedent;
257     }
258   }
259 }
260
261 void
262 Sprite::draw(DrawingContext& context, const Vector& pos, int layer,
263     Uint32 drawing_effect)
264 {
265   update();
266
267   if((int)frame >= get_frames() || (int)frame < 0)
268     std::cerr << "Warning: frame out of range: " << (int)frame
269               << "/" << get_frames() << " at " << get_name()
270               << "/" << get_action_name() << std::endl;
271   else
272     context.draw_surface(action->surfaces[(int)frame],
273             pos - Vector(action->x_offset, action->y_offset), layer + action->z_order,
274             action->mirror ? drawing_effect | HORIZONTAL_FLIP : drawing_effect);
275 }
276
277 void
278 Sprite::draw_part(DrawingContext& context, const Vector& source, const Vector& size,
279                   const Vector& pos, int layer, Uint32 drawing_effect)
280 {
281   update();
282
283   if((int)frame >= get_frames() || (int)frame < 0)
284     std::cerr << "Warning: frame out of range: " << (int)frame
285               << "/" << get_frames() << " at sprite: " << get_name()
286               << "/" << get_action_name() << std::endl;
287   else
288     context.draw_surface_part(action->surfaces[(int)frame], source, size,
289             pos - Vector(action->x_offset, action->y_offset), layer + action->z_order,
290             action->mirror ? drawing_effect | HORIZONTAL_FLIP : drawing_effect);
291 }
292
293 int
294 Sprite::get_width()
295 {
296   return action->surfaces[get_frame()]->w;
297 }
298
299 int
300 Sprite::get_height()
301 {
302   return action->surfaces[get_frame()]->h;
303 }
304
305 /* EOF */