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);
252 else if(menu == select_tilegroup_menu)
255 switch (it = select_tilegroup_menu->check())
260 cur_tilegroup = select_tilegroup_menu->get_item_by_id(it).text;
261 Menu::set_current(0);
268 else if(menu == select_objects_menu)
271 switch (it = select_objects_menu->check())
276 cur_objects = select_objects_menu->get_item_by_id(it).text;
279 Menu::set_current(0);
284 else if(menu == subset_load_menu)
286 switch (i = subset_load_menu->check())
293 if(le_load_level(level_subsets.item[i-1]))
299 else if(menu == subset_new_menu)
301 if(subset_new_menu->item[2].input[0] == '\0')
302 subset_new_menu->item[3].kind = MN_DEACTIVE;
305 subset_new_menu->item[3].kind = MN_ACTION;
307 switch (i = subset_new_menu->check())
309 case MNID_CREATESUBSET:
310 LevelSubset::create(subset_new_menu->get_item_by_id(MNID_SUBSETNAME).input);
311 le_level_subset->load(subset_new_menu->get_item_by_id(MNID_SUBSETNAME).input);
312 leveleditor_menu->get_item_by_id(MNID_SUBSETSETTINGS).kind = MN_GOTO;
314 le_world = new World(le_level_subset->name,1);
315 subset_new_menu->get_item_by_id(MNID_SUBSETNAME).change_input("");
317 Menu::set_current(subset_settings_menu);
322 else if(menu == subset_settings_menu)
324 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 )
325 subset_settings_menu->get_item_by_id(MNID_SUBSETSAVECHANGES).kind = MN_DEACTIVE;
327 subset_settings_menu->get_item_by_id(MNID_SUBSETSAVECHANGES).kind = MN_ACTION;
329 switch (i = subset_settings_menu->check())
331 case MNID_SUBSETSAVECHANGES:
332 save_subset_settings_menu();
333 Menu::set_current(leveleditor_menu);
339 mouse_cursor->draw();
347 ++global_frame_counter;
350 now_time = SDL_GetTicks();
351 if (now_time < last_time + FPS)
352 SDL_Delay(last_time + FPS - now_time); /* delay some time */
360 int le_load_level(char *filename)
362 le_level_subset->load(filename);
363 leveleditor_menu->get_item_by_id(MNID_SUBSETSETTINGS).kind = MN_GOTO;
366 le_world = new World(filename,le_level);
368 //GameSession* session = new GameSession(datadir + "/levels/" + le_level_subset->name + "/level1.stl", 0, ST_GL_DEMO_GAME);
370 Menu::set_current(NULL);
379 leveleditor_menu = new Menu();
380 subset_load_menu = new Menu();
381 subset_new_menu = new Menu();
382 subset_settings_menu = new Menu();
383 level_settings_menu = new Menu();
384 select_tilegroup_menu = new Menu();
385 select_objects_menu = new Menu();
387 leveleditor_menu->additem(MN_LABEL,"Level Editor Menu",0,0);
388 leveleditor_menu->additem(MN_HL,"",0,0);
389 leveleditor_menu->additem(MN_ACTION,"Return To Level Editor",0,0,MNID_RETURNLEVELEDITOR);
390 leveleditor_menu->additem(MN_DEACTIVE,"Level Subset Settings",0,subset_settings_menu,MNID_SUBSETSETTINGS);
391 leveleditor_menu->additem(MN_GOTO,"Load Level Subset",0,subset_load_menu);
392 leveleditor_menu->additem(MN_GOTO,"New Level Subset",0,subset_new_menu);
393 leveleditor_menu->additem(MN_HL,"",0,0);
394 leveleditor_menu->additem(MN_ACTION,"Quit Level Editor",0,0,MNID_QUITLEVELEDITOR);
396 Menu::set_current(leveleditor_menu);
398 subset_load_menu->additem(MN_LABEL, "Load Level Subset", 0, 0);
399 subset_load_menu->additem(MN_HL, "", 0, 0);
401 for(i = 0; i < level_subsets.num_items; ++i)
403 subset_load_menu->additem(MN_ACTION,level_subsets.item[i],0,0, i+1);
405 subset_load_menu->additem(MN_HL,"",0,0);
406 subset_load_menu->additem(MN_BACK,"Back",0,0);
408 subset_new_menu->additem(MN_LABEL,"New Level Subset",0,0);
409 subset_new_menu->additem(MN_HL,"",0,0);
410 subset_new_menu->additem(MN_TEXTFIELD,"Enter Name",0,0,MNID_SUBSETNAME);
411 subset_new_menu->additem(MN_ACTION,"Create",0,0, MNID_CREATESUBSET);
412 subset_new_menu->additem(MN_HL,"",0,0);
413 subset_new_menu->additem(MN_BACK,"Back",0,0);
415 subset_settings_menu->additem(MN_LABEL,"Level Subset Settings",0,0);
416 subset_settings_menu->additem(MN_HL,"",0,0);
417 subset_settings_menu->additem(MN_TEXTFIELD,"Title",0,0,MNID_SUBSETTITLE);
418 subset_settings_menu->additem(MN_TEXTFIELD,"Description",0,0,MNID_SUBSETDESCRIPTION);
419 subset_settings_menu->additem(MN_HL,"",0,0);
420 subset_settings_menu->additem(MN_ACTION,"Save Changes",0,0,MNID_SUBSETSAVECHANGES);
421 subset_settings_menu->additem(MN_HL,"",0,0);
422 subset_settings_menu->additem(MN_BACK,"Back",0,0);
424 level_settings_menu->arrange_left = true;
425 level_settings_menu->additem(MN_LABEL,"Level Settings",0,0);
426 level_settings_menu->additem(MN_HL,"",0,0);
427 level_settings_menu->additem(MN_TEXTFIELD, "Name ",0,0,MNID_NAME);
428 level_settings_menu->additem(MN_TEXTFIELD, "Author ",0,0,MNID_AUTHOR);
429 level_settings_menu->additem(MN_STRINGSELECT,"Song ",0,0,MNID_SONG);
430 level_settings_menu->additem(MN_STRINGSELECT,"Bg-Image",0,0,MNID_BGIMG);
431 level_settings_menu->additem(MN_STRINGSELECT,"Particle",0,0,MNID_PARTICLE);
432 level_settings_menu->additem(MN_NUMFIELD, "Length ",0,0,MNID_LENGTH);
433 level_settings_menu->additem(MN_NUMFIELD, "Time ",0,0,MNID_TIME);
434 level_settings_menu->additem(MN_NUMFIELD, "Gravity ",0,0,MNID_GRAVITY);
435 level_settings_menu->additem(MN_NUMFIELD, "Bg-Img-Speed",0,0,MNID_BGSPEED);
436 level_settings_menu->additem(MN_NUMFIELD, "Top Red ",0,0,MNID_TopRed);
437 level_settings_menu->additem(MN_NUMFIELD, "Top Green ",0,0,MNID_TopGreen);
438 level_settings_menu->additem(MN_NUMFIELD, "Top Blue ",0,0,MNID_TopBlue);
439 level_settings_menu->additem(MN_NUMFIELD, "Bottom Red ",0,0,MNID_BottomRed);
440 level_settings_menu->additem(MN_NUMFIELD, "Bottom Green",0,0,MNID_BottomGreen);
441 level_settings_menu->additem(MN_NUMFIELD, "Bottom Blue",0,0,MNID_BottomBlue);
442 level_settings_menu->additem(MN_HL,"",0,0);
443 level_settings_menu->additem(MN_ACTION,"Apply Changes",0,0,MNID_APPLY);
445 select_tilegroup_menu->arrange_left = true;
446 select_tilegroup_menu->additem(MN_LABEL,"Tilegroup",0,0);
447 select_tilegroup_menu->additem(MN_HL,"",0,0);
448 select_tilegroup_menu->additem(MN_ACTION,"asd",0,0);
449 std::set<TileGroup>* tilegroups = TileManager::tilegroups();
451 for(std::set<TileGroup>::iterator it = tilegroups->begin();
452 it != tilegroups->end(); ++it )
454 select_tilegroup_menu->additem(MN_ACTION, it->name, 0, 0, tileid);
456 tilegroups_map[(*it).name] = new ButtonPanel(screen->w - 64,96, 64, 318);
459 for(std::vector<int>::const_iterator sit = (*it).tiles.begin();
460 sit != (*it).tiles.end(); ++sit, ++i)
462 std::string imagefile = "/images/tilesets/" ;
463 bool only_editor_image = false;
464 if(!TileManager::instance()->get(*sit)->filenames.empty())
466 imagefile += TileManager::instance()->get(*sit)->filenames[0];
468 else if(!TileManager::instance()->get(*sit)->editor_filenames.empty())
470 imagefile += TileManager::instance()->get(*sit)->editor_filenames[0];
471 only_editor_image = true;
475 imagefile += "notile.png";
477 Button* button = new Button(imagefile, it->name, SDLKey(SDLK_a + i),
479 if(!only_editor_image)
480 if(!TileManager::instance()->get(*sit)->editor_filenames.empty())
482 imagefile = "/images/tilesets/" + TileManager::instance()->get(*sit)->editor_filenames[0];
483 button->add_icon(imagefile,32,32);
485 tilegroups_map[it->name]->additem(button, *sit);
488 select_tilegroup_menu->additem(MN_HL,"",0,0);
490 select_objects_menu->arrange_left = true;
491 select_objects_menu->additem(MN_LABEL,"Objects",0,0);
492 select_objects_menu->additem(MN_HL,"",0,0);
493 select_objects_menu->additem(MN_ACTION,"BadGuys",0,0,1);
494 objects_map["BadGuys"] = new ButtonPanel(screen->w - 64,96, 64, 318);
496 for(int i = 0; i < NUM_BadGuyKinds; ++i)
498 BadGuy bad_tmp(0,0,BadGuyKind(i),false);
499 objects_map["BadGuys"]->additem(new Button("", "BadGuy",(SDLKey)(i+'a'),0,0,32,32),1000000+i);
500 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));
503 select_objects_menu->additem(MN_HL,"",0,0);
511 level_subsets = dsubdirs("/levels", "info");
512 le_level_subset = new LevelSubset;
522 le_frame = 0; /* support for frames in some tiles, like waves and bad guys */
523 le_level_changed = false;
525 le_mouse_pressed[LEFT] = false;
526 le_mouse_pressed[RIGHT] = false;
528 le_mouse_clicked[LEFT] = false;
529 le_mouse_clicked[RIGHT] = false;
531 le_selection = new Surface(datadir + "/images/leveleditor/select.png", USE_ALPHA);
533 select_tilegroup_menu_effect.init(false);
534 select_objects_menu_effect.init(false);
537 le_save_level_bt = new Button("/images/icons/save.png","Save level", SDLK_F6,screen->w-64,32);
538 le_exit_bt = new Button("/images/icons/exit.png","Exit", SDLK_F6,screen->w-32,32);
539 le_next_level_bt = new Button("/images/icons/up.png","Next level", SDLK_PAGEUP,screen->w-64,0);
540 le_previous_level_bt = new Button("/images/icons/down.png","Previous level",SDLK_PAGEDOWN,screen->w-32,0);
541 le_rubber_bt = new Button("/images/icons/rubber.png","Rubber",SDLK_DELETE,screen->w-32,48);
542 le_select_mode_one_bt = new Button ("/images/icons/select-mode1.png","Select single tile",SDLK_F3,screen->w-64,48);
543 le_select_mode_two_bt = new Button("/images/icons/select-mode2.png","Select multiple tiles",SDLK_F3,screen->w-64,48);
544 le_test_level_bt = new Button("/images/icons/test-level.png","Test level",SDLK_F4,screen->w-64,screen->h - 64);
545 le_settings_bt = new Button("/images/icons/settings.png","Level settings",SDLK_F5,screen->w-32,screen->h - 64);
546 le_move_left_bt = new Button("/images/icons/left.png","Move left",SDLK_LEFT,0,0);
547 le_move_right_bt = new Button("/images/icons/right.png","Move right",SDLK_RIGHT,screen->w-80,0);
548 le_tilegroup_bt = new Button("/images/icons/tilegroup.png","Select Tilegroup", SDLK_F7,screen->w-64,64);
549 le_objects_bt = new Button("/images/icons/objects.png","Select Objects", SDLK_F7,screen->w-64,80);
551 le_tilemap_panel = new ButtonPanel(screen->w-64,screen->h-32,32,32);
552 le_tilemap_panel->set_button_size(32,10);
553 le_tilemap_panel->additem(new Button("/images/icons/bkgrd.png","Background",SDLK_b,0,0),TM_BG);
554 le_tilemap_panel->additem(new Button("/images/icons/intact.png","Interactive",SDLK_i,0,0),TM_IA);
555 le_tilemap_panel->additem(new Button("/images/icons/frgrd.png","Foreground",SDLK_f,0,0),TM_FG);
556 le_tilemap_panel->highlight_last(true);
562 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
568 void update_level_settings_menu()
573 level_settings_menu->get_item_by_id(MNID_NAME).change_input(le_world->get_level()->name.c_str());
574 level_settings_menu->get_item_by_id(MNID_AUTHOR).change_input(le_world->get_level()->author.c_str());
576 string_list_copy(level_settings_menu->get_item_by_id(MNID_SONG).list, dfiles("music/",NULL, "-fast"));
577 string_list_copy(level_settings_menu->get_item_by_id(MNID_BGIMG).list, dfiles("images/background",NULL, NULL));
578 string_list_add_item(level_settings_menu->get_item_by_id(MNID_BGIMG).list,"");
579 string_list_add_item(level_settings_menu->get_item_by_id(MNID_PARTICLE).list,"");
580 string_list_add_item(level_settings_menu->get_item_by_id(MNID_PARTICLE).list,"snow");
581 string_list_add_item(level_settings_menu->get_item_by_id(MNID_PARTICLE).list,"clouds");
583 if((i = string_list_find(level_settings_menu->get_item_by_id(MNID_SONG).list,le_world->get_level()->song_title.c_str())) != -1)
584 level_settings_menu->get_item_by_id(MNID_SONG).list->active_item = i;
585 if((i = string_list_find(level_settings_menu->get_item_by_id(MNID_BGIMG).list,le_world->get_level()->bkgd_image.c_str())) != -1)
586 level_settings_menu->get_item_by_id(MNID_BGIMG).list->active_item = i;
587 if((i = string_list_find(level_settings_menu->get_item_by_id(MNID_PARTICLE).list,le_world->get_level()->particle_system.c_str())) != -1)
588 level_settings_menu->get_item_by_id(MNID_PARTICLE).list->active_item = i;
590 sprintf(str,"%d",le_world->get_level()->width);
591 level_settings_menu->get_item_by_id(MNID_LENGTH).change_input(str);
592 sprintf(str,"%d",le_world->get_level()->time_left);
593 level_settings_menu->get_item_by_id(MNID_TIME).change_input(str);
594 sprintf(str,"%2.0f",le_world->get_level()->gravity);
595 level_settings_menu->get_item_by_id(MNID_GRAVITY).change_input(str);
596 sprintf(str,"%d",le_world->get_level()->bkgd_speed);
597 level_settings_menu->get_item_by_id(MNID_BGSPEED).change_input(str);
598 sprintf(str,"%d",le_world->get_level()->bkgd_top.red);
599 level_settings_menu->get_item_by_id(MNID_TopRed).change_input(str);
600 sprintf(str,"%d",le_world->get_level()->bkgd_top.green);
601 level_settings_menu->get_item_by_id(MNID_TopGreen).change_input(str);
602 sprintf(str,"%d",le_world->get_level()->bkgd_top.blue);
603 level_settings_menu->get_item_by_id(MNID_TopBlue).change_input(str);
604 sprintf(str,"%d",le_world->get_level()->bkgd_bottom.red);
605 level_settings_menu->get_item_by_id(MNID_BottomRed).change_input(str);
606 sprintf(str,"%d",le_world->get_level()->bkgd_bottom.green);
607 level_settings_menu->get_item_by_id(MNID_BottomGreen).change_input(str);
608 sprintf(str,"%d",le_world->get_level()->bkgd_bottom.blue);
609 level_settings_menu->get_item_by_id(MNID_BottomBlue).change_input(str);
612 void update_subset_settings_menu()
614 subset_settings_menu->item[2].change_input(le_level_subset->title.c_str());
615 subset_settings_menu->item[3].change_input(le_level_subset->description.c_str());
618 void apply_level_settings_menu()
623 le_world->get_level()->name = level_settings_menu->get_item_by_id(MNID_NAME).input;
624 le_world->get_level()->author = level_settings_menu->get_item_by_id(MNID_AUTHOR).input;
626 if(le_world->get_level()->bkgd_image.compare(string_list_active(level_settings_menu->get_item_by_id(MNID_BGIMG).list)) != 0)
628 le_world->get_level()->bkgd_image = string_list_active(level_settings_menu->get_item_by_id(MNID_BGIMG).list);
632 if(le_world->get_level()->particle_system.compare(string_list_active(level_settings_menu->get_item_by_id(MNID_PARTICLE).list)) != 0)
634 le_world->get_level()->particle_system = string_list_active(level_settings_menu->get_item_by_id(MNID_PARTICLE).list);
639 le_world->get_level()->load_gfx();
642 le_world->get_level()->song_title = string_list_active(level_settings_menu->get_item_by_id(MNID_SONG).list);
644 le_world->get_level()->change_size(atoi(level_settings_menu->get_item_by_id(MNID_LENGTH).input));
645 le_world->get_level()->time_left = atoi(level_settings_menu->get_item_by_id(MNID_TIME).input);
646 le_world->get_level()->gravity = atof(level_settings_menu->get_item_by_id(MNID_GRAVITY).input);
647 le_world->get_level()->bkgd_speed = atoi(level_settings_menu->get_item_by_id(MNID_BGSPEED).input);
648 le_world->get_level()->bkgd_top.red = atoi(level_settings_menu->get_item_by_id(MNID_TopRed).input);
649 le_world->get_level()->bkgd_top.green = atoi(level_settings_menu->get_item_by_id(MNID_TopGreen).input);
650 le_world->get_level()->bkgd_top.blue = atoi(level_settings_menu->get_item_by_id(MNID_TopBlue).input);
651 le_world->get_level()->bkgd_bottom.red = atoi(level_settings_menu->get_item_by_id(MNID_BottomRed).input);
652 le_world->get_level()->bkgd_bottom.green = atoi(level_settings_menu->get_item_by_id(MNID_BottomGreen).input);
653 le_world->get_level()->bkgd_bottom.blue = atoi(level_settings_menu->get_item_by_id(MNID_BottomBlue).input);
656 void save_subset_settings_menu()
658 le_level_subset->title = subset_settings_menu->item[2].input;
659 le_level_subset->description = subset_settings_menu->item[3].input;
660 le_level_subset->save();
663 void le_goto_level(int levelnb)
666 le_world = new World(le_level_subset->name, levelnb);
671 /*if(level_changed == true)
672 if(askforsaving() == CANCEL)
675 SDL_EnableKeyRepeat(0, 0); // disables key repeating
678 delete leveleditor_menu;
679 delete subset_load_menu;
680 delete subset_new_menu;
681 delete subset_settings_menu;
682 delete level_settings_menu;
683 delete select_tilegroup_menu;
684 delete select_objects_menu;
685 delete le_save_level_bt;
687 delete le_test_level_bt;
688 delete le_next_level_bt;
689 delete le_previous_level_bt;
690 delete le_move_right_bt;
691 delete le_move_left_bt;
693 delete le_select_mode_one_bt;
694 delete le_select_mode_two_bt;
695 delete le_settings_bt;
696 delete le_tilegroup_bt;
697 delete le_objects_bt;
698 delete le_tilemap_panel;
700 delete le_level_subset;
703 for(ButtonPanelMap::iterator i = tilegroups_map.begin();
704 i != tilegroups_map.end(); ++i)
708 for(ButtonPanelMap::iterator i = objects_map.begin();
709 i != objects_map.end(); ++i)
715 void le_drawminimap()
721 if(screen->w - 64 > le_world->get_level()->width * 4)
723 else if(screen->w - 64 > le_world->get_level()->width * 2)
727 int left_offset = (screen->w - 64 - le_world->get_level()->width*mini_tile_width) / 2;
729 for (int y = 0; y < 15; ++y)
730 for (int x = 0; x < le_world->get_level()->width; ++x)
733 Tile::draw_stretched(left_offset + mini_tile_width*x, y * 4, mini_tile_width , 4, le_world->get_level()->bg_tiles[y][x]);
735 Tile::draw_stretched(left_offset + mini_tile_width*x, y * 4, mini_tile_width , 4, le_world->get_level()->ia_tiles[y][x]);
737 Tile::draw_stretched(left_offset + mini_tile_width*x, y * 4, mini_tile_width , 4, le_world->get_level()->fg_tiles[y][x]);
741 fillrect(left_offset, 0, le_world->get_level()->width*mini_tile_width, 15*4, 200, 200, 200, 128);
743 fillrect(left_offset + (pos_x/32)*mini_tile_width, 0, 19*mini_tile_width, 2, 200, 200, 200, 200);
744 fillrect(left_offset + (pos_x/32)*mini_tile_width, 0, 2, 15*4, 200, 200, 200, 200);
745 fillrect(left_offset + (pos_x/32)*mini_tile_width + 19*mini_tile_width - 2, 0, 2, 15*4, 200, 200, 200, 200);
746 fillrect(left_offset + (pos_x/32)*mini_tile_width, 15*4-2, 19*mini_tile_width, 2, 200, 200, 200, 200);
750 void le_drawinterface()
757 /* draw a grid (if selected) */
760 for(x = 0; x < 19; x++)
761 fillrect(x*32 - ((int)pos_x % 32), 0, 1, screen->h, 225, 225, 225,255);
762 for(y = 0; y < 15; y++)
763 fillrect(0, y*32, screen->w - 32, 1, 225, 225, 225,255);
767 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
770 if(le_selection_mode == CURSOR)
771 if(le_current.IsTile())
772 le_selection->draw( cursor_x - pos_x, cursor_y);
774 le_selection->draw( cursor_x, cursor_y);
775 else if(le_selection_mode == SQUARE)
778 le_highlight_selection();
779 /* draw current selection */
780 w = selection.x2 - selection.x1;
781 h = selection.y2 - selection.y1;
782 fillrect(selection.x1 - pos_x, selection.y1, w, SELECT_W, SELECT_CLR);
783 fillrect(selection.x1 - pos_x + w, selection.y1, SELECT_W, h, SELECT_CLR);
784 fillrect(selection.x1 - pos_x, selection.y1 + h, w, SELECT_W, SELECT_CLR);
785 fillrect(selection.x1 - pos_x, selection.y1, SELECT_W, h, SELECT_CLR);
789 /* draw button bar */
790 fillrect(screen->w - 64, 0, 64, screen->h, 50, 50, 50,255);
792 if(le_current.IsTile())
794 Tile::draw(19 * 32, 14 * 32, le_current.tile);
795 if(TileManager::instance()->get(le_current.tile)->editor_images.size() > 0)
796 TileManager::instance()->get(le_current.tile)->editor_images[0]->draw( 19 * 32, 14 * 32);
798 if(le_current.IsObject())
800 le_current.obj->draw_on_screen(19 * 32, 14 * 32);
801 le_current.obj->draw_on_screen(cursor_x,cursor_y);
806 le_save_level_bt->draw();
808 le_test_level_bt->draw();
809 le_next_level_bt->draw();
810 le_previous_level_bt->draw();
811 le_rubber_bt->draw();
812 if(le_selection_mode == SQUARE)
813 le_select_mode_one_bt->draw();
814 else if(le_selection_mode == CURSOR)
815 le_select_mode_two_bt->draw();
816 le_settings_bt->draw();
817 le_move_right_bt->draw();
818 le_move_left_bt->draw();
819 le_tilegroup_bt->draw();
820 le_objects_bt->draw();
821 if(!cur_tilegroup.empty())
822 tilegroups_map[cur_tilegroup]->draw();
823 else if(!cur_objects.empty())
825 objects_map[cur_objects]->draw();
828 le_tilemap_panel->draw();
830 sprintf(str, "%d/%d", le_level,le_level_subset->levels);
831 white_text->drawf(str, -10, 16, A_RIGHT, A_TOP, 0);
833 white_small_text->draw("F1 for Help", 10, 430, 1);
838 white_small_text->draw("No Level Subset loaded - Press ESC and choose one in the menu", 10, 430, 1);
840 white_small_text->draw("No Level Subset loaded", 10, 430, 1);
850 /* Draw the real background */
851 if(le_world->get_level()->bkgd_image[0] != '\0')
853 s = (int)((float)pos_x * ((float)le_world->get_level()->bkgd_speed/60.)) % screen->w;
854 le_world->get_level()->img_bkgd->draw_part(s,0,0,0,
855 le_world->get_level()->img_bkgd->w - s - 32, le_world->get_level()->img_bkgd->h);
856 le_world->get_level()->img_bkgd->draw_part(0,0,screen->w - s - 32 ,0,s,
857 le_world->get_level()->img_bkgd->h);
861 drawgradient(le_world->get_level()->bkgd_top, le_world->get_level()->bkgd_bottom);
864 if(le_current.IsTile())
866 Tile::draw(cursor_x-pos_x, cursor_y,le_current.tile,128);
867 if(!TileManager::instance()->get(le_current.tile)->images.empty())
868 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);
870 if(le_current.IsObject())
872 le_current.obj->move_to(cursor_x, cursor_y);
875 /* clearscreen(current_level.bkgd_red, current_level.bkgd_green, current_level.bkgd_blue); */
877 for (y = 0; y < 15; ++y)
878 for (x = 0; x < 20; ++x)
881 if(active_tm == TM_BG)
886 Tile::draw(32*x - fmodf(pos_x, 32), y * 32, le_world->get_level()->bg_tiles[y][x + (int)(pos_x / 32)],a);
888 if(active_tm == TM_IA)
893 Tile::draw(32*x - fmodf(pos_x, 32), y * 32, le_world->get_level()->ia_tiles[y][x + (int)(pos_x / 32)],a);
895 if(active_tm == TM_FG)
900 Tile::draw(32*x - fmodf(pos_x, 32), y * 32, le_world->get_level()->fg_tiles[y][x + (int)(pos_x / 32)],a);
902 /* draw whats inside stuff when cursor is selecting those */
903 /* (draw them all the time - is this the right behaviour?) */
904 if(!TileManager::instance()->get(le_world->get_level()->ia_tiles[y][x + (int)(pos_x / 32)])->editor_images.empty())
905 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);
909 /* Draw the Bad guys: */
910 for (std::list<BadGuy*>::iterator it = le_world->bad_guys.begin(); it != le_world->bad_guys.end(); ++it)
912 /* to support frames: img_bsod_left[(frame / 5) % 4] */
919 /* Draw the player: */
920 /* for now, the position is fixed at (100, 240) */
921 largetux.walk_right->draw( 100 - pos_x, 240);
924 void le_change_object_properties(GameObject *pobj)
926 Menu* object_properties_menu = new Menu();
928 object_properties_menu->additem(MN_LABEL,pobj->type() + " Properties",0,0);
929 object_properties_menu->additem(MN_HL,"",0,0);
930 /*object_properties_menu->additem(MN_TEXTFIELD,"Title",0,0,MNID_SUBSETTITLE);
931 object_properties_menu->additem(MN_TEXTFIELD,"Description",0,0,MNID_SUBSETDESCRIPTION);
932 object_properties_menu->additem(MN_HL,"",0,0);
933 object_properties_menu->additem(MN_ACTION,"Save Changes",0,0,MNID_SUBSETSAVECHANGES);*/
934 object_properties_menu->additem(MN_HL,"",0,0);
935 object_properties_menu->additem(MN_BACK,"Apply",0,0);
937 delete object_properties_menu;
941 void le_checkevents()
948 keymod = SDL_GetModState();
950 while(SDL_PollEvent(&event))
954 Menu::current()->event(event);
958 mouse_cursor->set_state(MC_NORMAL);
960 /* testing SDL_KEYDOWN, SDL_KEYUP and SDL_QUIT events*/
961 if(event.type == SDL_KEYDOWN
962 || ((event.type == SDL_MOUSEBUTTONDOWN || SDL_MOUSEMOTION)
963 && (event.motion.x > 0
964 && event.motion.x < screen->w - 64 &&
965 event.motion.y > 0 && event.motion.y < screen->h)))
969 case SDL_KEYDOWN: // key pressed
970 key = event.key.keysym.sym;
974 Menu::set_current(leveleditor_menu);
977 cursor_x -= KEY_CURSOR_SPEED;
979 cursor_x -= KEY_CURSOR_FASTSPEED;
981 if(cursor_x < pos_x + MOUSE_LEFT_MARGIN)
982 pos_x = cursor_x - MOUSE_LEFT_MARGIN;
987 cursor_x += KEY_CURSOR_SPEED;
989 cursor_x += KEY_CURSOR_FASTSPEED;
991 if(cursor_x > pos_x + MOUSE_RIGHT_MARGIN-32)
992 pos_x = cursor_x - MOUSE_RIGHT_MARGIN+32;
997 cursor_y -= KEY_CURSOR_SPEED;
999 cursor_y -= KEY_CURSOR_FASTSPEED;
1006 cursor_y += KEY_CURSOR_SPEED;
1008 cursor_y += KEY_CURSOR_FASTSPEED;
1010 if(cursor_y > screen->h-32)
1011 cursor_y = screen->h-32;
1024 cursor_x = (le_world->get_level()->width * 32) - 32;
1028 le_show_grid = !le_show_grid;
1034 case SDL_KEYUP: /* key released */
1035 switch(event.key.keysym.sym)
1044 case SDL_MOUSEBUTTONDOWN:
1045 if(event.button.button == SDL_BUTTON_LEFT)
1047 le_mouse_pressed[LEFT] = true;
1049 selection.x1 = event.motion.x + pos_x;
1050 selection.y1 = event.motion.y;
1051 selection.x2 = event.motion.x + pos_x;
1052 selection.y2 = event.motion.y;
1054 else if(event.button.button == SDL_BUTTON_RIGHT)
1056 le_mouse_pressed[RIGHT] = true;
1059 case SDL_MOUSEBUTTONUP:
1060 if(event.button.button == SDL_BUTTON_LEFT)
1062 le_mouse_pressed[LEFT] = false;
1063 le_mouse_clicked[LEFT] = true;
1065 else if(event.button.button == SDL_BUTTON_RIGHT)
1067 le_mouse_pressed[RIGHT] = false;
1068 le_mouse_clicked[RIGHT] = true;
1071 case SDL_MOUSEMOTION:
1073 if(!Menu::current())
1078 if(le_current.IsTile())
1080 cursor_x = ((int)(pos_x + x) / 32) * 32;
1081 cursor_y = ((int) y / 32) * 32;
1089 if(le_mouse_pressed[LEFT])
1091 selection.x2 = x + pos_x;
1095 if(le_mouse_pressed[RIGHT])
1097 pos_x += -1 * event.motion.xrel;
1101 case SDL_QUIT: // window closed
1110 if(le_world != NULL)
1112 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 &&
1113 event.motion.y > 0 && event.motion.y < screen->h)))
1115 le_mouse_pressed[LEFT] = false;
1116 le_mouse_pressed[RIGHT] = false;
1118 if(!Menu::current())
1120 /* Check for button events */
1121 le_test_level_bt->event(event);
1122 if(le_test_level_bt->get_state() == BUTTON_CLICKED)
1124 le_save_level_bt->event(event);
1125 if(le_save_level_bt->get_state() == BUTTON_CLICKED)
1126 le_world->get_level()->save(le_level_subset->name.c_str(),le_level);
1127 le_exit_bt->event(event);
1128 if(le_exit_bt->get_state() == BUTTON_CLICKED)
1130 Menu::set_current(leveleditor_menu);
1132 le_next_level_bt->event(event);
1133 if(le_next_level_bt->get_state() == BUTTON_CLICKED)
1135 if(le_level < le_level_subset->levels)
1137 le_goto_level(++le_level);
1143 sprintf(str,"Level %d doesn't exist. Create it?",le_level+1);
1144 if(confirm_dialog(str))
1146 new_lev.init_defaults();
1147 new_lev.save(le_level_subset->name.c_str(),++le_level);
1148 le_level_subset->levels = le_level;
1149 le_goto_level(le_level);
1153 le_previous_level_bt->event(event);
1154 if(le_previous_level_bt->get_state() == BUTTON_CLICKED)
1157 le_goto_level(--le_level);
1159 le_rubber_bt->event(event);
1160 if(le_rubber_bt->get_state() == BUTTON_CLICKED)
1163 if(le_selection_mode == SQUARE)
1165 le_select_mode_one_bt->event(event);
1166 if(le_select_mode_one_bt->get_state() == BUTTON_CLICKED)
1167 le_selection_mode = CURSOR;
1171 le_select_mode_two_bt->event(event);
1172 if(le_select_mode_two_bt->get_state() == BUTTON_CLICKED)
1173 le_selection_mode = SQUARE;
1175 ButtonPanelMap::iterator it;
1176 le_tilegroup_bt->event(event);
1177 switch (le_tilegroup_bt->get_state())
1179 case BUTTON_CLICKED:
1180 Menu::set_current(select_tilegroup_menu);
1181 select_tilegroup_menu_effect.start(200);
1182 select_tilegroup_menu->set_pos(screen->w - 64,100,-0.5,0.5);
1184 case BUTTON_WHEELUP:
1185 if(cur_tilegroup.empty())
1187 cur_tilegroup = tilegroups_map.begin()->first;
1191 it = tilegroups_map.find(cur_tilegroup);
1192 if((++it) == tilegroups_map.end())
1194 cur_tilegroup = tilegroups_map.begin()->first;
1198 cur_tilegroup = (*it).first;
1204 case BUTTON_WHEELDOWN:
1205 it = tilegroups_map.find(cur_tilegroup);
1206 if(it == tilegroups_map.begin())
1208 cur_tilegroup = tilegroups_map.rbegin()->first;
1212 if(--it != --tilegroups_map.begin())
1213 cur_tilegroup = (*it).first;
1215 cur_tilegroup = tilegroups_map.rbegin()->first;
1223 le_objects_bt->event(event);
1224 switch (le_objects_bt->get_state())
1226 case BUTTON_CLICKED:
1227 Menu::set_current(select_objects_menu);
1228 select_objects_menu_effect.start(200);
1229 select_objects_menu->set_pos(screen->w - 64,100,-0.5,0.5);
1231 case BUTTON_WHEELUP:
1232 it = objects_map.find(cur_objects);
1233 if(it == objects_map.end())
1235 cur_objects = objects_map.begin()->first;
1239 if(++it != objects_map.end())
1240 cur_objects = (*it).first;
1242 cur_objects = objects_map.begin()->first;
1246 case BUTTON_WHEELDOWN:
1247 it = objects_map.find(cur_objects);
1248 if(it == objects_map.begin())
1250 cur_objects = objects_map.rbegin()->first;
1254 if(--it != --objects_map.begin())
1255 cur_objects = (*it).first;
1257 cur_objects = objects_map.rbegin()->first;
1266 le_settings_bt->event(event);
1267 if(le_settings_bt->get_state() == BUTTON_CLICKED)
1269 update_level_settings_menu();
1270 Menu::set_current(level_settings_menu);
1272 if(!cur_tilegroup.empty())
1274 if((pbutton = tilegroups_map[cur_tilegroup]->event(event)) != NULL)
1276 if(pbutton->get_state() == BUTTON_CLICKED)
1278 le_current.Tile(pbutton->get_tag());
1282 else if(!cur_objects.empty())
1284 if((pbutton = objects_map[cur_objects]->event(event)) != NULL)
1286 if(pbutton->get_state() == BUTTON_CLICKED)
1288 le_current.Object(pbutton->get_game_object());
1293 if((pbutton = le_tilemap_panel->event(event)) != NULL)
1295 if(pbutton->get_state() == BUTTON_CLICKED)
1297 active_tm = static_cast<TileMapType>(pbutton->get_tag());
1303 le_settings_bt->event(event);
1304 if(le_settings_bt->get_state() == BUTTON_CLICKED)
1306 Menu::set_current(0);
1308 le_tilegroup_bt->event(event);
1309 if(le_tilegroup_bt->get_state() == BUTTON_CLICKED)
1311 Menu::set_current(0);
1313 le_objects_bt->event(event);
1314 if(le_objects_bt->get_state() == BUTTON_CLICKED)
1316 Menu::set_current(0);
1321 if(!Menu::current() && !show_minimap)
1323 if(le_mouse_pressed[LEFT])
1325 if(le_current.IsTile())
1326 le_change(cursor_x, cursor_y, active_tm, le_current.tile);
1328 else if(le_mouse_clicked[LEFT])
1330 if(le_current.IsObject())
1332 std::string type = le_current.obj->type();
1333 if(type == "BadGuy")
1335 BadGuy* pbadguy = dynamic_cast<BadGuy*>(le_current.obj);
1337 le_world->bad_guys.push_back(new BadGuy(cursor_x+scroll_x, cursor_y,pbadguy->kind,false));
1338 le_world->get_level()->badguy_data.push_back(le_world->bad_guys.back());
1341 le_mouse_clicked[LEFT] = false;
1346 if(!Menu::current())
1348 show_minimap = false;
1350 le_move_left_bt->event(event);
1351 le_move_right_bt->event(event);
1352 switch(le_move_left_bt->get_state())
1354 case BUTTON_PRESSED:
1356 show_minimap = true;
1360 show_minimap = true;
1362 case BUTTON_CLICKED:
1363 show_minimap = true;
1369 switch(le_move_right_bt->get_state())
1371 case BUTTON_PRESSED:
1373 show_minimap = true;
1377 show_minimap = true;
1379 case BUTTON_CLICKED:
1380 show_minimap = true;
1390 void le_highlight_selection()
1394 if(selection.x1 < selection.x2)
1404 if(selection.y1 < selection.y2)
1420 fillrect(x1*32-pos_x, y1*32,32* (x2 - x1 + 1),32 * (y2 - y1 + 1),173,234,177,103);
1423 void le_change(float x, float y, int tm, unsigned int c)
1425 if(le_world != NULL)
1431 /* level_changed = true; */
1433 switch(le_selection_mode)
1436 le_world->get_level()->change(x,y,tm,c);
1438 base_type cursor_base;
1441 cursor_base.width = 32;
1442 cursor_base.height = 32;
1444 /* if there is a bad guy over there, remove it */
1445 for(std::list<BadGuy*>::iterator it = le_world->bad_guys.begin(); it != le_world->bad_guys.end(); ++it, ++i)
1446 if(rectcollision(cursor_base,(*it)->base))
1449 le_world->bad_guys.erase(it);
1450 le_world->get_level()->badguy_data.erase(le_world->get_level()->badguy_data.begin() + i);
1456 if(selection.x1 < selection.x2)
1466 if(selection.y1 < selection.y2)
1482 /* if there is a bad guy over there, remove it */
1483 for(std::list<BadGuy*>::iterator it = le_world->bad_guys.begin();
1484 it != le_world->bad_guys.end(); /* will be at end of loop */)
1486 if((*it)->base.x/32 >= x1 && (*it)->base.x/32 <= x2
1487 && (*it)->base.y/32 >= y1 && (*it)->base.y/32 <= y2)
1490 it = le_world->bad_guys.erase(it);
1491 le_world->get_level()->badguy_data.erase(le_world->get_level()->badguy_data.begin() + i);
1501 for(xx = x1; xx <= x2; xx++)
1502 for(yy = y1; yy <= y2; yy++)
1504 le_world->get_level()->change(xx*32, yy*32, tm, c);
1516 le_world->get_level()->save("test", le_level);
1518 GameSession session("test",le_level, ST_GL_TEST);
1520 player_status.reset();
1522 music_manager->halt_music();
1524 Menu::set_current(NULL);
1525 /*delete le_world.arrays_free();
1526 le_current_level->load_gfx();
1527 le_world.activate_bad_guys();*/
1533 unsigned int i, done_;
1535 " - This is SuperTux's built-in level editor -",
1536 "It has been designed to be light and easy to use from the start.",
1538 "When you first load the level editor you are given a menu where you",
1539 "can load level subsets, create a new level subset, edit the current",
1540 "subset's settings, or simply quit the editor. You can access this menu",
1541 "from the level editor at any time by pressing the escape key.",
1543 "To your right is your button bar. The center of this contains many",
1544 "tiles you can use to make your level. To select a tile, click on it",
1545 "with your left mouse button; your selection will be shown in the",
1546 "bottom right corner of the button box. Click anywhere on your level",
1547 "with the left mouse button to place that tile down. If you right click",
1548 "a tile in the button bar, you can find out what its keyboard shortcut",
1549 "is. The three buttons FGD, BGD and EMY let you pick from foreground,",
1550 "background, and enemy tiles. The eraser lets you remove tiles.",
1551 "The left and right arrow keys scroll back and forth through your level.",
1552 "The button with the wrench and screwdriver, lets you change the",
1553 "settings of your level, including how long it is or what music it will",
1554 "play. When you are ready to give your level a test, click on the little",
1555 "running Tux. If you like the changes you have made to your level,",
1556 "press the red save key to keep them.",
1557 "To change which level in your subset you are editing, press the white",
1558 "up and down arrow keys at the top of the button box.",
1560 "Have fun making levels! If you make some good ones, send them to us on",
1561 "the SuperTux mailing list!",
1566 blue_text->drawf("- Help -", 0, 30, A_HMIDDLE, A_TOP, 2);
1568 for(i = 0; i < sizeof(text)/sizeof(char *); i++)
1569 white_small_text->draw(text[i], 5, 80+(i*white_small_text->h), 1);
1571 gold_text->drawf("Press Any Key to Continue", 0, 440, A_HMIDDLE, A_TOP, 1);
1579 done_ = wait_for_event(event);