undo/redo for edit/clear quest

This commit is contained in:
2025-06-12 15:17:08 -05:00
parent 9aa348e28d
commit 71629e45bd
8 changed files with 108 additions and 18 deletions

View File

@@ -56,6 +56,7 @@
2BF04B2E0BF51924006C0831 /* boe.town.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2BF04B090BF51924006C0831 /* boe.town.cpp */; };
410CEEE82D84618C00FFF8CD /* profile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 410CEEE62D84618C00FFF8CD /* profile.cpp */; };
410CEEE92D84618C00FFF8CD /* profile.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 410CEEE72D84618C00FFF8CD /* profile.hpp */; };
41342CEA2DFB872400E66BEB /* quest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 41342CE92DFB872400E66BEB /* quest.cpp */; };
413AAF612D389F94002E9BF1 /* fileio_party.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91E30F2A1A74819B0057C54A /* fileio_party.cpp */; };
413AAF622D38A076002E9BF1 /* universe.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91AC61C50FA2729900EEAE67 /* universe.cpp */; };
413AAF632D38A1B8002E9BF1 /* party.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 913D05B50FA1E9E300184C18 /* party.cpp */; };
@@ -634,6 +635,7 @@
2BF04B0A0BF51924006C0831 /* boe.town.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = boe.town.hpp; sourceTree = "<group>"; };
410CEEE62D84618C00FFF8CD /* profile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = profile.cpp; sourceTree = "<group>"; };
410CEEE72D84618C00FFF8CD /* profile.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = profile.hpp; sourceTree = "<group>"; };
41342CE92DFB872400E66BEB /* quest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = quest.cpp; sourceTree = "<group>"; };
413FE08E2CECFAFF000D97DC /* winutil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = winutil.cpp; sourceTree = "<group>"; };
415EEEAF2D5534A500B47408 /* prefs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = prefs.cpp; sourceTree = "<group>"; };
41E550522DEB8C1400A7DF52 /* scen.undo.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = scen.undo.hpp; sourceTree = "<group>"; };
@@ -1286,6 +1288,7 @@
9185BD941EA01BCC0027C346 /* scenario */ = {
isa = PBXGroup;
children = (
41342CE92DFB872400E66BEB /* quest.cpp */,
91279D3D0F9D1D6A007B0D52 /* item.cpp */,
91279CC10F9D19DA007B0D52 /* monster.cpp */,
91E5C79D0F9F60FA00C21460 /* outdoors.cpp */,
@@ -2194,6 +2197,7 @@
9143044A2970EDC1003A3967 /* keymods.cpp in Sources */,
919BE8B32D6776A8000C64C6 /* special-outdoor.cpp in Sources */,
91E128EF1BC2076B00C8BE1D /* pictchoice.cpp in Sources */,
41342CEA2DFB872400E66BEB /* quest.cpp in Sources */,
91E128F01BC2076B00C8BE1D /* strchoice.cpp in Sources */,
91E128F11BC2076B00C8BE1D /* strdlog.cpp in Sources */,
91CE248A1EA12866005BDCE4 /* utility.cpp in Sources */,

View File

@@ -0,0 +1,12 @@
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<!-- NOTE: This file should be updated to use relative positioning the next time it changes. -->
<?xml-stylesheet href="dialog.xsl" type="text/xsl"?>
<dialog defbtn='keep' escbtn='cancel'>
<pict type='dlog' num='7' top='6' left='6'/>
<text name='keep-msg' top='6' left='49' width='256' height='32'>
Keep changes to {{quest}} before editing another quest?
</text>
<button name='cancel' type='regular' top='43' left='109'>Cancel</button>
<button name='revert' type='regular' top='43' left='175'>Discard</button>
<button name='keep' type='regular' top='43' left='240'>Keep</button>
</dialog>

15
src/scenario/quest.cpp Normal file
View File

@@ -0,0 +1,15 @@
#include "quest.hpp"
bool cQuest::operator==(const cQuest& other) {
CHECK_EQ(other, deadline_is_relative);
CHECK_EQ(other, auto_start);
CHECK_EQ(other, deadline);
CHECK_EQ(other, event);
CHECK_EQ(other, xp);
CHECK_EQ(other, gold);
CHECK_EQ(other, bank1);
CHECK_EQ(other, bank2);
CHECK_EQ(other, name);
CHECK_EQ(other, descr);
return true;
}

View File

@@ -21,6 +21,10 @@ public:
short bank1 = -1, bank2 = -1; // which job bank(s) this quest is in; -1 for none
std::string name;
std::string descr;
// For detecting actual changes to quests in the scenario editor
bool operator==(const cQuest& other);
bool operator!=(const cQuest& other) { return !(*this == other); }
};
class cJob {

View File

@@ -635,21 +635,17 @@ static bool handle_rb_action(location the_point, bool option_hit) {
}
// Clear quest (it can't be deleted fully)
else {
scenario.quests[j] = cQuest();
scenario.quests[j].name = "Unused Quest";
cQuest cleared;
cleared.name = "Unused Quest";
undo_list.add(action_ptr(new aEditClearQuest("Clear Quest", j, scenario.quests[j], cleared)));
update_edit_menu();
scenario.quests[j] = cleared;
}
} else {
bool is_new = (j == scenario.quests.size());
if(edit_quest(j)){
// Create new confirmed
if(is_new){
undo_list.add(action_ptr(new aCreateDeleteQuest(true, scenario.quests.back())));
update_edit_menu();
}
// Quest edited
else{
// TODO undo action
}
// Quest create/edit undo action is added in save_quest_from_dlog() because quest editor
// has left/right buttons and can be launched with create/edit buttons elsewhere.
}
// Create new canceled
else if(is_new){

View File

@@ -2296,7 +2296,7 @@ static void put_quest_in_dlog(cDialog& me, const cQuest& quest, size_t which_que
}
}
static bool save_quest_from_dlog(cDialog& me, cQuest& quest, size_t which_quest, bool close) {
static bool save_quest_from_dlog(cDialog& me, cQuest& quest, size_t which_quest, bool& is_new, bool need_confirm, bool close) {
if(!me.toast(true)) return false;
quest.name = me["name"].getText();
@@ -2313,13 +2313,37 @@ static bool save_quest_from_dlog(cDialog& me, cQuest& quest, size_t which_quest,
quest.bank2 = me["bank2"].getTextAsNum();
} else quest.bank1 = quest.bank2 = -1;
scenario.quests[which_quest] = quest;
// Edit confirmed and real changes made:
if(scenario.quests[which_quest] != quest || is_new){
if(need_confirm){
// Confirm keeping changes
cChoiceDlog dlog("confirm-edit-quest", {"keep","revert","cancel"}, &me);
dlog->getControl("keep-msg").replaceText("{{quest}}", quest.name);
std::string choice = dlog.show();
if(choice == "revert"){
put_quest_in_dlog(me, scenario.quests[which_quest], which_quest);
return false;
}else if(choice == "cancel"){
return false;
}
}
if(is_new){
undo_list.add(action_ptr(new aCreateDeleteQuest(true, scenario.quests.back())));
update_edit_menu();
}else{
undo_list.add(action_ptr(new aEditClearQuest("Edit Quest", which_quest, scenario.quests[which_quest], quest)));
update_edit_menu();
}
scenario.quests[which_quest] = quest;
is_new = false;
}
if(!close) me.untoast();
return true;
}
static bool change_quest_dlog_page(cDialog& me, std::string dir, cQuest& quest, size_t& which_quest) {
if(!save_quest_from_dlog(me, quest, which_quest, false))
static bool change_quest_dlog_page(cDialog& me, std::string dir, cQuest& quest, size_t& which_quest, bool& is_new) {
if(!save_quest_from_dlog(me, quest, which_quest, is_new, true, false))
return true;
if(dir == "left") {
@@ -2338,8 +2362,11 @@ static bool change_quest_dlog_page(cDialog& me, std::string dir, cQuest& quest,
}
bool edit_quest(size_t which_quest) {
short first = which_quest;
using namespace std::placeholders;
bool is_new = false;
if(which_quest == scenario.quests.size()){
is_new = true;
scenario.quests.resize(which_quest + 1);
scenario.quests[which_quest].name = "New Quest";
}
@@ -2347,7 +2374,7 @@ bool edit_quest(size_t which_quest) {
cDialog quest_dlg(*ResMgr::dialogs.get("edit-quest"));
quest_dlg["cancel"].attachClickHandler(std::bind(&cDialog::toast, _1, false));
quest_dlg["okay"].attachClickHandler(std::bind(save_quest_from_dlog, _1, std::ref(quest), std::ref(which_quest), true));
quest_dlg["okay"].attachClickHandler(std::bind(save_quest_from_dlog, _1, std::ref(quest), std::ref(which_quest), std::ref(is_new), false, true));
quest_dlg.attachClickHandlers([](cDialog& me, std::string item_hit, eKeyMod) {
std::string field_id = item_hit.substr(7);
std::string title = field_id == "evt" ? "Select an event:" : "Select a job board:";
@@ -2378,12 +2405,12 @@ bool edit_quest(size_t which_quest) {
quest_dlg["left"].hide();
quest_dlg["right"].hide();
} else {
quest_dlg.attachClickHandlers(std::bind(change_quest_dlog_page, _1, _2, std::ref(quest), std::ref(which_quest)), {"left", "right"});
quest_dlg.attachClickHandlers(std::bind(change_quest_dlog_page, _1, _2, std::ref(quest), std::ref(which_quest), std::ref(is_new)), {"left", "right"});
}
put_quest_in_dlog(quest_dlg, quest, which_quest);
quest_dlg.run();
return quest_dlg.accepted();
return quest_dlg.accepted() || first != which_quest;
}
static bool put_shop_item_in_dlog(cPict& pic, cControl& num, cControl& title, const cShop& shop, int which) {

View File

@@ -460,3 +460,23 @@ bool aEditClearSpecialItem::redo_me() {
scenario.special_items[which] = after;
return true;
}
bool aEditClearQuest::undo_me() {
// If not editing quests, show it
if(overall_mode != MODE_EDIT_QUESTS){
start_quest_editing();
// TODO scroll to show the quest
}
scenario.quests[which] = before;
return true;
}
bool aEditClearQuest::redo_me() {
// If not editing quests, show it
if(overall_mode != MODE_EDIT_QUESTS){
start_quest_editing();
// TODO scroll to show the quest
}
scenario.quests[which] = after;
return true;
}

View File

@@ -316,4 +316,16 @@ public:
cAction(name), which(which), before(before), after(after) {}
};
/// Action which edits or clears a quest
class aEditClearQuest : public cAction {
size_t which;
cQuest before;
cQuest after;
bool undo_me() override;
bool redo_me() override;
public:
aEditClearQuest(std::string name, size_t which, cQuest before, cQuest after) :
cAction(name), which(which), before(before), after(after) {}
};
#endif