From 56e3a6e627e9baaa54025521edf3ae9d93de6624 Mon Sep 17 00:00:00 2001 From: Nat Quayle Nelson Date: Thu, 19 Jun 2025 19:40:03 -0500 Subject: [PATCH] undo/redo for advanced town details --- src/scenario/town.cpp | 35 +++++++++++++++++++++++++++++ src/scenario/town.hpp | 42 +++++++++++++++++++++++++++++++++++ src/scenedit/scen.townout.cpp | 14 ++++++++++-- src/scenedit/scen.undo.cpp | 10 +++++++++ src/scenedit/scen.undo.hpp | 11 +++++++++ 5 files changed, 110 insertions(+), 2 deletions(-) diff --git a/src/scenario/town.cpp b/src/scenario/town.cpp index 92d20e98..2dc39a9c 100644 --- a/src/scenario/town.cpp +++ b/src/scenario/town.cpp @@ -287,4 +287,39 @@ bool cTown::cItem::operator==(const cTown::cItem& other) { CHECK_EQ(other, property); CHECK_EQ(other, contained); return true; +} + +town_advanced_t advanced_from_town(size_t which, cTown& town, cScenario& scenario) { + town_advanced_t details = { + town.exits, + town.spec_on_entry, + town.spec_on_entry_if_dead, + town.spec_on_hostile, + town.bg_town, + town.bg_fight, + town.is_hidden, + town.defy_mapping, + town.defy_scrying, + town.strong_barriers, + town.has_tavern + }; + auto iter = scenario.store_item_rects.find(which); + if(iter != scenario.store_item_rects.end()) details.store_item_rect = iter->second; + return details; +} + +void town_set_advanced(size_t which, cTown& town, cScenario& scenario, const town_advanced_t& details) { + town.exits = details.exits; + town.spec_on_entry = details.spec_on_entry; + town.spec_on_entry_if_dead = details.spec_on_entry_if_dead; + town.spec_on_hostile = details.spec_on_hostile; + town.bg_town = details.bg_town; + town.bg_fight = details.bg_fight; + town.is_hidden = details.is_hidden; + town.defy_mapping = details.defy_mapping; + town.defy_scrying = details.defy_scrying; + town.strong_barriers = details.strong_barriers; + town.has_tavern = details.has_tavern; + if(details.store_item_rect) scenario.store_item_rects[which] = *details.store_item_rect; + else scenario.store_item_rects.erase(which); } \ No newline at end of file diff --git a/src/scenario/town.hpp b/src/scenario/town.hpp index 5bbd9455..daee4cd8 100644 --- a/src/scenario/town.hpp +++ b/src/scenario/town.hpp @@ -181,4 +181,46 @@ inline void town_set_details(cTown& town, const town_details_t& details) { town.comment = details.comment; } +// Store a version of the advanced town details for undo history. +// This could be made a struct that cTown contains, and that would eliminate the next 2 functions, but it would +// require changing every reference to these detail values in the game and fileio code, making them more verbose. I don't know +// if that's worth it. +struct town_advanced_t { + std::array exits; + short spec_on_entry; + short spec_on_entry_if_dead; + short spec_on_hostile; + int bg_town; + int bg_fight; + bool is_hidden; + bool defy_mapping; + bool defy_scrying; + bool strong_barriers; + bool has_tavern; + boost::optional store_item_rect; + + bool operator==(const town_advanced_t& other) const { + for(int i = 0; i < exits.size(); ++i){ + if(other.exits[i].spec != exits[i].spec) return false; + if(other.exits[i] != exits[i]) return false; + } + CHECK_EQ(other,spec_on_entry); + CHECK_EQ(other,spec_on_entry_if_dead); + CHECK_EQ(other,spec_on_hostile); + CHECK_EQ(other,bg_town); + CHECK_EQ(other,bg_fight); + CHECK_EQ(other,is_hidden); + CHECK_EQ(other,defy_mapping); + CHECK_EQ(other,defy_scrying); + CHECK_EQ(other,strong_barriers); + CHECK_EQ(other,has_tavern); + CHECK_EQ(other,store_item_rect); + return true; + } + bool operator!=(const town_advanced_t& other) const { return !(*this == other); } +}; + +town_advanced_t advanced_from_town(size_t which, cTown& town, cScenario& scenario); +void town_set_advanced(size_t which, cTown& town, cScenario& scenario, const town_advanced_t& details); + #endif diff --git a/src/scenedit/scen.townout.cpp b/src/scenedit/scen.townout.cpp index f316c06c..664ed86e 100644 --- a/src/scenedit/scen.townout.cpp +++ b/src/scenedit/scen.townout.cpp @@ -1027,9 +1027,19 @@ void edit_advanced_town() { put_advanced_town_in_dlog(town_dlg); + town_advanced_t old_details = advanced_from_town(cur_town, *town, scenario); town_dlg.run(); - if(town_dlg.accepted() && delete_rect) - scenario.store_item_rects.erase(cur_town); + if(town_dlg.accepted()){ + if(delete_rect){ + scenario.store_item_rects.erase(cur_town); + } + + town_advanced_t new_details = advanced_from_town(cur_town, *town, scenario); + if(new_details != old_details){ + undo_list.add(action_ptr(new aEditTownAdvancedDetails(cur_town, old_details, new_details))); + update_edit_menu(); + } + } } static bool save_town_wand(cDialog& me, std::string, eKeyMod) { diff --git a/src/scenedit/scen.undo.cpp b/src/scenedit/scen.undo.cpp index 0cfbebc8..d6649317 100644 --- a/src/scenedit/scen.undo.cpp +++ b/src/scenedit/scen.undo.cpp @@ -950,4 +950,14 @@ bool aEditTownWandering::redo_me() { scenario.towns[which]->wandering = new_wandering; scenario.towns[which]->wandering_locs = new_wandering_locs; return true; +} + +bool aEditTownAdvancedDetails::undo_me() { + town_set_advanced(which, *scenario.towns[which], scenario, old_details); + return true; +} + +bool aEditTownAdvancedDetails::redo_me() { + town_set_advanced(which, *scenario.towns[which], scenario, new_details); + return true; } \ No newline at end of file diff --git a/src/scenedit/scen.undo.hpp b/src/scenedit/scen.undo.hpp index 77611fb3..d6318086 100644 --- a/src/scenedit/scen.undo.hpp +++ b/src/scenedit/scen.undo.hpp @@ -609,4 +609,15 @@ public: new_wandering(new_wandering), new_wandering_locs(new_wandering_locs) {} }; +class aEditTownAdvancedDetails : public cAction { + size_t which; + town_advanced_t old_details; + town_advanced_t new_details; + bool undo_me() override; + bool redo_me() override; +public: + aEditTownAdvancedDetails(size_t which, town_advanced_t old_details, town_advanced_t new_details) : + cAction("Edit Town Advanced Details"), which(which), old_details(old_details), new_details(new_details) {} +}; + #endif \ No newline at end of file