diff --git a/rsrc/dialogs/make-scenario1.xml b/rsrc/dialogs/make-scenario1.xml index 52f56e34..9e5f6f64 100644 --- a/rsrc/dialogs/make-scenario1.xml +++ b/rsrc/dialogs/make-scenario1.xml @@ -2,13 +2,12 @@ Scenario name - filename + Your name Create a new scenario: - What is the file name for your new scenario? - (max. length 8 characters, letters only)
- Examples: thorham, dragonq + What is your name, for crediting purposes?
+ This will be stored in the scenario but cannot easily be changed later.
What is the name of your new scenario? diff --git a/rsrc/menus/scenedit.xib b/rsrc/menus/scenedit.xib index ad2a3c0b..dae1fb02 100644 --- a/rsrc/menus/scenedit.xib +++ b/rsrc/menus/scenedit.xib @@ -222,6 +222,15 @@ + + + Save as… + S + 1048576 + 2147483647 + + + Revert to Saved @@ -1026,6 +1035,7 @@ + @@ -1620,6 +1630,11 @@ + + 890 + + + @@ -1728,12 +1743,13 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin - 889 + 890 0 diff --git a/src/BoE.vsproj/ScenEdit/Scen Editor.rc b/src/BoE.vsproj/ScenEdit/Scen Editor.rc index 6684476a..46dc2b2a 100644 --- a/src/BoE.vsproj/ScenEdit/Scen Editor.rc +++ b/src/BoE.vsproj/ScenEdit/Scen Editor.rc @@ -68,6 +68,7 @@ BEGIN MENUITEM SEPARATOR MENUITEM "Close Scenario\tCtrl+W", IDM_FILE_CLOSE MENUITEM "&Save Scenario\tCtrl+S", IDM_FILE_SAVE + MENUITEM "Save &As\tCtrl+Shift+S", IDM_FILE_SAVE_AS MENUITEM "Revert to Saved", IDM_FILE_REVERT MENUITEM SEPARATOR MENUITEM "&Quit\tCtrl+Q", IDM_FILE_QUIT diff --git a/src/BoE.vsproj/ScenEdit/scenresource.h b/src/BoE.vsproj/ScenEdit/scenresource.h index 7e45dcf2..830cca4e 100644 --- a/src/BoE.vsproj/ScenEdit/scenresource.h +++ b/src/BoE.vsproj/ScenEdit/scenresource.h @@ -68,6 +68,7 @@ #define IDM_SCEN_CUSTOM_PICS 163 #define IDM_SCEN_CUSTOM_SHEETS 164 #define IDM_SCEN_CUSTOM_SNDS 165 +#define IDM_FILE_SAVE_AS 166 // Next default values for new objects // @@ -76,6 +77,6 @@ #define _APS_NEXT_RESOURCE_VALUE 103 #define _APS_NEXT_COMMAND_VALUE 40014 #define _APS_NEXT_CONTROL_VALUE 1001 -#define _APS_NEXT_SYMED_VALUE 166 +#define _APS_NEXT_SYMED_VALUE 167 #endif #endif diff --git a/src/classes/scenario.cpp b/src/classes/scenario.cpp index ef81d74c..ec82dcfe 100644 --- a/src/classes/scenario.cpp +++ b/src/classes/scenario.cpp @@ -64,6 +64,7 @@ cScenario::cScenario() { rating = 0; difficulty = 0; intro_pic = intro_mess_pic = 0; + adjust_diff = true; bg_out = 10; bg_fight = 4; bg_town = 13; diff --git a/src/scenedit/scen.actions.cpp b/src/scenedit/scen.actions.cpp index d8f3a0d9..ae314140 100644 --- a/src/scenedit/scen.actions.cpp +++ b/src/scenedit/scen.actions.cpp @@ -2802,7 +2802,7 @@ bool save_check(std::string which_dlog) { else if(choice == "cancel") return false; town->set_up_lights(); - save_scenario(scenario.scen_file); + save_scenario(); return true; } diff --git a/src/scenedit/scen.core.cpp b/src/scenedit/scen.core.cpp index 430e513a..411c2538 100644 --- a/src/scenedit/scen.core.cpp +++ b/src/scenedit/scen.core.cpp @@ -2793,69 +2793,81 @@ void edit_scen_details() { info_dlg.run(); } -static bool edit_make_scen_1_event_filter(cDialog& me, std::string, eKeyMod) { - short i,j; - - std::string str = me["file"].getText(); - j = str.length(); - if(j == 0) { - giveError("You've left the file name empty.","",&me); - return true; - } - if(j > 50) { - giveError("The file name can be at most 50 characters long.","",&me); - return true; - } - for(i = 0; i < j; i++) - if((str[i] < 97) || (str[i] > 122)) { - giveError("The file name must consist of only lower case letters.","",&me); - return true; - } - me.setResult(true); - me.toast(true); - return true; -} - -bool edit_make_scen_1(std::string& filename,std::string& title,bool& grass) { +bool edit_make_scen_1(std::string& author,std::string& title,bool& grass) { cDialog new_dlog("make-scenario1"); - new_dlog["okay"].attachClickHandler(edit_make_scen_1_event_filter); + new_dlog["okay"].attachClickHandler(std::bind(&cDialog::toast, &new_dlog, true)); new_dlog["cancel"].attachClickHandler(std::bind(&cDialog::toast, &new_dlog, false)); - new_dlog.setResult(false); new_dlog.run(); - if(!new_dlog.getResult()) return false; + if(!new_dlog.accepted()) return false; title = new_dlog["name"].getText(); - filename = new_dlog["file"].getText(); + author = new_dlog["author"].getText(); grass = dynamic_cast(new_dlog["surface"]).getState() != led_off; return true; } -static bool edit_make_scen_2_event_filter(cDialog& me, std::string, eKeyMod) { - short i,j,k; - - i = me["out-w"].getTextAsNum(); - if(cre(i, 1,50,"Outdoors width must be between 1 and 50.","",&me)) return true; - j = me["out-h"].getTextAsNum(); - if(cre(j, 1,50,"Outdoors height must be between 1 and 50.","",&me)) return true; - if(cre(i * j, 1,100,"The total number of outdoor sections (width times height) must be between 1 and 100.","",&me)) return true; - i = me["town-s"].getTextAsNum(); - j = me["town-m"].getTextAsNum(); - k = me["town-l"].getTextAsNum(); - if(cre(i, 0,200,"Number of small towns must be between 0 and 200.","",&me)) return true; - if(cre(j, 1,200,"Number of medium towns must be between 1 and 200. The first town (Town 0) must always be of medium size.","",&me)) return true; - if(cre(k, 0,200,"Number of large towns must be between 0 and 200.","",&me)) return true; - if(cre(i + j + k, 1,200,"The total number of towns must be from 1 to 200 (you must have at least 1 town).","",&me)) return true; - - me.toast(true); +static bool make_scen_check_out(cDialog& me, std::string which, bool losing) { + if(!losing) return true; + int w = me["out-w"].getTextAsNum(), h = me["out-h"].getTextAsNum(); + if(w < 0 || w > 50 || h < 0 || h > 50) { + std::ostringstream error; + error << "Outdoors "; + if(which == "out-w") + error << "width"; + else if(which == "out-h") + error << "height"; + error << " must be between 1 and 50."; + giveError(error.str(), &me); + return false; + } + int total = w * h; + if(total < 1 || total > 50) { + giveError("The total number of outdoor sections (width times height) must be between 1 and 100.", &me); + return false; + } + return true; +} + +static bool make_scen_check_towns(cDialog& me, std::string which, bool losing) { + if(!losing) return true; + int sm = me["town-s"].getTextAsNum(), med = me["town-m"].getTextAsNum(), lg = me["town-l"].getTextAsNum(); + if(sm < 0 || sm > 200 || med < 0 || med > 200 || lg < 0 || lg > 200) { + std::ostringstream error; + error << "Number of "; + if(which == "town-s") + error << "small"; + else if(which == "town-m") + error << "medium"; + else if(which == "town-l") + error << "large"; + error << " must be between 0 and 200"; + giveError(error.str(), &me); + return false; + } + // TODO: Shouldn't this only be checked when exiting the dialog? At least for the case of no towns. + int total = sm + med + lg; + if(total < 1 || total > 200) { + giveError("The total number of towns must be from 1 to 200 (you must have at least 1 town).", &me); + return false; + } return true; } bool edit_make_scen_2(short& out_w, short& out_h, short& town_l, short& town_m, short& town_s, bool& def_town) { cDialog new_dlog("make-scenario2"); - new_dlog["okay"].attachClickHandler(edit_make_scen_2_event_filter); + new_dlog["okay"].attachClickHandler(std::bind(&cDialog::toast, &new_dlog, true)); new_dlog["cancel"].attachClickHandler(std::bind(&cDialog::toast, &new_dlog, false)); - new_dlog.setResult(false); + new_dlog.attachFocusHandlers(make_scen_check_out, {"out-w", "out-h"}); + new_dlog.attachFocusHandlers(make_scen_check_towns, {"town-s", "town-m", "town-l"}); + new_dlog["warrior-grove"].attachFocusHandler([](cDialog& me, std::string, bool losing) -> bool { + if(losing) return true; + if(me["town-m"].getTextAsNum() < 1) { + giveError("Warrior's Grove replaces your first medium town. As such, you must have at least one medium town in order to use it.", &me); + return false; + } + return true; + }); new_dlog.run(); if(!new_dlog.accepted()) return false; @@ -2865,7 +2877,7 @@ bool edit_make_scen_2(short& out_w, short& out_h, short& town_l, short& town_m, town_l = new_dlog["town-l"].getTextAsNum(); town_m = new_dlog["town-m"].getTextAsNum(); town_s = new_dlog["town-s"].getTextAsNum(); - def_town = dynamic_cast(new_dlog["warrior-grove"]).getState(); + def_town = dynamic_cast(new_dlog["warrior-grove"]).getState() != led_off; return true; } @@ -2874,19 +2886,19 @@ extern eScenMode overall_mode; bool build_scenario() { short width, height, lg, med, sm; bool default_town, grass; - std::string filename, title; + std::string author, title; short i; cTown* warriors_grove = nullptr; std::vector warriors_grove_shops; - if(!edit_make_scen_1(filename, title, grass)) + if(!edit_make_scen_1(author, title, grass)) return false; - filename += ".boes"; if(!edit_make_scen_2(width, height, lg, med, sm, default_town)) return false; scenario = cScenario(); scenario.scen_name = title; + scenario.contact_info[0] = author; scenario.default_ground = grass ? 2 : 0; fs::path basePath = progDir/"Scenario Editor"/"Blades of Exile Base"/"bladbase.exs"; @@ -3061,7 +3073,8 @@ bool build_scenario() { cur_town = 0; town = scenario.towns[0]; - save_scenario(progDir/filename); + scenario.scen_file.clear(); + save_scenario(); return true; } diff --git a/src/scenedit/scen.fileio.cpp b/src/scenedit/scen.fileio.cpp index 7498d201..691be8e5 100644 --- a/src/scenedit/scen.fileio.cpp +++ b/src/scenedit/scen.fileio.cpp @@ -19,6 +19,7 @@ #include "gzstream.h" #include "tinyprint.h" #include "map_parse.hpp" +#include "winutil.hpp" #define DONE_BUTTON_ITEM 1 @@ -914,7 +915,16 @@ struct overrides_sheet { } }; -void save_scenario(fs::path toFile) { +void save_scenario(bool rename) { + fs::path toFile = scenario.scen_file; + if(rename || toFile.empty()) { + fs::path def = scenario.scen_file; + if(def.empty()) + def = progDir/"Blades of Exile Scenarios/myscenario.boes"; + toFile = nav_put_scenario(def); + if(toFile.empty()) return; + } + scenario.format.prog_make_ver[0] = 2; scenario.format.prog_make_ver[1] = 0; scenario.format.prog_make_ver[2] = 0; @@ -1072,8 +1082,13 @@ void save_scenario(fs::path toFile) { // Make sure it has the proper file extension std::string fname = toFile.filename().string(); size_t dot = fname.find_last_of('.'); - if(dot == std::string::npos || fname.substr(dot) != ".boes") { - if(dot != std::string::npos && fname.substr(dot) == ".exs") + std::string ext; + if(dot != std::string::npos) { + ext = fname.substr(dot); + std::transform(ext.begin(), ext.end(), ext.begin(), tolower); + } + if(ext != ".boes") { + if(ext == ".exs") fname.replace(dot,4,".boes"); else fname += ".boes"; } diff --git a/src/scenedit/scen.fileio.hpp b/src/scenedit/scen.fileio.hpp index 181a6484..fabc21bf 100644 --- a/src/scenedit/scen.fileio.hpp +++ b/src/scenedit/scen.fileio.hpp @@ -1,5 +1,5 @@ -void save_scenario(fs::path toFile); +void save_scenario(bool rename = false); void start_data_dump(); void scen_text_dump(); diff --git a/src/scenedit/scen.main.cpp b/src/scenedit/scen.main.cpp index 9d18fe23..4132a4e9 100644 --- a/src/scenedit/scen.main.cpp +++ b/src/scenedit/scen.main.cpp @@ -196,6 +196,7 @@ void Handle_Update() { restore_cursor(); } +extern fs::path progDir; void handle_menu_choice(eMenu item_hit) { bool isEdit = false, isHelp = false; std::string helpDlog; @@ -222,7 +223,10 @@ void handle_menu_choice(eMenu item_hit) { } break; case eMenu::FILE_SAVE: - save_scenario(scenario.scen_file); + save_scenario(); + break; + case eMenu::FILE_SAVE_AS: + save_scenario(true); break; case eMenu::FILE_NEW: if(build_scenario()) { diff --git a/src/scenedit/scen.menus.hpp b/src/scenedit/scen.menus.hpp index 5c342b6b..62c1ffa4 100644 --- a/src/scenedit/scen.menus.hpp +++ b/src/scenedit/scen.menus.hpp @@ -14,7 +14,7 @@ void shut_down_menus(short mode); enum class eMenu { NONE, ABOUT, QUIT, FRILL, UNFRILL, - FILE_NEW, FILE_OPEN, FILE_CLOSE, FILE_SAVE, FILE_REVERT, + FILE_NEW, FILE_OPEN, FILE_CLOSE, FILE_SAVE, FILE_SAVE_AS, FILE_REVERT, EDIT_UNDO, EDIT_REDO, EDIT_CUT, EDIT_COPY, EDIT_PASTE, EDIT_DELETE, EDIT_SELECT_ALL, HELP_TOC, HELP_START, HELP_TEST, HELP_DIST, HELP_CONTEST, // Scenario menu diff --git a/src/scenedit/scen.menus.mac.mm b/src/scenedit/scen.menus.mac.mm index 9dcd010a..f6eacf33 100644 --- a/src/scenedit/scen.menus.mac.mm +++ b/src/scenedit/scen.menus.mac.mm @@ -45,7 +45,7 @@ void init_menubar() { help_menu = [[menu_bar_handle itemWithTitle: @"Help"] submenu]; static const eMenu file_choices[] = { - eMenu::FILE_NEW, eMenu::FILE_OPEN, eMenu::NONE, eMenu::FILE_CLOSE, eMenu::FILE_SAVE, eMenu::FILE_REVERT, + eMenu::FILE_NEW, eMenu::FILE_OPEN, eMenu::NONE, eMenu::FILE_CLOSE, eMenu::FILE_SAVE, eMenu::FILE_SAVE_AS, eMenu::FILE_REVERT, }; static const eMenu edit_choices[] = { eMenu::EDIT_UNDO, eMenu::EDIT_REDO, eMenu::NONE, diff --git a/src/scenedit/scen.menus.win.cpp b/src/scenedit/scen.menus.win.cpp index 1bde13a1..0a086206 100644 --- a/src/scenedit/scen.menus.win.cpp +++ b/src/scenedit/scen.menus.win.cpp @@ -73,7 +73,7 @@ void init_menubar() { inited = true; static const eMenu file_choices[] = { - eMenu::FILE_NEW, eMenu::FILE_OPEN, eMenu::NONE, eMenu::FILE_CLOSE, eMenu::FILE_SAVE, eMenu::FILE_REVERT, eMenu::NONE, eMenu::QUIT, + eMenu::FILE_NEW, eMenu::FILE_OPEN, eMenu::NONE, eMenu::FILE_CLOSE, eMenu::FILE_SAVE, eMenu::FILE_SAVE_AS, eMenu::FILE_REVERT, eMenu::NONE, eMenu::QUIT, }; static const eMenu edit_choices[] = { eMenu::EDIT_UNDO, eMenu::EDIT_REDO, eMenu::NONE,