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 tiles_board->add_button(Button(img_rubber_bt, _("Eraser"), SDLKey(SDLK_DELETE)), 0);
105 for(unsigned int id = 1; id < tile_manager->get_max_tileid(); id++)
107 const Tile* tile = tile_manager->get(id);
111 Surface* surface = tile->get_editor_image();
115 Button button = Button(surface, "", SDLKey(0));
116 tiles_board->add_button(button, id);
120 for(int i = 0; i < NUM_BadGuyKinds; i++)
122 // filter bomb, since it is only for internal use, not for levels
126 BadGuyKind kind = BadGuyKind(i);
127 BadGuy badguy(kind, 0,0);
128 badguy.activate(LEFT);
130 Surface *img = badguy.get_image();
131 tiles_board->add_button(Button(img, "", SDLKey(SDLK_1+i)), -(i+1));
136 tiles_board->add_button(Button(img_trampoline[0].get_frame(0), _("Trampoline"), SDLKey(0)), OBJ_TRAMPOLINE);
137 tiles_board->add_button(Button(img_flying_platform->get_frame(0), _("Flying Platform"), SDLKey(0)), OBJ_FLYING_PLATFORM);
140 tiles_layer = new ButtonGroup(Vector(12, screen->h-64), Vector(80,20), Vector(1,3));
141 tiles_layer->add_button(Button(img_foreground_bt, _("Edtit foreground tiles"),
142 SDLK_F10), LAYER_FOREGROUNDTILES);
143 tiles_layer->add_button(Button(img_interactive_bt, _("Edit interactive tiles"),
144 SDLK_F11), LAYER_TILES, true);
145 tiles_layer->add_button(Button(img_background_bt, _("Edit background tiles"),
146 SDLK_F12), LAYER_BACKGROUNDTILES);
148 level_options = new ButtonGroup(Vector(screen->w-164, screen->h-36), Vector(32,32), Vector(5,1));
149 level_options->add_pair_of_buttons(Button(img_next_sector_bt, _("Next sector"), SDLKey(0)), BT_NEXT_SECTOR,
150 Button(img_previous_sector_bt, _("Prevous sector"), SDLKey(0)), BT_PREVIOUS_SECTOR);
151 level_options->add_pair_of_buttons(Button(img_next_level_bt, _("Next level"), SDLKey(0)), BT_NEXT_LEVEL,
152 Button(img_previous_level_bt, _("Prevous level"), SDLKey(0)), BT_PREVIOUS_LEVEL);
153 level_options->add_button(Button(img_save_level_bt, _("Save level"), SDLK_F5), BT_LEVEL_SAVE);
154 level_options->add_button(Button(img_test_level_bt, _("Test level"), SDLK_F6), BT_LEVEL_TEST);
155 level_options->add_button(Button(img_setup_level_bt, _("Setup level"), SDLK_F7), BT_LEVEL_SETUP);
158 LevelEditor::~LevelEditor()
164 delete level_options;
167 delete create_subset_menu;
169 delete settings_menu;
175 void LevelEditor::load_buttons_gfx()
177 img_foreground_bt = new Surface(datadir + "/images/leveleditor/foreground.png", true);
178 img_interactive_bt = new Surface(datadir + "/images/leveleditor/interactive.png", true);
179 img_background_bt = new Surface(datadir + "/images/leveleditor/background.png", true);
181 img_save_level_bt = new Surface(datadir + "/images/leveleditor/save-level.png", true);
182 img_test_level_bt = new Surface(datadir + "/images/leveleditor/test-level.png", true);
183 img_setup_level_bt = new Surface(datadir + "/images/leveleditor/setup-level.png", true);
185 img_rubber_bt = new Surface(datadir + "/images/leveleditor/rubber.png", true);
187 img_previous_level_bt = new Surface(datadir + "/images/leveleditor/previous-level.png", true);
188 img_next_level_bt = new Surface(datadir + "/images/leveleditor/next-level.png", true);
189 img_previous_sector_bt = new Surface(datadir + "/images/leveleditor/previous-sector.png", true);
190 img_next_sector_bt = new Surface(datadir + "/images/leveleditor/next-sector.png", true);
193 void LevelEditor::free_buttons_gfx()
195 delete img_foreground_bt;
196 delete img_interactive_bt;
197 delete img_background_bt;
199 delete img_save_level_bt;
200 delete img_test_level_bt;
201 delete img_setup_level_bt;
203 delete img_rubber_bt;
205 delete img_previous_level_bt;
206 delete img_next_level_bt;
207 delete img_previous_sector_bt;
208 delete img_next_sector_bt;
211 void LevelEditor::run(const std::string filename)
213 SoundManager::get()->halt_music();
214 Menu::set_current(0);
216 DrawingContext context;
218 if(!filename.empty())
221 load_level(filename);
224 Menu::set_current(main_menu);
226 mouse_cursor->set_state(MC_NORMAL);
237 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
241 void LevelEditor::events()
245 while(SDL_PollEvent(&event))
247 Menu* menu = Menu::current();
252 if(menu == main_menu)
254 switch (main_menu->check())
257 Menu::set_current(0);
264 else if(menu == create_subset_menu)
266 // activate or deactivate Create button if any filename as been specified
267 if(create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input[0] == '\0')
268 create_subset_menu->get_item_by_id(MN_ID_CREATE_SUBSET).kind = MN_DEACTIVE;
270 create_subset_menu->get_item_by_id(MN_ID_CREATE_SUBSET).kind = MN_ACTION;
272 if(create_subset_menu->check() == MN_ID_CREATE_SUBSET)
273 { // applying settings:
274 std::string subset_name = create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input;
275 LevelSubset::create(subset_name);
278 level_subset = new LevelSubset();
279 level_subset->load(create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input);
281 level_subset->title = create_subset_menu->get_item_by_id(MN_ID_TITLE_SUBSET).input;
282 level_subset->description = create_subset_menu->get_item_by_id(MN_ID_DESCRIPTION_SUBSET).input;
283 //FIXME: generate better level filenames
284 level_subset->add_level(subset_name+'/'+"new_level.stl");
285 Level* newlevel = new Level();
286 newlevel->add_sector(create_sector("main", 25, 19));
287 newlevel->save(level_subset->get_level_filename(0));
288 level_subset->save();
292 create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).change_input("");
293 create_subset_menu->get_item_by_id(MN_ID_TITLE_SUBSET).change_input("");
294 create_subset_menu->get_item_by_id(MN_ID_DESCRIPTION_SUBSET).change_input("");
297 else if(menu == subset_menu)
299 int i = subset_menu->check();
302 std::set<std::string>::iterator it = level_subsets.begin();
303 for(int t = 0; t < i; t++)
305 load_level_subset(*it);
306 Menu::set_current(0);
309 else if(menu == settings_menu)
311 if(settings_menu->check() == MN_ID_APPLY_SETTINGS)
312 { // applying settings:
313 level_changed = true;
315 level->name = settings_menu->get_item_by_id(MN_ID_NAME).input;
316 level->author = settings_menu->get_item_by_id(MN_ID_AUTHOR).input;
318 solids->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()),
319 atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str()));
320 foregrounds->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 backgrounds->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()));
325 Menu::set_current(0);
329 // check for events in buttons
330 else if(tiles_board->event(event))
332 std::vector <int> vector;
333 vector.push_back(tiles_board->selected_id());
336 selection.push_back(vector);
339 else if(tiles_layer->event(event))
341 cur_layer = tiles_layer->selected_id();
344 else if(level_options->event(event))
346 switch(level_options->selected_id())
355 Menu::set_current(settings_menu);
358 if(level_nb + 1 < level_subset->get_num_levels())
359 load_level(level_nb + 1);
363 sprintf(str,_("Level %d doesn't exist. Create it?"), level_nb + 2);
364 if(confirm_dialog(NULL, str))
366 level_subset->add_level("new_level.stl");
367 Level* newlevel = new Level();
368 newlevel->add_sector(create_sector("main", 25, 19));
369 newlevel->save(level_subset->get_level_filename(level_nb + 1));
370 level_subset->save();
371 load_level(level_nb + 1);
375 case BT_PREVIOUS_LEVEL:
376 if(level_nb - 1 >= 0)
377 load_level(level_nb - 1);
380 std::cerr << "next sector.\n";
381 std::cerr << "total sectors: " << level->get_total_sectors() << std::endl;
382 load_sector(level->get_next_sector(sector));
384 case BT_PREVIOUS_SECTOR:
385 std::cerr << "previous sector.\n";
386 load_sector(level->get_previous_sector(sector));
389 level_options->set_unselected();
396 case SDL_MOUSEMOTION:
398 if(SDL_GetMouseState(NULL, NULL)&SDL_BUTTON(SDL_BUTTON_RIGHT))
399 { // movement like in strategy games
400 scroll.x += -1 * event.motion.xrel;
401 scroll.y += -1 * event.motion.yrel;
405 case SDL_MOUSEBUTTONDOWN:
407 if(event.button.button == SDL_BUTTON_LEFT)
409 else if(event.button.button == SDL_BUTTON_MIDDLE)
411 middle_button = true;
412 selection_ini = Vector(event.button.x, event.button.y);
416 case SDL_MOUSEBUTTONUP:
418 if(event.button.button == SDL_BUTTON_LEFT)
420 else if(event.button.button == SDL_BUTTON_MIDDLE)
422 middle_button = false;
423 selection_end = Vector(event.button.x, event.button.y);
425 if(selection_end.x < selection_ini.x)
427 float t = selection_ini.x;
428 selection_ini.x = selection_end.x;
431 if(selection_end.y < selection_ini.y)
433 float t = selection_ini.y;
434 selection_ini.y = selection_end.y;
439 std::vector <int> vector;
441 TileMap* tilemap = 0;
442 if(cur_layer == LAYER_FOREGROUNDTILES)
443 tilemap = foregrounds;
444 else if(cur_layer == LAYER_TILES)
446 else if(cur_layer == LAYER_BACKGROUNDTILES)
447 tilemap = backgrounds;
449 for(int x = 0; x < (int)((selection_end.x - selection_ini.x)*zoom / 32) + 1; x++)
452 for(int y = 0; y < (int)((selection_end.y - selection_ini.y)*zoom / 32) + 1; y++)
454 vector.push_back(tilemap->get_tile(x +
455 (int)(((selection_ini.x+scroll.x)*zoom)/32),
456 y + (int)(((selection_ini.y+scroll.y)*zoom)/32))->getID());
458 selection.push_back(vector);
463 case SDL_KEYDOWN: // key pressed
464 switch(event.key.keysym.sym)
467 Menu::set_current(main_menu);
469 /* scrolling related events: */
474 scroll.x = sector->solids->get_height()*32 - screen->w;
507 show_grid = !show_grid;
514 case SDL_QUIT: // window closed
525 void LevelEditor::action()
527 mouse_cursor->set_state(MC_NORMAL);
528 if(tiles_board->is_hover() || tiles_layer->is_hover() || level_options->is_hover())
529 mouse_cursor->set_state(MC_LINK);
533 // don't scroll before the start or after the level's end
534 float width = sector->solids->get_width() * 32;
535 float height = sector->solids->get_height() * 32;
537 if(scroll.x < -screen->w/2)
538 scroll.x = -screen->w/2;
539 if(scroll.x > width - screen->w/2)
540 scroll.x = width - screen->w/2;
541 if(scroll.y < -screen->h/2)
542 scroll.y = -screen->h/2;
543 if(scroll.y > height - screen->h/2)
544 scroll.y = height - screen->h/2;
546 // set camera translation, since BadGuys like it
547 sector->camera->set_scrolling((int)scroll.x, (int)scroll.y);
549 if(left_button && mouse_moved)
550 for(unsigned int x = 0; x < selection.size(); x++)
551 for(unsigned int y = 0; y < selection[x].size(); y++)
552 change((int)(scroll.x + event.button.x) + (x*32),
553 (int)(scroll.y + event.button.y) + (y*32), selection[x][y],
558 #define FADING_TIME .6
560 void LevelEditor::draw(DrawingContext& context)
562 context.draw_text(white_text, _("Level Editor"), Vector(10, 5), LEFT_ALLIGN, LAYER_GUI);
563 mouse_cursor->draw(context);
565 // draw a filled background
566 context.draw_filled_rect(Vector(0,0), Vector(screen->w,screen->h), Color(60,60,60), LAYER_BACKGROUND0-1);
568 if(level_name_timer.check())
570 context.push_transform();
571 if(level_name_timer.get_timeleft() < FADING_TIME)
572 context.set_alpha(int(level_name_timer.get_timeleft() * 255 / FADING_TIME));
574 context.draw_text(gold_text, level->name, Vector(screen->w/2, 30), CENTER_ALLIGN, LAYER_GUI);
578 sprintf(str, "%i/%i", level_nb+1, level_subset->get_num_levels());
579 context.draw_text(gold_text, str, Vector(screen->w/2, 50), CENTER_ALLIGN, LAYER_GUI);
582 context.pop_transform();
585 context.draw_text(white_small_text, _("F1 for help"), Vector(5, 510), LEFT_ALLIGN, LAYER_GUI-10);
587 context.draw_text(white_small_text, _("Choose a level subset"), Vector(5, 510), LEFT_ALLIGN, LAYER_GUI-10);
589 Menu* menu = Menu::current();
594 tiles_board->draw(context);
595 tiles_layer->draw(context);
596 level_options->draw(context);
604 context.set_drawing_effect(SEMI_TRANSPARENT);
608 if(selection[0][0] == 0 && selection.size() == 1)
609 context.draw_surface(img_rubber_bt, Vector(event.button.x - 8,
610 event.button.y - 8), LAYER_GUI-2);
611 else if(selection[0][0] < 0)
613 int id = selection[0][0];
616 if(id == OBJ_TRAMPOLINE)
617 context.draw_surface(img_trampoline[0].get_frame(0), Vector(event.button.x - 8,
618 event.button.y - 8), LAYER_GUI-2);
619 else if(id == OBJ_FLYING_PLATFORM)
620 context.draw_surface(img_flying_platform->get_frame(0), Vector(event.button.x - 8,
621 event.button.y - 8), LAYER_GUI-2);
625 /*context.draw_surface(door->get_frame(0), Vector(event.button.x - 8,
626 event.button.y - 8), LAYER_GUI-2);*/
631 BadGuyKind kind = BadGuyKind((-id)-1);
632 BadGuy badguy(kind, 0,0);
633 badguy.activate(LEFT);
634 Surface *img = badguy.get_image();
636 context.draw_surface(img, Vector(event.button.x - 8,
637 event.button.y - 8), LAYER_GUI-2);
643 for(unsigned int x = 0; x < selection.size(); x++)
644 for(unsigned int y = 0; y < selection[x].size(); y++) {
645 const Tile* tile = tile_manager->get(selection[x][y]);
647 Vector(event.button.x + x*32 - 8, event.button.y + y*32 - 8),
652 context.set_drawing_effect(NONE_EFFECT);
655 context.draw_filled_rect(Vector(std::min((int)selection_ini.x, (int)event.button.x)*zoom,
656 std::min((int)selection_ini.y, (int)event.button.y))*zoom,
657 Vector(abs(event.button.x - (int)selection_ini.x)*zoom,
658 abs(event.button.y - (int)selection_ini.y)*zoom),
659 Color(170,255,170,128), LAYER_GUI-2);
663 for(int x = 0; x < screen->w / (32*zoom); x++)
665 int pos = (int)(x*32*zoom) - ((int)scroll.x % 32);
666 context.draw_filled_rect(Vector (pos, 0), Vector(1, screen->h),
667 Color(225, 225, 225), LAYER_GUI-50);
669 for(int y = 0; y < screen->h / (32*zoom); y++)
671 int pos = (int)(y*32*zoom) - ((int)scroll.y % 32);
672 context.draw_filled_rect(Vector (0, pos), Vector(screen->w, 1),
673 Color(225, 225, 225), LAYER_GUI-50);
677 context.push_transform();
678 context.set_translation(scroll);
679 context.set_zooming(zoom);
681 for(Sector::GameObjects::iterator i = sector->gameobjects.begin(); i != sector->gameobjects.end(); ++i)
683 TileMap* tilemap = dynamic_cast<TileMap*> (*i);
685 { // draw the non-selected tiles semi-transparently
686 context.push_transform();
688 if(tilemap->get_layer() != cur_layer)
689 context.set_drawing_effect(SEMI_TRANSPARENT);
692 context.pop_transform();
695 Background* background = dynamic_cast<Background*> (*i);
697 { // don't resize background
698 context.push_transform();
699 context.set_zooming(1.0);
701 context.pop_transform();
707 context.pop_transform();
710 context.draw_filled_rect(Vector(0,0), Vector(screen->w,screen->h),Color(0,0,0), LAYER_BACKGROUND0);
712 context.do_drawing();
715 void LevelEditor::load_level_subset(std::string filename)
718 level_subset = new LevelSubset();
719 level_subset->load(filename.c_str());
723 void LevelEditor::load_level(std::string filename)
727 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
733 level_filename = filename;
737 level->load(filename);
740 level_name_timer.start(3000);
741 scroll.x = scroll.y = 0;
742 level_changed = false;
744 settings_menu->get_item_by_id(MN_ID_NAME).change_input(level->name.c_str());
745 settings_menu->get_item_by_id(MN_ID_AUTHOR).change_input(level->author.c_str());
748 void LevelEditor::load_level(int nb)
752 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
759 level_filename = level_subset->get_level_filename(level_nb);
761 load_level(level_filename);
764 void LevelEditor::load_sector(std::string name)
767 sector = level->get_sector(sector_name);
769 Termination::abort("Level has no " + sector_name + " sector.", "");
774 void LevelEditor::load_sector(Sector* sector_)
778 if(!confirm_dialog(NULL, _("No more sectors exist. Create another?")))
780 sector_ = create_sector("new_sector",25,19);
781 level->add_sector(sector_);
786 /* Load sector stuff */
788 sector->update_game_objects();
790 foregrounds = solids = backgrounds = 0;
791 /* Point foregrounds, backgrounds, solids to its layer */
792 for(Sector::GameObjects::iterator i = sector->gameobjects.begin(); i != sector->gameobjects.end(); i++)
795 BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
797 badguy->activate(LEFT);
800 TileMap* tilemap = dynamic_cast<TileMap*> (*i);
803 if(tilemap->get_layer() == LAYER_FOREGROUNDTILES)
804 foregrounds = tilemap;
805 else if(tilemap->get_layer() == LAYER_TILES)
807 else if(tilemap->get_layer() == LAYER_BACKGROUNDTILES)
808 backgrounds = tilemap;
814 TileMap* tilemap = new TileMap(LAYER_FOREGROUNDTILES, false, solids->get_width(), solids->get_height());
815 sector->add_object(tilemap);
816 sector->update_game_objects();
820 TileMap* tilemap = new TileMap(LAYER_BACKGROUNDTILES, false, solids->get_width(), solids->get_height());
821 sector->add_object(tilemap);
822 sector->update_game_objects();
826 sprintf(str, "%i", solids->get_width());
827 settings_menu->get_item_by_id(MN_ID_WIDTH).change_input(str);
828 sprintf(str, "%i", solids->get_height());
829 settings_menu->get_item_by_id(MN_ID_HEIGHT).change_input(str);
832 void LevelEditor::save_level()
834 level->save(level_filename);
835 level_changed = false;
838 void LevelEditor::test_level()
841 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
847 GameSession session(level_filename, ST_GL_TEST);
849 // player_status.reset();
850 SoundManager::get()->halt_music();
853 void LevelEditor::change(int x, int y, int newtile, int layer)
856 // find the tilemap of the current layer, and then change the tile
857 if(x < 0 || (unsigned int)x >= sector->solids->get_width()*32 ||
858 y < 0 || (unsigned int)y >= sector->solids->get_height()*32)
861 level_changed = true;
864 { // no need to do this for normal view (no zoom)
865 x = (int)(x * (zoom*32) / 32);
866 y = (int)(y * (zoom*32) / 32);
869 if(newtile < 0) // add object
871 // remove an active tile or object that might be there
872 change(x, y, 0, LAYER_TILES);
875 if(newtile == OBJ_TRAMPOLINE)
876 sector->add_object(new Trampoline(x, y));
877 else if(newtile == OBJ_FLYING_PLATFORM)
878 sector->add_object(new FlyingPlatform(x, y));
880 if(newtile == OBJ_DOOR)
881 sector->add_object(new Door(x, y));
883 sector->add_bad_guy(x, y, BadGuyKind((-newtile)-1), true);
886 sector->update_game_objects();
887 } else if(cur_layer == LAYER_FOREGROUNDTILES) {
888 foregrounds->change(x/32, y/32, newtile);
889 } else if(cur_layer == LAYER_TILES) {
890 // remove a bad guy if it's there
891 // we /32 in order to round numbers
892 for(Sector::GameObjects::iterator i = sector->gameobjects.begin();
893 i < sector->gameobjects.end(); i++) {
895 BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
897 if((int)badguy->base.x/32 == x/32 && (int)badguy->base.y/32 == y/32)
898 sector->gameobjects.erase(i);
901 Trampoline* trampoline = dynamic_cast<Trampoline*> (*i);
904 if((int)trampoline->base.x/32 == x/32 && (int)trampoline->base.y/32 == y/32)
905 sector->gameobjects.erase(i);
907 FlyingPlatform* flying_platform = dynamic_cast<FlyingPlatform*> (*i);
909 if((int)flying_platform->base.x/32 == x/32 && (int)flying_platform->base.y/32 == y/32)
910 sector->gameobjects.erase(i);
913 Door* door = dynamic_cast<Door*> (*i);
915 if((int)door->get_area().x/32 == x/32 && (int)door->get_area().y/32 == y/32)
916 sector->gameobjects.erase(i);
919 sector->update_game_objects();
920 solids->change(x/32, y/32, newtile);
921 } else if(cur_layer == LAYER_BACKGROUNDTILES)
922 backgrounds->change(x/32, y/32, newtile);
925 void LevelEditor::show_help()
927 DrawingContext context;
929 bool show_grid_t = show_grid;
931 mouse_cursor->set_state(MC_HIDE);
935 const char *text1[] = {
936 _("This is the built-in level editor. Its aim is to be intuitive\n"
937 "and simple to use, so it should be pretty straightforward.\n"
939 "To open a level, first you'll have to select a level subset from\n"
940 "the menu (or create your own).\n"
941 "A level subset is basically a collection of levels.\n"
942 "They can then be played from the Contrib menu.\n"
944 "To access the menu from the level editor, just press Esc.\n"
946 "You are currently looking at the level. To scroll it, just\n"
947 "press the right mouse button and drag the mouse. It will move like\n"
949 "You can also use the arrow keys and Page Up/Down.\n"
951 "'+' and '-' keys can be used to zoom the level in/out.\n"
953 "You probably already noticed those floating groups of buttons.\n"
954 "Each one serves a different purpose. To select a certain button\n"
955 "just press the Left mouse button on it. A few buttons have key\n"
956 "shortcuts. You can find them by pressing the Right mouse button on\n"
957 "a button. That will also show what that button does.\n"
958 "Groups of buttons can also be moved around by just dragging them,\n"
959 "while pressing the Left mouse button.\n"
961 "Let's learn a bit of what each group of buttons does, shall we?\n"
963 "To starting putting tiles and objects around use the bigger group\n"
964 "of buttons. Each button is a different tile. To put it on the level,\n"
965 "just press it and then left click in the level.\n"
966 "You can also copy tiles from the level by using the middle mouse button.\n"
967 "Use the mouse wheel to scroll that group of buttons. You will find\n"
968 "enemies and game objects in the bottom.\n")
971 const char *text2[] = {
972 _("The Foreground/Interactive/Background buttons may be used to\n"
973 "see and edit the respective layer. Levels have three tiles layers:\n"
974 "Foreground - tiles are drawn on top of everything and have no contact\n"
976 "Interactive - these are the tiles that have contact with the player.\n"
977 "Background - tiles are drawn underneath everything and have no contact\n"
979 "The unselected layers will be drawn semi-transparently.\n"
981 "Last, but not least, the group of buttons that's left serves\n"
982 "to do related actions with the level.\n"
983 "From left to right:\n"
984 "Mini arrows - can be used to choose other sectors.\n"
985 "Sectors are mini-levels, so to speak, that can be accessed using a door.\n"
986 "Big arrows - choose other level in the same level subset.\n"
987 "Diskette - save the level\n"
988 "Tux - test the level\n"
989 "Tools - set a few settings for the level, including resizing it.\n"
991 "We have reached the end of this Howto.\n"
993 "Don't forget to send us a few cool levels. :)\n"
996 " SuperTux development team\n"
998 "PS: If you are looking for something more powerful, you might like to\n"
999 "try FlexLay. FlexLay is a level editor that supports several games,\n"
1000 "including SuperTux. It is an independent project.\n"
1001 "Webpage: http://pingus.seul.org/~grumbel/flexlay/")
1004 const char **text[] = { text1, text2 };
1008 for(unsigned int i = 0; i < sizeof(text) / sizeof(text[0]); i++)
1012 context.draw_text(blue_text, _("- Level Editor's Help -"), Vector(screen->w/2, 60), CENTER_ALLIGN, LAYER_GUI);
1014 context.draw_text(white_small_text, *text[i], Vector(20, 120), LEFT_ALLIGN, LAYER_GUI);
1016 sprintf(str,_("Press any key to continue - Page %d/%d"), i+1, sizeof(text) / sizeof(text[0]));
1017 context.draw_text(gold_text, str, Vector(screen->w/2, screen->h-60), CENTER_ALLIGN, LAYER_GUI);
1019 context.do_drawing();
1025 done = wait_for_event(event);
1030 show_grid = show_grid_t;
1031 mouse_cursor->set_state(MC_NORMAL);
1035 LevelEditor::create_sector(const std::string& name, size_t width, size_t height)
1037 Sector* sector = new Sector;
1038 sector->set_name(name);
1040 sector->add_object(new TileMap(LAYER_BACKGROUNDTILES, false, width, height));
1041 sector->add_object(new TileMap(LAYER_TILES, true, width, height));
1042 sector->add_object(new TileMap(LAYER_FOREGROUNDTILES, false, width, height));
1043 sector->add_object(new Camera(sector));
1044 sector->update_game_objects();