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_factory.h"
38 #include "object/gameobjs.h"
39 #include "object/camera.h"
40 #include "object/tilemap.h"
41 #include "object/background.h"
43 LevelEditor::LevelEditor()
48 selection_end = selection_ini = Vector(0,0);
49 left_button = middle_button = mouse_moved = false;
53 cur_layer = LAYER_TILES;
54 level_changed = false;
60 level_subsets = FileSystem::dsubdirs("/levels", "info");
61 subset_menu = new Menu();
62 subset_menu->additem(MN_LABEL,_("Load Subset"),0,0);
63 subset_menu->additem(MN_HL,"",0,0);
65 for(std::set<std::string>::iterator it = level_subsets.begin(); it != level_subsets.end(); ++it, ++i)
66 subset_menu->additem(MN_ACTION, (*it),0,0,i);
67 subset_menu->additem(MN_HL,"",0,0);
68 subset_menu->additem(MN_BACK,_("Back"),0,0);
70 create_subset_menu = new Menu();
71 create_subset_menu->additem(MN_LABEL,_("New Level Subset"),0,0);
72 create_subset_menu->additem(MN_HL,"",0,0);
73 create_subset_menu->additem(MN_TEXTFIELD,_("Filename "),0,0,MN_ID_FILENAME_SUBSET);
74 create_subset_menu->additem(MN_TEXTFIELD,_("Title "),0,0,MN_ID_TITLE_SUBSET);
75 create_subset_menu->additem(MN_TEXTFIELD,_("Description"),0,0,MN_ID_DESCRIPTION_SUBSET);
76 create_subset_menu->additem(MN_ACTION,_("Create"),0,0, MN_ID_CREATE_SUBSET);
77 create_subset_menu->additem(MN_HL,"",0,0);
78 create_subset_menu->additem(MN_BACK,_("Back"),0,0);
80 main_menu = new Menu();
81 main_menu->additem(MN_LABEL,_("Level Editor Menu"),0,0);
82 main_menu->additem(MN_HL,"",0,0);
83 main_menu->additem(MN_ACTION,_("Return to Level Editor"),0,0,MN_ID_RETURN);
84 main_menu->additem(MN_GOTO,_("Create Level Subset"),0,create_subset_menu);
85 main_menu->additem(MN_GOTO,_("Load Level Subset"),0,subset_menu);
86 main_menu->additem(MN_HL,"",0,0);
87 main_menu->additem(MN_ACTION,_("Quit Level Editor"),0,0,MN_ID_QUIT);
89 settings_menu = new Menu();
90 settings_menu->additem(MN_LABEL,_("Level Settings"),0,0);
91 settings_menu->additem(MN_HL,"",0,0);
92 settings_menu->additem(MN_TEXTFIELD,_("Name "),0,0,MN_ID_NAME);
93 settings_menu->additem(MN_TEXTFIELD,_("Author "),0,0,MN_ID_AUTHOR);
94 settings_menu->additem(MN_NUMFIELD, _("Width "),0,0,MN_ID_WIDTH);
95 settings_menu->additem(MN_NUMFIELD, _("Height "),0,0,MN_ID_HEIGHT);
96 settings_menu->additem(MN_HL,"",0,0);
97 settings_menu->additem(MN_ACTION,_("Apply"),0,0,MN_ID_APPLY_SETTINGS);
99 /* Creating button groups */
102 tiles_board = new ButtonGroup(Vector(screen->w - 140, 100),
103 Vector(32,32), Vector(4,8));
105 tiles_board->add_button(Button(img_rubber_bt, _("Eraser"), SDLKey(SDLK_DELETE)), 0);
107 for(id = 1; id < tile_manager->get_max_tileid(); id++)
109 const Tile* tile = tile_manager->get(id);
113 Surface* surface = tile->get_editor_image();
117 Button button = Button(surface, "", SDLKey(0));
118 tiles_board->add_button(button, id);
120 gameobjs_first_id = id;
122 for(Factories::iterator i = object_factories->begin();
123 i != object_factories->end(); ++i) {
125 // Surface *img = badguy.get_image();
126 // FIXME: get image from object. Using the rubber in the meanwhile.
127 tiles_board->add_button(Button(img_rubber_bt, i->first,
128 SDLKey(SDLK_1+id)), id++);
131 tiles_layer = new ButtonGroup(Vector(12, screen->h-64), Vector(80,20), Vector(1,3));
132 tiles_layer->add_button(Button(img_foreground_bt, _("Edtit foreground tiles"),
133 SDLK_F10), LAYER_FOREGROUNDTILES);
134 tiles_layer->add_button(Button(img_interactive_bt, _("Edit interactive tiles"),
135 SDLK_F11), LAYER_TILES, true);
136 tiles_layer->add_button(Button(img_background_bt, _("Edit background tiles"),
137 SDLK_F12), LAYER_BACKGROUNDTILES);
139 level_options = new ButtonGroup(Vector(screen->w-164, screen->h-36), Vector(32,32), Vector(5,1));
140 level_options->add_pair_of_buttons(Button(img_next_sector_bt, _("Next sector"), SDLKey(0)), BT_NEXT_SECTOR,
141 Button(img_previous_sector_bt, _("Prevous sector"), SDLKey(0)), BT_PREVIOUS_SECTOR);
142 level_options->add_pair_of_buttons(Button(img_next_level_bt, _("Next level"), SDLKey(0)), BT_NEXT_LEVEL,
143 Button(img_previous_level_bt, _("Prevous level"), SDLKey(0)), BT_PREVIOUS_LEVEL);
144 level_options->add_button(Button(img_save_level_bt, _("Save level"), SDLK_F5), BT_LEVEL_SAVE);
145 level_options->add_button(Button(img_test_level_bt, _("Test level"), SDLK_F6), BT_LEVEL_TEST);
146 level_options->add_button(Button(img_setup_level_bt, _("Setup level"), SDLK_F7), BT_LEVEL_SETUP);
149 LevelEditor::~LevelEditor()
155 delete level_options;
158 delete create_subset_menu;
160 delete settings_menu;
166 void LevelEditor::load_buttons_gfx()
168 img_foreground_bt = new Surface(datadir + "/images/leveleditor/foreground.png", true);
169 img_interactive_bt = new Surface(datadir + "/images/leveleditor/interactive.png", true);
170 img_background_bt = new Surface(datadir + "/images/leveleditor/background.png", true);
172 img_save_level_bt = new Surface(datadir + "/images/leveleditor/save-level.png", true);
173 img_test_level_bt = new Surface(datadir + "/images/leveleditor/test-level.png", true);
174 img_setup_level_bt = new Surface(datadir + "/images/leveleditor/setup-level.png", true);
176 img_rubber_bt = new Surface(datadir + "/images/leveleditor/rubber.png", true);
178 img_previous_level_bt = new Surface(datadir + "/images/leveleditor/previous-level.png", true);
179 img_next_level_bt = new Surface(datadir + "/images/leveleditor/next-level.png", true);
180 img_previous_sector_bt = new Surface(datadir + "/images/leveleditor/previous-sector.png", true);
181 img_next_sector_bt = new Surface(datadir + "/images/leveleditor/next-sector.png", true);
184 void LevelEditor::free_buttons_gfx()
186 delete img_foreground_bt;
187 delete img_interactive_bt;
188 delete img_background_bt;
190 delete img_save_level_bt;
191 delete img_test_level_bt;
192 delete img_setup_level_bt;
194 delete img_rubber_bt;
196 delete img_previous_level_bt;
197 delete img_next_level_bt;
198 delete img_previous_sector_bt;
199 delete img_next_sector_bt;
202 void LevelEditor::run(const std::string filename)
204 SoundManager::get()->halt_music();
205 Menu::set_current(0);
207 DrawingContext context;
209 if(!filename.empty())
212 load_level(filename);
215 Menu::set_current(main_menu);
217 mouse_cursor->set_state(MC_NORMAL);
228 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
232 void LevelEditor::events()
237 while(SDL_PollEvent(&event))
239 Menu* menu = Menu::current();
244 if(menu == main_menu)
246 switch (main_menu->check())
249 Menu::set_current(0);
256 else if(menu == create_subset_menu)
258 // activate or deactivate Create button if any filename as been specified
259 if(create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input[0] == '\0')
260 create_subset_menu->get_item_by_id(MN_ID_CREATE_SUBSET).kind = MN_DEACTIVE;
262 create_subset_menu->get_item_by_id(MN_ID_CREATE_SUBSET).kind = MN_ACTION;
264 if(create_subset_menu->check() == MN_ID_CREATE_SUBSET)
265 { // applying settings:
266 std::string subset_name = create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input;
267 LevelSubset::create(subset_name);
270 level_subset = new LevelSubset();
271 level_subset->load(create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input);
273 level_subset->title = create_subset_menu->get_item_by_id(MN_ID_TITLE_SUBSET).input;
274 level_subset->description = create_subset_menu->get_item_by_id(MN_ID_DESCRIPTION_SUBSET).input;
275 //FIXME: generate better level filenames
276 level_subset->add_level(subset_name+'/'+"new_level.stl");
277 Level* newlevel = new Level();
278 newlevel->add_sector(create_sector("main", 25, 19));
279 newlevel->save(level_subset->get_level_filename(0));
280 level_subset->save();
284 create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).change_input("");
285 create_subset_menu->get_item_by_id(MN_ID_TITLE_SUBSET).change_input("");
286 create_subset_menu->get_item_by_id(MN_ID_DESCRIPTION_SUBSET).change_input("");
289 else if(menu == subset_menu)
291 int i = subset_menu->check();
294 std::set<std::string>::iterator it = level_subsets.begin();
295 for(int t = 0; t < i; t++)
297 load_level_subset(*it);
298 Menu::set_current(0);
301 else if(menu == settings_menu)
303 if(settings_menu->check() == MN_ID_APPLY_SETTINGS)
304 { // applying settings:
305 level_changed = true;
307 level->name = settings_menu->get_item_by_id(MN_ID_NAME).input;
308 level->author = settings_menu->get_item_by_id(MN_ID_AUTHOR).input;
310 solids->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()),
311 atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str()));
312 foregrounds->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()),
313 atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str()));
314 backgrounds->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()),
315 atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str()));
317 Menu::set_current(0);
321 // check for events in buttons
322 else if(tiles_board->event(event))
324 std::vector <int> vector;
325 vector.push_back(tiles_board->selected_id());
328 selection.push_back(vector);
331 else if(tiles_layer->event(event))
333 cur_layer = tiles_layer->selected_id();
336 else if(level_options->event(event))
338 switch(level_options->selected_id())
347 Menu::set_current(settings_menu);
350 if(level_nb + 1 < level_subset->get_num_levels())
351 load_level(level_nb + 1);
355 sprintf(str,_("Level %d doesn't exist. Create it?"), level_nb + 2);
356 if(confirm_dialog(NULL, str))
358 level_subset->add_level("new_level.stl");
359 Level* newlevel = new Level();
360 newlevel->add_sector(create_sector("main", 25, 19));
361 newlevel->save(level_subset->get_level_filename(level_nb + 1));
362 level_subset->save();
363 load_level(level_nb + 1);
367 case BT_PREVIOUS_LEVEL:
368 if(level_nb - 1 >= 0)
369 load_level(level_nb - 1);
372 std::cerr << "next sector.\n";
373 load_sector(sectornum+1);
375 case BT_PREVIOUS_SECTOR:
376 std::cerr << "previous sector.\n";
378 load_sector(sectornum-1);
381 level_options->set_unselected();
388 case SDL_MOUSEMOTION:
390 mouse_x = event.motion.x;
391 mouse_y = event.motion.y;
392 if(SDL_GetMouseState(NULL, NULL)&SDL_BUTTON(SDL_BUTTON_RIGHT))
393 { // movement like in strategy games
394 scroll.x += -1 * event.motion.xrel;
395 scroll.y += -1 * event.motion.yrel;
399 case SDL_MOUSEBUTTONDOWN:
401 mouse_x = event.motion.x;
402 mouse_y = event.motion.y;
403 if(event.button.button == SDL_BUTTON_LEFT)
405 else if(event.button.button == SDL_BUTTON_MIDDLE)
407 middle_button = true;
408 selection_ini = Vector(event.button.x, event.button.y);
412 case SDL_MOUSEBUTTONUP:
414 mouse_x = event.motion.x;
415 mouse_y = event.motion.y;
416 if(event.button.button == SDL_BUTTON_LEFT)
418 else if(event.button.button == SDL_BUTTON_MIDDLE)
420 middle_button = false;
421 selection_end = Vector(event.button.x, event.button.y);
423 if(selection_end.x < selection_ini.x)
425 float t = selection_ini.x;
426 selection_ini.x = selection_end.x;
429 if(selection_end.y < selection_ini.y)
431 float t = selection_ini.y;
432 selection_ini.y = selection_end.y;
437 std::vector <int> vector;
439 TileMap* tilemap = 0;
440 if(cur_layer == LAYER_FOREGROUNDTILES)
441 tilemap = foregrounds;
442 else if(cur_layer == LAYER_TILES)
444 else if(cur_layer == LAYER_BACKGROUNDTILES)
445 tilemap = backgrounds;
447 for(int x = 0; x < (int)((selection_end.x - selection_ini.x)*zoom / 32) + 1; x++)
450 for(int y = 0; y < (int)((selection_end.y - selection_ini.y)*zoom / 32) + 1; y++)
452 vector.push_back(tilemap->get_tile(x +
453 (int)(((selection_ini.x+scroll.x)*zoom)/32),
454 y + (int)(((selection_ini.y+scroll.y)*zoom)/32))->getID());
456 selection.push_back(vector);
461 case SDL_KEYDOWN: // key pressed
462 switch(event.key.keysym.sym)
465 Menu::set_current(main_menu);
467 /* scrolling related events: */
472 scroll.x = sector->solids->get_height()*32 - screen->w;
505 show_grid = !show_grid;
512 case SDL_QUIT: // window closed
523 void LevelEditor::action()
525 mouse_cursor->set_state(MC_NORMAL);
526 if(tiles_board->is_hover() || tiles_layer->is_hover() || level_options->is_hover())
527 mouse_cursor->set_state(MC_LINK);
531 // don't scroll before the start or after the level's end
532 float width = sector->solids->get_width() * 32;
533 float height = sector->solids->get_height() * 32;
535 if(scroll.x < -screen->w/2)
536 scroll.x = -screen->w/2;
537 if(scroll.x > width - screen->w/2)
538 scroll.x = width - screen->w/2;
539 if(scroll.y < -screen->h/2)
540 scroll.y = -screen->h/2;
541 if(scroll.y > height - screen->h/2)
542 scroll.y = height - screen->h/2;
544 // set camera translation, since BadGuys like it
545 sector->camera->set_scrolling((int)scroll.x, (int)scroll.y);
547 if(left_button && mouse_moved)
548 for(unsigned int x = 0; x < selection.size(); x++)
549 for(unsigned int y = 0; y < selection[x].size(); y++)
550 change((int)(scroll.x + mouse_x) + (x*32),
551 (int)(scroll.y + mouse_y) + (y*32), selection[x][y],
556 #define FADING_TIME .6
558 void LevelEditor::draw(DrawingContext& context)
560 context.draw_text(white_text, _("Level Editor"), Vector(10, 5), LEFT_ALLIGN, LAYER_GUI);
561 mouse_cursor->draw(context);
563 // draw a filled background
564 context.draw_filled_rect(Vector(0,0), Vector(screen->w,screen->h), Color(60,60,60), LAYER_BACKGROUND0-1);
566 if(level_name_timer.check())
568 context.push_transform();
569 if(level_name_timer.get_timeleft() < FADING_TIME)
570 context.set_alpha(int(level_name_timer.get_timeleft() * 255 / FADING_TIME));
572 context.draw_text(gold_text, level->name, Vector(screen->w/2, 30), CENTER_ALLIGN, LAYER_GUI);
576 sprintf(str, "%i/%i", level_nb+1, level_subset->get_num_levels());
577 context.draw_text(gold_text, str, Vector(screen->w/2, 50), CENTER_ALLIGN, LAYER_GUI);
580 context.pop_transform();
583 context.draw_text(white_small_text, _("F1 for help"), Vector(5, 510), LEFT_ALLIGN, LAYER_GUI-10);
585 context.draw_text(white_small_text, _("Choose a level subset"), Vector(5, 510), LEFT_ALLIGN, LAYER_GUI-10);
587 Menu* menu = Menu::current();
592 tiles_board->draw(context);
593 tiles_layer->draw(context);
594 level_options->draw(context);
602 context.set_drawing_effect(SEMI_TRANSPARENT);
606 if(selection[0][0] == 0 && selection.size() == 1)
607 context.draw_surface(img_rubber_bt, Vector(mouse_x - 8,
608 mouse_y - 8), LAYER_GUI-2);
609 else if(selection[0][0] >= gameobjs_first_id || selection[0][0] < 0)
611 // FIXME: this should draw an object image near cursor
613 int id = selection[0][0];
615 if(id == OBJ_TRAMPOLINE)
616 context.draw_surface(img_trampoline[0].get_frame(0), Vector(mouse_x - 8,
617 mouse_y - 8), LAYER_GUI-2);
618 else if(id == OBJ_FLYING_PLATFORM)
619 context.draw_surface(img_flying_platform->get_frame(0), Vector(mouse_x - 8,
620 mouse_y - 8), LAYER_GUI-2);
623 /*context.draw_surface(door->get_frame(0), Vector(mouse_x - 8,
624 mouse_y - 8), LAYER_GUI-2);*/
628 BadGuyKind kind = BadGuyKind((-id)-1);
629 BadGuy badguy(kind, 0,0);
630 badguy.activate(LEFT);
631 Surface *img = badguy.get_image();
633 context.draw_surface(img, Vector(mouse_x - 8,
634 mouse_y - 8), LAYER_GUI-2);
640 for(unsigned int x = 0; x < selection.size(); x++)
641 for(unsigned int y = 0; y < selection[x].size(); y++) {
642 const Tile* tile = tile_manager->get(selection[x][y]);
644 Vector(mouse_x + x*32 - 8, mouse_y + y*32 - 8),
649 context.set_drawing_effect(NONE_EFFECT);
652 context.draw_filled_rect(Vector(std::min((int)selection_ini.x, mouse_x)*zoom,
653 std::min((int)selection_ini.y, mouse_y))*zoom,
654 Vector(abs(mouse_x - (int)selection_ini.x)*zoom,
655 abs(mouse_y - (int)selection_ini.y)*zoom),
656 Color(170,255,170,128), LAYER_GUI-2);
660 for(int x = 0; x < screen->w / (32*zoom); x++)
662 int pos = (int)(x*32*zoom) - ((int)scroll.x % 32);
663 context.draw_filled_rect(Vector (pos, 0), Vector(1, screen->h),
664 Color(225, 225, 225), LAYER_GUI-50);
666 for(int y = 0; y < screen->h / (32*zoom); y++)
668 int pos = (int)(y*32*zoom) - ((int)scroll.y % 32);
669 context.draw_filled_rect(Vector (0, pos), Vector(screen->w, 1),
670 Color(225, 225, 225), LAYER_GUI-50);
674 context.push_transform();
675 context.set_translation(scroll);
676 context.set_zooming(zoom);
678 for(Sector::GameObjects::iterator i = sector->gameobjects.begin(); i != sector->gameobjects.end(); ++i)
680 TileMap* tilemap = dynamic_cast<TileMap*> (*i);
682 { // draw the non-selected tiles semi-transparently
683 context.push_transform();
685 if(tilemap->get_layer() != cur_layer)
686 context.set_drawing_effect(SEMI_TRANSPARENT);
689 context.pop_transform();
692 Background* background = dynamic_cast<Background*> (*i);
694 { // don't resize background
695 context.push_transform();
696 context.set_zooming(1.0);
698 context.pop_transform();
704 context.pop_transform();
707 context.draw_filled_rect(Vector(0,0), Vector(screen->w,screen->h),Color(0,0,0), LAYER_BACKGROUND0);
709 context.do_drawing();
712 void LevelEditor::load_level_subset(std::string filename)
715 level_subset = new LevelSubset();
716 level_subset->load(filename.c_str());
720 void LevelEditor::load_level(std::string filename)
724 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
730 level_filename = filename;
734 level->load(filename);
738 level_name_timer.start(3000);
739 scroll.x = scroll.y = 0;
740 level_changed = false;
742 settings_menu->get_item_by_id(MN_ID_NAME).change_input(level->name.c_str());
743 settings_menu->get_item_by_id(MN_ID_AUTHOR).change_input(level->author.c_str());
746 void LevelEditor::load_level(int nb)
750 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
757 level_filename = level_subset->get_level_filename(level_nb);
759 load_level(level_filename);
762 void LevelEditor::load_sector(size_t num)
764 assert(num <= level->get_sector_count());
766 if(num >= level->get_sector_count())
768 if(!confirm_dialog(NULL, _("No more sectors exist. Create another?")))
770 Sector* sector_ = create_sector("new_sector",25,19);
771 level->add_sector(sector_);
772 num = level->get_sector_count()-1;
775 sector = level->get_sector(num);
777 /* Load sector stuff */
779 sector->update_game_objects();
781 foregrounds = solids = backgrounds = 0;
782 /* Point foregrounds, backgrounds, solids to its layer */
783 for(Sector::GameObjects::iterator i = sector->gameobjects.begin(); i != sector->gameobjects.end(); i++)
785 TileMap* tilemap = dynamic_cast<TileMap*> (*i);
788 if(tilemap->get_layer() == LAYER_FOREGROUNDTILES)
789 foregrounds = tilemap;
790 else if(tilemap->get_layer() == LAYER_TILES)
792 else if(tilemap->get_layer() == LAYER_BACKGROUNDTILES)
793 backgrounds = tilemap;
799 TileMap* tilemap = new TileMap(LAYER_FOREGROUNDTILES, false, solids->get_width(), solids->get_height());
800 sector->add_object(tilemap);
801 sector->update_game_objects();
805 TileMap* tilemap = new TileMap(LAYER_BACKGROUNDTILES, false, solids->get_width(), solids->get_height());
806 sector->add_object(tilemap);
807 sector->update_game_objects();
811 sprintf(str, "%i", solids->get_width());
812 settings_menu->get_item_by_id(MN_ID_WIDTH).change_input(str);
813 sprintf(str, "%i", solids->get_height());
814 settings_menu->get_item_by_id(MN_ID_HEIGHT).change_input(str);
819 void LevelEditor::save_level()
821 level->save(level_filename);
822 level_changed = false;
825 void LevelEditor::test_level()
828 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
834 GameSession session(level_filename, ST_GL_TEST);
836 // player_status.reset();
837 SoundManager::get()->halt_music();
840 void LevelEditor::change(int x, int y, int newtile, int layer)
843 // find the tilemap of the current layer, and then change the tile
844 if(x < 0 || (unsigned int)x >= sector->solids->get_width()*32 ||
845 y < 0 || (unsigned int)y >= sector->solids->get_height()*32)
848 level_changed = true;
852 // no need to do this for normal view (no zoom)
853 x = (int)(x * (zoom*32) / 32);
854 y = (int)(y * (zoom*32) / 32);
857 if(newtile >= gameobjs_first_id) // add object
859 // remove an active tile or object that might be there
860 change(x, y, 0, LAYER_TILES);
863 GameObject* object = 0;
864 for(Factories::iterator i = object_factories->begin(); i !=
865 object_factories->end(); ++i) {
866 if(id == newtile - gameobjs_first_id) {
867 object = create_object(i->first, Vector(x, y));
873 sector->add_object(object);
874 sector->update_game_objects();
876 } else if(cur_layer == LAYER_FOREGROUNDTILES) {
877 foregrounds->change(x/32, y/32, newtile);
878 } else if(cur_layer == LAYER_TILES) {
879 // remove a bad guy if it's there
880 // we /32 in order to round numbers
881 for(Sector::GameObjects::iterator i = sector->gameobjects.begin();
882 i < sector->gameobjects.end(); i++) {
883 // FIXME: if there is a game objs in here, remove it!
885 BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
887 if((int)badguy->base.x/32 == x/32 && (int)badguy->base.y/32 == y/32)
888 sector->gameobjects.erase(i);
889 Trampoline* trampoline = dynamic_cast<Trampoline*> (*i);
892 if((int)trampoline->base.x/32 == x/32 && (int)trampoline->base.y/32 == y/32)
893 sector->gameobjects.erase(i);
895 FlyingPlatform* flying_platform = dynamic_cast<FlyingPlatform*> (*i);
897 if((int)flying_platform->base.x/32 == x/32 && (int)flying_platform->base.y/32 == y/32)
898 sector->gameobjects.erase(i);
899 Door* door = dynamic_cast<Door*> (*i);
901 if((int)door->get_area().x/32 == x/32 && (int)door->get_area().y/32 == y/32)
902 sector->gameobjects.erase(i);
905 sector->update_game_objects();
906 solids->change(x/32, y/32, newtile);
907 } else if(cur_layer == LAYER_BACKGROUNDTILES)
908 backgrounds->change(x/32, y/32, newtile);
911 void LevelEditor::show_help()
913 DrawingContext context;
915 bool show_grid_t = show_grid;
917 mouse_cursor->set_state(MC_HIDE);
921 const char *text1[] = {
922 _("This is the built-in level editor. Its aim is to be intuitive\n"
923 "and simple to use, so it should be pretty straightforward.\n"
925 "To open a level, first you'll have to select a level subset from\n"
926 "the menu (or create your own).\n"
927 "A level subset is basically a collection of levels.\n"
928 "They can then be played from the Contrib menu.\n"
930 "To access the menu from the level editor, just press Esc.\n"
932 "You are currently looking at the level. To scroll it, just\n"
933 "press the right mouse button and drag the mouse. It will move like\n"
935 "You can also use the arrow keys and Page Up/Down.\n"
937 "'+' and '-' keys can be used to zoom the level in/out.\n"
939 "You probably already noticed those floating groups of buttons.\n"
940 "Each one serves a different purpose. To select a certain button\n"
941 "just press the Left mouse button on it. A few buttons have key\n"
942 "shortcuts. You can find them by pressing the Right mouse button on\n"
943 "a button. That will also show what that button does.\n"
944 "Groups of buttons can also be moved around by just dragging them,\n"
945 "while pressing the Left mouse button.\n"
947 "Let's learn a bit of what each group of buttons does, shall we?\n"
949 "To starting putting tiles and objects around use the bigger group\n"
950 "of buttons. Each button is a different tile. To put it on the level,\n"
951 "just press it and then left click in the level.\n"
952 "You can also copy tiles from the level by using the middle mouse button.\n"
953 "Use the mouse wheel to scroll that group of buttons. You will find\n"
954 "enemies and game objects in the bottom.\n")
957 const char *text2[] = {
958 _("The Foreground/Interactive/Background buttons may be used to\n"
959 "see and edit the respective layer. Levels have three tiles layers:\n"
960 "Foreground - tiles are drawn on top of everything and have no contact\n"
962 "Interactive - these are the tiles that have contact with the player.\n"
963 "Background - tiles are drawn underneath everything and have no contact\n"
965 "The unselected layers will be drawn semi-transparently.\n"
967 "Last, but not least, the group of buttons that's left serves\n"
968 "to do related actions with the level.\n"
969 "From left to right:\n"
970 "Mini arrows - can be used to choose other sectors.\n"
971 "Sectors are mini-levels, so to speak, that can be accessed using a door.\n"
972 "Big arrows - choose other level in the same level subset.\n"
973 "Diskette - save the level\n"
974 "Tux - test the level\n"
975 "Tools - set a few settings for the level, including resizing it.\n"
977 "We have reached the end of this Howto.\n"
979 "Don't forget to send us a few cool levels. :)\n"
982 " SuperTux development team\n"
984 "PS: If you are looking for something more powerful, you might like to\n"
985 "try FlexLay. FlexLay is a level editor that supports several games,\n"
986 "including SuperTux. It is an independent project.\n"
987 "Webpage: http://pingus.seul.org/~grumbel/flexlay/")
990 const char **text[] = { text1, text2 };
994 for(unsigned int i = 0; i < sizeof(text) / sizeof(text[0]); i++)
998 context.draw_text(blue_text, _("- Level Editor's Help -"), Vector(screen->w/2, 60), CENTER_ALLIGN, LAYER_GUI);
1000 context.draw_text(white_small_text, *text[i], Vector(20, 120), LEFT_ALLIGN, LAYER_GUI);
1002 sprintf(str,_("Press any key to continue - Page %d/%d"), i+1, sizeof(text) / sizeof(text[0]));
1003 context.draw_text(gold_text, str, Vector(screen->w/2, screen->h-60), CENTER_ALLIGN, LAYER_GUI);
1005 context.do_drawing();
1011 done = wait_for_event(event);
1016 show_grid = show_grid_t;
1017 mouse_cursor->set_state(MC_NORMAL);
1021 LevelEditor::create_sector(const std::string& name, size_t width, size_t height)
1023 Sector* sector = new Sector;
1024 sector->set_name(name);
1026 sector->add_object(new TileMap(LAYER_BACKGROUNDTILES, false, width, height));
1027 sector->add_object(new TileMap(LAYER_TILES, true, width, height));
1028 sector->add_object(new TileMap(LAYER_FOREGROUNDTILES, false, width, height));
1029 sector->add_object(new Camera(sector));
1030 sector->update_game_objects();