4 // Copyright (C) 2003 Ricardo Cruz <rick2@aeiou.pt>
5 // Copyright (C) 2003 Tobias Glaesser <tobi.web@gmx.de>
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 #include <SDL_image.h>
30 #include "leveleditor.h"
43 #include "resources.h"
44 #include "music_manager.h"
46 /* definitions to aid development */
48 /* definitions that affect gameplay */
49 #define KEY_CURSOR_SPEED 32
50 #define KEY_CURSOR_FASTSPEED 64
52 /* when pagedown/up pressed speed:*/
53 #define PAGE_CURSOR_SPEED 13*32
55 #define MOUSE_LEFT_MARGIN 80
56 #define MOUSE_RIGHT_MARGIN (560-32)
57 /* right_margin should noticed that the cursor is 32 pixels,
58 so it should subtract that value */
59 #define MOUSE_POS_SPEED 20
62 #define SELECT_W 2 // size of the selections lines
63 #define SELECT_CLR 0, 255, 0, 255 // lines color (R, G, B, A)
65 /* own declerations */
66 /* crutial ones (main loop) */
69 int le_load_level(char *filename);
71 void le_drawinterface();
72 void le_checkevents();
73 void le_change(float x, float y, int tm, unsigned int c);
76 void le_set_defaults(void);
77 void le_activate_bad_guys(void);
79 void le_highlight_selection();
81 void apply_level_settings_menu();
82 void update_subset_settings_menu();
83 void save_subset_settings_menu();
85 static Level* le_current_level;
87 struct LevelEditorWorld
89 std::vector<BadGuy> bad_guys;
90 void arrays_free(void)
95 void add_bad_guy(float x, float y, BadGuyKind kind)
97 bad_guys.push_back(BadGuy(x,y,kind, false /* stay_on_platform */));
100 void activate_bad_guys()
102 for (std::vector<BadGuyData>::iterator i = le_current_level->badguy_data.begin();
103 i != le_current_level->badguy_data.end();
106 add_bad_guy(i->x, i->y, i->kind);
113 TileOrObject() : tile(0), obj(NULL) { is_tile = true; };
115 void Tile(unsigned int set_to) { tile = set_to; is_tile = true; }
116 void Object(GameObject* pobj) { obj = pobj; is_tile = false; }
117 //Returns true for a tile
118 bool IsTile() { return is_tile; };
119 //Returns true for a GameObject
120 bool IsObject() { return !is_tile; };
121 void Init() { tile = 0; obj = NULL; is_tile = true; };
123 bool is_tile; //true for tile (false for object)
128 /* leveleditor internals */
129 static string_list_type level_subsets;
130 static bool le_level_changed; /* if changes, ask for saving, when quiting*/
131 static bool show_minimap;
132 static int pos_x, cursor_x, cursor_y, fire;
134 static LevelEditorWorld le_world;
135 static LevelSubset* le_level_subset;
136 static int le_show_grid;
138 static Surface* le_selection;
140 static TileOrObject le_current;
141 static bool le_mouse_pressed[2];
142 static bool le_mouse_clicked[2];
143 static Button* le_save_level_bt;
144 static Button* le_exit_bt;
145 static Button* le_test_level_bt;
146 static Button* le_next_level_bt;
147 static Button* le_previous_level_bt;
148 static Button* le_move_right_bt;
149 static Button* le_move_left_bt;
150 static Button* le_rubber_bt;
151 static Button* le_select_mode_one_bt;
152 static Button* le_select_mode_two_bt;
153 static Button* le_settings_bt;
154 static Button* le_tilegroup_bt;
155 static Button* le_objects_bt;
156 static ButtonPanel* le_tilemap_panel;
157 static Menu* leveleditor_menu;
158 static Menu* subset_load_menu;
159 static Menu* subset_new_menu;
160 static Menu* subset_settings_menu;
161 static Menu* level_settings_menu;
162 static Menu* select_tilegroup_menu;
163 static Menu* select_objects_menu;
164 static Timer select_tilegroup_menu_effect;
165 static Timer select_objects_menu_effect;
166 typedef std::map<std::string, ButtonPanel*> ButtonPanelMap;
167 static ButtonPanelMap tilegroups_map;
168 static ButtonPanelMap objects_map;
169 static std::string cur_tilegroup;
170 static std::string cur_objects;
172 static square selection;
173 static int le_selection_mode;
174 static SDL_Event event;
175 TileMapType active_tm;
177 void le_set_defaults()
179 if(le_current_level != NULL)
183 if(le_current_level->time_left == 0)
184 le_current_level->time_left = 255;
188 int leveleditor(char* filename)
190 int last_time, now_time, i;
199 clearscreen(0, 0, 0);
202 music_manager->halt_music();
204 while (SDL_PollEvent(&event))
208 if(le_load_level(filename))
213 last_time = SDL_GetTicks();
218 if(Menu::current() == select_tilegroup_menu)
220 if(select_tilegroup_menu_effect.check())
222 select_tilegroup_menu->set_pos(screen->w - 64 + select_tilegroup_menu_effect.get_left(),
226 select_tilegroup_menu->set_pos(screen->w - 64,66,-0.5,0.5);
228 else if(Menu::current() == select_objects_menu)
230 if(select_objects_menu_effect.check())
232 select_objects_menu->set_pos(screen->w - 64 + select_objects_menu_effect.get_left(),82,-0.5,0.5);
235 select_objects_menu->set_pos(screen->w - 64,82,-0.5,0.5);
238 if(le_current_level != NULL)
240 /* making events results to be in order */
243 if(pos_x > (le_current_level->width * 32) - screen->w)
244 pos_x = (le_current_level->width * 32) - screen->w;
250 clearscreen(0, 0, 0);
252 /* draw editor interface */
255 Menu* menu = Menu::current();
261 if(menu == leveleditor_menu)
263 switch (leveleditor_menu->check())
265 case MNID_RETURNLEVELEDITOR:
266 Menu::set_current(0);
268 case MNID_SUBSETSETTINGS:
269 update_subset_settings_menu();
271 case MNID_QUITLEVELEDITOR:
276 else if(menu == level_settings_menu)
278 switch (level_settings_menu->check())
281 apply_level_settings_menu();
282 Menu::set_current(NULL);
290 else if(menu == select_tilegroup_menu)
293 switch (it = select_tilegroup_menu->check())
299 = select_tilegroup_menu->get_item_by_id(it).text;
300 Menu::set_current(0);
307 else if(menu == select_objects_menu)
310 switch (it = select_objects_menu->check())
315 cur_objects = select_objects_menu->get_item_by_id(it).text;
316 cur_tilegroup.clear();
318 Menu::set_current(0);
323 else if(menu == subset_load_menu)
325 switch (i = subset_load_menu->check())
332 if(le_load_level(level_subsets.item[i-1]))
338 else if(menu == subset_new_menu)
340 if(subset_new_menu->item[2].input[0] == '\0')
341 subset_new_menu->item[3].kind = MN_DEACTIVE;
344 subset_new_menu->item[3].kind = MN_ACTION;
346 switch (i = subset_new_menu->check())
348 case MNID_CREATESUBSET:
349 LevelSubset::create(subset_new_menu->get_item_by_id(MNID_SUBSETNAME).input);
350 le_level_subset->load(subset_new_menu->get_item_by_id(MNID_SUBSETNAME).input);
351 leveleditor_menu->get_item_by_id(MNID_SUBSETSETTINGS).kind = MN_GOTO;
353 le_world.arrays_free();
354 delete le_current_level;
355 le_current_level = new Level;
356 if(le_current_level->load(le_level_subset->name, le_level) != 0)
362 le_current_level->load_gfx();
363 le_world.activate_bad_guys();
364 subset_new_menu->get_item_by_id(MNID_SUBSETNAME).change_input("");
366 Menu::set_current(subset_settings_menu);
371 else if(menu == subset_settings_menu)
373 if(le_level_subset->title.compare(subset_settings_menu->get_item_by_id(MNID_SUBSETTITLE).input) == 0 && le_level_subset->description.compare(subset_settings_menu->get_item_by_id(MNID_SUBSETDESCRIPTION).input) == 0 )
374 subset_settings_menu->get_item_by_id(MNID_SUBSETSAVECHANGES).kind = MN_DEACTIVE;
376 subset_settings_menu->get_item_by_id(MNID_SUBSETSAVECHANGES).kind = MN_ACTION;
378 switch (i = subset_settings_menu->check())
380 case MNID_SUBSETSAVECHANGES:
381 save_subset_settings_menu();
382 Menu::set_current(leveleditor_menu);
388 mouse_cursor->draw();
396 ++global_frame_counter;
399 now_time = SDL_GetTicks();
400 if (now_time < last_time + FPS)
401 SDL_Delay(last_time + FPS - now_time); /* delay some time */
409 int le_load_level(char *filename)
411 le_level_subset->load(filename);
412 leveleditor_menu->get_item_by_id(MNID_SUBSETSETTINGS).kind = MN_GOTO;
414 le_world.arrays_free();
415 delete le_current_level;
416 le_current_level = new Level;
417 if(le_current_level->load(le_level_subset->name, le_level) != 0)
423 le_current_level->load_gfx();
424 le_world.activate_bad_guys();
426 Menu::set_current(NULL);
435 leveleditor_menu = new Menu();
436 subset_load_menu = new Menu();
437 subset_new_menu = new Menu();
438 subset_settings_menu = new Menu();
439 level_settings_menu = new Menu();
440 select_tilegroup_menu = new Menu();
441 select_objects_menu = new Menu();
443 leveleditor_menu->additem(MN_LABEL,"Level Editor Menu",0,0);
444 leveleditor_menu->additem(MN_HL,"",0,0);
445 leveleditor_menu->additem(MN_ACTION,"Return To Level Editor",0,0,MNID_RETURNLEVELEDITOR);
446 leveleditor_menu->additem(MN_DEACTIVE,"Level Subset Settings",0,subset_settings_menu,MNID_SUBSETSETTINGS);
447 leveleditor_menu->additem(MN_GOTO,"Load Level Subset",0,subset_load_menu);
448 leveleditor_menu->additem(MN_GOTO,"New Level Subset",0,subset_new_menu);
449 leveleditor_menu->additem(MN_HL,"",0,0);
450 leveleditor_menu->additem(MN_ACTION,"Quit Level Editor",0,0,MNID_QUITLEVELEDITOR);
452 Menu::set_current(leveleditor_menu);
454 subset_load_menu->additem(MN_LABEL, "Load Level Subset", 0, 0);
455 subset_load_menu->additem(MN_HL, "", 0, 0);
457 for(i = 0; i < level_subsets.num_items; ++i)
459 subset_load_menu->additem(MN_ACTION,level_subsets.item[i],0,0, i+1);
461 subset_load_menu->additem(MN_HL,"",0,0);
462 subset_load_menu->additem(MN_BACK,"Back",0,0);
464 subset_new_menu->additem(MN_LABEL,"New Level Subset",0,0);
465 subset_new_menu->additem(MN_HL,"",0,0);
466 subset_new_menu->additem(MN_TEXTFIELD,"Enter Name",0,0,MNID_SUBSETNAME);
467 subset_new_menu->additem(MN_ACTION,"Create",0,0, MNID_CREATESUBSET);
468 subset_new_menu->additem(MN_HL,"",0,0);
469 subset_new_menu->additem(MN_BACK,"Back",0,0);
471 subset_settings_menu->additem(MN_LABEL,"Level Subset Settings",0,0);
472 subset_settings_menu->additem(MN_HL,"",0,0);
473 subset_settings_menu->additem(MN_TEXTFIELD,"Title",0,0,MNID_SUBSETTITLE);
474 subset_settings_menu->additem(MN_TEXTFIELD,"Description",0,0,MNID_SUBSETDESCRIPTION);
475 subset_settings_menu->additem(MN_HL,"",0,0);
476 subset_settings_menu->additem(MN_ACTION,"Save Changes",0,0,MNID_SUBSETSAVECHANGES);
477 subset_settings_menu->additem(MN_HL,"",0,0);
478 subset_settings_menu->additem(MN_BACK,"Back",0,0);
480 level_settings_menu->arrange_left = true;
481 level_settings_menu->additem(MN_LABEL,"Level Settings",0,0);
482 level_settings_menu->additem(MN_HL,"",0,0);
483 level_settings_menu->additem(MN_TEXTFIELD,"Name ",0,0,MNID_NAME);
484 level_settings_menu->additem(MN_TEXTFIELD,"Author ",0,0,MNID_AUTHOR);
485 level_settings_menu->additem(MN_STRINGSELECT,"Song ",0,0,MNID_SONG);
486 level_settings_menu->additem(MN_STRINGSELECT,"Bg-Image",0,0,MNID_BGIMG);
487 level_settings_menu->additem(MN_STRINGSELECT,"Particle",0,0,MNID_PARTICLE);
488 level_settings_menu->additem(MN_NUMFIELD,"Length ",0,0,MNID_LENGTH);
489 level_settings_menu->additem(MN_NUMFIELD,"Time ",0,0,MNID_TIME);
490 level_settings_menu->additem(MN_NUMFIELD,"Gravity",0,0,MNID_GRAVITY);
491 level_settings_menu->additem(MN_NUMFIELD,"Bg-Img-Speed",0,0,MNID_BGSPEED);
492 level_settings_menu->additem(MN_NUMFIELD,"Top Red ",0,0,MNID_TopRed);
493 level_settings_menu->additem(MN_NUMFIELD,"Top Green ",0,0,MNID_TopGreen);
494 level_settings_menu->additem(MN_NUMFIELD,"Top Blue ",0,0,MNID_TopBlue);
495 level_settings_menu->additem(MN_NUMFIELD,"Bottom Red ",0,0,MNID_BottomRed);
496 level_settings_menu->additem(MN_NUMFIELD,"Bottom Green",0,0,MNID_BottomGreen);
497 level_settings_menu->additem(MN_NUMFIELD,"Bottom Blue",0,0,MNID_BottomBlue);
498 level_settings_menu->additem(MN_HL,"",0,0);
499 level_settings_menu->additem(MN_ACTION,"Apply Changes",0,0,MNID_APPLY);
501 select_tilegroup_menu->arrange_left = true;
502 select_tilegroup_menu->additem(MN_LABEL,"Tilegroup",0,0);
503 select_tilegroup_menu->additem(MN_HL,"",0,0);
504 std::set<TileGroup>* tilegroups = TileManager::tilegroups();
506 for(std::set<TileGroup>::iterator it = tilegroups->begin();
507 it != tilegroups->end(); ++it )
509 select_tilegroup_menu->additem(MN_ACTION, it->name, 0, 0, tileid);
511 tilegroups_map[(*it).name] = new ButtonPanel(screen->w - 64,96, 64, 318);
514 for(std::vector<int>::const_iterator sit = (*it).tiles.begin();
515 sit != (*it).tiles.end(); ++sit, ++i)
517 std::string imagefile = "/images/tilesets/" ;
518 bool only_editor_image = false;
519 if(!TileManager::instance()->get(*sit)->filenames.empty())
521 imagefile += TileManager::instance()->get(*sit)->filenames[0];
523 else if(!TileManager::instance()->get(*sit)->editor_filenames.empty())
525 imagefile += TileManager::instance()->get(*sit)->editor_filenames[0];
526 only_editor_image = true;
530 imagefile += "notile.png";
532 Button* button = new Button(imagefile, it->name, SDLKey(SDLK_a + i),
534 if(!only_editor_image)
535 if(!TileManager::instance()->get(*sit)->editor_filenames.empty())
537 imagefile = "/images/tilesets/" + TileManager::instance()->get(*sit)->editor_filenames[0];
538 button->add_icon(imagefile,32,32);
540 tilegroups_map[it->name]->additem(button, *sit);
543 select_tilegroup_menu->additem(MN_HL,"",0,0);
545 select_objects_menu->arrange_left = true;
546 select_objects_menu->additem(MN_LABEL,"Objects",0,0);
547 select_objects_menu->additem(MN_HL,"",0,0);
548 select_objects_menu->additem(MN_ACTION,"BadGuys",0,0,1);
549 objects_map["BadGuys"] = new ButtonPanel(screen->w - 64,96, 64, 318);
551 for(int i = 0; i < NUM_BadGuyKinds; ++i)
553 BadGuy bad_tmp(0,0,BadGuyKind(i),false);
554 objects_map["BadGuys"]->additem(new Button("", "BadGuy",(SDLKey)(i+'a'),0,0,32,32),1000000+i);
555 objects_map["BadGuys"]->manipulate_button(i)->set_game_object(new BadGuy(objects_map["BadGuys"]->manipulate_button(i)->get_pos().x,objects_map["BadGuys"]->manipulate_button(i)->get_pos().y,BadGuyKind(i),false));
558 select_objects_menu->additem(MN_HL,"",0,0);
566 level_subsets = dsubdirs("/levels", "info");
567 le_level_subset = new LevelSubset;
575 le_frame = 0; /* support for frames in some tiles, like waves and bad guys */
576 le_level_changed = false;
577 le_current_level = NULL;
579 le_mouse_pressed[LEFT] = false;
580 le_mouse_pressed[RIGHT] = false;
582 le_mouse_clicked[LEFT] = false;
583 le_mouse_clicked[RIGHT] = false;
585 le_selection = new Surface(datadir + "/images/leveleditor/select.png", USE_ALPHA);
587 select_tilegroup_menu_effect.init(false);
588 select_objects_menu_effect.init(false);
591 le_save_level_bt = new Button("/images/icons/save.png","Save level", SDLK_F6,screen->w-64,32);
592 le_exit_bt = new Button("/images/icons/exit.png","Exit", SDLK_F6,screen->w-32,32);
593 le_next_level_bt = new Button("/images/icons/up.png","Next level", SDLK_PAGEUP,screen->w-64,0);
594 le_previous_level_bt = new Button("/images/icons/down.png","Previous level",SDLK_PAGEDOWN,screen->w-32,0);
595 le_rubber_bt = new Button("/images/icons/rubber.png","Rubber",SDLK_DELETE,screen->w-32,48);
596 le_select_mode_one_bt = new Button ("/images/icons/select-mode1.png","Select single tile",SDLK_F3,screen->w-64,48);
597 le_select_mode_two_bt = new Button("/images/icons/select-mode2.png","Select multiple tiles",SDLK_F3,screen->w-64,48);
598 le_test_level_bt = new Button("/images/icons/test-level.png","Test level",SDLK_F4,screen->w-64,screen->h - 64);
599 le_settings_bt = new Button("/images/icons/settings.png","Level settings",SDLK_F5,screen->w-32,screen->h - 64);
600 le_move_left_bt = new Button("/images/icons/left.png","Move left",SDLK_LEFT,0,0);
601 le_move_right_bt = new Button("/images/icons/right.png","Move right",SDLK_RIGHT,screen->w-80,0);
602 le_tilegroup_bt = new Button("/images/icons/tilegroup.png","Select Tilegroup", SDLK_F7,screen->w-64,64);
603 le_objects_bt = new Button("/images/icons/objects.png","Select Objects", SDLK_F7,screen->w-64,80);
605 le_tilemap_panel = new ButtonPanel(screen->w-64,screen->h-32,32,32);
606 le_tilemap_panel->set_button_size(32,10);
607 le_tilemap_panel->additem(new Button("/images/icons/bkgrd.png","Background",SDLK_b,0,0),TM_BG);
608 le_tilemap_panel->additem(new Button("/images/icons/intact.png","Interactive",SDLK_i,0,0),TM_IA);
609 le_tilemap_panel->additem(new Button("/images/icons/frgrd.png","Foreground",SDLK_f,0,0),TM_FG);
610 le_tilemap_panel->highlight_last(true);
616 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
622 void update_level_settings_menu()
627 level_settings_menu->get_item_by_id(MNID_NAME).change_input(le_current_level->name.c_str());
628 level_settings_menu->get_item_by_id(MNID_AUTHOR).change_input(le_current_level->author.c_str());
630 string_list_copy(level_settings_menu->get_item_by_id(MNID_SONG).list, dfiles("music/",NULL, "-fast"));
631 string_list_copy(level_settings_menu->get_item_by_id(MNID_BGIMG).list, dfiles("images/background",NULL, NULL));
632 string_list_add_item(level_settings_menu->get_item_by_id(MNID_BGIMG).list,"");
633 string_list_add_item(level_settings_menu->get_item_by_id(MNID_PARTICLE).list,"");
634 string_list_add_item(level_settings_menu->get_item_by_id(MNID_PARTICLE).list,"snow");
635 string_list_add_item(level_settings_menu->get_item_by_id(MNID_PARTICLE).list,"clouds");
637 if((i = string_list_find(level_settings_menu->get_item_by_id(MNID_SONG).list,le_current_level->song_title.c_str())) != -1)
638 level_settings_menu->get_item_by_id(MNID_SONG).list->active_item = i;
639 if((i = string_list_find(level_settings_menu->get_item_by_id(MNID_BGIMG).list,le_current_level->bkgd_image.c_str())) != -1)
640 level_settings_menu->get_item_by_id(MNID_BGIMG).list->active_item = i;
641 if((i = string_list_find(level_settings_menu->get_item_by_id(MNID_PARTICLE).list,le_current_level->particle_system.c_str())) != -1)
642 level_settings_menu->get_item_by_id(MNID_PARTICLE).list->active_item = i;
644 sprintf(str,"%d",le_current_level->width);
645 level_settings_menu->get_item_by_id(MNID_LENGTH).change_input(str);
646 sprintf(str,"%d",le_current_level->time_left);
647 level_settings_menu->get_item_by_id(MNID_TIME).change_input(str);
648 sprintf(str,"%2.0f",le_current_level->gravity);
649 level_settings_menu->get_item_by_id(MNID_GRAVITY).change_input(str);
650 sprintf(str,"%d",le_current_level->bkgd_speed);
651 level_settings_menu->get_item_by_id(MNID_BGSPEED).change_input(str);
652 sprintf(str,"%d",le_current_level->bkgd_top.red);
653 level_settings_menu->get_item_by_id(MNID_TopRed).change_input(str);
654 sprintf(str,"%d",le_current_level->bkgd_top.green);
655 level_settings_menu->get_item_by_id(MNID_TopGreen).change_input(str);
656 sprintf(str,"%d",le_current_level->bkgd_top.blue);
657 level_settings_menu->get_item_by_id(MNID_TopBlue).change_input(str);
658 sprintf(str,"%d",le_current_level->bkgd_bottom.red);
659 level_settings_menu->get_item_by_id(MNID_BottomRed).change_input(str);
660 sprintf(str,"%d",le_current_level->bkgd_bottom.green);
661 level_settings_menu->get_item_by_id(MNID_BottomGreen).change_input(str);
662 sprintf(str,"%d",le_current_level->bkgd_bottom.blue);
663 level_settings_menu->get_item_by_id(MNID_BottomBlue).change_input(str);
666 void update_subset_settings_menu()
668 subset_settings_menu->item[2].change_input(le_level_subset->title.c_str());
669 subset_settings_menu->item[3].change_input(le_level_subset->description.c_str());
672 void apply_level_settings_menu()
677 le_current_level->name = level_settings_menu->get_item_by_id(MNID_NAME).input;
678 le_current_level->author = level_settings_menu->get_item_by_id(MNID_AUTHOR).input;
680 if(le_current_level->bkgd_image.compare(string_list_active(level_settings_menu->get_item_by_id(MNID_BGIMG).list)) != 0)
682 le_current_level->bkgd_image = string_list_active(level_settings_menu->get_item_by_id(MNID_BGIMG).list);
686 if(le_current_level->particle_system.compare(string_list_active(level_settings_menu->get_item_by_id(MNID_PARTICLE).list)) != 0)
688 le_current_level->particle_system = string_list_active(level_settings_menu->get_item_by_id(MNID_PARTICLE).list);
693 le_current_level->load_gfx();
696 le_current_level->song_title = string_list_active(level_settings_menu->get_item_by_id(MNID_SONG).list);
698 le_current_level->change_size(atoi(level_settings_menu->get_item_by_id(MNID_LENGTH).input));
699 le_current_level->time_left = atoi(level_settings_menu->get_item_by_id(MNID_BGIMG).input);
700 le_current_level->gravity = atof(level_settings_menu->get_item_by_id(MNID_GRAVITY).input);
701 le_current_level->bkgd_speed = atoi(level_settings_menu->get_item_by_id(MNID_BGSPEED).input);
702 le_current_level->bkgd_top.red = atoi(level_settings_menu->get_item_by_id(MNID_TopRed).input);
703 le_current_level->bkgd_top.green = atoi(level_settings_menu->get_item_by_id(MNID_TopGreen).input);
704 le_current_level->bkgd_top.blue = atoi(level_settings_menu->get_item_by_id(MNID_TopBlue).input);
705 le_current_level->bkgd_bottom.red = atoi(level_settings_menu->get_item_by_id(MNID_BottomRed).input);
706 le_current_level->bkgd_bottom.green = atoi(level_settings_menu->get_item_by_id(MNID_BottomGreen).input);
707 le_current_level->bkgd_bottom.blue = atoi(level_settings_menu->get_item_by_id(MNID_BottomBlue).input);
710 void save_subset_settings_menu()
712 le_level_subset->title = subset_settings_menu->item[2].input;
713 le_level_subset->description = subset_settings_menu->item[3].input;
714 le_level_subset->save();
717 void le_goto_level(int levelnb)
719 le_world.arrays_free();
721 le_current_level->cleanup();
722 if(le_current_level->load(le_level_subset->name.c_str(), levelnb) != 0)
724 le_current_level->load(le_level_subset->name.c_str(), le_level);
733 le_current_level->load_gfx();
735 le_world.activate_bad_guys();
740 /*if(level_changed == true)
741 if(askforsaving() == CANCEL)
744 SDL_EnableKeyRepeat(0, 0); // disables key repeating
747 delete leveleditor_menu;
748 delete subset_load_menu;
749 delete subset_new_menu;
750 delete subset_settings_menu;
751 delete level_settings_menu;
752 delete select_tilegroup_menu;
753 delete select_objects_menu;
754 delete le_save_level_bt;
756 delete le_test_level_bt;
757 delete le_next_level_bt;
758 delete le_previous_level_bt;
759 delete le_move_right_bt;
760 delete le_move_left_bt;
762 delete le_select_mode_one_bt;
763 delete le_select_mode_two_bt;
764 delete le_settings_bt;
765 delete le_tilegroup_bt;
766 delete le_objects_bt;
767 delete le_tilemap_panel;
769 delete le_current_level;
770 le_current_level = 0;
771 delete le_level_subset;
774 for(ButtonPanelMap::iterator i = tilegroups_map.begin();
775 i != tilegroups_map.end(); ++i)
779 for(ButtonPanelMap::iterator i = objects_map.begin();
780 i != objects_map.end(); ++i)
786 void le_drawminimap()
788 if(le_current_level == NULL)
792 if(screen->w - 64 > le_current_level->width * 4)
794 else if(screen->w - 64 > le_current_level->width * 2)
798 int left_offset = (screen->w - 64 - le_current_level->width*mini_tile_width) / 2;
800 for (int y = 0; y < 15; ++y)
801 for (int x = 0; x < le_current_level->width; ++x)
804 Tile::draw_stretched(left_offset + mini_tile_width*x, y * 4, mini_tile_width , 4, le_current_level->bg_tiles[y][x]);
806 Tile::draw_stretched(left_offset + mini_tile_width*x, y * 4, mini_tile_width , 4, le_current_level->ia_tiles[y][x]);
808 Tile::draw_stretched(left_offset + mini_tile_width*x, y * 4, mini_tile_width , 4, le_current_level->fg_tiles[y][x]);
812 fillrect(left_offset, 0, le_current_level->width*mini_tile_width, 15*4, 200, 200, 200, 128);
814 fillrect(left_offset + (pos_x/32)*mini_tile_width, 0, 19*mini_tile_width, 2, 200, 200, 200, 200);
815 fillrect(left_offset + (pos_x/32)*mini_tile_width, 0, 2, 15*4, 200, 200, 200, 200);
816 fillrect(left_offset + (pos_x/32)*mini_tile_width + 19*mini_tile_width - 2, 0, 2, 15*4, 200, 200, 200, 200);
817 fillrect(left_offset + (pos_x/32)*mini_tile_width, 15*4-2, 19*mini_tile_width, 2, 200, 200, 200, 200);
821 void le_drawinterface()
826 if(le_current_level != NULL)
828 /* draw a grid (if selected) */
831 for(x = 0; x < 19; x++)
832 fillrect(x*32 - ((int)pos_x % 32), 0, 1, screen->h, 225, 225, 225,255);
833 for(y = 0; y < 15; y++)
834 fillrect(0, y*32, screen->w - 32, 1, 225, 225, 225,255);
838 if(show_minimap && use_gl) // use_gl because the minimap isn't shown correctly in software mode. Any idea? FIXME Possible reasons: SDL_SoftStretch is a hack itsself || an alpha blitting issue SDL can't handle in software mode
841 if(le_selection_mode == CURSOR)
842 if(le_current.IsTile())
843 le_selection->draw( cursor_x - pos_x, cursor_y);
845 le_selection->draw( cursor_x, cursor_y);
846 else if(le_selection_mode == SQUARE)
849 le_highlight_selection();
850 /* draw current selection */
851 w = selection.x2 - selection.x1;
852 h = selection.y2 - selection.y1;
853 fillrect(selection.x1 - pos_x, selection.y1, w, SELECT_W, SELECT_CLR);
854 fillrect(selection.x1 - pos_x + w, selection.y1, SELECT_W, h, SELECT_CLR);
855 fillrect(selection.x1 - pos_x, selection.y1 + h, w, SELECT_W, SELECT_CLR);
856 fillrect(selection.x1 - pos_x, selection.y1, SELECT_W, h, SELECT_CLR);
860 /* draw button bar */
861 fillrect(screen->w - 64, 0, 64, screen->h, 50, 50, 50,255);
863 if(le_current.IsTile())
865 Tile::draw(19 * 32, 14 * 32, le_current.tile);
866 if(TileManager::instance()->get(le_current.tile)->editor_images.size() > 0)
867 TileManager::instance()->get(le_current.tile)->editor_images[0]->draw( 19 * 32, 14 * 32);
869 if(le_current.IsObject())
871 le_current.obj->draw_on_screen(19 * 32, 14 * 32);
872 le_current.obj->draw_on_screen(cursor_x,cursor_y);
875 if(le_current_level != NULL)
877 le_save_level_bt->draw();
879 le_test_level_bt->draw();
880 le_next_level_bt->draw();
881 le_previous_level_bt->draw();
882 le_rubber_bt->draw();
883 if(le_selection_mode == SQUARE)
884 le_select_mode_one_bt->draw();
885 else if(le_selection_mode == CURSOR)
886 le_select_mode_two_bt->draw();
887 le_settings_bt->draw();
888 le_move_right_bt->draw();
889 le_move_left_bt->draw();
890 le_tilegroup_bt->draw();
891 le_objects_bt->draw();
892 if(!cur_tilegroup.empty())
893 tilegroups_map[cur_tilegroup]->draw();
894 else if(!cur_objects.empty())
896 objects_map[cur_objects]->draw();
899 le_tilemap_panel->draw();
901 sprintf(str, "%d/%d", le_level,le_level_subset->levels);
902 white_text->drawf(str, -10, 16, A_RIGHT, A_TOP, 0);
904 white_small_text->draw("F1 for Help", 10, 430, 1);
909 white_small_text->draw("No Level Subset loaded - Press ESC and choose one in the menu", 10, 430, 1);
911 white_small_text->draw("No Level Subset loaded", 10, 430, 1);
918 unsigned int y,x,i,s;
921 /* Draw the real background */
922 if(le_current_level->bkgd_image[0] != '\0')
924 s = (int)((float)pos_x * ((float)le_current_level->bkgd_speed/60.)) % screen->w;
925 le_current_level->img_bkgd->draw_part(s,0,0,0,
926 le_current_level->img_bkgd->w - s - 32, le_current_level->img_bkgd->h);
927 le_current_level->img_bkgd->draw_part(0,0,screen->w - s - 32 ,0,s,
928 le_current_level->img_bkgd->h);
932 drawgradient(le_current_level->bkgd_top, le_current_level->bkgd_bottom);
935 if(le_current.IsTile())
937 Tile::draw(cursor_x-pos_x, cursor_y,le_current.tile,128);
938 if(!TileManager::instance()->get(le_current.tile)->images.empty())
939 fillrect(cursor_x-pos_x,cursor_y,TileManager::instance()->get(le_current.tile)->images[0]->w,TileManager::instance()->get(le_current.tile)->images[0]->h,50,50,50,50);
941 if(le_current.IsObject())
943 le_current.obj->move_to(cursor_x, cursor_y);
946 /* clearscreen(current_level.bkgd_red, current_level.bkgd_green, current_level.bkgd_blue); */
948 for (y = 0; y < 15; ++y)
949 for (x = 0; x < 20; ++x)
952 if(active_tm == TM_BG)
957 Tile::draw(32*x - fmodf(pos_x, 32), y * 32, le_current_level->bg_tiles[y][x + (int)(pos_x / 32)],a);
959 if(active_tm == TM_IA)
964 Tile::draw(32*x - fmodf(pos_x, 32), y * 32, le_current_level->ia_tiles[y][x + (int)(pos_x / 32)],a);
966 if(active_tm == TM_FG)
971 Tile::draw(32*x - fmodf(pos_x, 32), y * 32, le_current_level->fg_tiles[y][x + (int)(pos_x / 32)],a);
973 /* draw whats inside stuff when cursor is selecting those */
974 /* (draw them all the time - is this the right behaviour?) */
975 if(TileManager::instance()->get(le_current_level->ia_tiles[y][x + (int)(pos_x / 32)])->editor_images.size() > 0)
976 TileManager::instance()->get(le_current_level->ia_tiles[y][x + (int)(pos_x / 32)])->editor_images[0]->draw( x * 32 - ((int)pos_x % 32), y*32);
980 /* Draw the Bad guys: */
981 for (i = 0; i < le_world.bad_guys.size(); ++i)
983 /* to support frames: img_bsod_left[(frame / 5) % 4] */
986 le_world.bad_guys[i].draw();
990 /* Draw the player: */
991 /* for now, the position is fixed at (100, 240) */
992 largetux.walk_right->draw( 100 - pos_x, 240);
995 void le_checkevents()
1002 keymod = SDL_GetModState();
1004 while(SDL_PollEvent(&event))
1006 if (Menu::current())
1008 Menu::current()->event(event);
1012 mouse_cursor->set_state(MC_NORMAL);
1014 /* testing SDL_KEYDOWN, SDL_KEYUP and SDL_QUIT events*/
1015 if(event.type == SDL_KEYDOWN
1016 || ((event.type == SDL_MOUSEBUTTONDOWN || SDL_MOUSEMOTION)
1017 && (event.motion.x > 0
1018 && event.motion.x < screen->w - 64 &&
1019 event.motion.y > 0 && event.motion.y < screen->h)))
1023 case SDL_KEYDOWN: // key pressed
1024 key = event.key.keysym.sym;
1028 Menu::set_current(leveleditor_menu);
1031 cursor_x -= KEY_CURSOR_SPEED;
1033 cursor_x -= KEY_CURSOR_FASTSPEED;
1035 if(cursor_x < pos_x + MOUSE_LEFT_MARGIN)
1036 pos_x = cursor_x - MOUSE_LEFT_MARGIN;
1041 cursor_x += KEY_CURSOR_SPEED;
1043 cursor_x += KEY_CURSOR_FASTSPEED;
1045 if(cursor_x > pos_x + MOUSE_RIGHT_MARGIN-32)
1046 pos_x = cursor_x - MOUSE_RIGHT_MARGIN+32;
1051 cursor_y -= KEY_CURSOR_SPEED;
1053 cursor_y -= KEY_CURSOR_FASTSPEED;
1060 cursor_y += KEY_CURSOR_SPEED;
1062 cursor_y += KEY_CURSOR_FASTSPEED;
1064 if(cursor_y > screen->h-32)
1065 cursor_y = screen->h-32;
1078 cursor_x = (le_current_level->width * 32) - 32;
1082 le_show_grid = !le_show_grid;
1088 case SDL_KEYUP: /* key released */
1089 switch(event.key.keysym.sym)
1098 case SDL_MOUSEBUTTONDOWN:
1099 if(event.button.button == SDL_BUTTON_LEFT)
1101 le_mouse_pressed[LEFT] = true;
1103 selection.x1 = event.motion.x + pos_x;
1104 selection.y1 = event.motion.y;
1105 selection.x2 = event.motion.x + pos_x;
1106 selection.y2 = event.motion.y;
1108 else if(event.button.button == SDL_BUTTON_RIGHT)
1110 le_mouse_pressed[RIGHT] = true;
1113 case SDL_MOUSEBUTTONUP:
1114 if(event.button.button == SDL_BUTTON_LEFT)
1116 le_mouse_pressed[LEFT] = false;
1117 le_mouse_clicked[LEFT] = true;
1119 else if(event.button.button == SDL_BUTTON_RIGHT)
1121 le_mouse_pressed[RIGHT] = false;
1122 le_mouse_clicked[RIGHT] = true;
1125 case SDL_MOUSEMOTION:
1127 if(!Menu::current())
1132 if(le_current.IsTile())
1134 cursor_x = ((int)(pos_x + x) / 32) * 32;
1135 cursor_y = ((int) y / 32) * 32;
1143 if(le_mouse_pressed[LEFT])
1145 selection.x2 = x + pos_x;
1149 if(le_mouse_pressed[RIGHT])
1151 pos_x += -1 * event.motion.xrel;
1155 case SDL_QUIT: // window closed
1164 if(le_current_level != NULL)
1166 if(event.type == SDL_KEYDOWN || event.type == SDL_KEYUP || ((event.type == SDL_MOUSEBUTTONDOWN || SDL_MOUSEMOTION) && (event.motion.x > screen->w-64 && event.motion.x < screen->w &&
1167 event.motion.y > 0 && event.motion.y < screen->h)))
1169 le_mouse_pressed[LEFT] = false;
1170 le_mouse_pressed[RIGHT] = false;
1172 if(!Menu::current())
1174 /* Check for button events */
1175 le_test_level_bt->event(event);
1176 if(le_test_level_bt->get_state() == BUTTON_CLICKED)
1178 le_save_level_bt->event(event);
1179 if(le_save_level_bt->get_state() == BUTTON_CLICKED)
1180 le_current_level->save(le_level_subset->name.c_str(),le_level);
1181 le_exit_bt->event(event);
1182 if(le_exit_bt->get_state() == BUTTON_CLICKED)
1184 Menu::set_current(leveleditor_menu);
1186 le_next_level_bt->event(event);
1187 if(le_next_level_bt->get_state() == BUTTON_CLICKED)
1189 if(le_level < le_level_subset->levels)
1191 le_goto_level(++le_level);
1197 sprintf(str,"Level %d doesn't exist. Create it?",le_level+1);
1198 if(confirm_dialog(str))
1200 new_lev.init_defaults();
1201 new_lev.save(le_level_subset->name.c_str(),++le_level);
1202 le_level_subset->levels = le_level;
1203 le_goto_level(le_level);
1207 le_previous_level_bt->event(event);
1208 if(le_previous_level_bt->get_state() == BUTTON_CLICKED)
1211 le_goto_level(--le_level);
1213 le_rubber_bt->event(event);
1214 if(le_rubber_bt->get_state() == BUTTON_CLICKED)
1217 if(le_selection_mode == SQUARE)
1219 le_select_mode_one_bt->event(event);
1220 if(le_select_mode_one_bt->get_state() == BUTTON_CLICKED)
1221 le_selection_mode = CURSOR;
1225 le_select_mode_two_bt->event(event);
1226 if(le_select_mode_two_bt->get_state() == BUTTON_CLICKED)
1227 le_selection_mode = SQUARE;
1229 ButtonPanelMap::iterator it;
1230 le_tilegroup_bt->event(event);
1231 switch (le_tilegroup_bt->get_state())
1233 case BUTTON_CLICKED:
1234 Menu::set_current(select_tilegroup_menu);
1235 select_tilegroup_menu_effect.start(200);
1236 select_tilegroup_menu->set_pos(screen->w - 64,100,-0.5,0.5);
1238 case BUTTON_WHEELUP:
1239 if(cur_tilegroup.empty())
1241 cur_tilegroup = tilegroups_map.begin()->first;
1245 it = tilegroups_map.find(cur_tilegroup);
1246 if((++it) == tilegroups_map.end())
1248 cur_tilegroup = tilegroups_map.begin()->first;
1252 cur_tilegroup = (*it).first;
1256 cur_objects.clear();
1258 case BUTTON_WHEELDOWN:
1259 it = tilegroups_map.find(cur_tilegroup);
1260 if(it == tilegroups_map.begin())
1262 cur_tilegroup = tilegroups_map.rbegin()->first;
1263 cur_objects.clear();
1266 if(--it != --tilegroups_map.begin())
1267 cur_tilegroup = (*it).first;
1269 cur_tilegroup = tilegroups_map.rbegin()->first;
1271 cur_objects.clear();
1277 le_objects_bt->event(event);
1278 switch (le_objects_bt->get_state())
1280 case BUTTON_CLICKED:
1281 Menu::set_current(select_objects_menu);
1282 select_objects_menu_effect.start(200);
1283 select_objects_menu->set_pos(screen->w - 64,100,-0.5,0.5);
1285 case BUTTON_WHEELUP:
1286 it = objects_map.find(cur_objects);
1287 if(it == objects_map.end())
1289 cur_objects = objects_map.begin()->first;
1290 cur_tilegroup.clear();
1293 if(++it != objects_map.end())
1294 cur_objects = (*it).first;
1296 cur_objects = objects_map.begin()->first;
1298 cur_tilegroup.clear();
1300 case BUTTON_WHEELDOWN:
1301 it = objects_map.find(cur_objects);
1302 if(it == objects_map.begin())
1304 cur_objects = objects_map.rbegin()->first;
1305 cur_tilegroup.clear();
1308 if(--it != --objects_map.begin())
1309 cur_objects = (*it).first;
1311 cur_objects = objects_map.rbegin()->first;
1313 cur_tilegroup.clear();
1320 le_settings_bt->event(event);
1321 if(le_settings_bt->get_state() == BUTTON_CLICKED)
1323 update_level_settings_menu();
1324 Menu::set_current(level_settings_menu);
1326 if(!cur_tilegroup.empty())
1328 if((pbutton = tilegroups_map[cur_tilegroup]->event(event)) != NULL)
1330 if(pbutton->get_state() == BUTTON_CLICKED)
1332 le_current.Tile(pbutton->get_tag());
1336 else if(!cur_objects.empty())
1338 if((pbutton = objects_map[cur_objects]->event(event)) != NULL)
1340 if(pbutton->get_state() == BUTTON_CLICKED)
1342 le_current.Object(pbutton->get_game_object());
1347 if((pbutton = le_tilemap_panel->event(event)) != NULL)
1349 if(pbutton->get_state() == BUTTON_CLICKED)
1351 active_tm = static_cast<TileMapType>(pbutton->get_tag());
1357 le_settings_bt->event(event);
1358 if(le_settings_bt->get_state() == BUTTON_CLICKED)
1360 Menu::set_current(0);
1362 le_tilegroup_bt->event(event);
1363 if(le_tilegroup_bt->get_state() == BUTTON_CLICKED)
1365 Menu::set_current(0);
1370 if(!Menu::current() && !show_minimap)
1372 if(le_mouse_pressed[LEFT])
1374 if(le_current.IsTile())
1375 le_change(cursor_x, cursor_y, active_tm, le_current.tile);
1377 else if(le_mouse_clicked[LEFT])
1379 if(le_current.IsObject())
1381 std::string type = le_current.obj->type();
1382 if(type == "BadGuy")
1384 BadGuy* pbadguy = dynamic_cast<BadGuy*>(le_current.obj);
1386 le_world.bad_guys.push_back(BadGuy(cursor_x+scroll_x, cursor_y,pbadguy->kind,false));
1387 le_current_level->badguy_data.push_back(&le_world.bad_guys.back());
1390 le_mouse_clicked[LEFT] = false;
1395 if(!Menu::current())
1397 show_minimap = false;
1399 le_move_left_bt->event(event);
1400 le_move_right_bt->event(event);
1401 switch(le_move_left_bt->get_state())
1403 case BUTTON_PRESSED:
1405 show_minimap = true;
1409 show_minimap = true;
1411 case BUTTON_CLICKED:
1412 show_minimap = true;
1418 switch(le_move_right_bt->get_state())
1420 case BUTTON_PRESSED:
1422 show_minimap = true;
1426 show_minimap = true;
1428 case BUTTON_CLICKED:
1429 show_minimap = true;
1439 void le_highlight_selection()
1443 if(selection.x1 < selection.x2)
1453 if(selection.y1 < selection.y2)
1469 fillrect(x1*32-pos_x, y1*32,32* (x2 - x1 + 1),32 * (y2 - y1 + 1),173,234,177,103);
1472 void le_change(float x, float y, int tm, unsigned int c)
1474 if(le_current_level != NULL)
1480 /* level_changed = true; */
1482 switch(le_selection_mode)
1485 le_current_level->change(x,y,tm,c);
1487 base_type cursor_base;
1490 cursor_base.width = 32;
1491 cursor_base.height = 32;
1493 /* if there is a bad guy over there, remove it */
1494 for(i = 0; i < le_world.bad_guys.size(); ++i)
1495 if(rectcollision(cursor_base,le_world.bad_guys[i].base))
1497 le_world.bad_guys.erase(le_world.bad_guys.begin() + i);
1498 le_current_level->badguy_data.erase(le_current_level->badguy_data.begin() + i);
1503 if(selection.x1 < selection.x2)
1513 if(selection.y1 < selection.y2)
1529 /* if there is a bad guy over there, remove it */
1530 for(std::vector<BadGuy>::iterator i = le_world.bad_guys.begin();
1531 i != le_world.bad_guys.end(); /* will be at end of loop */)
1533 if(i->base.x/32 >= x1 && i->base.x/32 <= x2
1534 && i->base.y/32 >= y1 && i->base.y/32 <= y2)
1536 i = le_world.bad_guys.erase(i);
1545 for(xx = x1; xx <= x2; xx++)
1546 for(yy = y1; yy <= y2; yy++)
1548 le_current_level->change(xx*32, yy*32, tm, c);
1560 le_current_level->save("test", le_level);
1562 GameSession session("test",le_level, ST_GL_TEST);
1564 player_status.reset();
1566 music_manager->halt_music();
1568 Menu::set_current(NULL);
1569 le_world.arrays_free();
1570 le_current_level->load_gfx();
1571 le_world.activate_bad_guys();
1577 unsigned int i, done_;
1579 " - This is SuperTux's built-in level editor -",
1580 "It has been designed to be light and easy to use from the start.",
1582 "When you first load the level editor you are given a menu where you",
1583 "can load level subsets, create a new level subset, edit the current",
1584 "subset's settings, or simply quit the editor. You can access this menu",
1585 "from the level editor at any time by pressing the escape key.",
1587 "To your right is your button bar. The center of this contains many",
1588 "tiles you can use to make your level. To select a tile, click on it",
1589 "with your left mouse button; your selection will be shown in the",
1590 "bottom right corner of the button box. Click anywhere on your level",
1591 "with the left mouse button to place that tile down. If you right click",
1592 "a tile in the button bar, you can find out what its keyboard shortcut",
1593 "is. The three buttons FGD, BGD and EMY let you pick from foreground,",
1594 "background, and enemy tiles. The eraser lets you remove tiles.",
1595 "The left and right arrow keys scroll back and forth through your level.",
1596 "The button with the wrench and screwdriver, lets you change the",
1597 "settings of your level, including how long it is or what music it will",
1598 "play. When you are ready to give your level a test, click on the little",
1599 "running Tux. If you like the changes you have made to your level,",
1600 "press the red save key to keep them.",
1601 "To change which level in your subset you are editing, press the white",
1602 "up and down arrow keys at the top of the button box.",
1604 "Have fun making levels! If you make some good ones, send them to us on",
1605 "the SuperTux mailing list!",
1610 blue_text->drawf("- Help -", 0, 30, A_HMIDDLE, A_TOP, 2);
1612 for(i = 0; i < sizeof(text)/sizeof(char *); i++)
1613 white_small_text->draw(text[i], 5, 80+(i*white_small_text->h), 1);
1615 gold_text->drawf("Press Any Key to Continue", 0, 440, A_HMIDDLE, A_TOP, 1);
1623 done_ = wait_for_event(event);