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)
69 std::cerr << "adding entry level subset " << i << " entry: " << (*it) << std::endl;
70 subset_menu->additem(MN_ACTION, (*it),0,0,i);
72 subset_menu->additem(MN_HL,"",0,0);
73 subset_menu->additem(MN_BACK,_("Back"),0,0);
75 create_subset_menu = new Menu();
76 create_subset_menu->additem(MN_LABEL,_("New Level Subset"),0,0);
77 create_subset_menu->additem(MN_HL,"",0,0);
78 create_subset_menu->additem(MN_TEXTFIELD,_("Filename "),0,0,MN_ID_FILENAME_SUBSET);
79 create_subset_menu->additem(MN_TEXTFIELD,_("Title "),0,0,MN_ID_TITLE_SUBSET);
80 create_subset_menu->additem(MN_TEXTFIELD,_("Description"),0,0,MN_ID_DESCRIPTION_SUBSET);
81 create_subset_menu->additem(MN_ACTION,_("Create"),0,0, MN_ID_CREATE_SUBSET);
82 create_subset_menu->additem(MN_HL,"",0,0);
83 create_subset_menu->additem(MN_BACK,_("Back"),0,0);
85 main_menu = new Menu();
86 main_menu->additem(MN_LABEL,_("Level Editor Menu"),0,0);
87 main_menu->additem(MN_HL,"",0,0);
88 main_menu->additem(MN_ACTION,_("Return to Level Editor"),0,0,MN_ID_RETURN);
89 main_menu->additem(MN_GOTO,_("Create Level Subset"),0,create_subset_menu);
90 main_menu->additem(MN_GOTO,_("Load Level Subset"),0,subset_menu);
91 main_menu->additem(MN_HL,"",0,0);
92 main_menu->additem(MN_ACTION,_("Quit Level Editor"),0,0,MN_ID_QUIT);
94 settings_menu = new Menu();
95 settings_menu->additem(MN_LABEL,_("Level Settings"),0,0);
96 settings_menu->additem(MN_HL,"",0,0);
97 settings_menu->additem(MN_TEXTFIELD,_("Name "),0,0,MN_ID_NAME);
98 settings_menu->additem(MN_TEXTFIELD,_("Author "),0,0,MN_ID_AUTHOR);
99 settings_menu->additem(MN_NUMFIELD, _("Width "),0,0,MN_ID_WIDTH);
100 settings_menu->additem(MN_NUMFIELD, _("Height "),0,0,MN_ID_HEIGHT);
101 settings_menu->additem(MN_HL,"",0,0);
102 settings_menu->additem(MN_ACTION,_("Apply"),0,0,MN_ID_APPLY_SETTINGS);
104 /* Creating button groups */
107 tiles_board = new ButtonGroup(Vector(screen->w - 140, 100),
108 Vector(32,32), Vector(4,8));
110 TileManager* tilemanager = TileManager::instance();
112 tiles_board->add_button(Button(img_rubber_bt, _("Eraser"), SDLKey(SDLK_DELETE)), 0);
113 for(unsigned int id = 1; id < tilemanager->total_ids(); id++)
115 Tile* tile = tilemanager->get(id);
120 if(tile->editor_images.size())
121 surface = tile->editor_images[0];
122 else if(tile->images.size())
123 surface = tile->images[0];
127 Button button = Button(surface, "", SDLKey(0));
128 tiles_board->add_button(button, id);
130 for(int i = 0; i < NUM_BadGuyKinds; i++)
132 BadGuyKind kind = BadGuyKind(i);
133 BadGuy badguy(kind, 0,0);
134 badguy.activate(LEFT);
136 Surface *img = badguy.get_image();
137 tiles_board->add_button(Button(img, "", SDLKey(SDLK_1+i)), -(i+1));
140 tiles_board->add_button(Button(img_trampoline[0].get_frame(0), _("Trampoline"), SDLKey(0)), OBJ_TRAMPOLINE);
141 tiles_board->add_button(Button(img_flying_platform->get_frame(0), _("Flying Platform"), SDLKey(0)), OBJ_FLYING_PLATFORM);
142 tiles_board->add_button(Button(door->get_frame(0), _("Door"), SDLKey(0)), OBJ_DOOR);
144 tiles_layer = new ButtonGroup(Vector(12, screen->h-64), Vector(80,20), Vector(1,3));
145 tiles_layer->add_button(Button(img_foreground_bt, _("Edtit foreground tiles"),
146 SDLK_F10), LAYER_FOREGROUNDTILES);
147 tiles_layer->add_button(Button(img_interactive_bt, _("Edit interactive tiles"),
148 SDLK_F11), LAYER_TILES, true);
149 tiles_layer->add_button(Button(img_background_bt, _("Edit background tiles"),
150 SDLK_F12), LAYER_BACKGROUNDTILES);
152 level_options = new ButtonGroup(Vector(screen->w-164, screen->h-36), Vector(32,32), Vector(5,1));
153 level_options->add_pair_of_buttons(Button(img_next_sector_bt, _("Next sector"), SDLKey(0)), BT_NEXT_SECTOR,
154 Button(img_previous_sector_bt, _("Prevous sector"), SDLKey(0)), BT_PREVIOUS_SECTOR);
155 level_options->add_pair_of_buttons(Button(img_next_level_bt, _("Next level"), SDLKey(0)), BT_NEXT_LEVEL,
156 Button(img_previous_level_bt, _("Prevous level"), SDLKey(0)), BT_PREVIOUS_LEVEL);
157 level_options->add_button(Button(img_save_level_bt, _("Save level"), SDLK_F5), BT_LEVEL_SAVE);
158 level_options->add_button(Button(img_test_level_bt, _("Test level"), SDLK_F6), BT_LEVEL_TEST);
159 level_options->add_button(Button(img_setup_level_bt, _("Setup level"), SDLK_F7), BT_LEVEL_SETUP);
162 LevelEditor::~LevelEditor()
168 delete level_options;
171 delete create_subset_menu;
173 delete settings_menu;
179 void LevelEditor::load_buttons_gfx()
181 img_foreground_bt = new Surface(datadir + "/images/leveleditor/foreground.png", true);
182 img_interactive_bt = new Surface(datadir + "/images/leveleditor/interactive.png", true);
183 img_background_bt = new Surface(datadir + "/images/leveleditor/background.png", true);
185 img_save_level_bt = new Surface(datadir + "/images/leveleditor/save-level.png", true);
186 img_test_level_bt = new Surface(datadir + "/images/leveleditor/test-level.png", true);
187 img_setup_level_bt = new Surface(datadir + "/images/leveleditor/setup-level.png", true);
189 img_rubber_bt = new Surface(datadir + "/images/leveleditor/rubber.png", true);
191 img_previous_level_bt = new Surface(datadir + "/images/leveleditor/previous-level.png", true);
192 img_next_level_bt = new Surface(datadir + "/images/leveleditor/next-level.png", true);
193 img_previous_sector_bt = new Surface(datadir + "/images/leveleditor/previous-sector.png", true);
194 img_next_sector_bt = new Surface(datadir + "/images/leveleditor/next-sector.png", true);
197 void LevelEditor::free_buttons_gfx()
199 delete img_foreground_bt;
200 delete img_interactive_bt;
201 delete img_background_bt;
203 delete img_save_level_bt;
204 delete img_test_level_bt;
205 delete img_setup_level_bt;
207 delete img_rubber_bt;
209 delete img_previous_level_bt;
210 delete img_next_level_bt;
211 delete img_previous_sector_bt;
212 delete img_next_sector_bt;
215 void LevelEditor::run(const std::string filename)
217 SoundManager::get()->halt_music();
218 Menu::set_current(0);
220 DrawingContext context;
222 if(!filename.empty())
225 load_level(filename);
228 Menu::set_current(main_menu);
230 mouse_cursor->set_state(MC_NORMAL);
241 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
245 void LevelEditor::events()
249 while(SDL_PollEvent(&event))
251 Menu* menu = Menu::current();
256 if(menu == main_menu)
258 switch (main_menu->check())
261 Menu::set_current(0);
268 else if(menu == create_subset_menu)
270 if(create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input[0] == '\0')
271 create_subset_menu->get_item_by_id(MN_ID_CREATE_SUBSET).kind = MN_DEACTIVE;
272 else if(create_subset_menu->check() == MN_ID_CREATE_SUBSET)
273 { // applying settings:
274 LevelSubset::create(create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input);
277 level_subset = new LevelSubset();
278 level_subset->load(create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input);
280 level_subset->title = create_subset_menu->item[MN_ID_TITLE_SUBSET].input;
281 level_subset->description = create_subset_menu->item[MN_ID_DESCRIPTION_SUBSET].input;
285 create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).change_input("");
286 create_subset_menu->get_item_by_id(MN_ID_TITLE_SUBSET).change_input("");
287 create_subset_menu->get_item_by_id(MN_ID_DESCRIPTION_SUBSET).change_input("");
290 else if(menu == subset_menu)
292 int i = subset_menu->check();
295 std::set<std::string>::iterator it = level_subsets.begin();
296 for(int t = 0; t < i; t++)
298 std::cerr << "load subset level_subsets " << i << ": " << (*it) << std::endl;
299 load_level_subset(*it);
300 Menu::set_current(0);
303 else if(menu == settings_menu)
305 if(settings_menu->check() == MN_ID_APPLY_SETTINGS)
306 { // applying settings:
307 level_changed = true;
309 level->name = settings_menu->get_item_by_id(MN_ID_NAME).input;
310 level->author = settings_menu->get_item_by_id(MN_ID_AUTHOR).input;
312 solids->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 foregrounds->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()));
316 backgrounds->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()),
317 atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str()));
319 Menu::set_current(0);
323 // check for events in buttons
324 else if(tiles_board->event(event))
326 std::vector <int> vector;
327 vector.push_back(tiles_board->selected_id());
330 selection.push_back(vector);
333 else if(tiles_layer->event(event))
335 cur_layer = tiles_layer->selected_id();
338 else if(level_options->event(event))
340 switch(level_options->selected_id())
349 Menu::set_current(settings_menu);
352 if(level_nb+1 < level_subset->get_num_levels())
353 load_level(level_nb + 1);
357 sprintf(str,_("Level %d doesn't exist. Create it?"), level_nb + 1);
358 if(confirm_dialog(NULL, str))
361 level_subset->add_level("new_level.stl");
362 new_lev.save(level_subset->get_level_filename(level_nb + 1));
363 load_level(level_nb);
367 case BT_PREVIOUS_LEVEL:
369 load_level(level_nb - 1);
372 std::cerr << "next sector.\n";
373 std::cerr << "total sectors: " << level->get_total_sectors() << std::endl;
374 load_sector(level->get_next_sector(sector));
376 case BT_PREVIOUS_SECTOR:
377 std::cerr << "previous sector.\n";
378 load_sector(level->get_previous_sector(sector));
381 level_options->set_unselected();
388 case SDL_MOUSEMOTION:
390 if(SDL_GetMouseState(NULL, NULL)&SDL_BUTTON(SDL_BUTTON_RIGHT))
391 { // movement like in strategy games
392 scroll.x += -1 * event.motion.xrel;
393 scroll.y += -1 * event.motion.yrel;
397 case SDL_MOUSEBUTTONDOWN:
399 if(event.button.button == SDL_BUTTON_LEFT)
401 else if(event.button.button == SDL_BUTTON_MIDDLE)
403 middle_button = true;
404 selection_ini = Vector(event.button.x, event.button.y);
408 case SDL_MOUSEBUTTONUP:
410 if(event.button.button == SDL_BUTTON_LEFT)
412 else if(event.button.button == SDL_BUTTON_MIDDLE)
414 middle_button = false;
415 selection_end = Vector(event.button.x, event.button.y);
417 if(selection_end.x < selection_ini.x)
419 float t = selection_ini.x;
420 selection_ini.x = selection_end.x;
423 if(selection_end.y < selection_ini.y)
425 float t = selection_ini.y;
426 selection_ini.y = selection_end.y;
431 std::vector <int> vector;
433 TileMap* tilemap = 0;
434 if(cur_layer == LAYER_FOREGROUNDTILES)
435 tilemap = foregrounds;
436 else if(cur_layer == LAYER_TILES)
438 else if(cur_layer == LAYER_BACKGROUNDTILES)
439 tilemap = backgrounds;
441 for(int x = 0; x < (int)((selection_end.x - selection_ini.x)*zoom / 32) + 1; x++)
444 for(int y = 0; y < (int)((selection_end.y - selection_ini.y)*zoom / 32) + 1; y++)
446 vector.push_back(tilemap->get_tile(x +
447 (int)(((selection_ini.x+scroll.x)*zoom)/32),
448 y + (int)(((selection_ini.y+scroll.y)*zoom)/32))->id);
450 selection.push_back(vector);
455 case SDL_KEYDOWN: // key pressed
456 switch(event.key.keysym.sym)
459 Menu::set_current(main_menu);
461 /* scrolling related events: */
466 scroll.x = sector->solids->get_height()*32 - screen->w;
499 show_grid = !show_grid;
506 case SDL_QUIT: // window closed
517 void LevelEditor::action()
519 mouse_cursor->set_state(MC_NORMAL);
520 if(tiles_board->is_hover() || tiles_layer->is_hover() || level_options->is_hover())
521 mouse_cursor->set_state(MC_LINK);
525 if(!frame_timer.check())
527 frame_timer.start(25);
528 ++global_frame_counter;
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 + event.button.x) + (x*32),
551 (int)(scroll.y + event.button.y) + (y*32), selection[x][y],
556 #define FADING_TIME 600
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_left() < FADING_TIME)
570 context.set_alpha(level_name_timer.get_left() * 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(event.button.x - 8,
608 event.button.y - 8), LAYER_GUI-2);
609 else if(selection[0][0] < 0)
611 int id = selection[0][0];
613 if(id == OBJ_TRAMPOLINE)
614 context.draw_surface(img_trampoline[0].get_frame(0), Vector(event.button.x - 8,
615 event.button.y - 8), LAYER_GUI-2);
616 else if(id == OBJ_FLYING_PLATFORM)
617 context.draw_surface(img_flying_platform->get_frame(0), Vector(event.button.x - 8,
618 event.button.y - 8), LAYER_GUI-2);
619 else if(id == OBJ_DOOR)
620 context.draw_surface(door->get_frame(0), Vector(event.button.x - 8,
621 event.button.y - 8), LAYER_GUI-2);
624 BadGuyKind kind = BadGuyKind((-id)-1);
625 BadGuy badguy(kind, 0,0);
626 badguy.activate(LEFT);
627 Surface *img = badguy.get_image();
629 context.draw_surface(img, Vector(event.button.x - 8,
630 event.button.y - 8), LAYER_GUI-2);
635 TileManager* tilemanager = TileManager::instance();
636 for(unsigned int x = 0; x < selection.size(); x++)
637 for(unsigned int y = 0; y < selection[x].size(); y++)
638 tilemanager->draw_tile(context, selection[x][y],
639 Vector(event.button.x + x*32 - 8, event.button.y + y*32 - 8),
643 context.set_drawing_effect(NONE_EFFECT);
646 context.draw_filled_rect(Vector(std::min((int)selection_ini.x, (int)event.button.x)*zoom,
647 std::min((int)selection_ini.y, (int)event.button.y))*zoom,
648 Vector(abs(event.button.x - (int)selection_ini.x)*zoom,
649 abs(event.button.y - (int)selection_ini.y)*zoom),
650 Color(170,255,170,128), LAYER_GUI-2);
654 for(int x = 0; x < screen->w / (32*zoom); x++)
656 int pos = (int)(x*32*zoom) - ((int)scroll.x % 32);
657 context.draw_filled_rect(Vector (pos, 0), Vector(1, screen->h),
658 Color(225, 225, 225), LAYER_GUI-50);
660 for(int y = 0; y < screen->h / (32*zoom); y++)
662 int pos = (int)(y*32*zoom) - ((int)scroll.y % 32);
663 context.draw_filled_rect(Vector (0, pos), Vector(screen->w, 1),
664 Color(225, 225, 225), LAYER_GUI-50);
668 context.push_transform();
669 context.set_translation(scroll);
670 context.set_zooming(zoom);
672 for(Sector::GameObjects::iterator i = sector->gameobjects.begin(); i != sector->gameobjects.end(); ++i)
674 TileMap* tilemap = dynamic_cast<TileMap*> (*i);
676 { // draw the non-selected tiles semi-transparently
677 context.push_transform();
679 if(tilemap->get_layer() != cur_layer)
680 context.set_drawing_effect(SEMI_TRANSPARENT);
683 context.pop_transform();
685 Background* background = dynamic_cast<Background*> (*i);
687 { // don't resize background
688 context.push_transform();
689 context.set_translation(scroll);
690 context.set_zooming(1.0);
692 context.pop_transform();
698 context.pop_transform();
701 context.draw_filled_rect(Vector(0,0), Vector(screen->w,screen->h),Color(0,0,0), LAYER_BACKGROUND0);
703 context.do_drawing();
706 void LevelEditor::load_level_subset(std::string filename)
708 std::cerr << "loading subset...\n";
709 std::cerr << "filename: " << filename << std::endl;
711 level_subset = new LevelSubset();
712 level_subset->load(filename.c_str());
716 void LevelEditor::load_level(std::string filename)
720 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
726 level_filename = filename;
730 level->load(filename);
733 level_name_timer.start(3000);
734 scroll.x = scroll.y = 0;
735 level_changed = false;
737 settings_menu->get_item_by_id(MN_ID_NAME).change_input(level->name.c_str());
738 settings_menu->get_item_by_id(MN_ID_AUTHOR).change_input(level->author.c_str());
741 void LevelEditor::load_level(int nb)
745 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
752 std::cerr << "level_nb: " << level_nb << std::endl;
753 std::cerr << "level_subset->get_level_filename(level_nb): " << level_subset->get_level_filename(level_nb) << std::endl;
754 level_filename = level_subset->get_level_filename(level_nb);
756 load_level(level_filename);
759 void LevelEditor::load_sector(std::string name)
762 sector = level->get_sector(sector_name);
764 Termination::abort("Level has no " + sector_name + " sector.", "");
769 void LevelEditor::load_sector(Sector* sector_)
773 if(confirm_dialog(NULL, _("No more sectors exist. Create another?")))
775 Sector* nsector = new Sector();
776 level->add_sector(nsector);
784 /* Load sector stuff */
786 sector->update_game_objects();
788 foregrounds = solids = backgrounds = 0;
789 /* Point foregrounds, backgrounds, solids to its layer */
790 for(Sector::GameObjects::iterator i = sector->gameobjects.begin(); i != sector->gameobjects.end(); i++)
792 BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
794 badguy->activate(LEFT);
796 TileMap* tilemap = dynamic_cast<TileMap*> (*i);
799 if(tilemap->get_layer() == LAYER_FOREGROUNDTILES)
800 foregrounds = tilemap;
801 else if(tilemap->get_layer() == LAYER_TILES)
803 else if(tilemap->get_layer() == LAYER_BACKGROUNDTILES)
804 backgrounds = tilemap;
810 TileMap* tilemap = new TileMap(LAYER_FOREGROUNDTILES, false, solids->get_width(), solids->get_height());
811 sector->add_object(tilemap);
812 sector->update_game_objects();
816 TileMap* tilemap = new TileMap(LAYER_BACKGROUNDTILES, false, solids->get_width(), solids->get_height());
817 sector->add_object(tilemap);
818 sector->update_game_objects();
822 sprintf(str, "%i", solids->get_width());
823 settings_menu->get_item_by_id(MN_ID_WIDTH).change_input(str);
824 sprintf(str, "%i", solids->get_height());
825 settings_menu->get_item_by_id(MN_ID_HEIGHT).change_input(str);
828 void LevelEditor::save_level()
830 std::cerr << "saving level...\n";
831 level->save(level_filename);
832 level_changed = false;
835 void LevelEditor::test_level()
839 if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
845 GameSession session(level_filename, ST_GL_TEST);
847 // player_status.reset();
848 sound_manager->halt_music();
851 void LevelEditor::change(int x, int y, int newtile, int layer)
852 { // find the tilemap of the current layer, and then change the tile
853 if(x < 0 || (unsigned int)x > sector->solids->get_width()*32 ||
854 y < 0 || (unsigned int)y > sector->solids->get_height()*32)
857 level_changed = true;
860 { // no need to do this for normal view (no zoom)
861 x = (int)(x * (zoom*32) / 32);
862 y = (int)(y * (zoom*32) / 32);
865 if(newtile < 0) // add object
867 // remove an active tile or object that might be there
868 change(x, y, 0, LAYER_TILES);
870 if(newtile == OBJ_TRAMPOLINE)
871 sector->add_object(new Trampoline(x, y));
872 else if(newtile == OBJ_FLYING_PLATFORM)
873 sector->add_object(new FlyingPlatform(x, y));
874 else if(newtile == OBJ_DOOR)
875 sector->add_object(new Door(x, y));
877 sector->add_object(new BadGuy(BadGuyKind((-newtile)-1), x, y));
879 sector->update_game_objects();
881 else if(cur_layer == LAYER_FOREGROUNDTILES)
882 foregrounds->change(x/32, y/32, newtile);
883 else if(cur_layer == LAYER_TILES)
885 // remove a bad guy if it's there
886 // we /32 in order to round numbers
887 for(Sector::GameObjects::iterator i = sector->gameobjects.begin(); i < sector->gameobjects.end(); i++)
889 BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
891 if((int)badguy->base.x/32 == x/32 && (int)badguy->base.y/32 == y/32)
892 sector->gameobjects.erase(i);
893 Trampoline* trampoline = dynamic_cast<Trampoline*> (*i);
896 if((int)trampoline->base.x/32 == x/32 && (int)trampoline->base.y/32 == y/32)
897 sector->gameobjects.erase(i);
899 FlyingPlatform* flying_platform = dynamic_cast<FlyingPlatform*> (*i);
901 if((int)flying_platform->base.x/32 == x/32 && (int)flying_platform->base.y/32 == y/32)
902 sector->gameobjects.erase(i);
903 Door* door = dynamic_cast<Door*> (*i);
905 if((int)door->get_area().x/32 == x/32 && (int)door->get_area().y/32 == y/32)
906 sector->gameobjects.erase(i);
908 sector->update_game_objects();
909 solids->change(x/32, y/32, newtile);
911 else if(cur_layer == LAYER_BACKGROUNDTILES)
912 backgrounds->change(x/32, y/32, newtile);
915 void LevelEditor::show_help()
917 DrawingContext context;
919 bool show_grid_t = show_grid;
921 mouse_cursor->set_state(MC_HIDE);
926 _("This is the built-in level editor. It's aim is to be intuitive\n"
927 "and simple to use, so it should be pretty straight forward.\n"
929 "To open a level, first you'll have to select a level subset from\n"
930 "the menu (or create your own).\n"
931 "A level subset is basically a collection of levels.\n"
932 "They can then be played from the Contrib menu.\n"
934 "To access the menu from the level editor, just press Esc.\n"
936 "You are currently looking to the level, to scroll it, just\n"
937 "press the right mouse button and drag the mouse. It will move like\n"
939 "You can also use the arrow keys and Page Up/Down.\n"
941 "'+' and '-' keys can be used to zoom in/out the level.\n"
943 "You probably already noticed those floating group of buttons.\n"
944 "Each one serves a different purpose. To select a certain button\n"
945 "just press the Left mouse button on it. A few buttons have key\n"
946 "shortcuts, you can know it by pressing the Right mouse button on\n"
947 "it. That will also show what that button does.\n"
948 "Group of buttons can also be move around by just dragging them,\n"
949 "while pressing the Left mouse button.\n"
951 "Let's learn a bit of what each group of buttons do, shall we?\n"
953 "To starting putting tiles and objects around use the bigger gropup\n"
954 "of buttons. Each button is a different tile. To put it on the level,\n"
955 "just press it and then left click in the level.\n"
956 "You can also copy tiles from the level by using the middle mouse button.\n"
957 "Use the mouse wheel to scroll that group of buttons. You will find\n"
958 "enemies and game objects in the bottom.\n")
962 _("The Foreground/Interactive/Background buttons may be used to\n"
963 "see and edit the respective layer. Level's have three tiles layers:\n"
964 "Foreground - tiles are drawn in top of everything and have no contact\n"
966 "Interactive - these are the tiles that have contact with the player.\n"
967 "Background - tiles are drawn in bottom of everything and have no contact\n"
969 "The unselected layers will be drawn semi-transparently.\n"
971 "At last, but not least, the group of buttons that's left serves\n"
972 "to do related actions with the level.\n"
973 "From left to right:\n"
974 "Mini arrows - can be used to choose other sectors.\n"
975 "Sectors are mini-levels, so to speak, that can be accessed using a door.\n"
976 "Big arrows - choose other level in the same level subset.\n"
977 "Diskette - save the level\n"
978 "Tux - test the level\n"
979 "Tools - set a few settings for the level, incluiding resizing it.\n"
981 "We have reached the end of this Howto.\n"
983 "Don't forget to send us a few cool levels. :)\n"
986 " SuperTux development team\n"
988 "ps: If you are looking for something more powerfull, you can give it a\n"
989 "try to FlexLay. FlexLay is a level editor that supports several games,\n"
990 "including SuperTux. It is an independent project.\n"
991 "Webpage: http://pingus.seul.org/~grumbel/flexlay/")
994 char **text[] = { text1, text2 };
998 for(unsigned int i = 0; i < sizeof(text) / sizeof(text[0]); i++)
1002 context.draw_text(blue_text, _("- Level Editor's Help -"), Vector(screen->w/2, 60), CENTER_ALLIGN, LAYER_GUI);
1004 context.draw_text(white_small_text, *text[i], Vector(20, 120), LEFT_ALLIGN, LAYER_GUI);
1006 sprintf(str,_("Press any key to continue - Page %d/%d"), i+1, sizeof(text) / sizeof(text[0]));
1007 context.draw_text(gold_text, str, Vector(screen->w/2, screen->h-60), CENTER_ALLIGN, LAYER_GUI);
1009 context.do_drawing();
1015 done = wait_for_event(event);
1020 show_grid = show_grid_t;
1021 mouse_cursor->set_state(MC_NORMAL);