4 // Copyright (C) 2005 Matthias Braun <matze@braunis.de>
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
21 /***************************************************************************
22 leveleditor.cpp - built'in leveleditor
25 copyright : (C) 2004 by Ricardo Cruz
26 email : rick2@aeiou.pt
27 ***************************************************************************/
29 /***************************************************************************
31 * This program is free software; you can redistribute it and/or modify *
32 * it under the terms of the GNU General Public License as published by *
33 * the Free Software Foundation; either version 2 of the License, or *
34 * (at your option) any later version. *
36 ***************************************************************************/
43 #include "gui/mousecursor.h"
45 #include "gui/button.h"
46 #include "audio/sound_manager.h"
47 #include "app/gettext.h"
48 #include "app/setup.h"
49 #include "app/globals.h"
50 #include "sprite/sprite.h"
51 #include "leveleditor.h"
52 #include "resources.h"
54 #include "tile_manager.h"
56 #include "game_session.h"
57 #include "object_factory.h"
58 #include "object/gameobjs.h"
59 #include "object/camera.h"
60 #include "object/tilemap.h"
61 #include "object/background.h"
63 LevelEditor::LevelEditor()
68 selection_end = selection_ini = Vector(0,0);
69 left_button = middle_button = mouse_moved = false;
73 cur_layer = LAYER_TILES;
74 level_changed = false;
80 level_subsets = FileSystem::dsubdirs("/levels", "info");
81 subset_menu = new Menu();
82 subset_menu->add_label(_("Load Subset"));
83 subset_menu->additem(MN_HL,"",0,0);
85 for(std::set<std::string>::iterator it = level_subsets.begin(); it != level_subsets.end(); ++it, ++i)
86 subset_menu->additem(MN_ACTION, (*it),0,0,i);
87 subset_menu->additem(MN_HL,"",0,0);
88 subset_menu->additem(MN_BACK,_("Back"),0,0);
90 create_subset_menu = new Menu();
91 create_subset_menu->additem(MN_LABEL,_("New Level Subset"),0,0);
92 create_subset_menu->additem(MN_HL,"",0,0);
93 create_subset_menu->additem(MN_TEXTFIELD,_("Filename "),0,0,MN_ID_FILENAME_SUBSET);
94 create_subset_menu->additem(MN_TEXTFIELD,_("Title "),0,0,MN_ID_TITLE_SUBSET);
95 create_subset_menu->additem(MN_TEXTFIELD,_("Description"),0,0,MN_ID_DESCRIPTION_SUBSET);
96 create_subset_menu->additem(MN_ACTION,_("Create"),0,0, MN_ID_CREATE_SUBSET);
97 create_subset_menu->additem(MN_HL,"",0,0);
98 create_subset_menu->additem(MN_BACK,_("Back"),0,0);
100 main_menu = new Menu();
101 main_menu->additem(MN_LABEL,_("Level Editor Menu"),0,0);
102 main_menu->additem(MN_HL,"",0,0);
103 main_menu->additem(MN_ACTION,_("Return to Level Editor"),0,0,MN_ID_RETURN);
104 main_menu->additem(MN_GOTO,_("Create Level Subset"),0,create_subset_menu);
105 main_menu->additem(MN_GOTO,_("Load Level Subset"),0,subset_menu);
106 main_menu->additem(MN_HL,"",0,0);
107 main_menu->additem(MN_ACTION,_("Quit Level Editor"),0,0,MN_ID_QUIT);
109 settings_menu = new Menu();
110 settings_menu->additem(MN_LABEL,_("Level Settings"),0,0);
111 settings_menu->additem(MN_HL,"",0,0);
112 settings_menu->additem(MN_TEXTFIELD,_("Name "),0,0,MN_ID_NAME);
113 settings_menu->additem(MN_TEXTFIELD,_("Author "),0,0,MN_ID_AUTHOR);
114 settings_menu->additem(MN_NUMFIELD, _("Width "),0,0,MN_ID_WIDTH);
115 settings_menu->additem(MN_NUMFIELD, _("Height "),0,0,MN_ID_HEIGHT);
116 settings_menu->additem(MN_HL,"",0,0);
117 settings_menu->additem(MN_ACTION,_("Apply"),0,0,MN_ID_APPLY_SETTINGS);
119 /* Creating button groups */
122 tiles_board = new ButtonGroup(Vector(SCREEN_WIDTH - 140, 100),
123 Vector(32,32), Vector(4,8));
125 tiles_board->add_button(Button(img_rubber_bt, _("Eraser"), SDLKey(SDLK_DELETE)), 0);
127 for(id = 1; id < tile_manager->get_max_tileid(); id++)
129 const Tile* tile = tile_manager->get(id);
133 Surface* surface = tile->get_editor_image();
137 Button button = Button(surface, "", SDLKey(0));
138 tiles_board->add_button(button, id);
140 gameobjs_first_id = id;
142 for(Factories::iterator i = object_factories->begin();
143 i != object_factories->end(); ++i) {
145 // Surface *img = badguy.get_image();
146 // FIXME: get image from object. Using the rubber in the meanwhile.
147 tiles_board->add_button(Button(img_rubber_bt, i->first,
148 SDLKey(SDLK_1+id)), id++);
151 tiles_layer = new ButtonGroup(Vector(12, SCREEN_HEIGHT-64), Vector(80,20), Vector(1,3));
152 tiles_layer->add_button(Button(img_foreground_bt, _("Edtit foreground tiles"),
153 SDLK_F10), LAYER_FOREGROUNDTILES);
154 tiles_layer->add_button(Button(img_interactive_bt, _("Edit interactive tiles"),
155 SDLK_F11), LAYER_TILES, true);
156 tiles_layer->add_button(Button(img_background_bt, _("Edit background tiles"),
157 SDLK_F12), LAYER_BACKGROUNDTILES);
159 level_options = new ButtonGroup(Vector(SCREEN_WIDTH-164, SCREEN_HEIGHT-36), Vector(32,32), Vector(5,1));
160 level_options->add_pair_of_buttons(Button(img_next_sector_bt, _("Next sector"), SDLKey(0)), BT_NEXT_SECTOR,
161 Button(img_previous_sector_bt, _("Prevous sector"), SDLKey(0)), BT_PREVIOUS_SECTOR);
162 level_options->add_pair_of_buttons(Button(img_next_level_bt, _("Next level"), SDLKey(0)), BT_NEXT_LEVEL,
163 Button(img_previous_level_bt, _("Prevous level"), SDLKey(0)), BT_PREVIOUS_LEVEL);
164 level_options->add_button(Button(img_save_level_bt, _("Save level"), SDLK_F5), BT_LEVEL_SAVE);
165 level_options->add_button(Button(img_test_level_bt, _("Test level"), SDLK_F6), BT_LEVEL_TEST);
166 level_options->add_button(Button(img_setup_level_bt, _("Setup level"), SDLK_F7), BT_LEVEL_SETUP);
169 LevelEditor::~LevelEditor()
175 delete level_options;
178 delete create_subset_menu;
180 delete settings_menu;
186 void LevelEditor::load_buttons_gfx()
188 img_foreground_bt = new Surface(datadir + "/images/leveleditor/foreground.png", true);
189 img_interactive_bt = new Surface(datadir + "/images/leveleditor/interactive.png", true);
190 img_background_bt = new Surface(datadir + "/images/leveleditor/background.png", true);
192 img_save_level_bt = new Surface(datadir + "/images/leveleditor/save-level.png", true);
193 img_test_level_bt = new Surface(datadir + "/images/leveleditor/test-level.png", true);
194 img_setup_level_bt = new Surface(datadir + "/images/leveleditor/setup-level.png", true);
196 img_rubber_bt = new Surface(datadir + "/images/leveleditor/rubber.png", true);
198 img_previous_level_bt = new Surface(datadir + "/images/leveleditor/previous-level.png", true);
199 img_next_level_bt = new Surface(datadir + "/images/leveleditor/next-level.png", true);
200 img_previous_sector_bt = new Surface(datadir + "/images/leveleditor/previous-sector.png", true);
201 img_next_sector_bt = new Surface(datadir + "/images/leveleditor/next-sector.png", true);
204 void LevelEditor::free_buttons_gfx()
206 delete img_foreground_bt;
207 delete img_interactive_bt;
208 delete img_background_bt;
210 delete img_save_level_bt;
211 delete img_test_level_bt;
212 delete img_setup_level_bt;
214 delete img_rubber_bt;
216 delete img_previous_level_bt;
217 delete img_next_level_bt;
218 delete img_previous_sector_bt;
219 delete img_next_sector_bt;
222 void LevelEditor::run(const std::string filename)
224 sound_manager->halt_music();
225 Menu::set_current(0);
227 DrawingContext context;
229 if(!filename.empty())
232 load_level(filename);
235 Menu::set_current(main_menu);
237 mouse_cursor->set_state(MC_NORMAL);
248 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
252 void LevelEditor::events()
257 while(SDL_PollEvent(&event))
259 Menu* menu = Menu::current();
264 if(menu == main_menu)
266 switch (main_menu->check())
269 Menu::set_current(0);
276 else if(menu == create_subset_menu)
278 // activate or deactivate Create button if any filename as been specified
279 if(create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input[0] == '\0')
280 create_subset_menu->get_item_by_id(MN_ID_CREATE_SUBSET).kind = MN_DEACTIVE;
282 create_subset_menu->get_item_by_id(MN_ID_CREATE_SUBSET).kind = MN_ACTION;
284 if(create_subset_menu->check() == MN_ID_CREATE_SUBSET)
285 { // applying settings:
286 std::string subset_name = create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input;
287 LevelSubset::create(subset_name);
290 level_subset = new LevelSubset();
291 level_subset->load(create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input);
293 level_subset->title = create_subset_menu->get_item_by_id(MN_ID_TITLE_SUBSET).input;
294 level_subset->description = create_subset_menu->get_item_by_id(MN_ID_DESCRIPTION_SUBSET).input;
295 //FIXME: generate better level filenames
296 level_subset->add_level(subset_name+'/'+"new_level.stl");
297 Level* newlevel = new Level();
298 newlevel->add_sector(create_sector("main", 25, 19));
299 newlevel->save(level_subset->get_level_filename(0));
300 level_subset->save();
304 create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).change_input("");
305 create_subset_menu->get_item_by_id(MN_ID_TITLE_SUBSET).change_input("");
306 create_subset_menu->get_item_by_id(MN_ID_DESCRIPTION_SUBSET).change_input("");
309 else if(menu == subset_menu)
311 int i = subset_menu->check();
314 std::set<std::string>::iterator it = level_subsets.begin();
315 for(int t = 0; t < i; t++)
317 load_level_subset(*it);
318 Menu::set_current(0);
321 else if(menu == settings_menu)
323 if(settings_menu->check() == MN_ID_APPLY_SETTINGS)
324 { // applying settings:
325 level_changed = true;
327 level->name = settings_menu->get_item_by_id(MN_ID_NAME).input;
328 level->author = settings_menu->get_item_by_id(MN_ID_AUTHOR).input;
330 solids->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()),
331 atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str()));
332 foregrounds->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()),
333 atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str()));
334 backgrounds->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()),
335 atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str()));
337 Menu::set_current(0);
341 // check for events in buttons
342 else if(tiles_board->event(event))
344 std::vector <int> vector;
345 vector.push_back(tiles_board->selected_id());
348 selection.push_back(vector);
351 else if(tiles_layer->event(event))
353 cur_layer = tiles_layer->selected_id();
356 else if(level_options->event(event))
358 switch(level_options->selected_id())
367 Menu::set_current(settings_menu);
370 if(level_nb + 1 < level_subset->get_num_levels())
371 load_level(level_nb + 1);
375 sprintf(str,_("Level %d doesn't exist. Create it?"), level_nb + 2);
376 if(confirm_dialog(NULL, str))
378 level_subset->add_level("new_level.stl");
379 Level* newlevel = new Level();
380 newlevel->add_sector(create_sector("main", 25, 19));
381 newlevel->save(level_subset->get_level_filename(level_nb + 1));
382 level_subset->save();
383 load_level(level_nb + 1);
387 case BT_PREVIOUS_LEVEL:
388 if(level_nb - 1 >= 0)
389 load_level(level_nb - 1);
392 std::cerr << "next sector.\n";
393 load_sector(sectornum+1);
395 case BT_PREVIOUS_SECTOR:
396 std::cerr << "previous sector.\n";
398 load_sector(sectornum-1);
401 level_options->set_unselected();
408 case SDL_MOUSEMOTION:
410 mouse_x = event.motion.x;
411 mouse_y = event.motion.y;
412 if(SDL_GetMouseState(NULL, NULL)&SDL_BUTTON(SDL_BUTTON_RIGHT))
413 { // movement like in strategy games
414 scroll.x += -1 * event.motion.xrel;
415 scroll.y += -1 * event.motion.yrel;
419 case SDL_MOUSEBUTTONDOWN:
421 mouse_x = event.motion.x;
422 mouse_y = event.motion.y;
423 if(event.button.button == SDL_BUTTON_LEFT)
425 else if(event.button.button == SDL_BUTTON_MIDDLE)
427 middle_button = true;
428 selection_ini = Vector(event.button.x, event.button.y);
432 case SDL_MOUSEBUTTONUP:
434 mouse_x = event.motion.x;
435 mouse_y = event.motion.y;
436 if(event.button.button == SDL_BUTTON_LEFT)
438 else if(event.button.button == SDL_BUTTON_MIDDLE)
440 middle_button = false;
441 selection_end = Vector(event.button.x, event.button.y);
443 if(selection_end.x < selection_ini.x)
445 float t = selection_ini.x;
446 selection_ini.x = selection_end.x;
449 if(selection_end.y < selection_ini.y)
451 float t = selection_ini.y;
452 selection_ini.y = selection_end.y;
457 std::vector <int> vector;
459 TileMap* tilemap = 0;
460 if(cur_layer == LAYER_FOREGROUNDTILES)
461 tilemap = foregrounds;
462 else if(cur_layer == LAYER_TILES)
464 else if(cur_layer == LAYER_BACKGROUNDTILES)
465 tilemap = backgrounds;
467 for(int x = 0; x < (int)((selection_end.x - selection_ini.x)*zoom / 32) + 1; x++)
470 for(int y = 0; y < (int)((selection_end.y - selection_ini.y)*zoom / 32) + 1; y++)
472 vector.push_back(tilemap->get_tile(x +
473 (int)(((selection_ini.x+scroll.x)*zoom)/32),
474 y + (int)(((selection_ini.y+scroll.y)*zoom)/32))->getID());
476 selection.push_back(vector);
481 case SDL_KEYDOWN: // key pressed
482 switch(event.key.keysym.sym)
485 Menu::set_current(main_menu);
487 /* scrolling related events: */
492 scroll.x = sector->solids->get_height()*32 - SCREEN_WIDTH;
525 show_grid = !show_grid;
532 case SDL_QUIT: // window closed
543 void LevelEditor::action()
545 mouse_cursor->set_state(MC_NORMAL);
546 if(tiles_board->is_hover() || tiles_layer->is_hover() || level_options->is_hover())
547 mouse_cursor->set_state(MC_LINK);
551 // don't scroll before the start or after the level's end
552 float width = sector->solids->get_width() * 32;
553 float height = sector->solids->get_height() * 32;
555 if(scroll.x < -SCREEN_WIDTH/2)
556 scroll.x = -SCREEN_WIDTH/2;
557 if(scroll.x > width - SCREEN_WIDTH/2)
558 scroll.x = width - SCREEN_WIDTH/2;
559 if(scroll.y < -SCREEN_HEIGHT/2)
560 scroll.y = -SCREEN_HEIGHT/2;
561 if(scroll.y > height - SCREEN_HEIGHT/2)
562 scroll.y = height - SCREEN_HEIGHT/2;
564 // set camera translation, since BadGuys like it
565 sector->camera->set_scrolling((int)scroll.x, (int)scroll.y);
567 if(left_button && mouse_moved)
568 for(unsigned int x = 0; x < selection.size(); x++)
569 for(unsigned int y = 0; y < selection[x].size(); y++)
570 change((int)(scroll.x + mouse_x) + (x*32),
571 (int)(scroll.y + mouse_y) + (y*32), selection[x][y],
576 #define FADING_TIME .6
578 void LevelEditor::draw(DrawingContext& context)
580 context.draw_text(white_text, _("Level Editor"), Vector(10, 5), LEFT_ALLIGN, LAYER_GUI);
581 mouse_cursor->draw(context);
583 // draw a filled background
584 context.draw_filled_rect(Vector(0,0), Vector(SCREEN_WIDTH,SCREEN_HEIGHT), Color(60,60,60), LAYER_BACKGROUND0-1);
586 if(level_name_timer.check())
588 context.push_transform();
589 if(level_name_timer.get_timeleft() < FADING_TIME)
590 context.set_alpha(int(level_name_timer.get_timeleft() * 255 / FADING_TIME));
592 context.draw_text(gold_text, level->name, Vector(SCREEN_WIDTH/2, 30), CENTER_ALLIGN, LAYER_GUI);
596 sprintf(str, "%i/%i", level_nb+1, level_subset->get_num_levels());
597 context.draw_text(gold_text, str, Vector(SCREEN_WIDTH/2, 50), CENTER_ALLIGN, LAYER_GUI);
600 context.pop_transform();
603 context.draw_text(white_small_text, _("F1 for help"), Vector(5, 510), LEFT_ALLIGN, LAYER_GUI-10);
605 context.draw_text(white_small_text, _("Choose a level subset"), Vector(5, 510), LEFT_ALLIGN, LAYER_GUI-10);
607 Menu* menu = Menu::current();
612 tiles_board->draw(context);
613 tiles_layer->draw(context);
614 level_options->draw(context);
622 context.set_drawing_effect(SEMI_TRANSPARENT);
626 if(selection[0][0] == 0 && selection.size() == 1)
627 context.draw_surface(img_rubber_bt, Vector(mouse_x - 8,
628 mouse_y - 8), LAYER_GUI-2);
629 else if(selection[0][0] >= gameobjs_first_id || selection[0][0] < 0)
631 // FIXME: this should draw an object image near cursor
633 int id = selection[0][0];
635 if(id == OBJ_TRAMPOLINE)
636 context.draw_surface(img_trampoline[0].get_frame(0), Vector(mouse_x - 8,
637 mouse_y - 8), LAYER_GUI-2);
638 else if(id == OBJ_FLYING_PLATFORM)
639 context.draw_surface(img_flying_platform->get_frame(0), Vector(mouse_x - 8,
640 mouse_y - 8), LAYER_GUI-2);
643 /*context.draw_surface(door->get_frame(0), Vector(mouse_x - 8,
644 mouse_y - 8), LAYER_GUI-2);*/
648 BadGuyKind kind = BadGuyKind((-id)-1);
649 BadGuy badguy(kind, 0,0);
650 badguy.activate(LEFT);
651 Surface *img = badguy.get_image();
653 context.draw_surface(img, Vector(mouse_x - 8,
654 mouse_y - 8), LAYER_GUI-2);
660 for(unsigned int x = 0; x < selection.size(); x++)
661 for(unsigned int y = 0; y < selection[x].size(); y++) {
662 const Tile* tile = tile_manager->get(selection[x][y]);
664 Vector(mouse_x + x*32 - 8, mouse_y + y*32 - 8),
669 context.set_drawing_effect(NONE_EFFECT);
672 context.draw_filled_rect(Vector(std::min((int)selection_ini.x, mouse_x)*zoom,
673 std::min((int)selection_ini.y, mouse_y))*zoom,
674 Vector(abs(mouse_x - (int)selection_ini.x)*zoom,
675 abs(mouse_y - (int)selection_ini.y)*zoom),
676 Color(170,255,170,128), LAYER_GUI-2);
680 for(int x = 0; x < SCREEN_WIDTH / (32*zoom); x++)
682 int pos = (int)(x*32*zoom) - ((int)scroll.x % 32);
683 context.draw_filled_rect(Vector (pos, 0), Vector(1, SCREEN_HEIGHT),
684 Color(225, 225, 225), LAYER_GUI-50);
686 for(int y = 0; y < SCREEN_HEIGHT / (32*zoom); y++)
688 int pos = (int)(y*32*zoom) - ((int)scroll.y % 32);
689 context.draw_filled_rect(Vector (0, pos), Vector(SCREEN_WIDTH, 1),
690 Color(225, 225, 225), LAYER_GUI-50);
694 context.push_transform();
695 context.set_translation(scroll);
696 context.set_zooming(zoom);
698 for(Sector::GameObjects::iterator i = sector->gameobjects.begin(); i != sector->gameobjects.end(); ++i)
700 TileMap* tilemap = dynamic_cast<TileMap*> (*i);
702 { // draw the non-selected tiles semi-transparently
703 context.push_transform();
705 if(tilemap->get_layer() != cur_layer)
706 context.set_drawing_effect(SEMI_TRANSPARENT);
709 context.pop_transform();
712 Background* background = dynamic_cast<Background*> (*i);
714 { // don't resize background
715 context.push_transform();
716 context.set_zooming(1.0);
718 context.pop_transform();
724 context.pop_transform();
727 context.draw_filled_rect(Vector(0,0), Vector(SCREEN_WIDTH,SCREEN_HEIGHT),Color(0,0,0), LAYER_BACKGROUND0);
729 context.do_drawing();
732 void LevelEditor::load_level_subset(std::string filename)
735 level_subset = new LevelSubset();
736 level_subset->load(filename.c_str());
740 void LevelEditor::load_level(std::string filename)
744 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
750 level_filename = filename;
754 level->load(filename);
758 level_name_timer.start(3000);
759 scroll.x = scroll.y = 0;
760 level_changed = false;
762 settings_menu->get_item_by_id(MN_ID_NAME).change_input(level->name.c_str());
763 settings_menu->get_item_by_id(MN_ID_AUTHOR).change_input(level->author.c_str());
766 void LevelEditor::load_level(int nb)
770 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
777 level_filename = level_subset->get_level_filename(level_nb);
779 load_level(level_filename);
782 void LevelEditor::load_sector(size_t num)
784 assert(num <= level->get_sector_count());
786 if(num >= level->get_sector_count())
788 if(!confirm_dialog(NULL, _("No more sectors exist. Create another?")))
790 Sector* sector_ = create_sector("new_sector",25,19);
791 level->add_sector(sector_);
792 num = level->get_sector_count()-1;
795 sector = level->get_sector(num);
797 /* Load sector stuff */
799 sector->update_game_objects();
801 foregrounds = solids = backgrounds = 0;
802 /* Point foregrounds, backgrounds, solids to its layer */
803 for(Sector::GameObjects::iterator i = sector->gameobjects.begin(); i != sector->gameobjects.end(); i++)
805 TileMap* tilemap = dynamic_cast<TileMap*> (*i);
808 if(tilemap->get_layer() == LAYER_FOREGROUNDTILES)
809 foregrounds = tilemap;
810 else if(tilemap->get_layer() == LAYER_TILES)
812 else if(tilemap->get_layer() == LAYER_BACKGROUNDTILES)
813 backgrounds = tilemap;
819 TileMap* tilemap = new TileMap(LAYER_FOREGROUNDTILES, false, solids->get_width(), solids->get_height());
820 sector->add_object(tilemap);
821 sector->update_game_objects();
825 TileMap* tilemap = new TileMap(LAYER_BACKGROUNDTILES, false, solids->get_width(), solids->get_height());
826 sector->add_object(tilemap);
827 sector->update_game_objects();
831 sprintf(str, "%i", solids->get_width());
832 settings_menu->get_item_by_id(MN_ID_WIDTH).change_input(str);
833 sprintf(str, "%i", solids->get_height());
834 settings_menu->get_item_by_id(MN_ID_HEIGHT).change_input(str);
839 void LevelEditor::save_level()
841 level->save(level_filename);
842 level_changed = false;
845 void LevelEditor::test_level()
848 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
854 GameSession session(level_filename, ST_GL_TEST);
856 // player_status.reset();
857 sound_manager->halt_music();
860 void LevelEditor::change(int x, int y, int newtile, int layer)
863 // find the tilemap of the current layer, and then change the tile
864 if(x < 0 || (unsigned int)x >= sector->solids->get_width()*32 ||
865 y < 0 || (unsigned int)y >= sector->solids->get_height()*32)
868 level_changed = true;
872 // no need to do this for normal view (no zoom)
873 x = (int)(x * (zoom*32) / 32);
874 y = (int)(y * (zoom*32) / 32);
877 if(newtile >= gameobjs_first_id) // add object
879 // remove an active tile or object that might be there
880 change(x, y, 0, LAYER_TILES);
883 GameObject* object = 0;
884 for(Factories::iterator i = object_factories->begin(); i !=
885 object_factories->end(); ++i) {
886 if(id == newtile - gameobjs_first_id) {
887 object = create_object(i->first, Vector(x, y));
893 sector->add_object(object);
894 sector->update_game_objects();
896 } else if(cur_layer == LAYER_FOREGROUNDTILES) {
897 foregrounds->change(x/32, y/32, newtile);
898 } else if(cur_layer == LAYER_TILES) {
899 // remove a bad guy if it's there
900 // we /32 in order to round numbers
901 for(Sector::GameObjects::iterator i = sector->gameobjects.begin();
902 i < sector->gameobjects.end(); i++) {
903 // FIXME: if there is a game objs in here, remove it!
905 BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
907 if((int)badguy->base.x/32 == x/32 && (int)badguy->base.y/32 == y/32)
908 sector->gameobjects.erase(i);
909 Trampoline* trampoline = dynamic_cast<Trampoline*> (*i);
912 if((int)trampoline->base.x/32 == x/32 && (int)trampoline->base.y/32 == y/32)
913 sector->gameobjects.erase(i);
915 FlyingPlatform* flying_platform = dynamic_cast<FlyingPlatform*> (*i);
917 if((int)flying_platform->base.x/32 == x/32 && (int)flying_platform->base.y/32 == y/32)
918 sector->gameobjects.erase(i);
919 Door* door = dynamic_cast<Door*> (*i);
921 if((int)door->get_area().x/32 == x/32 && (int)door->get_area().y/32 == y/32)
922 sector->gameobjects.erase(i);
925 sector->update_game_objects();
926 solids->change(x/32, y/32, newtile);
927 } else if(cur_layer == LAYER_BACKGROUNDTILES)
928 backgrounds->change(x/32, y/32, newtile);
931 void LevelEditor::show_help()
933 DrawingContext context;
935 bool show_grid_t = show_grid;
937 mouse_cursor->set_state(MC_HIDE);
941 const char *text1[] = {
942 _("This is the built-in level editor. Its aim is to be intuitive\n"
943 "and simple to use, so it should be pretty straightforward.\n"
945 "To open a level, first you'll have to select a level subset from\n"
946 "the menu (or create your own).\n"
947 "A level subset is basically a collection of levels.\n"
948 "They can then be played from the Contrib menu.\n"
950 "To access the menu from the level editor, just press Esc.\n"
952 "You are currently looking at the level. To scroll it, just\n"
953 "press the right mouse button and drag the mouse. It will move like\n"
955 "You can also use the arrow keys and Page Up/Down.\n"
957 "'+' and '-' keys can be used to zoom the level in/out.\n"
959 "You probably already noticed those floating groups of buttons.\n"
960 "Each one serves a different purpose. To select a certain button\n"
961 "just press the Left mouse button on it. A few buttons have key\n"
962 "shortcuts. You can find them by pressing the Right mouse button on\n"
963 "a button. That will also show what that button does.\n"
964 "Groups of buttons can also be moved around by just dragging them,\n"
965 "while pressing the Left mouse button.\n"
967 "Let's learn a bit of what each group of buttons does, shall we?\n"
969 "To starting putting tiles and objects around use the bigger group\n"
970 "of buttons. Each button is a different tile. To put it on the level,\n"
971 "just press it and then left click in the level.\n"
972 "You can also copy tiles from the level by using the middle mouse button.\n"
973 "Use the mouse wheel to scroll that group of buttons. You will find\n"
974 "enemies and game objects in the bottom.\n")
977 const char *text2[] = {
978 _("The Foreground/Interactive/Background buttons may be used to\n"
979 "see and edit the respective layer. Levels have three tiles layers:\n"
980 "Foreground - tiles are drawn on top of everything and have no contact\n"
982 "Interactive - these are the tiles that have contact with the player.\n"
983 "Background - tiles are drawn underneath everything and have no contact\n"
985 "The unselected layers will be drawn semi-transparently.\n"
987 "Last, but not least, the group of buttons that's left serves\n"
988 "to do related actions with the level.\n"
989 "From left to right:\n"
990 "Mini arrows - can be used to choose other sectors.\n"
991 "Sectors are mini-levels, so to speak, that can be accessed using a door.\n"
992 "Big arrows - choose other level in the same level subset.\n"
993 "Diskette - save the level\n"
994 "Tux - test the level\n"
995 "Tools - set a few settings for the level, including resizing it.\n"
997 "We have reached the end of this Howto.\n"
999 "Don't forget to send us a few cool levels. :)\n"
1002 " SuperTux development team\n"
1004 "PS: If you are looking for something more powerful, you might like to\n"
1005 "try FlexLay. FlexLay is a level editor that supports several games,\n"
1006 "including SuperTux. It is an independent project.\n"
1007 "Webpage: http://pingus.seul.org/~grumbel/flexlay/")
1010 const char **text[] = { text1, text2 };
1014 for(unsigned int i = 0; i < sizeof(text) / sizeof(text[0]); i++)
1018 context.draw_text(blue_text, _("- Level Editor's Help -"), Vector(SCREEN_WIDTH/2, 60), CENTER_ALLIGN, LAYER_GUI);
1020 context.draw_text(white_small_text, *text[i], Vector(20, 120), LEFT_ALLIGN, LAYER_GUI);
1022 sprintf(str,_("Press any key to continue - Page %d/%d"), i+1, sizeof(text) / sizeof(text[0]));
1023 context.draw_text(gold_text, str, Vector(SCREEN_WIDTH/2, SCREEN_HEIGHT-60), CENTER_ALLIGN, LAYER_GUI);
1025 context.do_drawing();
1031 done = wait_for_event(event);
1036 show_grid = show_grid_t;
1037 mouse_cursor->set_state(MC_NORMAL);
1041 LevelEditor::create_sector(const std::string& name, size_t width, size_t height)
1043 Sector* sector = new Sector;
1044 sector->set_name(name);
1046 sector->add_object(new TileMap(LAYER_BACKGROUNDTILES, false, width, height));
1047 sector->add_object(new TileMap(LAYER_TILES, true, width, height));
1048 sector->add_object(new TileMap(LAYER_FOREGROUNDTILES, false, width, height));
1049 sector->add_object(new Camera(sector));
1050 sector->update_game_objects();