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 ***************************************************************************/
21 #include "gui/mousecursor.h"
23 #include "gui/button.h"
24 #include "audio/sound_manager.h"
25 #include "app/gettext.h"
26 #include "app/setup.h"
27 #include "app/globals.h"
28 #include "special/sprite.h"
29 #include "leveleditor.h"
30 #include "resources.h"
33 #include "tile_manager.h"
35 #include "background.h"
42 LevelEditor::LevelEditor()
47 global_frame_counter = 0;
48 frame_timer.init(true);
49 level_name_timer.init(true);
50 selection_end = selection_ini = Vector(0,0);
51 left_button = middle_button = mouse_moved = false;
55 cur_layer = LAYER_TILES;
56 level_changed = false;
62 level_subsets = FileSystem::dsubdirs("/levels", "info");
63 subset_menu = new Menu();
64 subset_menu->additem(MN_LABEL,_("Load Subset"),0,0);
65 subset_menu->additem(MN_HL,"",0,0);
67 for(std::set<std::string>::iterator it = level_subsets.begin(); it != level_subsets.end(); ++it, ++i)
68 subset_menu->additem(MN_ACTION, (*it),0,0,i);
69 subset_menu->additem(MN_HL,"",0,0);
70 subset_menu->additem(MN_BACK,_("Back"),0,0);
72 create_subset_menu = new Menu();
73 create_subset_menu->additem(MN_LABEL,_("New Level Subset"),0,0);
74 create_subset_menu->additem(MN_HL,"",0,0);
75 create_subset_menu->additem(MN_TEXTFIELD,_("Filename "),0,0,MN_ID_FILENAME_SUBSET);
76 create_subset_menu->additem(MN_TEXTFIELD,_("Title "),0,0,MN_ID_TITLE_SUBSET);
77 create_subset_menu->additem(MN_TEXTFIELD,_("Description"),0,0,MN_ID_DESCRIPTION_SUBSET);
78 create_subset_menu->additem(MN_ACTION,_("Create"),0,0, MN_ID_CREATE_SUBSET);
79 create_subset_menu->additem(MN_HL,"",0,0);
80 create_subset_menu->additem(MN_BACK,_("Back"),0,0);
82 main_menu = new Menu();
83 main_menu->additem(MN_LABEL,_("Level Editor Menu"),0,0);
84 main_menu->additem(MN_HL,"",0,0);
85 main_menu->additem(MN_ACTION,_("Return to Level Editor"),0,0,MN_ID_RETURN);
86 main_menu->additem(MN_GOTO,_("Create Level Subset"),0,create_subset_menu);
87 main_menu->additem(MN_GOTO,_("Load Level Subset"),0,subset_menu);
88 main_menu->additem(MN_HL,"",0,0);
89 main_menu->additem(MN_ACTION,_("Quit Level Editor"),0,0,MN_ID_QUIT);
91 settings_menu = new Menu();
92 settings_menu->additem(MN_LABEL,_("Level Settings"),0,0);
93 settings_menu->additem(MN_HL,"",0,0);
94 settings_menu->additem(MN_TEXTFIELD,_("Name "),0,0,MN_ID_NAME);
95 settings_menu->additem(MN_TEXTFIELD,_("Author "),0,0,MN_ID_AUTHOR);
96 settings_menu->additem(MN_NUMFIELD, _("Width "),0,0,MN_ID_WIDTH);
97 settings_menu->additem(MN_NUMFIELD, _("Height "),0,0,MN_ID_HEIGHT);
98 settings_menu->additem(MN_HL,"",0,0);
99 settings_menu->additem(MN_ACTION,_("Apply"),0,0,MN_ID_APPLY_SETTINGS);
101 /* Creating button groups */
104 tiles_board = new ButtonGroup(Vector(screen->w - 140, 100),
105 Vector(32,32), Vector(4,8));
107 TileManager* tilemanager = TileManager::instance();
109 tiles_board->add_button(Button(img_rubber_bt, _("Eraser"), SDLKey(SDLK_DELETE)), 0);
110 for(unsigned int id = 1; id < tilemanager->total_ids(); id++)
112 Tile* tile = tilemanager->get(id);
117 if(tile->editor_images.size())
118 surface = tile->editor_images[0];
119 else if(tile->images.size())
120 surface = tile->images[0];
124 Button button = Button(surface, "", SDLKey(0));
125 tiles_board->add_button(button, id);
127 for(int i = 0; i < NUM_BadGuyKinds; i++)
129 // filter bomb, since it is only for internal use, not for levels
133 BadGuyKind kind = BadGuyKind(i);
134 BadGuy badguy(kind, 0,0);
135 badguy.activate(LEFT);
137 Surface *img = badguy.get_image();
138 tiles_board->add_button(Button(img, "", SDLKey(SDLK_1+i)), -(i+1));
141 tiles_board->add_button(Button(img_trampoline[0].get_frame(0), _("Trampoline"), SDLKey(0)), OBJ_TRAMPOLINE);
142 tiles_board->add_button(Button(img_flying_platform->get_frame(0), _("Flying Platform"), SDLKey(0)), OBJ_FLYING_PLATFORM);
143 tiles_board->add_button(Button(door->get_frame(0), _("Door"), SDLKey(0)), OBJ_DOOR);
145 tiles_layer = new ButtonGroup(Vector(12, screen->h-64), Vector(80,20), Vector(1,3));
146 tiles_layer->add_button(Button(img_foreground_bt, _("Edtit foreground tiles"),
147 SDLK_F10), LAYER_FOREGROUNDTILES);
148 tiles_layer->add_button(Button(img_interactive_bt, _("Edit interactive tiles"),
149 SDLK_F11), LAYER_TILES, true);
150 tiles_layer->add_button(Button(img_background_bt, _("Edit background tiles"),
151 SDLK_F12), LAYER_BACKGROUNDTILES);
153 level_options = new ButtonGroup(Vector(screen->w-164, screen->h-36), Vector(32,32), Vector(5,1));
154 level_options->add_pair_of_buttons(Button(img_next_sector_bt, _("Next sector"), SDLKey(0)), BT_NEXT_SECTOR,
155 Button(img_previous_sector_bt, _("Prevous sector"), SDLKey(0)), BT_PREVIOUS_SECTOR);
156 level_options->add_pair_of_buttons(Button(img_next_level_bt, _("Next level"), SDLKey(0)), BT_NEXT_LEVEL,
157 Button(img_previous_level_bt, _("Prevous level"), SDLKey(0)), BT_PREVIOUS_LEVEL);
158 level_options->add_button(Button(img_save_level_bt, _("Save level"), SDLK_F5), BT_LEVEL_SAVE);
159 level_options->add_button(Button(img_test_level_bt, _("Test level"), SDLK_F6), BT_LEVEL_TEST);
160 level_options->add_button(Button(img_setup_level_bt, _("Setup level"), SDLK_F7), BT_LEVEL_SETUP);
163 LevelEditor::~LevelEditor()
169 delete level_options;
172 delete create_subset_menu;
174 delete settings_menu;
180 void LevelEditor::load_buttons_gfx()
182 img_foreground_bt = new Surface(datadir + "/images/leveleditor/foreground.png", true);
183 img_interactive_bt = new Surface(datadir + "/images/leveleditor/interactive.png", true);
184 img_background_bt = new Surface(datadir + "/images/leveleditor/background.png", true);
186 img_save_level_bt = new Surface(datadir + "/images/leveleditor/save-level.png", true);
187 img_test_level_bt = new Surface(datadir + "/images/leveleditor/test-level.png", true);
188 img_setup_level_bt = new Surface(datadir + "/images/leveleditor/setup-level.png", true);
190 img_rubber_bt = new Surface(datadir + "/images/leveleditor/rubber.png", true);
192 img_previous_level_bt = new Surface(datadir + "/images/leveleditor/previous-level.png", true);
193 img_next_level_bt = new Surface(datadir + "/images/leveleditor/next-level.png", true);
194 img_previous_sector_bt = new Surface(datadir + "/images/leveleditor/previous-sector.png", true);
195 img_next_sector_bt = new Surface(datadir + "/images/leveleditor/next-sector.png", true);
198 void LevelEditor::free_buttons_gfx()
200 delete img_foreground_bt;
201 delete img_interactive_bt;
202 delete img_background_bt;
204 delete img_save_level_bt;
205 delete img_test_level_bt;
206 delete img_setup_level_bt;
208 delete img_rubber_bt;
210 delete img_previous_level_bt;
211 delete img_next_level_bt;
212 delete img_previous_sector_bt;
213 delete img_next_sector_bt;
216 void LevelEditor::run(const std::string filename)
218 SoundManager::get()->halt_music();
219 Menu::set_current(0);
221 DrawingContext context;
223 if(!filename.empty())
226 load_level(filename);
229 Menu::set_current(main_menu);
231 mouse_cursor->set_state(MC_NORMAL);
242 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
246 void LevelEditor::events()
250 while(SDL_PollEvent(&event))
252 Menu* menu = Menu::current();
257 if(menu == main_menu)
259 switch (main_menu->check())
262 Menu::set_current(0);
269 else if(menu == create_subset_menu)
271 // activate or deactivate Create button if any filename as been specified
272 if(create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input[0] == '\0')
273 create_subset_menu->get_item_by_id(MN_ID_CREATE_SUBSET).kind = MN_DEACTIVE;
275 create_subset_menu->get_item_by_id(MN_ID_CREATE_SUBSET).kind = MN_ACTION;
277 if(create_subset_menu->check() == MN_ID_CREATE_SUBSET)
278 { // applying settings:
279 std::string subset_name = create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input;
280 LevelSubset::create(subset_name);
283 level_subset = new LevelSubset();
284 level_subset->load(create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input);
286 level_subset->title = create_subset_menu->get_item_by_id(MN_ID_TITLE_SUBSET).input;
287 level_subset->description = create_subset_menu->get_item_by_id(MN_ID_DESCRIPTION_SUBSET).input;
288 //FIXME: generate better level filenames
289 level_subset->add_level(subset_name+'/'+"new_level.stl");
290 Level::create(level_subset->get_level_filename(0));
291 level_subset->save();
295 create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).change_input("");
296 create_subset_menu->get_item_by_id(MN_ID_TITLE_SUBSET).change_input("");
297 create_subset_menu->get_item_by_id(MN_ID_DESCRIPTION_SUBSET).change_input("");
300 else if(menu == subset_menu)
302 int i = subset_menu->check();
305 std::set<std::string>::iterator it = level_subsets.begin();
306 for(int t = 0; t < i; t++)
308 load_level_subset(*it);
309 Menu::set_current(0);
312 else if(menu == settings_menu)
314 if(settings_menu->check() == MN_ID_APPLY_SETTINGS)
315 { // applying settings:
316 level_changed = true;
318 level->name = settings_menu->get_item_by_id(MN_ID_NAME).input;
319 level->author = settings_menu->get_item_by_id(MN_ID_AUTHOR).input;
321 solids->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()),
322 atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str()));
323 foregrounds->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()),
324 atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str()));
325 backgrounds->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()));
328 Menu::set_current(0);
332 // check for events in buttons
333 else if(tiles_board->event(event))
335 std::vector <int> vector;
336 vector.push_back(tiles_board->selected_id());
339 selection.push_back(vector);
342 else if(tiles_layer->event(event))
344 cur_layer = tiles_layer->selected_id();
347 else if(level_options->event(event))
349 switch(level_options->selected_id())
358 Menu::set_current(settings_menu);
361 if(level_nb + 1 < level_subset->get_num_levels())
362 load_level(level_nb + 1);
366 sprintf(str,_("Level %d doesn't exist. Create it?"), level_nb + 2);
367 if(confirm_dialog(NULL, str))
369 level_subset->add_level("new_level.stl");
370 Level::create(level_subset->get_level_filename(level_nb + 1));
371 level_subset->save();
372 load_level(level_nb + 1);
376 case BT_PREVIOUS_LEVEL:
377 if(level_nb - 1 >= 0)
378 load_level(level_nb - 1);
381 std::cerr << "next sector.\n";
382 std::cerr << "total sectors: " << level->get_total_sectors() << std::endl;
383 load_sector(level->get_next_sector(sector));
385 case BT_PREVIOUS_SECTOR:
386 std::cerr << "previous sector.\n";
387 load_sector(level->get_previous_sector(sector));
390 level_options->set_unselected();
397 case SDL_MOUSEMOTION:
399 if(SDL_GetMouseState(NULL, NULL)&SDL_BUTTON(SDL_BUTTON_RIGHT))
400 { // movement like in strategy games
401 scroll.x += -1 * event.motion.xrel;
402 scroll.y += -1 * event.motion.yrel;
406 case SDL_MOUSEBUTTONDOWN:
408 if(event.button.button == SDL_BUTTON_LEFT)
410 else if(event.button.button == SDL_BUTTON_MIDDLE)
412 middle_button = true;
413 selection_ini = Vector(event.button.x, event.button.y);
417 case SDL_MOUSEBUTTONUP:
419 if(event.button.button == SDL_BUTTON_LEFT)
421 else if(event.button.button == SDL_BUTTON_MIDDLE)
423 middle_button = false;
424 selection_end = Vector(event.button.x, event.button.y);
426 if(selection_end.x < selection_ini.x)
428 float t = selection_ini.x;
429 selection_ini.x = selection_end.x;
432 if(selection_end.y < selection_ini.y)
434 float t = selection_ini.y;
435 selection_ini.y = selection_end.y;
440 std::vector <int> vector;
442 TileMap* tilemap = 0;
443 if(cur_layer == LAYER_FOREGROUNDTILES)
444 tilemap = foregrounds;
445 else if(cur_layer == LAYER_TILES)
447 else if(cur_layer == LAYER_BACKGROUNDTILES)
448 tilemap = backgrounds;
450 for(int x = 0; x < (int)((selection_end.x - selection_ini.x)*zoom / 32) + 1; x++)
453 for(int y = 0; y < (int)((selection_end.y - selection_ini.y)*zoom / 32) + 1; y++)
455 vector.push_back(tilemap->get_tile(x +
456 (int)(((selection_ini.x+scroll.x)*zoom)/32),
457 y + (int)(((selection_ini.y+scroll.y)*zoom)/32))->id);
459 selection.push_back(vector);
464 case SDL_KEYDOWN: // key pressed
465 switch(event.key.keysym.sym)
468 Menu::set_current(main_menu);
470 /* scrolling related events: */
475 scroll.x = sector->solids->get_height()*32 - screen->w;
508 show_grid = !show_grid;
515 case SDL_QUIT: // window closed
526 void LevelEditor::action()
528 mouse_cursor->set_state(MC_NORMAL);
529 if(tiles_board->is_hover() || tiles_layer->is_hover() || level_options->is_hover())
530 mouse_cursor->set_state(MC_LINK);
534 if(!frame_timer.check())
536 frame_timer.start(25);
537 ++global_frame_counter;
540 // don't scroll before the start or after the level's end
541 float width = sector->solids->get_width() * 32;
542 float height = sector->solids->get_height() * 32;
544 if(scroll.x < -screen->w/2)
545 scroll.x = -screen->w/2;
546 if(scroll.x > width - screen->w/2)
547 scroll.x = width - screen->w/2;
548 if(scroll.y < -screen->h/2)
549 scroll.y = -screen->h/2;
550 if(scroll.y > height - screen->h/2)
551 scroll.y = height - screen->h/2;
553 // set camera translation, since BadGuys like it
554 sector->camera->set_scrolling((int)scroll.x, (int)scroll.y);
556 if(left_button && mouse_moved)
557 for(unsigned int x = 0; x < selection.size(); x++)
558 for(unsigned int y = 0; y < selection[x].size(); y++)
559 change((int)(scroll.x + event.button.x) + (x*32),
560 (int)(scroll.y + event.button.y) + (y*32), selection[x][y],
565 #define FADING_TIME 600
567 void LevelEditor::draw(DrawingContext& context)
569 context.draw_text(white_text, _("Level Editor"), Vector(10, 5), LEFT_ALLIGN, LAYER_GUI);
570 mouse_cursor->draw(context);
572 // draw a filled background
573 context.draw_filled_rect(Vector(0,0), Vector(screen->w,screen->h), Color(60,60,60), LAYER_BACKGROUND0-1);
575 if(level_name_timer.check())
577 context.push_transform();
578 if(level_name_timer.get_left() < FADING_TIME)
579 context.set_alpha(level_name_timer.get_left() * 255 / FADING_TIME);
581 context.draw_text(gold_text, level->name, Vector(screen->w/2, 30), CENTER_ALLIGN, LAYER_GUI);
585 sprintf(str, "%i/%i", level_nb+1, level_subset->get_num_levels());
586 context.draw_text(gold_text, str, Vector(screen->w/2, 50), CENTER_ALLIGN, LAYER_GUI);
589 context.pop_transform();
592 context.draw_text(white_small_text, _("F1 for help"), Vector(5, 510), LEFT_ALLIGN, LAYER_GUI-10);
594 context.draw_text(white_small_text, _("Choose a level subset"), Vector(5, 510), LEFT_ALLIGN, LAYER_GUI-10);
596 Menu* menu = Menu::current();
601 tiles_board->draw(context);
602 tiles_layer->draw(context);
603 level_options->draw(context);
611 context.set_drawing_effect(SEMI_TRANSPARENT);
615 if(selection[0][0] == 0 && selection.size() == 1)
616 context.draw_surface(img_rubber_bt, Vector(event.button.x - 8,
617 event.button.y - 8), LAYER_GUI-2);
618 else if(selection[0][0] < 0)
620 int id = selection[0][0];
622 if(id == OBJ_TRAMPOLINE)
623 context.draw_surface(img_trampoline[0].get_frame(0), Vector(event.button.x - 8,
624 event.button.y - 8), LAYER_GUI-2);
625 else if(id == OBJ_FLYING_PLATFORM)
626 context.draw_surface(img_flying_platform->get_frame(0), Vector(event.button.x - 8,
627 event.button.y - 8), LAYER_GUI-2);
628 else if(id == OBJ_DOOR)
629 context.draw_surface(door->get_frame(0), Vector(event.button.x - 8,
630 event.button.y - 8), LAYER_GUI-2);
633 BadGuyKind kind = BadGuyKind((-id)-1);
634 BadGuy badguy(kind, 0,0);
635 badguy.activate(LEFT);
636 Surface *img = badguy.get_image();
638 context.draw_surface(img, Vector(event.button.x - 8,
639 event.button.y - 8), LAYER_GUI-2);
644 TileManager* tilemanager = TileManager::instance();
645 for(unsigned int x = 0; x < selection.size(); x++)
646 for(unsigned int y = 0; y < selection[x].size(); y++)
647 tilemanager->draw_tile(context, selection[x][y],
648 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_ = Sector::create("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++)
794 BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
796 badguy->activate(LEFT);
798 TileMap* tilemap = dynamic_cast<TileMap*> (*i);
801 if(tilemap->get_layer() == LAYER_FOREGROUNDTILES)
802 foregrounds = tilemap;
803 else if(tilemap->get_layer() == LAYER_TILES)
805 else if(tilemap->get_layer() == LAYER_BACKGROUNDTILES)
806 backgrounds = tilemap;
812 TileMap* tilemap = new TileMap(LAYER_FOREGROUNDTILES, false, solids->get_width(), solids->get_height());
813 sector->add_object(tilemap);
814 sector->update_game_objects();
818 TileMap* tilemap = new TileMap(LAYER_BACKGROUNDTILES, false, solids->get_width(), solids->get_height());
819 sector->add_object(tilemap);
820 sector->update_game_objects();
824 sprintf(str, "%i", solids->get_width());
825 settings_menu->get_item_by_id(MN_ID_WIDTH).change_input(str);
826 sprintf(str, "%i", solids->get_height());
827 settings_menu->get_item_by_id(MN_ID_HEIGHT).change_input(str);
830 void LevelEditor::save_level()
832 level->save(level_filename);
833 level_changed = false;
836 void LevelEditor::test_level()
840 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
846 GameSession session(level_filename, ST_GL_TEST);
848 // player_status.reset();
850 sound_manager->halt_music();
853 void LevelEditor::change(int x, int y, int newtile, int layer)
854 { // find the tilemap of the current layer, and then change the tile
855 if(x < 0 || (unsigned int)x >= sector->solids->get_width()*32 ||
856 y < 0 || (unsigned int)y >= sector->solids->get_height()*32)
859 level_changed = true;
862 { // no need to do this for normal view (no zoom)
863 x = (int)(x * (zoom*32) / 32);
864 y = (int)(y * (zoom*32) / 32);
867 if(newtile < 0) // add object
869 // remove an active tile or object that might be there
870 change(x, y, 0, LAYER_TILES);
872 if(newtile == OBJ_TRAMPOLINE)
873 sector->add_object(new Trampoline(x, y));
874 else if(newtile == OBJ_FLYING_PLATFORM)
875 sector->add_object(new FlyingPlatform(x, y));
876 else if(newtile == OBJ_DOOR)
877 sector->add_object(new Door(x, y));
879 sector->add_bad_guy(x, y, BadGuyKind((-newtile)-1), true);
881 sector->update_game_objects();
883 else if(cur_layer == LAYER_FOREGROUNDTILES)
884 foregrounds->change(x/32, y/32, newtile);
885 else if(cur_layer == LAYER_TILES)
887 // remove a bad guy if it's there
888 // we /32 in order to round numbers
889 for(Sector::GameObjects::iterator i = sector->gameobjects.begin(); i < sector->gameobjects.end(); i++)
891 BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
893 if((int)badguy->base.x/32 == x/32 && (int)badguy->base.y/32 == y/32)
894 sector->gameobjects.erase(i);
895 Trampoline* trampoline = dynamic_cast<Trampoline*> (*i);
898 if((int)trampoline->base.x/32 == x/32 && (int)trampoline->base.y/32 == y/32)
899 sector->gameobjects.erase(i);
901 FlyingPlatform* flying_platform = dynamic_cast<FlyingPlatform*> (*i);
903 if((int)flying_platform->base.x/32 == x/32 && (int)flying_platform->base.y/32 == y/32)
904 sector->gameobjects.erase(i);
905 Door* door = dynamic_cast<Door*> (*i);
907 if((int)door->get_area().x/32 == x/32 && (int)door->get_area().y/32 == y/32)
908 sector->gameobjects.erase(i);
910 sector->update_game_objects();
911 solids->change(x/32, y/32, newtile);
913 else if(cur_layer == LAYER_BACKGROUNDTILES)
914 backgrounds->change(x/32, y/32, newtile);
917 void LevelEditor::show_help()
919 DrawingContext context;
921 bool show_grid_t = show_grid;
923 mouse_cursor->set_state(MC_HIDE);
928 _("This is the built-in level editor. Its aim is to be intuitive\n"
929 "and simple to use, so it should be pretty straightforward.\n"
931 "To open a level, first you'll have to select a level subset from\n"
932 "the menu (or create your own).\n"
933 "A level subset is basically a collection of levels.\n"
934 "They can then be played from the Contrib menu.\n"
936 "To access the menu from the level editor, just press Esc.\n"
938 "You are currently looking at the level. To scroll it, just\n"
939 "press the right mouse button and drag the mouse. It will move like\n"
941 "You can also use the arrow keys and Page Up/Down.\n"
943 "'+' and '-' keys can be used to zoom the level in/out.\n"
945 "You probably already noticed those floating groups of buttons.\n"
946 "Each one serves a different purpose. To select a certain button\n"
947 "just press the Left mouse button on it. A few buttons have key\n"
948 "shortcuts. You can find them by pressing the Right mouse button on\n"
949 "a button. That will also show what that button does.\n"
950 "Groups of buttons can also be moved around by just dragging them,\n"
951 "while pressing the Left mouse button.\n"
953 "Let's learn a bit of what each group of buttons does, shall we?\n"
955 "To starting putting tiles and objects around use the bigger group\n"
956 "of buttons. Each button is a different tile. To put it on the level,\n"
957 "just press it and then left click in the level.\n"
958 "You can also copy tiles from the level by using the middle mouse button.\n"
959 "Use the mouse wheel to scroll that group of buttons. You will find\n"
960 "enemies and game objects in the bottom.\n")
964 _("The Foreground/Interactive/Background buttons may be used to\n"
965 "see and edit the respective layer. Levels have three tiles layers:\n"
966 "Foreground - tiles are drawn on top of everything and have no contact\n"
968 "Interactive - these are the tiles that have contact with the player.\n"
969 "Background - tiles are drawn underneath everything and have no contact\n"
971 "The unselected layers will be drawn semi-transparently.\n"
973 "Last, but not least, the group of buttons that's left serves\n"
974 "to do related actions with the level.\n"
975 "From left to right:\n"
976 "Mini arrows - can be used to choose other sectors.\n"
977 "Sectors are mini-levels, so to speak, that can be accessed using a door.\n"
978 "Big arrows - choose other level in the same level subset.\n"
979 "Diskette - save the level\n"
980 "Tux - test the level\n"
981 "Tools - set a few settings for the level, including resizing it.\n"
983 "We have reached the end of this Howto.\n"
985 "Don't forget to send us a few cool levels. :)\n"
988 " SuperTux development team\n"
990 "PS: If you are looking for something more powerful, you might like to\n"
991 "try FlexLay. FlexLay is a level editor that supports several games,\n"
992 "including SuperTux. It is an independent project.\n"
993 "Webpage: http://pingus.seul.org/~grumbel/flexlay/")
996 char **text[] = { text1, text2 };
1000 for(unsigned int i = 0; i < sizeof(text) / sizeof(text[0]); i++)
1004 context.draw_text(blue_text, _("- Level Editor's Help -"), Vector(screen->w/2, 60), CENTER_ALLIGN, LAYER_GUI);
1006 context.draw_text(white_small_text, *text[i], Vector(20, 120), LEFT_ALLIGN, LAYER_GUI);
1008 sprintf(str,_("Press any key to continue - Page %d/%d"), i+1, sizeof(text) / sizeof(text[0]));
1009 context.draw_text(gold_text, str, Vector(screen->w/2, screen->h-60), CENTER_ALLIGN, LAYER_GUI);
1011 context.do_drawing();
1017 done = wait_for_event(event);
1022 show_grid = show_grid_t;
1023 mouse_cursor->set_state(MC_NORMAL);