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