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()
236 while(SDL_PollEvent(&event))
238 Menu* menu = Menu::current();
243 if(menu == main_menu)
245 switch (main_menu->check())
248 Menu::set_current(0);
255 else if(menu == create_subset_menu)
257 // activate or deactivate Create button if any filename as been specified
258 if(create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input[0] == '\0')
259 create_subset_menu->get_item_by_id(MN_ID_CREATE_SUBSET).kind = MN_DEACTIVE;
261 create_subset_menu->get_item_by_id(MN_ID_CREATE_SUBSET).kind = MN_ACTION;
263 if(create_subset_menu->check() == MN_ID_CREATE_SUBSET)
264 { // applying settings:
265 std::string subset_name = create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input;
266 LevelSubset::create(subset_name);
269 level_subset = new LevelSubset();
270 level_subset->load(create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input);
272 level_subset->title = create_subset_menu->get_item_by_id(MN_ID_TITLE_SUBSET).input;
273 level_subset->description = create_subset_menu->get_item_by_id(MN_ID_DESCRIPTION_SUBSET).input;
274 //FIXME: generate better level filenames
275 level_subset->add_level(subset_name+'/'+"new_level.stl");
276 Level* newlevel = new Level();
277 newlevel->add_sector(create_sector("main", 25, 19));
278 newlevel->save(level_subset->get_level_filename(0));
279 level_subset->save();
283 create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).change_input("");
284 create_subset_menu->get_item_by_id(MN_ID_TITLE_SUBSET).change_input("");
285 create_subset_menu->get_item_by_id(MN_ID_DESCRIPTION_SUBSET).change_input("");
288 else if(menu == subset_menu)
290 int i = subset_menu->check();
293 std::set<std::string>::iterator it = level_subsets.begin();
294 for(int t = 0; t < i; t++)
296 load_level_subset(*it);
297 Menu::set_current(0);
300 else if(menu == settings_menu)
302 if(settings_menu->check() == MN_ID_APPLY_SETTINGS)
303 { // applying settings:
304 level_changed = true;
306 level->name = settings_menu->get_item_by_id(MN_ID_NAME).input;
307 level->author = settings_menu->get_item_by_id(MN_ID_AUTHOR).input;
309 solids->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()),
310 atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str()));
311 foregrounds->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()),
312 atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str()));
313 backgrounds->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()),
314 atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str()));
316 Menu::set_current(0);
320 // check for events in buttons
321 else if(tiles_board->event(event))
323 std::vector <int> vector;
324 vector.push_back(tiles_board->selected_id());
327 selection.push_back(vector);
330 else if(tiles_layer->event(event))
332 cur_layer = tiles_layer->selected_id();
335 else if(level_options->event(event))
337 switch(level_options->selected_id())
346 Menu::set_current(settings_menu);
349 if(level_nb + 1 < level_subset->get_num_levels())
350 load_level(level_nb + 1);
354 sprintf(str,_("Level %d doesn't exist. Create it?"), level_nb + 2);
355 if(confirm_dialog(NULL, str))
357 level_subset->add_level("new_level.stl");
358 Level* newlevel = new Level();
359 newlevel->add_sector(create_sector("main", 25, 19));
360 newlevel->save(level_subset->get_level_filename(level_nb + 1));
361 level_subset->save();
362 load_level(level_nb + 1);
366 case BT_PREVIOUS_LEVEL:
367 if(level_nb - 1 >= 0)
368 load_level(level_nb - 1);
371 std::cerr << "next sector.\n";
372 std::cerr << "total sectors: " << level->get_total_sectors() << std::endl;
373 load_sector(level->get_next_sector(sector));
375 case BT_PREVIOUS_SECTOR:
376 std::cerr << "previous sector.\n";
377 load_sector(level->get_previous_sector(sector));
380 level_options->set_unselected();
387 case SDL_MOUSEMOTION:
389 if(SDL_GetMouseState(NULL, NULL)&SDL_BUTTON(SDL_BUTTON_RIGHT))
390 { // movement like in strategy games
391 scroll.x += -1 * event.motion.xrel;
392 scroll.y += -1 * event.motion.yrel;
396 case SDL_MOUSEBUTTONDOWN:
398 if(event.button.button == SDL_BUTTON_LEFT)
400 else if(event.button.button == SDL_BUTTON_MIDDLE)
402 middle_button = true;
403 selection_ini = Vector(event.button.x, event.button.y);
407 case SDL_MOUSEBUTTONUP:
409 if(event.button.button == SDL_BUTTON_LEFT)
411 else if(event.button.button == SDL_BUTTON_MIDDLE)
413 middle_button = false;
414 selection_end = Vector(event.button.x, event.button.y);
416 if(selection_end.x < selection_ini.x)
418 float t = selection_ini.x;
419 selection_ini.x = selection_end.x;
422 if(selection_end.y < selection_ini.y)
424 float t = selection_ini.y;
425 selection_ini.y = selection_end.y;
430 std::vector <int> vector;
432 TileMap* tilemap = 0;
433 if(cur_layer == LAYER_FOREGROUNDTILES)
434 tilemap = foregrounds;
435 else if(cur_layer == LAYER_TILES)
437 else if(cur_layer == LAYER_BACKGROUNDTILES)
438 tilemap = backgrounds;
440 for(int x = 0; x < (int)((selection_end.x - selection_ini.x)*zoom / 32) + 1; x++)
443 for(int y = 0; y < (int)((selection_end.y - selection_ini.y)*zoom / 32) + 1; y++)
445 vector.push_back(tilemap->get_tile(x +
446 (int)(((selection_ini.x+scroll.x)*zoom)/32),
447 y + (int)(((selection_ini.y+scroll.y)*zoom)/32))->getID());
449 selection.push_back(vector);
454 case SDL_KEYDOWN: // key pressed
455 switch(event.key.keysym.sym)
458 Menu::set_current(main_menu);
460 /* scrolling related events: */
465 scroll.x = sector->solids->get_height()*32 - screen->w;
498 show_grid = !show_grid;
505 case SDL_QUIT: // window closed
516 void LevelEditor::action()
518 mouse_cursor->set_state(MC_NORMAL);
519 if(tiles_board->is_hover() || tiles_layer->is_hover() || level_options->is_hover())
520 mouse_cursor->set_state(MC_LINK);
524 // don't scroll before the start or after the level's end
525 float width = sector->solids->get_width() * 32;
526 float height = sector->solids->get_height() * 32;
528 if(scroll.x < -screen->w/2)
529 scroll.x = -screen->w/2;
530 if(scroll.x > width - screen->w/2)
531 scroll.x = width - screen->w/2;
532 if(scroll.y < -screen->h/2)
533 scroll.y = -screen->h/2;
534 if(scroll.y > height - screen->h/2)
535 scroll.y = height - screen->h/2;
537 // set camera translation, since BadGuys like it
538 sector->camera->set_scrolling((int)scroll.x, (int)scroll.y);
540 if(left_button && mouse_moved)
541 for(unsigned int x = 0; x < selection.size(); x++)
542 for(unsigned int y = 0; y < selection[x].size(); y++)
543 change((int)(scroll.x + event.button.x) + (x*32),
544 (int)(scroll.y + event.button.y) + (y*32), selection[x][y],
549 #define FADING_TIME .6
551 void LevelEditor::draw(DrawingContext& context)
553 context.draw_text(white_text, _("Level Editor"), Vector(10, 5), LEFT_ALLIGN, LAYER_GUI);
554 mouse_cursor->draw(context);
556 // draw a filled background
557 context.draw_filled_rect(Vector(0,0), Vector(screen->w,screen->h), Color(60,60,60), LAYER_BACKGROUND0-1);
559 if(level_name_timer.check())
561 context.push_transform();
562 if(level_name_timer.get_timeleft() < FADING_TIME)
563 context.set_alpha(int(level_name_timer.get_timeleft() * 255 / FADING_TIME));
565 context.draw_text(gold_text, level->name, Vector(screen->w/2, 30), CENTER_ALLIGN, LAYER_GUI);
569 sprintf(str, "%i/%i", level_nb+1, level_subset->get_num_levels());
570 context.draw_text(gold_text, str, Vector(screen->w/2, 50), CENTER_ALLIGN, LAYER_GUI);
573 context.pop_transform();
576 context.draw_text(white_small_text, _("F1 for help"), Vector(5, 510), LEFT_ALLIGN, LAYER_GUI-10);
578 context.draw_text(white_small_text, _("Choose a level subset"), Vector(5, 510), LEFT_ALLIGN, LAYER_GUI-10);
580 Menu* menu = Menu::current();
585 tiles_board->draw(context);
586 tiles_layer->draw(context);
587 level_options->draw(context);
595 context.set_drawing_effect(SEMI_TRANSPARENT);
599 if(selection[0][0] == 0 && selection.size() == 1)
600 context.draw_surface(img_rubber_bt, Vector(event.button.x - 8,
601 event.button.y - 8), LAYER_GUI-2);
602 else if(selection[0][0] >= gameobjs_first_id || selection[0][0] < 0)
604 // FIXME: this should draw an object image near cursor
606 int id = selection[0][0];
608 if(id == OBJ_TRAMPOLINE)
609 context.draw_surface(img_trampoline[0].get_frame(0), Vector(event.button.x - 8,
610 event.button.y - 8), LAYER_GUI-2);
611 else if(id == OBJ_FLYING_PLATFORM)
612 context.draw_surface(img_flying_platform->get_frame(0), Vector(event.button.x - 8,
613 event.button.y - 8), LAYER_GUI-2);
616 /*context.draw_surface(door->get_frame(0), Vector(event.button.x - 8,
617 event.button.y - 8), LAYER_GUI-2);*/
621 BadGuyKind kind = BadGuyKind((-id)-1);
622 BadGuy badguy(kind, 0,0);
623 badguy.activate(LEFT);
624 Surface *img = badguy.get_image();
626 context.draw_surface(img, Vector(event.button.x - 8,
627 event.button.y - 8), LAYER_GUI-2);
633 for(unsigned int x = 0; x < selection.size(); x++)
634 for(unsigned int y = 0; y < selection[x].size(); y++) {
635 const Tile* tile = tile_manager->get(selection[x][y]);
637 Vector(event.button.x + x*32 - 8, event.button.y + y*32 - 8),
642 context.set_drawing_effect(NONE_EFFECT);
645 context.draw_filled_rect(Vector(std::min((int)selection_ini.x, (int)event.button.x)*zoom,
646 std::min((int)selection_ini.y, (int)event.button.y))*zoom,
647 Vector(abs(event.button.x - (int)selection_ini.x)*zoom,
648 abs(event.button.y - (int)selection_ini.y)*zoom),
649 Color(170,255,170,128), LAYER_GUI-2);
653 for(int x = 0; x < screen->w / (32*zoom); x++)
655 int pos = (int)(x*32*zoom) - ((int)scroll.x % 32);
656 context.draw_filled_rect(Vector (pos, 0), Vector(1, screen->h),
657 Color(225, 225, 225), LAYER_GUI-50);
659 for(int y = 0; y < screen->h / (32*zoom); y++)
661 int pos = (int)(y*32*zoom) - ((int)scroll.y % 32);
662 context.draw_filled_rect(Vector (0, pos), Vector(screen->w, 1),
663 Color(225, 225, 225), LAYER_GUI-50);
667 context.push_transform();
668 context.set_translation(scroll);
669 context.set_zooming(zoom);
671 for(Sector::GameObjects::iterator i = sector->gameobjects.begin(); i != sector->gameobjects.end(); ++i)
673 TileMap* tilemap = dynamic_cast<TileMap*> (*i);
675 { // draw the non-selected tiles semi-transparently
676 context.push_transform();
678 if(tilemap->get_layer() != cur_layer)
679 context.set_drawing_effect(SEMI_TRANSPARENT);
682 context.pop_transform();
685 Background* background = dynamic_cast<Background*> (*i);
687 { // don't resize background
688 context.push_transform();
689 context.set_zooming(1.0);
691 context.pop_transform();
697 context.pop_transform();
700 context.draw_filled_rect(Vector(0,0), Vector(screen->w,screen->h),Color(0,0,0), LAYER_BACKGROUND0);
702 context.do_drawing();
705 void LevelEditor::load_level_subset(std::string filename)
708 level_subset = new LevelSubset();
709 level_subset->load(filename.c_str());
713 void LevelEditor::load_level(std::string filename)
717 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
723 level_filename = filename;
727 level->load(filename);
730 level_name_timer.start(3000);
731 scroll.x = scroll.y = 0;
732 level_changed = false;
734 settings_menu->get_item_by_id(MN_ID_NAME).change_input(level->name.c_str());
735 settings_menu->get_item_by_id(MN_ID_AUTHOR).change_input(level->author.c_str());
738 void LevelEditor::load_level(int nb)
742 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
749 level_filename = level_subset->get_level_filename(level_nb);
751 load_level(level_filename);
754 void LevelEditor::load_sector(std::string name)
757 sector = level->get_sector(sector_name);
759 Termination::abort("Level has no " + sector_name + " sector.", "");
764 void LevelEditor::load_sector(Sector* sector_)
768 if(!confirm_dialog(NULL, _("No more sectors exist. Create another?")))
770 sector_ = create_sector("new_sector",25,19);
771 level->add_sector(sector_);
776 /* Load sector stuff */
778 sector->update_game_objects();
780 foregrounds = solids = backgrounds = 0;
781 /* Point foregrounds, backgrounds, solids to its layer */
782 for(Sector::GameObjects::iterator i = sector->gameobjects.begin(); i != sector->gameobjects.end(); i++)
784 TileMap* tilemap = dynamic_cast<TileMap*> (*i);
787 if(tilemap->get_layer() == LAYER_FOREGROUNDTILES)
788 foregrounds = tilemap;
789 else if(tilemap->get_layer() == LAYER_TILES)
791 else if(tilemap->get_layer() == LAYER_BACKGROUNDTILES)
792 backgrounds = tilemap;
798 TileMap* tilemap = new TileMap(LAYER_FOREGROUNDTILES, false, solids->get_width(), solids->get_height());
799 sector->add_object(tilemap);
800 sector->update_game_objects();
804 TileMap* tilemap = new TileMap(LAYER_BACKGROUNDTILES, false, solids->get_width(), solids->get_height());
805 sector->add_object(tilemap);
806 sector->update_game_objects();
810 sprintf(str, "%i", solids->get_width());
811 settings_menu->get_item_by_id(MN_ID_WIDTH).change_input(str);
812 sprintf(str, "%i", solids->get_height());
813 settings_menu->get_item_by_id(MN_ID_HEIGHT).change_input(str);
816 void LevelEditor::save_level()
818 level->save(level_filename);
819 level_changed = false;
822 void LevelEditor::test_level()
825 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
831 GameSession session(level_filename, ST_GL_TEST);
833 // player_status.reset();
834 SoundManager::get()->halt_music();
837 void LevelEditor::change(int x, int y, int newtile, int layer)
840 // find the tilemap of the current layer, and then change the tile
841 if(x < 0 || (unsigned int)x >= sector->solids->get_width()*32 ||
842 y < 0 || (unsigned int)y >= sector->solids->get_height()*32)
845 level_changed = true;
849 // no need to do this for normal view (no zoom)
850 x = (int)(x * (zoom*32) / 32);
851 y = (int)(y * (zoom*32) / 32);
854 if(newtile >= gameobjs_first_id) // add object
856 // remove an active tile or object that might be there
857 change(x, y, 0, LAYER_TILES);
860 GameObject* object = 0;
861 for(Factories::iterator i = object_factories->begin(); i !=
862 object_factories->end(); ++i) {
863 if(id == newtile - gameobjs_first_id) {
864 object = i->second->create_object(Vector(x, y));
870 sector->add_object(object);
871 sector->update_game_objects();
873 } else if(cur_layer == LAYER_FOREGROUNDTILES) {
874 foregrounds->change(x/32, y/32, newtile);
875 } else if(cur_layer == LAYER_TILES) {
876 // remove a bad guy if it's there
877 // we /32 in order to round numbers
878 for(Sector::GameObjects::iterator i = sector->gameobjects.begin();
879 i < sector->gameobjects.end(); i++) {
880 // FIXME: if there is a game objs in here, remove it!
882 BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
884 if((int)badguy->base.x/32 == x/32 && (int)badguy->base.y/32 == y/32)
885 sector->gameobjects.erase(i);
886 Trampoline* trampoline = dynamic_cast<Trampoline*> (*i);
889 if((int)trampoline->base.x/32 == x/32 && (int)trampoline->base.y/32 == y/32)
890 sector->gameobjects.erase(i);
892 FlyingPlatform* flying_platform = dynamic_cast<FlyingPlatform*> (*i);
894 if((int)flying_platform->base.x/32 == x/32 && (int)flying_platform->base.y/32 == y/32)
895 sector->gameobjects.erase(i);
896 Door* door = dynamic_cast<Door*> (*i);
898 if((int)door->get_area().x/32 == x/32 && (int)door->get_area().y/32 == y/32)
899 sector->gameobjects.erase(i);
902 sector->update_game_objects();
903 solids->change(x/32, y/32, newtile);
904 } else if(cur_layer == LAYER_BACKGROUNDTILES)
905 backgrounds->change(x/32, y/32, newtile);
908 void LevelEditor::show_help()
910 DrawingContext context;
912 bool show_grid_t = show_grid;
914 mouse_cursor->set_state(MC_HIDE);
918 const char *text1[] = {
919 _("This is the built-in level editor. Its aim is to be intuitive\n"
920 "and simple to use, so it should be pretty straightforward.\n"
922 "To open a level, first you'll have to select a level subset from\n"
923 "the menu (or create your own).\n"
924 "A level subset is basically a collection of levels.\n"
925 "They can then be played from the Contrib menu.\n"
927 "To access the menu from the level editor, just press Esc.\n"
929 "You are currently looking at the level. To scroll it, just\n"
930 "press the right mouse button and drag the mouse. It will move like\n"
932 "You can also use the arrow keys and Page Up/Down.\n"
934 "'+' and '-' keys can be used to zoom the level in/out.\n"
936 "You probably already noticed those floating groups of buttons.\n"
937 "Each one serves a different purpose. To select a certain button\n"
938 "just press the Left mouse button on it. A few buttons have key\n"
939 "shortcuts. You can find them by pressing the Right mouse button on\n"
940 "a button. That will also show what that button does.\n"
941 "Groups of buttons can also be moved around by just dragging them,\n"
942 "while pressing the Left mouse button.\n"
944 "Let's learn a bit of what each group of buttons does, shall we?\n"
946 "To starting putting tiles and objects around use the bigger group\n"
947 "of buttons. Each button is a different tile. To put it on the level,\n"
948 "just press it and then left click in the level.\n"
949 "You can also copy tiles from the level by using the middle mouse button.\n"
950 "Use the mouse wheel to scroll that group of buttons. You will find\n"
951 "enemies and game objects in the bottom.\n")
954 const char *text2[] = {
955 _("The Foreground/Interactive/Background buttons may be used to\n"
956 "see and edit the respective layer. Levels have three tiles layers:\n"
957 "Foreground - tiles are drawn on top of everything and have no contact\n"
959 "Interactive - these are the tiles that have contact with the player.\n"
960 "Background - tiles are drawn underneath everything and have no contact\n"
962 "The unselected layers will be drawn semi-transparently.\n"
964 "Last, but not least, the group of buttons that's left serves\n"
965 "to do related actions with the level.\n"
966 "From left to right:\n"
967 "Mini arrows - can be used to choose other sectors.\n"
968 "Sectors are mini-levels, so to speak, that can be accessed using a door.\n"
969 "Big arrows - choose other level in the same level subset.\n"
970 "Diskette - save the level\n"
971 "Tux - test the level\n"
972 "Tools - set a few settings for the level, including resizing it.\n"
974 "We have reached the end of this Howto.\n"
976 "Don't forget to send us a few cool levels. :)\n"
979 " SuperTux development team\n"
981 "PS: If you are looking for something more powerful, you might like to\n"
982 "try FlexLay. FlexLay is a level editor that supports several games,\n"
983 "including SuperTux. It is an independent project.\n"
984 "Webpage: http://pingus.seul.org/~grumbel/flexlay/")
987 const char **text[] = { text1, text2 };
991 for(unsigned int i = 0; i < sizeof(text) / sizeof(text[0]); i++)
995 context.draw_text(blue_text, _("- Level Editor's Help -"), Vector(screen->w/2, 60), CENTER_ALLIGN, LAYER_GUI);
997 context.draw_text(white_small_text, *text[i], Vector(20, 120), LEFT_ALLIGN, LAYER_GUI);
999 sprintf(str,_("Press any key to continue - Page %d/%d"), i+1, sizeof(text) / sizeof(text[0]));
1000 context.draw_text(gold_text, str, Vector(screen->w/2, screen->h-60), CENTER_ALLIGN, LAYER_GUI);
1002 context.do_drawing();
1008 done = wait_for_event(event);
1013 show_grid = show_grid_t;
1014 mouse_cursor->set_state(MC_NORMAL);
1018 LevelEditor::create_sector(const std::string& name, size_t width, size_t height)
1020 Sector* sector = new Sector;
1021 sector->set_name(name);
1023 sector->add_object(new TileMap(LAYER_BACKGROUNDTILES, false, width, height));
1024 sector->add_object(new TileMap(LAYER_TILES, true, width, height));
1025 sector->add_object(new TileMap(LAYER_FOREGROUNDTILES, false, width, height));
1026 sector->add_object(new Camera(sector));
1027 sector->update_game_objects();