4 // Copyright (C) 2003 Ricardo Cruz <rick2@aeiou.pt>
5 // Copyright (C) 2003 Tobias Glaesser <tobi.web@gmx.de>
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 #include <SDL_image.h>
30 #include "leveleditor.h"
43 #include "resources.h"
44 #include "music_manager.h"
46 /* definitions to aid development */
48 /* definitions that affect gameplay */
49 #define KEY_CURSOR_SPEED 32
50 #define KEY_CURSOR_FASTSPEED 64
52 /* when pagedown/up pressed speed:*/
53 #define PAGE_CURSOR_SPEED 13*32
55 #define MOUSE_LEFT_MARGIN 80
56 #define MOUSE_RIGHT_MARGIN (560-32)
57 /* right_margin should noticed that the cursor is 32 pixels,
58 so it should subtract that value */
59 #define MOUSE_POS_SPEED 20
62 #define SELECT_W 2 // size of the selections lines
63 #define SELECT_CLR 0, 255, 0, 255 // lines color (R, G, B, A)
65 /* own declerations */
66 /* crutial ones (main loop) */
69 int le_load_level(char *filename);
71 void le_drawinterface();
72 void le_checkevents();
73 void le_change(float x, float y, int tm, unsigned int c);
76 void le_set_defaults(void);
77 void le_activate_bad_guys(void);
79 void le_highlight_selection();
81 void apply_level_settings_menu();
82 void update_subset_settings_menu();
83 void save_subset_settings_menu();
85 /*static Level* le_current_level;*/
87 /*struct LevelEditorWorld
89 std::vector<BadGuy> bad_guys;
90 void arrays_free(void)
95 void add_bad_guy(float x, float y, BadGuyKind kind)
97 bad_guys.push_back(BadGuy(x,y,kind, false /* stay_on_platform * /));
100 void activate_bad_guys()
102 for (std::vector<BadGuyData>::iterator i = le_current_level->badguy_data.begin();
103 i != le_current_level->badguy_data.end();
106 add_bad_guy(i->x, i->y, i->kind);
113 TileOrObject() : tile(0), obj(NULL) { is_tile = true; };
115 void Tile(unsigned int set_to) { tile = set_to; is_tile = true; }
116 void Object(GameObject* pobj) { obj = pobj; is_tile = false; }
117 //Returns true for a tile
118 bool IsTile() { return is_tile; };
119 //Returns true for a GameObject
120 bool IsObject() { return !is_tile; };
121 void Init() { tile = 0; obj = NULL; is_tile = true; };
123 bool is_tile; //true for tile (false for object)
128 /* leveleditor internals */
129 static string_list_type level_subsets;
130 static bool le_level_changed; /* if changes, ask for saving, when quiting*/
131 static bool show_minimap;
132 static int pos_x, cursor_x, cursor_y, fire;
134 static World* le_world;
135 static LevelSubset* le_level_subset;
136 static int le_show_grid;
138 static Surface* le_selection;
140 static TileOrObject le_current;
141 static bool le_mouse_pressed[2];
142 static bool le_mouse_clicked[2];
143 static Button* le_save_level_bt;
144 static Button* le_exit_bt;
145 static Button* le_test_level_bt;
146 static Button* le_next_level_bt;
147 static Button* le_previous_level_bt;
148 static Button* le_move_right_bt;
149 static Button* le_move_left_bt;
150 static Button* le_rubber_bt;
151 static Button* le_select_mode_one_bt;
152 static Button* le_select_mode_two_bt;
153 static Button* le_settings_bt;
154 static Button* le_tilegroup_bt;
155 static Button* le_objects_bt;
156 static ButtonPanel* le_tilemap_panel;
157 static Menu* leveleditor_menu;
158 static Menu* subset_load_menu;
159 static Menu* subset_new_menu;
160 static Menu* subset_settings_menu;
161 static Menu* level_settings_menu;
162 static Menu* select_tilegroup_menu;
163 static Menu* select_objects_menu;
164 static Timer select_tilegroup_menu_effect;
165 static Timer select_objects_menu_effect;
166 typedef std::map<std::string, ButtonPanel*> ButtonPanelMap;
167 static ButtonPanelMap tilegroups_map;
168 static ButtonPanelMap objects_map;
169 static std::string cur_tilegroup;
170 static std::string cur_objects;
172 static square selection;
173 static int le_selection_mode;
174 static SDL_Event event;
175 TileMapType active_tm;
177 int leveleditor(char* filename)
179 int last_time, now_time, i;
188 clearscreen(0, 0, 0);
191 music_manager->halt_music();
193 while (SDL_PollEvent(&event))
197 if(le_load_level(filename))
202 last_time = SDL_GetTicks();
207 if(Menu::current() == select_tilegroup_menu)
209 if(select_tilegroup_menu_effect.check())
211 select_tilegroup_menu->set_pos(screen->w - 64 + select_tilegroup_menu_effect.get_left(),
215 select_tilegroup_menu->set_pos(screen->w - 64,66,-0.5,0.5);
217 else if(Menu::current() == select_objects_menu)
219 if(select_objects_menu_effect.check())
221 select_objects_menu->set_pos(screen->w - 64 + select_objects_menu_effect.get_left(),82,-0.5,0.5);
224 select_objects_menu->set_pos(screen->w - 64,82,-0.5,0.5);
229 /* making events results to be in order */
232 if(pos_x > (le_world->get_level()->width * 32) - screen->w)
233 pos_x = (le_world->get_level()->width * 32) - screen->w;
239 clearscreen(0, 0, 0);
241 /* draw editor interface */
244 Menu* menu = Menu::current();
250 if(menu == leveleditor_menu)
252 switch (leveleditor_menu->check())
254 case MNID_RETURNLEVELEDITOR:
255 Menu::set_current(0);
257 case MNID_SUBSETSETTINGS:
258 update_subset_settings_menu();
260 case MNID_QUITLEVELEDITOR:
265 else if(menu == level_settings_menu)
267 switch (level_settings_menu->check())
270 apply_level_settings_menu();
271 Menu::set_current(NULL);
279 else if(menu == select_tilegroup_menu)
282 switch (it = select_tilegroup_menu->check())
288 = select_tilegroup_menu->get_item_by_id(it).text;
289 Menu::set_current(0);
296 else if(menu == select_objects_menu)
299 switch (it = select_objects_menu->check())
304 cur_objects = select_objects_menu->get_item_by_id(it).text;
307 Menu::set_current(0);
312 else if(menu == subset_load_menu)
314 switch (i = subset_load_menu->check())
321 if(le_load_level(level_subsets.item[i-1]))
327 else if(menu == subset_new_menu)
329 if(subset_new_menu->item[2].input[0] == '\0')
330 subset_new_menu->item[3].kind = MN_DEACTIVE;
333 subset_new_menu->item[3].kind = MN_ACTION;
335 switch (i = subset_new_menu->check())
337 case MNID_CREATESUBSET:
338 LevelSubset::create(subset_new_menu->get_item_by_id(MNID_SUBSETNAME).input);
339 le_level_subset->load(subset_new_menu->get_item_by_id(MNID_SUBSETNAME).input);
340 leveleditor_menu->get_item_by_id(MNID_SUBSETSETTINGS).kind = MN_GOTO;
343 le_world = new World;
344 subset_new_menu->get_item_by_id(MNID_SUBSETNAME).change_input("");
346 Menu::set_current(subset_settings_menu);
351 else if(menu == subset_settings_menu)
353 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 )
354 subset_settings_menu->get_item_by_id(MNID_SUBSETSAVECHANGES).kind = MN_DEACTIVE;
356 subset_settings_menu->get_item_by_id(MNID_SUBSETSAVECHANGES).kind = MN_ACTION;
358 switch (i = subset_settings_menu->check())
360 case MNID_SUBSETSAVECHANGES:
361 save_subset_settings_menu();
362 Menu::set_current(leveleditor_menu);
368 mouse_cursor->draw();
376 ++global_frame_counter;
379 now_time = SDL_GetTicks();
380 if (now_time < last_time + FPS)
381 SDL_Delay(last_time + FPS - now_time); /* delay some time */
389 int le_load_level(char *filename)
391 le_level_subset->load(filename);
392 leveleditor_menu->get_item_by_id(MNID_SUBSETSETTINGS).kind = MN_GOTO;
395 le_world = new World(filename,le_level);
397 //GameSession* session = new GameSession(datadir + "/levels/" + le_level_subset->name + "/level1.stl", 0, ST_GL_DEMO_GAME);
399 Menu::set_current(NULL);
408 leveleditor_menu = new Menu();
409 subset_load_menu = new Menu();
410 subset_new_menu = new Menu();
411 subset_settings_menu = new Menu();
412 level_settings_menu = new Menu();
413 select_tilegroup_menu = new Menu();
414 select_objects_menu = new Menu();
416 leveleditor_menu->additem(MN_LABEL,"Level Editor Menu",0,0);
417 leveleditor_menu->additem(MN_HL,"",0,0);
418 leveleditor_menu->additem(MN_ACTION,"Return To Level Editor",0,0,MNID_RETURNLEVELEDITOR);
419 leveleditor_menu->additem(MN_DEACTIVE,"Level Subset Settings",0,subset_settings_menu,MNID_SUBSETSETTINGS);
420 leveleditor_menu->additem(MN_GOTO,"Load Level Subset",0,subset_load_menu);
421 leveleditor_menu->additem(MN_GOTO,"New Level Subset",0,subset_new_menu);
422 leveleditor_menu->additem(MN_HL,"",0,0);
423 leveleditor_menu->additem(MN_ACTION,"Quit Level Editor",0,0,MNID_QUITLEVELEDITOR);
425 Menu::set_current(leveleditor_menu);
427 subset_load_menu->additem(MN_LABEL, "Load Level Subset", 0, 0);
428 subset_load_menu->additem(MN_HL, "", 0, 0);
430 for(i = 0; i < level_subsets.num_items; ++i)
432 subset_load_menu->additem(MN_ACTION,level_subsets.item[i],0,0, i+1);
434 subset_load_menu->additem(MN_HL,"",0,0);
435 subset_load_menu->additem(MN_BACK,"Back",0,0);
437 subset_new_menu->additem(MN_LABEL,"New Level Subset",0,0);
438 subset_new_menu->additem(MN_HL,"",0,0);
439 subset_new_menu->additem(MN_TEXTFIELD,"Enter Name",0,0,MNID_SUBSETNAME);
440 subset_new_menu->additem(MN_ACTION,"Create",0,0, MNID_CREATESUBSET);
441 subset_new_menu->additem(MN_HL,"",0,0);
442 subset_new_menu->additem(MN_BACK,"Back",0,0);
444 subset_settings_menu->additem(MN_LABEL,"Level Subset Settings",0,0);
445 subset_settings_menu->additem(MN_HL,"",0,0);
446 subset_settings_menu->additem(MN_TEXTFIELD,"Title",0,0,MNID_SUBSETTITLE);
447 subset_settings_menu->additem(MN_TEXTFIELD,"Description",0,0,MNID_SUBSETDESCRIPTION);
448 subset_settings_menu->additem(MN_HL,"",0,0);
449 subset_settings_menu->additem(MN_ACTION,"Save Changes",0,0,MNID_SUBSETSAVECHANGES);
450 subset_settings_menu->additem(MN_HL,"",0,0);
451 subset_settings_menu->additem(MN_BACK,"Back",0,0);
453 level_settings_menu->arrange_left = true;
454 level_settings_menu->additem(MN_LABEL,"Level Settings",0,0);
455 level_settings_menu->additem(MN_HL,"",0,0);
456 level_settings_menu->additem(MN_TEXTFIELD,"Name ",0,0,MNID_NAME);
457 level_settings_menu->additem(MN_TEXTFIELD,"Author ",0,0,MNID_AUTHOR);
458 level_settings_menu->additem(MN_STRINGSELECT,"Song ",0,0,MNID_SONG);
459 level_settings_menu->additem(MN_STRINGSELECT,"Bg-Image",0,0,MNID_BGIMG);
460 level_settings_menu->additem(MN_STRINGSELECT,"Particle",0,0,MNID_PARTICLE);
461 level_settings_menu->additem(MN_NUMFIELD,"Length ",0,0,MNID_LENGTH);
462 level_settings_menu->additem(MN_NUMFIELD,"Time ",0,0,MNID_TIME);
463 level_settings_menu->additem(MN_NUMFIELD,"Gravity",0,0,MNID_GRAVITY);
464 level_settings_menu->additem(MN_NUMFIELD,"Bg-Img-Speed",0,0,MNID_BGSPEED);
465 level_settings_menu->additem(MN_NUMFIELD,"Top Red ",0,0,MNID_TopRed);
466 level_settings_menu->additem(MN_NUMFIELD,"Top Green ",0,0,MNID_TopGreen);
467 level_settings_menu->additem(MN_NUMFIELD,"Top Blue ",0,0,MNID_TopBlue);
468 level_settings_menu->additem(MN_NUMFIELD,"Bottom Red ",0,0,MNID_BottomRed);
469 level_settings_menu->additem(MN_NUMFIELD,"Bottom Green",0,0,MNID_BottomGreen);
470 level_settings_menu->additem(MN_NUMFIELD,"Bottom Blue",0,0,MNID_BottomBlue);
471 level_settings_menu->additem(MN_HL,"",0,0);
472 level_settings_menu->additem(MN_ACTION,"Apply Changes",0,0,MNID_APPLY);
474 select_tilegroup_menu->arrange_left = true;
475 select_tilegroup_menu->additem(MN_LABEL,"Tilegroup",0,0);
476 select_tilegroup_menu->additem(MN_HL,"",0,0);
477 std::set<TileGroup>* tilegroups = TileManager::tilegroups();
479 for(std::set<TileGroup>::iterator it = tilegroups->begin();
480 it != tilegroups->end(); ++it )
482 select_tilegroup_menu->additem(MN_ACTION, it->name, 0, 0, tileid);
484 tilegroups_map[(*it).name] = new ButtonPanel(screen->w - 64,96, 64, 318);
487 for(std::vector<int>::const_iterator sit = (*it).tiles.begin();
488 sit != (*it).tiles.end(); ++sit, ++i)
490 std::string imagefile = "/images/tilesets/" ;
491 bool only_editor_image = false;
492 if(!TileManager::instance()->get(*sit)->filenames.empty())
494 imagefile += TileManager::instance()->get(*sit)->filenames[0];
496 else if(!TileManager::instance()->get(*sit)->editor_filenames.empty())
498 imagefile += TileManager::instance()->get(*sit)->editor_filenames[0];
499 only_editor_image = true;
503 imagefile += "notile.png";
505 Button* button = new Button(imagefile, it->name, SDLKey(SDLK_a + i),
507 if(!only_editor_image)
508 if(!TileManager::instance()->get(*sit)->editor_filenames.empty())
510 imagefile = "/images/tilesets/" + TileManager::instance()->get(*sit)->editor_filenames[0];
511 button->add_icon(imagefile,32,32);
513 tilegroups_map[it->name]->additem(button, *sit);
516 select_tilegroup_menu->additem(MN_HL,"",0,0);
518 select_objects_menu->arrange_left = true;
519 select_objects_menu->additem(MN_LABEL,"Objects",0,0);
520 select_objects_menu->additem(MN_HL,"",0,0);
521 select_objects_menu->additem(MN_ACTION,"BadGuys",0,0,1);
522 objects_map["BadGuys"] = new ButtonPanel(screen->w - 64,96, 64, 318);
524 for(int i = 0; i < NUM_BadGuyKinds; ++i)
526 BadGuy bad_tmp(0,0,BadGuyKind(i),false);
527 objects_map["BadGuys"]->additem(new Button("", "BadGuy",(SDLKey)(i+'a'),0,0,32,32),1000000+i);
528 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));
531 select_objects_menu->additem(MN_HL,"",0,0);
539 level_subsets = dsubdirs("/levels", "info");
540 le_level_subset = new LevelSubset;
550 le_frame = 0; /* support for frames in some tiles, like waves and bad guys */
551 le_level_changed = false;
553 le_mouse_pressed[LEFT] = false;
554 le_mouse_pressed[RIGHT] = false;
556 le_mouse_clicked[LEFT] = false;
557 le_mouse_clicked[RIGHT] = false;
559 le_selection = new Surface(datadir + "/images/leveleditor/select.png", USE_ALPHA);
561 select_tilegroup_menu_effect.init(false);
562 select_objects_menu_effect.init(false);
565 le_save_level_bt = new Button("/images/icons/save.png","Save level", SDLK_F6,screen->w-64,32);
566 le_exit_bt = new Button("/images/icons/exit.png","Exit", SDLK_F6,screen->w-32,32);
567 le_next_level_bt = new Button("/images/icons/up.png","Next level", SDLK_PAGEUP,screen->w-64,0);
568 le_previous_level_bt = new Button("/images/icons/down.png","Previous level",SDLK_PAGEDOWN,screen->w-32,0);
569 le_rubber_bt = new Button("/images/icons/rubber.png","Rubber",SDLK_DELETE,screen->w-32,48);
570 le_select_mode_one_bt = new Button ("/images/icons/select-mode1.png","Select single tile",SDLK_F3,screen->w-64,48);
571 le_select_mode_two_bt = new Button("/images/icons/select-mode2.png","Select multiple tiles",SDLK_F3,screen->w-64,48);
572 le_test_level_bt = new Button("/images/icons/test-level.png","Test level",SDLK_F4,screen->w-64,screen->h - 64);
573 le_settings_bt = new Button("/images/icons/settings.png","Level settings",SDLK_F5,screen->w-32,screen->h - 64);
574 le_move_left_bt = new Button("/images/icons/left.png","Move left",SDLK_LEFT,0,0);
575 le_move_right_bt = new Button("/images/icons/right.png","Move right",SDLK_RIGHT,screen->w-80,0);
576 le_tilegroup_bt = new Button("/images/icons/tilegroup.png","Select Tilegroup", SDLK_F7,screen->w-64,64);
577 le_objects_bt = new Button("/images/icons/objects.png","Select Objects", SDLK_F7,screen->w-64,80);
579 le_tilemap_panel = new ButtonPanel(screen->w-64,screen->h-32,32,32);
580 le_tilemap_panel->set_button_size(32,10);
581 le_tilemap_panel->additem(new Button("/images/icons/bkgrd.png","Background",SDLK_b,0,0),TM_BG);
582 le_tilemap_panel->additem(new Button("/images/icons/intact.png","Interactive",SDLK_i,0,0),TM_IA);
583 le_tilemap_panel->additem(new Button("/images/icons/frgrd.png","Foreground",SDLK_f,0,0),TM_FG);
584 le_tilemap_panel->highlight_last(true);
590 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
596 void update_level_settings_menu()
601 level_settings_menu->get_item_by_id(MNID_NAME).change_input(le_world->get_level()->name.c_str());
602 level_settings_menu->get_item_by_id(MNID_AUTHOR).change_input(le_world->get_level()->author.c_str());
604 string_list_copy(level_settings_menu->get_item_by_id(MNID_SONG).list, dfiles("music/",NULL, "-fast"));
605 string_list_copy(level_settings_menu->get_item_by_id(MNID_BGIMG).list, dfiles("images/background",NULL, NULL));
606 string_list_add_item(level_settings_menu->get_item_by_id(MNID_BGIMG).list,"");
607 string_list_add_item(level_settings_menu->get_item_by_id(MNID_PARTICLE).list,"");
608 string_list_add_item(level_settings_menu->get_item_by_id(MNID_PARTICLE).list,"snow");
609 string_list_add_item(level_settings_menu->get_item_by_id(MNID_PARTICLE).list,"clouds");
611 if((i = string_list_find(level_settings_menu->get_item_by_id(MNID_SONG).list,le_world->get_level()->song_title.c_str())) != -1)
612 level_settings_menu->get_item_by_id(MNID_SONG).list->active_item = i;
613 if((i = string_list_find(level_settings_menu->get_item_by_id(MNID_BGIMG).list,le_world->get_level()->bkgd_image.c_str())) != -1)
614 level_settings_menu->get_item_by_id(MNID_BGIMG).list->active_item = i;
615 if((i = string_list_find(level_settings_menu->get_item_by_id(MNID_PARTICLE).list,le_world->get_level()->particle_system.c_str())) != -1)
616 level_settings_menu->get_item_by_id(MNID_PARTICLE).list->active_item = i;
618 sprintf(str,"%d",le_world->get_level()->width);
619 level_settings_menu->get_item_by_id(MNID_LENGTH).change_input(str);
620 sprintf(str,"%d",le_world->get_level()->time_left);
621 level_settings_menu->get_item_by_id(MNID_TIME).change_input(str);
622 sprintf(str,"%2.0f",le_world->get_level()->gravity);
623 level_settings_menu->get_item_by_id(MNID_GRAVITY).change_input(str);
624 sprintf(str,"%d",le_world->get_level()->bkgd_speed);
625 level_settings_menu->get_item_by_id(MNID_BGSPEED).change_input(str);
626 sprintf(str,"%d",le_world->get_level()->bkgd_top.red);
627 level_settings_menu->get_item_by_id(MNID_TopRed).change_input(str);
628 sprintf(str,"%d",le_world->get_level()->bkgd_top.green);
629 level_settings_menu->get_item_by_id(MNID_TopGreen).change_input(str);
630 sprintf(str,"%d",le_world->get_level()->bkgd_top.blue);
631 level_settings_menu->get_item_by_id(MNID_TopBlue).change_input(str);
632 sprintf(str,"%d",le_world->get_level()->bkgd_bottom.red);
633 level_settings_menu->get_item_by_id(MNID_BottomRed).change_input(str);
634 sprintf(str,"%d",le_world->get_level()->bkgd_bottom.green);
635 level_settings_menu->get_item_by_id(MNID_BottomGreen).change_input(str);
636 sprintf(str,"%d",le_world->get_level()->bkgd_bottom.blue);
637 level_settings_menu->get_item_by_id(MNID_BottomBlue).change_input(str);
640 void update_subset_settings_menu()
642 subset_settings_menu->item[2].change_input(le_level_subset->title.c_str());
643 subset_settings_menu->item[3].change_input(le_level_subset->description.c_str());
646 void apply_level_settings_menu()
651 le_world->get_level()->name = level_settings_menu->get_item_by_id(MNID_NAME).input;
652 le_world->get_level()->author = level_settings_menu->get_item_by_id(MNID_AUTHOR).input;
654 if(le_world->get_level()->bkgd_image.compare(string_list_active(level_settings_menu->get_item_by_id(MNID_BGIMG).list)) != 0)
656 le_world->get_level()->bkgd_image = string_list_active(level_settings_menu->get_item_by_id(MNID_BGIMG).list);
660 if(le_world->get_level()->particle_system.compare(string_list_active(level_settings_menu->get_item_by_id(MNID_PARTICLE).list)) != 0)
662 le_world->get_level()->particle_system = string_list_active(level_settings_menu->get_item_by_id(MNID_PARTICLE).list);
667 le_world->get_level()->load_gfx();
670 le_world->get_level()->song_title = string_list_active(level_settings_menu->get_item_by_id(MNID_SONG).list);
672 le_world->get_level()->change_size(atoi(level_settings_menu->get_item_by_id(MNID_LENGTH).input));
673 le_world->get_level()->time_left = atoi(level_settings_menu->get_item_by_id(MNID_BGIMG).input);
674 le_world->get_level()->gravity = atof(level_settings_menu->get_item_by_id(MNID_GRAVITY).input);
675 le_world->get_level()->bkgd_speed = atoi(level_settings_menu->get_item_by_id(MNID_BGSPEED).input);
676 le_world->get_level()->bkgd_top.red = atoi(level_settings_menu->get_item_by_id(MNID_TopRed).input);
677 le_world->get_level()->bkgd_top.green = atoi(level_settings_menu->get_item_by_id(MNID_TopGreen).input);
678 le_world->get_level()->bkgd_top.blue = atoi(level_settings_menu->get_item_by_id(MNID_TopBlue).input);
679 le_world->get_level()->bkgd_bottom.red = atoi(level_settings_menu->get_item_by_id(MNID_BottomRed).input);
680 le_world->get_level()->bkgd_bottom.green = atoi(level_settings_menu->get_item_by_id(MNID_BottomGreen).input);
681 le_world->get_level()->bkgd_bottom.blue = atoi(level_settings_menu->get_item_by_id(MNID_BottomBlue).input);
684 void save_subset_settings_menu()
686 le_level_subset->title = subset_settings_menu->item[2].input;
687 le_level_subset->description = subset_settings_menu->item[3].input;
688 le_level_subset->save();
691 void le_goto_level(int levelnb)
694 le_world = new World(le_level_subset->name, levelnb);
699 /*if(level_changed == true)
700 if(askforsaving() == CANCEL)
703 SDL_EnableKeyRepeat(0, 0); // disables key repeating
706 delete leveleditor_menu;
707 delete subset_load_menu;
708 delete subset_new_menu;
709 delete subset_settings_menu;
710 delete level_settings_menu;
711 delete select_tilegroup_menu;
712 delete select_objects_menu;
713 delete le_save_level_bt;
715 delete le_test_level_bt;
716 delete le_next_level_bt;
717 delete le_previous_level_bt;
718 delete le_move_right_bt;
719 delete le_move_left_bt;
721 delete le_select_mode_one_bt;
722 delete le_select_mode_two_bt;
723 delete le_settings_bt;
724 delete le_tilegroup_bt;
725 delete le_objects_bt;
726 delete le_tilemap_panel;
728 delete le_level_subset;
731 for(ButtonPanelMap::iterator i = tilegroups_map.begin();
732 i != tilegroups_map.end(); ++i)
736 for(ButtonPanelMap::iterator i = objects_map.begin();
737 i != objects_map.end(); ++i)
743 void le_drawminimap()
749 if(screen->w - 64 > le_world->get_level()->width * 4)
751 else if(screen->w - 64 > le_world->get_level()->width * 2)
755 int left_offset = (screen->w - 64 - le_world->get_level()->width*mini_tile_width) / 2;
757 for (int y = 0; y < 15; ++y)
758 for (int x = 0; x < le_world->get_level()->width; ++x)
761 Tile::draw_stretched(left_offset + mini_tile_width*x, y * 4, mini_tile_width , 4, le_world->get_level()->bg_tiles[y][x]);
763 Tile::draw_stretched(left_offset + mini_tile_width*x, y * 4, mini_tile_width , 4, le_world->get_level()->ia_tiles[y][x]);
765 Tile::draw_stretched(left_offset + mini_tile_width*x, y * 4, mini_tile_width , 4, le_world->get_level()->fg_tiles[y][x]);
769 fillrect(left_offset, 0, le_world->get_level()->width*mini_tile_width, 15*4, 200, 200, 200, 128);
771 fillrect(left_offset + (pos_x/32)*mini_tile_width, 0, 19*mini_tile_width, 2, 200, 200, 200, 200);
772 fillrect(left_offset + (pos_x/32)*mini_tile_width, 0, 2, 15*4, 200, 200, 200, 200);
773 fillrect(left_offset + (pos_x/32)*mini_tile_width + 19*mini_tile_width - 2, 0, 2, 15*4, 200, 200, 200, 200);
774 fillrect(left_offset + (pos_x/32)*mini_tile_width, 15*4-2, 19*mini_tile_width, 2, 200, 200, 200, 200);
778 void le_drawinterface()
785 /* draw a grid (if selected) */
788 for(x = 0; x < 19; x++)
789 fillrect(x*32 - ((int)pos_x % 32), 0, 1, screen->h, 225, 225, 225,255);
790 for(y = 0; y < 15; y++)
791 fillrect(0, y*32, screen->w - 32, 1, 225, 225, 225,255);
795 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
798 if(le_selection_mode == CURSOR)
799 if(le_current.IsTile())
800 le_selection->draw( cursor_x - pos_x, cursor_y);
802 le_selection->draw( cursor_x, cursor_y);
803 else if(le_selection_mode == SQUARE)
806 le_highlight_selection();
807 /* draw current selection */
808 w = selection.x2 - selection.x1;
809 h = selection.y2 - selection.y1;
810 fillrect(selection.x1 - pos_x, selection.y1, w, SELECT_W, SELECT_CLR);
811 fillrect(selection.x1 - pos_x + w, selection.y1, SELECT_W, h, SELECT_CLR);
812 fillrect(selection.x1 - pos_x, selection.y1 + h, w, SELECT_W, SELECT_CLR);
813 fillrect(selection.x1 - pos_x, selection.y1, SELECT_W, h, SELECT_CLR);
817 /* draw button bar */
818 fillrect(screen->w - 64, 0, 64, screen->h, 50, 50, 50,255);
820 if(le_current.IsTile())
822 Tile::draw(19 * 32, 14 * 32, le_current.tile);
823 if(TileManager::instance()->get(le_current.tile)->editor_images.size() > 0)
824 TileManager::instance()->get(le_current.tile)->editor_images[0]->draw( 19 * 32, 14 * 32);
826 if(le_current.IsObject())
828 le_current.obj->draw_on_screen(19 * 32, 14 * 32);
829 le_current.obj->draw_on_screen(cursor_x,cursor_y);
834 le_save_level_bt->draw();
836 le_test_level_bt->draw();
837 le_next_level_bt->draw();
838 le_previous_level_bt->draw();
839 le_rubber_bt->draw();
840 if(le_selection_mode == SQUARE)
841 le_select_mode_one_bt->draw();
842 else if(le_selection_mode == CURSOR)
843 le_select_mode_two_bt->draw();
844 le_settings_bt->draw();
845 le_move_right_bt->draw();
846 le_move_left_bt->draw();
847 le_tilegroup_bt->draw();
848 le_objects_bt->draw();
849 if(!cur_tilegroup.empty())
850 tilegroups_map[cur_tilegroup]->draw();
851 else if(!cur_objects.empty())
853 objects_map[cur_objects]->draw();
856 le_tilemap_panel->draw();
858 sprintf(str, "%d/%d", le_level,le_level_subset->levels);
859 white_text->drawf(str, -10, 16, A_RIGHT, A_TOP, 0);
861 white_small_text->draw("F1 for Help", 10, 430, 1);
866 white_small_text->draw("No Level Subset loaded - Press ESC and choose one in the menu", 10, 430, 1);
868 white_small_text->draw("No Level Subset loaded", 10, 430, 1);
878 /* Draw the real background */
879 if(le_world->get_level()->bkgd_image[0] != '\0')
881 s = (int)((float)pos_x * ((float)le_world->get_level()->bkgd_speed/60.)) % screen->w;
882 le_world->get_level()->img_bkgd->draw_part(s,0,0,0,
883 le_world->get_level()->img_bkgd->w - s - 32, le_world->get_level()->img_bkgd->h);
884 le_world->get_level()->img_bkgd->draw_part(0,0,screen->w - s - 32 ,0,s,
885 le_world->get_level()->img_bkgd->h);
889 drawgradient(le_world->get_level()->bkgd_top, le_world->get_level()->bkgd_bottom);
892 if(le_current.IsTile())
894 Tile::draw(cursor_x-pos_x, cursor_y,le_current.tile,128);
895 if(!TileManager::instance()->get(le_current.tile)->images.empty())
896 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);
898 if(le_current.IsObject())
900 le_current.obj->move_to(cursor_x, cursor_y);
903 /* clearscreen(current_level.bkgd_red, current_level.bkgd_green, current_level.bkgd_blue); */
905 for (y = 0; y < 15; ++y)
906 for (x = 0; x < 20; ++x)
909 if(active_tm == TM_BG)
914 Tile::draw(32*x - fmodf(pos_x, 32), y * 32, le_world->get_level()->bg_tiles[y][x + (int)(pos_x / 32)],a);
916 if(active_tm == TM_IA)
921 Tile::draw(32*x - fmodf(pos_x, 32), y * 32, le_world->get_level()->ia_tiles[y][x + (int)(pos_x / 32)],a);
923 if(active_tm == TM_FG)
928 Tile::draw(32*x - fmodf(pos_x, 32), y * 32, le_world->get_level()->fg_tiles[y][x + (int)(pos_x / 32)],a);
930 /* draw whats inside stuff when cursor is selecting those */
931 /* (draw them all the time - is this the right behaviour?) */
932 if(TileManager::instance()->get(le_world->get_level()->ia_tiles[y][x + (int)(pos_x / 32)])->editor_images.size() > 0)
933 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);
937 /* Draw the Bad guys: */
938 for (std::list<BadGuy*>::iterator it = le_world->bad_guys.begin(); it != le_world->bad_guys.end(); ++it)
940 /* to support frames: img_bsod_left[(frame / 5) % 4] */
947 /* Draw the player: */
948 /* for now, the position is fixed at (100, 240) */
949 largetux.walk_right->draw( 100 - pos_x, 240);
952 void le_change_object_properties(GameObject *pobj)
954 Menu* object_properties_menu = new Menu();
956 object_properties_menu->additem(MN_LABEL,pobj->type() + " Properties",0,0);
957 object_properties_menu->additem(MN_HL,"",0,0);
958 /*object_properties_menu->additem(MN_TEXTFIELD,"Title",0,0,MNID_SUBSETTITLE);
959 object_properties_menu->additem(MN_TEXTFIELD,"Description",0,0,MNID_SUBSETDESCRIPTION);
960 object_properties_menu->additem(MN_HL,"",0,0);
961 object_properties_menu->additem(MN_ACTION,"Save Changes",0,0,MNID_SUBSETSAVECHANGES);*/
962 object_properties_menu->additem(MN_HL,"",0,0);
963 object_properties_menu->additem(MN_BACK,"Apply",0,0);
965 delete object_properties_menu;
969 void le_checkevents()
976 keymod = SDL_GetModState();
978 while(SDL_PollEvent(&event))
982 Menu::current()->event(event);
986 mouse_cursor->set_state(MC_NORMAL);
988 /* testing SDL_KEYDOWN, SDL_KEYUP and SDL_QUIT events*/
989 if(event.type == SDL_KEYDOWN
990 || ((event.type == SDL_MOUSEBUTTONDOWN || SDL_MOUSEMOTION)
991 && (event.motion.x > 0
992 && event.motion.x < screen->w - 64 &&
993 event.motion.y > 0 && event.motion.y < screen->h)))
997 case SDL_KEYDOWN: // key pressed
998 key = event.key.keysym.sym;
1002 Menu::set_current(leveleditor_menu);
1005 cursor_x -= KEY_CURSOR_SPEED;
1007 cursor_x -= KEY_CURSOR_FASTSPEED;
1009 if(cursor_x < pos_x + MOUSE_LEFT_MARGIN)
1010 pos_x = cursor_x - MOUSE_LEFT_MARGIN;
1015 cursor_x += KEY_CURSOR_SPEED;
1017 cursor_x += KEY_CURSOR_FASTSPEED;
1019 if(cursor_x > pos_x + MOUSE_RIGHT_MARGIN-32)
1020 pos_x = cursor_x - MOUSE_RIGHT_MARGIN+32;
1025 cursor_y -= KEY_CURSOR_SPEED;
1027 cursor_y -= KEY_CURSOR_FASTSPEED;
1034 cursor_y += KEY_CURSOR_SPEED;
1036 cursor_y += KEY_CURSOR_FASTSPEED;
1038 if(cursor_y > screen->h-32)
1039 cursor_y = screen->h-32;
1052 cursor_x = (le_world->get_level()->width * 32) - 32;
1056 le_show_grid = !le_show_grid;
1062 case SDL_KEYUP: /* key released */
1063 switch(event.key.keysym.sym)
1072 case SDL_MOUSEBUTTONDOWN:
1073 if(event.button.button == SDL_BUTTON_LEFT)
1075 le_mouse_pressed[LEFT] = true;
1077 selection.x1 = event.motion.x + pos_x;
1078 selection.y1 = event.motion.y;
1079 selection.x2 = event.motion.x + pos_x;
1080 selection.y2 = event.motion.y;
1082 else if(event.button.button == SDL_BUTTON_RIGHT)
1084 le_mouse_pressed[RIGHT] = true;
1087 case SDL_MOUSEBUTTONUP:
1088 if(event.button.button == SDL_BUTTON_LEFT)
1090 le_mouse_pressed[LEFT] = false;
1091 le_mouse_clicked[LEFT] = true;
1093 else if(event.button.button == SDL_BUTTON_RIGHT)
1095 le_mouse_pressed[RIGHT] = false;
1096 le_mouse_clicked[RIGHT] = true;
1099 case SDL_MOUSEMOTION:
1101 if(!Menu::current())
1106 if(le_current.IsTile())
1108 cursor_x = ((int)(pos_x + x) / 32) * 32;
1109 cursor_y = ((int) y / 32) * 32;
1117 if(le_mouse_pressed[LEFT])
1119 selection.x2 = x + pos_x;
1123 if(le_mouse_pressed[RIGHT])
1125 pos_x += -1 * event.motion.xrel;
1129 case SDL_QUIT: // window closed
1138 if(le_world != NULL)
1140 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 &&
1141 event.motion.y > 0 && event.motion.y < screen->h)))
1143 le_mouse_pressed[LEFT] = false;
1144 le_mouse_pressed[RIGHT] = false;
1146 if(!Menu::current())
1148 /* Check for button events */
1149 le_test_level_bt->event(event);
1150 if(le_test_level_bt->get_state() == BUTTON_CLICKED)
1152 le_save_level_bt->event(event);
1153 if(le_save_level_bt->get_state() == BUTTON_CLICKED)
1154 le_world->get_level()->save(le_level_subset->name.c_str(),le_level);
1155 le_exit_bt->event(event);
1156 if(le_exit_bt->get_state() == BUTTON_CLICKED)
1158 Menu::set_current(leveleditor_menu);
1160 le_next_level_bt->event(event);
1161 if(le_next_level_bt->get_state() == BUTTON_CLICKED)
1163 if(le_level < le_level_subset->levels)
1165 le_goto_level(++le_level);
1171 sprintf(str,"Level %d doesn't exist. Create it?",le_level+1);
1172 if(confirm_dialog(str))
1174 new_lev.init_defaults();
1175 new_lev.save(le_level_subset->name.c_str(),++le_level);
1176 le_level_subset->levels = le_level;
1177 le_goto_level(le_level);
1181 le_previous_level_bt->event(event);
1182 if(le_previous_level_bt->get_state() == BUTTON_CLICKED)
1185 le_goto_level(--le_level);
1187 le_rubber_bt->event(event);
1188 if(le_rubber_bt->get_state() == BUTTON_CLICKED)
1191 if(le_selection_mode == SQUARE)
1193 le_select_mode_one_bt->event(event);
1194 if(le_select_mode_one_bt->get_state() == BUTTON_CLICKED)
1195 le_selection_mode = CURSOR;
1199 le_select_mode_two_bt->event(event);
1200 if(le_select_mode_two_bt->get_state() == BUTTON_CLICKED)
1201 le_selection_mode = SQUARE;
1203 ButtonPanelMap::iterator it;
1204 le_tilegroup_bt->event(event);
1205 switch (le_tilegroup_bt->get_state())
1207 case BUTTON_CLICKED:
1208 Menu::set_current(select_tilegroup_menu);
1209 select_tilegroup_menu_effect.start(200);
1210 select_tilegroup_menu->set_pos(screen->w - 64,100,-0.5,0.5);
1212 case BUTTON_WHEELUP:
1213 if(cur_tilegroup.empty())
1215 cur_tilegroup = tilegroups_map.begin()->first;
1219 it = tilegroups_map.find(cur_tilegroup);
1220 if((++it) == tilegroups_map.end())
1222 cur_tilegroup = tilegroups_map.begin()->first;
1226 cur_tilegroup = (*it).first;
1232 case BUTTON_WHEELDOWN:
1233 it = tilegroups_map.find(cur_tilegroup);
1234 if(it == tilegroups_map.begin())
1236 cur_tilegroup = tilegroups_map.rbegin()->first;
1240 if(--it != --tilegroups_map.begin())
1241 cur_tilegroup = (*it).first;
1243 cur_tilegroup = tilegroups_map.rbegin()->first;
1251 le_objects_bt->event(event);
1252 switch (le_objects_bt->get_state())
1254 case BUTTON_CLICKED:
1255 Menu::set_current(select_objects_menu);
1256 select_objects_menu_effect.start(200);
1257 select_objects_menu->set_pos(screen->w - 64,100,-0.5,0.5);
1259 case BUTTON_WHEELUP:
1260 it = objects_map.find(cur_objects);
1261 if(it == objects_map.end())
1263 cur_objects = objects_map.begin()->first;
1267 if(++it != objects_map.end())
1268 cur_objects = (*it).first;
1270 cur_objects = objects_map.begin()->first;
1274 case BUTTON_WHEELDOWN:
1275 it = objects_map.find(cur_objects);
1276 if(it == objects_map.begin())
1278 cur_objects = objects_map.rbegin()->first;
1282 if(--it != --objects_map.begin())
1283 cur_objects = (*it).first;
1285 cur_objects = objects_map.rbegin()->first;
1294 le_settings_bt->event(event);
1295 if(le_settings_bt->get_state() == BUTTON_CLICKED)
1297 update_level_settings_menu();
1298 Menu::set_current(level_settings_menu);
1300 if(!cur_tilegroup.empty())
1302 if((pbutton = tilegroups_map[cur_tilegroup]->event(event)) != NULL)
1304 if(pbutton->get_state() == BUTTON_CLICKED)
1306 le_current.Tile(pbutton->get_tag());
1310 else if(!cur_objects.empty())
1312 if((pbutton = objects_map[cur_objects]->event(event)) != NULL)
1314 if(pbutton->get_state() == BUTTON_CLICKED)
1316 le_current.Object(pbutton->get_game_object());
1321 if((pbutton = le_tilemap_panel->event(event)) != NULL)
1323 if(pbutton->get_state() == BUTTON_CLICKED)
1325 active_tm = static_cast<TileMapType>(pbutton->get_tag());
1331 le_settings_bt->event(event);
1332 if(le_settings_bt->get_state() == BUTTON_CLICKED)
1334 Menu::set_current(0);
1336 le_tilegroup_bt->event(event);
1337 if(le_tilegroup_bt->get_state() == BUTTON_CLICKED)
1339 Menu::set_current(0);
1344 if(!Menu::current() && !show_minimap)
1346 if(le_mouse_pressed[LEFT])
1348 if(le_current.IsTile())
1349 le_change(cursor_x, cursor_y, active_tm, le_current.tile);
1351 else if(le_mouse_clicked[LEFT])
1353 if(le_current.IsObject())
1355 std::string type = le_current.obj->type();
1356 if(type == "BadGuy")
1358 BadGuy* pbadguy = dynamic_cast<BadGuy*>(le_current.obj);
1360 le_world->bad_guys.push_back(new BadGuy(cursor_x+scroll_x, cursor_y,pbadguy->kind,false));
1361 le_world->get_level()->badguy_data.push_back(le_world->bad_guys.back());
1364 le_mouse_clicked[LEFT] = false;
1369 if(!Menu::current())
1371 show_minimap = false;
1373 le_move_left_bt->event(event);
1374 le_move_right_bt->event(event);
1375 switch(le_move_left_bt->get_state())
1377 case BUTTON_PRESSED:
1379 show_minimap = true;
1383 show_minimap = true;
1385 case BUTTON_CLICKED:
1386 show_minimap = true;
1392 switch(le_move_right_bt->get_state())
1394 case BUTTON_PRESSED:
1396 show_minimap = true;
1400 show_minimap = true;
1402 case BUTTON_CLICKED:
1403 show_minimap = true;
1413 void le_highlight_selection()
1417 if(selection.x1 < selection.x2)
1427 if(selection.y1 < selection.y2)
1443 fillrect(x1*32-pos_x, y1*32,32* (x2 - x1 + 1),32 * (y2 - y1 + 1),173,234,177,103);
1446 void le_change(float x, float y, int tm, unsigned int c)
1448 if(le_world != NULL)
1454 /* level_changed = true; */
1456 switch(le_selection_mode)
1459 le_world->get_level()->change(x,y,tm,c);
1461 base_type cursor_base;
1464 cursor_base.width = 32;
1465 cursor_base.height = 32;
1467 /* if there is a bad guy over there, remove it */
1468 for(std::list<BadGuy*>::iterator it = le_world->bad_guys.begin(); it != le_world->bad_guys.end(); ++it)
1469 if(rectcollision(cursor_base,(*it)->base))
1471 le_world->bad_guys.erase(le_world->bad_guys.begin(),it);
1472 le_world->get_level()->badguy_data.erase(le_world->get_level()->badguy_data.begin() + i);
1477 if(selection.x1 < selection.x2)
1487 if(selection.y1 < selection.y2)
1503 /* if there is a bad guy over there, remove it */
1504 for(std::list<BadGuy*>::iterator it = le_world->bad_guys.begin();
1505 it != le_world->bad_guys.end(); /* will be at end of loop */)
1507 if((*it)->base.x/32 >= x1 && (*it)->base.x/32 <= x2
1508 && (*it)->base.y/32 >= y1 && (*it)->base.y/32 <= y2)
1510 it = le_world->bad_guys.erase(it);
1519 for(xx = x1; xx <= x2; xx++)
1520 for(yy = y1; yy <= y2; yy++)
1522 le_world->get_level()->change(xx*32, yy*32, tm, c);
1534 le_world->get_level()->save("test", le_level);
1536 GameSession session("test",le_level, ST_GL_TEST);
1538 player_status.reset();
1540 music_manager->halt_music();
1542 Menu::set_current(NULL);
1543 /*delete le_world.arrays_free();
1544 le_current_level->load_gfx();
1545 le_world.activate_bad_guys();*/
1551 unsigned int i, done_;
1553 " - This is SuperTux's built-in level editor -",
1554 "It has been designed to be light and easy to use from the start.",
1556 "When you first load the level editor you are given a menu where you",
1557 "can load level subsets, create a new level subset, edit the current",
1558 "subset's settings, or simply quit the editor. You can access this menu",
1559 "from the level editor at any time by pressing the escape key.",
1561 "To your right is your button bar. The center of this contains many",
1562 "tiles you can use to make your level. To select a tile, click on it",
1563 "with your left mouse button; your selection will be shown in the",
1564 "bottom right corner of the button box. Click anywhere on your level",
1565 "with the left mouse button to place that tile down. If you right click",
1566 "a tile in the button bar, you can find out what its keyboard shortcut",
1567 "is. The three buttons FGD, BGD and EMY let you pick from foreground,",
1568 "background, and enemy tiles. The eraser lets you remove tiles.",
1569 "The left and right arrow keys scroll back and forth through your level.",
1570 "The button with the wrench and screwdriver, lets you change the",
1571 "settings of your level, including how long it is or what music it will",
1572 "play. When you are ready to give your level a test, click on the little",
1573 "running Tux. If you like the changes you have made to your level,",
1574 "press the red save key to keep them.",
1575 "To change which level in your subset you are editing, press the white",
1576 "up and down arrow keys at the top of the button box.",
1578 "Have fun making levels! If you make some good ones, send them to us on",
1579 "the SuperTux mailing list!",
1584 blue_text->drawf("- Help -", 0, 30, A_HMIDDLE, A_TOP, 2);
1586 for(i = 0; i < sizeof(text)/sizeof(char *); i++)
1587 white_small_text->draw(text[i], 5, 80+(i*white_small_text->h), 1);
1589 gold_text->drawf("Press Any Key to Continue", 0, 440, A_HMIDDLE, A_TOP, 1);
1597 done_ = wait_for_event(event);