diff --git a/rsrc/dialogs/confirm-edit-terrain.xml b/rsrc/dialogs/confirm-edit-terrain.xml new file mode 100644 index 00000000..3135c73a --- /dev/null +++ b/rsrc/dialogs/confirm-edit-terrain.xml @@ -0,0 +1,12 @@ + + + + + + + Keep changes to {{ter}} before editing another terrain? + + + + + diff --git a/src/global.hpp b/src/global.hpp index ed36db29..015c9efc 100644 --- a/src/global.hpp +++ b/src/global.hpp @@ -63,6 +63,8 @@ inline void LOG(std::string line) { #define LOG_VALUE(x) std::cout << #x << ": " << (x) << std::endl; +#define CHECK_EQ(other, x) if(this->x != other.x) return false; + const std::string FINISH_FIRST = "Finish what you're doing first."; #endif diff --git a/src/scenario/terrain.cpp b/src/scenario/terrain.cpp index b37fc329..6b07b1a3 100644 --- a/src/scenario/terrain.cpp +++ b/src/scenario/terrain.cpp @@ -413,3 +413,35 @@ bool cTerrain::blocksMove() const { int code = (int) blockage; return code > 2; } + +bool cTerrain::operator==(const cTerrain& other) { + CHECK_EQ(other, name); + CHECK_EQ(other, picture); + CHECK_EQ(other, blockage); + CHECK_EQ(other, flag1); + CHECK_EQ(other, flag2); + CHECK_EQ(other, flag3); + CHECK_EQ(other, special); + CHECK_EQ(other, trans_to_what); + CHECK_EQ(other, fly_over); + CHECK_EQ(other, boat_over); + CHECK_EQ(other, block_horse); + CHECK_EQ(other, is_archetype); + CHECK_EQ(other, light_radius); + CHECK_EQ(other, step_sound); + CHECK_EQ(other, shortcut_key); + CHECK_EQ(other, obj_num); + CHECK_EQ(other, ground_type); + CHECK_EQ(other, trim_type); + CHECK_EQ(other, trim_ter); + CHECK_EQ(other, frill_for); + CHECK_EQ(other, frill_chance); + CHECK_EQ(other, combat_arena); + CHECK_EQ(other, obj_pos); + CHECK_EQ(other, obj_size); + CHECK_EQ(other, map_pic); + // This field is just temporary for porting. I'm guessing checking it for equality + // would actually be *incorrect.* + // CHECK_EQ(other, i); + return true; +} \ No newline at end of file diff --git a/src/scenario/terrain.hpp b/src/scenario/terrain.hpp index f51c363b..6c11b5ab 100644 --- a/src/scenario/terrain.hpp +++ b/src/scenario/terrain.hpp @@ -45,6 +45,10 @@ public: pic_num_t map_pic = -1; unsigned short i; // for temporary use in porting + // For detecting actual changes to types in the scenario editor + bool operator==(const cTerrain& other); + bool operator!=(const cTerrain& other) { return !(*this == other); } + bool blocksMove() const; void import_legacy(legacy::terrain_type_type& old); void writeTo(std::ostream& file) const; diff --git a/src/scenedit/scen.actions.cpp b/src/scenedit/scen.actions.cpp index 319f835a..fcfa02af 100644 --- a/src/scenedit/scen.actions.cpp +++ b/src/scenedit/scen.actions.cpp @@ -1302,8 +1302,12 @@ static bool handle_terpal_action(location cur_point, bool option_hit) { // option-click type that can't be deleted (because it would break other types' numbers, or is in use somewhere): // reset type info else{ - scenario.ter_types[i] = cTerrain(); - scenario.ter_types[i].name = "Unused Terrain"; + cTerrain before = scenario.ter_types[i]; + cTerrain after; + after.name = "Unused Terrain"; + scenario.ter_types[i] = after; + undo_list.add(action_ptr(new aEditClearTerrain("Clear Terrain Type", i, before, after))); + update_edit_menu(); } break; case DRAW_MONST: diff --git a/src/scenedit/scen.core.cpp b/src/scenedit/scen.core.cpp index c573b46e..7fce9975 100644 --- a/src/scenedit/scen.core.cpp +++ b/src/scenedit/scen.core.cpp @@ -53,6 +53,7 @@ extern ter_num_t template_terrain[64][64]; extern cScenario scenario; extern cCustomGraphics spec_scen_g; extern location cur_out; +extern cUndoList undo_list; const std::set items_no_strength = { eItemAbil::NONE, eItemAbil::HEALING_WEAPON, eItemAbil::RETURNING_MISSILE, eItemAbil::SEEKING_MISSILE, eItemAbil::DRAIN_MISSILES, @@ -463,8 +464,31 @@ static void fill_ter_info(cDialog& me, short ter){ } static bool finish_editing_ter(cDialog& me, std::string id, ter_num_t& which) { - if(!save_ter_info(me, scenario.ter_types[which])) return true; - + cTerrain after; + if(!save_ter_info(me, after)) return true; + cTerrain before = scenario.ter_types[which]; + + bool changed = (after != before); + + if(changed){ + if(id == "left" || id == "right"){ + // Confirm keeping changes + cChoiceDlog dlog("confirm-edit-terrain", {"keep","revert","cancel"}, &me); + dlog->getControl("keep-msg").replaceText("{{ter}}", after.name); + std::string choice = dlog.show(); + if(choice == "keep"){ + scenario.ter_types[which] = after; + }else if(choice == "cancel"){ + return true; + } + }else{ + scenario.ter_types[which] = after; + } + // Store an undo action + undo_list.add(action_ptr(new aEditClearTerrain("Edit Terrain Type", which, before, after))); + update_edit_menu(); + } + if(!me.toast(true)) return true; if(id == "left") { me.untoast(); diff --git a/src/scenedit/scen.undo.cpp b/src/scenedit/scen.undo.cpp index 795fcc91..ea489841 100644 --- a/src/scenedit/scen.undo.cpp +++ b/src/scenedit/scen.undo.cpp @@ -110,6 +110,18 @@ bool aCreateDeleteTerrain::redo_me() { return true; } +bool aEditClearTerrain::undo_me() { + // TODO show the type + scenario.ter_types[which] = before; + return true; +} + +bool aEditClearTerrain::redo_me() { + // TODO show the type + scenario.ter_types[which] = after; + return true; +} + bool aCreateDeleteMonster::undo_me() { // TODO if not in MODE_EDIT_TYPES, show it for(cMonster monst : monsters){ diff --git a/src/scenedit/scen.undo.hpp b/src/scenedit/scen.undo.hpp index 1776db15..5c3190e3 100644 --- a/src/scenedit/scen.undo.hpp +++ b/src/scenedit/scen.undo.hpp @@ -144,4 +144,16 @@ public: items(items) {} }; +/// Action which edits or clears a terrain type +class aEditClearTerrain : public cAction { + ter_num_t which; + cTerrain before; + cTerrain after; + bool undo_me() override; + bool redo_me() override; +public: + aEditClearTerrain(std::string name, ter_num_t which, cTerrain before, cTerrain after) : + cAction(name), which(which), before(before), after(after) {} +}; + #endif \ No newline at end of file