Updated addon repository URL and improved debug output on download
[supertux.git] / src / worldmap / tux.cpp
1 //  SuperTux -  A Jump'n Run
2 //  Copyright (C) 2004 Ingo Ruhnke <grumbel@gmail.com>
3 //  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
4 //
5 //  This program is free software: you can redistribute it and/or modify
6 //  it under the terms of the GNU General Public License as published by
7 //  the Free Software Foundation, either version 3 of the License, or
8 //  (at your option) any later version.
9 //
10 //  This program is distributed in the hope that it will be useful,
11 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 //  GNU General Public License for more details.
14 //
15 //  You should have received a copy of the GNU General Public License
16 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18 #include "control/input_manager.hpp"
19 #include "scripting/squirrel_util.hpp"
20 #include "sprite/sprite.hpp"
21 #include "sprite/sprite_manager.hpp"
22 #include "supertux/globals.hpp"
23 #include "supertux/player_status.hpp"
24 #include "supertux/savegame.hpp"
25 #include "supertux/tile.hpp"
26 #include "worldmap/level.hpp"
27 #include "worldmap/tux.hpp"
28
29 namespace worldmap {
30
31 static const float TUXSPEED = 200;
32 static const float map_message_TIME = 2.8f;
33
34 Tux::Tux(WorldMap* worldmap_) :
35   back_direction(),
36   worldmap(worldmap_),
37   sprite(SpriteManager::current()->create("images/worldmap/common/tux.sprite")),
38   controller(),
39   input_direction(),
40   direction(),
41   tile_pos(),
42   offset(),
43   moving(),
44   ghost_mode()
45 {
46   offset = 0;
47   moving = false;
48   direction = D_NONE;
49   input_direction = D_NONE;
50
51   ghost_mode = false;
52 }
53
54 Tux::~Tux()
55 {
56 }
57
58 void
59 Tux::draw(DrawingContext& context)
60 {
61   switch (worldmap->get_savegame().get_player_status()->bonus) {
62     case GROWUP_BONUS:
63       sprite->set_action(moving ? "large-walking" : "large-stop");
64       break;
65     case FIRE_BONUS:
66       sprite->set_action(moving ? "fire-walking" : "fire-stop");
67       break;
68     case ICE_BONUS:
69       sprite->set_action(moving ? "ice-walking" : "ice-stop");
70       break;
71     case AIR_BONUS:
72       sprite->set_action(moving ? "air-walking" : "air-stop");
73       break;
74     case EARTH_BONUS:
75       sprite->set_action(moving ? "earth-walking" : "earth-stop");
76       break;
77     case NO_BONUS:
78       sprite->set_action(moving ? "small-walking" : "small-stop");
79       break;
80     default:
81       log_debug << "Bonus type not handled in worldmap." << std::endl;
82       sprite->set_action("large-stop");
83       break;
84   }
85
86   sprite->draw(context, get_pos(), LAYER_OBJECTS);
87 }
88
89 Vector
90 Tux::get_pos()
91 {
92   float x = tile_pos.x * 32;
93   float y = tile_pos.y * 32;
94
95   switch(direction)
96   {
97     case D_WEST:
98       x -= offset - 32;
99       break;
100     case D_EAST:
101       x += offset - 32;
102       break;
103     case D_NORTH:
104       y -= offset - 32;
105       break;
106     case D_SOUTH:
107       y += offset - 32;
108       break;
109     case D_NONE:
110       break;
111   }
112
113   return Vector(x, y);
114 }
115
116 void
117 Tux::stop()
118 {
119   offset = 0;
120   direction = D_NONE;
121   input_direction = D_NONE;
122   moving = false;
123 }
124
125 void
126 Tux::set_direction(Direction dir)
127 {
128   input_direction = dir;
129 }
130
131 void
132 Tux::set_ghost_mode(bool enabled)
133 {
134   ghost_mode = enabled;
135 }
136
137 bool
138 Tux::get_ghost_mode()
139 {
140   return ghost_mode;
141 }
142
143 void
144 Tux::tryStartWalking()
145 {
146   if (moving)
147     return;
148   if (input_direction == D_NONE)
149     return;
150
151   LevelTile* level = worldmap->at_level();
152
153   // We got a new direction, so lets start walking when possible
154   Vector next_tile;
155   if ((!level || level->solved || level->perfect)
156       && worldmap->path_ok(input_direction, tile_pos, &next_tile)) {
157     tile_pos = next_tile;
158     moving = true;
159     direction = input_direction;
160     back_direction = reverse_dir(direction);
161   } else if (ghost_mode || (input_direction == back_direction)) {
162     moving = true;
163     direction = input_direction;
164     tile_pos = worldmap->get_next_tile(tile_pos, direction);
165     back_direction = reverse_dir(direction);
166   }
167 }
168
169 bool
170 Tux::canWalk(int tile_data, Direction dir)
171 {
172   return ghost_mode ||
173     ((tile_data & Tile::WORLDMAP_NORTH && dir == D_NORTH) ||
174      (tile_data & Tile::WORLDMAP_SOUTH && dir == D_SOUTH) ||
175      (tile_data & Tile::WORLDMAP_EAST  && dir == D_EAST) ||
176      (tile_data & Tile::WORLDMAP_WEST  && dir == D_WEST));
177 }
178
179 void
180 Tux::tryContinueWalking(float elapsed_time)
181 {
182   if (!moving)
183     return;
184
185   // Let tux walk
186   offset += TUXSPEED * elapsed_time;
187
188   // Do nothing if we have not yet reached the next tile
189   if (offset <= 32)
190     return;
191
192   offset -= 32;
193
194   SpriteChange* sprite_change = worldmap->at_sprite_change(tile_pos);
195   if(sprite_change != NULL) {
196     sprite = sprite_change->sprite->clone();
197     sprite_change->clear_stay_action();
198   }
199
200   // if this is a special_tile with passive_message, display it
201   SpecialTile* special_tile = worldmap->at_special_tile();
202   if(special_tile)
203   {
204     // direction and the apply_action_ are opposites, since they "see"
205     // directions in a different way
206     if((direction == D_NORTH && special_tile->apply_action_south) ||
207        (direction == D_SOUTH && special_tile->apply_action_north) ||
208        (direction == D_WEST && special_tile->apply_action_east) ||
209        (direction == D_EAST && special_tile->apply_action_west))
210     {
211       if(special_tile->passive_message) {
212         worldmap->passive_message = special_tile->map_message;
213         worldmap->passive_message_timer.start(map_message_TIME);
214       } else if(special_tile->script != "") {
215         try {
216           std::istringstream in(special_tile->script);
217           worldmap->run_script(in, "specialtile");
218         } catch(std::exception& e) {
219           log_warning << "Couldn't execute special tile script: " << e.what()
220                       << std::endl;
221         }
222       }
223     }
224   }
225
226   // check if we are at a Teleporter
227   Teleporter* teleporter = worldmap->at_teleporter(tile_pos);
228
229   // stop if we reached a level, a WORLDMAP_STOP tile, a teleporter or a special tile without a passive_message
230   if ((worldmap->at_level())
231       || (worldmap->tile_data_at(tile_pos) & Tile::WORLDMAP_STOP)
232       || (special_tile && !special_tile->passive_message
233           && special_tile->script == "")
234       || (teleporter) || ghost_mode) {
235     if(special_tile && !special_tile->map_message.empty()
236        && !special_tile->passive_message)
237       worldmap->passive_message_timer.start(0);
238     stop();
239     return;
240   }
241
242   // if user wants to change direction, try changing, else guess the direction in which to walk next
243   const int tile_data = worldmap->tile_data_at(tile_pos);
244   if ((direction != input_direction) && canWalk(tile_data, input_direction)) {
245     direction = input_direction;
246     back_direction = reverse_dir(direction);
247   } else {
248     Direction dir = D_NONE;
249     if (tile_data & Tile::WORLDMAP_NORTH && back_direction != D_NORTH)
250       dir = D_NORTH;
251     else if (tile_data & Tile::WORLDMAP_SOUTH && back_direction != D_SOUTH)
252       dir = D_SOUTH;
253     else if (tile_data & Tile::WORLDMAP_EAST && back_direction != D_EAST)
254       dir = D_EAST;
255     else if (tile_data & Tile::WORLDMAP_WEST && back_direction != D_WEST)
256       dir = D_WEST;
257
258     if (dir == D_NONE) {
259       // Should never be reached if tiledata is good
260       log_warning << "Could not determine where to walk next" << std::endl;
261       stop();
262       return;
263     }
264
265     direction = dir;
266     input_direction = direction;
267     back_direction = reverse_dir(direction);
268   }
269
270   // Walk automatically to the next tile
271   if(direction == D_NONE)
272     return;
273
274   Vector next_tile;
275   if (!ghost_mode && !worldmap->path_ok(direction, tile_pos, &next_tile)) {
276     log_debug << "Tilemap data is buggy" << std::endl;
277     stop();
278     return;
279   }
280
281   SpriteChange* next_sprite = worldmap->at_sprite_change(next_tile);
282   if(next_sprite != NULL && next_sprite->change_on_touch) {
283     sprite = next_sprite->sprite->clone();
284     next_sprite->clear_stay_action();
285   }
286   SpriteChange* last_sprite = worldmap->at_sprite_change(tile_pos);
287   if(last_sprite != NULL && next_sprite != NULL) {
288     log_debug << "Old: " << tile_pos << " New: " << next_tile << std::endl;
289     last_sprite->set_stay_action();
290   }
291
292   tile_pos = next_tile;
293 }
294
295 void
296 Tux::updateInputDirection()
297 {
298   Controller* controller_ = InputManager::current()->get_controller();
299   if(controller_->hold(Controller::UP))
300     input_direction = D_NORTH;
301   else if(controller_->hold(Controller::DOWN))
302     input_direction = D_SOUTH;
303   else if(controller_->hold(Controller::LEFT))
304     input_direction = D_WEST;
305   else if(controller_->hold(Controller::RIGHT))
306     input_direction = D_EAST;
307 }
308
309 void
310 Tux::update(float elapsed_time)
311 {
312   updateInputDirection();
313   if (moving)
314     tryContinueWalking(elapsed_time);
315   else
316     tryStartWalking();
317 }
318
319 void
320 Tux::setup()
321 {
322   // check if we already touch a SpriteChange object
323   SpriteChange* sprite_change = worldmap->at_sprite_change(tile_pos);
324   if(sprite_change != NULL) {
325     sprite = sprite_change->sprite->clone();
326     sprite_change->clear_stay_action();
327   }
328 }
329
330 } // namespace WorldmapNS
331
332 /* EOF */