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) */
70 void le_drawinterface();
71 void le_checkevents();
72 void le_change(float x, float y, int tm, unsigned int c);
75 void le_set_defaults(void);
76 void le_activate_bad_guys(void);
78 void le_highlight_selection();
80 void apply_level_settings_menu();
81 void update_subset_settings_menu();
82 void save_subset_settings_menu();
84 static Level* le_current_level;
86 struct LevelEditorWorld
88 std::vector<BadGuy> bad_guys;
89 void arrays_free(void)
94 void add_bad_guy(float x, float y, BadGuyKind kind)
96 bad_guys.push_back(BadGuy(x,y,kind, false /* stay_on_platform */));
99 void activate_bad_guys()
101 for (std::vector<BadGuyData>::iterator i = le_current_level->badguy_data.begin();
102 i != le_current_level->badguy_data.end();
105 add_bad_guy(i->x, i->y, i->kind);
112 TileOrObject() : tile(0), obj(NULL) { is_tile = true; };
114 void Tile(unsigned int set_to) { tile = set_to; is_tile = true; }
115 void Object(GameObject* pobj) { obj = pobj; is_tile = false; }
116 //Returns true for a tile
117 bool IsTile() { return is_tile; };
118 //Returns true for a GameObject
119 bool IsObject() { return !is_tile; };
120 void Init() { tile = 0; obj = NULL; is_tile = true; };
122 bool is_tile; //true for tile (false for object)
127 /* leveleditor internals */
128 static string_list_type level_subsets;
129 static bool le_level_changed; /* if changes, ask for saving, when quiting*/
130 static int pos_x, cursor_x, cursor_y, fire;
132 static LevelEditorWorld le_world;
133 static LevelSubset* le_level_subset;
134 static int le_show_grid;
136 static Surface* le_selection;
138 static TileOrObject le_current;
139 static bool le_mouse_pressed[2];
140 static bool le_mouse_clicked[2];
141 static Button* le_save_level_bt;
142 static Button* le_exit_bt;
143 static Button* le_test_level_bt;
144 static Button* le_next_level_bt;
145 static Button* le_previous_level_bt;
146 static Button* le_move_right_bt;
147 static Button* le_move_left_bt;
148 static Button* le_rubber_bt;
149 static Button* le_select_mode_one_bt;
150 static Button* le_select_mode_two_bt;
151 static Button* le_settings_bt;
152 static Button* le_tilegroup_bt;
153 static Button* le_objects_bt;
154 static ButtonPanel* le_tilemap_panel;
155 static Menu* leveleditor_menu;
156 static Menu* subset_load_menu;
157 static Menu* subset_new_menu;
158 static Menu* subset_settings_menu;
159 static Menu* level_settings_menu;
160 static Menu* select_tilegroup_menu;
161 static Menu* select_objects_menu;
162 static Timer select_tilegroup_menu_effect;
163 static Timer select_objects_menu_effect;
164 typedef std::map<std::string, ButtonPanel*> ButtonPanelMap;
165 static ButtonPanelMap tilegroups_map;
166 static ButtonPanelMap objects_map;
167 static std::string cur_tilegroup;
168 static std::string cur_objects;
170 static square selection;
171 static int le_selection_mode;
172 static SDL_Event event;
173 TileMapType active_tm;
175 void le_set_defaults()
177 if(le_current_level != NULL)
181 if(le_current_level->time_left == 0)
182 le_current_level->time_left = 255;
186 int leveleditor(int levelnb)
188 int last_time, now_time, i;
196 clearscreen(0, 0, 0);
199 music_manager->halt_music();
201 while (SDL_PollEvent(&event))
206 last_time = SDL_GetTicks();
211 if(Menu::current() == select_tilegroup_menu)
213 if(select_tilegroup_menu_effect.check())
215 select_tilegroup_menu->set_pos(screen->w - 64 + select_tilegroup_menu_effect.get_left(),
219 select_tilegroup_menu->set_pos(screen->w - 64,66,-0.5,0.5);
221 else if(Menu::current() == select_objects_menu)
223 if(select_objects_menu_effect.check())
225 select_objects_menu->set_pos(screen->w - 64 + select_objects_menu_effect.get_left(),82,-0.5,0.5);
228 select_objects_menu->set_pos(screen->w - 64,82,-0.5,0.5);
231 if(le_current_level != NULL)
233 /* making events results to be in order */
236 if(pos_x > (le_current_level->width * 32) - screen->w)
237 pos_x = (le_current_level->width * 32) - screen->w;
243 clearscreen(0, 0, 0);
245 /* draw editor interface */
248 Menu* menu = Menu::current();
254 if(menu == leveleditor_menu)
256 switch (leveleditor_menu->check())
258 case MNID_RETURNLEVELEDITOR:
259 Menu::set_current(0);
261 case MNID_SUBSETSETTINGS:
262 update_subset_settings_menu();
264 case MNID_QUITLEVELEDITOR:
269 else if(menu == level_settings_menu)
271 switch (level_settings_menu->check())
274 apply_level_settings_menu();
275 Menu::set_current(NULL);
283 else if(menu == select_tilegroup_menu)
286 switch (it = select_tilegroup_menu->check())
292 = select_tilegroup_menu->get_item_by_id(it).text;
293 Menu::set_current(0);
300 else if(menu == select_objects_menu)
303 switch (it = select_objects_menu->check())
308 cur_objects = select_objects_menu->get_item_by_id(it).text;
309 cur_tilegroup.clear();
311 Menu::set_current(0);
316 else if(menu == subset_load_menu)
318 switch (i = subset_load_menu->check())
325 le_level_subset->load(level_subsets.item[i-1]);
326 leveleditor_menu->get_item_by_id(MNID_SUBSETSETTINGS).kind = MN_GOTO;
328 le_world.arrays_free();
329 delete le_current_level;
330 le_current_level = new Level;
331 if(le_current_level->load(le_level_subset->name, le_level) != 0)
337 le_current_level->load_gfx();
338 le_world.activate_bad_guys();
340 Menu::set_current(NULL);
345 else if(menu == subset_new_menu)
347 if(subset_new_menu->item[2].input[0] == '\0')
348 subset_new_menu->item[3].kind = MN_DEACTIVE;
351 subset_new_menu->item[3].kind = MN_ACTION;
353 switch (i = subset_new_menu->check())
355 case MNID_CREATESUBSET:
356 LevelSubset::create(subset_new_menu->get_item_by_id(MNID_SUBSETNAME).input);
357 le_level_subset->load(subset_new_menu->get_item_by_id(MNID_SUBSETNAME).input);
358 leveleditor_menu->get_item_by_id(MNID_SUBSETSETTINGS).kind = MN_GOTO;
360 le_world.arrays_free();
361 delete le_current_level;
362 le_current_level = new Level;
363 if(le_current_level->load(le_level_subset->name, le_level) != 0)
369 le_current_level->load_gfx();
370 le_world.activate_bad_guys();
371 subset_new_menu->get_item_by_id(MNID_SUBSETNAME).change_input("");
373 Menu::set_current(subset_settings_menu);
378 else if(menu == subset_settings_menu)
380 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 )
381 subset_settings_menu->get_item_by_id(MNID_SUBSETSAVECHANGES).kind = MN_DEACTIVE;
383 subset_settings_menu->get_item_by_id(MNID_SUBSETSAVECHANGES).kind = MN_ACTION;
385 switch (i = subset_settings_menu->check())
387 case MNID_SUBSETSAVECHANGES:
388 save_subset_settings_menu();
389 Menu::set_current(leveleditor_menu);
395 mouse_cursor->draw();
403 ++global_frame_counter;
406 now_time = SDL_GetTicks();
407 if (now_time < last_time + FPS)
408 SDL_Delay(last_time + FPS - now_time); /* delay some time */
421 leveleditor_menu = new Menu();
422 subset_load_menu = new Menu();
423 subset_new_menu = new Menu();
424 subset_settings_menu = new Menu();
425 level_settings_menu = new Menu();
426 select_tilegroup_menu = new Menu();
427 select_objects_menu = new Menu();
429 leveleditor_menu->additem(MN_LABEL,"Level Editor Menu",0,0);
430 leveleditor_menu->additem(MN_HL,"",0,0);
431 leveleditor_menu->additem(MN_ACTION,"Return To Level Editor",0,0,MNID_RETURNLEVELEDITOR);
432 leveleditor_menu->additem(MN_DEACTIVE,"Level Subset Settings",0,subset_settings_menu,MNID_SUBSETSETTINGS);
433 leveleditor_menu->additem(MN_GOTO,"Load Level Subset",0,subset_load_menu);
434 leveleditor_menu->additem(MN_GOTO,"New Level Subset",0,subset_new_menu);
435 leveleditor_menu->additem(MN_HL,"",0,0);
436 leveleditor_menu->additem(MN_ACTION,"Quit Level Editor",0,0,MNID_QUITLEVELEDITOR);
438 Menu::set_current(leveleditor_menu);
440 subset_load_menu->additem(MN_LABEL, "Load Level Subset", 0, 0);
441 subset_load_menu->additem(MN_HL, "", 0, 0);
443 for(i = 0; i < level_subsets.num_items; ++i)
445 subset_load_menu->additem(MN_ACTION,level_subsets.item[i],0,0, i+1);
447 subset_load_menu->additem(MN_HL,"",0,0);
448 subset_load_menu->additem(MN_BACK,"Back",0,0);
450 subset_new_menu->additem(MN_LABEL,"New Level Subset",0,0);
451 subset_new_menu->additem(MN_HL,"",0,0);
452 subset_new_menu->additem(MN_TEXTFIELD,"Enter Name",0,0,MNID_SUBSETNAME);
453 subset_new_menu->additem(MN_ACTION,"Create",0,0, MNID_CREATESUBSET);
454 subset_new_menu->additem(MN_HL,"",0,0);
455 subset_new_menu->additem(MN_BACK,"Back",0,0);
457 subset_settings_menu->additem(MN_LABEL,"Level Subset Settings",0,0);
458 subset_settings_menu->additem(MN_HL,"",0,0);
459 subset_settings_menu->additem(MN_TEXTFIELD,"Title",0,0,MNID_SUBSETTITLE);
460 subset_settings_menu->additem(MN_TEXTFIELD,"Description",0,0,MNID_SUBSETDESCRIPTION);
461 subset_settings_menu->additem(MN_HL,"",0,0);
462 subset_settings_menu->additem(MN_ACTION,"Save Changes",0,0,MNID_SUBSETSAVECHANGES);
463 subset_settings_menu->additem(MN_HL,"",0,0);
464 subset_settings_menu->additem(MN_BACK,"Back",0,0);
466 level_settings_menu->arrange_left = true;
467 level_settings_menu->additem(MN_LABEL,"Level Settings",0,0);
468 level_settings_menu->additem(MN_HL,"",0,0);
469 level_settings_menu->additem(MN_TEXTFIELD,"Name ",0,0,MNID_NAME);
470 level_settings_menu->additem(MN_TEXTFIELD,"Author ",0,0,MNID_AUTHOR);
471 level_settings_menu->additem(MN_STRINGSELECT,"Song ",0,0,MNID_SONG);
472 level_settings_menu->additem(MN_STRINGSELECT,"Bg-Image",0,0,MNID_BGIMG);
473 level_settings_menu->additem(MN_STRINGSELECT,"Particle",0,0,MNID_PARTICLE);
474 level_settings_menu->additem(MN_NUMFIELD,"Length ",0,0,MNID_LENGTH);
475 level_settings_menu->additem(MN_NUMFIELD,"Time ",0,0,MNID_TIME);
476 level_settings_menu->additem(MN_NUMFIELD,"Gravity",0,0,MNID_GRAVITY);
477 level_settings_menu->additem(MN_NUMFIELD,"Bg-Img-Speed",0,0,MNID_BGSPEED);
478 level_settings_menu->additem(MN_NUMFIELD,"Top Red ",0,0,MNID_TopRed);
479 level_settings_menu->additem(MN_NUMFIELD,"Top Green ",0,0,MNID_TopGreen);
480 level_settings_menu->additem(MN_NUMFIELD,"Top Blue ",0,0,MNID_TopBlue);
481 level_settings_menu->additem(MN_NUMFIELD,"Bottom Red ",0,0,MNID_BottomRed);
482 level_settings_menu->additem(MN_NUMFIELD,"Bottom Green",0,0,MNID_BottomGreen);
483 level_settings_menu->additem(MN_NUMFIELD,"Bottom Blue",0,0,MNID_BottomBlue);
484 level_settings_menu->additem(MN_HL,"",0,0);
485 level_settings_menu->additem(MN_ACTION,"Apply Changes",0,0,MNID_APPLY);
487 select_tilegroup_menu->arrange_left = true;
488 select_tilegroup_menu->additem(MN_LABEL,"Tilegroup",0,0);
489 select_tilegroup_menu->additem(MN_HL,"",0,0);
490 std::vector<TileGroup>* tilegroups = TileManager::tilegroups();
492 for(std::vector<TileGroup>::iterator it = tilegroups->begin();
493 it != tilegroups->end(); ++it )
495 select_tilegroup_menu->additem(MN_ACTION, it->name, 0, 0, tileid);
497 tilegroups_map[(*it).name] = new ButtonPanel(screen->w - 64,96, 64, 318);
500 for(std::vector<int>::iterator sit = (*it).tiles.begin();
501 sit != (*it).tiles.end(); ++sit, ++i)
503 std::string imagefile = "/images/tilesets/" ;
504 bool only_editor_image = false;
505 if(!TileManager::instance()->get(*sit)->filenames.empty())
507 imagefile += TileManager::instance()->get(*sit)->filenames[0];
509 else if(!TileManager::instance()->get(*sit)->editor_filenames.empty())
511 imagefile += TileManager::instance()->get(*sit)->editor_filenames[0];
512 only_editor_image = true;
516 imagefile += "notile.png";
518 Button* button = new Button(imagefile, it->name, SDLKey(SDLK_a + i),
520 if(!only_editor_image)
521 if(!TileManager::instance()->get(*sit)->editor_filenames.empty())
523 imagefile = "/images/tilesets/" + TileManager::instance()->get(*sit)->editor_filenames[0];
524 button->add_icon(imagefile,32,32);
526 tilegroups_map[it->name]->additem(button, *sit);
529 select_tilegroup_menu->additem(MN_HL,"",0,0);
531 select_objects_menu->arrange_left = true;
532 select_objects_menu->additem(MN_LABEL,"Objects",0,0);
533 select_objects_menu->additem(MN_HL,"",0,0);
534 select_objects_menu->additem(MN_ACTION,"BadGuys",0,0,1);
535 objects_map["BadGuys"] = new ButtonPanel(screen->w - 64,96, 64, 318);
537 for(int i = 0; i < NUM_BadGuyKinds; ++i)
539 BadGuy bad_tmp(0,0,BadGuyKind(i),false);
540 objects_map["BadGuys"]->additem(new Button("", "BadGuy",(SDLKey)(i+'a'),0,0,32,32),1000000+i);
541 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));
544 select_objects_menu->additem(MN_HL,"",0,0);
550 level_subsets = dsubdirs("/levels", "info");
551 le_level_subset = new LevelSubset;
559 le_frame = 0; /* support for frames in some tiles, like waves and bad guys */
560 le_level_changed = false;
561 le_current_level = NULL;
563 le_mouse_pressed[LEFT] = false;
564 le_mouse_pressed[RIGHT] = false;
566 le_mouse_clicked[LEFT] = false;
567 le_mouse_clicked[RIGHT] = false;
569 le_selection = new Surface(datadir + "/images/leveleditor/select.png", USE_ALPHA);
571 select_tilegroup_menu_effect.init(false);
572 select_objects_menu_effect.init(false);
575 le_save_level_bt = new Button("/images/icons/save.png","Save level", SDLK_F6,screen->w-64,32);
576 le_exit_bt = new Button("/images/icons/exit.png","Exit", SDLK_F6,screen->w-32,32);
577 le_next_level_bt = new Button("/images/icons/up.png","Next level", SDLK_PAGEUP,screen->w-64,0);
578 le_previous_level_bt = new Button("/images/icons/down.png","Previous level",SDLK_PAGEDOWN,screen->w-32,0);
579 le_rubber_bt = new Button("/images/icons/rubber.png","Rubber",SDLK_DELETE,screen->w-32,48);
580 le_select_mode_one_bt = new Button ("/images/icons/select-mode1.png","Select single tile",SDLK_F3,screen->w-64,48);
581 le_select_mode_two_bt = new Button("/images/icons/select-mode2.png","Select multiple tiles",SDLK_F3,screen->w-64,48);
582 le_test_level_bt = new Button("/images/icons/test-level.png","Test level",SDLK_F4,screen->w-64,screen->h - 64);
583 le_settings_bt = new Button("/images/icons/settings.png","Level settings",SDLK_F5,screen->w-32,screen->h - 64);
584 le_move_left_bt = new Button("/images/icons/left.png","Move left",SDLK_LEFT,0,0);
585 le_move_right_bt = new Button("/images/icons/right.png","Move right",SDLK_RIGHT,screen->w-80,0);
586 le_tilegroup_bt = new Button("/images/icons/tilegroup.png","Select Tilegroup", SDLK_F7,screen->w-64,64);
587 le_objects_bt = new Button("/images/icons/objects.png","Select Objects", SDLK_F7,screen->w-64,80);
589 le_tilemap_panel = new ButtonPanel(screen->w-64,screen->h-32,32,32);
590 le_tilemap_panel->set_button_size(32,10);
591 le_tilemap_panel->additem(new Button("/images/icons/bkgrd.png","Background",SDLK_b,0,0),TM_BG);
592 le_tilemap_panel->additem(new Button("/images/icons/intact.png","Interactive",SDLK_i,0,0),TM_IA);
593 le_tilemap_panel->additem(new Button("/images/icons/frgrd.png","Foreground",SDLK_f,0,0),TM_FG);
594 le_tilemap_panel->highlight_last(true);
600 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
605 void update_level_settings_menu()
610 level_settings_menu->get_item_by_id(MNID_NAME).change_input(le_current_level->name.c_str());
611 level_settings_menu->get_item_by_id(MNID_AUTHOR).change_input(le_current_level->author.c_str());
613 string_list_copy(level_settings_menu->get_item_by_id(MNID_SONG).list, dfiles("music/",NULL, "-fast"));
614 string_list_copy(level_settings_menu->get_item_by_id(MNID_BGIMG).list, dfiles("images/background",NULL, NULL));
615 string_list_add_item(level_settings_menu->get_item_by_id(MNID_BGIMG).list,"");
616 string_list_add_item(level_settings_menu->get_item_by_id(MNID_PARTICLE).list,"");
617 string_list_add_item(level_settings_menu->get_item_by_id(MNID_PARTICLE).list,"snow");
618 string_list_add_item(level_settings_menu->get_item_by_id(MNID_PARTICLE).list,"clouds");
620 if((i = string_list_find(level_settings_menu->get_item_by_id(MNID_SONG).list,le_current_level->song_title.c_str())) != -1)
621 level_settings_menu->get_item_by_id(MNID_SONG).list->active_item = i;
622 if((i = string_list_find(level_settings_menu->get_item_by_id(MNID_BGIMG).list,le_current_level->bkgd_image.c_str())) != -1)
623 level_settings_menu->get_item_by_id(MNID_BGIMG).list->active_item = i;
624 if((i = string_list_find(level_settings_menu->get_item_by_id(MNID_PARTICLE).list,le_current_level->particle_system.c_str())) != -1)
625 level_settings_menu->get_item_by_id(MNID_PARTICLE).list->active_item = i;
627 sprintf(str,"%d",le_current_level->width);
628 level_settings_menu->get_item_by_id(MNID_LENGTH).change_input(str);
629 sprintf(str,"%d",le_current_level->time_left);
630 level_settings_menu->get_item_by_id(MNID_TIME).change_input(str);
631 sprintf(str,"%2.0f",le_current_level->gravity);
632 level_settings_menu->get_item_by_id(MNID_GRAVITY).change_input(str);
633 sprintf(str,"%d",le_current_level->bkgd_speed);
634 level_settings_menu->get_item_by_id(MNID_BGSPEED).change_input(str);
635 sprintf(str,"%d",le_current_level->bkgd_top.red);
636 level_settings_menu->get_item_by_id(MNID_TopRed).change_input(str);
637 sprintf(str,"%d",le_current_level->bkgd_top.green);
638 level_settings_menu->get_item_by_id(MNID_TopGreen).change_input(str);
639 sprintf(str,"%d",le_current_level->bkgd_top.blue);
640 level_settings_menu->get_item_by_id(MNID_TopBlue).change_input(str);
641 sprintf(str,"%d",le_current_level->bkgd_bottom.red);
642 level_settings_menu->get_item_by_id(MNID_BottomRed).change_input(str);
643 sprintf(str,"%d",le_current_level->bkgd_bottom.green);
644 level_settings_menu->get_item_by_id(MNID_BottomGreen).change_input(str);
645 sprintf(str,"%d",le_current_level->bkgd_bottom.blue);
646 level_settings_menu->get_item_by_id(MNID_BottomBlue).change_input(str);
649 void update_subset_settings_menu()
651 subset_settings_menu->item[2].change_input(le_level_subset->title.c_str());
652 subset_settings_menu->item[3].change_input(le_level_subset->description.c_str());
655 void apply_level_settings_menu()
660 le_current_level->name = level_settings_menu->get_item_by_id(MNID_NAME).input;
661 le_current_level->author = level_settings_menu->get_item_by_id(MNID_AUTHOR).input;
663 if(le_current_level->bkgd_image.compare(string_list_active(level_settings_menu->get_item_by_id(MNID_BGIMG).list)) != 0)
665 le_current_level->bkgd_image = string_list_active(level_settings_menu->get_item_by_id(MNID_BGIMG).list);
669 if(le_current_level->particle_system.compare(string_list_active(level_settings_menu->get_item_by_id(MNID_PARTICLE).list)) != 0)
671 le_current_level->particle_system = string_list_active(level_settings_menu->get_item_by_id(MNID_PARTICLE).list);
676 le_current_level->load_gfx();
679 le_current_level->song_title = string_list_active(level_settings_menu->get_item_by_id(MNID_SONG).list);
681 le_current_level->change_size(atoi(level_settings_menu->get_item_by_id(MNID_LENGTH).input));
682 le_current_level->time_left = atoi(level_settings_menu->get_item_by_id(MNID_BGIMG).input);
683 le_current_level->gravity = atof(level_settings_menu->get_item_by_id(MNID_GRAVITY).input);
684 le_current_level->bkgd_speed = atoi(level_settings_menu->get_item_by_id(MNID_BGSPEED).input);
685 le_current_level->bkgd_top.red = atoi(level_settings_menu->get_item_by_id(MNID_TopRed).input);
686 le_current_level->bkgd_top.green = atoi(level_settings_menu->get_item_by_id(MNID_TopGreen).input);
687 le_current_level->bkgd_top.blue = atoi(level_settings_menu->get_item_by_id(MNID_TopBlue).input);
688 le_current_level->bkgd_bottom.red = atoi(level_settings_menu->get_item_by_id(MNID_BottomRed).input);
689 le_current_level->bkgd_bottom.green = atoi(level_settings_menu->get_item_by_id(MNID_BottomGreen).input);
690 le_current_level->bkgd_bottom.blue = atoi(level_settings_menu->get_item_by_id(MNID_BottomBlue).input);
693 void save_subset_settings_menu()
695 le_level_subset->title = subset_settings_menu->item[2].input;
696 le_level_subset->description = subset_settings_menu->item[3].input;
697 le_level_subset->save();
700 void le_goto_level(int levelnb)
702 le_world.arrays_free();
704 le_current_level->cleanup();
705 if(le_current_level->load(le_level_subset->name.c_str(), levelnb) != 0)
707 le_current_level->load(le_level_subset->name.c_str(), le_level);
716 le_current_level->load_gfx();
718 le_world.activate_bad_guys();
723 /*if(level_changed == true)
724 if(askforsaving() == CANCEL)
727 SDL_EnableKeyRepeat(0, 0); // disables key repeating
730 delete leveleditor_menu;
731 delete subset_load_menu;
732 delete subset_new_menu;
733 delete subset_settings_menu;
734 delete level_settings_menu;
735 delete select_tilegroup_menu;
736 delete select_objects_menu;
737 delete le_save_level_bt;
739 delete le_test_level_bt;
740 delete le_next_level_bt;
741 delete le_previous_level_bt;
742 delete le_move_right_bt;
743 delete le_move_left_bt;
745 delete le_select_mode_one_bt;
746 delete le_select_mode_two_bt;
747 delete le_settings_bt;
748 delete le_tilegroup_bt;
749 delete le_objects_bt;
750 delete le_tilemap_panel;
752 delete le_current_level;
753 le_current_level = 0;
754 delete le_level_subset;
757 for(ButtonPanelMap::iterator i = tilegroups_map.begin();
758 i != tilegroups_map.end(); ++i)
762 for(ButtonPanelMap::iterator i = objects_map.begin();
763 i != objects_map.end(); ++i)
769 void le_drawinterface()
774 if(le_current_level != NULL)
776 /* draw a grid (if selected) */
779 for(x = 0; x < 19; x++)
780 fillrect(x*32 - ((int)pos_x % 32), 0, 1, screen->h, 225, 225, 225,255);
781 for(y = 0; y < 15; y++)
782 fillrect(0, y*32, screen->w - 32, 1, 225, 225, 225,255);
786 if(le_selection_mode == CURSOR)
787 le_selection->draw( cursor_x - scroll_x, cursor_y);
788 else if(le_selection_mode == SQUARE)
791 le_highlight_selection();
792 /* draw current selection */
793 w = selection.x2 - selection.x1;
794 h = selection.y2 - selection.y1;
795 fillrect(selection.x1 - pos_x, selection.y1, w, SELECT_W, SELECT_CLR);
796 fillrect(selection.x1 - pos_x + w, selection.y1, SELECT_W, h, SELECT_CLR);
797 fillrect(selection.x1 - pos_x, selection.y1 + h, w, SELECT_W, SELECT_CLR);
798 fillrect(selection.x1 - pos_x, selection.y1, SELECT_W, h, SELECT_CLR);
802 /* draw button bar */
803 fillrect(screen->w - 64, 0, 64, screen->h, 50, 50, 50,255);
805 if(le_current.IsTile())
807 Tile::draw(19 * 32, 14 * 32, le_current.tile);
808 if(TileManager::instance()->get(le_current.tile)->editor_images.size() > 0)
809 TileManager::instance()->get(le_current.tile)->editor_images[0]->draw( 19 * 32, 14 * 32);
811 if(le_current.IsObject())
813 le_current.obj->draw_on_screen(19 * 32, 14 * 32);
816 //if(le_current.IsObject())
819 if(le_current_level != NULL)
821 le_save_level_bt->draw();
823 le_test_level_bt->draw();
824 le_next_level_bt->draw();
825 le_previous_level_bt->draw();
826 le_rubber_bt->draw();
827 if(le_selection_mode == SQUARE)
828 le_select_mode_one_bt->draw();
829 else if(le_selection_mode == CURSOR)
830 le_select_mode_two_bt->draw();
831 le_settings_bt->draw();
832 le_move_right_bt->draw();
833 le_move_left_bt->draw();
834 le_tilegroup_bt->draw();
835 le_objects_bt->draw();
836 if(!cur_tilegroup.empty())
837 tilegroups_map[cur_tilegroup]->draw();
838 else if(!cur_objects.empty())
840 objects_map[cur_objects]->draw();
843 le_tilemap_panel->draw();
845 sprintf(str, "%d/%d", le_level,le_level_subset->levels);
846 white_text->drawf(str, -10, 16, A_RIGHT, A_TOP, 0);
848 white_small_text->draw("F1 for Help", 10, 430, 1);
853 white_small_text->draw("No Level Subset loaded - Press ESC and choose one in the menu", 10, 430, 1);
855 white_small_text->draw("No Level Subset loaded", 10, 430, 1);
862 unsigned int y,x,i,s;
865 /* Draw the real background */
866 if(le_current_level->bkgd_image[0] != '\0')
868 s = (int)((float)pos_x * ((float)le_current_level->bkgd_speed/60.)) % screen->w;
869 le_current_level->img_bkgd->draw_part(s,0,0,0,
870 le_current_level->img_bkgd->w - s - 32, le_current_level->img_bkgd->h);
871 le_current_level->img_bkgd->draw_part(0,0,screen->w - s - 32 ,0,s,
872 le_current_level->img_bkgd->h);
876 drawgradient(le_current_level->bkgd_top, le_current_level->bkgd_bottom);
879 if(le_current.IsTile())
881 Tile::draw(cursor_x, cursor_y,le_current.tile,128);
882 if(!TileManager::instance()->get(le_current.tile)->images.empty())
883 fillrect(cursor_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);
885 if(le_current.IsObject())
887 le_current.obj->move_to(cursor_x, cursor_y);
890 /* clearscreen(current_level.bkgd_red, current_level.bkgd_green, current_level.bkgd_blue); */
892 for (y = 0; y < 15; ++y)
893 for (x = 0; x < 20; ++x)
896 if(active_tm == TM_BG)
901 Tile::draw(32*x - fmodf(pos_x, 32), y * 32, le_current_level->bg_tiles[y][x + (int)(pos_x / 32)],a);
903 if(active_tm == TM_IA)
908 Tile::draw(32*x - fmodf(pos_x, 32), y * 32, le_current_level->ia_tiles[y][x + (int)(pos_x / 32)],a);
910 if(active_tm == TM_FG)
915 Tile::draw(32*x - fmodf(pos_x, 32), y * 32, le_current_level->fg_tiles[y][x + (int)(pos_x / 32)],a);
917 /* draw whats inside stuff when cursor is selecting those */
918 /* (draw them all the time - is this the right behaviour?) */
919 if(TileManager::instance()->get(le_current_level->ia_tiles[y][x + (int)(pos_x / 32)])->editor_images.size() > 0)
920 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);
924 /* Draw the Bad guys: */
925 for (i = 0; i < le_world.bad_guys.size(); ++i)
927 /* to support frames: img_bsod_left[(frame / 5) % 4] */
930 le_world.bad_guys[i].draw();
934 /* Draw the player: */
935 /* for now, the position is fixed at (100, 240) */
936 largetux.walk_right->draw( 100 - pos_x, 240);
939 void le_checkevents()
946 keymod = SDL_GetModState();
948 while(SDL_PollEvent(&event))
952 Menu::current()->event(event);
956 mouse_cursor->set_state(MC_NORMAL);
958 /* testing SDL_KEYDOWN, SDL_KEYUP and SDL_QUIT events*/
959 if(event.type == SDL_KEYDOWN
960 || ((event.type == SDL_MOUSEBUTTONDOWN || SDL_MOUSEMOTION)
961 && (event.motion.x > 0
962 && event.motion.x < screen->w - 64 &&
963 event.motion.y > 0 && event.motion.y < screen->h)))
967 case SDL_KEYDOWN: // key pressed
968 key = event.key.keysym.sym;
972 Menu::set_current(leveleditor_menu);
975 cursor_x -= KEY_CURSOR_SPEED;
977 cursor_x -= KEY_CURSOR_FASTSPEED;
979 if(cursor_x < pos_x + MOUSE_LEFT_MARGIN)
980 pos_x = cursor_x - MOUSE_LEFT_MARGIN;
985 cursor_x += KEY_CURSOR_SPEED;
987 cursor_x += KEY_CURSOR_FASTSPEED;
989 if(cursor_x > pos_x + MOUSE_RIGHT_MARGIN-32)
990 pos_x = cursor_x - MOUSE_RIGHT_MARGIN+32;
995 cursor_y -= KEY_CURSOR_SPEED;
997 cursor_y -= KEY_CURSOR_FASTSPEED;
1004 cursor_y += KEY_CURSOR_SPEED;
1006 cursor_y += KEY_CURSOR_FASTSPEED;
1008 if(cursor_y > screen->h-32)
1009 cursor_y = screen->h-32;
1022 cursor_x = (le_current_level->width * 32) - 32;
1026 le_show_grid = !le_show_grid;
1032 case SDL_KEYUP: /* key released */
1033 switch(event.key.keysym.sym)
1042 case SDL_MOUSEBUTTONDOWN:
1043 if(event.button.button == SDL_BUTTON_LEFT)
1045 le_mouse_pressed[LEFT] = true;
1047 selection.x1 = event.motion.x + pos_x;
1048 selection.y1 = event.motion.y;
1049 selection.x2 = event.motion.x + pos_x;
1050 selection.y2 = event.motion.y;
1052 else if(event.button.button == SDL_BUTTON_RIGHT)
1054 le_mouse_pressed[RIGHT] = true;
1057 case SDL_MOUSEBUTTONUP:
1058 if(event.button.button == SDL_BUTTON_LEFT)
1060 le_mouse_pressed[LEFT] = false;
1061 le_mouse_clicked[LEFT] = true;
1063 else if(event.button.button == SDL_BUTTON_RIGHT)
1065 le_mouse_pressed[RIGHT] = false;
1066 le_mouse_clicked[RIGHT] = true;
1069 case SDL_MOUSEMOTION:
1071 if(!Menu::current())
1076 if(le_current.IsTile())
1078 cursor_x = ((int)(pos_x + x) / 32) * 32;
1079 cursor_y = ((int) y / 32) * 32;
1087 if(le_mouse_pressed[LEFT])
1089 selection.x2 = x + pos_x;
1093 if(le_mouse_pressed[RIGHT])
1095 pos_x += -1 * event.motion.xrel;
1099 case SDL_QUIT: // window closed
1108 if(le_current_level != NULL)
1110 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 &&
1111 event.motion.y > 0 && event.motion.y < screen->h)))
1113 le_mouse_pressed[LEFT] = false;
1114 le_mouse_pressed[RIGHT] = false;
1116 if(!Menu::current())
1118 /* Check for button events */
1119 le_test_level_bt->event(event);
1120 if(le_test_level_bt->get_state() == BUTTON_CLICKED)
1122 le_save_level_bt->event(event);
1123 if(le_save_level_bt->get_state() == BUTTON_CLICKED)
1124 le_current_level->save(le_level_subset->name.c_str(),le_level);
1125 le_exit_bt->event(event);
1126 if(le_exit_bt->get_state() == BUTTON_CLICKED)
1128 Menu::set_current(leveleditor_menu);
1130 le_next_level_bt->event(event);
1131 if(le_next_level_bt->get_state() == BUTTON_CLICKED)
1133 if(le_level < le_level_subset->levels)
1135 le_goto_level(++le_level);
1141 sprintf(str,"Level %d doesn't exist. Create it?",le_level+1);
1142 if(confirm_dialog(str))
1144 new_lev.init_defaults();
1145 new_lev.save(le_level_subset->name.c_str(),++le_level);
1146 le_level_subset->levels = le_level;
1147 le_goto_level(le_level);
1151 le_previous_level_bt->event(event);
1152 if(le_previous_level_bt->get_state() == BUTTON_CLICKED)
1155 le_goto_level(--le_level);
1157 le_rubber_bt->event(event);
1158 if(le_rubber_bt->get_state() == BUTTON_CLICKED)
1161 if(le_selection_mode == SQUARE)
1163 le_select_mode_one_bt->event(event);
1164 if(le_select_mode_one_bt->get_state() == BUTTON_CLICKED)
1165 le_selection_mode = CURSOR;
1169 le_select_mode_two_bt->event(event);
1170 if(le_select_mode_two_bt->get_state() == BUTTON_CLICKED)
1171 le_selection_mode = SQUARE;
1174 le_tilegroup_bt->event(event);
1175 if(le_tilegroup_bt->get_state() == BUTTON_CLICKED)
1177 Menu::set_current(select_tilegroup_menu);
1178 select_tilegroup_menu_effect.start(200);
1179 select_tilegroup_menu->set_pos(screen->w - 64,100,-0.5,0.5);
1182 le_objects_bt->event(event);
1183 if(le_objects_bt->get_state() == BUTTON_CLICKED)
1185 Menu::set_current(select_objects_menu);
1186 select_objects_menu_effect.start(200);
1187 select_objects_menu->set_pos(screen->w - 64,100,-0.5,0.5);
1190 le_settings_bt->event(event);
1191 if(le_settings_bt->get_state() == BUTTON_CLICKED)
1193 update_level_settings_menu();
1194 Menu::set_current(level_settings_menu);
1196 if(!cur_tilegroup.empty())
1198 if((pbutton = tilegroups_map[cur_tilegroup]->event(event)) != NULL)
1200 if(pbutton->get_state() == BUTTON_CLICKED)
1202 if(le_current.IsObject())
1203 le_current.obj->move_to(pbutton->get_pos().x,pbutton->get_pos().y);
1204 le_current.Tile(pbutton->get_tag());
1208 else if(!cur_objects.empty())
1210 if((pbutton = objects_map[cur_objects]->event(event)) != NULL)
1212 if(pbutton->get_state() == BUTTON_CLICKED)
1214 if(le_current.IsObject())
1215 le_current.obj->move_to(pbutton->get_pos().x,pbutton->get_pos().y);
1216 le_current.Object(pbutton->get_game_object());
1221 if((pbutton = le_tilemap_panel->event(event)) != NULL)
1223 if(pbutton->get_state() == BUTTON_CLICKED)
1225 active_tm = static_cast<TileMapType>(pbutton->get_tag());
1231 le_settings_bt->event(event);
1232 if(le_settings_bt->get_state() == BUTTON_CLICKED)
1234 Menu::set_current(0);
1236 le_tilegroup_bt->event(event);
1237 if(le_tilegroup_bt->get_state() == BUTTON_CLICKED)
1239 Menu::set_current(0);
1244 if(!Menu::current())
1246 le_move_left_bt->event(event);
1247 le_move_right_bt->event(event);
1249 if(le_mouse_pressed[LEFT])
1251 if(le_current.IsTile())
1252 le_change(cursor_x, cursor_y, active_tm, le_current.tile);
1254 else if(le_mouse_clicked[LEFT])
1256 if(le_current.IsObject())
1258 std::string type = le_current.obj->type();
1259 if(type == "BadGuy")
1261 BadGuy* pbadguy = dynamic_cast<BadGuy*>(le_current.obj);
1263 le_world.bad_guys.push_back(BadGuy(cursor_x+scroll_x, cursor_y,pbadguy->kind,false));
1264 le_current_level->badguy_data.push_back(&le_world.bad_guys.back());
1267 le_mouse_clicked[LEFT] = false;
1272 if(!Menu::current())
1274 if(le_move_left_bt->get_state() == BUTTON_PRESSED)
1278 else if(le_move_left_bt->get_state() == BUTTON_HOVER)
1283 if(le_move_right_bt->get_state() == BUTTON_PRESSED)
1287 else if(le_move_right_bt->get_state() == BUTTON_HOVER)
1295 void le_highlight_selection()
1299 if(selection.x1 < selection.x2)
1309 if(selection.y1 < selection.y2)
1325 fillrect(x1*32-pos_x, y1*32,32* (x2 - x1 + 1),32 * (y2 - y1 + 1),173,234,177,103);
1328 void le_change(float x, float y, int tm, unsigned int c)
1330 if(le_current_level != NULL)
1336 /* level_changed = true; */
1338 switch(le_selection_mode)
1341 le_current_level->change(x,y,tm,c);
1343 base_type cursor_base;
1346 cursor_base.width = 32;
1347 cursor_base.height = 32;
1349 /* if there is a bad guy over there, remove it */
1350 for(i = 0; i < le_world.bad_guys.size(); ++i)
1351 if(rectcollision(cursor_base,le_world.bad_guys[i].base))
1353 le_world.bad_guys.erase(le_world.bad_guys.begin() + i);
1354 le_current_level->badguy_data.erase(le_current_level->badguy_data.begin() + i);
1359 if(selection.x1 < selection.x2)
1369 if(selection.y1 < selection.y2)
1385 /* if there is a bad guy over there, remove it */
1386 for(std::vector<BadGuy>::iterator i = le_world.bad_guys.begin();
1387 i != le_world.bad_guys.end(); /* will be at end of loop */)
1389 if(i->base.x/32 >= x1 && i->base.x/32 <= x2
1390 && i->base.y/32 >= y1 && i->base.y/32 <= y2)
1392 i = le_world.bad_guys.erase(i);
1401 for(xx = x1; xx <= x2; xx++)
1402 for(yy = y1; yy <= y2; yy++)
1404 le_current_level->change(xx*32, yy*32, tm, c);
1416 le_current_level->save("test", le_level);
1418 GameSession session("test",le_level, ST_GL_TEST);
1420 player_status.reset();
1422 music_manager->halt_music();
1424 Menu::set_current(NULL);
1425 le_world.arrays_free();
1426 le_current_level->load_gfx();
1427 le_world.activate_bad_guys();
1433 unsigned int i, done_;
1435 " - This is SuperTux's built-in level editor -",
1436 "It has been designed to be light and easy to use from the start.",
1438 "When you first load the level editor you are given a menu where you",
1439 "can load level subsets, create a new level subset, edit the current",
1440 "subset's settings, or simply quit the editor. You can access this menu",
1441 "from the level editor at any time by pressing the escape key.",
1443 "To your right is your button bar. The center of this contains many",
1444 "tiles you can use to make your level. To select a tile, click on it",
1445 "with your left mouse button; your selection will be shown in the",
1446 "bottom right corner of the button box. Click anywhere on your level",
1447 "with the left mouse button to place that tile down. If you right click",
1448 "a tile in the button bar, you can find out what its keyboard shortcut",
1449 "is. The three buttons FGD, BGD and EMY let you pick from foreground,",
1450 "background, and enemy tiles. The eraser lets you remove tiles.",
1451 "The left and right arrow keys scroll back and forth through your level.",
1452 "The button with the wrench and screwdriver, lets you change the",
1453 "settings of your level, including how long it is or what music it will",
1454 "play. When you are ready to give your level a test, click on the little",
1455 "running Tux. If you like the changes you have made to your level,",
1456 "press the red save key to keep them.",
1457 "To change which level in your subset you are editing, press the white",
1458 "up and down arrow keys at the top of the button box.",
1460 "Have fun making levels! If you make some good ones, send them to us on",
1461 "the SuperTux mailing list!",
1466 blue_text->drawf("- Help -", 0, 30, A_HMIDDLE, A_TOP, 2);
1468 for(i = 0; i < sizeof(text)/sizeof(char *); i++)
1469 white_small_text->draw(text[i], 5, 80+(i*white_small_text->h), 1);
1471 gold_text->drawf("Press Any Key to Continue", 0, 440, A_HMIDDLE, A_TOP, 1);
1479 done_ = wait_for_event(event);