86249cc91be544ad3c0fb7cdba7fa56db6ff2b1b
[supertux.git] / src / worldmap.cpp
1 //  $Id$
2 //
3 //  SuperTux -  A Jump'n Run
4 //  Copyright (C) 2004 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 stwt_filename = datadir +  "images/worldmap/antarctica.stwt";
38   lisp_object_t* root_obj = lisp_read_from_file(stwt_filename);
39  
40   if (!root_obj)
41     st_abort("Couldn't load file", stwt_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", &level.x);
264                       reader.read_int("y", &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   input_direction = NONE;
288
289   while (SDL_PollEvent(&event))
290     {
291       switch(event.type)
292         {
293         case SDL_QUIT:
294           quit = true;
295           break;
296           
297         case SDL_KEYDOWN:
298           switch(event.key.keysym.sym)
299             {
300             case SDLK_ESCAPE:
301               quit = true;
302               break;
303             case SDLK_LCTRL:
304             case SDLK_RETURN:
305               enter_level = true;
306               break;
307             default:
308               break;
309             }
310           break;
311           
312         case SDL_JOYAXISMOTION:
313           switch(event.jaxis.axis)
314             {
315             case JOY_X:
316               if (event.jaxis.value < -JOYSTICK_DEAD_ZONE)
317                 input_direction = WEST;
318               else if (event.jaxis.value > JOYSTICK_DEAD_ZONE)
319                 input_direction = EAST;
320               break;
321             case JOY_Y:
322               if (event.jaxis.value > JOYSTICK_DEAD_ZONE)
323                 input_direction = SOUTH;
324               else if (event.jaxis.value < -JOYSTICK_DEAD_ZONE)
325                 input_direction = NORTH;
326               break;
327             }
328           break;
329
330         case SDL_JOYBUTTONDOWN:
331           if (event.jbutton.button == JOY_B)
332             enter_level = true;
333           break;
334
335         default:
336           break;
337         }
338     }
339
340   Uint8 *keystate = SDL_GetKeyState(NULL);
341   
342   if (keystate[SDLK_LEFT])
343     input_direction = WEST;
344   else if (keystate[SDLK_RIGHT])
345     input_direction = EAST;
346   else if (keystate[SDLK_UP])
347     input_direction = NORTH;
348   else if (keystate[SDLK_DOWN])
349     input_direction = SOUTH;
350 }
351
352 Point
353 WorldMap::get_next_tile(Point pos, Direction direction)
354 {
355   switch(direction)
356     {
357     case WEST:
358       pos.x -= 1;
359       break;
360     case EAST:
361       pos.x += 1;
362       break;
363     case NORTH:
364       pos.y -= 1;
365       break;
366     case SOUTH:
367       pos.y += 1;
368       break;
369     case NONE:
370       break;
371     }
372   return pos;
373 }
374
375 bool
376 WorldMap::path_ok(Direction direction, Point old_pos, Point* new_pos)
377 {
378   *new_pos = get_next_tile(old_pos, direction);
379
380   if (!(new_pos->x >= 0 && new_pos->x < width
381         && new_pos->y >= 0 && new_pos->y < height))
382     { // New position is outsite the tilemap
383       return false;
384     }
385   else
386     { // Check if we the tile allows us to go to new_pos
387       switch(direction)
388         {
389         case WEST:
390           return (at(old_pos)->west && at(*new_pos)->east);
391
392         case EAST:
393           return (at(old_pos)->east && at(*new_pos)->west);
394
395         case NORTH:
396           return (at(old_pos)->north && at(*new_pos)->south);
397
398         case SOUTH:
399           return (at(old_pos)->south && at(*new_pos)->north);
400
401         case NONE:
402           assert(!"path_ok() can't work if direction is NONE");
403         }
404       return false;
405     }
406 }
407
408 void
409 WorldMap::update()
410 {
411   if (enter_level && !tux->is_moving())
412     {
413       for(Levels::iterator i = levels.begin(); i != levels.end(); ++i)
414         {
415           if (i->x == tux->get_tile_pos().x && 
416               i->y == tux->get_tile_pos().y)
417             {
418               std::cout << "Enter the current level: " << i->name << std::endl;;
419               halt_music();
420               gameloop(const_cast<char*>((datadir +  "levels/default/" + i->name).c_str()),
421                        1, ST_GL_LOAD_LEVEL_FILE);
422               play_music(song, 1);
423               return;
424             }
425         }
426       std::cout << "Nothing to enter at: "
427                 << tux->get_tile_pos().x << ", " << tux->get_tile_pos().y << std::endl;
428     }
429   else
430     {
431       tux->set_direction(input_direction);
432       tux->update(0.33f);
433     }
434 }
435
436 Tile*
437 WorldMap::at(Point p)
438 {
439   assert(p.x >= 0 
440          && p.x < width
441          && p.y >= 0
442          && p.y < height);
443   return TileManager::instance()->get(tilemap[width * p.y + p.x]);
444 }
445
446 void
447 WorldMap::draw()
448 {
449   for(int y = 0; y < height; ++y)
450     for(int x = 0; x < width; ++x)
451       {
452         Tile* tile = at(Point(x, y));
453         texture_draw(&tile->sprite, x*32, y*32, NO_UPDATE);
454       }
455   
456   for(Levels::iterator i = levels.begin(); i != levels.end(); ++i)
457     {
458       texture_draw(&level_sprite, i->x*32, i->y*32, NO_UPDATE);
459     }
460
461   tux->draw();
462   flipscreen();
463 }
464
465 void
466 WorldMap::display()
467 {
468   quit = false;
469
470   song = load_song(datadir +  "/music/" + music);
471   play_music(song, 1);
472
473   while(!quit) {
474     draw();
475     get_input();
476     update();
477     SDL_Delay(20);
478   }
479
480   free_music(song);
481 }
482
483 } // namespace WorldMapNS
484
485 void worldmap_run()
486 {
487   WorldMapNS::WorldMap worldmap;
488   
489   worldmap.display();
490 }
491
492 /* EOF */