Renamed code variables, as well.
[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_float("fps",     action->fps);
76
77   std::vector<std::string> images;
78   if(!lispreader.read_string_vector("images", images))
79     Termination::abort("Sprite contains no images: ", action->name.c_str());
80
81   for(std::vector<std::string>::size_type i = 0; i < images.size(); ++i)
82     {
83       action->surfaces.push_back(
84           new Surface(datadir + "/images/" + images[i], true));
85     }        
86
87   actions[action->name] = action;
88 }
89
90 void
91 Sprite::init_defaults(Action* act)
92 {
93   act->x_offset = 0;
94   act->y_offset = 0;
95   act->fps = 10;
96
97   start_animation(-1);
98 }
99
100 void
101 Sprite::set_action(std::string act)
102 {
103 if(!next_action.empty() && animation_loops > 0)
104   {
105   next_action = act;
106   return;
107   }
108 Actions::iterator i = actions.find(act);
109 if(i == actions.end())
110   {
111   std::cerr << "Warning: Action '" << act << "' not found on Sprite '" << name << "'\n";
112   return;
113   }
114 action = i->second;
115 }
116
117 void
118 Sprite::start_animation(int loops)
119 {
120 reset();
121 animation_loops = loops;
122 }
123
124 void
125 Sprite::reset()
126 {
127 frame = 0;
128 last_tick = SDL_GetTicks();
129 animation_reversed = false;
130 next_action.clear();
131 }
132
133 bool
134 Sprite::check_animation()
135 {
136 return animation_loops;
137 }
138
139 void
140 Sprite::reverse_animation(bool reverse)
141 {
142 animation_reversed = reverse;
143
144 if(animation_reversed)
145   frame = get_frames()-1;
146 else
147   frame = 0;
148 }
149
150 void
151 Sprite::update()
152 {
153 if(animation_loops == 0)
154   return;
155
156 float frame_inc = (action->fps/1000.0) * (SDL_GetTicks() - last_tick);
157 last_tick = SDL_GetTicks();
158
159 if(animation_reversed)
160   frame -= frame_inc;
161 else
162   frame += frame_inc;
163
164 if(animation_reversed)
165   {
166   float excedent = frame - 0;
167   if((int)excedent < 0 || excedent >= get_frames())
168     {  // last case can happen when not used reverse_animation()
169     frame = get_frames() - 1;
170     if(animation_loops > 0)
171       {
172       animation_loops--;
173       if(animation_loops == 0 && !next_action.empty())
174         {
175         set_action(next_action);
176         start_animation(-1);
177         }
178       }
179
180     if(fabsf(excedent) < get_frames())
181       frame += excedent;
182     }
183   }
184 else
185   {
186   float excedent = frame - action->surfaces.size();
187   if((int)excedent >= 0)
188     {
189     frame = 0;
190     if(animation_loops > 0)
191       {
192       animation_loops--;
193       if(animation_loops == 0 && !next_action.empty())
194         {
195         set_action(next_action);
196         start_animation(-1);
197         }
198       }
199
200     if(excedent < get_frames())
201       frame += excedent;
202     }
203   }
204 }
205
206 void
207 Sprite::draw(DrawingContext& context, const Vector& pos, int layer,
208     Uint32 drawing_effect)
209 {
210   update();
211
212   if((int)frame >= get_frames() || (int)frame < 0)
213     std::cerr << "Warning: frame out of range: " << (int)frame
214               << "/" << get_frames() << " at sprite: " << get_name()
215               << "/" << get_action_name() << std::endl;
216   else
217     context.draw_surface(action->surfaces[(int)frame],
218             pos - Vector(action->x_offset, action->y_offset), layer, drawing_effect);
219 }
220
221 #if 0
222 void
223 Sprite::draw_part(float sx, float sy, float x, float y, float w, float h)
224 {
225   time = SDL_GetTicks();
226   unsigned int frame = get_current_frame();
227
228   if (frame < surfaces.size())
229     surfaces[frame]->draw_part(sx, sy, x - x_offset, y - y_offset, w, h);
230 }
231 #endif
232
233 int
234 Sprite::get_width()
235 {
236   return action->surfaces[get_frame()]->w;
237 }
238
239 int
240 Sprite::get_height()
241 {
242   return action->surfaces[get_frame()]->h;
243 }
244
245 /* EOF */