2b132760e5c0a9fe2cf5aa45aac2b389c8f26977
[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 >= int(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 < int(tiles.size()));
97   return tiles[i];
98 }
99
100 WorldMap::WorldMap()
101 {
102   quit = false;
103   width  = 20;
104   height = 15;
105
106   texture_load(&tux_sprite, DATA_PREFIX "/images/worldmap/tux.png", USE_ALPHA);
107   texture_load(&level_sprite, DATA_PREFIX "/images/worldmap/levelmarker.png", USE_ALPHA);
108
109   tux_offset = 0;
110   tux_moving = false;
111   tux_tile_pos.x = 0;
112   tux_tile_pos.y = 0;
113   tux_direction = NONE;
114
115   input_direction = NONE;
116   enter_level = false;
117
118   name = "<no name>";
119   music = "SALCON.MOD";
120   song = 0;
121
122   load_map();
123 }
124
125 WorldMap::~WorldMap()
126 {
127 }
128
129 void
130 WorldMap::load_map()
131 {
132   lisp_stream_t stream;
133   FILE* in = fopen(DATA_PREFIX "levels/default/worldmap.scm", "r");
134   assert(in);
135   lisp_stream_init_file (&stream, in);
136   lisp_object_t* root_obj = lisp_read (&stream);
137   
138   if (strcmp(lisp_symbol(lisp_car(root_obj)), "supertux-worldmap") == 0)
139     {
140       lisp_object_t* cur = lisp_cdr(root_obj);
141
142       while(!lisp_nil_p(cur))
143         {
144           lisp_object_t* element = lisp_car(cur);
145
146           if (strcmp(lisp_symbol(lisp_car(element)), "tilemap") == 0)
147             {
148               LispReader reader(lisp_cdr(element));
149               reader.read_int("width",  &width);
150               reader.read_int("height", &height);
151               reader.read_int_vector("data", &tilemap);
152             }
153           else if (strcmp(lisp_symbol(lisp_car(element)), "properties") == 0)
154             {
155               LispReader reader(lisp_cdr(element));
156               reader.read_string("name",  &name);
157               reader.read_string("music", &music);
158             }
159           else if (strcmp(lisp_symbol(lisp_car(element)), "levels") == 0)
160             {
161               lisp_object_t* cur = lisp_cdr(element);
162               
163               while(!lisp_nil_p(cur))
164                 {
165                   lisp_object_t* element = lisp_car(cur);
166                   
167                   if (strcmp(lisp_symbol(lisp_car(element)), "level") == 0)
168                     {
169                       Level level;
170                       LispReader reader(lisp_cdr(element));
171                       reader.read_string("name",  &level.name);
172                       reader.read_int("x-pos", &level.x);
173                       reader.read_int("y-pos", &level.y);
174                       levels.push_back(level);
175                     }
176                   
177                   cur = lisp_cdr(cur);      
178                 }
179             }
180           else
181             {
182               
183             }
184           
185           cur = lisp_cdr(cur);
186         }
187     }
188 }
189
190 void
191 WorldMap::get_input()
192 {
193   SDL_Event event;
194
195   enter_level = false;
196
197   while (SDL_PollEvent(&event))
198     {
199       switch(event.type)
200         {
201         case SDL_QUIT:
202           quit = true;
203           break;
204           
205         case SDL_KEYDOWN:
206           switch(event.key.keysym.sym)
207             {
208             case SDLK_ESCAPE:
209               quit = true;
210               break;
211             case SDLK_LCTRL:
212             case SDLK_RETURN:
213               enter_level = true;
214               break;
215             default:
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   switch(direction)
240     {
241     case WEST:
242       pos.x -= 1;
243       break;
244     case EAST:
245       pos.x += 1;
246       break;
247     case NORTH:
248       pos.y -= 1;
249       break;
250     case SOUTH:
251       pos.y += 1;
252       break;
253     case NONE:
254       break;
255     }
256   return pos;
257 }
258
259 bool
260 WorldMap::path_ok(Direction direction, Point old_pos, Point* new_pos)
261 {
262   *new_pos = get_next_tile(old_pos, direction);
263
264   if (!(new_pos->x >= 0 && new_pos->x < width
265         && new_pos->y >= 0 && new_pos->y < height))
266     { // New position is outsite the tilemap
267       return false;
268     }
269   else
270     { // Check if we the tile allows us to go to new_pos
271       switch(direction)
272         {
273         case WEST:
274           return (at(old_pos)->west && at(*new_pos)->east);
275
276         case EAST:
277           return (at(old_pos)->east && at(*new_pos)->west);
278
279         case NORTH:
280           return (at(old_pos)->north && at(*new_pos)->south);
281
282         case SOUTH:
283           return (at(old_pos)->south && at(*new_pos)->north);
284
285         case NONE:
286           assert(!"path_ok() can't work if direction is NONE");
287         }
288       return false;
289     }
290 }
291
292 void
293 WorldMap::update()
294 {
295   float speed = 4.5;
296
297   if (enter_level && !tux_moving)
298     {
299       for(Levels::iterator i = levels.begin(); i != levels.end(); ++i)
300         {
301           if (i->x == tux_tile_pos.x && 
302               i->y == tux_tile_pos.y)
303             {
304               std::cout << "Enter the current level: " << i->name << std::endl;;
305               halt_music();
306               gameloop(const_cast<char*>((DATA_PREFIX "levels/default/" + i->name).c_str()),
307                        1, ST_GL_LOAD_LEVEL_FILE);
308               play_music(song, 1);
309               break;
310             }
311         }
312     }
313   else
314     {
315       if (!tux_moving)
316         {
317           if (input_direction == NONE)
318             {
319               tux_offset = 0;
320               tux_direction = NONE;
321             }
322           else
323             {
324               Point next_tile;
325               if (path_ok(input_direction, tux_tile_pos, &next_tile))
326                 {
327                   tux_tile_pos = next_tile;
328                   tux_moving = true;
329                   tux_direction = input_direction;
330                 }
331               else
332                 { // Stop
333                   tux_offset = 0;
334                   tux_direction = NONE;
335                 }
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                   Point next_tile;
354                   if (path_ok(tux_direction, tux_tile_pos, &next_tile))
355                     {
356                       tux_tile_pos = next_tile;
357                     }
358                   else
359                     {
360                       puts("Tilemap data is buggy");
361                       tux_direction = NONE;
362                       tux_moving = false;
363                       tux_offset = 0;
364                     }
365                 }
366             }
367         }
368     }
369 }
370
371 Tile*
372 WorldMap::at(Point p)
373 {
374   assert(p.x >= 0 
375          && p.x < width
376          && p.y >= 0
377          && p.y < height);
378   return TileManager::instance()->get(tilemap[width * p.y + p.x]);
379 }
380
381 void
382 WorldMap::draw()
383 {
384   for(int y = 0; y < height; ++y)
385     for(int x = 0; x < width; ++x)
386       {
387         Tile* tile = at(Point(x, y));
388         texture_draw(&tile->sprite, x*32, y*32, NO_UPDATE);
389       }
390   
391   float x = tux_tile_pos.x * 32;
392   float y = tux_tile_pos.y * 32;
393
394   switch(tux_direction)
395     {
396     case WEST:
397       x -= tux_offset - 32;
398       break;
399     case EAST:
400       x += tux_offset - 32;
401       break;
402     case NORTH:
403       y -= tux_offset - 32;
404       break;
405     case SOUTH:
406       y += tux_offset - 32;
407       break;
408     case NONE:
409       break;
410     }
411
412   for(Levels::iterator i = levels.begin(); i != levels.end(); ++i)
413     {
414       texture_draw(&level_sprite, i->x*32, i->y*32, NO_UPDATE);
415     }
416
417   texture_draw(&tux_sprite, (int)x, (int)y, NO_UPDATE);
418   flipscreen();
419 }
420
421 void
422 WorldMap::display()
423 {
424   quit = false;
425
426   song = load_song(const_cast<char*>((DATA_PREFIX "/music/" + music).c_str()));
427   play_music(song, 1);
428
429   while(!quit) {
430     draw();
431     get_input();
432     update();
433     SDL_Delay(20);
434   }
435
436   free_music(song);
437 }
438
439 } // namespace WorldMapNS
440
441 void worldmap_run()
442 {
443   WorldMapNS::WorldMap worldmap;
444   
445   worldmap.display();
446 }
447
448 /* EOF */