4cbd378bb9964179bb160e0827ba3562814c036c
[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               if (!tux_moving)
214                 enter_level = true;
215               break;
216             default:
217               break;
218             }
219           break;
220         }
221     }
222
223   Uint8 *keystate = SDL_GetKeyState(NULL);
224   
225   input_direction = NONE;
226   
227   if (keystate[SDLK_LEFT])
228     input_direction = WEST;
229   else if (keystate[SDLK_RIGHT])
230     input_direction = EAST;
231   else if (keystate[SDLK_UP])
232     input_direction = NORTH;
233   else if (keystate[SDLK_DOWN])
234     input_direction = SOUTH;
235 }
236
237 Point
238 WorldMap::get_next_tile(Point pos, Direction direction)
239 {
240   switch(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 bool
261 WorldMap::path_ok(Direction direction, Point old_pos, Point* new_pos)
262 {
263   *new_pos = get_next_tile(old_pos, direction);
264
265   if (!(new_pos->x >= 0 && new_pos->x < width
266         && new_pos->y >= 0 && new_pos->y < height))
267     { // New position is outsite the tilemap
268       return false;
269     }
270   else
271     { // Check if we the tile allows us to go to new_pos
272       switch(direction)
273         {
274         case WEST:
275           return (at(old_pos)->west && at(*new_pos)->east);
276
277         case EAST:
278           return (at(old_pos)->east && at(*new_pos)->west);
279
280         case NORTH:
281           return (at(old_pos)->north && at(*new_pos)->south);
282
283         case SOUTH:
284           return (at(old_pos)->south && at(*new_pos)->north);
285
286         case NONE:
287           assert(!"path_ok() can't work if direction is NONE");
288         }
289       return false;
290     }
291 }
292
293 void
294 WorldMap::update()
295 {
296   float speed = 4.5;
297
298   if (enter_level)
299     {
300       for(Levels::iterator i = levels.begin(); i != levels.end(); ++i)
301         {
302           if (i->x == tux_tile_pos.x && 
303               i->y == tux_tile_pos.y)
304             {
305               std::cout << "Enter the current level: " << i->name << std::endl;;
306               halt_music();
307               gameloop(const_cast<char*>((DATA_PREFIX "levels/default/" + i->name).c_str()),
308                        1, ST_GL_LOAD_LEVEL_FILE);
309               play_music(song, 1);
310               break;
311             }
312         }
313     }
314   else
315     {
316       if (!tux_moving)
317         {
318           if (input_direction == NONE)
319             {
320               tux_offset = 0;
321               tux_direction = NONE;
322             }
323           else
324             {
325               Point next_tile;
326               if (path_ok(input_direction, tux_tile_pos, &next_tile))
327                 {
328                   tux_tile_pos = next_tile;
329                   tux_moving = true;
330                   tux_direction = input_direction;
331                 }
332               else
333                 { // Stop
334                   tux_offset = 0;
335                   tux_direction = NONE;
336                 }
337             }
338         }
339       else
340         {
341           tux_offset += speed;
342
343           if (tux_offset > 32)
344             {
345               tux_offset -= 32;
346
347               if (at(tux_tile_pos)->stop)
348                 {
349                   tux_direction = NONE;
350                   tux_moving = false;
351                 }
352               else
353                 {
354                   Point next_tile;
355                   if (path_ok(tux_direction, tux_tile_pos, &next_tile))
356                     {
357                       tux_tile_pos = next_tile;
358                     }
359                   else
360                     {
361                       puts("Tilemap data is buggy");
362                       tux_direction = NONE;
363                       tux_moving = false;
364                       tux_offset = 0;
365                     }
366                 }
367             }
368         }
369     }
370 }
371
372 Tile*
373 WorldMap::at(Point p)
374 {
375   assert(p.x >= 0 
376          && p.x < width
377          && p.y >= 0
378          && p.y < height);
379   return TileManager::instance()->get(tilemap[width * p.y + p.x]);
380 }
381
382 void
383 WorldMap::draw()
384 {
385   for(int y = 0; y < height; ++y)
386     for(int x = 0; x < width; ++x)
387       {
388         Tile* tile = at(Point(x, y));
389         texture_draw(&tile->sprite, x*32, y*32, NO_UPDATE);
390       }
391   
392   float x = tux_tile_pos.x * 32;
393   float y = tux_tile_pos.y * 32;
394
395   switch(tux_direction)
396     {
397     case WEST:
398       x -= tux_offset - 32;
399       break;
400     case EAST:
401       x += tux_offset - 32;
402       break;
403     case NORTH:
404       y -= tux_offset - 32;
405       break;
406     case SOUTH:
407       y += tux_offset - 32;
408       break;
409     case NONE:
410       break;
411     }
412
413   for(Levels::iterator i = levels.begin(); i != levels.end(); ++i)
414     {
415       texture_draw(&level_sprite, i->x*32, i->y*32, NO_UPDATE);
416     }
417
418   texture_draw(&tux_sprite, (int)x, (int)y, NO_UPDATE);
419   flipscreen();
420 }
421
422 void
423 WorldMap::display()
424 {
425   quit = false;
426
427   song = load_song(const_cast<char*>((DATA_PREFIX "/music/" + music).c_str()));
428   play_music(song, 1);
429
430   while(!quit) {
431     draw();
432     get_input();
433     update();
434     SDL_Delay(20);
435   }
436
437   free_music(song);
438 }
439
440 } // namespace WorldMapNS
441
442 void worldmap_run()
443 {
444   WorldMapNS::WorldMap worldmap;
445   
446   worldmap.display();
447 }
448
449 /* EOF */