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"
42 LevelEditor::LevelEditor()
47 selection_end = selection_ini = Vector(0,0);
48 left_button = middle_button = mouse_moved = false;
52 cur_layer = LAYER_TILES;
53 level_changed = false;
59 level_subsets = FileSystem::dsubdirs("/levels", "info");
60 subset_menu = new Menu();
61 subset_menu->additem(MN_LABEL,_("Load Subset"),0,0);
62 subset_menu->additem(MN_HL,"",0,0);
64 for(std::set<std::string>::iterator it = level_subsets.begin(); it != level_subsets.end(); ++it, ++i)
65 subset_menu->additem(MN_ACTION, (*it),0,0,i);
66 subset_menu->additem(MN_HL,"",0,0);
67 subset_menu->additem(MN_BACK,_("Back"),0,0);
69 create_subset_menu = new Menu();
70 create_subset_menu->additem(MN_LABEL,_("New Level Subset"),0,0);
71 create_subset_menu->additem(MN_HL,"",0,0);
72 create_subset_menu->additem(MN_TEXTFIELD,_("Filename "),0,0,MN_ID_FILENAME_SUBSET);
73 create_subset_menu->additem(MN_TEXTFIELD,_("Title "),0,0,MN_ID_TITLE_SUBSET);
74 create_subset_menu->additem(MN_TEXTFIELD,_("Description"),0,0,MN_ID_DESCRIPTION_SUBSET);
75 create_subset_menu->additem(MN_ACTION,_("Create"),0,0, MN_ID_CREATE_SUBSET);
76 create_subset_menu->additem(MN_HL,"",0,0);
77 create_subset_menu->additem(MN_BACK,_("Back"),0,0);
79 main_menu = new Menu();
80 main_menu->additem(MN_LABEL,_("Level Editor Menu"),0,0);
81 main_menu->additem(MN_HL,"",0,0);
82 main_menu->additem(MN_ACTION,_("Return to Level Editor"),0,0,MN_ID_RETURN);
83 main_menu->additem(MN_GOTO,_("Create Level Subset"),0,create_subset_menu);
84 main_menu->additem(MN_GOTO,_("Load Level Subset"),0,subset_menu);
85 main_menu->additem(MN_HL,"",0,0);
86 main_menu->additem(MN_ACTION,_("Quit Level Editor"),0,0,MN_ID_QUIT);
88 settings_menu = new Menu();
89 settings_menu->additem(MN_LABEL,_("Level Settings"),0,0);
90 settings_menu->additem(MN_HL,"",0,0);
91 settings_menu->additem(MN_TEXTFIELD,_("Name "),0,0,MN_ID_NAME);
92 settings_menu->additem(MN_TEXTFIELD,_("Author "),0,0,MN_ID_AUTHOR);
93 settings_menu->additem(MN_NUMFIELD, _("Width "),0,0,MN_ID_WIDTH);
94 settings_menu->additem(MN_NUMFIELD, _("Height "),0,0,MN_ID_HEIGHT);
95 settings_menu->additem(MN_HL,"",0,0);
96 settings_menu->additem(MN_ACTION,_("Apply"),0,0,MN_ID_APPLY_SETTINGS);
98 /* Creating button groups */
101 tiles_board = new ButtonGroup(Vector(screen->w - 140, 100),
102 Vector(32,32), Vector(4,8));
104 TileManager* tilemanager = TileManager::instance();
106 tiles_board->add_button(Button(img_rubber_bt, _("Eraser"), SDLKey(SDLK_DELETE)), 0);
107 for(unsigned int id = 1; id < tilemanager->get_max_tileid(); id++)
109 const Tile* tile = tilemanager->get(id);
114 if(tile->editor_images.size())
115 surface = tile->editor_images[0];
116 else if(tile->images.size())
117 surface = tile->images[0];
121 Button button = Button(surface, "", SDLKey(0));
122 tiles_board->add_button(button, id);
126 for(int i = 0; i < NUM_BadGuyKinds; i++)
128 // filter bomb, since it is only for internal use, not for levels
132 BadGuyKind kind = BadGuyKind(i);
133 BadGuy badguy(kind, 0,0);
134 badguy.activate(LEFT);
136 Surface *img = badguy.get_image();
137 tiles_board->add_button(Button(img, "", SDLKey(SDLK_1+i)), -(i+1));
142 tiles_board->add_button(Button(img_trampoline[0].get_frame(0), _("Trampoline"), SDLKey(0)), OBJ_TRAMPOLINE);
143 tiles_board->add_button(Button(img_flying_platform->get_frame(0), _("Flying Platform"), SDLKey(0)), OBJ_FLYING_PLATFORM);
146 tiles_layer = new ButtonGroup(Vector(12, screen->h-64), Vector(80,20), Vector(1,3));
147 tiles_layer->add_button(Button(img_foreground_bt, _("Edtit foreground tiles"),
148 SDLK_F10), LAYER_FOREGROUNDTILES);
149 tiles_layer->add_button(Button(img_interactive_bt, _("Edit interactive tiles"),
150 SDLK_F11), LAYER_TILES, true);
151 tiles_layer->add_button(Button(img_background_bt, _("Edit background tiles"),
152 SDLK_F12), LAYER_BACKGROUNDTILES);
154 level_options = new ButtonGroup(Vector(screen->w-164, screen->h-36), Vector(32,32), Vector(5,1));
155 level_options->add_pair_of_buttons(Button(img_next_sector_bt, _("Next sector"), SDLKey(0)), BT_NEXT_SECTOR,
156 Button(img_previous_sector_bt, _("Prevous sector"), SDLKey(0)), BT_PREVIOUS_SECTOR);
157 level_options->add_pair_of_buttons(Button(img_next_level_bt, _("Next level"), SDLKey(0)), BT_NEXT_LEVEL,
158 Button(img_previous_level_bt, _("Prevous level"), SDLKey(0)), BT_PREVIOUS_LEVEL);
159 level_options->add_button(Button(img_save_level_bt, _("Save level"), SDLK_F5), BT_LEVEL_SAVE);
160 level_options->add_button(Button(img_test_level_bt, _("Test level"), SDLK_F6), BT_LEVEL_TEST);
161 level_options->add_button(Button(img_setup_level_bt, _("Setup level"), SDLK_F7), BT_LEVEL_SETUP);
164 LevelEditor::~LevelEditor()
170 delete level_options;
173 delete create_subset_menu;
175 delete settings_menu;
181 void LevelEditor::load_buttons_gfx()
183 img_foreground_bt = new Surface(datadir + "/images/leveleditor/foreground.png", true);
184 img_interactive_bt = new Surface(datadir + "/images/leveleditor/interactive.png", true);
185 img_background_bt = new Surface(datadir + "/images/leveleditor/background.png", true);
187 img_save_level_bt = new Surface(datadir + "/images/leveleditor/save-level.png", true);
188 img_test_level_bt = new Surface(datadir + "/images/leveleditor/test-level.png", true);
189 img_setup_level_bt = new Surface(datadir + "/images/leveleditor/setup-level.png", true);
191 img_rubber_bt = new Surface(datadir + "/images/leveleditor/rubber.png", true);
193 img_previous_level_bt = new Surface(datadir + "/images/leveleditor/previous-level.png", true);
194 img_next_level_bt = new Surface(datadir + "/images/leveleditor/next-level.png", true);
195 img_previous_sector_bt = new Surface(datadir + "/images/leveleditor/previous-sector.png", true);
196 img_next_sector_bt = new Surface(datadir + "/images/leveleditor/next-sector.png", true);
199 void LevelEditor::free_buttons_gfx()
201 delete img_foreground_bt;
202 delete img_interactive_bt;
203 delete img_background_bt;
205 delete img_save_level_bt;
206 delete img_test_level_bt;
207 delete img_setup_level_bt;
209 delete img_rubber_bt;
211 delete img_previous_level_bt;
212 delete img_next_level_bt;
213 delete img_previous_sector_bt;
214 delete img_next_sector_bt;
217 void LevelEditor::run(const std::string filename)
219 SoundManager::get()->halt_music();
220 Menu::set_current(0);
222 DrawingContext context;
224 if(!filename.empty())
227 load_level(filename);
230 Menu::set_current(main_menu);
232 mouse_cursor->set_state(MC_NORMAL);
243 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
247 void LevelEditor::events()
251 while(SDL_PollEvent(&event))
253 Menu* menu = Menu::current();
258 if(menu == main_menu)
260 switch (main_menu->check())
263 Menu::set_current(0);
270 else if(menu == create_subset_menu)
272 // activate or deactivate Create button if any filename as been specified
273 if(create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input[0] == '\0')
274 create_subset_menu->get_item_by_id(MN_ID_CREATE_SUBSET).kind = MN_DEACTIVE;
276 create_subset_menu->get_item_by_id(MN_ID_CREATE_SUBSET).kind = MN_ACTION;
278 if(create_subset_menu->check() == MN_ID_CREATE_SUBSET)
279 { // applying settings:
280 std::string subset_name = create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input;
281 LevelSubset::create(subset_name);
284 level_subset = new LevelSubset();
285 level_subset->load(create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input);
287 level_subset->title = create_subset_menu->get_item_by_id(MN_ID_TITLE_SUBSET).input;
288 level_subset->description = create_subset_menu->get_item_by_id(MN_ID_DESCRIPTION_SUBSET).input;
289 //FIXME: generate better level filenames
290 level_subset->add_level(subset_name+'/'+"new_level.stl");
291 Level* newlevel = new Level();
292 newlevel->add_sector(create_sector("main", 25, 19));
293 newlevel->save(level_subset->get_level_filename(0));
294 level_subset->save();
298 create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).change_input("");
299 create_subset_menu->get_item_by_id(MN_ID_TITLE_SUBSET).change_input("");
300 create_subset_menu->get_item_by_id(MN_ID_DESCRIPTION_SUBSET).change_input("");
303 else if(menu == subset_menu)
305 int i = subset_menu->check();
308 std::set<std::string>::iterator it = level_subsets.begin();
309 for(int t = 0; t < i; t++)
311 load_level_subset(*it);
312 Menu::set_current(0);
315 else if(menu == settings_menu)
317 if(settings_menu->check() == MN_ID_APPLY_SETTINGS)
318 { // applying settings:
319 level_changed = true;
321 level->name = settings_menu->get_item_by_id(MN_ID_NAME).input;
322 level->author = settings_menu->get_item_by_id(MN_ID_AUTHOR).input;
324 solids->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()),
325 atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str()));
326 foregrounds->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()),
327 atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str()));
328 backgrounds->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()));
331 Menu::set_current(0);
335 // check for events in buttons
336 else if(tiles_board->event(event))
338 std::vector <int> vector;
339 vector.push_back(tiles_board->selected_id());
342 selection.push_back(vector);
345 else if(tiles_layer->event(event))
347 cur_layer = tiles_layer->selected_id();
350 else if(level_options->event(event))
352 switch(level_options->selected_id())
361 Menu::set_current(settings_menu);
364 if(level_nb + 1 < level_subset->get_num_levels())
365 load_level(level_nb + 1);
369 sprintf(str,_("Level %d doesn't exist. Create it?"), level_nb + 2);
370 if(confirm_dialog(NULL, str))
372 level_subset->add_level("new_level.stl");
373 Level* newlevel = new Level();
374 newlevel->add_sector(create_sector("main", 25, 19));
375 newlevel->save(level_subset->get_level_filename(level_nb + 1));
376 level_subset->save();
377 load_level(level_nb + 1);
381 case BT_PREVIOUS_LEVEL:
382 if(level_nb - 1 >= 0)
383 load_level(level_nb - 1);
386 std::cerr << "next sector.\n";
387 std::cerr << "total sectors: " << level->get_total_sectors() << std::endl;
388 load_sector(level->get_next_sector(sector));
390 case BT_PREVIOUS_SECTOR:
391 std::cerr << "previous sector.\n";
392 load_sector(level->get_previous_sector(sector));
395 level_options->set_unselected();
402 case SDL_MOUSEMOTION:
404 if(SDL_GetMouseState(NULL, NULL)&SDL_BUTTON(SDL_BUTTON_RIGHT))
405 { // movement like in strategy games
406 scroll.x += -1 * event.motion.xrel;
407 scroll.y += -1 * event.motion.yrel;
411 case SDL_MOUSEBUTTONDOWN:
413 if(event.button.button == SDL_BUTTON_LEFT)
415 else if(event.button.button == SDL_BUTTON_MIDDLE)
417 middle_button = true;
418 selection_ini = Vector(event.button.x, event.button.y);
422 case SDL_MOUSEBUTTONUP:
424 if(event.button.button == SDL_BUTTON_LEFT)
426 else if(event.button.button == SDL_BUTTON_MIDDLE)
428 middle_button = false;
429 selection_end = Vector(event.button.x, event.button.y);
431 if(selection_end.x < selection_ini.x)
433 float t = selection_ini.x;
434 selection_ini.x = selection_end.x;
437 if(selection_end.y < selection_ini.y)
439 float t = selection_ini.y;
440 selection_ini.y = selection_end.y;
445 std::vector <int> vector;
447 TileMap* tilemap = 0;
448 if(cur_layer == LAYER_FOREGROUNDTILES)
449 tilemap = foregrounds;
450 else if(cur_layer == LAYER_TILES)
452 else if(cur_layer == LAYER_BACKGROUNDTILES)
453 tilemap = backgrounds;
455 for(int x = 0; x < (int)((selection_end.x - selection_ini.x)*zoom / 32) + 1; x++)
458 for(int y = 0; y < (int)((selection_end.y - selection_ini.y)*zoom / 32) + 1; y++)
460 vector.push_back(tilemap->get_tile(x +
461 (int)(((selection_ini.x+scroll.x)*zoom)/32),
462 y + (int)(((selection_ini.y+scroll.y)*zoom)/32))->id);
464 selection.push_back(vector);
469 case SDL_KEYDOWN: // key pressed
470 switch(event.key.keysym.sym)
473 Menu::set_current(main_menu);
475 /* scrolling related events: */
480 scroll.x = sector->solids->get_height()*32 - screen->w;
513 show_grid = !show_grid;
520 case SDL_QUIT: // window closed
531 void LevelEditor::action()
533 mouse_cursor->set_state(MC_NORMAL);
534 if(tiles_board->is_hover() || tiles_layer->is_hover() || level_options->is_hover())
535 mouse_cursor->set_state(MC_LINK);
539 // don't scroll before the start or after the level's end
540 float width = sector->solids->get_width() * 32;
541 float height = sector->solids->get_height() * 32;
543 if(scroll.x < -screen->w/2)
544 scroll.x = -screen->w/2;
545 if(scroll.x > width - screen->w/2)
546 scroll.x = width - screen->w/2;
547 if(scroll.y < -screen->h/2)
548 scroll.y = -screen->h/2;
549 if(scroll.y > height - screen->h/2)
550 scroll.y = height - screen->h/2;
552 // set camera translation, since BadGuys like it
553 sector->camera->set_scrolling((int)scroll.x, (int)scroll.y);
555 if(left_button && mouse_moved)
556 for(unsigned int x = 0; x < selection.size(); x++)
557 for(unsigned int y = 0; y < selection[x].size(); y++)
558 change((int)(scroll.x + event.button.x) + (x*32),
559 (int)(scroll.y + event.button.y) + (y*32), selection[x][y],
564 #define FADING_TIME .6
566 void LevelEditor::draw(DrawingContext& context)
568 context.draw_text(white_text, _("Level Editor"), Vector(10, 5), LEFT_ALLIGN, LAYER_GUI);
569 mouse_cursor->draw(context);
571 // draw a filled background
572 context.draw_filled_rect(Vector(0,0), Vector(screen->w,screen->h), Color(60,60,60), LAYER_BACKGROUND0-1);
574 if(level_name_timer.check())
576 context.push_transform();
577 if(level_name_timer.get_timeleft() < FADING_TIME)
578 context.set_alpha(int(level_name_timer.get_timeleft() * 255 / FADING_TIME));
580 context.draw_text(gold_text, level->name, Vector(screen->w/2, 30), CENTER_ALLIGN, LAYER_GUI);
584 sprintf(str, "%i/%i", level_nb+1, level_subset->get_num_levels());
585 context.draw_text(gold_text, str, Vector(screen->w/2, 50), CENTER_ALLIGN, LAYER_GUI);
588 context.pop_transform();
591 context.draw_text(white_small_text, _("F1 for help"), Vector(5, 510), LEFT_ALLIGN, LAYER_GUI-10);
593 context.draw_text(white_small_text, _("Choose a level subset"), Vector(5, 510), LEFT_ALLIGN, LAYER_GUI-10);
595 Menu* menu = Menu::current();
600 tiles_board->draw(context);
601 tiles_layer->draw(context);
602 level_options->draw(context);
610 context.set_drawing_effect(SEMI_TRANSPARENT);
614 if(selection[0][0] == 0 && selection.size() == 1)
615 context.draw_surface(img_rubber_bt, Vector(event.button.x - 8,
616 event.button.y - 8), LAYER_GUI-2);
617 else if(selection[0][0] < 0)
619 int id = selection[0][0];
622 if(id == OBJ_TRAMPOLINE)
623 context.draw_surface(img_trampoline[0].get_frame(0), Vector(event.button.x - 8,
624 event.button.y - 8), LAYER_GUI-2);
625 else if(id == OBJ_FLYING_PLATFORM)
626 context.draw_surface(img_flying_platform->get_frame(0), Vector(event.button.x - 8,
627 event.button.y - 8), LAYER_GUI-2);
631 /*context.draw_surface(door->get_frame(0), Vector(event.button.x - 8,
632 event.button.y - 8), LAYER_GUI-2);*/
637 BadGuyKind kind = BadGuyKind((-id)-1);
638 BadGuy badguy(kind, 0,0);
639 badguy.activate(LEFT);
640 Surface *img = badguy.get_image();
642 context.draw_surface(img, Vector(event.button.x - 8,
643 event.button.y - 8), LAYER_GUI-2);
649 TileManager* tilemanager = TileManager::instance();
650 for(unsigned int x = 0; x < selection.size(); x++)
651 for(unsigned int y = 0; y < selection[x].size(); y++) {
652 const Tile* tile = tilemanager->get(selection[x][y]);
654 Vector(event.button.x + x*32 - 8, event.button.y + y*32 - 8),
659 context.set_drawing_effect(NONE_EFFECT);
662 context.draw_filled_rect(Vector(std::min((int)selection_ini.x, (int)event.button.x)*zoom,
663 std::min((int)selection_ini.y, (int)event.button.y))*zoom,
664 Vector(abs(event.button.x - (int)selection_ini.x)*zoom,
665 abs(event.button.y - (int)selection_ini.y)*zoom),
666 Color(170,255,170,128), LAYER_GUI-2);
670 for(int x = 0; x < screen->w / (32*zoom); x++)
672 int pos = (int)(x*32*zoom) - ((int)scroll.x % 32);
673 context.draw_filled_rect(Vector (pos, 0), Vector(1, screen->h),
674 Color(225, 225, 225), LAYER_GUI-50);
676 for(int y = 0; y < screen->h / (32*zoom); y++)
678 int pos = (int)(y*32*zoom) - ((int)scroll.y % 32);
679 context.draw_filled_rect(Vector (0, pos), Vector(screen->w, 1),
680 Color(225, 225, 225), LAYER_GUI-50);
684 context.push_transform();
685 context.set_translation(scroll);
686 context.set_zooming(zoom);
688 for(Sector::GameObjects::iterator i = sector->gameobjects.begin(); i != sector->gameobjects.end(); ++i)
690 TileMap* tilemap = dynamic_cast<TileMap*> (*i);
692 { // draw the non-selected tiles semi-transparently
693 context.push_transform();
695 if(tilemap->get_layer() != cur_layer)
696 context.set_drawing_effect(SEMI_TRANSPARENT);
699 context.pop_transform();
702 Background* background = dynamic_cast<Background*> (*i);
704 { // don't resize background
705 context.push_transform();
706 context.set_zooming(1.0);
708 context.pop_transform();
714 context.pop_transform();
717 context.draw_filled_rect(Vector(0,0), Vector(screen->w,screen->h),Color(0,0,0), LAYER_BACKGROUND0);
719 context.do_drawing();
722 void LevelEditor::load_level_subset(std::string filename)
725 level_subset = new LevelSubset();
726 level_subset->load(filename.c_str());
730 void LevelEditor::load_level(std::string filename)
734 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
740 level_filename = filename;
744 level->load(filename);
747 level_name_timer.start(3000);
748 scroll.x = scroll.y = 0;
749 level_changed = false;
751 settings_menu->get_item_by_id(MN_ID_NAME).change_input(level->name.c_str());
752 settings_menu->get_item_by_id(MN_ID_AUTHOR).change_input(level->author.c_str());
755 void LevelEditor::load_level(int nb)
759 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
766 level_filename = level_subset->get_level_filename(level_nb);
768 load_level(level_filename);
771 void LevelEditor::load_sector(std::string name)
774 sector = level->get_sector(sector_name);
776 Termination::abort("Level has no " + sector_name + " sector.", "");
781 void LevelEditor::load_sector(Sector* sector_)
785 if(!confirm_dialog(NULL, _("No more sectors exist. Create another?")))
787 sector_ = create_sector("new_sector",25,19);
788 level->add_sector(sector_);
793 /* Load sector stuff */
795 sector->update_game_objects();
797 foregrounds = solids = backgrounds = 0;
798 /* Point foregrounds, backgrounds, solids to its layer */
799 for(Sector::GameObjects::iterator i = sector->gameobjects.begin(); i != sector->gameobjects.end(); i++)
802 BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
804 badguy->activate(LEFT);
807 TileMap* tilemap = dynamic_cast<TileMap*> (*i);
810 if(tilemap->get_layer() == LAYER_FOREGROUNDTILES)
811 foregrounds = tilemap;
812 else if(tilemap->get_layer() == LAYER_TILES)
814 else if(tilemap->get_layer() == LAYER_BACKGROUNDTILES)
815 backgrounds = tilemap;
821 TileMap* tilemap = new TileMap(LAYER_FOREGROUNDTILES, false, solids->get_width(), solids->get_height());
822 sector->add_object(tilemap);
823 sector->update_game_objects();
827 TileMap* tilemap = new TileMap(LAYER_BACKGROUNDTILES, false, solids->get_width(), solids->get_height());
828 sector->add_object(tilemap);
829 sector->update_game_objects();
833 sprintf(str, "%i", solids->get_width());
834 settings_menu->get_item_by_id(MN_ID_WIDTH).change_input(str);
835 sprintf(str, "%i", solids->get_height());
836 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 SoundManager::get()->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;
871 { // no need to do this for normal view (no zoom)
872 x = (int)(x * (zoom*32) / 32);
873 y = (int)(y * (zoom*32) / 32);
876 if(newtile < 0) // add object
878 // remove an active tile or object that might be there
879 change(x, y, 0, LAYER_TILES);
882 if(newtile == OBJ_TRAMPOLINE)
883 sector->add_object(new Trampoline(x, y));
884 else if(newtile == OBJ_FLYING_PLATFORM)
885 sector->add_object(new FlyingPlatform(x, y));
887 if(newtile == OBJ_DOOR)
888 sector->add_object(new Door(x, y));
890 sector->add_bad_guy(x, y, BadGuyKind((-newtile)-1), true);
893 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++) {
902 BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
904 if((int)badguy->base.x/32 == x/32 && (int)badguy->base.y/32 == y/32)
905 sector->gameobjects.erase(i);
908 Trampoline* trampoline = dynamic_cast<Trampoline*> (*i);
911 if((int)trampoline->base.x/32 == x/32 && (int)trampoline->base.y/32 == y/32)
912 sector->gameobjects.erase(i);
914 FlyingPlatform* flying_platform = dynamic_cast<FlyingPlatform*> (*i);
916 if((int)flying_platform->base.x/32 == x/32 && (int)flying_platform->base.y/32 == y/32)
917 sector->gameobjects.erase(i);
920 Door* door = dynamic_cast<Door*> (*i);
922 if((int)door->get_area().x/32 == x/32 && (int)door->get_area().y/32 == y/32)
923 sector->gameobjects.erase(i);
926 sector->update_game_objects();
927 solids->change(x/32, y/32, newtile);
928 } else if(cur_layer == LAYER_BACKGROUNDTILES)
929 backgrounds->change(x/32, y/32, newtile);
932 void LevelEditor::show_help()
934 DrawingContext context;
936 bool show_grid_t = show_grid;
938 mouse_cursor->set_state(MC_HIDE);
942 const char *text1[] = {
943 _("This is the built-in level editor. Its aim is to be intuitive\n"
944 "and simple to use, so it should be pretty straightforward.\n"
946 "To open a level, first you'll have to select a level subset from\n"
947 "the menu (or create your own).\n"
948 "A level subset is basically a collection of levels.\n"
949 "They can then be played from the Contrib menu.\n"
951 "To access the menu from the level editor, just press Esc.\n"
953 "You are currently looking at the level. To scroll it, just\n"
954 "press the right mouse button and drag the mouse. It will move like\n"
956 "You can also use the arrow keys and Page Up/Down.\n"
958 "'+' and '-' keys can be used to zoom the level in/out.\n"
960 "You probably already noticed those floating groups of buttons.\n"
961 "Each one serves a different purpose. To select a certain button\n"
962 "just press the Left mouse button on it. A few buttons have key\n"
963 "shortcuts. You can find them by pressing the Right mouse button on\n"
964 "a button. That will also show what that button does.\n"
965 "Groups of buttons can also be moved around by just dragging them,\n"
966 "while pressing the Left mouse button.\n"
968 "Let's learn a bit of what each group of buttons does, shall we?\n"
970 "To starting putting tiles and objects around use the bigger group\n"
971 "of buttons. Each button is a different tile. To put it on the level,\n"
972 "just press it and then left click in the level.\n"
973 "You can also copy tiles from the level by using the middle mouse button.\n"
974 "Use the mouse wheel to scroll that group of buttons. You will find\n"
975 "enemies and game objects in the bottom.\n")
978 const char *text2[] = {
979 _("The Foreground/Interactive/Background buttons may be used to\n"
980 "see and edit the respective layer. Levels have three tiles layers:\n"
981 "Foreground - tiles are drawn on top of everything and have no contact\n"
983 "Interactive - these are the tiles that have contact with the player.\n"
984 "Background - tiles are drawn underneath everything and have no contact\n"
986 "The unselected layers will be drawn semi-transparently.\n"
988 "Last, but not least, the group of buttons that's left serves\n"
989 "to do related actions with the level.\n"
990 "From left to right:\n"
991 "Mini arrows - can be used to choose other sectors.\n"
992 "Sectors are mini-levels, so to speak, that can be accessed using a door.\n"
993 "Big arrows - choose other level in the same level subset.\n"
994 "Diskette - save the level\n"
995 "Tux - test the level\n"
996 "Tools - set a few settings for the level, including resizing it.\n"
998 "We have reached the end of this Howto.\n"
1000 "Don't forget to send us a few cool levels. :)\n"
1003 " SuperTux development team\n"
1005 "PS: If you are looking for something more powerful, you might like to\n"
1006 "try FlexLay. FlexLay is a level editor that supports several games,\n"
1007 "including SuperTux. It is an independent project.\n"
1008 "Webpage: http://pingus.seul.org/~grumbel/flexlay/")
1011 const char **text[] = { text1, text2 };
1015 for(unsigned int i = 0; i < sizeof(text) / sizeof(text[0]); i++)
1019 context.draw_text(blue_text, _("- Level Editor's Help -"), Vector(screen->w/2, 60), CENTER_ALLIGN, LAYER_GUI);
1021 context.draw_text(white_small_text, *text[i], Vector(20, 120), LEFT_ALLIGN, LAYER_GUI);
1023 sprintf(str,_("Press any key to continue - Page %d/%d"), i+1, sizeof(text) / sizeof(text[0]));
1024 context.draw_text(gold_text, str, Vector(screen->w/2, screen->h-60), CENTER_ALLIGN, LAYER_GUI);
1026 context.do_drawing();
1032 done = wait_for_event(event);
1037 show_grid = show_grid_t;
1038 mouse_cursor->set_state(MC_NORMAL);
1042 LevelEditor::create_sector(const std::string& name, size_t width, size_t height)
1044 Sector* sector = new Sector;
1045 sector->set_name(name);
1047 sector->add_object(new TileMap(LAYER_BACKGROUNDTILES, false, width, height));
1048 sector->add_object(new TileMap(LAYER_TILES, true, width, height));
1049 sector->add_object(new TileMap(LAYER_FOREGROUNDTILES, false, width, height));
1050 sector->add_object(new Camera(sector));
1051 sector->update_game_objects();