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