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);
565 level_subsets = dsubdirs("/levels", "info");
566 le_level_subset = new LevelSubset;
574 le_frame = 0; /* support for frames in some tiles, like waves and bad guys */
575 le_level_changed = false;
576 le_current_level = NULL;
578 le_mouse_pressed[LEFT] = false;
579 le_mouse_pressed[RIGHT] = false;
581 le_mouse_clicked[LEFT] = false;
582 le_mouse_clicked[RIGHT] = false;
584 le_selection = new Surface(datadir + "/images/leveleditor/select.png", USE_ALPHA);
586 select_tilegroup_menu_effect.init(false);
587 select_objects_menu_effect.init(false);
590 le_save_level_bt = new Button("/images/icons/save.png","Save level", SDLK_F6,screen->w-64,32);
591 le_exit_bt = new Button("/images/icons/exit.png","Exit", SDLK_F6,screen->w-32,32);
592 le_next_level_bt = new Button("/images/icons/up.png","Next level", SDLK_PAGEUP,screen->w-64,0);
593 le_previous_level_bt = new Button("/images/icons/down.png","Previous level",SDLK_PAGEDOWN,screen->w-32,0);
594 le_rubber_bt = new Button("/images/icons/rubber.png","Rubber",SDLK_DELETE,screen->w-32,48);
595 le_select_mode_one_bt = new Button ("/images/icons/select-mode1.png","Select single tile",SDLK_F3,screen->w-64,48);
596 le_select_mode_two_bt = new Button("/images/icons/select-mode2.png","Select multiple tiles",SDLK_F3,screen->w-64,48);
597 le_test_level_bt = new Button("/images/icons/test-level.png","Test level",SDLK_F4,screen->w-64,screen->h - 64);
598 le_settings_bt = new Button("/images/icons/settings.png","Level settings",SDLK_F5,screen->w-32,screen->h - 64);
599 le_move_left_bt = new Button("/images/icons/left.png","Move left",SDLK_LEFT,0,0);
600 le_move_right_bt = new Button("/images/icons/right.png","Move right",SDLK_RIGHT,screen->w-80,0);
601 le_tilegroup_bt = new Button("/images/icons/tilegroup.png","Select Tilegroup", SDLK_F7,screen->w-64,64);
602 le_objects_bt = new Button("/images/icons/objects.png","Select Objects", SDLK_F7,screen->w-64,80);
604 le_tilemap_panel = new ButtonPanel(screen->w-64,screen->h-32,32,32);
605 le_tilemap_panel->set_button_size(32,10);
606 le_tilemap_panel->additem(new Button("/images/icons/bkgrd.png","Background",SDLK_b,0,0),TM_BG);
607 le_tilemap_panel->additem(new Button("/images/icons/intact.png","Interactive",SDLK_i,0,0),TM_IA);
608 le_tilemap_panel->additem(new Button("/images/icons/frgrd.png","Foreground",SDLK_f,0,0),TM_FG);
609 le_tilemap_panel->highlight_last(true);
615 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
621 void update_level_settings_menu()
626 level_settings_menu->get_item_by_id(MNID_NAME).change_input(le_current_level->name.c_str());
627 level_settings_menu->get_item_by_id(MNID_AUTHOR).change_input(le_current_level->author.c_str());
629 string_list_copy(level_settings_menu->get_item_by_id(MNID_SONG).list, dfiles("music/",NULL, "-fast"));
630 string_list_copy(level_settings_menu->get_item_by_id(MNID_BGIMG).list, dfiles("images/background",NULL, NULL));
631 string_list_add_item(level_settings_menu->get_item_by_id(MNID_BGIMG).list,"");
632 string_list_add_item(level_settings_menu->get_item_by_id(MNID_PARTICLE).list,"");
633 string_list_add_item(level_settings_menu->get_item_by_id(MNID_PARTICLE).list,"snow");
634 string_list_add_item(level_settings_menu->get_item_by_id(MNID_PARTICLE).list,"clouds");
636 if((i = string_list_find(level_settings_menu->get_item_by_id(MNID_SONG).list,le_current_level->song_title.c_str())) != -1)
637 level_settings_menu->get_item_by_id(MNID_SONG).list->active_item = i;
638 if((i = string_list_find(level_settings_menu->get_item_by_id(MNID_BGIMG).list,le_current_level->bkgd_image.c_str())) != -1)
639 level_settings_menu->get_item_by_id(MNID_BGIMG).list->active_item = i;
640 if((i = string_list_find(level_settings_menu->get_item_by_id(MNID_PARTICLE).list,le_current_level->particle_system.c_str())) != -1)
641 level_settings_menu->get_item_by_id(MNID_PARTICLE).list->active_item = i;
643 sprintf(str,"%d",le_current_level->width);
644 level_settings_menu->get_item_by_id(MNID_LENGTH).change_input(str);
645 sprintf(str,"%d",le_current_level->time_left);
646 level_settings_menu->get_item_by_id(MNID_TIME).change_input(str);
647 sprintf(str,"%2.0f",le_current_level->gravity);
648 level_settings_menu->get_item_by_id(MNID_GRAVITY).change_input(str);
649 sprintf(str,"%d",le_current_level->bkgd_speed);
650 level_settings_menu->get_item_by_id(MNID_BGSPEED).change_input(str);
651 sprintf(str,"%d",le_current_level->bkgd_top.red);
652 level_settings_menu->get_item_by_id(MNID_TopRed).change_input(str);
653 sprintf(str,"%d",le_current_level->bkgd_top.green);
654 level_settings_menu->get_item_by_id(MNID_TopGreen).change_input(str);
655 sprintf(str,"%d",le_current_level->bkgd_top.blue);
656 level_settings_menu->get_item_by_id(MNID_TopBlue).change_input(str);
657 sprintf(str,"%d",le_current_level->bkgd_bottom.red);
658 level_settings_menu->get_item_by_id(MNID_BottomRed).change_input(str);
659 sprintf(str,"%d",le_current_level->bkgd_bottom.green);
660 level_settings_menu->get_item_by_id(MNID_BottomGreen).change_input(str);
661 sprintf(str,"%d",le_current_level->bkgd_bottom.blue);
662 level_settings_menu->get_item_by_id(MNID_BottomBlue).change_input(str);
665 void update_subset_settings_menu()
667 subset_settings_menu->item[2].change_input(le_level_subset->title.c_str());
668 subset_settings_menu->item[3].change_input(le_level_subset->description.c_str());
671 void apply_level_settings_menu()
676 le_current_level->name = level_settings_menu->get_item_by_id(MNID_NAME).input;
677 le_current_level->author = level_settings_menu->get_item_by_id(MNID_AUTHOR).input;
679 if(le_current_level->bkgd_image.compare(string_list_active(level_settings_menu->get_item_by_id(MNID_BGIMG).list)) != 0)
681 le_current_level->bkgd_image = string_list_active(level_settings_menu->get_item_by_id(MNID_BGIMG).list);
685 if(le_current_level->particle_system.compare(string_list_active(level_settings_menu->get_item_by_id(MNID_PARTICLE).list)) != 0)
687 le_current_level->particle_system = string_list_active(level_settings_menu->get_item_by_id(MNID_PARTICLE).list);
692 le_current_level->load_gfx();
695 le_current_level->song_title = string_list_active(level_settings_menu->get_item_by_id(MNID_SONG).list);
697 le_current_level->change_size(atoi(level_settings_menu->get_item_by_id(MNID_LENGTH).input));
698 le_current_level->time_left = atoi(level_settings_menu->get_item_by_id(MNID_BGIMG).input);
699 le_current_level->gravity = atof(level_settings_menu->get_item_by_id(MNID_GRAVITY).input);
700 le_current_level->bkgd_speed = atoi(level_settings_menu->get_item_by_id(MNID_BGSPEED).input);
701 le_current_level->bkgd_top.red = atoi(level_settings_menu->get_item_by_id(MNID_TopRed).input);
702 le_current_level->bkgd_top.green = atoi(level_settings_menu->get_item_by_id(MNID_TopGreen).input);
703 le_current_level->bkgd_top.blue = atoi(level_settings_menu->get_item_by_id(MNID_TopBlue).input);
704 le_current_level->bkgd_bottom.red = atoi(level_settings_menu->get_item_by_id(MNID_BottomRed).input);
705 le_current_level->bkgd_bottom.green = atoi(level_settings_menu->get_item_by_id(MNID_BottomGreen).input);
706 le_current_level->bkgd_bottom.blue = atoi(level_settings_menu->get_item_by_id(MNID_BottomBlue).input);
709 void save_subset_settings_menu()
711 le_level_subset->title = subset_settings_menu->item[2].input;
712 le_level_subset->description = subset_settings_menu->item[3].input;
713 le_level_subset->save();
716 void le_goto_level(int levelnb)
718 le_world.arrays_free();
720 le_current_level->cleanup();
721 if(le_current_level->load(le_level_subset->name.c_str(), levelnb) != 0)
723 le_current_level->load(le_level_subset->name.c_str(), le_level);
732 le_current_level->load_gfx();
734 le_world.activate_bad_guys();
739 /*if(level_changed == true)
740 if(askforsaving() == CANCEL)
743 SDL_EnableKeyRepeat(0, 0); // disables key repeating
746 delete leveleditor_menu;
747 delete subset_load_menu;
748 delete subset_new_menu;
749 delete subset_settings_menu;
750 delete level_settings_menu;
751 delete select_tilegroup_menu;
752 delete select_objects_menu;
753 delete le_save_level_bt;
755 delete le_test_level_bt;
756 delete le_next_level_bt;
757 delete le_previous_level_bt;
758 delete le_move_right_bt;
759 delete le_move_left_bt;
761 delete le_select_mode_one_bt;
762 delete le_select_mode_two_bt;
763 delete le_settings_bt;
764 delete le_tilegroup_bt;
765 delete le_objects_bt;
766 delete le_tilemap_panel;
768 delete le_current_level;
769 le_current_level = 0;
770 delete le_level_subset;
773 for(ButtonPanelMap::iterator i = tilegroups_map.begin();
774 i != tilegroups_map.end(); ++i)
778 for(ButtonPanelMap::iterator i = objects_map.begin();
779 i != objects_map.end(); ++i)
785 void le_drawinterface()
790 if(le_current_level != NULL)
792 /* draw a grid (if selected) */
795 for(x = 0; x < 19; x++)
796 fillrect(x*32 - ((int)pos_x % 32), 0, 1, screen->h, 225, 225, 225,255);
797 for(y = 0; y < 15; y++)
798 fillrect(0, y*32, screen->w - 32, 1, 225, 225, 225,255);
802 if(le_selection_mode == CURSOR)
803 if(le_current.IsTile())
804 le_selection->draw( cursor_x - pos_x, cursor_y);
806 le_selection->draw( cursor_x, cursor_y);
807 else if(le_selection_mode == SQUARE)
810 le_highlight_selection();
811 /* draw current selection */
812 w = selection.x2 - selection.x1;
813 h = selection.y2 - selection.y1;
814 fillrect(selection.x1 - pos_x, selection.y1, w, SELECT_W, SELECT_CLR);
815 fillrect(selection.x1 - pos_x + w, selection.y1, SELECT_W, h, SELECT_CLR);
816 fillrect(selection.x1 - pos_x, selection.y1 + h, w, SELECT_W, SELECT_CLR);
817 fillrect(selection.x1 - pos_x, selection.y1, SELECT_W, h, SELECT_CLR);
821 /* draw button bar */
822 fillrect(screen->w - 64, 0, 64, screen->h, 50, 50, 50,255);
824 if(le_current.IsTile())
826 Tile::draw(19 * 32, 14 * 32, le_current.tile);
827 if(TileManager::instance()->get(le_current.tile)->editor_images.size() > 0)
828 TileManager::instance()->get(le_current.tile)->editor_images[0]->draw( 19 * 32, 14 * 32);
830 if(le_current.IsObject())
832 le_current.obj->draw_on_screen(19 * 32, 14 * 32);
833 le_current.obj->draw_on_screen(cursor_x,cursor_y);
836 if(le_current_level != NULL)
838 le_save_level_bt->draw();
840 le_test_level_bt->draw();
841 le_next_level_bt->draw();
842 le_previous_level_bt->draw();
843 le_rubber_bt->draw();
844 if(le_selection_mode == SQUARE)
845 le_select_mode_one_bt->draw();
846 else if(le_selection_mode == CURSOR)
847 le_select_mode_two_bt->draw();
848 le_settings_bt->draw();
849 le_move_right_bt->draw();
850 le_move_left_bt->draw();
851 le_tilegroup_bt->draw();
852 le_objects_bt->draw();
853 if(!cur_tilegroup.empty())
854 tilegroups_map[cur_tilegroup]->draw();
855 else if(!cur_objects.empty())
857 objects_map[cur_objects]->draw();
860 le_tilemap_panel->draw();
862 sprintf(str, "%d/%d", le_level,le_level_subset->levels);
863 white_text->drawf(str, -10, 16, A_RIGHT, A_TOP, 0);
865 white_small_text->draw("F1 for Help", 10, 430, 1);
870 white_small_text->draw("No Level Subset loaded - Press ESC and choose one in the menu", 10, 430, 1);
872 white_small_text->draw("No Level Subset loaded", 10, 430, 1);
879 unsigned int y,x,i,s;
882 /* Draw the real background */
883 if(le_current_level->bkgd_image[0] != '\0')
885 s = (int)((float)pos_x * ((float)le_current_level->bkgd_speed/60.)) % screen->w;
886 le_current_level->img_bkgd->draw_part(s,0,0,0,
887 le_current_level->img_bkgd->w - s - 32, le_current_level->img_bkgd->h);
888 le_current_level->img_bkgd->draw_part(0,0,screen->w - s - 32 ,0,s,
889 le_current_level->img_bkgd->h);
893 drawgradient(le_current_level->bkgd_top, le_current_level->bkgd_bottom);
896 if(le_current.IsTile())
898 Tile::draw(cursor_x-pos_x, cursor_y,le_current.tile,128);
899 if(!TileManager::instance()->get(le_current.tile)->images.empty())
900 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);
902 if(le_current.IsObject())
904 le_current.obj->move_to(cursor_x, cursor_y);
907 /* clearscreen(current_level.bkgd_red, current_level.bkgd_green, current_level.bkgd_blue); */
909 for (y = 0; y < 15; ++y)
910 for (x = 0; x < 20; ++x)
913 if(active_tm == TM_BG)
918 Tile::draw(32*x - fmodf(pos_x, 32), y * 32, le_current_level->bg_tiles[y][x + (int)(pos_x / 32)],a);
920 if(active_tm == TM_IA)
925 Tile::draw(32*x - fmodf(pos_x, 32), y * 32, le_current_level->ia_tiles[y][x + (int)(pos_x / 32)],a);
927 if(active_tm == TM_FG)
932 Tile::draw(32*x - fmodf(pos_x, 32), y * 32, le_current_level->fg_tiles[y][x + (int)(pos_x / 32)],a);
934 /* draw whats inside stuff when cursor is selecting those */
935 /* (draw them all the time - is this the right behaviour?) */
936 if(TileManager::instance()->get(le_current_level->ia_tiles[y][x + (int)(pos_x / 32)])->editor_images.size() > 0)
937 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);
941 /* Draw the Bad guys: */
942 for (i = 0; i < le_world.bad_guys.size(); ++i)
944 /* to support frames: img_bsod_left[(frame / 5) % 4] */
947 le_world.bad_guys[i].draw();
951 /* Draw the player: */
952 /* for now, the position is fixed at (100, 240) */
953 largetux.walk_right->draw( 100 - pos_x, 240);
956 void le_checkevents()
963 keymod = SDL_GetModState();
965 while(SDL_PollEvent(&event))
969 Menu::current()->event(event);
973 mouse_cursor->set_state(MC_NORMAL);
975 /* testing SDL_KEYDOWN, SDL_KEYUP and SDL_QUIT events*/
976 if(event.type == SDL_KEYDOWN
977 || ((event.type == SDL_MOUSEBUTTONDOWN || SDL_MOUSEMOTION)
978 && (event.motion.x > 0
979 && event.motion.x < screen->w - 64 &&
980 event.motion.y > 0 && event.motion.y < screen->h)))
984 case SDL_KEYDOWN: // key pressed
985 key = event.key.keysym.sym;
989 Menu::set_current(leveleditor_menu);
992 cursor_x -= KEY_CURSOR_SPEED;
994 cursor_x -= KEY_CURSOR_FASTSPEED;
996 if(cursor_x < pos_x + MOUSE_LEFT_MARGIN)
997 pos_x = cursor_x - MOUSE_LEFT_MARGIN;
1002 cursor_x += KEY_CURSOR_SPEED;
1004 cursor_x += KEY_CURSOR_FASTSPEED;
1006 if(cursor_x > pos_x + MOUSE_RIGHT_MARGIN-32)
1007 pos_x = cursor_x - MOUSE_RIGHT_MARGIN+32;
1012 cursor_y -= KEY_CURSOR_SPEED;
1014 cursor_y -= KEY_CURSOR_FASTSPEED;
1021 cursor_y += KEY_CURSOR_SPEED;
1023 cursor_y += KEY_CURSOR_FASTSPEED;
1025 if(cursor_y > screen->h-32)
1026 cursor_y = screen->h-32;
1039 cursor_x = (le_current_level->width * 32) - 32;
1043 le_show_grid = !le_show_grid;
1049 case SDL_KEYUP: /* key released */
1050 switch(event.key.keysym.sym)
1059 case SDL_MOUSEBUTTONDOWN:
1060 if(event.button.button == SDL_BUTTON_LEFT)
1062 le_mouse_pressed[LEFT] = true;
1064 selection.x1 = event.motion.x + pos_x;
1065 selection.y1 = event.motion.y;
1066 selection.x2 = event.motion.x + pos_x;
1067 selection.y2 = event.motion.y;
1069 else if(event.button.button == SDL_BUTTON_RIGHT)
1071 le_mouse_pressed[RIGHT] = true;
1074 case SDL_MOUSEBUTTONUP:
1075 if(event.button.button == SDL_BUTTON_LEFT)
1077 le_mouse_pressed[LEFT] = false;
1078 le_mouse_clicked[LEFT] = true;
1080 else if(event.button.button == SDL_BUTTON_RIGHT)
1082 le_mouse_pressed[RIGHT] = false;
1083 le_mouse_clicked[RIGHT] = true;
1086 case SDL_MOUSEMOTION:
1088 if(!Menu::current())
1093 if(le_current.IsTile())
1095 cursor_x = ((int)(pos_x + x) / 32) * 32;
1096 cursor_y = ((int) y / 32) * 32;
1104 if(le_mouse_pressed[LEFT])
1106 selection.x2 = x + pos_x;
1110 if(le_mouse_pressed[RIGHT])
1112 pos_x += -1 * event.motion.xrel;
1116 case SDL_QUIT: // window closed
1125 if(le_current_level != NULL)
1127 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 &&
1128 event.motion.y > 0 && event.motion.y < screen->h)))
1130 le_mouse_pressed[LEFT] = false;
1131 le_mouse_pressed[RIGHT] = false;
1133 if(!Menu::current())
1135 /* Check for button events */
1136 le_test_level_bt->event(event);
1137 if(le_test_level_bt->get_state() == BUTTON_CLICKED)
1139 le_save_level_bt->event(event);
1140 if(le_save_level_bt->get_state() == BUTTON_CLICKED)
1141 le_current_level->save(le_level_subset->name.c_str(),le_level);
1142 le_exit_bt->event(event);
1143 if(le_exit_bt->get_state() == BUTTON_CLICKED)
1145 Menu::set_current(leveleditor_menu);
1147 le_next_level_bt->event(event);
1148 if(le_next_level_bt->get_state() == BUTTON_CLICKED)
1150 if(le_level < le_level_subset->levels)
1152 le_goto_level(++le_level);
1158 sprintf(str,"Level %d doesn't exist. Create it?",le_level+1);
1159 if(confirm_dialog(str))
1161 new_lev.init_defaults();
1162 new_lev.save(le_level_subset->name.c_str(),++le_level);
1163 le_level_subset->levels = le_level;
1164 le_goto_level(le_level);
1168 le_previous_level_bt->event(event);
1169 if(le_previous_level_bt->get_state() == BUTTON_CLICKED)
1172 le_goto_level(--le_level);
1174 le_rubber_bt->event(event);
1175 if(le_rubber_bt->get_state() == BUTTON_CLICKED)
1178 if(le_selection_mode == SQUARE)
1180 le_select_mode_one_bt->event(event);
1181 if(le_select_mode_one_bt->get_state() == BUTTON_CLICKED)
1182 le_selection_mode = CURSOR;
1186 le_select_mode_two_bt->event(event);
1187 if(le_select_mode_two_bt->get_state() == BUTTON_CLICKED)
1188 le_selection_mode = SQUARE;
1190 ButtonPanelMap::iterator it;
1191 le_tilegroup_bt->event(event);
1192 switch (le_tilegroup_bt->get_state())
1194 case BUTTON_CLICKED:
1195 Menu::set_current(select_tilegroup_menu);
1196 select_tilegroup_menu_effect.start(200);
1197 select_tilegroup_menu->set_pos(screen->w - 64,100,-0.5,0.5);
1199 case BUTTON_WHEELUP:
1200 it = tilegroups_map.find(cur_tilegroup);
1201 if(it == tilegroups_map.end())
1203 cur_tilegroup = tilegroups_map.begin()->first;
1204 cur_objects.clear();
1207 if(++it != tilegroups_map.end())
1208 cur_tilegroup = (*it).first;
1210 cur_tilegroup = tilegroups_map.begin()->first;
1212 cur_objects.clear();
1214 case BUTTON_WHEELDOWN:
1215 it = tilegroups_map.find(cur_tilegroup);
1216 if(it == tilegroups_map.begin())
1218 cur_tilegroup = tilegroups_map.rbegin()->first;
1219 cur_objects.clear();
1222 if(--it != --tilegroups_map.begin())
1223 cur_tilegroup = (*it).first;
1225 cur_tilegroup = tilegroups_map.rbegin()->first;
1227 cur_objects.clear();
1233 le_objects_bt->event(event);
1234 switch (le_objects_bt->get_state())
1236 case BUTTON_CLICKED:
1237 Menu::set_current(select_objects_menu);
1238 select_objects_menu_effect.start(200);
1239 select_objects_menu->set_pos(screen->w - 64,100,-0.5,0.5);
1241 case BUTTON_WHEELUP:
1242 it = objects_map.find(cur_objects);
1243 if(it == objects_map.end())
1245 cur_objects = objects_map.begin()->first;
1246 cur_tilegroup.clear();
1249 if(++it != objects_map.end())
1250 cur_objects = (*it).first;
1252 cur_objects = objects_map.begin()->first;
1254 cur_tilegroup.clear();
1256 case BUTTON_WHEELDOWN:
1257 it = objects_map.find(cur_objects);
1258 if(it == objects_map.begin())
1260 cur_objects = objects_map.rbegin()->first;
1261 cur_tilegroup.clear();
1264 if(--it != --objects_map.begin())
1265 cur_objects = (*it).first;
1267 cur_objects = objects_map.rbegin()->first;
1269 cur_tilegroup.clear();
1276 le_settings_bt->event(event);
1277 if(le_settings_bt->get_state() == BUTTON_CLICKED)
1279 update_level_settings_menu();
1280 Menu::set_current(level_settings_menu);
1282 if(!cur_tilegroup.empty())
1284 if((pbutton = tilegroups_map[cur_tilegroup]->event(event)) != NULL)
1286 if(pbutton->get_state() == BUTTON_CLICKED)
1288 le_current.Tile(pbutton->get_tag());
1292 else if(!cur_objects.empty())
1294 if((pbutton = objects_map[cur_objects]->event(event)) != NULL)
1296 if(pbutton->get_state() == BUTTON_CLICKED)
1298 le_current.Object(pbutton->get_game_object());
1303 if((pbutton = le_tilemap_panel->event(event)) != NULL)
1305 if(pbutton->get_state() == BUTTON_CLICKED)
1307 active_tm = static_cast<TileMapType>(pbutton->get_tag());
1313 le_settings_bt->event(event);
1314 if(le_settings_bt->get_state() == BUTTON_CLICKED)
1316 Menu::set_current(0);
1318 le_tilegroup_bt->event(event);
1319 if(le_tilegroup_bt->get_state() == BUTTON_CLICKED)
1321 Menu::set_current(0);
1326 if(!Menu::current())
1328 le_move_left_bt->event(event);
1329 le_move_right_bt->event(event);
1331 if(le_mouse_pressed[LEFT])
1333 if(le_current.IsTile())
1334 le_change(cursor_x, cursor_y, active_tm, le_current.tile);
1336 else if(le_mouse_clicked[LEFT])
1338 if(le_current.IsObject())
1340 std::string type = le_current.obj->type();
1341 if(type == "BadGuy")
1343 BadGuy* pbadguy = dynamic_cast<BadGuy*>(le_current.obj);
1345 le_world.bad_guys.push_back(BadGuy(cursor_x+scroll_x, cursor_y,pbadguy->kind,false));
1346 le_current_level->badguy_data.push_back(&le_world.bad_guys.back());
1349 le_mouse_clicked[LEFT] = false;
1354 if(!Menu::current())
1356 if(le_move_left_bt->get_state() == BUTTON_PRESSED)
1360 else if(le_move_left_bt->get_state() == BUTTON_HOVER)
1365 if(le_move_right_bt->get_state() == BUTTON_PRESSED)
1369 else if(le_move_right_bt->get_state() == BUTTON_HOVER)
1377 void le_highlight_selection()
1381 if(selection.x1 < selection.x2)
1391 if(selection.y1 < selection.y2)
1407 fillrect(x1*32-pos_x, y1*32,32* (x2 - x1 + 1),32 * (y2 - y1 + 1),173,234,177,103);
1410 void le_change(float x, float y, int tm, unsigned int c)
1412 if(le_current_level != NULL)
1418 /* level_changed = true; */
1420 switch(le_selection_mode)
1423 le_current_level->change(x,y,tm,c);
1425 base_type cursor_base;
1428 cursor_base.width = 32;
1429 cursor_base.height = 32;
1431 /* if there is a bad guy over there, remove it */
1432 for(i = 0; i < le_world.bad_guys.size(); ++i)
1433 if(rectcollision(cursor_base,le_world.bad_guys[i].base))
1435 le_world.bad_guys.erase(le_world.bad_guys.begin() + i);
1436 le_current_level->badguy_data.erase(le_current_level->badguy_data.begin() + i);
1441 if(selection.x1 < selection.x2)
1451 if(selection.y1 < selection.y2)
1467 /* if there is a bad guy over there, remove it */
1468 for(std::vector<BadGuy>::iterator i = le_world.bad_guys.begin();
1469 i != le_world.bad_guys.end(); /* will be at end of loop */)
1471 if(i->base.x/32 >= x1 && i->base.x/32 <= x2
1472 && i->base.y/32 >= y1 && i->base.y/32 <= y2)
1474 i = le_world.bad_guys.erase(i);
1483 for(xx = x1; xx <= x2; xx++)
1484 for(yy = y1; yy <= y2; yy++)
1486 le_current_level->change(xx*32, yy*32, tm, c);
1498 le_current_level->save("test", le_level);
1500 GameSession session("test",le_level, ST_GL_TEST);
1502 player_status.reset();
1504 music_manager->halt_music();
1506 Menu::set_current(NULL);
1507 le_world.arrays_free();
1508 le_current_level->load_gfx();
1509 le_world.activate_bad_guys();
1515 unsigned int i, done_;
1517 " - This is SuperTux's built-in level editor -",
1518 "It has been designed to be light and easy to use from the start.",
1520 "When you first load the level editor you are given a menu where you",
1521 "can load level subsets, create a new level subset, edit the current",
1522 "subset's settings, or simply quit the editor. You can access this menu",
1523 "from the level editor at any time by pressing the escape key.",
1525 "To your right is your button bar. The center of this contains many",
1526 "tiles you can use to make your level. To select a tile, click on it",
1527 "with your left mouse button; your selection will be shown in the",
1528 "bottom right corner of the button box. Click anywhere on your level",
1529 "with the left mouse button to place that tile down. If you right click",
1530 "a tile in the button bar, you can find out what its keyboard shortcut",
1531 "is. The three buttons FGD, BGD and EMY let you pick from foreground,",
1532 "background, and enemy tiles. The eraser lets you remove tiles.",
1533 "The left and right arrow keys scroll back and forth through your level.",
1534 "The button with the wrench and screwdriver, lets you change the",
1535 "settings of your level, including how long it is or what music it will",
1536 "play. When you are ready to give your level a test, click on the little",
1537 "running Tux. If you like the changes you have made to your level,",
1538 "press the red save key to keep them.",
1539 "To change which level in your subset you are editing, press the white",
1540 "up and down arrow keys at the top of the button box.",
1542 "Have fun making levels! If you make some good ones, send them to us on",
1543 "the SuperTux mailing list!",
1548 blue_text->drawf("- Help -", 0, 30, A_HMIDDLE, A_TOP, 2);
1550 for(i = 0; i < sizeof(text)/sizeof(char *); i++)
1551 white_small_text->draw(text[i], 5, 80+(i*white_small_text->h), 1);
1553 gold_text->drawf("Press Any Key to Continue", 0, 440, A_HMIDDLE, A_TOP, 1);
1561 done_ = wait_for_event(event);