f6cbdebfc36f6e0e3ccb5667bb5adfd106a9ed31
[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         delete *i_sur;
60       }
61     delete i_act->second;
62     }
63 }
64
65 void
66 Sprite::parse_action(LispReader& lispreader)
67 {
68   action = new Action;
69
70   init_defaults(action);
71
72   if(!lispreader.read_string("name", action->name))
73     if(!actions.empty())
74       Termination::abort("Error: If there are more than one action, they need names!", "");
75   lispreader.read_int("x-offset", action->x_offset);
76   lispreader.read_int("y-offset", action->y_offset);
77   lispreader.read_int("z-order", action->z_order);
78   lispreader.read_float("fps",     action->fps);
79
80   /* TODO: add a top filter entry */
81   std::vector <int> mask_color;
82   lispreader.read_int_vector("apply-mask", mask_color);
83   if(mask_color.size() == 4)
84     {
85     for(std::vector<Surface*>::iterator i = action->surfaces.begin();
86         i < action->surfaces.end(); i++)
87       {
88         (*i)->apply_filter(MASK_FILTER, Color(mask_color));
89       }
90     }
91
92   std::string mirror_action;
93   lispreader.read_string("mirror-action", mirror_action);
94   if(!mirror_action.empty())
95     {
96     Action* act_tmp = get_action(mirror_action);
97     if(act_tmp == NULL)
98       std::cerr << "Warning: Could not mirror action. Action not found\n"
99                    "Mirror actions must be defined after the real one!\n";
100     else
101       {
102       for(int i = 0; static_cast<unsigned int>(i) < act_tmp->surfaces.size(); i++)
103         {
104         Surface* surface = new Surface(sdl_surface_from_sdl_surface(
105             act_tmp->surfaces[i]->impl->get_sdl_surface(), true), true);
106         surface->apply_filter(HORIZONTAL_FLIP_FILTER);
107         action->surfaces.push_back(surface);
108         }
109       }
110     }
111   else // Load images
112     {
113     std::vector<std::string> images;
114     if(!lispreader.read_string_vector("images", images))
115       Termination::abort("Sprite contains no images: ", action->name);
116
117     for(std::vector<std::string>::size_type i = 0; i < images.size(); i++)
118       {
119       action->surfaces.push_back(
120           new Surface(datadir + "/images/" + images[i], true));
121       }
122     }
123   actions[action->name] = action;
124 }
125
126 /*void Sprite::parse_filter(LispReader& lispreader)
127 {
128
129 }*/
130
131 void
132 Sprite::init_defaults(Action* act)
133 {
134   act->x_offset = 0;
135   act->y_offset = 0;
136   act->z_order = 0;
137   act->fps = 10;
138
139   start_animation(-1);
140 }
141
142 void
143 Sprite::set_action(std::string act)
144 {
145 if(!next_action.empty() && animation_loops > 0)
146   {
147   next_action = act;
148   return;
149   }
150 Actions::iterator i = actions.find(act);
151 if(i == actions.end())
152   {
153   std::cerr << "Warning: Action '" << act << "' not found on Sprite '" << name << "'\n";
154   return;
155   }
156 action = i->second;
157 }
158
159 Sprite::Action*
160 Sprite::get_action(std::string act)
161 {
162 Actions::iterator i = actions.find(act);
163 if(i == actions.end())
164   {
165   std::cerr << "Warning: Action '" << act << "' not found on Sprite '" << name << "'\n";
166   return NULL;
167   }
168 return i->second;
169 }
170
171 void
172 Sprite::start_animation(int loops)
173 {
174 reset();
175 animation_loops = loops;
176 }
177
178 void
179 Sprite::reset()
180 {
181 frame = 0;
182 last_tick = SDL_GetTicks();
183 animation_reversed = false;
184 next_action.clear();
185 }
186
187 bool
188 Sprite::check_animation()
189 {
190 return animation_loops;
191 }
192
193 void
194 Sprite::reverse_animation(bool reverse)
195 {
196 animation_reversed = reverse;
197
198 if(animation_reversed)
199   frame = get_frames()-1;
200 else
201   frame = 0;
202 }
203
204 void
205 Sprite::update()
206 {
207 if(animation_loops == 0)
208   {
209   if(frame >= get_frames() || frame < 0)
210     frame = 0;
211   return;
212   }
213
214 float frame_inc = (action->fps/1000.0) * (SDL_GetTicks() - last_tick);
215 last_tick = SDL_GetTicks();
216
217 if(animation_reversed)
218   frame -= frame_inc;
219 else
220   frame += frame_inc;
221
222 if(animation_reversed)
223   {
224   if(frame < 0 || frame >= (float)get_frames())
225     {  // last case can happen when not used reverse_animation()
226     float excedent = frame - 0;
227     frame = get_frames() - 1;
228     if(animation_loops > 0)
229       {
230       animation_loops--;
231       if(animation_loops == 0 && !next_action.empty())
232         {
233         set_action(next_action);
234         start_animation(-1);
235         }
236       }
237
238     if(fabsf(excedent) < get_frames())
239       frame += excedent;
240     }
241   }
242 else
243   {
244   if(frame >= (float)get_frames())
245     {
246     float excedent = frame - get_frames();
247     frame = 0;
248     if(animation_loops > 0)
249       {
250       animation_loops--;
251       if(animation_loops == 0 && !next_action.empty())
252         {
253         set_action(next_action);
254         start_animation(-1);
255         }
256       }
257
258     if(excedent < get_frames())
259       frame += excedent;
260     }
261   }
262 }
263
264 void
265 Sprite::draw(DrawingContext& context, const Vector& pos, int layer,
266     Uint32 drawing_effect)
267 {
268   update();
269
270   if((int)frame >= get_frames() || (int)frame < 0)
271     std::cerr << "Warning: frame out of range: " << (int)frame
272               << "/" << get_frames() << " at " << get_name()
273               << "/" << get_action_name() << std::endl;
274   else
275     context.draw_surface(action->surfaces[(int)frame],
276             pos - Vector(action->x_offset, action->y_offset), layer + action->z_order, drawing_effect);
277 }
278
279 void
280 Sprite::draw_part(DrawingContext& context, const Vector& source, const Vector& size,
281                   const Vector& pos, int layer, Uint32 drawing_effect)
282 {
283   update();
284
285   if((int)frame >= get_frames() || (int)frame < 0)
286     std::cerr << "Warning: frame out of range: " << (int)frame
287               << "/" << get_frames() << " at sprite: " << get_name()
288               << "/" << get_action_name() << std::endl;
289   else
290     context.draw_surface_part(action->surfaces[(int)frame], source, size,
291             pos - Vector(action->x_offset, action->y_offset), layer + action->z_order, drawing_effect);
292 }
293
294 int
295 Sprite::get_width()
296 {
297   return action->surfaces[get_frame()]->w;
298 }
299
300 int
301 Sprite::get_height()
302 {
303   return action->surfaces[get_frame()]->h;
304 }
305
306 /* EOF */