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 "sprite/sprite.h"
49 #include "leveleditor.h"
50 #include "resources.h"
52 #include "tile_manager.h"
54 #include "game_session.h"
55 #include "object_factory.h"
56 #include "object/gameobjs.h"
57 #include "object/camera.h"
58 #include "object/tilemap.h"
59 #include "object/background.h"
61 LevelEditor::LevelEditor()
66 selection_end = selection_ini = Vector(0,0);
67 left_button = middle_button = mouse_moved = false;
71 cur_layer = LAYER_TILES;
72 level_changed = false;
78 level_subsets = FileSystem::dsubdirs("/levels", "info");
79 subset_menu = new Menu();
80 subset_menu->add_label(_("Load Subset"));
81 subset_menu->additem(MN_HL,"",0,0);
83 for(std::set<std::string>::iterator it = level_subsets.begin(); it != level_subsets.end(); ++it, ++i)
84 subset_menu->additem(MN_ACTION, (*it),0,0,i);
85 subset_menu->additem(MN_HL,"",0,0);
86 subset_menu->additem(MN_BACK,_("Back"),0,0);
88 create_subset_menu = new Menu();
89 create_subset_menu->additem(MN_LABEL,_("New Level Subset"),0,0);
90 create_subset_menu->additem(MN_HL,"",0,0);
91 create_subset_menu->additem(MN_TEXTFIELD,_("Filename "),0,0,MN_ID_FILENAME_SUBSET);
92 create_subset_menu->additem(MN_TEXTFIELD,_("Title "),0,0,MN_ID_TITLE_SUBSET);
93 create_subset_menu->additem(MN_TEXTFIELD,_("Description"),0,0,MN_ID_DESCRIPTION_SUBSET);
94 create_subset_menu->additem(MN_ACTION,_("Create"),0,0, MN_ID_CREATE_SUBSET);
95 create_subset_menu->additem(MN_HL,"",0,0);
96 create_subset_menu->additem(MN_BACK,_("Back"),0,0);
98 main_menu = new Menu();
99 main_menu->additem(MN_LABEL,_("Level Editor Menu"),0,0);
100 main_menu->additem(MN_HL,"",0,0);
101 main_menu->additem(MN_ACTION,_("Return to Level Editor"),0,0,MN_ID_RETURN);
102 main_menu->additem(MN_GOTO,_("Create Level Subset"),0,create_subset_menu);
103 main_menu->additem(MN_GOTO,_("Load Level Subset"),0,subset_menu);
104 main_menu->additem(MN_HL,"",0,0);
105 main_menu->additem(MN_ACTION,_("Quit Level Editor"),0,0,MN_ID_QUIT);
107 settings_menu = new Menu();
108 settings_menu->additem(MN_LABEL,_("Level Settings"),0,0);
109 settings_menu->additem(MN_HL,"",0,0);
110 settings_menu->additem(MN_TEXTFIELD,_("Name "),0,0,MN_ID_NAME);
111 settings_menu->additem(MN_TEXTFIELD,_("Author "),0,0,MN_ID_AUTHOR);
112 settings_menu->additem(MN_NUMFIELD, _("Width "),0,0,MN_ID_WIDTH);
113 settings_menu->additem(MN_NUMFIELD, _("Height "),0,0,MN_ID_HEIGHT);
114 settings_menu->additem(MN_HL,"",0,0);
115 settings_menu->additem(MN_ACTION,_("Apply"),0,0,MN_ID_APPLY_SETTINGS);
117 /* Creating button groups */
120 tiles_board = new ButtonGroup(Vector(SCREEN_WIDTH - 140, 100),
121 Vector(32,32), Vector(4,8));
123 tiles_board->add_button(Button(img_rubber_bt, _("Eraser"), SDLKey(SDLK_DELETE)), 0);
125 for(id = 1; id < tile_manager->get_max_tileid(); id++)
127 const Tile* tile = tile_manager->get(id);
131 Surface* surface = tile->get_editor_image();
135 Button button = Button(surface, "", SDLKey(0));
136 tiles_board->add_button(button, id);
138 gameobjs_first_id = id;
140 for(Factories::iterator i = object_factories->begin();
141 i != object_factories->end(); ++i) {
143 // Surface *img = badguy.get_image();
144 // FIXME: get image from object. Using the rubber in the meanwhile.
145 tiles_board->add_button(Button(img_rubber_bt, i->first,
146 SDLKey(SDLK_1+id)), id++);
149 tiles_layer = new ButtonGroup(Vector(12, SCREEN_HEIGHT-64), Vector(80,20), Vector(1,3));
150 tiles_layer->add_button(Button(img_foreground_bt, _("Edtit foreground tiles"),
151 SDLK_F10), LAYER_FOREGROUNDTILES);
152 tiles_layer->add_button(Button(img_interactive_bt, _("Edit interactive tiles"),
153 SDLK_F11), LAYER_TILES, true);
154 tiles_layer->add_button(Button(img_background_bt, _("Edit background tiles"),
155 SDLK_F12), LAYER_BACKGROUNDTILES);
157 level_options = new ButtonGroup(Vector(SCREEN_WIDTH-164, SCREEN_HEIGHT-36), Vector(32,32), Vector(5,1));
158 level_options->add_pair_of_buttons(Button(img_next_sector_bt, _("Next sector"), SDLKey(0)), BT_NEXT_SECTOR,
159 Button(img_previous_sector_bt, _("Prevous sector"), SDLKey(0)), BT_PREVIOUS_SECTOR);
160 level_options->add_pair_of_buttons(Button(img_next_level_bt, _("Next level"), SDLKey(0)), BT_NEXT_LEVEL,
161 Button(img_previous_level_bt, _("Prevous level"), SDLKey(0)), BT_PREVIOUS_LEVEL);
162 level_options->add_button(Button(img_save_level_bt, _("Save level"), SDLK_F5), BT_LEVEL_SAVE);
163 level_options->add_button(Button(img_test_level_bt, _("Test level"), SDLK_F6), BT_LEVEL_TEST);
164 level_options->add_button(Button(img_setup_level_bt, _("Setup level"), SDLK_F7), BT_LEVEL_SETUP);
167 LevelEditor::~LevelEditor()
173 delete level_options;
176 delete create_subset_menu;
178 delete settings_menu;
184 void LevelEditor::load_buttons_gfx()
186 img_foreground_bt = new Surface(datadir + "/images/leveleditor/foreground.png", true);
187 img_interactive_bt = new Surface(datadir + "/images/leveleditor/interactive.png", true);
188 img_background_bt = new Surface(datadir + "/images/leveleditor/background.png", true);
190 img_save_level_bt = new Surface(datadir + "/images/leveleditor/save-level.png", true);
191 img_test_level_bt = new Surface(datadir + "/images/leveleditor/test-level.png", true);
192 img_setup_level_bt = new Surface(datadir + "/images/leveleditor/setup-level.png", true);
194 img_rubber_bt = new Surface(datadir + "/images/leveleditor/rubber.png", true);
196 img_previous_level_bt = new Surface(datadir + "/images/leveleditor/previous-level.png", true);
197 img_next_level_bt = new Surface(datadir + "/images/leveleditor/next-level.png", true);
198 img_previous_sector_bt = new Surface(datadir + "/images/leveleditor/previous-sector.png", true);
199 img_next_sector_bt = new Surface(datadir + "/images/leveleditor/next-sector.png", true);
202 void LevelEditor::free_buttons_gfx()
204 delete img_foreground_bt;
205 delete img_interactive_bt;
206 delete img_background_bt;
208 delete img_save_level_bt;
209 delete img_test_level_bt;
210 delete img_setup_level_bt;
212 delete img_rubber_bt;
214 delete img_previous_level_bt;
215 delete img_next_level_bt;
216 delete img_previous_sector_bt;
217 delete img_next_sector_bt;
220 void LevelEditor::run(const std::string filename)
222 sound_manager->halt_music();
223 Menu::set_current(0);
225 DrawingContext context;
227 if(!filename.empty())
230 load_level(filename);
233 Menu::set_current(main_menu);
235 mouse_cursor->set_state(MC_NORMAL);
246 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
250 void LevelEditor::events()
255 while(SDL_PollEvent(&event))
257 Menu* menu = Menu::current();
262 if(menu == main_menu)
264 switch (main_menu->check())
267 Menu::set_current(0);
274 else if(menu == create_subset_menu)
276 // activate or deactivate Create button if any filename as been specified
277 if(create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input[0] == '\0')
278 create_subset_menu->get_item_by_id(MN_ID_CREATE_SUBSET).kind = MN_DEACTIVE;
280 create_subset_menu->get_item_by_id(MN_ID_CREATE_SUBSET).kind = MN_ACTION;
282 if(create_subset_menu->check() == MN_ID_CREATE_SUBSET)
283 { // applying settings:
284 std::string subset_name = create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input;
285 LevelSubset::create(subset_name);
288 level_subset = new LevelSubset();
289 level_subset->load(create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input);
291 level_subset->title = create_subset_menu->get_item_by_id(MN_ID_TITLE_SUBSET).input;
292 level_subset->description = create_subset_menu->get_item_by_id(MN_ID_DESCRIPTION_SUBSET).input;
293 //FIXME: generate better level filenames
294 level_subset->add_level(subset_name+'/'+"new_level.stl");
295 Level* newlevel = new Level();
296 newlevel->add_sector(create_sector("main", 25, 19));
297 newlevel->save(level_subset->get_level_filename(0));
298 level_subset->save();
302 create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).change_input("");
303 create_subset_menu->get_item_by_id(MN_ID_TITLE_SUBSET).change_input("");
304 create_subset_menu->get_item_by_id(MN_ID_DESCRIPTION_SUBSET).change_input("");
307 else if(menu == subset_menu)
309 int i = subset_menu->check();
312 std::set<std::string>::iterator it = level_subsets.begin();
313 for(int t = 0; t < i; t++)
315 load_level_subset(*it);
316 Menu::set_current(0);
319 else if(menu == settings_menu)
321 if(settings_menu->check() == MN_ID_APPLY_SETTINGS)
322 { // applying settings:
323 level_changed = true;
325 level->name = settings_menu->get_item_by_id(MN_ID_NAME).input;
326 level->author = settings_menu->get_item_by_id(MN_ID_AUTHOR).input;
328 solids->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()),
329 atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str()));
330 foregrounds->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 backgrounds->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()));
335 Menu::set_current(0);
339 // check for events in buttons
340 else if(tiles_board->event(event))
342 std::vector <int> vector;
343 vector.push_back(tiles_board->selected_id());
346 selection.push_back(vector);
349 else if(tiles_layer->event(event))
351 cur_layer = tiles_layer->selected_id();
354 else if(level_options->event(event))
356 switch(level_options->selected_id())
365 Menu::set_current(settings_menu);
368 if(level_nb + 1 < level_subset->get_num_levels())
369 load_level(level_nb + 1);
373 sprintf(str,_("Level %d doesn't exist. Create it?"), level_nb + 2);
374 if(confirm_dialog(NULL, str))
376 level_subset->add_level("new_level.stl");
377 Level* newlevel = new Level();
378 newlevel->add_sector(create_sector("main", 25, 19));
379 newlevel->save(level_subset->get_level_filename(level_nb + 1));
380 level_subset->save();
381 load_level(level_nb + 1);
385 case BT_PREVIOUS_LEVEL:
386 if(level_nb - 1 >= 0)
387 load_level(level_nb - 1);
390 std::cerr << "next sector.\n";
391 load_sector(sectornum+1);
393 case BT_PREVIOUS_SECTOR:
394 std::cerr << "previous sector.\n";
396 load_sector(sectornum-1);
399 level_options->set_unselected();
406 case SDL_MOUSEMOTION:
408 mouse_x = event.motion.x;
409 mouse_y = event.motion.y;
410 if(SDL_GetMouseState(NULL, NULL)&SDL_BUTTON(SDL_BUTTON_RIGHT))
411 { // movement like in strategy games
412 scroll.x += -1 * event.motion.xrel;
413 scroll.y += -1 * event.motion.yrel;
417 case SDL_MOUSEBUTTONDOWN:
419 mouse_x = event.motion.x;
420 mouse_y = event.motion.y;
421 if(event.button.button == SDL_BUTTON_LEFT)
423 else if(event.button.button == SDL_BUTTON_MIDDLE)
425 middle_button = true;
426 selection_ini = Vector(event.button.x, event.button.y);
430 case SDL_MOUSEBUTTONUP:
432 mouse_x = event.motion.x;
433 mouse_y = event.motion.y;
434 if(event.button.button == SDL_BUTTON_LEFT)
436 else if(event.button.button == SDL_BUTTON_MIDDLE)
438 middle_button = false;
439 selection_end = Vector(event.button.x, event.button.y);
441 if(selection_end.x < selection_ini.x)
443 float t = selection_ini.x;
444 selection_ini.x = selection_end.x;
447 if(selection_end.y < selection_ini.y)
449 float t = selection_ini.y;
450 selection_ini.y = selection_end.y;
455 std::vector <int> vector;
457 TileMap* tilemap = 0;
458 if(cur_layer == LAYER_FOREGROUNDTILES)
459 tilemap = foregrounds;
460 else if(cur_layer == LAYER_TILES)
462 else if(cur_layer == LAYER_BACKGROUNDTILES)
463 tilemap = backgrounds;
465 for(int x = 0; x < (int)((selection_end.x - selection_ini.x)*zoom / 32) + 1; x++)
468 for(int y = 0; y < (int)((selection_end.y - selection_ini.y)*zoom / 32) + 1; y++)
470 vector.push_back(tilemap->get_tile(x +
471 (int)(((selection_ini.x+scroll.x)*zoom)/32),
472 y + (int)(((selection_ini.y+scroll.y)*zoom)/32))->getID());
474 selection.push_back(vector);
479 case SDL_KEYDOWN: // key pressed
480 switch(event.key.keysym.sym)
483 Menu::set_current(main_menu);
485 /* scrolling related events: */
490 scroll.x = sector->solids->get_height()*32 - SCREEN_WIDTH;
523 show_grid = !show_grid;
530 case SDL_QUIT: // window closed
541 void LevelEditor::action()
543 mouse_cursor->set_state(MC_NORMAL);
544 if(tiles_board->is_hover() || tiles_layer->is_hover() || level_options->is_hover())
545 mouse_cursor->set_state(MC_LINK);
549 // don't scroll before the start or after the level's end
550 float width = sector->solids->get_width() * 32;
551 float height = sector->solids->get_height() * 32;
553 if(scroll.x < -SCREEN_WIDTH/2)
554 scroll.x = -SCREEN_WIDTH/2;
555 if(scroll.x > width - SCREEN_WIDTH/2)
556 scroll.x = width - SCREEN_WIDTH/2;
557 if(scroll.y < -SCREEN_HEIGHT/2)
558 scroll.y = -SCREEN_HEIGHT/2;
559 if(scroll.y > height - SCREEN_HEIGHT/2)
560 scroll.y = height - SCREEN_HEIGHT/2;
562 // set camera translation, since BadGuys like it
563 sector->camera->set_scrolling((int)scroll.x, (int)scroll.y);
565 if(left_button && mouse_moved)
566 for(unsigned int x = 0; x < selection.size(); x++)
567 for(unsigned int y = 0; y < selection[x].size(); y++)
568 change((int)(scroll.x + mouse_x) + (x*32),
569 (int)(scroll.y + mouse_y) + (y*32), selection[x][y],
574 #define FADING_TIME .6
576 void LevelEditor::draw(DrawingContext& context)
578 context.draw_text(white_text, _("Level Editor"), Vector(10, 5), LEFT_ALLIGN, LAYER_GUI);
579 mouse_cursor->draw(context);
581 // draw a filled background
582 context.draw_filled_rect(Vector(0,0), Vector(SCREEN_WIDTH,SCREEN_HEIGHT), Color(60,60,60), LAYER_BACKGROUND0-1);
584 if(level_name_timer.check())
586 context.push_transform();
587 if(level_name_timer.get_timeleft() < FADING_TIME)
588 context.set_alpha(int(level_name_timer.get_timeleft() * 255 / FADING_TIME));
590 context.draw_text(gold_text, level->name, Vector(SCREEN_WIDTH/2, 30), CENTER_ALLIGN, LAYER_GUI);
594 sprintf(str, "%i/%i", level_nb+1, level_subset->get_num_levels());
595 context.draw_text(gold_text, str, Vector(SCREEN_WIDTH/2, 50), CENTER_ALLIGN, LAYER_GUI);
598 context.pop_transform();
601 context.draw_text(white_small_text, _("F1 for help"), Vector(5, 510), LEFT_ALLIGN, LAYER_GUI-10);
603 context.draw_text(white_small_text, _("Choose a level subset"), Vector(5, 510), LEFT_ALLIGN, LAYER_GUI-10);
605 Menu* menu = Menu::current();
610 tiles_board->draw(context);
611 tiles_layer->draw(context);
612 level_options->draw(context);
620 context.set_drawing_effect(SEMI_TRANSPARENT);
624 if(selection[0][0] == 0 && selection.size() == 1)
625 context.draw_surface(img_rubber_bt, Vector(mouse_x - 8,
626 mouse_y - 8), LAYER_GUI-2);
627 else if(selection[0][0] >= gameobjs_first_id || selection[0][0] < 0)
629 // FIXME: this should draw an object image near cursor
631 int id = selection[0][0];
633 if(id == OBJ_TRAMPOLINE)
634 context.draw_surface(img_trampoline[0].get_frame(0), Vector(mouse_x - 8,
635 mouse_y - 8), LAYER_GUI-2);
636 else if(id == OBJ_FLYING_PLATFORM)
637 context.draw_surface(img_flying_platform->get_frame(0), Vector(mouse_x - 8,
638 mouse_y - 8), LAYER_GUI-2);
641 /*context.draw_surface(door->get_frame(0), Vector(mouse_x - 8,
642 mouse_y - 8), LAYER_GUI-2);*/
646 BadGuyKind kind = BadGuyKind((-id)-1);
647 BadGuy badguy(kind, 0,0);
648 badguy.activate(LEFT);
649 Surface *img = badguy.get_image();
651 context.draw_surface(img, Vector(mouse_x - 8,
652 mouse_y - 8), LAYER_GUI-2);
658 for(unsigned int x = 0; x < selection.size(); x++)
659 for(unsigned int y = 0; y < selection[x].size(); y++) {
660 const Tile* tile = tile_manager->get(selection[x][y]);
662 Vector(mouse_x + x*32 - 8, mouse_y + y*32 - 8),
667 context.set_drawing_effect(NONE_EFFECT);
670 context.draw_filled_rect(Vector(std::min((int)selection_ini.x, mouse_x)*zoom,
671 std::min((int)selection_ini.y, mouse_y))*zoom,
672 Vector(abs(mouse_x - (int)selection_ini.x)*zoom,
673 abs(mouse_y - (int)selection_ini.y)*zoom),
674 Color(170,255,170,128), LAYER_GUI-2);
678 for(int x = 0; x < SCREEN_WIDTH / (32*zoom); x++)
680 int pos = (int)(x*32*zoom) - ((int)scroll.x % 32);
681 context.draw_filled_rect(Vector (pos, 0), Vector(1, SCREEN_HEIGHT),
682 Color(225, 225, 225), LAYER_GUI-50);
684 for(int y = 0; y < SCREEN_HEIGHT / (32*zoom); y++)
686 int pos = (int)(y*32*zoom) - ((int)scroll.y % 32);
687 context.draw_filled_rect(Vector (0, pos), Vector(SCREEN_WIDTH, 1),
688 Color(225, 225, 225), LAYER_GUI-50);
692 context.push_transform();
693 context.set_translation(scroll);
694 context.set_zooming(zoom);
696 for(Sector::GameObjects::iterator i = sector->gameobjects.begin(); i != sector->gameobjects.end(); ++i)
698 TileMap* tilemap = dynamic_cast<TileMap*> (*i);
700 { // draw the non-selected tiles semi-transparently
701 context.push_transform();
703 if(tilemap->get_layer() != cur_layer)
704 context.set_drawing_effect(SEMI_TRANSPARENT);
707 context.pop_transform();
710 Background* background = dynamic_cast<Background*> (*i);
712 { // don't resize background
713 context.push_transform();
714 context.set_zooming(1.0);
716 context.pop_transform();
722 context.pop_transform();
725 context.draw_filled_rect(Vector(0,0), Vector(SCREEN_WIDTH,SCREEN_HEIGHT),Color(0,0,0), LAYER_BACKGROUND0);
727 context.do_drawing();
730 void LevelEditor::load_level_subset(std::string filename)
733 level_subset = new LevelSubset();
734 level_subset->load(filename.c_str());
738 void LevelEditor::load_level(std::string filename)
742 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
748 level_filename = filename;
752 level->load(filename);
756 level_name_timer.start(3000);
757 scroll.x = scroll.y = 0;
758 level_changed = false;
760 settings_menu->get_item_by_id(MN_ID_NAME).change_input(level->name.c_str());
761 settings_menu->get_item_by_id(MN_ID_AUTHOR).change_input(level->author.c_str());
764 void LevelEditor::load_level(int nb)
768 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
775 level_filename = level_subset->get_level_filename(level_nb);
777 load_level(level_filename);
780 void LevelEditor::load_sector(size_t num)
782 assert(num <= level->get_sector_count());
784 if(num >= level->get_sector_count())
786 if(!confirm_dialog(NULL, _("No more sectors exist. Create another?")))
788 Sector* sector_ = create_sector("new_sector",25,19);
789 level->add_sector(sector_);
790 num = level->get_sector_count()-1;
793 sector = level->get_sector(num);
795 /* Load sector stuff */
797 sector->update_game_objects();
799 foregrounds = solids = backgrounds = 0;
800 /* Point foregrounds, backgrounds, solids to its layer */
801 for(Sector::GameObjects::iterator i = sector->gameobjects.begin(); i != sector->gameobjects.end(); i++)
803 TileMap* tilemap = dynamic_cast<TileMap*> (*i);
806 if(tilemap->get_layer() == LAYER_FOREGROUNDTILES)
807 foregrounds = tilemap;
808 else if(tilemap->get_layer() == LAYER_TILES)
810 else if(tilemap->get_layer() == LAYER_BACKGROUNDTILES)
811 backgrounds = tilemap;
817 TileMap* tilemap = new TileMap(LAYER_FOREGROUNDTILES, false, solids->get_width(), solids->get_height());
818 sector->add_object(tilemap);
819 sector->update_game_objects();
823 TileMap* tilemap = new TileMap(LAYER_BACKGROUNDTILES, false, solids->get_width(), solids->get_height());
824 sector->add_object(tilemap);
825 sector->update_game_objects();
829 sprintf(str, "%i", solids->get_width());
830 settings_menu->get_item_by_id(MN_ID_WIDTH).change_input(str);
831 sprintf(str, "%i", solids->get_height());
832 settings_menu->get_item_by_id(MN_ID_HEIGHT).change_input(str);
837 void LevelEditor::save_level()
839 level->save(level_filename);
840 level_changed = false;
843 void LevelEditor::test_level()
846 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
852 GameSession session(level_filename, ST_GL_TEST);
854 // player_status.reset();
855 sound_manager->halt_music();
858 void LevelEditor::change(int x, int y, int newtile, int layer)
861 // find the tilemap of the current layer, and then change the tile
862 if(x < 0 || (unsigned int)x >= sector->solids->get_width()*32 ||
863 y < 0 || (unsigned int)y >= sector->solids->get_height()*32)
866 level_changed = true;
870 // no need to do this for normal view (no zoom)
871 x = (int)(x * (zoom*32) / 32);
872 y = (int)(y * (zoom*32) / 32);
875 if(newtile >= gameobjs_first_id) // add object
877 // remove an active tile or object that might be there
878 change(x, y, 0, LAYER_TILES);
881 GameObject* object = 0;
882 for(Factories::iterator i = object_factories->begin(); i !=
883 object_factories->end(); ++i) {
884 if(id == newtile - gameobjs_first_id) {
885 object = create_object(i->first, Vector(x, y));
891 sector->add_object(object);
892 sector->update_game_objects();
894 } else if(cur_layer == LAYER_FOREGROUNDTILES) {
895 foregrounds->change(x/32, y/32, newtile);
896 } else if(cur_layer == LAYER_TILES) {
897 // remove a bad guy if it's there
898 // we /32 in order to round numbers
899 for(Sector::GameObjects::iterator i = sector->gameobjects.begin();
900 i < sector->gameobjects.end(); i++) {
901 // FIXME: if there is a game objs in here, remove it!
903 BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
905 if((int)badguy->base.x/32 == x/32 && (int)badguy->base.y/32 == y/32)
906 sector->gameobjects.erase(i);
907 Trampoline* trampoline = dynamic_cast<Trampoline*> (*i);
910 if((int)trampoline->base.x/32 == x/32 && (int)trampoline->base.y/32 == y/32)
911 sector->gameobjects.erase(i);
913 FlyingPlatform* flying_platform = dynamic_cast<FlyingPlatform*> (*i);
915 if((int)flying_platform->base.x/32 == x/32 && (int)flying_platform->base.y/32 == y/32)
916 sector->gameobjects.erase(i);
917 Door* door = dynamic_cast<Door*> (*i);
919 if((int)door->get_area().x/32 == x/32 && (int)door->get_area().y/32 == y/32)
920 sector->gameobjects.erase(i);
923 sector->update_game_objects();
924 solids->change(x/32, y/32, newtile);
925 } else if(cur_layer == LAYER_BACKGROUNDTILES)
926 backgrounds->change(x/32, y/32, newtile);
929 void LevelEditor::show_help()
931 DrawingContext context;
933 bool show_grid_t = show_grid;
935 mouse_cursor->set_state(MC_HIDE);
939 const char *text1[] = {
940 _("This is the built-in level editor. Its aim is to be intuitive\n"
941 "and simple to use, so it should be pretty straightforward.\n"
943 "To open a level, first you'll have to select a level subset from\n"
944 "the menu (or create your own).\n"
945 "A level subset is basically a collection of levels.\n"
946 "They can then be played from the Contrib menu.\n"
948 "To access the menu from the level editor, just press Esc.\n"
950 "You are currently looking at the level. To scroll it, just\n"
951 "press the right mouse button and drag the mouse. It will move like\n"
953 "You can also use the arrow keys and Page Up/Down.\n"
955 "'+' and '-' keys can be used to zoom the level in/out.\n"
957 "You probably already noticed those floating groups of buttons.\n"
958 "Each one serves a different purpose. To select a certain button\n"
959 "just press the Left mouse button on it. A few buttons have key\n"
960 "shortcuts. You can find them by pressing the Right mouse button on\n"
961 "a button. That will also show what that button does.\n"
962 "Groups of buttons can also be moved around by just dragging them,\n"
963 "while pressing the Left mouse button.\n"
965 "Let's learn a bit of what each group of buttons does, shall we?\n"
967 "To starting putting tiles and objects around use the bigger group\n"
968 "of buttons. Each button is a different tile. To put it on the level,\n"
969 "just press it and then left click in the level.\n"
970 "You can also copy tiles from the level by using the middle mouse button.\n"
971 "Use the mouse wheel to scroll that group of buttons. You will find\n"
972 "enemies and game objects in the bottom.\n")
975 const char *text2[] = {
976 _("The Foreground/Interactive/Background buttons may be used to\n"
977 "see and edit the respective layer. Levels have three tiles layers:\n"
978 "Foreground - tiles are drawn on top of everything and have no contact\n"
980 "Interactive - these are the tiles that have contact with the player.\n"
981 "Background - tiles are drawn underneath everything and have no contact\n"
983 "The unselected layers will be drawn semi-transparently.\n"
985 "Last, but not least, the group of buttons that's left serves\n"
986 "to do related actions with the level.\n"
987 "From left to right:\n"
988 "Mini arrows - can be used to choose other sectors.\n"
989 "Sectors are mini-levels, so to speak, that can be accessed using a door.\n"
990 "Big arrows - choose other level in the same level subset.\n"
991 "Diskette - save the level\n"
992 "Tux - test the level\n"
993 "Tools - set a few settings for the level, including resizing it.\n"
995 "We have reached the end of this Howto.\n"
997 "Don't forget to send us a few cool levels. :)\n"
1000 " SuperTux development team\n"
1002 "PS: If you are looking for something more powerful, you might like to\n"
1003 "try FlexLay. FlexLay is a level editor that supports several games,\n"
1004 "including SuperTux. It is an independent project.\n"
1005 "Webpage: http://pingus.seul.org/~grumbel/flexlay/")
1008 const char **text[] = { text1, text2 };
1012 for(unsigned int i = 0; i < sizeof(text) / sizeof(text[0]); i++)
1016 context.draw_text(blue_text, _("- Level Editor's Help -"), Vector(SCREEN_WIDTH/2, 60), CENTER_ALLIGN, LAYER_GUI);
1018 context.draw_text(white_small_text, *text[i], Vector(20, 120), LEFT_ALLIGN, LAYER_GUI);
1020 sprintf(str,_("Press any key to continue - Page %d/%d"), i+1, sizeof(text) / sizeof(text[0]));
1021 context.draw_text(gold_text, str, Vector(SCREEN_WIDTH/2, SCREEN_HEIGHT-60), CENTER_ALLIGN, LAYER_GUI);
1023 context.do_drawing();
1029 done = wait_for_event(event);
1034 show_grid = show_grid_t;
1035 mouse_cursor->set_state(MC_NORMAL);
1039 LevelEditor::create_sector(const std::string& name, size_t width, size_t height)
1041 Sector* sector = new Sector;
1042 sector->set_name(name);
1044 sector->add_object(new TileMap(LAYER_BACKGROUNDTILES, false, width, height));
1045 sector->add_object(new TileMap(LAYER_TILES, true, width, height));
1046 sector->add_object(new TileMap(LAYER_FOREGROUNDTILES, false, width, height));
1047 sector->add_object(new Camera(sector));
1048 sector->update_game_objects();