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