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