b329d87005bebb37a7e9920892496dcfb13f5d83
[supertux.git] / src / worldmap.cpp
1 //  $Id$
2 //
3 //  SuperTux -  A Jump'n Run
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 <vector>
22 #include <assert.h>
23 #include "globals.h"
24 #include "texture.h"
25 #include "screen.h"
26 #include "lispreader.h"
27 #include "gameloop.h"
28 #include "setup.h"
29 #include "worldmap.h"
30
31 namespace WorldMapNS {
32
33 TileManager* TileManager::instance_  = 0;
34
35 TileManager::TileManager()
36 {
37   std::string stwt_filename = datadir +  "images/worldmap/antarctica.stwt";
38   lisp_object_t* root_obj = lisp_read_from_file(stwt_filename);
39  
40   if (!root_obj)
41     st_abort("Couldn't load file", stwt_filename);
42
43   if (strcmp(lisp_symbol(lisp_car(root_obj)), "supertux-worldmap-tiles") == 0)
44     {
45       lisp_object_t* cur = lisp_cdr(root_obj);
46
47       while(!lisp_nil_p(cur))
48         {
49           lisp_object_t* element = lisp_car(cur);
50
51           if (strcmp(lisp_symbol(lisp_car(element)), "tile") == 0)
52             {
53               int id = 0;
54               std::string filename = "<invalid>";
55
56               Tile* tile = new Tile;             
57               tile->north = true;
58               tile->east  = true;
59               tile->south = true;
60               tile->west  = true;
61               tile->stop  = true;
62   
63               LispReader reader(lisp_cdr(element));
64               reader.read_int("id",  &id);
65               reader.read_bool("north", &tile->north);
66               reader.read_bool("south", &tile->south);
67               reader.read_bool("west",  &tile->west);
68               reader.read_bool("east",  &tile->east);
69               reader.read_bool("stop",  &tile->stop);
70               reader.read_string("image",  &filename);
71
72               texture_load(&tile->sprite, 
73                            datadir +  "/images/worldmap/" + filename, 
74                            USE_ALPHA);
75
76               if (id >= int(tiles.size()))
77                 tiles.resize(id+1);
78
79               tiles[id] = tile;
80             }
81           else
82             {
83               puts("Unhandled symbol");
84             }
85
86           cur = lisp_cdr(cur);
87         }
88     }
89   else
90     {
91       assert(0);
92     }
93 }
94
95 Tile*
96 TileManager::get(int i)
97 {
98   assert(i >=0 && i < int(tiles.size()));
99   return tiles[i];
100 }
101
102 Tux::Tux(WorldMap* worldmap_)
103   : worldmap(worldmap_)
104 {
105   texture_load(&sprite, datadir +  "/images/worldmap/tux.png", USE_ALPHA);
106   offset = 0;
107   moving = false;
108   tile_pos.x = 5;
109   tile_pos.y = 5;
110   direction = NONE;
111   input_direction = NONE;
112 }
113
114 void
115 Tux::draw()
116 {
117   float x = tile_pos.x * 32;
118   float y = tile_pos.y * 32;
119
120   switch(direction)
121     {
122     case WEST:
123       x -= offset - 32;
124       break;
125     case EAST:
126       x += offset - 32;
127       break;
128     case NORTH:
129       y -= offset - 32;
130       break;
131     case SOUTH:
132       y += offset - 32;
133       break;
134     case NONE:
135       break;
136     }
137
138   texture_draw(&sprite, (int)x, (int)y);
139 }
140
141 void
142 Tux::stop()
143 {
144   offset = 0;
145   direction = NONE;
146   moving = false;
147 }
148
149 void
150 Tux::update(float delta)
151 {
152   if (!moving)
153     {
154       if (input_direction != NONE)
155         { // We got a new direction, so lets start walking when possible
156           Point next_tile;
157           if (worldmap->path_ok(input_direction, tile_pos, &next_tile))
158             {
159               tile_pos = next_tile;
160               moving = true;
161               direction = input_direction;
162             }
163         }
164     }
165   else
166     {
167       // Let tux walk a few pixels (20 pixel/sec)
168       offset += 20.0f * delta;
169
170       if (offset > 32)
171         { // We reached the next tile, so we check what to do now
172           offset -= 32;
173
174           if (worldmap->at(tile_pos)->stop)
175             {
176               stop();
177             }
178           else
179             {
180               Point next_tile;
181               if (worldmap->path_ok(direction, tile_pos, &next_tile))
182                 {
183                   tile_pos = next_tile;
184                 }
185               else
186                 {
187                   puts("Tilemap data is buggy");
188                   stop();
189                 }
190             }
191         }
192     }
193 }
194
195 WorldMap::WorldMap()
196 {
197   tux = new Tux(this);
198
199   quit = false;
200   width  = 20;
201   height = 15;
202
203   texture_load(&level_sprite, datadir +  "/images/worldmap/levelmarker.png", USE_ALPHA);
204   texture_load(&leveldot_green, datadir +  "/images/worldmap/leveldot_green.png", USE_ALPHA);
205   texture_load(&leveldot_red, datadir +  "/images/worldmap/leveldot_red.png", USE_ALPHA);
206
207   input_direction = NONE;
208   enter_level = false;
209
210   name = "<no name>";
211   music = "SALCON.MOD";
212   song = 0;
213
214   load_map();
215 }
216
217 WorldMap::~WorldMap()
218 {
219   delete tux;
220 }
221
222 void
223 WorldMap::load_map()
224 {
225   std::string filename = datadir +  "levels/default/worldmap.stwm";
226   
227   lisp_object_t* root_obj = lisp_read_from_file(filename);
228   if (!root_obj)
229     st_abort("Couldn't load file", filename);
230   
231   if (strcmp(lisp_symbol(lisp_car(root_obj)), "supertux-worldmap") == 0)
232     {
233       lisp_object_t* cur = lisp_cdr(root_obj);
234
235       while(!lisp_nil_p(cur))
236         {
237           lisp_object_t* element = lisp_car(cur);
238
239           if (strcmp(lisp_symbol(lisp_car(element)), "tilemap") == 0)
240             {
241               LispReader reader(lisp_cdr(element));
242               reader.read_int("width",  &width);
243               reader.read_int("height", &height);
244               reader.read_int_vector("data", &tilemap);
245             }
246           else if (strcmp(lisp_symbol(lisp_car(element)), "properties") == 0)
247             {
248               LispReader reader(lisp_cdr(element));
249               reader.read_string("name",  &name);
250               reader.read_string("music", &music);
251             }
252           else if (strcmp(lisp_symbol(lisp_car(element)), "levels") == 0)
253             {
254               lisp_object_t* cur = lisp_cdr(element);
255               
256               while(!lisp_nil_p(cur))
257                 {
258                   lisp_object_t* element = lisp_car(cur);
259                   
260                   if (strcmp(lisp_symbol(lisp_car(element)), "level") == 0)
261                     {
262                       Level level;
263                       LispReader reader(lisp_cdr(element));
264                       reader.read_string("name",  &level.name);
265                       reader.read_int("x", &level.x);
266                       reader.read_int("y", &level.y);
267                       levels.push_back(level);
268                     }
269                   
270                   cur = lisp_cdr(cur);      
271                 }
272             }
273           else
274             {
275               
276             }
277           
278           cur = lisp_cdr(cur);
279         }
280     }
281 }
282
283 void
284 WorldMap::get_input()
285 {
286   SDL_Event event;
287
288   enter_level = false;
289   input_direction = NONE;
290
291   while (SDL_PollEvent(&event))
292     {
293       switch(event.type)
294         {
295         case SDL_QUIT:
296           quit = true;
297           break;
298           
299         case SDL_KEYDOWN:
300           switch(event.key.keysym.sym)
301             {
302             case SDLK_ESCAPE:
303               quit = true;
304               break;
305             case SDLK_LCTRL:
306             case SDLK_RETURN:
307               enter_level = true;
308               break;
309             default:
310               break;
311             }
312           break;
313           
314         case SDL_JOYAXISMOTION:
315           switch(event.jaxis.axis)
316             {
317             case JOY_X:
318               if (event.jaxis.value < -JOYSTICK_DEAD_ZONE)
319                 input_direction = WEST;
320               else if (event.jaxis.value > JOYSTICK_DEAD_ZONE)
321                 input_direction = EAST;
322               break;
323             case JOY_Y:
324               if (event.jaxis.value > JOYSTICK_DEAD_ZONE)
325                 input_direction = SOUTH;
326               else if (event.jaxis.value < -JOYSTICK_DEAD_ZONE)
327                 input_direction = NORTH;
328               break;
329             }
330           break;
331
332         case SDL_JOYBUTTONDOWN:
333           if (event.jbutton.button == JOY_B)
334             enter_level = true;
335           break;
336
337         default:
338           break;
339         }
340     }
341
342   Uint8 *keystate = SDL_GetKeyState(NULL);
343   
344   if (keystate[SDLK_LEFT])
345     input_direction = WEST;
346   else if (keystate[SDLK_RIGHT])
347     input_direction = EAST;
348   else if (keystate[SDLK_UP])
349     input_direction = NORTH;
350   else if (keystate[SDLK_DOWN])
351     input_direction = SOUTH;
352 }
353
354 Point
355 WorldMap::get_next_tile(Point pos, Direction direction)
356 {
357   switch(direction)
358     {
359     case WEST:
360       pos.x -= 1;
361       break;
362     case EAST:
363       pos.x += 1;
364       break;
365     case NORTH:
366       pos.y -= 1;
367       break;
368     case SOUTH:
369       pos.y += 1;
370       break;
371     case NONE:
372       break;
373     }
374   return pos;
375 }
376
377 bool
378 WorldMap::path_ok(Direction direction, Point old_pos, Point* new_pos)
379 {
380   *new_pos = get_next_tile(old_pos, direction);
381
382   if (!(new_pos->x >= 0 && new_pos->x < width
383         && new_pos->y >= 0 && new_pos->y < height))
384     { // New position is outsite the tilemap
385       return false;
386     }
387   else
388     { // Check if we the tile allows us to go to new_pos
389       switch(direction)
390         {
391         case WEST:
392           return (at(old_pos)->west && at(*new_pos)->east);
393
394         case EAST:
395           return (at(old_pos)->east && at(*new_pos)->west);
396
397         case NORTH:
398           return (at(old_pos)->north && at(*new_pos)->south);
399
400         case SOUTH:
401           return (at(old_pos)->south && at(*new_pos)->north);
402
403         case NONE:
404           assert(!"path_ok() can't work if direction is NONE");
405         }
406       return false;
407     }
408 }
409
410 void
411 WorldMap::update()
412 {
413   if (enter_level && !tux->is_moving())
414     {
415       for(Levels::iterator i = levels.begin(); i != levels.end(); ++i)
416         {
417           if (i->x == tux->get_tile_pos().x && 
418               i->y == tux->get_tile_pos().y)
419             {
420               std::cout << "Enter the current level: " << i->name << std::endl;;
421               halt_music();
422               GameSession session(datadir +  "levels/default/" + i->name,
423                                   1, ST_GL_LOAD_LEVEL_FILE);
424               session.run();
425               play_music(song, 1);
426               return;
427             }
428         }
429       std::cout << "Nothing to enter at: "
430                 << tux->get_tile_pos().x << ", " << tux->get_tile_pos().y << std::endl;
431     }
432   else
433     {
434       tux->set_direction(input_direction);
435       tux->update(0.33f);
436     }
437 }
438
439 Tile*
440 WorldMap::at(Point p)
441 {
442   assert(p.x >= 0 
443          && p.x < width
444          && p.y >= 0
445          && p.y < height);
446   return TileManager::instance()->get(tilemap[width * p.y + p.x]);
447 }
448
449 void
450 WorldMap::draw()
451 {
452   for(int y = 0; y < height; ++y)
453     for(int x = 0; x < width; ++x)
454       {
455         Tile* tile = at(Point(x, y));
456         texture_draw(&tile->sprite, x*32, y*32);
457       }
458   
459   for(Levels::iterator i = levels.begin(); i != levels.end(); ++i)
460     {
461       texture_draw(&leveldot_green, i->x*32, i->y*32);
462     }
463
464   tux->draw();
465   flipscreen();
466 }
467
468 void
469 WorldMap::display()
470 {
471   quit = false;
472
473   song = load_song(datadir +  "/music/" + music);
474   play_music(song, 1);
475
476   while(!quit) {
477     draw();
478     get_input();
479     update();
480     SDL_Delay(20);
481   }
482
483   free_music(song);
484 }
485
486 } // namespace WorldMapNS
487
488 void worldmap_run()
489 {
490   WorldMapNS::WorldMap worldmap;
491   
492   worldmap.display();
493 }
494
495 /* EOF */