#define BOOST_NO_CXX11_NUMERIC_LIMITS // Because my libc++ is old and not quite standard-compliant, which breaks Boost.Thread #include #include #include #include "scen.global.hpp" #include "scenario.hpp" #include "graphtool.hpp" #include "scen.graphics.hpp" #include "scen.actions.hpp" #include "scen.fileio.hpp" #include "scen.btnmg.hpp" #include "soundtool.hpp" #include "scen.townout.hpp" #include "scen.core.hpp" #include "scen.keydlgs.hpp" #include "mathutil.hpp" #include "fileio.hpp" #include "scrollbar.hpp" #include "winutil.hpp" #include "cursors.hpp" #include "dlogutil.hpp" #include "scen.menus.hpp" /* Globals */ bool All_Done = false; sf::Event event; sf::RenderWindow mainPtr; cTown* town = NULL; bool diff_depth_ok = false,mouse_button_held = false,editing_town = false; short cur_viewing_mode = 0; short cen_x, cen_y; eScenMode overall_mode = MODE_INTRO_SCREEN; std::shared_ptr right_sbar, pal_sbar; short mode_count = 0; cOutdoors* current_terrain; short pixel_depth,old_depth = 8; bool change_made = false, ae_loading = false; // Numbers of current areas being edited short cur_town; location cur_out; /* Prototypes */ void Initialize(void); void Handle_One_Event(); void Handle_Activate(); void Handle_Update(); void Mouse_Pressed(); void close_program(); void ding(); cScenario scenario; rectangle right_sbar_rect; extern rectangle terrain_buttons_rect; extern void set_up_apple_events(int argc, char* argv[]); //Changed to ISO C specified argument and return type. int main(int argc, char* argv[]) { try { init_directories(argv[0]); init_menubar(); init_current_terrain(); Initialize(); init_fileio(); init_snd_tool(); load_graphics(); cDialog::init(); cDialog::defaultBackground = cDialog::BG_LIGHT; cDialog::doAnimations = true; init_graph_tool(); check_for_intel(); set_up_apple_events(argc, argv); cen_x = 18; cen_y = 18; run_startup_g(); init_lb(); init_rb(); make_cursor_sword(); Set_up_win(); init_screen_locs(); if(ae_loading) set_up_main_screen(); else { shut_down_menus(0); set_up_start_screen(); } redraw_screen(); while(!All_Done) Handle_One_Event(); close_program(); return 0; } catch(std::exception& x) { giveError(x.what()); throw; } catch(std::string& x) { giveError(x); throw; } catch(...) { giveError("An unknown error occurred!"); throw; } } void Initialize(void) { // To make the Random sequences truly random, we need to make the seed start // at a different number. An easy way to do this is to put the current time // and date into the seed. Since it is always incrementing the starting seed // will always be different. Don’t for each call of Random, or the sequence // will no longer be random. Only needed once, here in the init. srand(time(NULL)); // Make a new window for drawing in, and it must be a color window. // The window is full screen size, made smaller to make it more visible. sf::VideoMode mode = sf::VideoMode::getDesktopMode(); rectangle windRect; windRect.width() = mode.width; windRect.height() = mode.height; int height = 420 + getMenubarHeight(); windRect.inset((windRect.right - 584) / 2,(windRect.bottom - height) / 2); windRect.offset(0,18); // TODO: I think it should have a close button as well mainPtr.create(sf::VideoMode(windRect.width(), windRect.height()), "Blades of Exile Scenario Editor", sf::Style::Titlebar); mainPtr.setPosition(windRect.topLeft()); init_menubar(); right_sbar_rect.top = RIGHT_AREA_UL_Y - 1; right_sbar_rect.left = RIGHT_AREA_UL_X + RIGHT_AREA_WIDTH - 1 - 16; right_sbar_rect.bottom = RIGHT_AREA_UL_Y + RIGHT_AREA_HEIGHT + 1; right_sbar_rect.right = RIGHT_AREA_UL_X + RIGHT_AREA_WIDTH - 1; right_sbar.reset(new cScrollbar(mainPtr)); right_sbar->setBounds(right_sbar_rect); right_sbar->setPageSize(NRSONPAGE - 1); right_sbar->hide(); rectangle pal_sbar_rect = terrain_buttons_rect; pal_sbar_rect.offset(RIGHT_AREA_UL_X,RIGHT_AREA_UL_Y); pal_sbar_rect.left = pal_sbar_rect.right - 16; pal_sbar_rect.height() = 17 * 16; pal_sbar.reset(new cScrollbar(mainPtr)); pal_sbar->setBounds(pal_sbar_rect); pal_sbar->setPageSize(16); pal_sbar->hide(); } void Handle_One_Event() { ae_loading = false; Handle_Update(); if(!mainPtr.waitEvent(event)) return; switch(event.type) { case sf::Event::KeyPressed: if(!(event.key.*systemKey)) handle_keystroke(event); break; case sf::Event::MouseButtonPressed: Mouse_Pressed(); break; case sf::Event::MouseMoved: if(mouse_button_held) handle_action(loc(event.mouseMove.x,event.mouseMove.y),event); update_mouse_spot(loc(event.mouseMove.x,event.mouseMove.y)); break; case sf::Event::MouseWheelMoved: handle_scroll(event); break; case sf::Event::MouseButtonReleased: mouse_button_held = false; break; default: break; } } void Handle_Update() { redraw_screen(); restore_cursor(); } extern fs::path progDir; void handle_menu_choice(eMenu item_hit) { bool isEdit = false, isHelp = false; std::string helpDlog; fs::path file_to_load; cKey editKey = {true}; switch(item_hit) { case eMenu::NONE: return; case eMenu::FILE_OPEN: if(change_made && !save_check("save-before-load")) break; if(false) case eMenu::FILE_REVERT: if(change_made && cChoiceDlog("save-before-revert", {"revert", "cancel"}).show() == "cancel") break; file_to_load = item_hit == eMenu::FILE_OPEN ? nav_get_scenario() : scenario.scen_file; if(!file_to_load.empty() && load_scenario(file_to_load, scenario)) { cur_town = scenario.last_town_edited; town = scenario.towns[cur_town]; cur_out = scenario.last_out_edited; current_terrain = scenario.outdoors[cur_out.x][cur_out.y]; overall_mode = MODE_MAIN_SCREEN; change_made = false; set_up_main_screen(); } break; case eMenu::FILE_SAVE: save_scenario(); break; case eMenu::FILE_SAVE_AS: save_scenario(true); break; case eMenu::FILE_NEW: if(build_scenario()) { overall_mode = MODE_MAIN_SCREEN; set_up_main_screen(); } break; case eMenu::FILE_CLOSE: if(!save_check("save-before-close")) break; overall_mode = MODE_INTRO_SCREEN; right_sbar->hide(); pal_sbar->hide(); shut_down_menus(0); set_up_start_screen(); break; case eMenu::QUIT: // quit if(!save_check("save-before-quit")) break; All_Done = true; break; case eMenu::EDIT_UNDO: editKey.k = key_undo; isEdit = true; break; case eMenu::EDIT_REDO: editKey.k = key_redo; isEdit = true; break; case eMenu::EDIT_CUT: editKey.k = key_cut; isEdit = true; break; case eMenu::EDIT_COPY: editKey.k = key_copy; isEdit = true; break; case eMenu::EDIT_PASTE: editKey.k = key_paste; isEdit = true; break; case eMenu::EDIT_DELETE: editKey.k = key_del; isEdit = true; break; case eMenu::EDIT_SELECT_ALL: editKey.k = key_selectall; isEdit = true; break; case eMenu::TOWN_CREATE: if(change_made) { giveError("You need to save the changes made to your scenario before you can add a new town."); return; } if(scenario.towns.size() >= 200) { giveError("You have reached the limit of 200 towns you can have in one scenario."); return; } if(new_town(scenario.towns.size())) set_up_main_screen(); change_made = true; break; case eMenu::OUT_RESIZE: if(resize_outdoors()) change_made = true; if(overall_mode == MODE_MAIN_SCREEN) set_up_main_screen(); break; case eMenu::SCEN_DETAILS: edit_scen_details(); change_made = true; break; case eMenu::SCEN_INTRO: edit_scen_intro(); change_made = true; break; case eMenu::TOWN_START: set_starting_loc(); change_made = true; break; case eMenu::SCEN_SHEETS: edit_custom_sheets(); change_made = true; break; case eMenu::SCEN_PICS: edit_custom_pics_types(); change_made = true; break; case eMenu::SCEN_SNDS: edit_custom_sounds(); change_made = true; break; case eMenu::SCEN_SPECIALS: right_sbar->setPosition(0); start_special_editing(0,0); break; case eMenu::SCEN_TEXT: right_sbar->setPosition(0); start_string_editing(STRS_SCEN,0); break; case eMenu::SCEN_JOURNALS: right_sbar->setPosition(0); start_string_editing(STRS_JOURNAL,0); break; case eMenu::TOWN_IMPORT: if(change_made) { giveError("You need to save the changes made to your scenario before you can import a town."); return; } if(cTown* town = pick_import_town()) { town->reattach(scenario); delete scenario.towns[cur_town]; scenario.towns[cur_town] = town; ::town = town; change_made = true; redraw_screen(); } break; case eMenu::OUT_IMPORT: if(change_made) { giveError("You need to save the changes made to your scenario before you can import a sector."); return; } if(cOutdoors* out = pick_import_out()) { out->reattach(scenario); delete scenario.outdoors[cur_out.x][cur_out.y]; scenario.outdoors[cur_out.x][cur_out.y] = out; current_terrain = out; change_made = true; redraw_screen(); } break; case eMenu::SCEN_SAVE_ITEM_RECTS: edit_save_rects(); change_made = true; break; case eMenu::SCEN_HORSES: edit_horses(); change_made = true; break; case eMenu::SCEN_BOATS: edit_boats(); change_made = true; break; case eMenu::TOWN_VARYING: edit_add_town(); change_made = true; break; case eMenu::SCEN_TIMERS: edit_scenario_events(); change_made = true; break; case eMenu::SCEN_ITEM_SHORTCUTS: edit_item_placement(); break; case eMenu::TOWN_DELETE: if(change_made) { giveError("You need to save the changes made to your scenario before you can delete a town."); return; } if(scenario.towns.size() == 1) { giveError("You can't delete the last town in a scenario. All scenarios must have at least 1 town."); return; } if(scenario.towns.size() - 1 == cur_town) { giveError("You can't delete the last town in a scenario while you're working on it. Load a different town, and try this again."); return; } if(scenario.towns.size() - 1 == scenario.which_town_start) { giveError("You can't delete the last town in a scenario while it's the town the party starts the scenario in. Change the parties starting point and try this again."); return; } if(cChoiceDlog("delete-town-confirm", {"okay", "cancel"}).show() == "okay") delete_last_town(); break; case eMenu::SCEN_DATA_DUMP: if(cChoiceDlog("data-dump-confirm", {"okay", "cancel"}).show() == "okay") start_data_dump(); break; case eMenu::SCEN_TEXT_DUMP: if(cChoiceDlog("text-dump-confirm", {"okay", "cancel"}).show() == "okay") scen_text_dump(); redraw_screen(); break; case eMenu::TOWN_DETAILS: edit_town_details(); change_made = true; break; case eMenu::TOWN_WANDERING: edit_town_wand(); change_made = true; break; case eMenu::TOWN_BOUNDARIES: overall_mode = MODE_SET_TOWN_RECT; mode_count = 2; set_cursor(topleft_curs); set_string("Set town boundary","Select upper left corner"); break; case eMenu::FRILL: frill_up_terrain(); change_made = true; break; case eMenu::UNFRILL: unfrill_terrain(); change_made = true; break; case eMenu::TOWN_AREAS: right_sbar->setPosition(0); start_string_editing(STRS_TOWN_RECT,0); break; case eMenu::TOWN_ITEMS_RANDOM: if(cChoiceDlog("add-random-items", {"okay", "cancel"}).show() == "cancel") break; place_items_in_town(); change_made = true; break; case eMenu::TOWN_ITEMS_NOT_PROPERTY: for(int i = 0; i < town->preset_items.size(); i++) town->preset_items[i].property = 0; cChoiceDlog("set-not-owned").show(); draw_terrain(); change_made = true; break; case eMenu::TOWN_ITEMS_CLEAR: if(cChoiceDlog("clear-items-confirm", {"okay", "cancel"}).show() == "cancel") break; town->preset_items.clear(); draw_terrain(); change_made = true; break; case eMenu::TOWN_SPECIALS: right_sbar->setPosition(0); start_special_editing(2,0); break; case eMenu::TOWN_TEXT: right_sbar->setPosition(0); start_string_editing(STRS_TOWN,0); break; case eMenu::TOWN_SIGNS: right_sbar->setPosition(0); start_string_editing(STRS_TOWN_SIGN,0); break; case eMenu::TOWN_ADVANCED: edit_advanced_town(); change_made = true; break; case eMenu::TOWN_TIMERS: edit_town_events(); change_made = true; break; case eMenu::OUT_DETAILS: outdoor_details(); change_made = true; break; case eMenu::OUT_WANDERING: edit_out_wand(0); change_made = true; break; case eMenu::OUT_ENCOUNTERS: edit_out_wand(1); change_made = true; break; case eMenu::OUT_AREAS: right_sbar->setPosition(0); start_string_editing(STRS_OUT_RECT,0); break; case eMenu::OUT_START: overall_mode = MODE_SET_OUT_START; set_string("Select party starting location.",""); break; case eMenu::OUT_SPECIALS: right_sbar->setPosition(0); start_special_editing(1,0); break; case eMenu::OUT_TEXT: right_sbar->setPosition(0); start_string_editing(STRS_OUT,0); break; case eMenu::OUT_SIGNS: right_sbar->setPosition(0); start_string_editing(STRS_OUT_SIGN,0); break; case eMenu::ABOUT: helpDlog = "about-scened"; isHelp = true; break; case eMenu::HELP_TOC: if(fs::is_directory(progDir/"doc")) launchURL("file://" + (progDir/"doc/editor/Contents.html").string()); else launchURL("https://blades.calref.net/doc/editor/Contents.html"); break; case eMenu::HELP_START: helpDlog = "help-editing"; isHelp = true; break; case eMenu::HELP_TEST: helpDlog = "help-testing"; isHelp = true; break; case eMenu::HELP_DIST: helpDlog = "help-distributing"; isHelp = true; break; case eMenu::HELP_CONTEST: helpDlog = "help-contest"; isHelp = true; break; } if(isEdit) { if(!cDialog::sendInput(editKey)) { // Handle non-dialog edit operations here. // switch(editKey.k) {} } } if(isHelp) cChoiceDlog(helpDlog).show(); } static void handleUpdateWhileScrolling(volatile bool& doneScrolling) { while(!doneScrolling) { sf::sleep(sf::milliseconds(10)); // TODO: redraw_screen should probably take the argument specifying what to update redraw_screen(/*REFRESH_RIGHT_BAR*/); if(overall_mode < MODE_MAIN_SCREEN || overall_mode == MODE_EDIT_TYPES) set_up_terrain_buttons(false); } } void Mouse_Pressed() { location mousePos(event.mouseButton.x, event.mouseButton.y); volatile bool doneScrolling = false; if(right_sbar->isVisible() && mousePos.in(right_sbar->getBounds())) { boost::thread updater(std::bind(handleUpdateWhileScrolling, std::ref(doneScrolling))); right_sbar->handleClick(mousePos); doneScrolling = true; updater.join(); redraw_screen(/*REFRESH_RIGHT_BAR*/); } else if(pal_sbar->isVisible() && mousePos.in(pal_sbar->getBounds())) { boost::thread updater(std::bind(handleUpdateWhileScrolling, std::ref(doneScrolling))); pal_sbar->handleClick(mousePos); doneScrolling = true; updater.join(); redraw_screen(/*REFRESH_RIGHT_BAR*/); set_up_terrain_buttons(false); } else handle_action(loc(event.mouseButton.x,event.mouseButton.y),event); } void close_program() { } // TODO: Remove this function and replace it with beep() or play_sound() everywhere. void ding() { beep(); }