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();
87 TileOrObject() : tile(0), obj(NULL) { is_tile = true; };
89 void Tile(unsigned int set_to) { tile = set_to; is_tile = true; }
90 void Object(GameObject* pobj) { obj = pobj; is_tile = false; }
91 //Returns true for a tile
92 bool IsTile() { return is_tile; };
93 //Returns true for a GameObject
94 bool IsObject() { return !is_tile; };
95 void Init() { tile = 0; obj = NULL; is_tile = true; };
97 bool is_tile; //true for tile (false for object)
102 /* leveleditor internals */
103 static string_list_type level_subsets;
104 static bool le_level_changed; /* if changes, ask for saving, when quiting*/
105 static bool show_minimap;
106 static int pos_x, cursor_x, cursor_y, fire;
108 static World* le_world;
109 static LevelSubset* le_level_subset;
110 static int le_show_grid;
112 static Surface* le_selection;
114 static TileOrObject le_current;
115 static bool le_mouse_pressed[2];
116 static bool le_mouse_clicked[2];
117 static Button* le_save_level_bt;
118 static Button* le_exit_bt;
119 static Button* le_test_level_bt;
120 static Button* le_next_level_bt;
121 static Button* le_previous_level_bt;
122 static Button* le_move_right_bt;
123 static Button* le_move_left_bt;
124 static Button* le_rubber_bt;
125 static Button* le_select_mode_one_bt;
126 static Button* le_select_mode_two_bt;
127 static Button* le_settings_bt;
128 static Button* le_tilegroup_bt;
129 static Button* le_objects_bt;
130 static ButtonPanel* le_tilemap_panel;
131 static Menu* leveleditor_menu;
132 static Menu* subset_load_menu;
133 static Menu* subset_new_menu;
134 static Menu* subset_settings_menu;
135 static Menu* level_settings_menu;
136 static Menu* select_tilegroup_menu;
137 static Menu* select_objects_menu;
138 static Timer select_tilegroup_menu_effect;
139 static Timer select_objects_menu_effect;
140 typedef std::map<std::string, ButtonPanel*> ButtonPanelMap;
141 static ButtonPanelMap tilegroups_map;
142 static ButtonPanelMap objects_map;
143 static std::string cur_tilegroup;
144 static std::string cur_objects;
146 static square selection;
147 static int le_selection_mode;
148 static SDL_Event event;
149 TileMapType active_tm;
151 int leveleditor(char* filename)
153 int last_time, now_time, i;
162 clearscreen(0, 0, 0);
165 music_manager->halt_music();
167 while (SDL_PollEvent(&event))
171 if(le_load_level(filename))
176 last_time = SDL_GetTicks();
181 if(Menu::current() == select_tilegroup_menu)
183 if(select_tilegroup_menu_effect.check())
185 select_tilegroup_menu->set_pos(screen->w - 64 + select_tilegroup_menu_effect.get_left(),
189 select_tilegroup_menu->set_pos(screen->w - 64,66,-0.5,0.5);
191 else if(Menu::current() == select_objects_menu)
193 if(select_objects_menu_effect.check())
195 select_objects_menu->set_pos(screen->w - 64 + select_objects_menu_effect.get_left(),82,-0.5,0.5);
198 select_objects_menu->set_pos(screen->w - 64,82,-0.5,0.5);
203 /* making events results to be in order */
206 if(pos_x > (le_world->get_level()->width * 32) - screen->w)
207 pos_x = (le_world->get_level()->width * 32) - screen->w;
213 clearscreen(0, 0, 0);
215 /* draw editor interface */
218 Menu* menu = Menu::current();
224 if(menu == leveleditor_menu)
226 switch (leveleditor_menu->check())
228 case MNID_RETURNLEVELEDITOR:
229 Menu::set_current(0);
231 case MNID_SUBSETSETTINGS:
232 update_subset_settings_menu();
234 case MNID_QUITLEVELEDITOR:
239 else if(menu == level_settings_menu)
241 switch (level_settings_menu->check())
244 apply_level_settings_menu();
245 Menu::set_current(NULL);
253 else if(menu == select_tilegroup_menu)
256 switch (it = select_tilegroup_menu->check())
262 = select_tilegroup_menu->get_item_by_id(it).text;
263 Menu::set_current(0);
270 else if(menu == select_objects_menu)
273 switch (it = select_objects_menu->check())
278 cur_objects = select_objects_menu->get_item_by_id(it).text;
281 Menu::set_current(0);
286 else if(menu == subset_load_menu)
288 switch (i = subset_load_menu->check())
295 if(le_load_level(level_subsets.item[i-1]))
301 else if(menu == subset_new_menu)
303 if(subset_new_menu->item[2].input[0] == '\0')
304 subset_new_menu->item[3].kind = MN_DEACTIVE;
307 subset_new_menu->item[3].kind = MN_ACTION;
309 switch (i = subset_new_menu->check())
311 case MNID_CREATESUBSET:
312 LevelSubset::create(subset_new_menu->get_item_by_id(MNID_SUBSETNAME).input);
313 le_level_subset->load(subset_new_menu->get_item_by_id(MNID_SUBSETNAME).input);
314 leveleditor_menu->get_item_by_id(MNID_SUBSETSETTINGS).kind = MN_GOTO;
316 le_world = new World(le_level_subset->name,1);
317 subset_new_menu->get_item_by_id(MNID_SUBSETNAME).change_input("");
319 Menu::set_current(subset_settings_menu);
324 else if(menu == subset_settings_menu)
326 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 )
327 subset_settings_menu->get_item_by_id(MNID_SUBSETSAVECHANGES).kind = MN_DEACTIVE;
329 subset_settings_menu->get_item_by_id(MNID_SUBSETSAVECHANGES).kind = MN_ACTION;
331 switch (i = subset_settings_menu->check())
333 case MNID_SUBSETSAVECHANGES:
334 save_subset_settings_menu();
335 Menu::set_current(leveleditor_menu);
341 mouse_cursor->draw();
349 ++global_frame_counter;
352 now_time = SDL_GetTicks();
353 if (now_time < last_time + FPS)
354 SDL_Delay(last_time + FPS - now_time); /* delay some time */
362 int le_load_level(char *filename)
364 le_level_subset->load(filename);
365 leveleditor_menu->get_item_by_id(MNID_SUBSETSETTINGS).kind = MN_GOTO;
368 le_world = new World(filename,le_level);
370 //GameSession* session = new GameSession(datadir + "/levels/" + le_level_subset->name + "/level1.stl", 0, ST_GL_DEMO_GAME);
372 Menu::set_current(NULL);
381 leveleditor_menu = new Menu();
382 subset_load_menu = new Menu();
383 subset_new_menu = new Menu();
384 subset_settings_menu = new Menu();
385 level_settings_menu = new Menu();
386 select_tilegroup_menu = new Menu();
387 select_objects_menu = new Menu();
389 leveleditor_menu->additem(MN_LABEL,"Level Editor Menu",0,0);
390 leveleditor_menu->additem(MN_HL,"",0,0);
391 leveleditor_menu->additem(MN_ACTION,"Return To Level Editor",0,0,MNID_RETURNLEVELEDITOR);
392 leveleditor_menu->additem(MN_DEACTIVE,"Level Subset Settings",0,subset_settings_menu,MNID_SUBSETSETTINGS);
393 leveleditor_menu->additem(MN_GOTO,"Load Level Subset",0,subset_load_menu);
394 leveleditor_menu->additem(MN_GOTO,"New Level Subset",0,subset_new_menu);
395 leveleditor_menu->additem(MN_HL,"",0,0);
396 leveleditor_menu->additem(MN_ACTION,"Quit Level Editor",0,0,MNID_QUITLEVELEDITOR);
398 Menu::set_current(leveleditor_menu);
400 subset_load_menu->additem(MN_LABEL, "Load Level Subset", 0, 0);
401 subset_load_menu->additem(MN_HL, "", 0, 0);
403 for(i = 0; i < level_subsets.num_items; ++i)
405 subset_load_menu->additem(MN_ACTION,level_subsets.item[i],0,0, i+1);
407 subset_load_menu->additem(MN_HL,"",0,0);
408 subset_load_menu->additem(MN_BACK,"Back",0,0);
410 subset_new_menu->additem(MN_LABEL,"New Level Subset",0,0);
411 subset_new_menu->additem(MN_HL,"",0,0);
412 subset_new_menu->additem(MN_TEXTFIELD,"Enter Name",0,0,MNID_SUBSETNAME);
413 subset_new_menu->additem(MN_ACTION,"Create",0,0, MNID_CREATESUBSET);
414 subset_new_menu->additem(MN_HL,"",0,0);
415 subset_new_menu->additem(MN_BACK,"Back",0,0);
417 subset_settings_menu->additem(MN_LABEL,"Level Subset Settings",0,0);
418 subset_settings_menu->additem(MN_HL,"",0,0);
419 subset_settings_menu->additem(MN_TEXTFIELD,"Title",0,0,MNID_SUBSETTITLE);
420 subset_settings_menu->additem(MN_TEXTFIELD,"Description",0,0,MNID_SUBSETDESCRIPTION);
421 subset_settings_menu->additem(MN_HL,"",0,0);
422 subset_settings_menu->additem(MN_ACTION,"Save Changes",0,0,MNID_SUBSETSAVECHANGES);
423 subset_settings_menu->additem(MN_HL,"",0,0);
424 subset_settings_menu->additem(MN_BACK,"Back",0,0);
426 level_settings_menu->arrange_left = true;
427 level_settings_menu->additem(MN_LABEL,"Level Settings",0,0);
428 level_settings_menu->additem(MN_HL,"",0,0);
429 level_settings_menu->additem(MN_TEXTFIELD, "Name ",0,0,MNID_NAME);
430 level_settings_menu->additem(MN_TEXTFIELD, "Author ",0,0,MNID_AUTHOR);
431 level_settings_menu->additem(MN_STRINGSELECT,"Song ",0,0,MNID_SONG);
432 level_settings_menu->additem(MN_STRINGSELECT,"Bg-Image",0,0,MNID_BGIMG);
433 level_settings_menu->additem(MN_STRINGSELECT,"Particle",0,0,MNID_PARTICLE);
434 level_settings_menu->additem(MN_NUMFIELD, "Length ",0,0,MNID_LENGTH);
435 level_settings_menu->additem(MN_NUMFIELD, "Time ",0,0,MNID_TIME);
436 level_settings_menu->additem(MN_NUMFIELD, "Gravity ",0,0,MNID_GRAVITY);
437 level_settings_menu->additem(MN_NUMFIELD, "Bg-Img-Speed",0,0,MNID_BGSPEED);
438 level_settings_menu->additem(MN_NUMFIELD, "Top Red ",0,0,MNID_TopRed);
439 level_settings_menu->additem(MN_NUMFIELD, "Top Green ",0,0,MNID_TopGreen);
440 level_settings_menu->additem(MN_NUMFIELD, "Top Blue ",0,0,MNID_TopBlue);
441 level_settings_menu->additem(MN_NUMFIELD, "Bottom Red ",0,0,MNID_BottomRed);
442 level_settings_menu->additem(MN_NUMFIELD, "Bottom Green",0,0,MNID_BottomGreen);
443 level_settings_menu->additem(MN_NUMFIELD, "Bottom Blue",0,0,MNID_BottomBlue);
444 level_settings_menu->additem(MN_HL,"",0,0);
445 level_settings_menu->additem(MN_ACTION,"Apply Changes",0,0,MNID_APPLY);
447 select_tilegroup_menu->arrange_left = true;
448 select_tilegroup_menu->additem(MN_LABEL,"Tilegroup",0,0);
449 select_tilegroup_menu->additem(MN_HL,"",0,0);
450 std::set<TileGroup>* tilegroups = TileManager::tilegroups();
452 for(std::set<TileGroup>::iterator it = tilegroups->begin();
453 it != tilegroups->end(); ++it )
455 select_tilegroup_menu->additem(MN_ACTION, it->name, 0, 0, tileid);
457 tilegroups_map[(*it).name] = new ButtonPanel(screen->w - 64,96, 64, 318);
460 for(std::vector<int>::const_iterator sit = (*it).tiles.begin();
461 sit != (*it).tiles.end(); ++sit, ++i)
463 std::string imagefile = "/images/tilesets/" ;
464 bool only_editor_image = false;
465 if(!TileManager::instance()->get(*sit)->filenames.empty())
467 imagefile += TileManager::instance()->get(*sit)->filenames[0];
469 else if(!TileManager::instance()->get(*sit)->editor_filenames.empty())
471 imagefile += TileManager::instance()->get(*sit)->editor_filenames[0];
472 only_editor_image = true;
476 imagefile += "notile.png";
478 Button* button = new Button(imagefile, it->name, SDLKey(SDLK_a + i),
480 if(!only_editor_image)
481 if(!TileManager::instance()->get(*sit)->editor_filenames.empty())
483 imagefile = "/images/tilesets/" + TileManager::instance()->get(*sit)->editor_filenames[0];
484 button->add_icon(imagefile,32,32);
486 tilegroups_map[it->name]->additem(button, *sit);
489 select_tilegroup_menu->additem(MN_HL,"",0,0);
491 select_objects_menu->arrange_left = true;
492 select_objects_menu->additem(MN_LABEL,"Objects",0,0);
493 select_objects_menu->additem(MN_HL,"",0,0);
494 select_objects_menu->additem(MN_ACTION,"BadGuys",0,0,1);
495 objects_map["BadGuys"] = new ButtonPanel(screen->w - 64,96, 64, 318);
497 for(int i = 0; i < NUM_BadGuyKinds; ++i)
499 BadGuy bad_tmp(0,0,BadGuyKind(i),false);
500 objects_map["BadGuys"]->additem(new Button("", "BadGuy",(SDLKey)(i+'a'),0,0,32,32),1000000+i);
501 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));
504 select_objects_menu->additem(MN_HL,"",0,0);
512 level_subsets = dsubdirs("/levels", "info");
513 le_level_subset = new LevelSubset;
523 le_frame = 0; /* support for frames in some tiles, like waves and bad guys */
524 le_level_changed = false;
526 le_mouse_pressed[LEFT] = false;
527 le_mouse_pressed[RIGHT] = false;
529 le_mouse_clicked[LEFT] = false;
530 le_mouse_clicked[RIGHT] = false;
532 le_selection = new Surface(datadir + "/images/leveleditor/select.png", USE_ALPHA);
534 select_tilegroup_menu_effect.init(false);
535 select_objects_menu_effect.init(false);
538 le_save_level_bt = new Button("/images/icons/save.png","Save level", SDLK_F6,screen->w-64,32);
539 le_exit_bt = new Button("/images/icons/exit.png","Exit", SDLK_F6,screen->w-32,32);
540 le_next_level_bt = new Button("/images/icons/up.png","Next level", SDLK_PAGEUP,screen->w-64,0);
541 le_previous_level_bt = new Button("/images/icons/down.png","Previous level",SDLK_PAGEDOWN,screen->w-32,0);
542 le_rubber_bt = new Button("/images/icons/rubber.png","Rubber",SDLK_DELETE,screen->w-32,48);
543 le_select_mode_one_bt = new Button ("/images/icons/select-mode1.png","Select single tile",SDLK_F3,screen->w-64,48);
544 le_select_mode_two_bt = new Button("/images/icons/select-mode2.png","Select multiple tiles",SDLK_F3,screen->w-64,48);
545 le_test_level_bt = new Button("/images/icons/test-level.png","Test level",SDLK_F4,screen->w-64,screen->h - 64);
546 le_settings_bt = new Button("/images/icons/settings.png","Level settings",SDLK_F5,screen->w-32,screen->h - 64);
547 le_move_left_bt = new Button("/images/icons/left.png","Move left",SDLK_LEFT,0,0);
548 le_move_right_bt = new Button("/images/icons/right.png","Move right",SDLK_RIGHT,screen->w-80,0);
549 le_tilegroup_bt = new Button("/images/icons/tilegroup.png","Select Tilegroup", SDLK_F7,screen->w-64,64);
550 le_objects_bt = new Button("/images/icons/objects.png","Select Objects", SDLK_F7,screen->w-64,80);
552 le_tilemap_panel = new ButtonPanel(screen->w-64,screen->h-32,32,32);
553 le_tilemap_panel->set_button_size(32,10);
554 le_tilemap_panel->additem(new Button("/images/icons/bkgrd.png","Background",SDLK_b,0,0),TM_BG);
555 le_tilemap_panel->additem(new Button("/images/icons/intact.png","Interactive",SDLK_i,0,0),TM_IA);
556 le_tilemap_panel->additem(new Button("/images/icons/frgrd.png","Foreground",SDLK_f,0,0),TM_FG);
557 le_tilemap_panel->highlight_last(true);
563 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
569 void update_level_settings_menu()
574 level_settings_menu->get_item_by_id(MNID_NAME).change_input(le_world->get_level()->name.c_str());
575 level_settings_menu->get_item_by_id(MNID_AUTHOR).change_input(le_world->get_level()->author.c_str());
577 string_list_copy(level_settings_menu->get_item_by_id(MNID_SONG).list, dfiles("music/",NULL, "-fast"));
578 string_list_copy(level_settings_menu->get_item_by_id(MNID_BGIMG).list, dfiles("images/background",NULL, NULL));
579 string_list_add_item(level_settings_menu->get_item_by_id(MNID_BGIMG).list,"");
580 string_list_add_item(level_settings_menu->get_item_by_id(MNID_PARTICLE).list,"");
581 string_list_add_item(level_settings_menu->get_item_by_id(MNID_PARTICLE).list,"snow");
582 string_list_add_item(level_settings_menu->get_item_by_id(MNID_PARTICLE).list,"clouds");
584 if((i = string_list_find(level_settings_menu->get_item_by_id(MNID_SONG).list,le_world->get_level()->song_title.c_str())) != -1)
585 level_settings_menu->get_item_by_id(MNID_SONG).list->active_item = i;
586 if((i = string_list_find(level_settings_menu->get_item_by_id(MNID_BGIMG).list,le_world->get_level()->bkgd_image.c_str())) != -1)
587 level_settings_menu->get_item_by_id(MNID_BGIMG).list->active_item = i;
588 if((i = string_list_find(level_settings_menu->get_item_by_id(MNID_PARTICLE).list,le_world->get_level()->particle_system.c_str())) != -1)
589 level_settings_menu->get_item_by_id(MNID_PARTICLE).list->active_item = i;
591 sprintf(str,"%d",le_world->get_level()->width);
592 level_settings_menu->get_item_by_id(MNID_LENGTH).change_input(str);
593 sprintf(str,"%d",le_world->get_level()->time_left);
594 level_settings_menu->get_item_by_id(MNID_TIME).change_input(str);
595 sprintf(str,"%2.0f",le_world->get_level()->gravity);
596 level_settings_menu->get_item_by_id(MNID_GRAVITY).change_input(str);
597 sprintf(str,"%d",le_world->get_level()->bkgd_speed);
598 level_settings_menu->get_item_by_id(MNID_BGSPEED).change_input(str);
599 sprintf(str,"%d",le_world->get_level()->bkgd_top.red);
600 level_settings_menu->get_item_by_id(MNID_TopRed).change_input(str);
601 sprintf(str,"%d",le_world->get_level()->bkgd_top.green);
602 level_settings_menu->get_item_by_id(MNID_TopGreen).change_input(str);
603 sprintf(str,"%d",le_world->get_level()->bkgd_top.blue);
604 level_settings_menu->get_item_by_id(MNID_TopBlue).change_input(str);
605 sprintf(str,"%d",le_world->get_level()->bkgd_bottom.red);
606 level_settings_menu->get_item_by_id(MNID_BottomRed).change_input(str);
607 sprintf(str,"%d",le_world->get_level()->bkgd_bottom.green);
608 level_settings_menu->get_item_by_id(MNID_BottomGreen).change_input(str);
609 sprintf(str,"%d",le_world->get_level()->bkgd_bottom.blue);
610 level_settings_menu->get_item_by_id(MNID_BottomBlue).change_input(str);
613 void update_subset_settings_menu()
615 subset_settings_menu->item[2].change_input(le_level_subset->title.c_str());
616 subset_settings_menu->item[3].change_input(le_level_subset->description.c_str());
619 void apply_level_settings_menu()
624 le_world->get_level()->name = level_settings_menu->get_item_by_id(MNID_NAME).input;
625 le_world->get_level()->author = level_settings_menu->get_item_by_id(MNID_AUTHOR).input;
627 if(le_world->get_level()->bkgd_image.compare(string_list_active(level_settings_menu->get_item_by_id(MNID_BGIMG).list)) != 0)
629 le_world->get_level()->bkgd_image = string_list_active(level_settings_menu->get_item_by_id(MNID_BGIMG).list);
633 if(le_world->get_level()->particle_system.compare(string_list_active(level_settings_menu->get_item_by_id(MNID_PARTICLE).list)) != 0)
635 le_world->get_level()->particle_system = string_list_active(level_settings_menu->get_item_by_id(MNID_PARTICLE).list);
640 le_world->get_level()->load_gfx();
643 le_world->get_level()->song_title = string_list_active(level_settings_menu->get_item_by_id(MNID_SONG).list);
645 le_world->get_level()->change_size(atoi(level_settings_menu->get_item_by_id(MNID_LENGTH).input));
646 le_world->get_level()->time_left = atoi(level_settings_menu->get_item_by_id(MNID_BGIMG).input);
647 le_world->get_level()->gravity = atof(level_settings_menu->get_item_by_id(MNID_GRAVITY).input);
648 le_world->get_level()->bkgd_speed = atoi(level_settings_menu->get_item_by_id(MNID_BGSPEED).input);
649 le_world->get_level()->bkgd_top.red = atoi(level_settings_menu->get_item_by_id(MNID_TopRed).input);
650 le_world->get_level()->bkgd_top.green = atoi(level_settings_menu->get_item_by_id(MNID_TopGreen).input);
651 le_world->get_level()->bkgd_top.blue = atoi(level_settings_menu->get_item_by_id(MNID_TopBlue).input);
652 le_world->get_level()->bkgd_bottom.red = atoi(level_settings_menu->get_item_by_id(MNID_BottomRed).input);
653 le_world->get_level()->bkgd_bottom.green = atoi(level_settings_menu->get_item_by_id(MNID_BottomGreen).input);
654 le_world->get_level()->bkgd_bottom.blue = atoi(level_settings_menu->get_item_by_id(MNID_BottomBlue).input);
657 void save_subset_settings_menu()
659 le_level_subset->title = subset_settings_menu->item[2].input;
660 le_level_subset->description = subset_settings_menu->item[3].input;
661 le_level_subset->save();
664 void le_goto_level(int levelnb)
667 le_world = new World(le_level_subset->name, levelnb);
672 /*if(level_changed == true)
673 if(askforsaving() == CANCEL)
676 SDL_EnableKeyRepeat(0, 0); // disables key repeating
679 delete leveleditor_menu;
680 delete subset_load_menu;
681 delete subset_new_menu;
682 delete subset_settings_menu;
683 delete level_settings_menu;
684 delete select_tilegroup_menu;
685 delete select_objects_menu;
686 delete le_save_level_bt;
688 delete le_test_level_bt;
689 delete le_next_level_bt;
690 delete le_previous_level_bt;
691 delete le_move_right_bt;
692 delete le_move_left_bt;
694 delete le_select_mode_one_bt;
695 delete le_select_mode_two_bt;
696 delete le_settings_bt;
697 delete le_tilegroup_bt;
698 delete le_objects_bt;
699 delete le_tilemap_panel;
701 delete le_level_subset;
704 for(ButtonPanelMap::iterator i = tilegroups_map.begin();
705 i != tilegroups_map.end(); ++i)
709 for(ButtonPanelMap::iterator i = objects_map.begin();
710 i != objects_map.end(); ++i)
716 void le_drawminimap()
722 if(screen->w - 64 > le_world->get_level()->width * 4)
724 else if(screen->w - 64 > le_world->get_level()->width * 2)
728 int left_offset = (screen->w - 64 - le_world->get_level()->width*mini_tile_width) / 2;
730 for (int y = 0; y < 15; ++y)
731 for (int x = 0; x < le_world->get_level()->width; ++x)
734 Tile::draw_stretched(left_offset + mini_tile_width*x, y * 4, mini_tile_width , 4, le_world->get_level()->bg_tiles[y][x]);
736 Tile::draw_stretched(left_offset + mini_tile_width*x, y * 4, mini_tile_width , 4, le_world->get_level()->ia_tiles[y][x]);
738 Tile::draw_stretched(left_offset + mini_tile_width*x, y * 4, mini_tile_width , 4, le_world->get_level()->fg_tiles[y][x]);
742 fillrect(left_offset, 0, le_world->get_level()->width*mini_tile_width, 15*4, 200, 200, 200, 128);
744 fillrect(left_offset + (pos_x/32)*mini_tile_width, 0, 19*mini_tile_width, 2, 200, 200, 200, 200);
745 fillrect(left_offset + (pos_x/32)*mini_tile_width, 0, 2, 15*4, 200, 200, 200, 200);
746 fillrect(left_offset + (pos_x/32)*mini_tile_width + 19*mini_tile_width - 2, 0, 2, 15*4, 200, 200, 200, 200);
747 fillrect(left_offset + (pos_x/32)*mini_tile_width, 15*4-2, 19*mini_tile_width, 2, 200, 200, 200, 200);
751 void le_drawinterface()
758 /* draw a grid (if selected) */
761 for(x = 0; x < 19; x++)
762 fillrect(x*32 - ((int)pos_x % 32), 0, 1, screen->h, 225, 225, 225,255);
763 for(y = 0; y < 15; y++)
764 fillrect(0, y*32, screen->w - 32, 1, 225, 225, 225,255);
768 if(show_minimap && use_gl) // use_gl because the minimap isn't shown correctly in software mode. Any idea? FIXME Possible reasons: SDL_SoftStretch is a hack itsself || an alpha blitting issue SDL can't handle in software mode
771 if(le_selection_mode == CURSOR)
772 if(le_current.IsTile())
773 le_selection->draw( cursor_x - pos_x, cursor_y);
775 le_selection->draw( cursor_x, cursor_y);
776 else if(le_selection_mode == SQUARE)
779 le_highlight_selection();
780 /* draw current selection */
781 w = selection.x2 - selection.x1;
782 h = selection.y2 - selection.y1;
783 fillrect(selection.x1 - pos_x, selection.y1, w, SELECT_W, SELECT_CLR);
784 fillrect(selection.x1 - pos_x + w, selection.y1, SELECT_W, h, SELECT_CLR);
785 fillrect(selection.x1 - pos_x, selection.y1 + h, w, SELECT_W, SELECT_CLR);
786 fillrect(selection.x1 - pos_x, selection.y1, SELECT_W, h, SELECT_CLR);
790 /* draw button bar */
791 fillrect(screen->w - 64, 0, 64, screen->h, 50, 50, 50,255);
793 if(le_current.IsTile())
795 Tile::draw(19 * 32, 14 * 32, le_current.tile);
796 if(TileManager::instance()->get(le_current.tile)->editor_images.size() > 0)
797 TileManager::instance()->get(le_current.tile)->editor_images[0]->draw( 19 * 32, 14 * 32);
799 if(le_current.IsObject())
801 le_current.obj->draw_on_screen(19 * 32, 14 * 32);
802 le_current.obj->draw_on_screen(cursor_x,cursor_y);
807 le_save_level_bt->draw();
809 le_test_level_bt->draw();
810 le_next_level_bt->draw();
811 le_previous_level_bt->draw();
812 le_rubber_bt->draw();
813 if(le_selection_mode == SQUARE)
814 le_select_mode_one_bt->draw();
815 else if(le_selection_mode == CURSOR)
816 le_select_mode_two_bt->draw();
817 le_settings_bt->draw();
818 le_move_right_bt->draw();
819 le_move_left_bt->draw();
820 le_tilegroup_bt->draw();
821 le_objects_bt->draw();
822 if(!cur_tilegroup.empty())
823 tilegroups_map[cur_tilegroup]->draw();
824 else if(!cur_objects.empty())
826 objects_map[cur_objects]->draw();
829 le_tilemap_panel->draw();
831 sprintf(str, "%d/%d", le_level,le_level_subset->levels);
832 white_text->drawf(str, -10, 16, A_RIGHT, A_TOP, 0);
834 white_small_text->draw("F1 for Help", 10, 430, 1);
839 white_small_text->draw("No Level Subset loaded - Press ESC and choose one in the menu", 10, 430, 1);
841 white_small_text->draw("No Level Subset loaded", 10, 430, 1);
851 /* Draw the real background */
852 if(le_world->get_level()->bkgd_image[0] != '\0')
854 s = (int)((float)pos_x * ((float)le_world->get_level()->bkgd_speed/60.)) % screen->w;
855 le_world->get_level()->img_bkgd->draw_part(s,0,0,0,
856 le_world->get_level()->img_bkgd->w - s - 32, le_world->get_level()->img_bkgd->h);
857 le_world->get_level()->img_bkgd->draw_part(0,0,screen->w - s - 32 ,0,s,
858 le_world->get_level()->img_bkgd->h);
862 drawgradient(le_world->get_level()->bkgd_top, le_world->get_level()->bkgd_bottom);
865 if(le_current.IsTile())
867 Tile::draw(cursor_x-pos_x, cursor_y,le_current.tile,128);
868 if(!TileManager::instance()->get(le_current.tile)->images.empty())
869 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);
871 if(le_current.IsObject())
873 le_current.obj->move_to(cursor_x, cursor_y);
876 /* clearscreen(current_level.bkgd_red, current_level.bkgd_green, current_level.bkgd_blue); */
878 for (y = 0; y < 15; ++y)
879 for (x = 0; x < 20; ++x)
882 if(active_tm == TM_BG)
887 Tile::draw(32*x - fmodf(pos_x, 32), y * 32, le_world->get_level()->bg_tiles[y][x + (int)(pos_x / 32)],a);
889 if(active_tm == TM_IA)
894 Tile::draw(32*x - fmodf(pos_x, 32), y * 32, le_world->get_level()->ia_tiles[y][x + (int)(pos_x / 32)],a);
896 if(active_tm == TM_FG)
901 Tile::draw(32*x - fmodf(pos_x, 32), y * 32, le_world->get_level()->fg_tiles[y][x + (int)(pos_x / 32)],a);
903 /* draw whats inside stuff when cursor is selecting those */
904 /* (draw them all the time - is this the right behaviour?) */
905 if(TileManager::instance()->get(le_world->get_level()->ia_tiles[y][x + (int)(pos_x / 32)])->editor_images.size() > 0)
906 TileManager::instance()->get(le_world->get_level()->ia_tiles[y][x + (int)(pos_x / 32)])->editor_images[0]->draw( x * 32 - ((int)pos_x % 32), y*32);
910 /* Draw the Bad guys: */
911 for (std::list<BadGuy*>::iterator it = le_world->bad_guys.begin(); it != le_world->bad_guys.end(); ++it)
913 /* to support frames: img_bsod_left[(frame / 5) % 4] */
920 /* Draw the player: */
921 /* for now, the position is fixed at (100, 240) */
922 largetux.walk_right->draw( 100 - pos_x, 240);
925 void le_change_object_properties(GameObject *pobj)
927 Menu* object_properties_menu = new Menu();
929 object_properties_menu->additem(MN_LABEL,pobj->type() + " Properties",0,0);
930 object_properties_menu->additem(MN_HL,"",0,0);
931 /*object_properties_menu->additem(MN_TEXTFIELD,"Title",0,0,MNID_SUBSETTITLE);
932 object_properties_menu->additem(MN_TEXTFIELD,"Description",0,0,MNID_SUBSETDESCRIPTION);
933 object_properties_menu->additem(MN_HL,"",0,0);
934 object_properties_menu->additem(MN_ACTION,"Save Changes",0,0,MNID_SUBSETSAVECHANGES);*/
935 object_properties_menu->additem(MN_HL,"",0,0);
936 object_properties_menu->additem(MN_BACK,"Apply",0,0);
938 delete object_properties_menu;
942 void le_checkevents()
949 keymod = SDL_GetModState();
951 while(SDL_PollEvent(&event))
955 Menu::current()->event(event);
959 mouse_cursor->set_state(MC_NORMAL);
961 /* testing SDL_KEYDOWN, SDL_KEYUP and SDL_QUIT events*/
962 if(event.type == SDL_KEYDOWN
963 || ((event.type == SDL_MOUSEBUTTONDOWN || SDL_MOUSEMOTION)
964 && (event.motion.x > 0
965 && event.motion.x < screen->w - 64 &&
966 event.motion.y > 0 && event.motion.y < screen->h)))
970 case SDL_KEYDOWN: // key pressed
971 key = event.key.keysym.sym;
975 Menu::set_current(leveleditor_menu);
978 cursor_x -= KEY_CURSOR_SPEED;
980 cursor_x -= KEY_CURSOR_FASTSPEED;
982 if(cursor_x < pos_x + MOUSE_LEFT_MARGIN)
983 pos_x = cursor_x - MOUSE_LEFT_MARGIN;
988 cursor_x += KEY_CURSOR_SPEED;
990 cursor_x += KEY_CURSOR_FASTSPEED;
992 if(cursor_x > pos_x + MOUSE_RIGHT_MARGIN-32)
993 pos_x = cursor_x - MOUSE_RIGHT_MARGIN+32;
998 cursor_y -= KEY_CURSOR_SPEED;
1000 cursor_y -= KEY_CURSOR_FASTSPEED;
1007 cursor_y += KEY_CURSOR_SPEED;
1009 cursor_y += KEY_CURSOR_FASTSPEED;
1011 if(cursor_y > screen->h-32)
1012 cursor_y = screen->h-32;
1025 cursor_x = (le_world->get_level()->width * 32) - 32;
1029 le_show_grid = !le_show_grid;
1035 case SDL_KEYUP: /* key released */
1036 switch(event.key.keysym.sym)
1045 case SDL_MOUSEBUTTONDOWN:
1046 if(event.button.button == SDL_BUTTON_LEFT)
1048 le_mouse_pressed[LEFT] = true;
1050 selection.x1 = event.motion.x + pos_x;
1051 selection.y1 = event.motion.y;
1052 selection.x2 = event.motion.x + pos_x;
1053 selection.y2 = event.motion.y;
1055 else if(event.button.button == SDL_BUTTON_RIGHT)
1057 le_mouse_pressed[RIGHT] = true;
1060 case SDL_MOUSEBUTTONUP:
1061 if(event.button.button == SDL_BUTTON_LEFT)
1063 le_mouse_pressed[LEFT] = false;
1064 le_mouse_clicked[LEFT] = true;
1066 else if(event.button.button == SDL_BUTTON_RIGHT)
1068 le_mouse_pressed[RIGHT] = false;
1069 le_mouse_clicked[RIGHT] = true;
1072 case SDL_MOUSEMOTION:
1074 if(!Menu::current())
1079 if(le_current.IsTile())
1081 cursor_x = ((int)(pos_x + x) / 32) * 32;
1082 cursor_y = ((int) y / 32) * 32;
1090 if(le_mouse_pressed[LEFT])
1092 selection.x2 = x + pos_x;
1096 if(le_mouse_pressed[RIGHT])
1098 pos_x += -1 * event.motion.xrel;
1102 case SDL_QUIT: // window closed
1111 if(le_world != NULL)
1113 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 &&
1114 event.motion.y > 0 && event.motion.y < screen->h)))
1116 le_mouse_pressed[LEFT] = false;
1117 le_mouse_pressed[RIGHT] = false;
1119 if(!Menu::current())
1121 /* Check for button events */
1122 le_test_level_bt->event(event);
1123 if(le_test_level_bt->get_state() == BUTTON_CLICKED)
1125 le_save_level_bt->event(event);
1126 if(le_save_level_bt->get_state() == BUTTON_CLICKED)
1127 le_world->get_level()->save(le_level_subset->name.c_str(),le_level);
1128 le_exit_bt->event(event);
1129 if(le_exit_bt->get_state() == BUTTON_CLICKED)
1131 Menu::set_current(leveleditor_menu);
1133 le_next_level_bt->event(event);
1134 if(le_next_level_bt->get_state() == BUTTON_CLICKED)
1136 if(le_level < le_level_subset->levels)
1138 le_goto_level(++le_level);
1144 sprintf(str,"Level %d doesn't exist. Create it?",le_level+1);
1145 if(confirm_dialog(str))
1147 new_lev.init_defaults();
1148 new_lev.save(le_level_subset->name.c_str(),++le_level);
1149 le_level_subset->levels = le_level;
1150 le_goto_level(le_level);
1154 le_previous_level_bt->event(event);
1155 if(le_previous_level_bt->get_state() == BUTTON_CLICKED)
1158 le_goto_level(--le_level);
1160 le_rubber_bt->event(event);
1161 if(le_rubber_bt->get_state() == BUTTON_CLICKED)
1164 if(le_selection_mode == SQUARE)
1166 le_select_mode_one_bt->event(event);
1167 if(le_select_mode_one_bt->get_state() == BUTTON_CLICKED)
1168 le_selection_mode = CURSOR;
1172 le_select_mode_two_bt->event(event);
1173 if(le_select_mode_two_bt->get_state() == BUTTON_CLICKED)
1174 le_selection_mode = SQUARE;
1176 ButtonPanelMap::iterator it;
1177 le_tilegroup_bt->event(event);
1178 switch (le_tilegroup_bt->get_state())
1180 case BUTTON_CLICKED:
1181 Menu::set_current(select_tilegroup_menu);
1182 select_tilegroup_menu_effect.start(200);
1183 select_tilegroup_menu->set_pos(screen->w - 64,100,-0.5,0.5);
1185 case BUTTON_WHEELUP:
1186 if(cur_tilegroup.empty())
1188 cur_tilegroup = tilegroups_map.begin()->first;
1192 it = tilegroups_map.find(cur_tilegroup);
1193 if((++it) == tilegroups_map.end())
1195 cur_tilegroup = tilegroups_map.begin()->first;
1199 cur_tilegroup = (*it).first;
1205 case BUTTON_WHEELDOWN:
1206 it = tilegroups_map.find(cur_tilegroup);
1207 if(it == tilegroups_map.begin())
1209 cur_tilegroup = tilegroups_map.rbegin()->first;
1213 if(--it != --tilegroups_map.begin())
1214 cur_tilegroup = (*it).first;
1216 cur_tilegroup = tilegroups_map.rbegin()->first;
1224 le_objects_bt->event(event);
1225 switch (le_objects_bt->get_state())
1227 case BUTTON_CLICKED:
1228 Menu::set_current(select_objects_menu);
1229 select_objects_menu_effect.start(200);
1230 select_objects_menu->set_pos(screen->w - 64,100,-0.5,0.5);
1232 case BUTTON_WHEELUP:
1233 it = objects_map.find(cur_objects);
1234 if(it == objects_map.end())
1236 cur_objects = objects_map.begin()->first;
1240 if(++it != objects_map.end())
1241 cur_objects = (*it).first;
1243 cur_objects = objects_map.begin()->first;
1247 case BUTTON_WHEELDOWN:
1248 it = objects_map.find(cur_objects);
1249 if(it == objects_map.begin())
1251 cur_objects = objects_map.rbegin()->first;
1255 if(--it != --objects_map.begin())
1256 cur_objects = (*it).first;
1258 cur_objects = objects_map.rbegin()->first;
1267 le_settings_bt->event(event);
1268 if(le_settings_bt->get_state() == BUTTON_CLICKED)
1270 update_level_settings_menu();
1271 Menu::set_current(level_settings_menu);
1273 if(!cur_tilegroup.empty())
1275 if((pbutton = tilegroups_map[cur_tilegroup]->event(event)) != NULL)
1277 if(pbutton->get_state() == BUTTON_CLICKED)
1279 le_current.Tile(pbutton->get_tag());
1283 else if(!cur_objects.empty())
1285 if((pbutton = objects_map[cur_objects]->event(event)) != NULL)
1287 if(pbutton->get_state() == BUTTON_CLICKED)
1289 le_current.Object(pbutton->get_game_object());
1294 if((pbutton = le_tilemap_panel->event(event)) != NULL)
1296 if(pbutton->get_state() == BUTTON_CLICKED)
1298 active_tm = static_cast<TileMapType>(pbutton->get_tag());
1304 le_settings_bt->event(event);
1305 if(le_settings_bt->get_state() == BUTTON_CLICKED)
1307 Menu::set_current(0);
1309 le_tilegroup_bt->event(event);
1310 if(le_tilegroup_bt->get_state() == BUTTON_CLICKED)
1312 Menu::set_current(0);
1317 if(!Menu::current() && !show_minimap)
1319 if(le_mouse_pressed[LEFT])
1321 if(le_current.IsTile())
1322 le_change(cursor_x, cursor_y, active_tm, le_current.tile);
1324 else if(le_mouse_clicked[LEFT])
1326 if(le_current.IsObject())
1328 std::string type = le_current.obj->type();
1329 if(type == "BadGuy")
1331 BadGuy* pbadguy = dynamic_cast<BadGuy*>(le_current.obj);
1333 le_world->bad_guys.push_back(new BadGuy(cursor_x+scroll_x, cursor_y,pbadguy->kind,false));
1334 le_world->get_level()->badguy_data.push_back(le_world->bad_guys.back());
1337 le_mouse_clicked[LEFT] = false;
1342 if(!Menu::current())
1344 show_minimap = false;
1346 le_move_left_bt->event(event);
1347 le_move_right_bt->event(event);
1348 switch(le_move_left_bt->get_state())
1350 case BUTTON_PRESSED:
1352 show_minimap = true;
1356 show_minimap = true;
1358 case BUTTON_CLICKED:
1359 show_minimap = true;
1365 switch(le_move_right_bt->get_state())
1367 case BUTTON_PRESSED:
1369 show_minimap = true;
1373 show_minimap = true;
1375 case BUTTON_CLICKED:
1376 show_minimap = true;
1386 void le_highlight_selection()
1390 if(selection.x1 < selection.x2)
1400 if(selection.y1 < selection.y2)
1416 fillrect(x1*32-pos_x, y1*32,32* (x2 - x1 + 1),32 * (y2 - y1 + 1),173,234,177,103);
1419 void le_change(float x, float y, int tm, unsigned int c)
1421 if(le_world != NULL)
1427 /* level_changed = true; */
1429 switch(le_selection_mode)
1432 le_world->get_level()->change(x,y,tm,c);
1434 base_type cursor_base;
1437 cursor_base.width = 32;
1438 cursor_base.height = 32;
1440 /* if there is a bad guy over there, remove it */
1441 for(std::list<BadGuy*>::iterator it = le_world->bad_guys.begin(); it != le_world->bad_guys.end(); ++it)
1442 if(rectcollision(cursor_base,(*it)->base))
1444 le_world->bad_guys.erase(le_world->bad_guys.begin(),it);
1445 le_world->get_level()->badguy_data.erase(le_world->get_level()->badguy_data.begin() + i);
1450 if(selection.x1 < selection.x2)
1460 if(selection.y1 < selection.y2)
1476 /* if there is a bad guy over there, remove it */
1477 for(std::list<BadGuy*>::iterator it = le_world->bad_guys.begin();
1478 it != le_world->bad_guys.end(); /* will be at end of loop */)
1480 if((*it)->base.x/32 >= x1 && (*it)->base.x/32 <= x2
1481 && (*it)->base.y/32 >= y1 && (*it)->base.y/32 <= y2)
1483 it = le_world->bad_guys.erase(it);
1492 for(xx = x1; xx <= x2; xx++)
1493 for(yy = y1; yy <= y2; yy++)
1495 le_world->get_level()->change(xx*32, yy*32, tm, c);
1507 le_world->get_level()->save("test", le_level);
1509 GameSession session("test",le_level, ST_GL_TEST);
1511 player_status.reset();
1513 music_manager->halt_music();
1515 Menu::set_current(NULL);
1516 /*delete le_world.arrays_free();
1517 le_current_level->load_gfx();
1518 le_world.activate_bad_guys();*/
1524 unsigned int i, done_;
1526 " - This is SuperTux's built-in level editor -",
1527 "It has been designed to be light and easy to use from the start.",
1529 "When you first load the level editor you are given a menu where you",
1530 "can load level subsets, create a new level subset, edit the current",
1531 "subset's settings, or simply quit the editor. You can access this menu",
1532 "from the level editor at any time by pressing the escape key.",
1534 "To your right is your button bar. The center of this contains many",
1535 "tiles you can use to make your level. To select a tile, click on it",
1536 "with your left mouse button; your selection will be shown in the",
1537 "bottom right corner of the button box. Click anywhere on your level",
1538 "with the left mouse button to place that tile down. If you right click",
1539 "a tile in the button bar, you can find out what its keyboard shortcut",
1540 "is. The three buttons FGD, BGD and EMY let you pick from foreground,",
1541 "background, and enemy tiles. The eraser lets you remove tiles.",
1542 "The left and right arrow keys scroll back and forth through your level.",
1543 "The button with the wrench and screwdriver, lets you change the",
1544 "settings of your level, including how long it is or what music it will",
1545 "play. When you are ready to give your level a test, click on the little",
1546 "running Tux. If you like the changes you have made to your level,",
1547 "press the red save key to keep them.",
1548 "To change which level in your subset you are editing, press the white",
1549 "up and down arrow keys at the top of the button box.",
1551 "Have fun making levels! If you make some good ones, send them to us on",
1552 "the SuperTux mailing list!",
1557 blue_text->drawf("- Help -", 0, 30, A_HMIDDLE, A_TOP, 2);
1559 for(i = 0; i < sizeof(text)/sizeof(char *); i++)
1560 white_small_text->draw(text[i], 5, 80+(i*white_small_text->h), 1);
1562 gold_text->drawf("Press Any Key to Continue", 0, 440, A_HMIDDLE, A_TOP, 1);
1570 done_ = wait_for_event(event);