diff --git a/proj/xc12/BoE.xcodeproj/project.pbxproj b/proj/xc12/BoE.xcodeproj/project.pbxproj index 679b9455..6c88c6fd 100755 --- a/proj/xc12/BoE.xcodeproj/project.pbxproj +++ b/proj/xc12/BoE.xcodeproj/project.pbxproj @@ -65,6 +65,7 @@ 413AAF672D38A4A5002E9BF1 /* living.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 914698FB1A7362D900F20F5E /* living.cpp */; }; 413FE08F2CECFAFF000D97DC /* winutil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 413FE08E2CECFAFF000D97DC /* winutil.cpp */; }; 415EEEB02D5534A500B47408 /* prefs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 415EEEAF2D5534A500B47408 /* prefs.cpp */; }; + 41E550542DEB8C2A00A7DF52 /* scen.undo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 41E550532DEB8C2A00A7DF52 /* scen.undo.cpp */; }; 91034D211B225E4A008F01C1 /* scen.appleevents.mm in Sources */ = {isa = PBXBuildFile; fileRef = 91034D201B225E49008F01C1 /* scen.appleevents.mm */; }; 911A14031B8FAFC600900FD9 /* town_read.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91C2A6EC1B8FA91400346948 /* town_read.cpp */; }; 911A14041B8FB00300900FD9 /* talk_read.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91C2A6EE1B8FAA8E00346948 /* talk_read.cpp */; }; @@ -635,6 +636,8 @@ 410CEEE72D84618C00FFF8CD /* profile.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = profile.hpp; sourceTree = ""; }; 413FE08E2CECFAFF000D97DC /* winutil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = winutil.cpp; sourceTree = ""; }; 415EEEAF2D5534A500B47408 /* prefs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = prefs.cpp; sourceTree = ""; }; + 41E550522DEB8C1400A7DF52 /* scen.undo.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = scen.undo.hpp; sourceTree = ""; }; + 41E550532DEB8C2A00A7DF52 /* scen.undo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = scen.undo.cpp; sourceTree = ""; }; 91034D201B225E49008F01C1 /* scen.appleevents.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = scen.appleevents.mm; sourceTree = ""; }; 9103DC652C6A406600849E60 /* cli.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = cli.hpp; sourceTree = ""; }; 910BBA170FB8BECA001E34EA /* dialog.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = dialog.hpp; sourceTree = ""; }; @@ -1468,6 +1471,7 @@ 91B3EEE70F969BA700BF5B67 /* scen.btnmg.hpp */, 91B3EEE10F969BA700BF5B67 /* scen.core.hpp */, 91B3EEE40F969BA700BF5B67 /* scen.fileio.hpp */, + 41E550522DEB8C1400A7DF52 /* scen.undo.hpp */, 91B3EEDE0F969BA700BF5B67 /* scen.global.hpp */, 91B3EEE50F969BA700BF5B67 /* scen.graphics.hpp */, 91B3EEE00F969BA700BF5B67 /* scen.keydlgs.hpp */, @@ -1482,6 +1486,7 @@ 91B3EEEA0F969BA700BF5B67 /* src */ = { isa = PBXGroup; children = ( + 41E550532DEB8C2A00A7DF52 /* scen.undo.cpp */, 91B3EEF10F969BA700BF5B67 /* scen.actions.cpp */, 91034D201B225E49008F01C1 /* scen.appleevents.mm */, 91B3EEF50F969BA700BF5B67 /* scen.btnmg.cpp */, @@ -2250,6 +2255,7 @@ 914CA45819074E0100B6ADD1 /* scen.menus.mac.mm in Sources */, 91034D211B225E4A008F01C1 /* scen.appleevents.mm in Sources */, 91B0D5D21E344300002BE4DA /* view_dialogs.cpp in Sources */, + 41E550542DEB8C2A00A7DF52 /* scen.undo.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/src/scenedit/SConscript b/src/scenedit/SConscript index a5066e18..aa1dcfdf 100644 --- a/src/scenedit/SConscript +++ b/src/scenedit/SConscript @@ -12,6 +12,7 @@ scened_sources = Split(""" scen.main.cpp scen.sdfpicker.cpp scen.townout.cpp + scen.undo.cpp ../view_dialogs.cpp ../fileio/fileio_party.cpp ../damage.cpp diff --git a/src/scenedit/scen.actions.cpp b/src/scenedit/scen.actions.cpp index 3cb215f8..9e1a5532 100644 --- a/src/scenedit/scen.actions.cpp +++ b/src/scenedit/scen.actions.cpp @@ -20,6 +20,7 @@ #include "scen.keydlgs.hpp" #include "scen.townout.hpp" #include "scen.menus.hpp" +#include "scen.undo.hpp" #include "mathutil.hpp" #include "fileio/fileio.hpp" #include "tools/keymods.hpp" @@ -710,6 +711,7 @@ static bool handle_rb_action(location the_point, bool option_hit) { static bool handle_terrain_action(location the_point, bool ctrl_hit) { cArea* cur_area = get_current_area(); + std::shared_ptr undo_action = nullptr; if(mouse_spot.x >= 0 && mouse_spot.y >= 0) { if(cur_viewing_mode == 0) { spot_hit.x = cen_x + mouse_spot.x - 4; @@ -1090,6 +1092,7 @@ static bool handle_terrain_action(location the_point, bool ctrl_hit) { auto& specials = cur_area->special_locs; for(short x = 0; x < specials.size(); x++) if(specials[x] == spot_hit && specials[x].spec >= 0) { + spec_loc_t for_redo = specials[x]; specials[x] = {-1,-1}; specials[x].spec = -1; if(x == specials.size() - 1) { @@ -1098,6 +1101,7 @@ static bool handle_terrain_action(location the_point, bool ctrl_hit) { specials.pop_back(); } while(!specials.empty() && specials.back().spec < 0); } + undo_action.reset(new aEraseSpecial(for_redo)); break; } overall_mode = MODE_DRAWING; @@ -1204,6 +1208,11 @@ static bool handle_terrain_action(location the_point, bool ctrl_hit) { if((overall_mode == MODE_DRAWING) && (old_mode != MODE_DRAWING)) set_string("Drawing mode",scenario.ter_types[current_terrain_type].name); draw_terrain(); + + if(undo_action != nullptr){ + undo_list.add(undo_action); + update_edit_menu(); + } return true; } bool need_redraw = false; diff --git a/src/scenedit/scen.actions.hpp b/src/scenedit/scen.actions.hpp index f3b3487b..67aff823 100644 --- a/src/scenedit/scen.actions.hpp +++ b/src/scenedit/scen.actions.hpp @@ -48,13 +48,3 @@ bool monst_on_space(location loc,short m_num); void place_edit_special(location loc); void set_special(location spot_hit); bool save_check(std::string which_dlog, bool allow_no = true); - -/// Represents the action of adding a new town to the end of the list -class aNewTown : public cAction { - class cTown* theTown; - bool undo_me() override; - bool redo_me() override; -public: - aNewTown(class cTown* t); - ~aNewTown(); -}; diff --git a/src/scenedit/scen.townout.cpp b/src/scenedit/scen.townout.cpp index 1b5d7751..923d4f55 100644 --- a/src/scenedit/scen.townout.cpp +++ b/src/scenedit/scen.townout.cpp @@ -17,6 +17,7 @@ #include "scen.sdfpicker.hpp" #include "scen.fileio.hpp" #include "scen.core.hpp" +#include "scen.undo.hpp" #include "mathutil.hpp" #include "dialogxml/widgets/button.hpp" #include "dialogxml/widgets/field.hpp" @@ -1642,27 +1643,6 @@ void set_current_out(location out_sec, bool continuous_shift, bool first_restore set_up_main_screen(); } -aNewTown::aNewTown(cTown* t) - : cAction("add town") - , theTown(t) -{} - -bool aNewTown::undo_me() { - scenario.towns.resize(scenario.towns.size() - 1); - set_current_town(scenario.towns.size() - 1); - return true; -} - -bool aNewTown::redo_me() { - scenario.towns.push_back(theTown); - set_current_town(scenario.towns.size() - 1); - return true; -} - -aNewTown::~aNewTown() { - if(!isDone()) delete theTown; -} - bool new_town() { ter_num_t preset = 0; cChoiceDlog new_dlg("new-town", {"okay", "cancel"}); diff --git a/src/scenedit/scen.undo.cpp b/src/scenedit/scen.undo.cpp new file mode 100644 index 00000000..c590ccd0 --- /dev/null +++ b/src/scenedit/scen.undo.cpp @@ -0,0 +1,93 @@ +#include "scen.undo.hpp" + +#include "scenario/scenario.hpp" +#include "scenario/area.hpp" + +extern bool editing_town; +extern short cur_town; +extern short cur_town; +extern location cur_out; +extern short cen_x; +extern short cen_y; +extern cScenario scenario; +extern cArea* get_current_area(); +extern void start_town_edit(); +extern void start_out_edit(); +extern void redraw_screen(); + +cTerrainAction::cTerrainAction(std::string name, short town_num, location where) : cAction(name) { + area.is_town = true; + area.town_num = town_num; + area.where = where; +} +cTerrainAction::cTerrainAction(std::string name, location out_sec, location where) : cAction(name) { + area.is_town = false; + area.out_sec = out_sec; + area.where = where; +} +cTerrainAction::cTerrainAction(std::string name, location where) : cAction(name) { + area.is_town = editing_town; + // One of these two will be ignored + area.town_num = cur_town; + area.out_sec = cur_out; + + area.where = where; +} + +void cTerrainAction::showChangeSite() { + if(area.is_town){ + cur_town = area.town_num; + }else{ + cur_out = area.out_sec; + } + editing_town = area.is_town; + + // TODO this isn't working and I don't know why: + cen_x = area.where.x; + cen_y = area.where.y; + redraw_screen(); +} + +void cTerrainAction::undo() { + showChangeSite(); + cAction::undo(); +} +void cTerrainAction::redo() { + showChangeSite(); + cAction::redo(); +} + +// Undo/Redo implementations for actions: + +bool aEraseSpecial::undo_me() { + cArea* cur_area = get_current_area(); + auto& specials = cur_area->special_locs; + specials.push_back(for_redo); + return true; +} +bool aEraseSpecial::redo_me() { + cArea* cur_area = get_current_area(); + auto& specials = cur_area->special_locs; + specials.pop_back(); + return true; +} + +aNewTown::aNewTown(cTown* t) + : cAction("Add Town") + , theTown(t) +{} + +bool aNewTown::undo_me() { + scenario.towns.resize(scenario.towns.size() - 1); + cur_town = min(cur_town, scenario.towns.size() - 1); + return true; +} + +bool aNewTown::redo_me() { + scenario.towns.push_back(theTown); + return true; +} + +aNewTown::~aNewTown() { + if(!isDone()) delete theTown; +} diff --git a/src/scenedit/scen.undo.hpp b/src/scenedit/scen.undo.hpp new file mode 100644 index 00000000..8909ab6e --- /dev/null +++ b/src/scenedit/scen.undo.hpp @@ -0,0 +1,51 @@ +#ifndef BoE_scen_undo_h +#define BoE_scen_undo_h + +#include "location.hpp" +#include "tools/undo.hpp" + +struct area_ref_t { + bool is_town; + // Can't just make the next two a union for compiler reasons. + short town_num; + location out_sec; + location where; +}; + +// Action that modified town or outdoor terrain, so we should show the modified area when undoing or redoing +class cTerrainAction : public cAction { +public: + cTerrainAction(std::string name, short town_num, location where); + cTerrainAction(std::string name, location out_sec, location where); + cTerrainAction(std::string name, location where); + void undo(); + void redo(); + bool undo_me() = 0; + bool redo_me() = 0; +private: + /// Show where the change happened + void showChangeSite(); + area_ref_t area; +}; + +/// Represents the action of adding a new town to the end of the list +class aNewTown : public cAction { + class cTown* theTown; + bool undo_me() override; + bool redo_me() override; +public: + aNewTown(class cTown* t); + ~aNewTown(); +}; + +class aEraseSpecial : public cTerrainAction { +public: + aEraseSpecial(spec_loc_t special) : cTerrainAction("Erase Special Encounter", special), for_redo(special) {} + bool undo_me() override; + bool redo_me() override; +private: + spec_loc_t for_redo; + bool editing_town; +}; + +#endif \ No newline at end of file diff --git a/src/tools/undo.hpp b/src/tools/undo.hpp index 78592e76..84958699 100644 --- a/src/tools/undo.hpp +++ b/src/tools/undo.hpp @@ -15,6 +15,7 @@ #include #include #include +#include "location.hpp" class cAction { std::string actname; @@ -93,6 +94,6 @@ public: static size_t maxUndoSize; }; -// As a special convention, I will prefix action classes with 'a' instead of 'c' +// As a special convention, I will prefix non-abstract action classes with 'a' instead of 'c' #endif