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);
113 Surface* surface = tile->get_editor_image();
117 Button button = Button(surface, "", SDLKey(0));
118 tiles_board->add_button(button, id);
122 for(int i = 0; i < NUM_BadGuyKinds; i++)
124 // filter bomb, since it is only for internal use, not for levels
128 BadGuyKind kind = BadGuyKind(i);
129 BadGuy badguy(kind, 0,0);
130 badguy.activate(LEFT);
132 Surface *img = badguy.get_image();
133 tiles_board->add_button(Button(img, "", SDLKey(SDLK_1+i)), -(i+1));
138 tiles_board->add_button(Button(img_trampoline[0].get_frame(0), _("Trampoline"), SDLKey(0)), OBJ_TRAMPOLINE);
139 tiles_board->add_button(Button(img_flying_platform->get_frame(0), _("Flying Platform"), SDLKey(0)), OBJ_FLYING_PLATFORM);
142 tiles_layer = new ButtonGroup(Vector(12, screen->h-64), Vector(80,20), Vector(1,3));
143 tiles_layer->add_button(Button(img_foreground_bt, _("Edtit foreground tiles"),
144 SDLK_F10), LAYER_FOREGROUNDTILES);
145 tiles_layer->add_button(Button(img_interactive_bt, _("Edit interactive tiles"),
146 SDLK_F11), LAYER_TILES, true);
147 tiles_layer->add_button(Button(img_background_bt, _("Edit background tiles"),
148 SDLK_F12), LAYER_BACKGROUNDTILES);
150 level_options = new ButtonGroup(Vector(screen->w-164, screen->h-36), Vector(32,32), Vector(5,1));
151 level_options->add_pair_of_buttons(Button(img_next_sector_bt, _("Next sector"), SDLKey(0)), BT_NEXT_SECTOR,
152 Button(img_previous_sector_bt, _("Prevous sector"), SDLKey(0)), BT_PREVIOUS_SECTOR);
153 level_options->add_pair_of_buttons(Button(img_next_level_bt, _("Next level"), SDLKey(0)), BT_NEXT_LEVEL,
154 Button(img_previous_level_bt, _("Prevous level"), SDLKey(0)), BT_PREVIOUS_LEVEL);
155 level_options->add_button(Button(img_save_level_bt, _("Save level"), SDLK_F5), BT_LEVEL_SAVE);
156 level_options->add_button(Button(img_test_level_bt, _("Test level"), SDLK_F6), BT_LEVEL_TEST);
157 level_options->add_button(Button(img_setup_level_bt, _("Setup level"), SDLK_F7), BT_LEVEL_SETUP);
160 LevelEditor::~LevelEditor()
166 delete level_options;
169 delete create_subset_menu;
171 delete settings_menu;
177 void LevelEditor::load_buttons_gfx()
179 img_foreground_bt = new Surface(datadir + "/images/leveleditor/foreground.png", true);
180 img_interactive_bt = new Surface(datadir + "/images/leveleditor/interactive.png", true);
181 img_background_bt = new Surface(datadir + "/images/leveleditor/background.png", true);
183 img_save_level_bt = new Surface(datadir + "/images/leveleditor/save-level.png", true);
184 img_test_level_bt = new Surface(datadir + "/images/leveleditor/test-level.png", true);
185 img_setup_level_bt = new Surface(datadir + "/images/leveleditor/setup-level.png", true);
187 img_rubber_bt = new Surface(datadir + "/images/leveleditor/rubber.png", true);
189 img_previous_level_bt = new Surface(datadir + "/images/leveleditor/previous-level.png", true);
190 img_next_level_bt = new Surface(datadir + "/images/leveleditor/next-level.png", true);
191 img_previous_sector_bt = new Surface(datadir + "/images/leveleditor/previous-sector.png", true);
192 img_next_sector_bt = new Surface(datadir + "/images/leveleditor/next-sector.png", true);
195 void LevelEditor::free_buttons_gfx()
197 delete img_foreground_bt;
198 delete img_interactive_bt;
199 delete img_background_bt;
201 delete img_save_level_bt;
202 delete img_test_level_bt;
203 delete img_setup_level_bt;
205 delete img_rubber_bt;
207 delete img_previous_level_bt;
208 delete img_next_level_bt;
209 delete img_previous_sector_bt;
210 delete img_next_sector_bt;
213 void LevelEditor::run(const std::string filename)
215 SoundManager::get()->halt_music();
216 Menu::set_current(0);
218 DrawingContext context;
220 if(!filename.empty())
223 load_level(filename);
226 Menu::set_current(main_menu);
228 mouse_cursor->set_state(MC_NORMAL);
239 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
243 void LevelEditor::events()
247 while(SDL_PollEvent(&event))
249 Menu* menu = Menu::current();
254 if(menu == main_menu)
256 switch (main_menu->check())
259 Menu::set_current(0);
266 else if(menu == create_subset_menu)
268 // activate or deactivate Create button if any filename as been specified
269 if(create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input[0] == '\0')
270 create_subset_menu->get_item_by_id(MN_ID_CREATE_SUBSET).kind = MN_DEACTIVE;
272 create_subset_menu->get_item_by_id(MN_ID_CREATE_SUBSET).kind = MN_ACTION;
274 if(create_subset_menu->check() == MN_ID_CREATE_SUBSET)
275 { // applying settings:
276 std::string subset_name = create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input;
277 LevelSubset::create(subset_name);
280 level_subset = new LevelSubset();
281 level_subset->load(create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input);
283 level_subset->title = create_subset_menu->get_item_by_id(MN_ID_TITLE_SUBSET).input;
284 level_subset->description = create_subset_menu->get_item_by_id(MN_ID_DESCRIPTION_SUBSET).input;
285 //FIXME: generate better level filenames
286 level_subset->add_level(subset_name+'/'+"new_level.stl");
287 Level* newlevel = new Level();
288 newlevel->add_sector(create_sector("main", 25, 19));
289 newlevel->save(level_subset->get_level_filename(0));
290 level_subset->save();
294 create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).change_input("");
295 create_subset_menu->get_item_by_id(MN_ID_TITLE_SUBSET).change_input("");
296 create_subset_menu->get_item_by_id(MN_ID_DESCRIPTION_SUBSET).change_input("");
299 else if(menu == subset_menu)
301 int i = subset_menu->check();
304 std::set<std::string>::iterator it = level_subsets.begin();
305 for(int t = 0; t < i; t++)
307 load_level_subset(*it);
308 Menu::set_current(0);
311 else if(menu == settings_menu)
313 if(settings_menu->check() == MN_ID_APPLY_SETTINGS)
314 { // applying settings:
315 level_changed = true;
317 level->name = settings_menu->get_item_by_id(MN_ID_NAME).input;
318 level->author = settings_menu->get_item_by_id(MN_ID_AUTHOR).input;
320 solids->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()),
321 atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str()));
322 foregrounds->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()),
323 atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str()));
324 backgrounds->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()));
327 Menu::set_current(0);
331 // check for events in buttons
332 else if(tiles_board->event(event))
334 std::vector <int> vector;
335 vector.push_back(tiles_board->selected_id());
338 selection.push_back(vector);
341 else if(tiles_layer->event(event))
343 cur_layer = tiles_layer->selected_id();
346 else if(level_options->event(event))
348 switch(level_options->selected_id())
357 Menu::set_current(settings_menu);
360 if(level_nb + 1 < level_subset->get_num_levels())
361 load_level(level_nb + 1);
365 sprintf(str,_("Level %d doesn't exist. Create it?"), level_nb + 2);
366 if(confirm_dialog(NULL, str))
368 level_subset->add_level("new_level.stl");
369 Level* newlevel = new Level();
370 newlevel->add_sector(create_sector("main", 25, 19));
371 newlevel->save(level_subset->get_level_filename(level_nb + 1));
372 level_subset->save();
373 load_level(level_nb + 1);
377 case BT_PREVIOUS_LEVEL:
378 if(level_nb - 1 >= 0)
379 load_level(level_nb - 1);
382 std::cerr << "next sector.\n";
383 std::cerr << "total sectors: " << level->get_total_sectors() << std::endl;
384 load_sector(level->get_next_sector(sector));
386 case BT_PREVIOUS_SECTOR:
387 std::cerr << "previous sector.\n";
388 load_sector(level->get_previous_sector(sector));
391 level_options->set_unselected();
398 case SDL_MOUSEMOTION:
400 if(SDL_GetMouseState(NULL, NULL)&SDL_BUTTON(SDL_BUTTON_RIGHT))
401 { // movement like in strategy games
402 scroll.x += -1 * event.motion.xrel;
403 scroll.y += -1 * event.motion.yrel;
407 case SDL_MOUSEBUTTONDOWN:
409 if(event.button.button == SDL_BUTTON_LEFT)
411 else if(event.button.button == SDL_BUTTON_MIDDLE)
413 middle_button = true;
414 selection_ini = Vector(event.button.x, event.button.y);
418 case SDL_MOUSEBUTTONUP:
420 if(event.button.button == SDL_BUTTON_LEFT)
422 else if(event.button.button == SDL_BUTTON_MIDDLE)
424 middle_button = false;
425 selection_end = Vector(event.button.x, event.button.y);
427 if(selection_end.x < selection_ini.x)
429 float t = selection_ini.x;
430 selection_ini.x = selection_end.x;
433 if(selection_end.y < selection_ini.y)
435 float t = selection_ini.y;
436 selection_ini.y = selection_end.y;
441 std::vector <int> vector;
443 TileMap* tilemap = 0;
444 if(cur_layer == LAYER_FOREGROUNDTILES)
445 tilemap = foregrounds;
446 else if(cur_layer == LAYER_TILES)
448 else if(cur_layer == LAYER_BACKGROUNDTILES)
449 tilemap = backgrounds;
451 for(int x = 0; x < (int)((selection_end.x - selection_ini.x)*zoom / 32) + 1; x++)
454 for(int y = 0; y < (int)((selection_end.y - selection_ini.y)*zoom / 32) + 1; y++)
456 vector.push_back(tilemap->get_tile(x +
457 (int)(((selection_ini.x+scroll.x)*zoom)/32),
458 y + (int)(((selection_ini.y+scroll.y)*zoom)/32))->getID());
460 selection.push_back(vector);
465 case SDL_KEYDOWN: // key pressed
466 switch(event.key.keysym.sym)
469 Menu::set_current(main_menu);
471 /* scrolling related events: */
476 scroll.x = sector->solids->get_height()*32 - screen->w;
509 show_grid = !show_grid;
516 case SDL_QUIT: // window closed
527 void LevelEditor::action()
529 mouse_cursor->set_state(MC_NORMAL);
530 if(tiles_board->is_hover() || tiles_layer->is_hover() || level_options->is_hover())
531 mouse_cursor->set_state(MC_LINK);
535 // don't scroll before the start or after the level's end
536 float width = sector->solids->get_width() * 32;
537 float height = sector->solids->get_height() * 32;
539 if(scroll.x < -screen->w/2)
540 scroll.x = -screen->w/2;
541 if(scroll.x > width - screen->w/2)
542 scroll.x = width - screen->w/2;
543 if(scroll.y < -screen->h/2)
544 scroll.y = -screen->h/2;
545 if(scroll.y > height - screen->h/2)
546 scroll.y = height - screen->h/2;
548 // set camera translation, since BadGuys like it
549 sector->camera->set_scrolling((int)scroll.x, (int)scroll.y);
551 if(left_button && mouse_moved)
552 for(unsigned int x = 0; x < selection.size(); x++)
553 for(unsigned int y = 0; y < selection[x].size(); y++)
554 change((int)(scroll.x + event.button.x) + (x*32),
555 (int)(scroll.y + event.button.y) + (y*32), selection[x][y],
560 #define FADING_TIME .6
562 void LevelEditor::draw(DrawingContext& context)
564 context.draw_text(white_text, _("Level Editor"), Vector(10, 5), LEFT_ALLIGN, LAYER_GUI);
565 mouse_cursor->draw(context);
567 // draw a filled background
568 context.draw_filled_rect(Vector(0,0), Vector(screen->w,screen->h), Color(60,60,60), LAYER_BACKGROUND0-1);
570 if(level_name_timer.check())
572 context.push_transform();
573 if(level_name_timer.get_timeleft() < FADING_TIME)
574 context.set_alpha(int(level_name_timer.get_timeleft() * 255 / FADING_TIME));
576 context.draw_text(gold_text, level->name, Vector(screen->w/2, 30), CENTER_ALLIGN, LAYER_GUI);
580 sprintf(str, "%i/%i", level_nb+1, level_subset->get_num_levels());
581 context.draw_text(gold_text, str, Vector(screen->w/2, 50), CENTER_ALLIGN, LAYER_GUI);
584 context.pop_transform();
587 context.draw_text(white_small_text, _("F1 for help"), Vector(5, 510), LEFT_ALLIGN, LAYER_GUI-10);
589 context.draw_text(white_small_text, _("Choose a level subset"), Vector(5, 510), LEFT_ALLIGN, LAYER_GUI-10);
591 Menu* menu = Menu::current();
596 tiles_board->draw(context);
597 tiles_layer->draw(context);
598 level_options->draw(context);
606 context.set_drawing_effect(SEMI_TRANSPARENT);
610 if(selection[0][0] == 0 && selection.size() == 1)
611 context.draw_surface(img_rubber_bt, Vector(event.button.x - 8,
612 event.button.y - 8), LAYER_GUI-2);
613 else if(selection[0][0] < 0)
615 int id = selection[0][0];
618 if(id == OBJ_TRAMPOLINE)
619 context.draw_surface(img_trampoline[0].get_frame(0), Vector(event.button.x - 8,
620 event.button.y - 8), LAYER_GUI-2);
621 else if(id == OBJ_FLYING_PLATFORM)
622 context.draw_surface(img_flying_platform->get_frame(0), Vector(event.button.x - 8,
623 event.button.y - 8), LAYER_GUI-2);
627 /*context.draw_surface(door->get_frame(0), Vector(event.button.x - 8,
628 event.button.y - 8), LAYER_GUI-2);*/
633 BadGuyKind kind = BadGuyKind((-id)-1);
634 BadGuy badguy(kind, 0,0);
635 badguy.activate(LEFT);
636 Surface *img = badguy.get_image();
638 context.draw_surface(img, Vector(event.button.x - 8,
639 event.button.y - 8), LAYER_GUI-2);
645 TileManager* tilemanager = TileManager::instance();
646 for(unsigned int x = 0; x < selection.size(); x++)
647 for(unsigned int y = 0; y < selection[x].size(); y++) {
648 const Tile* tile = tilemanager->get(selection[x][y]);
650 Vector(event.button.x + x*32 - 8, event.button.y + y*32 - 8),
655 context.set_drawing_effect(NONE_EFFECT);
658 context.draw_filled_rect(Vector(std::min((int)selection_ini.x, (int)event.button.x)*zoom,
659 std::min((int)selection_ini.y, (int)event.button.y))*zoom,
660 Vector(abs(event.button.x - (int)selection_ini.x)*zoom,
661 abs(event.button.y - (int)selection_ini.y)*zoom),
662 Color(170,255,170,128), LAYER_GUI-2);
666 for(int x = 0; x < screen->w / (32*zoom); x++)
668 int pos = (int)(x*32*zoom) - ((int)scroll.x % 32);
669 context.draw_filled_rect(Vector (pos, 0), Vector(1, screen->h),
670 Color(225, 225, 225), LAYER_GUI-50);
672 for(int y = 0; y < screen->h / (32*zoom); y++)
674 int pos = (int)(y*32*zoom) - ((int)scroll.y % 32);
675 context.draw_filled_rect(Vector (0, pos), Vector(screen->w, 1),
676 Color(225, 225, 225), LAYER_GUI-50);
680 context.push_transform();
681 context.set_translation(scroll);
682 context.set_zooming(zoom);
684 for(Sector::GameObjects::iterator i = sector->gameobjects.begin(); i != sector->gameobjects.end(); ++i)
686 TileMap* tilemap = dynamic_cast<TileMap*> (*i);
688 { // draw the non-selected tiles semi-transparently
689 context.push_transform();
691 if(tilemap->get_layer() != cur_layer)
692 context.set_drawing_effect(SEMI_TRANSPARENT);
695 context.pop_transform();
698 Background* background = dynamic_cast<Background*> (*i);
700 { // don't resize background
701 context.push_transform();
702 context.set_zooming(1.0);
704 context.pop_transform();
710 context.pop_transform();
713 context.draw_filled_rect(Vector(0,0), Vector(screen->w,screen->h),Color(0,0,0), LAYER_BACKGROUND0);
715 context.do_drawing();
718 void LevelEditor::load_level_subset(std::string filename)
721 level_subset = new LevelSubset();
722 level_subset->load(filename.c_str());
726 void LevelEditor::load_level(std::string filename)
730 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
736 level_filename = filename;
740 level->load(filename);
743 level_name_timer.start(3000);
744 scroll.x = scroll.y = 0;
745 level_changed = false;
747 settings_menu->get_item_by_id(MN_ID_NAME).change_input(level->name.c_str());
748 settings_menu->get_item_by_id(MN_ID_AUTHOR).change_input(level->author.c_str());
751 void LevelEditor::load_level(int nb)
755 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
762 level_filename = level_subset->get_level_filename(level_nb);
764 load_level(level_filename);
767 void LevelEditor::load_sector(std::string name)
770 sector = level->get_sector(sector_name);
772 Termination::abort("Level has no " + sector_name + " sector.", "");
777 void LevelEditor::load_sector(Sector* sector_)
781 if(!confirm_dialog(NULL, _("No more sectors exist. Create another?")))
783 sector_ = create_sector("new_sector",25,19);
784 level->add_sector(sector_);
789 /* Load sector stuff */
791 sector->update_game_objects();
793 foregrounds = solids = backgrounds = 0;
794 /* Point foregrounds, backgrounds, solids to its layer */
795 for(Sector::GameObjects::iterator i = sector->gameobjects.begin(); i != sector->gameobjects.end(); i++)
798 BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
800 badguy->activate(LEFT);
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);
835 void LevelEditor::save_level()
837 level->save(level_filename);
838 level_changed = false;
841 void LevelEditor::test_level()
844 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
850 GameSession session(level_filename, ST_GL_TEST);
852 // player_status.reset();
853 SoundManager::get()->halt_music();
856 void LevelEditor::change(int x, int y, int newtile, int layer)
859 // find the tilemap of the current layer, and then change the tile
860 if(x < 0 || (unsigned int)x >= sector->solids->get_width()*32 ||
861 y < 0 || (unsigned int)y >= sector->solids->get_height()*32)
864 level_changed = true;
867 { // no need to do this for normal view (no zoom)
868 x = (int)(x * (zoom*32) / 32);
869 y = (int)(y * (zoom*32) / 32);
872 if(newtile < 0) // add object
874 // remove an active tile or object that might be there
875 change(x, y, 0, LAYER_TILES);
878 if(newtile == OBJ_TRAMPOLINE)
879 sector->add_object(new Trampoline(x, y));
880 else if(newtile == OBJ_FLYING_PLATFORM)
881 sector->add_object(new FlyingPlatform(x, y));
883 if(newtile == OBJ_DOOR)
884 sector->add_object(new Door(x, y));
886 sector->add_bad_guy(x, y, BadGuyKind((-newtile)-1), true);
889 sector->update_game_objects();
890 } else if(cur_layer == LAYER_FOREGROUNDTILES) {
891 foregrounds->change(x/32, y/32, newtile);
892 } else if(cur_layer == LAYER_TILES) {
893 // remove a bad guy if it's there
894 // we /32 in order to round numbers
895 for(Sector::GameObjects::iterator i = sector->gameobjects.begin();
896 i < sector->gameobjects.end(); i++) {
898 BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
900 if((int)badguy->base.x/32 == x/32 && (int)badguy->base.y/32 == y/32)
901 sector->gameobjects.erase(i);
904 Trampoline* trampoline = dynamic_cast<Trampoline*> (*i);
907 if((int)trampoline->base.x/32 == x/32 && (int)trampoline->base.y/32 == y/32)
908 sector->gameobjects.erase(i);
910 FlyingPlatform* flying_platform = dynamic_cast<FlyingPlatform*> (*i);
912 if((int)flying_platform->base.x/32 == x/32 && (int)flying_platform->base.y/32 == y/32)
913 sector->gameobjects.erase(i);
916 Door* door = dynamic_cast<Door*> (*i);
918 if((int)door->get_area().x/32 == x/32 && (int)door->get_area().y/32 == y/32)
919 sector->gameobjects.erase(i);
922 sector->update_game_objects();
923 solids->change(x/32, y/32, newtile);
924 } else if(cur_layer == LAYER_BACKGROUNDTILES)
925 backgrounds->change(x/32, y/32, newtile);
928 void LevelEditor::show_help()
930 DrawingContext context;
932 bool show_grid_t = show_grid;
934 mouse_cursor->set_state(MC_HIDE);
938 const char *text1[] = {
939 _("This is the built-in level editor. Its aim is to be intuitive\n"
940 "and simple to use, so it should be pretty straightforward.\n"
942 "To open a level, first you'll have to select a level subset from\n"
943 "the menu (or create your own).\n"
944 "A level subset is basically a collection of levels.\n"
945 "They can then be played from the Contrib menu.\n"
947 "To access the menu from the level editor, just press Esc.\n"
949 "You are currently looking at the level. To scroll it, just\n"
950 "press the right mouse button and drag the mouse. It will move like\n"
952 "You can also use the arrow keys and Page Up/Down.\n"
954 "'+' and '-' keys can be used to zoom the level in/out.\n"
956 "You probably already noticed those floating groups of buttons.\n"
957 "Each one serves a different purpose. To select a certain button\n"
958 "just press the Left mouse button on it. A few buttons have key\n"
959 "shortcuts. You can find them by pressing the Right mouse button on\n"
960 "a button. That will also show what that button does.\n"
961 "Groups of buttons can also be moved around by just dragging them,\n"
962 "while pressing the Left mouse button.\n"
964 "Let's learn a bit of what each group of buttons does, shall we?\n"
966 "To starting putting tiles and objects around use the bigger group\n"
967 "of buttons. Each button is a different tile. To put it on the level,\n"
968 "just press it and then left click in the level.\n"
969 "You can also copy tiles from the level by using the middle mouse button.\n"
970 "Use the mouse wheel to scroll that group of buttons. You will find\n"
971 "enemies and game objects in the bottom.\n")
974 const char *text2[] = {
975 _("The Foreground/Interactive/Background buttons may be used to\n"
976 "see and edit the respective layer. Levels have three tiles layers:\n"
977 "Foreground - tiles are drawn on top of everything and have no contact\n"
979 "Interactive - these are the tiles that have contact with the player.\n"
980 "Background - tiles are drawn underneath everything and have no contact\n"
982 "The unselected layers will be drawn semi-transparently.\n"
984 "Last, but not least, the group of buttons that's left serves\n"
985 "to do related actions with the level.\n"
986 "From left to right:\n"
987 "Mini arrows - can be used to choose other sectors.\n"
988 "Sectors are mini-levels, so to speak, that can be accessed using a door.\n"
989 "Big arrows - choose other level in the same level subset.\n"
990 "Diskette - save the level\n"
991 "Tux - test the level\n"
992 "Tools - set a few settings for the level, including resizing it.\n"
994 "We have reached the end of this Howto.\n"
996 "Don't forget to send us a few cool levels. :)\n"
999 " SuperTux development team\n"
1001 "PS: If you are looking for something more powerful, you might like to\n"
1002 "try FlexLay. FlexLay is a level editor that supports several games,\n"
1003 "including SuperTux. It is an independent project.\n"
1004 "Webpage: http://pingus.seul.org/~grumbel/flexlay/")
1007 const char **text[] = { text1, text2 };
1011 for(unsigned int i = 0; i < sizeof(text) / sizeof(text[0]); i++)
1015 context.draw_text(blue_text, _("- Level Editor's Help -"), Vector(screen->w/2, 60), CENTER_ALLIGN, LAYER_GUI);
1017 context.draw_text(white_small_text, *text[i], Vector(20, 120), LEFT_ALLIGN, LAYER_GUI);
1019 sprintf(str,_("Press any key to continue - Page %d/%d"), i+1, sizeof(text) / sizeof(text[0]));
1020 context.draw_text(gold_text, str, Vector(screen->w/2, screen->h-60), CENTER_ALLIGN, LAYER_GUI);
1022 context.do_drawing();
1028 done = wait_for_event(event);
1033 show_grid = show_grid_t;
1034 mouse_cursor->set_state(MC_NORMAL);
1038 LevelEditor::create_sector(const std::string& name, size_t width, size_t height)
1040 Sector* sector = new Sector;
1041 sector->set_name(name);
1043 sector->add_object(new TileMap(LAYER_BACKGROUNDTILES, false, width, height));
1044 sector->add_object(new TileMap(LAYER_TILES, true, width, height));
1045 sector->add_object(new TileMap(LAYER_FOREGROUNDTILES, false, width, height));
1046 sector->add_object(new Camera(sector));
1047 sector->update_game_objects();