undo/redo for edit/clear terrain type.

This commit is contained in:
2025-06-05 09:04:30 -05:00
parent 654dadc868
commit efba526fb9
8 changed files with 106 additions and 4 deletions

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 {{ter}} before editing another terrain?
</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>

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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:

View File

@@ -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<eItemAbil> 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();

View File

@@ -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){

View File

@@ -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