- replaced YES/NO with true/false
[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 "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 filename = datadir +  "images/worldmap/antarctica.scm";
38   lisp_object_t* root_obj = lisp_read_from_file(filename);
39  
40   if (!root_obj)
41     st_abort("Couldn't load file", 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 = 0;
109   tile_pos.y = 0;
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, NO_UPDATE);
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
205   input_direction = NONE;
206   enter_level = false;
207
208   name = "<no name>";
209   music = "SALCON.MOD";
210   song = 0;
211
212   load_map();
213 }
214
215 WorldMap::~WorldMap()
216 {
217   delete tux;
218 }
219
220 void
221 WorldMap::load_map()
222 {
223   std::string filename = datadir +  "levels/default/worldmap.stwm";
224   
225   lisp_object_t* root_obj = lisp_read_from_file(filename);
226   if (!root_obj)
227     st_abort("Couldn't load file", filename);
228   
229   if (strcmp(lisp_symbol(lisp_car(root_obj)), "supertux-worldmap") == 0)
230     {
231       lisp_object_t* cur = lisp_cdr(root_obj);
232
233       while(!lisp_nil_p(cur))
234         {
235           lisp_object_t* element = lisp_car(cur);
236
237           if (strcmp(lisp_symbol(lisp_car(element)), "tilemap") == 0)
238             {
239               LispReader reader(lisp_cdr(element));
240               reader.read_int("width",  &width);
241               reader.read_int("height", &height);
242               reader.read_int_vector("data", &tilemap);
243             }
244           else if (strcmp(lisp_symbol(lisp_car(element)), "properties") == 0)
245             {
246               LispReader reader(lisp_cdr(element));
247               reader.read_string("name",  &name);
248               reader.read_string("music", &music);
249             }
250           else if (strcmp(lisp_symbol(lisp_car(element)), "levels") == 0)
251             {
252               lisp_object_t* cur = lisp_cdr(element);
253               
254               while(!lisp_nil_p(cur))
255                 {
256                   lisp_object_t* element = lisp_car(cur);
257                   
258                   if (strcmp(lisp_symbol(lisp_car(element)), "level") == 0)
259                     {
260                       Level level;
261                       LispReader reader(lisp_cdr(element));
262                       reader.read_string("name",  &level.name);
263                       reader.read_int("x-pos", &level.x);
264                       reader.read_int("y-pos", &level.y);
265                       levels.push_back(level);
266                     }
267                   
268                   cur = lisp_cdr(cur);      
269                 }
270             }
271           else
272             {
273               
274             }
275           
276           cur = lisp_cdr(cur);
277         }
278     }
279 }
280
281 void
282 WorldMap::get_input()
283 {
284   SDL_Event event;
285
286   enter_level = false;
287
288   while (SDL_PollEvent(&event))
289     {
290       switch(event.type)
291         {
292         case SDL_QUIT:
293           quit = true;
294           break;
295           
296         case SDL_KEYDOWN:
297           switch(event.key.keysym.sym)
298             {
299             case SDLK_ESCAPE:
300               quit = true;
301               break;
302             case SDLK_LCTRL:
303             case SDLK_RETURN:
304               enter_level = true;
305               break;
306             default:
307               break;
308             }
309           break;
310         }
311     }
312
313   Uint8 *keystate = SDL_GetKeyState(NULL);
314   
315   input_direction = NONE;
316   
317   if (keystate[SDLK_LEFT])
318     input_direction = WEST;
319   else if (keystate[SDLK_RIGHT])
320     input_direction = EAST;
321   else if (keystate[SDLK_UP])
322     input_direction = NORTH;
323   else if (keystate[SDLK_DOWN])
324     input_direction = SOUTH;
325 }
326
327 Point
328 WorldMap::get_next_tile(Point pos, Direction direction)
329 {
330   switch(direction)
331     {
332     case WEST:
333       pos.x -= 1;
334       break;
335     case EAST:
336       pos.x += 1;
337       break;
338     case NORTH:
339       pos.y -= 1;
340       break;
341     case SOUTH:
342       pos.y += 1;
343       break;
344     case NONE:
345       break;
346     }
347   return pos;
348 }
349
350 bool
351 WorldMap::path_ok(Direction direction, Point old_pos, Point* new_pos)
352 {
353   *new_pos = get_next_tile(old_pos, direction);
354
355   if (!(new_pos->x >= 0 && new_pos->x < width
356         && new_pos->y >= 0 && new_pos->y < height))
357     { // New position is outsite the tilemap
358       return false;
359     }
360   else
361     { // Check if we the tile allows us to go to new_pos
362       switch(direction)
363         {
364         case WEST:
365           return (at(old_pos)->west && at(*new_pos)->east);
366
367         case EAST:
368           return (at(old_pos)->east && at(*new_pos)->west);
369
370         case NORTH:
371           return (at(old_pos)->north && at(*new_pos)->south);
372
373         case SOUTH:
374           return (at(old_pos)->south && at(*new_pos)->north);
375
376         case NONE:
377           assert(!"path_ok() can't work if direction is NONE");
378         }
379       return false;
380     }
381 }
382
383 void
384 WorldMap::update()
385 {
386   if (enter_level && !tux->is_moving())
387     {
388       for(Levels::iterator i = levels.begin(); i != levels.end(); ++i)
389         {
390           if (i->x == tux->get_tile_pos().x && 
391               i->y == tux->get_tile_pos().y)
392             {
393               std::cout << "Enter the current level: " << i->name << std::endl;;
394               halt_music();
395               gameloop(const_cast<char*>((datadir +  "levels/default/" + i->name).c_str()),
396                        1, ST_GL_LOAD_LEVEL_FILE);
397               play_music(song, 1);
398               break;
399             }
400         }
401     }
402   else
403     {
404       tux->set_direction(input_direction);
405       tux->update(0.33f);
406     }
407 }
408
409 Tile*
410 WorldMap::at(Point p)
411 {
412   assert(p.x >= 0 
413          && p.x < width
414          && p.y >= 0
415          && p.y < height);
416   return TileManager::instance()->get(tilemap[width * p.y + p.x]);
417 }
418
419 void
420 WorldMap::draw()
421 {
422   for(int y = 0; y < height; ++y)
423     for(int x = 0; x < width; ++x)
424       {
425         Tile* tile = at(Point(x, y));
426         texture_draw(&tile->sprite, x*32, y*32, NO_UPDATE);
427       }
428   
429   for(Levels::iterator i = levels.begin(); i != levels.end(); ++i)
430     {
431       texture_draw(&level_sprite, i->x*32, i->y*32, NO_UPDATE);
432     }
433
434   tux->draw();
435   flipscreen();
436 }
437
438 void
439 WorldMap::display()
440 {
441   quit = false;
442
443   song = load_song(datadir +  "/music/" + music);
444   play_music(song, 1);
445
446   while(!quit) {
447     draw();
448     get_input();
449     update();
450     SDL_Delay(20);
451   }
452
453   free_music(song);
454 }
455
456 } // namespace WorldMapNS
457
458 void worldmap_run()
459 {
460   WorldMapNS::WorldMap worldmap;
461   
462   worldmap.display();
463 }
464
465 /* EOF */