- added check to catch brocken worldmaps
[supertux.git] / src / worldmap.cpp
1 //  $Id$
2 //
3 //  Pingus - A free Lemmings clone
4 //  Copyright (C) 2002 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 "texture.h"
24 #include "screen.h"
25 #include "lispreader.h"
26 #include "gameloop.h"
27 #include "worldmap.h"
28
29 namespace WorldMapNS {
30
31 TileManager* TileManager::instance_  = 0;
32
33 TileManager::TileManager()
34 {
35   lisp_stream_t stream;
36   FILE* in = fopen(DATA_PREFIX "images/worldmap/antarctica.scm", "r");
37   assert(in);
38   lisp_stream_init_file (&stream, in);
39   lisp_object_t* root_obj = lisp_read (&stream);
40   
41   if (strcmp(lisp_symbol(lisp_car(root_obj)), "supertux-worldmap-tiles") == 0)
42     {
43       lisp_object_t* cur = lisp_cdr(root_obj);
44
45       while(!lisp_nil_p(cur))
46         {
47           lisp_object_t* element = lisp_car(cur);
48
49           if (strcmp(lisp_symbol(lisp_car(element)), "tile") == 0)
50             {
51               int id = 0;
52               std::string filename = "<invalid>";
53
54               Tile* tile = new Tile;             
55               tile->north = true;
56               tile->east  = true;
57               tile->south = true;
58               tile->west  = true;
59               tile->stop  = true;
60   
61               LispReader reader(lisp_cdr(element));
62               reader.read_int("id",  &id);
63               reader.read_bool("north", &tile->north);
64               reader.read_bool("south", &tile->south);
65               reader.read_bool("west",  &tile->west);
66               reader.read_bool("east",  &tile->east);
67               reader.read_bool("stop",  &tile->stop);
68               reader.read_string("image",  &filename);
69
70               texture_load(&tile->sprite, 
71                            const_cast<char*>((std::string(DATA_PREFIX "/images/worldmap/") + filename).c_str()), 
72                            USE_ALPHA);
73
74               if (id >= tiles.size())
75                 tiles.resize(id+1);
76
77               tiles[id] = tile;
78             }
79           else
80             {
81               puts("Unhandled symbol");
82             }
83
84           cur = lisp_cdr(cur);
85         }
86     }
87   else
88     {
89       assert(0);
90     }
91 }
92
93 Tile*
94 TileManager::get(int i)
95 {
96   assert(i >=0 && i < tiles.size());
97   return tiles[i];
98 }
99
100 WorldMap::WorldMap()
101 {
102   quit = false;
103   width  = 20;
104   height = 15;
105   tux_moving = false;
106
107   texture_load(&tux_sprite, DATA_PREFIX "/images/worldmap/tux.png", USE_ALPHA);
108   texture_load(&level_sprite, DATA_PREFIX "/images/worldmap/levelmarker.png", USE_ALPHA);
109
110   tux_offset = 0;
111
112   tux_tile_pos.x = 0;
113   tux_tile_pos.y = 0;
114
115   input_direction = NONE;
116   tux_direction = NONE;
117   enter_level = false;
118
119   name = "<no name>";
120   music = "SALCON.MOD";
121   song = 0;
122
123   load_map();
124 }
125
126 WorldMap::~WorldMap()
127 {
128 }
129
130 void
131 WorldMap::load_map()
132 {
133   lisp_stream_t stream;
134   FILE* in = fopen(DATA_PREFIX "levels/default/worldmap.scm", "r");
135   assert(in);
136   lisp_stream_init_file (&stream, in);
137   lisp_object_t* root_obj = lisp_read (&stream);
138   
139   if (strcmp(lisp_symbol(lisp_car(root_obj)), "supertux-worldmap") == 0)
140     {
141       lisp_object_t* cur = lisp_cdr(root_obj);
142
143       while(!lisp_nil_p(cur))
144         {
145           lisp_object_t* element = lisp_car(cur);
146
147           if (strcmp(lisp_symbol(lisp_car(element)), "tilemap") == 0)
148             {
149               LispReader reader(lisp_cdr(element));
150               reader.read_int("width",  &width);
151               reader.read_int("height", &height);
152               reader.read_int_vector("data", &tilemap);
153             }
154           else if (strcmp(lisp_symbol(lisp_car(element)), "properties") == 0)
155             {
156               LispReader reader(lisp_cdr(element));
157               reader.read_string("name",  &name);
158               reader.read_string("music", &music);
159             }
160           else if (strcmp(lisp_symbol(lisp_car(element)), "levels") == 0)
161             {
162               lisp_object_t* cur = lisp_cdr(element);
163               
164               while(!lisp_nil_p(cur))
165                 {
166                   lisp_object_t* element = lisp_car(cur);
167                   
168                   if (strcmp(lisp_symbol(lisp_car(element)), "level") == 0)
169                     {
170                       Level level;
171                       LispReader reader(lisp_cdr(element));
172                       reader.read_string("name",  &level.name);
173                       reader.read_int("x-pos", &level.x);
174                       reader.read_int("y-pos", &level.y);
175                       levels.push_back(level);
176                     }
177                   
178                   cur = lisp_cdr(cur);      
179                 }
180             }
181           else
182             {
183               
184             }
185           
186           cur = lisp_cdr(cur);
187         }
188     }
189 }
190
191 void
192 WorldMap::get_input()
193 {
194   SDL_Event event;
195
196   enter_level = false;
197
198   while (SDL_PollEvent(&event))
199     {
200       switch(event.type)
201         {
202         case SDL_QUIT:
203           quit = true;
204           break;
205           
206         case SDL_KEYDOWN:
207           switch(event.key.keysym.sym)
208             {
209             case SDLK_ESCAPE:
210               quit = true;
211               break;
212             case SDLK_LCTRL:
213             case SDLK_RETURN:
214               if (!tux_moving)
215                 enter_level = true;
216               break;
217             }
218           break;
219         }
220     }
221
222   Uint8 *keystate = SDL_GetKeyState(NULL);
223   
224   input_direction = NONE;
225   
226   if (keystate[SDLK_LEFT])
227     input_direction = WEST;
228   else if (keystate[SDLK_RIGHT])
229     input_direction = EAST;
230   else if (keystate[SDLK_UP])
231     input_direction = NORTH;
232   else if (keystate[SDLK_DOWN])
233     input_direction = SOUTH;
234 }
235
236 Point
237 WorldMap::get_next_tile(Point pos, Direction direction)
238 {
239   // FIXME: Cleanup, seperate tux
240   switch(input_direction)
241     {
242     case WEST:
243       pos.x -= 1;
244       break;
245     case EAST:
246       pos.x += 1;
247       break;
248     case NORTH:
249       pos.y -= 1;
250       break;
251     case SOUTH:
252       pos.y += 1;
253       break;
254     case NONE:
255       break;
256     }
257   return pos;
258 }
259
260 void
261 WorldMap::update()
262 {
263   float speed = 4.5;
264
265   if (enter_level)
266     {
267       for(Levels::iterator i = levels.begin(); i != levels.end(); ++i)
268         {
269           if (i->x == tux_tile_pos.x && 
270               i->y == tux_tile_pos.y)
271             {
272               std::cout << "Enter the current level: " << i->name << std::endl;;
273               halt_music();
274               gameloop(const_cast<char*>((DATA_PREFIX "levels/default/" + i->name).c_str()),
275                        1, ST_GL_LOAD_LEVEL_FILE);
276               play_music(song, 1);
277               break;
278             }
279         }
280     }
281   else
282     {
283       if (!tux_moving)
284         {
285           Point next_tile = get_next_tile(tux_tile_pos, input_direction);
286           if (next_tile.x >= 0 && next_tile.x < width
287               && next_tile.y >= 0 && next_tile.y < height)
288             {
289               // FIXME: Cleanup, seperate tux
290               switch(input_direction)
291                 {
292                 case WEST:
293                   if (at(tux_tile_pos)->west && at(next_tile)->east)
294                     {
295                       tux_tile_pos.x -= 1;
296                       tux_moving = true;
297                       tux_direction = input_direction;
298                     }
299                   break;
300                 case EAST:
301                   if (at(tux_tile_pos)->east && at(next_tile)->west)
302                     {
303                       tux_tile_pos.x += 1;
304                       tux_moving = true;
305                       tux_direction = input_direction;
306                     }
307                   break;
308                 case NORTH:
309                   if (at(tux_tile_pos)->north && at(next_tile)->south)
310                     {
311                       tux_tile_pos.y -= 1;
312                       tux_moving = true;
313                       tux_direction = input_direction;
314                     }
315                   break;
316                 case SOUTH:
317                   if (at(tux_tile_pos)->south && at(next_tile)->north)
318                     {
319                       tux_tile_pos.y += 1;
320                       tux_moving = true;
321                       tux_direction = input_direction;
322                     }
323                   break;
324                 case NONE:
325                   tux_moving = false;
326                   tux_offset = 0;
327                   tux_direction = input_direction;
328                   break;
329                 }
330             }
331           else
332             {
333               tux_moving = false;
334               tux_offset = 0;
335               tux_direction = NONE;
336             }
337         }
338       else
339         {
340           tux_offset += speed;
341
342           if (tux_offset > 32)
343             {
344               tux_offset -= 32;
345
346               if (at(tux_tile_pos)->stop)
347                 {
348                   tux_direction = NONE;
349                   tux_moving = false;
350                 }
351               else
352                 {
353                   // FIXME: Cleanup, seperate tux
354                   switch(tux_direction)
355                     {
356                     case WEST:
357                       if (at(tux_tile_pos)->west)
358                         tux_tile_pos.x -= 1;
359                       break;
360                     case EAST:
361                       if (at(tux_tile_pos)->east)
362                         tux_tile_pos.x += 1;
363                       break;
364                     case NORTH:
365                       if (at(tux_tile_pos)->north)
366                         tux_tile_pos.y -= 1;
367                       break;
368                     case SOUTH:
369                       if (at(tux_tile_pos)->south)
370                         tux_tile_pos.y += 1;
371                       break;
372                     }                      
373                 }
374             }
375         }
376     }
377 }
378
379 Tile*
380 WorldMap::at(Point p)
381 {
382   assert(p.x >= 0 
383          && p.x < width
384          && p.y >= 0
385          && p.y < height);
386   return TileManager::instance()->get(tilemap[width * p.y + p.x]);
387 }
388
389 void
390 WorldMap::draw()
391 {
392   for(int y = 0; y < height; ++y)
393     for(int x = 0; x < width; ++x)
394       {
395         Tile* tile = at(Point(x, y));
396         texture_draw(&tile->sprite, x*32, y*32, NO_UPDATE);
397       }
398
399   
400   float x = tux_tile_pos.x * 32;
401   float y = tux_tile_pos.y * 32;
402
403   switch(tux_direction)
404     {
405     case WEST:
406       x -= tux_offset - 32;
407       break;
408     case EAST:
409       x += tux_offset - 32;
410       break;
411     case NORTH:
412       y -= tux_offset - 32;
413       break;
414     case SOUTH:
415       y += tux_offset - 32;
416       break;
417     }
418
419   for(Levels::iterator i = levels.begin(); i != levels.end(); ++i)
420     {
421       texture_draw(&level_sprite, i->x*32, i->y*32, NO_UPDATE);
422     }
423
424   texture_draw(&tux_sprite, (int)x, (int)y, NO_UPDATE);
425   flipscreen();
426 }
427
428 void
429 WorldMap::display()
430 {
431   quit = false;
432
433   song = load_song(const_cast<char*>((DATA_PREFIX "/music/" + music).c_str()));
434   play_music(song, 1);
435
436   while(!quit) {
437     draw();
438     get_input();
439     update();
440     SDL_Delay(20);
441   }
442
443   free_music(song);
444 }
445
446 } // namespace WorldMapNS
447
448 void worldmap_run()
449 {
450   WorldMapNS::WorldMap worldmap;
451   
452   worldmap.display();
453 }
454
455 /* EOF */