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