1 /***************************************************************************
2 leveleditor.cpp - built'in leveleditor
5 copyright : (C) 2004 by Ricardo Cruz
7 ***************************************************************************/
9 /***************************************************************************
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
16 ***************************************************************************/
23 #include "gui/mousecursor.h"
25 #include "gui/button.h"
26 #include "audio/sound_manager.h"
27 #include "app/gettext.h"
28 #include "app/setup.h"
29 #include "app/globals.h"
30 #include "special/sprite.h"
31 #include "leveleditor.h"
32 #include "resources.h"
34 #include "tile_manager.h"
37 #include "object/gameobjs.h"
38 #include "object/camera.h"
39 #include "object/tilemap.h"
40 #include "object/background.h"
41 #include "gameobjs_bridge.h"
43 LevelEditor::LevelEditor()
48 selection_end = selection_ini = Vector(0,0);
49 left_button = middle_button = mouse_moved = false;
53 cur_layer = LAYER_TILES;
54 level_changed = false;
60 level_subsets = FileSystem::dsubdirs("/levels", "info");
61 subset_menu = new Menu();
62 subset_menu->additem(MN_LABEL,_("Load Subset"),0,0);
63 subset_menu->additem(MN_HL,"",0,0);
65 for(std::set<std::string>::iterator it = level_subsets.begin(); it != level_subsets.end(); ++it, ++i)
66 subset_menu->additem(MN_ACTION, (*it),0,0,i);
67 subset_menu->additem(MN_HL,"",0,0);
68 subset_menu->additem(MN_BACK,_("Back"),0,0);
70 create_subset_menu = new Menu();
71 create_subset_menu->additem(MN_LABEL,_("New Level Subset"),0,0);
72 create_subset_menu->additem(MN_HL,"",0,0);
73 create_subset_menu->additem(MN_TEXTFIELD,_("Filename "),0,0,MN_ID_FILENAME_SUBSET);
74 create_subset_menu->additem(MN_TEXTFIELD,_("Title "),0,0,MN_ID_TITLE_SUBSET);
75 create_subset_menu->additem(MN_TEXTFIELD,_("Description"),0,0,MN_ID_DESCRIPTION_SUBSET);
76 create_subset_menu->additem(MN_ACTION,_("Create"),0,0, MN_ID_CREATE_SUBSET);
77 create_subset_menu->additem(MN_HL,"",0,0);
78 create_subset_menu->additem(MN_BACK,_("Back"),0,0);
80 main_menu = new Menu();
81 main_menu->additem(MN_LABEL,_("Level Editor Menu"),0,0);
82 main_menu->additem(MN_HL,"",0,0);
83 main_menu->additem(MN_ACTION,_("Return to Level Editor"),0,0,MN_ID_RETURN);
84 main_menu->additem(MN_GOTO,_("Create Level Subset"),0,create_subset_menu);
85 main_menu->additem(MN_GOTO,_("Load Level Subset"),0,subset_menu);
86 main_menu->additem(MN_HL,"",0,0);
87 main_menu->additem(MN_ACTION,_("Quit Level Editor"),0,0,MN_ID_QUIT);
89 settings_menu = new Menu();
90 settings_menu->additem(MN_LABEL,_("Level Settings"),0,0);
91 settings_menu->additem(MN_HL,"",0,0);
92 settings_menu->additem(MN_TEXTFIELD,_("Name "),0,0,MN_ID_NAME);
93 settings_menu->additem(MN_TEXTFIELD,_("Author "),0,0,MN_ID_AUTHOR);
94 settings_menu->additem(MN_NUMFIELD, _("Width "),0,0,MN_ID_WIDTH);
95 settings_menu->additem(MN_NUMFIELD, _("Height "),0,0,MN_ID_HEIGHT);
96 settings_menu->additem(MN_HL,"",0,0);
97 settings_menu->additem(MN_ACTION,_("Apply"),0,0,MN_ID_APPLY_SETTINGS);
99 /* Creating button groups */
102 tiles_board = new ButtonGroup(Vector(screen->w - 140, 100),
103 Vector(32,32), Vector(4,8));
105 tiles_board->add_button(Button(img_rubber_bt, _("Eraser"), SDLKey(SDLK_DELETE)), 0);
107 for(id = 1; id < tile_manager->get_max_tileid(); id++)
109 const Tile* tile = tile_manager->get(id);
113 Surface* surface = tile->get_editor_image();
117 Button button = Button(surface, "", SDLKey(0));
118 tiles_board->add_button(button, id);
120 gameobjs_first_id = id;
122 for(id = 0; id < TOTAL_GAMEOBJECTS; id++)
124 // Surface *img = badguy.get_image();
125 // FIXME: get image from object. Using the rubber in the meanwhile.
126 tiles_board->add_button(Button(img_rubber_bt, object_type_to_string(id),
127 SDLKey(SDLK_1+id)), id+gameobjs_first_id);
130 tiles_layer = new ButtonGroup(Vector(12, screen->h-64), Vector(80,20), Vector(1,3));
131 tiles_layer->add_button(Button(img_foreground_bt, _("Edtit foreground tiles"),
132 SDLK_F10), LAYER_FOREGROUNDTILES);
133 tiles_layer->add_button(Button(img_interactive_bt, _("Edit interactive tiles"),
134 SDLK_F11), LAYER_TILES, true);
135 tiles_layer->add_button(Button(img_background_bt, _("Edit background tiles"),
136 SDLK_F12), LAYER_BACKGROUNDTILES);
138 level_options = new ButtonGroup(Vector(screen->w-164, screen->h-36), Vector(32,32), Vector(5,1));
139 level_options->add_pair_of_buttons(Button(img_next_sector_bt, _("Next sector"), SDLKey(0)), BT_NEXT_SECTOR,
140 Button(img_previous_sector_bt, _("Prevous sector"), SDLKey(0)), BT_PREVIOUS_SECTOR);
141 level_options->add_pair_of_buttons(Button(img_next_level_bt, _("Next level"), SDLKey(0)), BT_NEXT_LEVEL,
142 Button(img_previous_level_bt, _("Prevous level"), SDLKey(0)), BT_PREVIOUS_LEVEL);
143 level_options->add_button(Button(img_save_level_bt, _("Save level"), SDLK_F5), BT_LEVEL_SAVE);
144 level_options->add_button(Button(img_test_level_bt, _("Test level"), SDLK_F6), BT_LEVEL_TEST);
145 level_options->add_button(Button(img_setup_level_bt, _("Setup level"), SDLK_F7), BT_LEVEL_SETUP);
148 LevelEditor::~LevelEditor()
154 delete level_options;
157 delete create_subset_menu;
159 delete settings_menu;
165 void LevelEditor::load_buttons_gfx()
167 img_foreground_bt = new Surface(datadir + "/images/leveleditor/foreground.png", true);
168 img_interactive_bt = new Surface(datadir + "/images/leveleditor/interactive.png", true);
169 img_background_bt = new Surface(datadir + "/images/leveleditor/background.png", true);
171 img_save_level_bt = new Surface(datadir + "/images/leveleditor/save-level.png", true);
172 img_test_level_bt = new Surface(datadir + "/images/leveleditor/test-level.png", true);
173 img_setup_level_bt = new Surface(datadir + "/images/leveleditor/setup-level.png", true);
175 img_rubber_bt = new Surface(datadir + "/images/leveleditor/rubber.png", true);
177 img_previous_level_bt = new Surface(datadir + "/images/leveleditor/previous-level.png", true);
178 img_next_level_bt = new Surface(datadir + "/images/leveleditor/next-level.png", true);
179 img_previous_sector_bt = new Surface(datadir + "/images/leveleditor/previous-sector.png", true);
180 img_next_sector_bt = new Surface(datadir + "/images/leveleditor/next-sector.png", true);
183 void LevelEditor::free_buttons_gfx()
185 delete img_foreground_bt;
186 delete img_interactive_bt;
187 delete img_background_bt;
189 delete img_save_level_bt;
190 delete img_test_level_bt;
191 delete img_setup_level_bt;
193 delete img_rubber_bt;
195 delete img_previous_level_bt;
196 delete img_next_level_bt;
197 delete img_previous_sector_bt;
198 delete img_next_sector_bt;
201 void LevelEditor::run(const std::string filename)
203 SoundManager::get()->halt_music();
204 Menu::set_current(0);
206 DrawingContext context;
208 if(!filename.empty())
211 load_level(filename);
214 Menu::set_current(main_menu);
216 mouse_cursor->set_state(MC_NORMAL);
227 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
231 void LevelEditor::events()
235 while(SDL_PollEvent(&event))
237 Menu* menu = Menu::current();
242 if(menu == main_menu)
244 switch (main_menu->check())
247 Menu::set_current(0);
254 else if(menu == create_subset_menu)
256 // activate or deactivate Create button if any filename as been specified
257 if(create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input[0] == '\0')
258 create_subset_menu->get_item_by_id(MN_ID_CREATE_SUBSET).kind = MN_DEACTIVE;
260 create_subset_menu->get_item_by_id(MN_ID_CREATE_SUBSET).kind = MN_ACTION;
262 if(create_subset_menu->check() == MN_ID_CREATE_SUBSET)
263 { // applying settings:
264 std::string subset_name = create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input;
265 LevelSubset::create(subset_name);
268 level_subset = new LevelSubset();
269 level_subset->load(create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input);
271 level_subset->title = create_subset_menu->get_item_by_id(MN_ID_TITLE_SUBSET).input;
272 level_subset->description = create_subset_menu->get_item_by_id(MN_ID_DESCRIPTION_SUBSET).input;
273 //FIXME: generate better level filenames
274 level_subset->add_level(subset_name+'/'+"new_level.stl");
275 Level* newlevel = new Level();
276 newlevel->add_sector(create_sector("main", 25, 19));
277 newlevel->save(level_subset->get_level_filename(0));
278 level_subset->save();
282 create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).change_input("");
283 create_subset_menu->get_item_by_id(MN_ID_TITLE_SUBSET).change_input("");
284 create_subset_menu->get_item_by_id(MN_ID_DESCRIPTION_SUBSET).change_input("");
287 else if(menu == subset_menu)
289 int i = subset_menu->check();
292 std::set<std::string>::iterator it = level_subsets.begin();
293 for(int t = 0; t < i; t++)
295 load_level_subset(*it);
296 Menu::set_current(0);
299 else if(menu == settings_menu)
301 if(settings_menu->check() == MN_ID_APPLY_SETTINGS)
302 { // applying settings:
303 level_changed = true;
305 level->name = settings_menu->get_item_by_id(MN_ID_NAME).input;
306 level->author = settings_menu->get_item_by_id(MN_ID_AUTHOR).input;
308 solids->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()),
309 atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str()));
310 foregrounds->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()),
311 atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str()));
312 backgrounds->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()),
313 atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str()));
315 Menu::set_current(0);
319 // check for events in buttons
320 else if(tiles_board->event(event))
322 std::vector <int> vector;
323 vector.push_back(tiles_board->selected_id());
326 selection.push_back(vector);
329 else if(tiles_layer->event(event))
331 cur_layer = tiles_layer->selected_id();
334 else if(level_options->event(event))
336 switch(level_options->selected_id())
345 Menu::set_current(settings_menu);
348 if(level_nb + 1 < level_subset->get_num_levels())
349 load_level(level_nb + 1);
353 sprintf(str,_("Level %d doesn't exist. Create it?"), level_nb + 2);
354 if(confirm_dialog(NULL, str))
356 level_subset->add_level("new_level.stl");
357 Level* newlevel = new Level();
358 newlevel->add_sector(create_sector("main", 25, 19));
359 newlevel->save(level_subset->get_level_filename(level_nb + 1));
360 level_subset->save();
361 load_level(level_nb + 1);
365 case BT_PREVIOUS_LEVEL:
366 if(level_nb - 1 >= 0)
367 load_level(level_nb - 1);
370 std::cerr << "next sector.\n";
371 std::cerr << "total sectors: " << level->get_total_sectors() << std::endl;
372 load_sector(level->get_next_sector(sector));
374 case BT_PREVIOUS_SECTOR:
375 std::cerr << "previous sector.\n";
376 load_sector(level->get_previous_sector(sector));
379 level_options->set_unselected();
386 case SDL_MOUSEMOTION:
388 if(SDL_GetMouseState(NULL, NULL)&SDL_BUTTON(SDL_BUTTON_RIGHT))
389 { // movement like in strategy games
390 scroll.x += -1 * event.motion.xrel;
391 scroll.y += -1 * event.motion.yrel;
395 case SDL_MOUSEBUTTONDOWN:
397 if(event.button.button == SDL_BUTTON_LEFT)
399 else if(event.button.button == SDL_BUTTON_MIDDLE)
401 middle_button = true;
402 selection_ini = Vector(event.button.x, event.button.y);
406 case SDL_MOUSEBUTTONUP:
408 if(event.button.button == SDL_BUTTON_LEFT)
410 else if(event.button.button == SDL_BUTTON_MIDDLE)
412 middle_button = false;
413 selection_end = Vector(event.button.x, event.button.y);
415 if(selection_end.x < selection_ini.x)
417 float t = selection_ini.x;
418 selection_ini.x = selection_end.x;
421 if(selection_end.y < selection_ini.y)
423 float t = selection_ini.y;
424 selection_ini.y = selection_end.y;
429 std::vector <int> vector;
431 TileMap* tilemap = 0;
432 if(cur_layer == LAYER_FOREGROUNDTILES)
433 tilemap = foregrounds;
434 else if(cur_layer == LAYER_TILES)
436 else if(cur_layer == LAYER_BACKGROUNDTILES)
437 tilemap = backgrounds;
439 for(int x = 0; x < (int)((selection_end.x - selection_ini.x)*zoom / 32) + 1; x++)
442 for(int y = 0; y < (int)((selection_end.y - selection_ini.y)*zoom / 32) + 1; y++)
444 vector.push_back(tilemap->get_tile(x +
445 (int)(((selection_ini.x+scroll.x)*zoom)/32),
446 y + (int)(((selection_ini.y+scroll.y)*zoom)/32))->getID());
448 selection.push_back(vector);
453 case SDL_KEYDOWN: // key pressed
454 switch(event.key.keysym.sym)
457 Menu::set_current(main_menu);
459 /* scrolling related events: */
464 scroll.x = sector->solids->get_height()*32 - screen->w;
497 show_grid = !show_grid;
504 case SDL_QUIT: // window closed
515 void LevelEditor::action()
517 mouse_cursor->set_state(MC_NORMAL);
518 if(tiles_board->is_hover() || tiles_layer->is_hover() || level_options->is_hover())
519 mouse_cursor->set_state(MC_LINK);
523 // don't scroll before the start or after the level's end
524 float width = sector->solids->get_width() * 32;
525 float height = sector->solids->get_height() * 32;
527 if(scroll.x < -screen->w/2)
528 scroll.x = -screen->w/2;
529 if(scroll.x > width - screen->w/2)
530 scroll.x = width - screen->w/2;
531 if(scroll.y < -screen->h/2)
532 scroll.y = -screen->h/2;
533 if(scroll.y > height - screen->h/2)
534 scroll.y = height - screen->h/2;
536 // set camera translation, since BadGuys like it
537 sector->camera->set_scrolling((int)scroll.x, (int)scroll.y);
539 if(left_button && mouse_moved)
540 for(unsigned int x = 0; x < selection.size(); x++)
541 for(unsigned int y = 0; y < selection[x].size(); y++)
542 change((int)(scroll.x + event.button.x) + (x*32),
543 (int)(scroll.y + event.button.y) + (y*32), selection[x][y],
548 #define FADING_TIME .6
550 void LevelEditor::draw(DrawingContext& context)
552 context.draw_text(white_text, _("Level Editor"), Vector(10, 5), LEFT_ALLIGN, LAYER_GUI);
553 mouse_cursor->draw(context);
555 // draw a filled background
556 context.draw_filled_rect(Vector(0,0), Vector(screen->w,screen->h), Color(60,60,60), LAYER_BACKGROUND0-1);
558 if(level_name_timer.check())
560 context.push_transform();
561 if(level_name_timer.get_timeleft() < FADING_TIME)
562 context.set_alpha(int(level_name_timer.get_timeleft() * 255 / FADING_TIME));
564 context.draw_text(gold_text, level->name, Vector(screen->w/2, 30), CENTER_ALLIGN, LAYER_GUI);
568 sprintf(str, "%i/%i", level_nb+1, level_subset->get_num_levels());
569 context.draw_text(gold_text, str, Vector(screen->w/2, 50), CENTER_ALLIGN, LAYER_GUI);
572 context.pop_transform();
575 context.draw_text(white_small_text, _("F1 for help"), Vector(5, 510), LEFT_ALLIGN, LAYER_GUI-10);
577 context.draw_text(white_small_text, _("Choose a level subset"), Vector(5, 510), LEFT_ALLIGN, LAYER_GUI-10);
579 Menu* menu = Menu::current();
584 tiles_board->draw(context);
585 tiles_layer->draw(context);
586 level_options->draw(context);
594 context.set_drawing_effect(SEMI_TRANSPARENT);
598 if(selection[0][0] == 0 && selection.size() == 1)
599 context.draw_surface(img_rubber_bt, Vector(event.button.x - 8,
600 event.button.y - 8), LAYER_GUI-2);
601 else if(selection[0][0] >= gameobjs_first_id || selection[0][0] < 0)
603 // FIXME: this should draw an object image near cursor
605 int id = selection[0][0];
607 if(id == OBJ_TRAMPOLINE)
608 context.draw_surface(img_trampoline[0].get_frame(0), Vector(event.button.x - 8,
609 event.button.y - 8), LAYER_GUI-2);
610 else if(id == OBJ_FLYING_PLATFORM)
611 context.draw_surface(img_flying_platform->get_frame(0), Vector(event.button.x - 8,
612 event.button.y - 8), LAYER_GUI-2);
615 /*context.draw_surface(door->get_frame(0), Vector(event.button.x - 8,
616 event.button.y - 8), LAYER_GUI-2);*/
620 BadGuyKind kind = BadGuyKind((-id)-1);
621 BadGuy badguy(kind, 0,0);
622 badguy.activate(LEFT);
623 Surface *img = badguy.get_image();
625 context.draw_surface(img, Vector(event.button.x - 8,
626 event.button.y - 8), LAYER_GUI-2);
632 for(unsigned int x = 0; x < selection.size(); x++)
633 for(unsigned int y = 0; y < selection[x].size(); y++) {
634 const Tile* tile = tile_manager->get(selection[x][y]);
636 Vector(event.button.x + x*32 - 8, event.button.y + y*32 - 8),
641 context.set_drawing_effect(NONE_EFFECT);
644 context.draw_filled_rect(Vector(std::min((int)selection_ini.x, (int)event.button.x)*zoom,
645 std::min((int)selection_ini.y, (int)event.button.y))*zoom,
646 Vector(abs(event.button.x - (int)selection_ini.x)*zoom,
647 abs(event.button.y - (int)selection_ini.y)*zoom),
648 Color(170,255,170,128), LAYER_GUI-2);
652 for(int x = 0; x < screen->w / (32*zoom); x++)
654 int pos = (int)(x*32*zoom) - ((int)scroll.x % 32);
655 context.draw_filled_rect(Vector (pos, 0), Vector(1, screen->h),
656 Color(225, 225, 225), LAYER_GUI-50);
658 for(int y = 0; y < screen->h / (32*zoom); y++)
660 int pos = (int)(y*32*zoom) - ((int)scroll.y % 32);
661 context.draw_filled_rect(Vector (0, pos), Vector(screen->w, 1),
662 Color(225, 225, 225), LAYER_GUI-50);
666 context.push_transform();
667 context.set_translation(scroll);
668 context.set_zooming(zoom);
670 for(Sector::GameObjects::iterator i = sector->gameobjects.begin(); i != sector->gameobjects.end(); ++i)
672 TileMap* tilemap = dynamic_cast<TileMap*> (*i);
674 { // draw the non-selected tiles semi-transparently
675 context.push_transform();
677 if(tilemap->get_layer() != cur_layer)
678 context.set_drawing_effect(SEMI_TRANSPARENT);
681 context.pop_transform();
684 Background* background = dynamic_cast<Background*> (*i);
686 { // don't resize background
687 context.push_transform();
688 context.set_zooming(1.0);
690 context.pop_transform();
696 context.pop_transform();
699 context.draw_filled_rect(Vector(0,0), Vector(screen->w,screen->h),Color(0,0,0), LAYER_BACKGROUND0);
701 context.do_drawing();
704 void LevelEditor::load_level_subset(std::string filename)
707 level_subset = new LevelSubset();
708 level_subset->load(filename.c_str());
712 void LevelEditor::load_level(std::string filename)
716 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
722 level_filename = filename;
726 level->load(filename);
729 level_name_timer.start(3000);
730 scroll.x = scroll.y = 0;
731 level_changed = false;
733 settings_menu->get_item_by_id(MN_ID_NAME).change_input(level->name.c_str());
734 settings_menu->get_item_by_id(MN_ID_AUTHOR).change_input(level->author.c_str());
737 void LevelEditor::load_level(int nb)
741 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
748 level_filename = level_subset->get_level_filename(level_nb);
750 load_level(level_filename);
753 void LevelEditor::load_sector(std::string name)
756 sector = level->get_sector(sector_name);
758 Termination::abort("Level has no " + sector_name + " sector.", "");
763 void LevelEditor::load_sector(Sector* sector_)
767 if(!confirm_dialog(NULL, _("No more sectors exist. Create another?")))
769 sector_ = create_sector("new_sector",25,19);
770 level->add_sector(sector_);
775 /* Load sector stuff */
777 sector->update_game_objects();
779 foregrounds = solids = backgrounds = 0;
780 /* Point foregrounds, backgrounds, solids to its layer */
781 for(Sector::GameObjects::iterator i = sector->gameobjects.begin(); i != sector->gameobjects.end(); i++)
783 TileMap* tilemap = dynamic_cast<TileMap*> (*i);
786 if(tilemap->get_layer() == LAYER_FOREGROUNDTILES)
787 foregrounds = tilemap;
788 else if(tilemap->get_layer() == LAYER_TILES)
790 else if(tilemap->get_layer() == LAYER_BACKGROUNDTILES)
791 backgrounds = tilemap;
797 TileMap* tilemap = new TileMap(LAYER_FOREGROUNDTILES, false, solids->get_width(), solids->get_height());
798 sector->add_object(tilemap);
799 sector->update_game_objects();
803 TileMap* tilemap = new TileMap(LAYER_BACKGROUNDTILES, false, solids->get_width(), solids->get_height());
804 sector->add_object(tilemap);
805 sector->update_game_objects();
809 sprintf(str, "%i", solids->get_width());
810 settings_menu->get_item_by_id(MN_ID_WIDTH).change_input(str);
811 sprintf(str, "%i", solids->get_height());
812 settings_menu->get_item_by_id(MN_ID_HEIGHT).change_input(str);
815 void LevelEditor::save_level()
817 level->save(level_filename);
818 level_changed = false;
821 void LevelEditor::test_level()
824 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
830 GameSession session(level_filename, ST_GL_TEST);
832 // player_status.reset();
833 SoundManager::get()->halt_music();
836 void LevelEditor::change(int x, int y, int newtile, int layer)
839 // find the tilemap of the current layer, and then change the tile
840 if(x < 0 || (unsigned int)x >= sector->solids->get_width()*32 ||
841 y < 0 || (unsigned int)y >= sector->solids->get_height()*32)
844 level_changed = true;
847 { // no need to do this for normal view (no zoom)
848 x = (int)(x * (zoom*32) / 32);
849 y = (int)(y * (zoom*32) / 32);
852 if(newtile >= gameobjs_first_id) // add object
854 // remove an active tile or object that might be there
855 change(x, y, 0, LAYER_TILES);
857 create_object((GameObjectsType)(newtile-gameobjs_first_id),Vector(x,y));
859 sector->update_game_objects();
860 } else if(cur_layer == LAYER_FOREGROUNDTILES) {
861 foregrounds->change(x/32, y/32, newtile);
862 } else if(cur_layer == LAYER_TILES) {
863 // remove a bad guy if it's there
864 // we /32 in order to round numbers
865 for(Sector::GameObjects::iterator i = sector->gameobjects.begin();
866 i < sector->gameobjects.end(); i++) {
867 // FIXME: if there is a game objs in here, remove it!
869 BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
871 if((int)badguy->base.x/32 == x/32 && (int)badguy->base.y/32 == y/32)
872 sector->gameobjects.erase(i);
873 Trampoline* trampoline = dynamic_cast<Trampoline*> (*i);
876 if((int)trampoline->base.x/32 == x/32 && (int)trampoline->base.y/32 == y/32)
877 sector->gameobjects.erase(i);
879 FlyingPlatform* flying_platform = dynamic_cast<FlyingPlatform*> (*i);
881 if((int)flying_platform->base.x/32 == x/32 && (int)flying_platform->base.y/32 == y/32)
882 sector->gameobjects.erase(i);
883 Door* door = dynamic_cast<Door*> (*i);
885 if((int)door->get_area().x/32 == x/32 && (int)door->get_area().y/32 == y/32)
886 sector->gameobjects.erase(i);
889 sector->update_game_objects();
890 solids->change(x/32, y/32, newtile);
891 } else if(cur_layer == LAYER_BACKGROUNDTILES)
892 backgrounds->change(x/32, y/32, newtile);
895 void LevelEditor::show_help()
897 DrawingContext context;
899 bool show_grid_t = show_grid;
901 mouse_cursor->set_state(MC_HIDE);
905 const char *text1[] = {
906 _("This is the built-in level editor. Its aim is to be intuitive\n"
907 "and simple to use, so it should be pretty straightforward.\n"
909 "To open a level, first you'll have to select a level subset from\n"
910 "the menu (or create your own).\n"
911 "A level subset is basically a collection of levels.\n"
912 "They can then be played from the Contrib menu.\n"
914 "To access the menu from the level editor, just press Esc.\n"
916 "You are currently looking at the level. To scroll it, just\n"
917 "press the right mouse button and drag the mouse. It will move like\n"
919 "You can also use the arrow keys and Page Up/Down.\n"
921 "'+' and '-' keys can be used to zoom the level in/out.\n"
923 "You probably already noticed those floating groups of buttons.\n"
924 "Each one serves a different purpose. To select a certain button\n"
925 "just press the Left mouse button on it. A few buttons have key\n"
926 "shortcuts. You can find them by pressing the Right mouse button on\n"
927 "a button. That will also show what that button does.\n"
928 "Groups of buttons can also be moved around by just dragging them,\n"
929 "while pressing the Left mouse button.\n"
931 "Let's learn a bit of what each group of buttons does, shall we?\n"
933 "To starting putting tiles and objects around use the bigger group\n"
934 "of buttons. Each button is a different tile. To put it on the level,\n"
935 "just press it and then left click in the level.\n"
936 "You can also copy tiles from the level by using the middle mouse button.\n"
937 "Use the mouse wheel to scroll that group of buttons. You will find\n"
938 "enemies and game objects in the bottom.\n")
941 const char *text2[] = {
942 _("The Foreground/Interactive/Background buttons may be used to\n"
943 "see and edit the respective layer. Levels have three tiles layers:\n"
944 "Foreground - tiles are drawn on top of everything and have no contact\n"
946 "Interactive - these are the tiles that have contact with the player.\n"
947 "Background - tiles are drawn underneath everything and have no contact\n"
949 "The unselected layers will be drawn semi-transparently.\n"
951 "Last, but not least, the group of buttons that's left serves\n"
952 "to do related actions with the level.\n"
953 "From left to right:\n"
954 "Mini arrows - can be used to choose other sectors.\n"
955 "Sectors are mini-levels, so to speak, that can be accessed using a door.\n"
956 "Big arrows - choose other level in the same level subset.\n"
957 "Diskette - save the level\n"
958 "Tux - test the level\n"
959 "Tools - set a few settings for the level, including resizing it.\n"
961 "We have reached the end of this Howto.\n"
963 "Don't forget to send us a few cool levels. :)\n"
966 " SuperTux development team\n"
968 "PS: If you are looking for something more powerful, you might like to\n"
969 "try FlexLay. FlexLay is a level editor that supports several games,\n"
970 "including SuperTux. It is an independent project.\n"
971 "Webpage: http://pingus.seul.org/~grumbel/flexlay/")
974 const char **text[] = { text1, text2 };
978 for(unsigned int i = 0; i < sizeof(text) / sizeof(text[0]); i++)
982 context.draw_text(blue_text, _("- Level Editor's Help -"), Vector(screen->w/2, 60), CENTER_ALLIGN, LAYER_GUI);
984 context.draw_text(white_small_text, *text[i], Vector(20, 120), LEFT_ALLIGN, LAYER_GUI);
986 sprintf(str,_("Press any key to continue - Page %d/%d"), i+1, sizeof(text) / sizeof(text[0]));
987 context.draw_text(gold_text, str, Vector(screen->w/2, screen->h-60), CENTER_ALLIGN, LAYER_GUI);
989 context.do_drawing();
995 done = wait_for_event(event);
1000 show_grid = show_grid_t;
1001 mouse_cursor->set_state(MC_NORMAL);
1005 LevelEditor::create_sector(const std::string& name, size_t width, size_t height)
1007 Sector* sector = new Sector;
1008 sector->set_name(name);
1010 sector->add_object(new TileMap(LAYER_BACKGROUNDTILES, false, width, height));
1011 sector->add_object(new TileMap(LAYER_TILES, true, width, height));
1012 sector->add_object(new TileMap(LAYER_FOREGROUNDTILES, false, width, height));
1013 sector->add_object(new Camera(sector));
1014 sector->update_game_objects();