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