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 int pos_x, cursor_x, cursor_y, fire;
133 static LevelEditorWorld le_world;
134 static LevelSubset* le_level_subset;
135 static int le_show_grid;
137 static Surface* le_selection;
139 static TileOrObject le_current;
140 static bool le_mouse_pressed[2];
141 static bool le_mouse_clicked[2];
142 static Button* le_save_level_bt;
143 static Button* le_exit_bt;
144 static Button* le_test_level_bt;
145 static Button* le_next_level_bt;
146 static Button* le_previous_level_bt;
147 static Button* le_move_right_bt;
148 static Button* le_move_left_bt;
149 static Button* le_rubber_bt;
150 static Button* le_select_mode_one_bt;
151 static Button* le_select_mode_two_bt;
152 static Button* le_settings_bt;
153 static Button* le_tilegroup_bt;
154 static Button* le_objects_bt;
155 static ButtonPanel* le_tilemap_panel;
156 static Menu* leveleditor_menu;
157 static Menu* subset_load_menu;
158 static Menu* subset_new_menu;
159 static Menu* subset_settings_menu;
160 static Menu* level_settings_menu;
161 static Menu* select_tilegroup_menu;
162 static Menu* select_objects_menu;
163 static Timer select_tilegroup_menu_effect;
164 static Timer select_objects_menu_effect;
165 typedef std::map<std::string, ButtonPanel*> ButtonPanelMap;
166 static ButtonPanelMap tilegroups_map;
167 static ButtonPanelMap objects_map;
168 static std::string cur_tilegroup;
169 static std::string cur_objects;
171 static square selection;
172 static int le_selection_mode;
173 static SDL_Event event;
174 TileMapType active_tm;
176 void le_set_defaults()
178 if(le_current_level != NULL)
182 if(le_current_level->time_left == 0)
183 le_current_level->time_left = 255;
187 int leveleditor(char* filename)
189 int last_time, now_time, i;
198 clearscreen(0, 0, 0);
201 music_manager->halt_music();
203 while (SDL_PollEvent(&event))
207 if(le_load_level(filename))
212 last_time = SDL_GetTicks();
217 if(Menu::current() == select_tilegroup_menu)
219 if(select_tilegroup_menu_effect.check())
221 select_tilegroup_menu->set_pos(screen->w - 64 + select_tilegroup_menu_effect.get_left(),
225 select_tilegroup_menu->set_pos(screen->w - 64,66,-0.5,0.5);
227 else if(Menu::current() == select_objects_menu)
229 if(select_objects_menu_effect.check())
231 select_objects_menu->set_pos(screen->w - 64 + select_objects_menu_effect.get_left(),82,-0.5,0.5);
234 select_objects_menu->set_pos(screen->w - 64,82,-0.5,0.5);
237 if(le_current_level != NULL)
239 /* making events results to be in order */
242 if(pos_x > (le_current_level->width * 32) - screen->w)
243 pos_x = (le_current_level->width * 32) - screen->w;
249 clearscreen(0, 0, 0);
251 /* draw editor interface */
254 Menu* menu = Menu::current();
260 if(menu == leveleditor_menu)
262 switch (leveleditor_menu->check())
264 case MNID_RETURNLEVELEDITOR:
265 Menu::set_current(0);
267 case MNID_SUBSETSETTINGS:
268 update_subset_settings_menu();
270 case MNID_QUITLEVELEDITOR:
275 else if(menu == level_settings_menu)
277 switch (level_settings_menu->check())
280 apply_level_settings_menu();
281 Menu::set_current(NULL);
289 else if(menu == select_tilegroup_menu)
292 switch (it = select_tilegroup_menu->check())
298 = select_tilegroup_menu->get_item_by_id(it).text;
299 Menu::set_current(0);
306 else if(menu == select_objects_menu)
309 switch (it = select_objects_menu->check())
314 cur_objects = select_objects_menu->get_item_by_id(it).text;
315 cur_tilegroup.clear();
317 Menu::set_current(0);
322 else if(menu == subset_load_menu)
324 switch (i = subset_load_menu->check())
331 if(le_load_level(level_subsets.item[i-1]))
337 else if(menu == subset_new_menu)
339 if(subset_new_menu->item[2].input[0] == '\0')
340 subset_new_menu->item[3].kind = MN_DEACTIVE;
343 subset_new_menu->item[3].kind = MN_ACTION;
345 switch (i = subset_new_menu->check())
347 case MNID_CREATESUBSET:
348 LevelSubset::create(subset_new_menu->get_item_by_id(MNID_SUBSETNAME).input);
349 le_level_subset->load(subset_new_menu->get_item_by_id(MNID_SUBSETNAME).input);
350 leveleditor_menu->get_item_by_id(MNID_SUBSETSETTINGS).kind = MN_GOTO;
352 le_world.arrays_free();
353 delete le_current_level;
354 le_current_level = new Level;
355 if(le_current_level->load(le_level_subset->name, le_level) != 0)
361 le_current_level->load_gfx();
362 le_world.activate_bad_guys();
363 subset_new_menu->get_item_by_id(MNID_SUBSETNAME).change_input("");
365 Menu::set_current(subset_settings_menu);
370 else if(menu == subset_settings_menu)
372 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 )
373 subset_settings_menu->get_item_by_id(MNID_SUBSETSAVECHANGES).kind = MN_DEACTIVE;
375 subset_settings_menu->get_item_by_id(MNID_SUBSETSAVECHANGES).kind = MN_ACTION;
377 switch (i = subset_settings_menu->check())
379 case MNID_SUBSETSAVECHANGES:
380 save_subset_settings_menu();
381 Menu::set_current(leveleditor_menu);
387 mouse_cursor->draw();
395 ++global_frame_counter;
398 now_time = SDL_GetTicks();
399 if (now_time < last_time + FPS)
400 SDL_Delay(last_time + FPS - now_time); /* delay some time */
408 int le_load_level(char *filename)
410 le_level_subset->load(filename);
411 leveleditor_menu->get_item_by_id(MNID_SUBSETSETTINGS).kind = MN_GOTO;
413 le_world.arrays_free();
414 delete le_current_level;
415 le_current_level = new Level;
416 if(le_current_level->load(le_level_subset->name, le_level) != 0)
422 le_current_level->load_gfx();
423 le_world.activate_bad_guys();
425 Menu::set_current(NULL);
434 leveleditor_menu = new Menu();
435 subset_load_menu = new Menu();
436 subset_new_menu = new Menu();
437 subset_settings_menu = new Menu();
438 level_settings_menu = new Menu();
439 select_tilegroup_menu = new Menu();
440 select_objects_menu = new Menu();
442 leveleditor_menu->additem(MN_LABEL,"Level Editor Menu",0,0);
443 leveleditor_menu->additem(MN_HL,"",0,0);
444 leveleditor_menu->additem(MN_ACTION,"Return To Level Editor",0,0,MNID_RETURNLEVELEDITOR);
445 leveleditor_menu->additem(MN_DEACTIVE,"Level Subset Settings",0,subset_settings_menu,MNID_SUBSETSETTINGS);
446 leveleditor_menu->additem(MN_GOTO,"Load Level Subset",0,subset_load_menu);
447 leveleditor_menu->additem(MN_GOTO,"New Level Subset",0,subset_new_menu);
448 leveleditor_menu->additem(MN_HL,"",0,0);
449 leveleditor_menu->additem(MN_ACTION,"Quit Level Editor",0,0,MNID_QUITLEVELEDITOR);
451 Menu::set_current(leveleditor_menu);
453 subset_load_menu->additem(MN_LABEL, "Load Level Subset", 0, 0);
454 subset_load_menu->additem(MN_HL, "", 0, 0);
456 for(i = 0; i < level_subsets.num_items; ++i)
458 subset_load_menu->additem(MN_ACTION,level_subsets.item[i],0,0, i+1);
460 subset_load_menu->additem(MN_HL,"",0,0);
461 subset_load_menu->additem(MN_BACK,"Back",0,0);
463 subset_new_menu->additem(MN_LABEL,"New Level Subset",0,0);
464 subset_new_menu->additem(MN_HL,"",0,0);
465 subset_new_menu->additem(MN_TEXTFIELD,"Enter Name",0,0,MNID_SUBSETNAME);
466 subset_new_menu->additem(MN_ACTION,"Create",0,0, MNID_CREATESUBSET);
467 subset_new_menu->additem(MN_HL,"",0,0);
468 subset_new_menu->additem(MN_BACK,"Back",0,0);
470 subset_settings_menu->additem(MN_LABEL,"Level Subset Settings",0,0);
471 subset_settings_menu->additem(MN_HL,"",0,0);
472 subset_settings_menu->additem(MN_TEXTFIELD,"Title",0,0,MNID_SUBSETTITLE);
473 subset_settings_menu->additem(MN_TEXTFIELD,"Description",0,0,MNID_SUBSETDESCRIPTION);
474 subset_settings_menu->additem(MN_HL,"",0,0);
475 subset_settings_menu->additem(MN_ACTION,"Save Changes",0,0,MNID_SUBSETSAVECHANGES);
476 subset_settings_menu->additem(MN_HL,"",0,0);
477 subset_settings_menu->additem(MN_BACK,"Back",0,0);
479 level_settings_menu->arrange_left = true;
480 level_settings_menu->additem(MN_LABEL,"Level Settings",0,0);
481 level_settings_menu->additem(MN_HL,"",0,0);
482 level_settings_menu->additem(MN_TEXTFIELD,"Name ",0,0,MNID_NAME);
483 level_settings_menu->additem(MN_TEXTFIELD,"Author ",0,0,MNID_AUTHOR);
484 level_settings_menu->additem(MN_STRINGSELECT,"Song ",0,0,MNID_SONG);
485 level_settings_menu->additem(MN_STRINGSELECT,"Bg-Image",0,0,MNID_BGIMG);
486 level_settings_menu->additem(MN_STRINGSELECT,"Particle",0,0,MNID_PARTICLE);
487 level_settings_menu->additem(MN_NUMFIELD,"Length ",0,0,MNID_LENGTH);
488 level_settings_menu->additem(MN_NUMFIELD,"Time ",0,0,MNID_TIME);
489 level_settings_menu->additem(MN_NUMFIELD,"Gravity",0,0,MNID_GRAVITY);
490 level_settings_menu->additem(MN_NUMFIELD,"Bg-Img-Speed",0,0,MNID_BGSPEED);
491 level_settings_menu->additem(MN_NUMFIELD,"Top Red ",0,0,MNID_TopRed);
492 level_settings_menu->additem(MN_NUMFIELD,"Top Green ",0,0,MNID_TopGreen);
493 level_settings_menu->additem(MN_NUMFIELD,"Top Blue ",0,0,MNID_TopBlue);
494 level_settings_menu->additem(MN_NUMFIELD,"Bottom Red ",0,0,MNID_BottomRed);
495 level_settings_menu->additem(MN_NUMFIELD,"Bottom Green",0,0,MNID_BottomGreen);
496 level_settings_menu->additem(MN_NUMFIELD,"Bottom Blue",0,0,MNID_BottomBlue);
497 level_settings_menu->additem(MN_HL,"",0,0);
498 level_settings_menu->additem(MN_ACTION,"Apply Changes",0,0,MNID_APPLY);
500 select_tilegroup_menu->arrange_left = true;
501 select_tilegroup_menu->additem(MN_LABEL,"Tilegroup",0,0);
502 select_tilegroup_menu->additem(MN_HL,"",0,0);
503 std::vector<TileGroup>* tilegroups = TileManager::tilegroups();
505 for(std::vector<TileGroup>::iterator it = tilegroups->begin();
506 it != tilegroups->end(); ++it )
508 select_tilegroup_menu->additem(MN_ACTION, it->name, 0, 0, tileid);
510 tilegroups_map[(*it).name] = new ButtonPanel(screen->w - 64,96, 64, 318);
513 for(std::vector<int>::iterator sit = (*it).tiles.begin();
514 sit != (*it).tiles.end(); ++sit, ++i)
516 std::string imagefile = "/images/tilesets/" ;
517 bool only_editor_image = false;
518 if(!TileManager::instance()->get(*sit)->filenames.empty())
520 imagefile += TileManager::instance()->get(*sit)->filenames[0];
522 else if(!TileManager::instance()->get(*sit)->editor_filenames.empty())
524 imagefile += TileManager::instance()->get(*sit)->editor_filenames[0];
525 only_editor_image = true;
529 imagefile += "notile.png";
531 Button* button = new Button(imagefile, it->name, SDLKey(SDLK_a + i),
533 if(!only_editor_image)
534 if(!TileManager::instance()->get(*sit)->editor_filenames.empty())
536 imagefile = "/images/tilesets/" + TileManager::instance()->get(*sit)->editor_filenames[0];
537 button->add_icon(imagefile,32,32);
539 tilegroups_map[it->name]->additem(button, *sit);
542 select_tilegroup_menu->additem(MN_HL,"",0,0);
544 select_objects_menu->arrange_left = true;
545 select_objects_menu->additem(MN_LABEL,"Objects",0,0);
546 select_objects_menu->additem(MN_HL,"",0,0);
547 select_objects_menu->additem(MN_ACTION,"BadGuys",0,0,1);
548 objects_map["BadGuys"] = new ButtonPanel(screen->w - 64,96, 64, 318);
550 for(int i = 0; i < NUM_BadGuyKinds; ++i)
552 BadGuy bad_tmp(0,0,BadGuyKind(i),false);
553 objects_map["BadGuys"]->additem(new Button("", "BadGuy",(SDLKey)(i+'a'),0,0,32,32),1000000+i);
554 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));
557 select_objects_menu->additem(MN_HL,"",0,0);
563 level_subsets = dsubdirs("/levels", "info");
564 le_level_subset = new LevelSubset;
572 le_frame = 0; /* support for frames in some tiles, like waves and bad guys */
573 le_level_changed = false;
574 le_current_level = NULL;
576 le_mouse_pressed[LEFT] = false;
577 le_mouse_pressed[RIGHT] = false;
579 le_mouse_clicked[LEFT] = false;
580 le_mouse_clicked[RIGHT] = false;
582 le_selection = new Surface(datadir + "/images/leveleditor/select.png", USE_ALPHA);
584 select_tilegroup_menu_effect.init(false);
585 select_objects_menu_effect.init(false);
588 le_save_level_bt = new Button("/images/icons/save.png","Save level", SDLK_F6,screen->w-64,32);
589 le_exit_bt = new Button("/images/icons/exit.png","Exit", SDLK_F6,screen->w-32,32);
590 le_next_level_bt = new Button("/images/icons/up.png","Next level", SDLK_PAGEUP,screen->w-64,0);
591 le_previous_level_bt = new Button("/images/icons/down.png","Previous level",SDLK_PAGEDOWN,screen->w-32,0);
592 le_rubber_bt = new Button("/images/icons/rubber.png","Rubber",SDLK_DELETE,screen->w-32,48);
593 le_select_mode_one_bt = new Button ("/images/icons/select-mode1.png","Select single tile",SDLK_F3,screen->w-64,48);
594 le_select_mode_two_bt = new Button("/images/icons/select-mode2.png","Select multiple tiles",SDLK_F3,screen->w-64,48);
595 le_test_level_bt = new Button("/images/icons/test-level.png","Test level",SDLK_F4,screen->w-64,screen->h - 64);
596 le_settings_bt = new Button("/images/icons/settings.png","Level settings",SDLK_F5,screen->w-32,screen->h - 64);
597 le_move_left_bt = new Button("/images/icons/left.png","Move left",SDLK_LEFT,0,0);
598 le_move_right_bt = new Button("/images/icons/right.png","Move right",SDLK_RIGHT,screen->w-80,0);
599 le_tilegroup_bt = new Button("/images/icons/tilegroup.png","Select Tilegroup", SDLK_F7,screen->w-64,64);
600 le_objects_bt = new Button("/images/icons/objects.png","Select Objects", SDLK_F7,screen->w-64,80);
602 le_tilemap_panel = new ButtonPanel(screen->w-64,screen->h-32,32,32);
603 le_tilemap_panel->set_button_size(32,10);
604 le_tilemap_panel->additem(new Button("/images/icons/bkgrd.png","Background",SDLK_b,0,0),TM_BG);
605 le_tilemap_panel->additem(new Button("/images/icons/intact.png","Interactive",SDLK_i,0,0),TM_IA);
606 le_tilemap_panel->additem(new Button("/images/icons/frgrd.png","Foreground",SDLK_f,0,0),TM_FG);
607 le_tilemap_panel->highlight_last(true);
613 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
619 void update_level_settings_menu()
624 level_settings_menu->get_item_by_id(MNID_NAME).change_input(le_current_level->name.c_str());
625 level_settings_menu->get_item_by_id(MNID_AUTHOR).change_input(le_current_level->author.c_str());
627 string_list_copy(level_settings_menu->get_item_by_id(MNID_SONG).list, dfiles("music/",NULL, "-fast"));
628 string_list_copy(level_settings_menu->get_item_by_id(MNID_BGIMG).list, dfiles("images/background",NULL, NULL));
629 string_list_add_item(level_settings_menu->get_item_by_id(MNID_BGIMG).list,"");
630 string_list_add_item(level_settings_menu->get_item_by_id(MNID_PARTICLE).list,"");
631 string_list_add_item(level_settings_menu->get_item_by_id(MNID_PARTICLE).list,"snow");
632 string_list_add_item(level_settings_menu->get_item_by_id(MNID_PARTICLE).list,"clouds");
634 if((i = string_list_find(level_settings_menu->get_item_by_id(MNID_SONG).list,le_current_level->song_title.c_str())) != -1)
635 level_settings_menu->get_item_by_id(MNID_SONG).list->active_item = i;
636 if((i = string_list_find(level_settings_menu->get_item_by_id(MNID_BGIMG).list,le_current_level->bkgd_image.c_str())) != -1)
637 level_settings_menu->get_item_by_id(MNID_BGIMG).list->active_item = i;
638 if((i = string_list_find(level_settings_menu->get_item_by_id(MNID_PARTICLE).list,le_current_level->particle_system.c_str())) != -1)
639 level_settings_menu->get_item_by_id(MNID_PARTICLE).list->active_item = i;
641 sprintf(str,"%d",le_current_level->width);
642 level_settings_menu->get_item_by_id(MNID_LENGTH).change_input(str);
643 sprintf(str,"%d",le_current_level->time_left);
644 level_settings_menu->get_item_by_id(MNID_TIME).change_input(str);
645 sprintf(str,"%2.0f",le_current_level->gravity);
646 level_settings_menu->get_item_by_id(MNID_GRAVITY).change_input(str);
647 sprintf(str,"%d",le_current_level->bkgd_speed);
648 level_settings_menu->get_item_by_id(MNID_BGSPEED).change_input(str);
649 sprintf(str,"%d",le_current_level->bkgd_top.red);
650 level_settings_menu->get_item_by_id(MNID_TopRed).change_input(str);
651 sprintf(str,"%d",le_current_level->bkgd_top.green);
652 level_settings_menu->get_item_by_id(MNID_TopGreen).change_input(str);
653 sprintf(str,"%d",le_current_level->bkgd_top.blue);
654 level_settings_menu->get_item_by_id(MNID_TopBlue).change_input(str);
655 sprintf(str,"%d",le_current_level->bkgd_bottom.red);
656 level_settings_menu->get_item_by_id(MNID_BottomRed).change_input(str);
657 sprintf(str,"%d",le_current_level->bkgd_bottom.green);
658 level_settings_menu->get_item_by_id(MNID_BottomGreen).change_input(str);
659 sprintf(str,"%d",le_current_level->bkgd_bottom.blue);
660 level_settings_menu->get_item_by_id(MNID_BottomBlue).change_input(str);
663 void update_subset_settings_menu()
665 subset_settings_menu->item[2].change_input(le_level_subset->title.c_str());
666 subset_settings_menu->item[3].change_input(le_level_subset->description.c_str());
669 void apply_level_settings_menu()
674 le_current_level->name = level_settings_menu->get_item_by_id(MNID_NAME).input;
675 le_current_level->author = level_settings_menu->get_item_by_id(MNID_AUTHOR).input;
677 if(le_current_level->bkgd_image.compare(string_list_active(level_settings_menu->get_item_by_id(MNID_BGIMG).list)) != 0)
679 le_current_level->bkgd_image = string_list_active(level_settings_menu->get_item_by_id(MNID_BGIMG).list);
683 if(le_current_level->particle_system.compare(string_list_active(level_settings_menu->get_item_by_id(MNID_PARTICLE).list)) != 0)
685 le_current_level->particle_system = string_list_active(level_settings_menu->get_item_by_id(MNID_PARTICLE).list);
690 le_current_level->load_gfx();
693 le_current_level->song_title = string_list_active(level_settings_menu->get_item_by_id(MNID_SONG).list);
695 le_current_level->change_size(atoi(level_settings_menu->get_item_by_id(MNID_LENGTH).input));
696 le_current_level->time_left = atoi(level_settings_menu->get_item_by_id(MNID_BGIMG).input);
697 le_current_level->gravity = atof(level_settings_menu->get_item_by_id(MNID_GRAVITY).input);
698 le_current_level->bkgd_speed = atoi(level_settings_menu->get_item_by_id(MNID_BGSPEED).input);
699 le_current_level->bkgd_top.red = atoi(level_settings_menu->get_item_by_id(MNID_TopRed).input);
700 le_current_level->bkgd_top.green = atoi(level_settings_menu->get_item_by_id(MNID_TopGreen).input);
701 le_current_level->bkgd_top.blue = atoi(level_settings_menu->get_item_by_id(MNID_TopBlue).input);
702 le_current_level->bkgd_bottom.red = atoi(level_settings_menu->get_item_by_id(MNID_BottomRed).input);
703 le_current_level->bkgd_bottom.green = atoi(level_settings_menu->get_item_by_id(MNID_BottomGreen).input);
704 le_current_level->bkgd_bottom.blue = atoi(level_settings_menu->get_item_by_id(MNID_BottomBlue).input);
707 void save_subset_settings_menu()
709 le_level_subset->title = subset_settings_menu->item[2].input;
710 le_level_subset->description = subset_settings_menu->item[3].input;
711 le_level_subset->save();
714 void le_goto_level(int levelnb)
716 le_world.arrays_free();
718 le_current_level->cleanup();
719 if(le_current_level->load(le_level_subset->name.c_str(), levelnb) != 0)
721 le_current_level->load(le_level_subset->name.c_str(), le_level);
730 le_current_level->load_gfx();
732 le_world.activate_bad_guys();
737 /*if(level_changed == true)
738 if(askforsaving() == CANCEL)
741 SDL_EnableKeyRepeat(0, 0); // disables key repeating
744 delete leveleditor_menu;
745 delete subset_load_menu;
746 delete subset_new_menu;
747 delete subset_settings_menu;
748 delete level_settings_menu;
749 delete select_tilegroup_menu;
750 delete select_objects_menu;
751 delete le_save_level_bt;
753 delete le_test_level_bt;
754 delete le_next_level_bt;
755 delete le_previous_level_bt;
756 delete le_move_right_bt;
757 delete le_move_left_bt;
759 delete le_select_mode_one_bt;
760 delete le_select_mode_two_bt;
761 delete le_settings_bt;
762 delete le_tilegroup_bt;
763 delete le_objects_bt;
764 delete le_tilemap_panel;
766 delete le_current_level;
767 le_current_level = 0;
768 delete le_level_subset;
771 for(ButtonPanelMap::iterator i = tilegroups_map.begin();
772 i != tilegroups_map.end(); ++i)
776 for(ButtonPanelMap::iterator i = objects_map.begin();
777 i != objects_map.end(); ++i)
783 void le_drawinterface()
788 if(le_current_level != NULL)
790 /* draw a grid (if selected) */
793 for(x = 0; x < 19; x++)
794 fillrect(x*32 - ((int)pos_x % 32), 0, 1, screen->h, 225, 225, 225,255);
795 for(y = 0; y < 15; y++)
796 fillrect(0, y*32, screen->w - 32, 1, 225, 225, 225,255);
800 if(le_selection_mode == CURSOR)
801 le_selection->draw( cursor_x - scroll_x, cursor_y);
802 else if(le_selection_mode == SQUARE)
805 le_highlight_selection();
806 /* draw current selection */
807 w = selection.x2 - selection.x1;
808 h = selection.y2 - selection.y1;
809 fillrect(selection.x1 - pos_x, selection.y1, w, SELECT_W, SELECT_CLR);
810 fillrect(selection.x1 - pos_x + w, selection.y1, SELECT_W, h, SELECT_CLR);
811 fillrect(selection.x1 - pos_x, selection.y1 + h, w, SELECT_W, SELECT_CLR);
812 fillrect(selection.x1 - pos_x, selection.y1, SELECT_W, h, SELECT_CLR);
816 /* draw button bar */
817 fillrect(screen->w - 64, 0, 64, screen->h, 50, 50, 50,255);
819 if(le_current.IsTile())
821 Tile::draw(19 * 32, 14 * 32, le_current.tile);
822 if(TileManager::instance()->get(le_current.tile)->editor_images.size() > 0)
823 TileManager::instance()->get(le_current.tile)->editor_images[0]->draw( 19 * 32, 14 * 32);
825 if(le_current.IsObject())
827 le_current.obj->draw_on_screen(19 * 32, 14 * 32);
830 //if(le_current.IsObject())
833 if(le_current_level != NULL)
835 le_save_level_bt->draw();
837 le_test_level_bt->draw();
838 le_next_level_bt->draw();
839 le_previous_level_bt->draw();
840 le_rubber_bt->draw();
841 if(le_selection_mode == SQUARE)
842 le_select_mode_one_bt->draw();
843 else if(le_selection_mode == CURSOR)
844 le_select_mode_two_bt->draw();
845 le_settings_bt->draw();
846 le_move_right_bt->draw();
847 le_move_left_bt->draw();
848 le_tilegroup_bt->draw();
849 le_objects_bt->draw();
850 if(!cur_tilegroup.empty())
851 tilegroups_map[cur_tilegroup]->draw();
852 else if(!cur_objects.empty())
854 objects_map[cur_objects]->draw();
857 le_tilemap_panel->draw();
859 sprintf(str, "%d/%d", le_level,le_level_subset->levels);
860 white_text->drawf(str, -10, 16, A_RIGHT, A_TOP, 0);
862 white_small_text->draw("F1 for Help", 10, 430, 1);
867 white_small_text->draw("No Level Subset loaded - Press ESC and choose one in the menu", 10, 430, 1);
869 white_small_text->draw("No Level Subset loaded", 10, 430, 1);
876 unsigned int y,x,i,s;
879 /* Draw the real background */
880 if(le_current_level->bkgd_image[0] != '\0')
882 s = (int)((float)pos_x * ((float)le_current_level->bkgd_speed/60.)) % screen->w;
883 le_current_level->img_bkgd->draw_part(s,0,0,0,
884 le_current_level->img_bkgd->w - s - 32, le_current_level->img_bkgd->h);
885 le_current_level->img_bkgd->draw_part(0,0,screen->w - s - 32 ,0,s,
886 le_current_level->img_bkgd->h);
890 drawgradient(le_current_level->bkgd_top, le_current_level->bkgd_bottom);
893 if(le_current.IsTile())
895 Tile::draw(cursor_x, cursor_y,le_current.tile,128);
896 if(!TileManager::instance()->get(le_current.tile)->images.empty())
897 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);
899 if(le_current.IsObject())
901 le_current.obj->move_to(cursor_x, cursor_y);
904 /* clearscreen(current_level.bkgd_red, current_level.bkgd_green, current_level.bkgd_blue); */
906 for (y = 0; y < 15; ++y)
907 for (x = 0; x < 20; ++x)
910 if(active_tm == TM_BG)
915 Tile::draw(32*x - fmodf(pos_x, 32), y * 32, le_current_level->bg_tiles[y][x + (int)(pos_x / 32)],a);
917 if(active_tm == TM_IA)
922 Tile::draw(32*x - fmodf(pos_x, 32), y * 32, le_current_level->ia_tiles[y][x + (int)(pos_x / 32)],a);
924 if(active_tm == TM_FG)
929 Tile::draw(32*x - fmodf(pos_x, 32), y * 32, le_current_level->fg_tiles[y][x + (int)(pos_x / 32)],a);
931 /* draw whats inside stuff when cursor is selecting those */
932 /* (draw them all the time - is this the right behaviour?) */
933 if(TileManager::instance()->get(le_current_level->ia_tiles[y][x + (int)(pos_x / 32)])->editor_images.size() > 0)
934 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);
938 /* Draw the Bad guys: */
939 for (i = 0; i < le_world.bad_guys.size(); ++i)
941 /* to support frames: img_bsod_left[(frame / 5) % 4] */
944 le_world.bad_guys[i].draw();
948 /* Draw the player: */
949 /* for now, the position is fixed at (100, 240) */
950 largetux.walk_right->draw( 100 - pos_x, 240);
953 void le_checkevents()
960 keymod = SDL_GetModState();
962 while(SDL_PollEvent(&event))
966 Menu::current()->event(event);
970 mouse_cursor->set_state(MC_NORMAL);
972 /* testing SDL_KEYDOWN, SDL_KEYUP and SDL_QUIT events*/
973 if(event.type == SDL_KEYDOWN
974 || ((event.type == SDL_MOUSEBUTTONDOWN || SDL_MOUSEMOTION)
975 && (event.motion.x > 0
976 && event.motion.x < screen->w - 64 &&
977 event.motion.y > 0 && event.motion.y < screen->h)))
981 case SDL_KEYDOWN: // key pressed
982 key = event.key.keysym.sym;
986 Menu::set_current(leveleditor_menu);
989 cursor_x -= KEY_CURSOR_SPEED;
991 cursor_x -= KEY_CURSOR_FASTSPEED;
993 if(cursor_x < pos_x + MOUSE_LEFT_MARGIN)
994 pos_x = cursor_x - MOUSE_LEFT_MARGIN;
999 cursor_x += KEY_CURSOR_SPEED;
1001 cursor_x += KEY_CURSOR_FASTSPEED;
1003 if(cursor_x > pos_x + MOUSE_RIGHT_MARGIN-32)
1004 pos_x = cursor_x - MOUSE_RIGHT_MARGIN+32;
1009 cursor_y -= KEY_CURSOR_SPEED;
1011 cursor_y -= KEY_CURSOR_FASTSPEED;
1018 cursor_y += KEY_CURSOR_SPEED;
1020 cursor_y += KEY_CURSOR_FASTSPEED;
1022 if(cursor_y > screen->h-32)
1023 cursor_y = screen->h-32;
1036 cursor_x = (le_current_level->width * 32) - 32;
1040 le_show_grid = !le_show_grid;
1046 case SDL_KEYUP: /* key released */
1047 switch(event.key.keysym.sym)
1056 case SDL_MOUSEBUTTONDOWN:
1057 if(event.button.button == SDL_BUTTON_LEFT)
1059 le_mouse_pressed[LEFT] = true;
1061 selection.x1 = event.motion.x + pos_x;
1062 selection.y1 = event.motion.y;
1063 selection.x2 = event.motion.x + pos_x;
1064 selection.y2 = event.motion.y;
1066 else if(event.button.button == SDL_BUTTON_RIGHT)
1068 le_mouse_pressed[RIGHT] = true;
1071 case SDL_MOUSEBUTTONUP:
1072 if(event.button.button == SDL_BUTTON_LEFT)
1074 le_mouse_pressed[LEFT] = false;
1075 le_mouse_clicked[LEFT] = true;
1077 else if(event.button.button == SDL_BUTTON_RIGHT)
1079 le_mouse_pressed[RIGHT] = false;
1080 le_mouse_clicked[RIGHT] = true;
1083 case SDL_MOUSEMOTION:
1085 if(!Menu::current())
1090 if(le_current.IsTile())
1092 cursor_x = ((int)(pos_x + x) / 32) * 32;
1093 cursor_y = ((int) y / 32) * 32;
1101 if(le_mouse_pressed[LEFT])
1103 selection.x2 = x + pos_x;
1107 if(le_mouse_pressed[RIGHT])
1109 pos_x += -1 * event.motion.xrel;
1113 case SDL_QUIT: // window closed
1122 if(le_current_level != NULL)
1124 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 &&
1125 event.motion.y > 0 && event.motion.y < screen->h)))
1127 le_mouse_pressed[LEFT] = false;
1128 le_mouse_pressed[RIGHT] = false;
1130 if(!Menu::current())
1132 /* Check for button events */
1133 le_test_level_bt->event(event);
1134 if(le_test_level_bt->get_state() == BUTTON_CLICKED)
1136 le_save_level_bt->event(event);
1137 if(le_save_level_bt->get_state() == BUTTON_CLICKED)
1138 le_current_level->save(le_level_subset->name.c_str(),le_level);
1139 le_exit_bt->event(event);
1140 if(le_exit_bt->get_state() == BUTTON_CLICKED)
1142 Menu::set_current(leveleditor_menu);
1144 le_next_level_bt->event(event);
1145 if(le_next_level_bt->get_state() == BUTTON_CLICKED)
1147 if(le_level < le_level_subset->levels)
1149 le_goto_level(++le_level);
1155 sprintf(str,"Level %d doesn't exist. Create it?",le_level+1);
1156 if(confirm_dialog(str))
1158 new_lev.init_defaults();
1159 new_lev.save(le_level_subset->name.c_str(),++le_level);
1160 le_level_subset->levels = le_level;
1161 le_goto_level(le_level);
1165 le_previous_level_bt->event(event);
1166 if(le_previous_level_bt->get_state() == BUTTON_CLICKED)
1169 le_goto_level(--le_level);
1171 le_rubber_bt->event(event);
1172 if(le_rubber_bt->get_state() == BUTTON_CLICKED)
1175 if(le_selection_mode == SQUARE)
1177 le_select_mode_one_bt->event(event);
1178 if(le_select_mode_one_bt->get_state() == BUTTON_CLICKED)
1179 le_selection_mode = CURSOR;
1183 le_select_mode_two_bt->event(event);
1184 if(le_select_mode_two_bt->get_state() == BUTTON_CLICKED)
1185 le_selection_mode = SQUARE;
1187 ButtonPanelMap::iterator it;
1188 le_tilegroup_bt->event(event);
1189 switch (le_tilegroup_bt->get_state())
1191 case BUTTON_CLICKED:
1192 Menu::set_current(select_tilegroup_menu);
1193 select_tilegroup_menu_effect.start(200);
1194 select_tilegroup_menu->set_pos(screen->w - 64,100,-0.5,0.5);
1196 case BUTTON_WHEELUP:
1197 it = tilegroups_map.find(cur_tilegroup);
1198 if(it == tilegroups_map.end())
1200 cur_tilegroup = tilegroups_map.begin()->first;
1201 cur_objects.clear();
1204 if(++it != tilegroups_map.end())
1205 cur_tilegroup = (*it).first;
1207 cur_tilegroup = tilegroups_map.begin()->first;
1209 cur_objects.clear();
1211 case BUTTON_WHEELDOWN:
1212 it = tilegroups_map.find(cur_tilegroup);
1213 if(it == tilegroups_map.begin())
1215 cur_tilegroup = tilegroups_map.rbegin()->first;
1216 cur_objects.clear();
1219 if(--it != --tilegroups_map.begin())
1220 cur_tilegroup = (*it).first;
1222 cur_tilegroup = tilegroups_map.rbegin()->first;
1224 cur_objects.clear();
1230 le_objects_bt->event(event);
1231 switch (le_objects_bt->get_state())
1233 case BUTTON_CLICKED:
1234 Menu::set_current(select_objects_menu);
1235 select_objects_menu_effect.start(200);
1236 select_objects_menu->set_pos(screen->w - 64,100,-0.5,0.5);
1238 case BUTTON_WHEELUP:
1239 it = objects_map.find(cur_objects);
1240 if(it == objects_map.end())
1242 cur_objects = objects_map.begin()->first;
1243 cur_tilegroup.clear();
1246 if(++it != objects_map.end())
1247 cur_objects = (*it).first;
1249 cur_objects = objects_map.begin()->first;
1251 cur_tilegroup.clear();
1253 case BUTTON_WHEELDOWN:
1254 it = objects_map.find(cur_objects);
1255 if(it == objects_map.begin())
1257 cur_objects = objects_map.rbegin()->first;
1258 cur_tilegroup.clear();
1261 if(--it != --objects_map.begin())
1262 cur_objects = (*it).first;
1264 cur_objects = objects_map.rbegin()->first;
1266 cur_tilegroup.clear();
1273 le_settings_bt->event(event);
1274 if(le_settings_bt->get_state() == BUTTON_CLICKED)
1276 update_level_settings_menu();
1277 Menu::set_current(level_settings_menu);
1279 if(!cur_tilegroup.empty())
1281 if((pbutton = tilegroups_map[cur_tilegroup]->event(event)) != NULL)
1283 if(pbutton->get_state() == BUTTON_CLICKED)
1285 if(le_current.IsObject())
1286 le_current.obj->move_to(pbutton->get_pos().x,pbutton->get_pos().y);
1287 le_current.Tile(pbutton->get_tag());
1291 else if(!cur_objects.empty())
1293 if((pbutton = objects_map[cur_objects]->event(event)) != NULL)
1295 if(pbutton->get_state() == BUTTON_CLICKED)
1297 if(le_current.IsObject())
1298 le_current.obj->move_to(pbutton->get_pos().x,pbutton->get_pos().y);
1299 le_current.Object(pbutton->get_game_object());
1304 if((pbutton = le_tilemap_panel->event(event)) != NULL)
1306 if(pbutton->get_state() == BUTTON_CLICKED)
1308 active_tm = static_cast<TileMapType>(pbutton->get_tag());
1314 le_settings_bt->event(event);
1315 if(le_settings_bt->get_state() == BUTTON_CLICKED)
1317 Menu::set_current(0);
1319 le_tilegroup_bt->event(event);
1320 if(le_tilegroup_bt->get_state() == BUTTON_CLICKED)
1322 Menu::set_current(0);
1327 if(!Menu::current())
1329 le_move_left_bt->event(event);
1330 le_move_right_bt->event(event);
1332 if(le_mouse_pressed[LEFT])
1334 if(le_current.IsTile())
1335 le_change(cursor_x, cursor_y, active_tm, le_current.tile);
1337 else if(le_mouse_clicked[LEFT])
1339 if(le_current.IsObject())
1341 std::string type = le_current.obj->type();
1342 if(type == "BadGuy")
1344 BadGuy* pbadguy = dynamic_cast<BadGuy*>(le_current.obj);
1346 le_world.bad_guys.push_back(BadGuy(cursor_x+scroll_x, cursor_y,pbadguy->kind,false));
1347 le_current_level->badguy_data.push_back(&le_world.bad_guys.back());
1350 le_mouse_clicked[LEFT] = false;
1355 if(!Menu::current())
1357 if(le_move_left_bt->get_state() == BUTTON_PRESSED)
1361 else if(le_move_left_bt->get_state() == BUTTON_HOVER)
1366 if(le_move_right_bt->get_state() == BUTTON_PRESSED)
1370 else if(le_move_right_bt->get_state() == BUTTON_HOVER)
1378 void le_highlight_selection()
1382 if(selection.x1 < selection.x2)
1392 if(selection.y1 < selection.y2)
1408 fillrect(x1*32-pos_x, y1*32,32* (x2 - x1 + 1),32 * (y2 - y1 + 1),173,234,177,103);
1411 void le_change(float x, float y, int tm, unsigned int c)
1413 if(le_current_level != NULL)
1419 /* level_changed = true; */
1421 switch(le_selection_mode)
1424 le_current_level->change(x,y,tm,c);
1426 base_type cursor_base;
1429 cursor_base.width = 32;
1430 cursor_base.height = 32;
1432 /* if there is a bad guy over there, remove it */
1433 for(i = 0; i < le_world.bad_guys.size(); ++i)
1434 if(rectcollision(cursor_base,le_world.bad_guys[i].base))
1436 le_world.bad_guys.erase(le_world.bad_guys.begin() + i);
1437 le_current_level->badguy_data.erase(le_current_level->badguy_data.begin() + i);
1442 if(selection.x1 < selection.x2)
1452 if(selection.y1 < selection.y2)
1468 /* if there is a bad guy over there, remove it */
1469 for(std::vector<BadGuy>::iterator i = le_world.bad_guys.begin();
1470 i != le_world.bad_guys.end(); /* will be at end of loop */)
1472 if(i->base.x/32 >= x1 && i->base.x/32 <= x2
1473 && i->base.y/32 >= y1 && i->base.y/32 <= y2)
1475 i = le_world.bad_guys.erase(i);
1484 for(xx = x1; xx <= x2; xx++)
1485 for(yy = y1; yy <= y2; yy++)
1487 le_current_level->change(xx*32, yy*32, tm, c);
1499 le_current_level->save("test", le_level);
1501 GameSession session("test",le_level, ST_GL_TEST);
1503 player_status.reset();
1505 music_manager->halt_music();
1507 Menu::set_current(NULL);
1508 le_world.arrays_free();
1509 le_current_level->load_gfx();
1510 le_world.activate_bad_guys();
1516 unsigned int i, done_;
1518 " - This is SuperTux's built-in level editor -",
1519 "It has been designed to be light and easy to use from the start.",
1521 "When you first load the level editor you are given a menu where you",
1522 "can load level subsets, create a new level subset, edit the current",
1523 "subset's settings, or simply quit the editor. You can access this menu",
1524 "from the level editor at any time by pressing the escape key.",
1526 "To your right is your button bar. The center of this contains many",
1527 "tiles you can use to make your level. To select a tile, click on it",
1528 "with your left mouse button; your selection will be shown in the",
1529 "bottom right corner of the button box. Click anywhere on your level",
1530 "with the left mouse button to place that tile down. If you right click",
1531 "a tile in the button bar, you can find out what its keyboard shortcut",
1532 "is. The three buttons FGD, BGD and EMY let you pick from foreground,",
1533 "background, and enemy tiles. The eraser lets you remove tiles.",
1534 "The left and right arrow keys scroll back and forth through your level.",
1535 "The button with the wrench and screwdriver, lets you change the",
1536 "settings of your level, including how long it is or what music it will",
1537 "play. When you are ready to give your level a test, click on the little",
1538 "running Tux. If you like the changes you have made to your level,",
1539 "press the red save key to keep them.",
1540 "To change which level in your subset you are editing, press the white",
1541 "up and down arrow keys at the top of the button box.",
1543 "Have fun making levels! If you make some good ones, send them to us on",
1544 "the SuperTux mailing list!",
1549 blue_text->drawf("- Help -", 0, 30, A_HMIDDLE, A_TOP, 2);
1551 for(i = 0; i < sizeof(text)/sizeof(char *); i++)
1552 white_small_text->draw(text[i], 5, 80+(i*white_small_text->h), 1);
1554 gold_text->drawf("Press Any Key to Continue", 0, 440, A_HMIDDLE, A_TOP, 1);
1562 done_ = wait_for_event(event);