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