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"
35 #include "tile_manager.h"
37 #include "background.h"
43 LevelEditor::LevelEditor()
48 global_frame_counter = 0;
49 selection_end = selection_ini = Vector(0,0);
50 left_button = middle_button = mouse_moved = false;
54 cur_layer = LAYER_TILES;
55 level_changed = false;
61 level_subsets = FileSystem::dsubdirs("/levels", "info");
62 subset_menu = new Menu();
63 subset_menu->additem(MN_LABEL,_("Load Subset"),0,0);
64 subset_menu->additem(MN_HL,"",0,0);
66 for(std::set<std::string>::iterator it = level_subsets.begin(); it != level_subsets.end(); ++it, ++i)
67 subset_menu->additem(MN_ACTION, (*it),0,0,i);
68 subset_menu->additem(MN_HL,"",0,0);
69 subset_menu->additem(MN_BACK,_("Back"),0,0);
71 create_subset_menu = new Menu();
72 create_subset_menu->additem(MN_LABEL,_("New Level Subset"),0,0);
73 create_subset_menu->additem(MN_HL,"",0,0);
74 create_subset_menu->additem(MN_TEXTFIELD,_("Filename "),0,0,MN_ID_FILENAME_SUBSET);
75 create_subset_menu->additem(MN_TEXTFIELD,_("Title "),0,0,MN_ID_TITLE_SUBSET);
76 create_subset_menu->additem(MN_TEXTFIELD,_("Description"),0,0,MN_ID_DESCRIPTION_SUBSET);
77 create_subset_menu->additem(MN_ACTION,_("Create"),0,0, MN_ID_CREATE_SUBSET);
78 create_subset_menu->additem(MN_HL,"",0,0);
79 create_subset_menu->additem(MN_BACK,_("Back"),0,0);
81 main_menu = new Menu();
82 main_menu->additem(MN_LABEL,_("Level Editor Menu"),0,0);
83 main_menu->additem(MN_HL,"",0,0);
84 main_menu->additem(MN_ACTION,_("Return to Level Editor"),0,0,MN_ID_RETURN);
85 main_menu->additem(MN_GOTO,_("Create Level Subset"),0,create_subset_menu);
86 main_menu->additem(MN_GOTO,_("Load Level Subset"),0,subset_menu);
87 main_menu->additem(MN_HL,"",0,0);
88 main_menu->additem(MN_ACTION,_("Quit Level Editor"),0,0,MN_ID_QUIT);
90 settings_menu = new Menu();
91 settings_menu->additem(MN_LABEL,_("Level Settings"),0,0);
92 settings_menu->additem(MN_HL,"",0,0);
93 settings_menu->additem(MN_TEXTFIELD,_("Name "),0,0,MN_ID_NAME);
94 settings_menu->additem(MN_TEXTFIELD,_("Author "),0,0,MN_ID_AUTHOR);
95 settings_menu->additem(MN_NUMFIELD, _("Width "),0,0,MN_ID_WIDTH);
96 settings_menu->additem(MN_NUMFIELD, _("Height "),0,0,MN_ID_HEIGHT);
97 settings_menu->additem(MN_HL,"",0,0);
98 settings_menu->additem(MN_ACTION,_("Apply"),0,0,MN_ID_APPLY_SETTINGS);
100 /* Creating button groups */
103 tiles_board = new ButtonGroup(Vector(screen->w - 140, 100),
104 Vector(32,32), Vector(4,8));
106 TileManager* tilemanager = TileManager::instance();
108 tiles_board->add_button(Button(img_rubber_bt, _("Eraser"), SDLKey(SDLK_DELETE)), 0);
109 for(unsigned int id = 1; id < tilemanager->get_max_tileid(); id++)
111 const Tile* tile = tilemanager->get(id);
116 if(tile->editor_images.size())
117 surface = tile->editor_images[0];
118 else if(tile->images.size())
119 surface = tile->images[0];
123 Button button = Button(surface, "", SDLKey(0));
124 tiles_board->add_button(button, id);
128 for(int i = 0; i < NUM_BadGuyKinds; i++)
130 // filter bomb, since it is only for internal use, not for levels
134 BadGuyKind kind = BadGuyKind(i);
135 BadGuy badguy(kind, 0,0);
136 badguy.activate(LEFT);
138 Surface *img = badguy.get_image();
139 tiles_board->add_button(Button(img, "", SDLKey(SDLK_1+i)), -(i+1));
144 tiles_board->add_button(Button(img_trampoline[0].get_frame(0), _("Trampoline"), SDLKey(0)), OBJ_TRAMPOLINE);
145 tiles_board->add_button(Button(img_flying_platform->get_frame(0), _("Flying Platform"), SDLKey(0)), OBJ_FLYING_PLATFORM);
148 tiles_layer = new ButtonGroup(Vector(12, screen->h-64), Vector(80,20), Vector(1,3));
149 tiles_layer->add_button(Button(img_foreground_bt, _("Edtit foreground tiles"),
150 SDLK_F10), LAYER_FOREGROUNDTILES);
151 tiles_layer->add_button(Button(img_interactive_bt, _("Edit interactive tiles"),
152 SDLK_F11), LAYER_TILES, true);
153 tiles_layer->add_button(Button(img_background_bt, _("Edit background tiles"),
154 SDLK_F12), LAYER_BACKGROUNDTILES);
156 level_options = new ButtonGroup(Vector(screen->w-164, screen->h-36), Vector(32,32), Vector(5,1));
157 level_options->add_pair_of_buttons(Button(img_next_sector_bt, _("Next sector"), SDLKey(0)), BT_NEXT_SECTOR,
158 Button(img_previous_sector_bt, _("Prevous sector"), SDLKey(0)), BT_PREVIOUS_SECTOR);
159 level_options->add_pair_of_buttons(Button(img_next_level_bt, _("Next level"), SDLKey(0)), BT_NEXT_LEVEL,
160 Button(img_previous_level_bt, _("Prevous level"), SDLKey(0)), BT_PREVIOUS_LEVEL);
161 level_options->add_button(Button(img_save_level_bt, _("Save level"), SDLK_F5), BT_LEVEL_SAVE);
162 level_options->add_button(Button(img_test_level_bt, _("Test level"), SDLK_F6), BT_LEVEL_TEST);
163 level_options->add_button(Button(img_setup_level_bt, _("Setup level"), SDLK_F7), BT_LEVEL_SETUP);
166 LevelEditor::~LevelEditor()
172 delete level_options;
175 delete create_subset_menu;
177 delete settings_menu;
183 void LevelEditor::load_buttons_gfx()
185 img_foreground_bt = new Surface(datadir + "/images/leveleditor/foreground.png", true);
186 img_interactive_bt = new Surface(datadir + "/images/leveleditor/interactive.png", true);
187 img_background_bt = new Surface(datadir + "/images/leveleditor/background.png", true);
189 img_save_level_bt = new Surface(datadir + "/images/leveleditor/save-level.png", true);
190 img_test_level_bt = new Surface(datadir + "/images/leveleditor/test-level.png", true);
191 img_setup_level_bt = new Surface(datadir + "/images/leveleditor/setup-level.png", true);
193 img_rubber_bt = new Surface(datadir + "/images/leveleditor/rubber.png", true);
195 img_previous_level_bt = new Surface(datadir + "/images/leveleditor/previous-level.png", true);
196 img_next_level_bt = new Surface(datadir + "/images/leveleditor/next-level.png", true);
197 img_previous_sector_bt = new Surface(datadir + "/images/leveleditor/previous-sector.png", true);
198 img_next_sector_bt = new Surface(datadir + "/images/leveleditor/next-sector.png", true);
201 void LevelEditor::free_buttons_gfx()
203 delete img_foreground_bt;
204 delete img_interactive_bt;
205 delete img_background_bt;
207 delete img_save_level_bt;
208 delete img_test_level_bt;
209 delete img_setup_level_bt;
211 delete img_rubber_bt;
213 delete img_previous_level_bt;
214 delete img_next_level_bt;
215 delete img_previous_sector_bt;
216 delete img_next_sector_bt;
219 void LevelEditor::run(const std::string filename)
221 SoundManager::get()->halt_music();
222 Menu::set_current(0);
224 DrawingContext context;
226 if(!filename.empty())
229 load_level(filename);
232 Menu::set_current(main_menu);
234 mouse_cursor->set_state(MC_NORMAL);
236 frame_timer.start(.25, true);
246 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
250 void LevelEditor::events()
254 while(SDL_PollEvent(&event))
256 Menu* menu = Menu::current();
261 if(menu == main_menu)
263 switch (main_menu->check())
266 Menu::set_current(0);
273 else if(menu == create_subset_menu)
275 // activate or deactivate Create button if any filename as been specified
276 if(create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input[0] == '\0')
277 create_subset_menu->get_item_by_id(MN_ID_CREATE_SUBSET).kind = MN_DEACTIVE;
279 create_subset_menu->get_item_by_id(MN_ID_CREATE_SUBSET).kind = MN_ACTION;
281 if(create_subset_menu->check() == MN_ID_CREATE_SUBSET)
282 { // applying settings:
283 std::string subset_name = create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input;
284 LevelSubset::create(subset_name);
287 level_subset = new LevelSubset();
288 level_subset->load(create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input);
290 level_subset->title = create_subset_menu->get_item_by_id(MN_ID_TITLE_SUBSET).input;
291 level_subset->description = create_subset_menu->get_item_by_id(MN_ID_DESCRIPTION_SUBSET).input;
292 //FIXME: generate better level filenames
293 level_subset->add_level(subset_name+'/'+"new_level.stl");
294 Level::create(level_subset->get_level_filename(0));
295 level_subset->save();
299 create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).change_input("");
300 create_subset_menu->get_item_by_id(MN_ID_TITLE_SUBSET).change_input("");
301 create_subset_menu->get_item_by_id(MN_ID_DESCRIPTION_SUBSET).change_input("");
304 else if(menu == subset_menu)
306 int i = subset_menu->check();
309 std::set<std::string>::iterator it = level_subsets.begin();
310 for(int t = 0; t < i; t++)
312 load_level_subset(*it);
313 Menu::set_current(0);
316 else if(menu == settings_menu)
318 if(settings_menu->check() == MN_ID_APPLY_SETTINGS)
319 { // applying settings:
320 level_changed = true;
322 level->name = settings_menu->get_item_by_id(MN_ID_NAME).input;
323 level->author = settings_menu->get_item_by_id(MN_ID_AUTHOR).input;
325 solids->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()),
326 atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str()));
327 foregrounds->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()),
328 atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str()));
329 backgrounds->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()),
330 atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str()));
332 Menu::set_current(0);
336 // check for events in buttons
337 else if(tiles_board->event(event))
339 std::vector <int> vector;
340 vector.push_back(tiles_board->selected_id());
343 selection.push_back(vector);
346 else if(tiles_layer->event(event))
348 cur_layer = tiles_layer->selected_id();
351 else if(level_options->event(event))
353 switch(level_options->selected_id())
362 Menu::set_current(settings_menu);
365 if(level_nb + 1 < level_subset->get_num_levels())
366 load_level(level_nb + 1);
370 sprintf(str,_("Level %d doesn't exist. Create it?"), level_nb + 2);
371 if(confirm_dialog(NULL, str))
373 level_subset->add_level("new_level.stl");
374 Level::create(level_subset->get_level_filename(level_nb + 1));
375 level_subset->save();
376 load_level(level_nb + 1);
380 case BT_PREVIOUS_LEVEL:
381 if(level_nb - 1 >= 0)
382 load_level(level_nb - 1);
385 std::cerr << "next sector.\n";
386 std::cerr << "total sectors: " << level->get_total_sectors() << std::endl;
387 load_sector(level->get_next_sector(sector));
389 case BT_PREVIOUS_SECTOR:
390 std::cerr << "previous sector.\n";
391 load_sector(level->get_previous_sector(sector));
394 level_options->set_unselected();
401 case SDL_MOUSEMOTION:
403 if(SDL_GetMouseState(NULL, NULL)&SDL_BUTTON(SDL_BUTTON_RIGHT))
404 { // movement like in strategy games
405 scroll.x += -1 * event.motion.xrel;
406 scroll.y += -1 * event.motion.yrel;
410 case SDL_MOUSEBUTTONDOWN:
412 if(event.button.button == SDL_BUTTON_LEFT)
414 else if(event.button.button == SDL_BUTTON_MIDDLE)
416 middle_button = true;
417 selection_ini = Vector(event.button.x, event.button.y);
421 case SDL_MOUSEBUTTONUP:
423 if(event.button.button == SDL_BUTTON_LEFT)
425 else if(event.button.button == SDL_BUTTON_MIDDLE)
427 middle_button = false;
428 selection_end = Vector(event.button.x, event.button.y);
430 if(selection_end.x < selection_ini.x)
432 float t = selection_ini.x;
433 selection_ini.x = selection_end.x;
436 if(selection_end.y < selection_ini.y)
438 float t = selection_ini.y;
439 selection_ini.y = selection_end.y;
444 std::vector <int> vector;
446 TileMap* tilemap = 0;
447 if(cur_layer == LAYER_FOREGROUNDTILES)
448 tilemap = foregrounds;
449 else if(cur_layer == LAYER_TILES)
451 else if(cur_layer == LAYER_BACKGROUNDTILES)
452 tilemap = backgrounds;
454 for(int x = 0; x < (int)((selection_end.x - selection_ini.x)*zoom / 32) + 1; x++)
457 for(int y = 0; y < (int)((selection_end.y - selection_ini.y)*zoom / 32) + 1; y++)
459 vector.push_back(tilemap->get_tile(x +
460 (int)(((selection_ini.x+scroll.x)*zoom)/32),
461 y + (int)(((selection_ini.y+scroll.y)*zoom)/32))->id);
463 selection.push_back(vector);
468 case SDL_KEYDOWN: // key pressed
469 switch(event.key.keysym.sym)
472 Menu::set_current(main_menu);
474 /* scrolling related events: */
479 scroll.x = sector->solids->get_height()*32 - screen->w;
512 show_grid = !show_grid;
519 case SDL_QUIT: // window closed
530 void LevelEditor::action()
532 mouse_cursor->set_state(MC_NORMAL);
533 if(tiles_board->is_hover() || tiles_layer->is_hover() || level_options->is_hover())
534 mouse_cursor->set_state(MC_LINK);
538 if(frame_timer.check()) {
539 ++global_frame_counter;
542 // don't scroll before the start or after the level's end
543 float width = sector->solids->get_width() * 32;
544 float height = sector->solids->get_height() * 32;
546 if(scroll.x < -screen->w/2)
547 scroll.x = -screen->w/2;
548 if(scroll.x > width - screen->w/2)
549 scroll.x = width - screen->w/2;
550 if(scroll.y < -screen->h/2)
551 scroll.y = -screen->h/2;
552 if(scroll.y > height - screen->h/2)
553 scroll.y = height - screen->h/2;
555 // set camera translation, since BadGuys like it
556 sector->camera->set_scrolling((int)scroll.x, (int)scroll.y);
558 if(left_button && mouse_moved)
559 for(unsigned int x = 0; x < selection.size(); x++)
560 for(unsigned int y = 0; y < selection[x].size(); y++)
561 change((int)(scroll.x + event.button.x) + (x*32),
562 (int)(scroll.y + event.button.y) + (y*32), selection[x][y],
567 #define FADING_TIME .6
569 void LevelEditor::draw(DrawingContext& context)
571 context.draw_text(white_text, _("Level Editor"), Vector(10, 5), LEFT_ALLIGN, LAYER_GUI);
572 mouse_cursor->draw(context);
574 // draw a filled background
575 context.draw_filled_rect(Vector(0,0), Vector(screen->w,screen->h), Color(60,60,60), LAYER_BACKGROUND0-1);
577 if(level_name_timer.check())
579 context.push_transform();
580 if(level_name_timer.get_timeleft() < FADING_TIME)
581 context.set_alpha(int(level_name_timer.get_timeleft() * 255 / FADING_TIME));
583 context.draw_text(gold_text, level->name, Vector(screen->w/2, 30), CENTER_ALLIGN, LAYER_GUI);
587 sprintf(str, "%i/%i", level_nb+1, level_subset->get_num_levels());
588 context.draw_text(gold_text, str, Vector(screen->w/2, 50), CENTER_ALLIGN, LAYER_GUI);
591 context.pop_transform();
594 context.draw_text(white_small_text, _("F1 for help"), Vector(5, 510), LEFT_ALLIGN, LAYER_GUI-10);
596 context.draw_text(white_small_text, _("Choose a level subset"), Vector(5, 510), LEFT_ALLIGN, LAYER_GUI-10);
598 Menu* menu = Menu::current();
603 tiles_board->draw(context);
604 tiles_layer->draw(context);
605 level_options->draw(context);
613 context.set_drawing_effect(SEMI_TRANSPARENT);
617 if(selection[0][0] == 0 && selection.size() == 1)
618 context.draw_surface(img_rubber_bt, Vector(event.button.x - 8,
619 event.button.y - 8), LAYER_GUI-2);
620 else if(selection[0][0] < 0)
622 int id = selection[0][0];
625 if(id == OBJ_TRAMPOLINE)
626 context.draw_surface(img_trampoline[0].get_frame(0), Vector(event.button.x - 8,
627 event.button.y - 8), LAYER_GUI-2);
628 else if(id == OBJ_FLYING_PLATFORM)
629 context.draw_surface(img_flying_platform->get_frame(0), Vector(event.button.x - 8,
630 event.button.y - 8), LAYER_GUI-2);
634 /*context.draw_surface(door->get_frame(0), Vector(event.button.x - 8,
635 event.button.y - 8), LAYER_GUI-2);*/
640 BadGuyKind kind = BadGuyKind((-id)-1);
641 BadGuy badguy(kind, 0,0);
642 badguy.activate(LEFT);
643 Surface *img = badguy.get_image();
645 context.draw_surface(img, Vector(event.button.x - 8,
646 event.button.y - 8), LAYER_GUI-2);
652 TileManager* tilemanager = TileManager::instance();
653 for(unsigned int x = 0; x < selection.size(); x++)
654 for(unsigned int y = 0; y < selection[x].size(); y++) {
655 const Tile* tile = tilemanager->get(selection[x][y]);
657 Vector(event.button.x + x*32 - 8, event.button.y + y*32 - 8),
662 context.set_drawing_effect(NONE_EFFECT);
665 context.draw_filled_rect(Vector(std::min((int)selection_ini.x, (int)event.button.x)*zoom,
666 std::min((int)selection_ini.y, (int)event.button.y))*zoom,
667 Vector(abs(event.button.x - (int)selection_ini.x)*zoom,
668 abs(event.button.y - (int)selection_ini.y)*zoom),
669 Color(170,255,170,128), LAYER_GUI-2);
673 for(int x = 0; x < screen->w / (32*zoom); x++)
675 int pos = (int)(x*32*zoom) - ((int)scroll.x % 32);
676 context.draw_filled_rect(Vector (pos, 0), Vector(1, screen->h),
677 Color(225, 225, 225), LAYER_GUI-50);
679 for(int y = 0; y < screen->h / (32*zoom); y++)
681 int pos = (int)(y*32*zoom) - ((int)scroll.y % 32);
682 context.draw_filled_rect(Vector (0, pos), Vector(screen->w, 1),
683 Color(225, 225, 225), LAYER_GUI-50);
687 context.push_transform();
688 context.set_translation(scroll);
689 context.set_zooming(zoom);
691 for(Sector::GameObjects::iterator i = sector->gameobjects.begin(); i != sector->gameobjects.end(); ++i)
693 TileMap* tilemap = dynamic_cast<TileMap*> (*i);
695 { // draw the non-selected tiles semi-transparently
696 context.push_transform();
698 if(tilemap->get_layer() != cur_layer)
699 context.set_drawing_effect(SEMI_TRANSPARENT);
702 context.pop_transform();
705 Background* background = dynamic_cast<Background*> (*i);
707 { // don't resize background
708 context.push_transform();
709 context.set_zooming(1.0);
711 context.pop_transform();
717 context.pop_transform();
720 context.draw_filled_rect(Vector(0,0), Vector(screen->w,screen->h),Color(0,0,0), LAYER_BACKGROUND0);
722 context.do_drawing();
725 void LevelEditor::load_level_subset(std::string filename)
728 level_subset = new LevelSubset();
729 level_subset->load(filename.c_str());
733 void LevelEditor::load_level(std::string filename)
737 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
743 level_filename = filename;
747 level->load(filename);
750 level_name_timer.start(3000);
751 scroll.x = scroll.y = 0;
752 level_changed = false;
754 settings_menu->get_item_by_id(MN_ID_NAME).change_input(level->name.c_str());
755 settings_menu->get_item_by_id(MN_ID_AUTHOR).change_input(level->author.c_str());
758 void LevelEditor::load_level(int nb)
762 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
769 level_filename = level_subset->get_level_filename(level_nb);
771 load_level(level_filename);
774 void LevelEditor::load_sector(std::string name)
777 sector = level->get_sector(sector_name);
779 Termination::abort("Level has no " + sector_name + " sector.", "");
784 void LevelEditor::load_sector(Sector* sector_)
788 if(!confirm_dialog(NULL, _("No more sectors exist. Create another?")))
790 sector_ = Sector::create("new_sector",25,19);
791 level->add_sector(sector_);
796 /* Load sector stuff */
798 sector->update_game_objects();
800 foregrounds = solids = backgrounds = 0;
801 /* Point foregrounds, backgrounds, solids to its layer */
802 for(Sector::GameObjects::iterator i = sector->gameobjects.begin(); i != sector->gameobjects.end(); i++)
805 BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
807 badguy->activate(LEFT);
810 TileMap* tilemap = dynamic_cast<TileMap*> (*i);
813 if(tilemap->get_layer() == LAYER_FOREGROUNDTILES)
814 foregrounds = tilemap;
815 else if(tilemap->get_layer() == LAYER_TILES)
817 else if(tilemap->get_layer() == LAYER_BACKGROUNDTILES)
818 backgrounds = tilemap;
824 TileMap* tilemap = new TileMap(LAYER_FOREGROUNDTILES, false, solids->get_width(), solids->get_height());
825 sector->add_object(tilemap);
826 sector->update_game_objects();
830 TileMap* tilemap = new TileMap(LAYER_BACKGROUNDTILES, false, solids->get_width(), solids->get_height());
831 sector->add_object(tilemap);
832 sector->update_game_objects();
836 sprintf(str, "%i", solids->get_width());
837 settings_menu->get_item_by_id(MN_ID_WIDTH).change_input(str);
838 sprintf(str, "%i", solids->get_height());
839 settings_menu->get_item_by_id(MN_ID_HEIGHT).change_input(str);
842 void LevelEditor::save_level()
844 level->save(level_filename);
845 level_changed = false;
848 void LevelEditor::test_level()
851 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
857 GameSession session(level_filename, ST_GL_TEST);
859 // player_status.reset();
860 SoundManager::get()->halt_music();
863 void LevelEditor::change(int x, int y, int newtile, int layer)
865 // find the tilemap of the current layer, and then change the tile
866 if(x < 0 || (unsigned int)x >= sector->solids->get_width()*32 ||
867 y < 0 || (unsigned int)y >= sector->solids->get_height()*32)
870 level_changed = true;
873 { // no need to do this for normal view (no zoom)
874 x = (int)(x * (zoom*32) / 32);
875 y = (int)(y * (zoom*32) / 32);
878 if(newtile < 0) // add object
880 // remove an active tile or object that might be there
881 change(x, y, 0, LAYER_TILES);
884 if(newtile == OBJ_TRAMPOLINE)
885 sector->add_object(new Trampoline(x, y));
886 else if(newtile == OBJ_FLYING_PLATFORM)
887 sector->add_object(new FlyingPlatform(x, y));
889 if(newtile == OBJ_DOOR)
890 sector->add_object(new Door(x, y));
892 sector->add_bad_guy(x, y, BadGuyKind((-newtile)-1), true);
895 sector->update_game_objects();
896 } else if(cur_layer == LAYER_FOREGROUNDTILES) {
897 foregrounds->change(x/32, y/32, newtile);
898 } else if(cur_layer == LAYER_TILES) {
899 // remove a bad guy if it's there
900 // we /32 in order to round numbers
901 for(Sector::GameObjects::iterator i = sector->gameobjects.begin();
902 i < sector->gameobjects.end(); i++) {
904 BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
906 if((int)badguy->base.x/32 == x/32 && (int)badguy->base.y/32 == y/32)
907 sector->gameobjects.erase(i);
910 Trampoline* trampoline = dynamic_cast<Trampoline*> (*i);
913 if((int)trampoline->base.x/32 == x/32 && (int)trampoline->base.y/32 == y/32)
914 sector->gameobjects.erase(i);
916 FlyingPlatform* flying_platform = dynamic_cast<FlyingPlatform*> (*i);
918 if((int)flying_platform->base.x/32 == x/32 && (int)flying_platform->base.y/32 == y/32)
919 sector->gameobjects.erase(i);
922 Door* door = dynamic_cast<Door*> (*i);
924 if((int)door->get_area().x/32 == x/32 && (int)door->get_area().y/32 == y/32)
925 sector->gameobjects.erase(i);
928 sector->update_game_objects();
929 solids->change(x/32, y/32, newtile);
930 } else if(cur_layer == LAYER_BACKGROUNDTILES)
931 backgrounds->change(x/32, y/32, newtile);
934 void LevelEditor::show_help()
936 DrawingContext context;
938 bool show_grid_t = show_grid;
940 mouse_cursor->set_state(MC_HIDE);
945 _("This is the built-in level editor. Its aim is to be intuitive\n"
946 "and simple to use, so it should be pretty straightforward.\n"
948 "To open a level, first you'll have to select a level subset from\n"
949 "the menu (or create your own).\n"
950 "A level subset is basically a collection of levels.\n"
951 "They can then be played from the Contrib menu.\n"
953 "To access the menu from the level editor, just press Esc.\n"
955 "You are currently looking at the level. To scroll it, just\n"
956 "press the right mouse button and drag the mouse. It will move like\n"
958 "You can also use the arrow keys and Page Up/Down.\n"
960 "'+' and '-' keys can be used to zoom the level in/out.\n"
962 "You probably already noticed those floating groups of buttons.\n"
963 "Each one serves a different purpose. To select a certain button\n"
964 "just press the Left mouse button on it. A few buttons have key\n"
965 "shortcuts. You can find them by pressing the Right mouse button on\n"
966 "a button. That will also show what that button does.\n"
967 "Groups of buttons can also be moved around by just dragging them,\n"
968 "while pressing the Left mouse button.\n"
970 "Let's learn a bit of what each group of buttons does, shall we?\n"
972 "To starting putting tiles and objects around use the bigger group\n"
973 "of buttons. Each button is a different tile. To put it on the level,\n"
974 "just press it and then left click in the level.\n"
975 "You can also copy tiles from the level by using the middle mouse button.\n"
976 "Use the mouse wheel to scroll that group of buttons. You will find\n"
977 "enemies and game objects in the bottom.\n")
981 _("The Foreground/Interactive/Background buttons may be used to\n"
982 "see and edit the respective layer. Levels have three tiles layers:\n"
983 "Foreground - tiles are drawn on top of everything and have no contact\n"
985 "Interactive - these are the tiles that have contact with the player.\n"
986 "Background - tiles are drawn underneath everything and have no contact\n"
988 "The unselected layers will be drawn semi-transparently.\n"
990 "Last, but not least, the group of buttons that's left serves\n"
991 "to do related actions with the level.\n"
992 "From left to right:\n"
993 "Mini arrows - can be used to choose other sectors.\n"
994 "Sectors are mini-levels, so to speak, that can be accessed using a door.\n"
995 "Big arrows - choose other level in the same level subset.\n"
996 "Diskette - save the level\n"
997 "Tux - test the level\n"
998 "Tools - set a few settings for the level, including resizing it.\n"
1000 "We have reached the end of this Howto.\n"
1002 "Don't forget to send us a few cool levels. :)\n"
1005 " SuperTux development team\n"
1007 "PS: If you are looking for something more powerful, you might like to\n"
1008 "try FlexLay. FlexLay is a level editor that supports several games,\n"
1009 "including SuperTux. It is an independent project.\n"
1010 "Webpage: http://pingus.seul.org/~grumbel/flexlay/")
1013 char **text[] = { text1, text2 };
1017 for(unsigned int i = 0; i < sizeof(text) / sizeof(text[0]); i++)
1021 context.draw_text(blue_text, _("- Level Editor's Help -"), Vector(screen->w/2, 60), CENTER_ALLIGN, LAYER_GUI);
1023 context.draw_text(white_small_text, *text[i], Vector(20, 120), LEFT_ALLIGN, LAYER_GUI);
1025 sprintf(str,_("Press any key to continue - Page %d/%d"), i+1, sizeof(text) / sizeof(text[0]));
1026 context.draw_text(gold_text, str, Vector(screen->w/2, screen->h-60), CENTER_ALLIGN, LAYER_GUI);
1028 context.do_drawing();
1034 done = wait_for_event(event);
1039 show_grid = show_grid_t;
1040 mouse_cursor->set_state(MC_NORMAL);