1 // $Id: level.cpp 1634 2004-07-27 16:43:23Z rmcruz $
2 //
3 // SuperTux
4 // Copyright (C) 2004 SuperTux Development Team, see AUTHORS for details
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
19 // 02111-1307, USA.
20
21 #include <map>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <iostream>
26 #include "globals.h"
27 #include "setup.h"
28 #include "screen.h"
29 #include "level.h"
30 #include "physic.h"
31 #include "scene.h"
32 #include "tile.h"
33 #include "lispreader.h"
34 #include "resources.h"
35 #include "music_manager.h"
36
37 using namespace std;
38
LevelSubset()39 LevelSubset::LevelSubset()
40 : image(0), levels(0)
41 {
42 }
43
~LevelSubset()44 LevelSubset::~LevelSubset()
45 {
46 delete image;
47 }
48
create(const std::string & subset_name)49 void LevelSubset::create(const std::string& subset_name)
50 {
51 Level new_lev;
52 LevelSubset new_subset;
53 new_subset.name = subset_name;
54 new_subset.title = "Unknown Title";
55 new_subset.description = "No description so far.";
56 new_subset.save();
57 new_lev.init_defaults();
58 new_lev.save(subset_name, 1);
59 }
60
parse(lisp_object_t * cursor)61 void LevelSubset::parse (lisp_object_t* cursor)
62 {
63 while(!lisp_nil_p(cursor))
64 {
65 lisp_object_t* cur = lisp_car(cursor);
66 char *s;
67
68 if (!lisp_cons_p(cur) || !lisp_symbol_p (lisp_car(cur)))
69 {
70 printf("Not good");
71 }
72 else
73 {
74 if (strcmp(lisp_symbol(lisp_car(cur)), "title") == 0)
75 {
76 if(( s = lisp_string(lisp_car(lisp_cdr(cur)))) != NULL)
77 {
78 title = s;
79 }
80 }
81 else if (strcmp(lisp_symbol(lisp_car(cur)), "description") == 0)
82 {
83 if(( s = lisp_string(lisp_car(lisp_cdr(cur)))) != NULL)
84 {
85 description = s;
86 }
87 }
88 }
89 cursor = lisp_cdr (cursor);
90 }
91 }
92
load(char * subset)93 void LevelSubset::load(char *subset)
94 {
95 FILE* fi;
96 char filename[1024];
97 char str[1024];
98 int i;
99 lisp_object_t* root_obj = 0;
100
101 name = subset;
102
103 snprintf(filename, 1024, "%s/levels/%s/info", st_dir, subset);
104 if(!faccessible(filename))
105 snprintf(filename, 1024, "%s/levels/%s/info", datadir.c_str(), subset);
106 if(faccessible(filename))
107 {
108 fi = fopen(filename, "r");
109 if (fi == NULL)
110 {
111 perror(filename);
112 }
113 lisp_stream_t stream;
114 lisp_stream_init_file (&stream, fi);
115 root_obj = lisp_read (&stream);
116
117 if (root_obj->type == LISP_TYPE_EOF || root_obj->type == LISP_TYPE_PARSE_ERROR)
118 {
119 printf("World: Parse Error in file %s", filename);
120 }
121
122 lisp_object_t* cur = lisp_car(root_obj);
123
124 if (!lisp_symbol_p (cur))
125 {
126 printf("World: Read error in %s",filename);
127 }
128
129 if (strcmp(lisp_symbol(cur), "supertux-level-subset") == 0)
130 {
131 parse(lisp_cdr(root_obj));
132
133 }
134
135 lisp_free(root_obj);
136 fclose(fi);
137
138 snprintf(str, 1024, "%s.png", filename);
139 if(faccessible(str))
140 {
141 delete image;
142 image = new Surface(str,IGNORE_ALPHA);
143 }
144 else
145 {
146 snprintf(filename, 1024, "%s/images/status/level-subset-info.png", datadir.c_str());
147 delete image;
148 image = new Surface(filename,IGNORE_ALPHA);
149 }
150 }
151
152 for(i=1; i != -1; ++i)
153 {
154 /* Get the number of levels in this subset */
155 snprintf(filename, 1024, "%s/levels/%s/level%d.stl", st_dir, subset,i);
156 if(!faccessible(filename))
157 {
158 snprintf(filename, 1024, "%s/levels/%s/level%d.stl", datadir.c_str(), subset,i);
159 if(!faccessible(filename))
160 break;
161 }
162 }
163 levels = --i;
164 }
165
save()166 void LevelSubset::save()
167 {
168 FILE* fi;
169 string filename;
170
171 /* Save data file: */
172 filename = "/levels/" + name + "/";
173
174 fcreatedir(filename.c_str());
175 filename = string(st_dir) + "/levels/" + name + "/info";
176 if(!fwriteable(filename.c_str()))
177 filename = datadir + "/levels/" + name + "/info";
178 if(fwriteable(filename.c_str()))
179 {
180 fi = fopen(filename.c_str(), "w");
181 if (fi == NULL)
182 {
183 perror(filename.c_str());
184 }
185
186 /* Write header: */
187 fprintf(fi,";SuperTux-Level-Subset\n");
188 fprintf(fi,"(supertux-level-subset\n");
189
190 /* Save title info: */
191 fprintf(fi," (title \"%s\")\n", title.c_str());
192
193 /* Save the description: */
194 fprintf(fi," (description \"%s\")\n", description.c_str());
195
196 fprintf( fi,")");
197 fclose(fi);
198
199 }
200 }
201
Level()202 Level::Level()
203 : img_bkgd(0)
204 {
205 init_defaults();
206 }
207
Level(const std::string & subset,int level)208 Level::Level(const std::string& subset, int level)
209 : img_bkgd(0)
210 {
211 if(load(subset, level) < 0)
212 st_abort("Couldn't load level from subset", subset.c_str());
213 }
214
Level(const std::string & filename)215 Level::Level(const std::string& filename)
216 : img_bkgd(0)
217 {
218 if(load(filename) < 0)
219 st_abort("Couldn't load level " , filename.c_str());
220 }
221
~Level()222 Level::~Level()
223 {
224 delete img_bkgd;
225 }
226
227 void
init_defaults()228 Level::init_defaults()
229 {
230 name = "UnNamed";
231 author = "UnNamed";
232 song_title = "Mortimers_chipdisko.mod";
233 bkgd_image = "arctis.png";
234 width = 21;
235 start_pos_x = 100;
236 start_pos_y = 170;
237 time_left = 100;
238 gravity = 10.;
239 back_scrolling = false;
240 hor_autoscroll_speed = 0;
241 bkgd_speed = 50;
242 bkgd_top.red = 0;
243 bkgd_top.green = 0;
244 bkgd_top.blue = 0;
245 bkgd_bottom.red = 255;
246 bkgd_bottom.green = 255;
247 bkgd_bottom.blue = 255;
248
249 for(int i = 0; i < 15; ++i)
250 {
251 ia_tiles[i].resize(width+1, 0);
252 ia_tiles[i][width] = (unsigned int) '\0';
253
254 for(int y = 0; y < width; ++y)
255 ia_tiles[i][y] = 0;
256
257 bg_tiles[i].resize(width+1, 0);
258 bg_tiles[i][width] = (unsigned int) '\0';
259 for(int y = 0; y < width; ++y)
260 bg_tiles[i][y] = 0;
261
262 fg_tiles[i].resize(width+1, 0);
263 fg_tiles[i][width] = (unsigned int) '\0';
264 for(int y = 0; y < width; ++y)
265 fg_tiles[i][y] = 0;
266 }
267 }
268
269 int
load(const std::string & subset,int level)270 Level::load(const std::string& subset, int level)
271 {
272 char filename[1024];
273
274 // Load data file:
275 snprintf(filename, 1024, "%s/levels/%s/level%d.stl", st_dir, subset.c_str(), level);
276 if(!faccessible(filename))
277 snprintf(filename, 1024, "%s/levels/%s/level%d.stl", datadir.c_str(), subset.c_str(), level);
278
279 return load(filename);
280 }
281
282 int
load(const std::string & filename)283 Level::load(const std::string& filename)
284 {
285 lisp_object_t* root_obj = lisp_read_from_file(filename);
286 if (!root_obj)
287 {
288 std::cout << "Level: Couldn't load file: " << filename << std::endl;
289 return -1;
290 }
291
292 if (root_obj->type == LISP_TYPE_EOF || root_obj->type == LISP_TYPE_PARSE_ERROR)
293 {
294 printf("World: Parse Error in file %s", filename.c_str());
295 return -1;
296 }
297
298 vector<int> ia_tm;
299 vector<int> bg_tm;
300 vector<int> fg_tm;
301
302 int version = 0;
303 if (strcmp(lisp_symbol(lisp_car(root_obj)), "supertux-level") == 0)
304 {
305 LispReader reader(lisp_cdr(root_obj));
306 version = 0;
307 reader.read_int("version", &version);
308 if(!reader.read_int("width", &width))
309 st_abort("No width specified for level.", "");
310 if (!reader.read_int("start_pos_x", &start_pos_x)) start_pos_x = 100;
311 if (!reader.read_int("start_pos_y", &start_pos_y)) start_pos_y = 170;
312 time_left = 500;
313 if(!reader.read_int("time", &time_left)) {
314 printf("Warning no time specified for level.\n");
315 }
316
317 back_scrolling = false;
318 reader.read_bool("back_scrolling", &back_scrolling);
319
320 hor_autoscroll_speed = 0;
321 reader.read_float("hor_autoscroll_speed", &hor_autoscroll_speed);
322
323 bkgd_speed = 50;
324 reader.read_int("bkgd_speed", &bkgd_speed);
325
326
327 bkgd_top.red = bkgd_top.green = bkgd_top.blue = 0;
328 reader.read_int("bkgd_red_top", &bkgd_top.red);
329 reader.read_int("bkgd_green_top", &bkgd_top.green);
330 reader.read_int("bkgd_blue_top", &bkgd_top.blue);
331
332 bkgd_bottom.red = bkgd_bottom.green = bkgd_bottom.blue = 0;
333 reader.read_int("bkgd_red_bottom", &bkgd_bottom.red);
334 reader.read_int("bkgd_green_bottom", &bkgd_bottom.green);
335 reader.read_int("bkgd_blue_bottom", &bkgd_bottom.blue);
336
337 gravity = 10;
338 reader.read_float("gravity", &gravity);
339 name = "Noname";
340 reader.read_string("name", &name);
341 author = "unknown author";
342 reader.read_string("author", &author);
343 song_title = "";
344 reader.read_string("music", &song_title);
345 bkgd_image = "";
346 reader.read_string("background", &bkgd_image);
347 particle_system = "";
348 reader.read_string("particle_system", &particle_system);
349
350 reader.read_int_vector("background-tm", &bg_tm);
351
352 if (!reader.read_int_vector("interactive-tm", &ia_tm))
353 reader.read_int_vector("tilemap", &ia_tm);
354
355 reader.read_int_vector("foreground-tm", &fg_tm);
356
357 { // Read ResetPoints
358 lisp_object_t* cur = 0;
359 if (reader.read_lisp("reset-points", &cur))
360 {
361 while (!lisp_nil_p(cur))
362 {
363 lisp_object_t* data = lisp_car(cur);
364
365 ResetPoint pos;
366
367 LispReader reader(lisp_cdr(data));
368 if (reader.read_int("x", &pos.x)
369 && reader.read_int("y", &pos.y))
370 {
371 reset_points.push_back(pos);
372 }
373
374 cur = lisp_cdr(cur);
375 }
376 }
377 }
378
379 { // Read BadGuys
380 lisp_object_t* cur = 0;
381 if (reader.read_lisp("objects", &cur))
382 {
383 while (!lisp_nil_p(cur))
384 {
385 lisp_object_t* data = lisp_car(cur);
386
387 BadGuyData bg_data;
388 bg_data.kind = badguykind_from_string(lisp_symbol(lisp_car(data)));
389 LispReader reader(lisp_cdr(data));
390 reader.read_int("x", &bg_data.x);
391 reader.read_int("y", &bg_data.y);
392 reader.read_bool("stay-on-platform", &bg_data.stay_on_platform);
393
394 badguy_data.push_back(bg_data);
395
396 cur = lisp_cdr(cur);
397 }
398 }
399 }
400
401 // Convert old levels to the new tile numbers
402 if (version == 0)
403 {
404 std::map<char, int> transtable;
405 transtable['.'] = 0;
406 transtable['x'] = 104;
407 transtable['X'] = 77;
408 transtable['y'] = 78;
409 transtable['Y'] = 105;
410 transtable['A'] = 83;
411 transtable['B'] = 102;
412 transtable['!'] = 103;
413 transtable['a'] = 84;
414 transtable['C'] = 85;
415 transtable['D'] = 86;
416 transtable['E'] = 87;
417 transtable['F'] = 88;
418 transtable['c'] = 89;
419 transtable['d'] = 90;
420 transtable['e'] = 91;
421 transtable['f'] = 92;
422
423 transtable['G'] = 93;
424 transtable['H'] = 94;
425 transtable['I'] = 95;
426 transtable['J'] = 96;
427
428 transtable['g'] = 97;
429 transtable['h'] = 98;
430 transtable['i'] = 99;
431 transtable['j'] = 100
432 ;
433 transtable['#'] = 11;
434 transtable['['] = 13;
435 transtable['='] = 14;
436 transtable[']'] = 15;
437 transtable['$'] = 82;
438 transtable['^'] = 76;
439 transtable['*'] = 80;
440 transtable['|'] = 79;
441 transtable['\\'] = 81;
442 transtable['&'] = 75;
443
444 int x = 0;
445 int y = 0;
446 for(std::vector<int>::iterator i = ia_tm.begin(); i != ia_tm.end(); ++i)
447 {
448 if (*i == '0' || *i == '1' || *i == '2')
449 {
450 badguy_data.push_back(BadGuyData(static_cast<BadGuyKind>(*i-'0'),
451 x*32, y*32, false));
452 *i = 0;
453 }
454 else
455 {
456 std::map<char, int>::iterator j = transtable.find(*i);
457 if (j != transtable.end())
458 *i = j->second;
459 else
460 printf("Error: conversion will fail, unsupported char: '%c' (%d)\n", *i, *i);
461 }
462 ++x;
463 if (x >= width)
464 {
465 x = 0;
466 ++y;
467 }
468 }
469 }
470 }
471
472 for(int i = 0; i < 15; ++i)
473 {
474 ia_tiles[i].resize(width + 1, 0);
475 bg_tiles[i].resize(width + 1, 0);
476 fg_tiles[i].resize(width + 1, 0);
477 }
478
479 int i = 0;
480 int j = 0;
481 for(vector<int>::iterator it = ia_tm.begin(); it != ia_tm.end(); ++it, ++i)
482 {
483 if(j >= 15)
484 {
485 std::cerr << "Warning: Level higher than 15 interactive tiles."
486 "Ignoring by cutting tiles.\n"
487 "The level might not be finishable anymore!\n";
488 break;
489 }
490
491 ia_tiles[j][i] = (*it);
492 if(i == width - 1)
493 {
494 i = -1;
495 ++j;
496 }
497 }
498
499 i = j = 0;
500 for(vector<int>::iterator it = bg_tm.begin(); it != bg_tm.end(); ++it, ++i)
501 {
502 if(j >= 15)
503 {
504 std::cerr << "Warning: Level higher than 15 background tiles."
505 "Ignoring by cutting tiles.\n";
506 break;
507 }
508
509 bg_tiles[j][i] = (*it);
510 if(i == width - 1)
511 {
512 i = -1;
513 ++j;
514 }
515 }
516
517 i = j = 0;
518 for(vector<int>::iterator it = fg_tm.begin(); it != fg_tm.end(); ++it, ++i)
519 {
520 if(j >= 15)
521 {
522 std::cerr << "Warning: Level higher than 15 foreground tiles."
523 "Ignoring by cutting tiles.\n";
524 break;
525 }
526
527 fg_tiles[j][i] = (*it);
528 if(i == width - 1)
529 {
530 i = -1;
531 ++j;
532 }
533 }
534
535 lisp_free(root_obj);
536 return 0;
537 }
538
539 /* Save data for level: */
540
541 void
save(const std::string & subset,int level)542 Level::save(const std::string& subset, int level)
543 {
544 char filename[1024];
545 char str[80];
546
547 /* Save data file: */
548 sprintf(str, "/levels/%s/", subset.c_str());
549 fcreatedir(str);
550 snprintf(filename, 1024, "%s/levels/%s/level%d.stl", st_dir, subset.c_str(),
551 level);
552 if(!fwriteable(filename))
553 snprintf(filename, 1024, "%s/levels/%s/level%d.stl", datadir.c_str(),
554 subset.c_str(), level);
555
556 FILE * fi = fopen(filename, "w");
557 if (fi == NULL)
558 {
559 perror(filename);
560 st_shutdown();
561 exit(-1);
562 }
563
564
565 /* Write header: */
566 fprintf(fi,";SuperTux-Level\n");
567 fprintf(fi,"(supertux-level\n");
568
569 fprintf(fi," (version %d)\n", 1);
570 fprintf(fi," (name \"%s\")\n", name.c_str());
571 fprintf(fi," (author \"%s\")\n", author.c_str());
572 fprintf(fi," (music \"%s\")\n", song_title.c_str());
573 fprintf(fi," (background \"%s\")\n", bkgd_image.c_str());
574 fprintf(fi," (particle_system \"%s\")\n", particle_system.c_str());
575 fprintf(fi," (bkgd_speed %d)\n", bkgd_speed);
576 fprintf(fi," (bkgd_red_top %d)\n", bkgd_top.red);
577 fprintf(fi," (bkgd_green_top %d)\n", bkgd_top.green);
578 fprintf(fi," (bkgd_blue_top %d)\n", bkgd_top.blue);
579 fprintf(fi," (bkgd_red_bottom %d)\n", bkgd_bottom.red);
580 fprintf(fi," (bkgd_green_bottom %d)\n", bkgd_bottom.green);
581 fprintf(fi," (bkgd_blue_bottom %d)\n", bkgd_bottom.blue);
582 fprintf(fi," (time %d)\n", time_left);
583 fprintf(fi," (width %d)\n", width);
584 if(back_scrolling)
585 fprintf(fi," (back_scrolling #t)\n");
586 else
587 fprintf(fi," (back_scrolling #f)\n");
588 fprintf(fi," (hor_autoscroll_speed %2.1f)\n", hor_autoscroll_speed);
589 fprintf(fi," (gravity %2.1f)\n", gravity);
590 fprintf(fi," (background-tm ");
591
592 for(int y = 0; y < 15; ++y)
593 {
594 for(int i = 0; i < width; ++i)
595 fprintf(fi," %d ", bg_tiles[y][i]);
596 }
597
598 fprintf( fi,")\n");
599 fprintf(fi," (interactive-tm ");
600
601 for(int y = 0; y < 15; ++y)
602 {
603 for(int i = 0; i < width; ++i)
604 fprintf(fi," %d ", ia_tiles[y][i]);
605 }
606
607 fprintf( fi,")\n");
608 fprintf(fi," (foreground-tm ");
609
610 for(int y = 0; y < 15; ++y)
611 {
612 for(int i = 0; i < width; ++i)
613 fprintf(fi," %d ", fg_tiles[y][i]);
614 }
615
616 fprintf( fi,")\n");
617
618 fprintf( fi,"(reset-points\n");
619 for(std::vector<ResetPoint>::iterator i = reset_points.begin();
620 i != reset_points.end(); ++i)
621 fprintf( fi,"(point (x %d) (y %d))\n",i->x, i->y);
622 fprintf( fi,")\n");
623
624 fprintf( fi,"(objects\n");
625
626 for(std::vector<BadGuyData>::iterator it = badguy_data.begin();
627 it != badguy_data.end();
628 ++it)
629 fprintf( fi,"(%s (x %d) (y %d) (stay-on-platform %s))\n",
630 badguykind_to_string((*it).kind).c_str(),(*it).x,(*it).y,
631 it->stay_on_platform ? "#t" : "#f");
632
633 fprintf( fi,")\n");
634
635 fprintf( fi,")\n");
636
637 fclose(fi);
638 }
639
640
641 /* Unload data for this level: */
642
643 void
cleanup()644 Level::cleanup()
645 {
646 for(int i=0; i < 15; ++i)
647 {
648 bg_tiles[i].clear();
649 ia_tiles[i].clear();
650 fg_tiles[i].clear();
651 }
652
653 reset_points.clear();
654 name = "";
655 author = "";
656 song_title = "";
657 bkgd_image = "";
658
659 badguy_data.clear();
660 }
661
662 void
load_gfx()663 Level::load_gfx()
664 {
665 if(!bkgd_image.empty())
666 {
667 char fname[1024];
668 snprintf(fname, 1024, "%s/background/%s", st_dir, bkgd_image.c_str());
669 if(!faccessible(fname))
670 snprintf(fname, 1024, "%s/images/background/%s", datadir.c_str(), bkgd_image.c_str());
671 delete img_bkgd;
672 img_bkgd = new Surface(fname, IGNORE_ALPHA);
673 }
674 else
675 {
676 delete img_bkgd;
677 img_bkgd = 0;
678 }
679 }
680
681 /* Load a level-specific graphic... */
load_image(Surface ** ptexture,string theme,const char * file,int use_alpha)682 void Level::load_image(Surface** ptexture, string theme,const char * file, int use_alpha)
683 {
684 char fname[1024];
685
686 snprintf(fname, 1024, "%s/themes/%s/%s", st_dir, theme.c_str(), file);
687 if(!faccessible(fname))
688 snprintf(fname, 1024, "%s/images/themes/%s/%s", datadir.c_str(), theme.c_str(), file);
689
690 *ptexture = new Surface(fname, use_alpha);
691 }
692
693 /* Change the size of a level (width) */
694 void
change_size(int new_width)695 Level::change_size (int new_width)
696 {
697 if(new_width < 21)
698 new_width = 21;
699
700 for(int y = 0; y < 15; ++y)
701 {
702 ia_tiles[y].resize(new_width, 0);
703 bg_tiles[y].resize(new_width, 0);
704 fg_tiles[y].resize(new_width, 0);
705 }
706
707 width = new_width;
708 }
709
710 void
change(float x,float y,int tm,unsigned int c)711 Level::change(float x, float y, int tm, unsigned int c)
712 {
713 int yy = ((int)y / 32);
714 int xx = ((int)x / 32);
715
716 if (yy >= 0 && yy < 15 && xx >= 0 && xx <= width)
717 {
718 switch(tm)
719 {
720 case TM_BG:
721 bg_tiles[yy][xx] = c;
722 break;
723 case TM_IA:
724 ia_tiles[yy][xx] = c;
725 break;
726 case TM_FG:
727 fg_tiles[yy][xx] = c;
728 break;
729 }
730 }
731 }
732
733 void
load_song()734 Level::load_song()
735 {
736 char* song_path;
737 char* song_subtitle;
738
739 level_song = music_manager->load_music(datadir + "/music/" + song_title);
740
741 song_path = (char *) malloc(sizeof(char) * datadir.length() +
742 strlen(song_title.c_str()) + 8 + 5);
743 song_subtitle = strdup(song_title.c_str());
744 strcpy(strstr(song_subtitle, "."), "\0");
745 sprintf(song_path, "%s/music/%s-fast%s", datadir.c_str(),
746 song_subtitle, strstr(song_title.c_str(), "."));
747 if(!music_manager->exists_music(song_path)) {
748 level_song_fast = level_song;
749 } else {
750 level_song_fast = music_manager->load_music(song_path);
751 }
752 free(song_subtitle);
753 free(song_path);
754 }
755
756 MusicRef
get_level_music()757 Level::get_level_music()
758 {
759 return level_song;
760 }
761
762 MusicRef
get_level_music_fast()763 Level::get_level_music_fast()
764 {
765 return level_song_fast;
766 }
767
768 unsigned int
gettileid(float x,float y) const769 Level::gettileid(float x, float y) const
770 {
771 int xx, yy;
772 unsigned int c;
773
774 yy = ((int)y / 32);
775 xx = ((int)x / 32);
776
777 if (yy >= 0 && yy < 15 && xx >= 0 && xx <= width)
778 c = ia_tiles[yy][xx];
779 else
780 c = 0;
781
782 return c;
783 }
784
785 unsigned int
get_tile_at(int x,int y) const786 Level::get_tile_at(int x, int y) const
787 {
788 if(x < 0 || x > width || y < 0 || y > 14)
789 return 0;
790
791 return ia_tiles[y][x];
792 }
793
794 /* EOF */
795