- converted texture_type into a Surface class
[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               tile->sprite = new Surface(
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   sprite = new Surface(datadir +  "/images/worldmap/tux.png", USE_ALPHA);
106   offset = 0;
107   moving = false;
108   tile_pos.x = 5;
109   tile_pos.y = 5;
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   sprite->draw((int)x, (int)y);
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   level_sprite = new Surface(datadir +  "/images/worldmap/levelmarker.png", USE_ALPHA);
204   leveldot_green = new Surface(datadir +  "/images/worldmap/leveldot_green.png", USE_ALPHA);
205   leveldot_red = new Surface(datadir +  "/images/worldmap/leveldot_red.png", USE_ALPHA);
206
207   input_direction = NONE;
208   enter_level = false;
209
210   name = "<no name>";
211   music = "SALCON.MOD";
212   song = 0;
213
214   load_map();
215 }
216
217 WorldMap::~WorldMap()
218 {
219   delete tux;
220 }
221
222 void
223 WorldMap::load_map()
224 {
225   std::string filename = datadir +  "levels/default/worldmap.stwm";
226   
227   lisp_object_t* root_obj = lisp_read_from_file(filename);
228   if (!root_obj)
229     st_abort("Couldn't load file", filename);
230   
231   if (strcmp(lisp_symbol(lisp_car(root_obj)), "supertux-worldmap") == 0)
232     {
233       lisp_object_t* cur = lisp_cdr(root_obj);
234
235       while(!lisp_nil_p(cur))
236         {
237           lisp_object_t* element = lisp_car(cur);
238
239           if (strcmp(lisp_symbol(lisp_car(element)), "tilemap") == 0)
240             {
241               LispReader reader(lisp_cdr(element));
242               reader.read_int("width",  &width);
243               reader.read_int("height", &height);
244               reader.read_int_vector("data", &tilemap);
245             }
246           else if (strcmp(lisp_symbol(lisp_car(element)), "properties") == 0)
247             {
248               LispReader reader(lisp_cdr(element));
249               reader.read_string("name",  &name);
250               reader.read_string("music", &music);
251             }
252           else if (strcmp(lisp_symbol(lisp_car(element)), "levels") == 0)
253             {
254               lisp_object_t* cur = lisp_cdr(element);
255               
256               while(!lisp_nil_p(cur))
257                 {
258                   lisp_object_t* element = lisp_car(cur);
259                   
260                   if (strcmp(lisp_symbol(lisp_car(element)), "level") == 0)
261                     {
262                       Level level;
263                       LispReader reader(lisp_cdr(element));
264                       reader.read_string("name",  &level.name);
265                       reader.read_int("x", &level.x);
266                       reader.read_int("y", &level.y);
267                       levels.push_back(level);
268                     }
269                   
270                   cur = lisp_cdr(cur);      
271                 }
272             }
273           else
274             {
275               
276             }
277           
278           cur = lisp_cdr(cur);
279         }
280     }
281 }
282
283 void
284 WorldMap::get_input()
285 {
286   enter_level = false;
287   input_direction = NONE;
288    
289   SDL_Event event;
290   while (SDL_PollEvent(&event))
291     {
292       if(show_menu)
293         {
294           current_menu->event(event);
295         }
296       else
297         {
298           switch(event.type)
299             {
300             case SDL_QUIT:
301               quit = true;
302               break;
303           
304             case SDL_KEYDOWN:
305               switch(event.key.keysym.sym)
306                 {
307                 case SDLK_ESCAPE:
308                   Menu::set_current(worldmap_menu);
309                   show_menu = !show_menu;
310                   break;
311                 case SDLK_LCTRL:
312                 case SDLK_RETURN:
313                   enter_level = true;
314                   break;
315                 default:
316                   break;
317                 }
318               break;
319           
320             case SDL_JOYAXISMOTION:
321               switch(event.jaxis.axis)
322                 {
323                 case JOY_X:
324                   if (event.jaxis.value < -JOYSTICK_DEAD_ZONE)
325                     input_direction = WEST;
326                   else if (event.jaxis.value > JOYSTICK_DEAD_ZONE)
327                     input_direction = EAST;
328                   break;
329                 case JOY_Y:
330                   if (event.jaxis.value > JOYSTICK_DEAD_ZONE)
331                     input_direction = SOUTH;
332                   else if (event.jaxis.value < -JOYSTICK_DEAD_ZONE)
333                     input_direction = NORTH;
334                   break;
335                 }
336               break;
337
338             case SDL_JOYBUTTONDOWN:
339               if (event.jbutton.button == JOY_B)
340                 enter_level = true;
341               break;
342
343             default:
344               break;
345             }
346         }
347     }
348
349   if (!show_menu)
350     {
351       Uint8 *keystate = SDL_GetKeyState(NULL);
352   
353       if (keystate[SDLK_LEFT])
354         input_direction = WEST;
355       else if (keystate[SDLK_RIGHT])
356         input_direction = EAST;
357       else if (keystate[SDLK_UP])
358         input_direction = NORTH;
359       else if (keystate[SDLK_DOWN])
360         input_direction = SOUTH;
361     }
362 }
363
364 Point
365 WorldMap::get_next_tile(Point pos, Direction direction)
366 {
367   switch(direction)
368     {
369     case WEST:
370       pos.x -= 1;
371       break;
372     case EAST:
373       pos.x += 1;
374       break;
375     case NORTH:
376       pos.y -= 1;
377       break;
378     case SOUTH:
379       pos.y += 1;
380       break;
381     case NONE:
382       break;
383     }
384   return pos;
385 }
386
387 bool
388 WorldMap::path_ok(Direction direction, Point old_pos, Point* new_pos)
389 {
390   *new_pos = get_next_tile(old_pos, direction);
391
392   if (!(new_pos->x >= 0 && new_pos->x < width
393         && new_pos->y >= 0 && new_pos->y < height))
394     { // New position is outsite the tilemap
395       return false;
396     }
397   else
398     { // Check if we the tile allows us to go to new_pos
399       switch(direction)
400         {
401         case WEST:
402           return (at(old_pos)->west && at(*new_pos)->east);
403
404         case EAST:
405           return (at(old_pos)->east && at(*new_pos)->west);
406
407         case NORTH:
408           return (at(old_pos)->north && at(*new_pos)->south);
409
410         case SOUTH:
411           return (at(old_pos)->south && at(*new_pos)->north);
412
413         case NONE:
414           assert(!"path_ok() can't work if direction is NONE");
415         }
416       return false;
417     }
418 }
419
420 void
421 WorldMap::update()
422 {
423   if (enter_level && !tux->is_moving())
424     {
425       for(Levels::iterator i = levels.begin(); i != levels.end(); ++i)
426         {
427           if (i->x == tux->get_tile_pos().x && 
428               i->y == tux->get_tile_pos().y)
429             {
430               std::cout << "Enter the current level: " << i->name << std::endl;;
431               halt_music();
432               GameSession session(datadir +  "levels/default/" + i->name,
433                                   1, ST_GL_LOAD_LEVEL_FILE);
434               session.run();
435               play_music(song, 1);
436               return;
437             }
438         }
439       std::cout << "Nothing to enter at: "
440                 << tux->get_tile_pos().x << ", " << tux->get_tile_pos().y << std::endl;
441     }
442   else
443     {
444       tux->set_direction(input_direction);
445       tux->update(0.33f);
446     }
447   
448   if(show_menu)
449     {
450       if(current_menu == worldmap_menu)
451         {
452           switch (worldmap_menu->check())
453             {
454             case 2: // Return to game
455               menu_reset();
456               break;
457             case 5: // Quit Worldmap
458               quit = true;
459               break;
460             }
461         }
462     }
463 }
464
465 Tile*
466 WorldMap::at(Point p)
467 {
468   assert(p.x >= 0 
469          && p.x < width
470          && p.y >= 0
471          && p.y < height);
472   return TileManager::instance()->get(tilemap[width * p.y + p.x]);
473 }
474
475 void
476 WorldMap::draw()
477 {
478   for(int y = 0; y < height; ++y)
479     for(int x = 0; x < width; ++x)
480       {
481         Tile* tile = at(Point(x, y));
482         tile->sprite->draw(x*32, y*32);
483       }
484   
485   for(Levels::iterator i = levels.begin(); i != levels.end(); ++i)
486     {
487       leveldot_green->draw(i->x*32, i->y*32);
488     }
489
490   tux->draw();
491 }
492
493 void
494 WorldMap::display()
495 {
496   quit = false;
497
498   song = load_song(datadir +  "/music/" + music);
499   play_music(song, 1);
500
501   while(!quit) {
502     draw();
503     get_input();
504     update();
505
506     menu_process_current();
507     flipscreen();
508
509     SDL_Delay(20);
510   }
511
512   free_music(song);
513 }
514
515 } // namespace WorldMapNS
516
517 void worldmap_run()
518 {
519   WorldMapNS::WorldMap worldmap;
520   
521   worldmap.display();
522 }
523
524 /* EOF */