undo/redo for drawing/erasing fields
This commit is contained in:
@@ -353,15 +353,29 @@ cEnumLookup field_names = {
|
|||||||
"explored", "wall-force", "wall-fire", "field-antimagic", "cloud-stink", "wall-ice", "wall-blades", "cloud-sleep",
|
"explored", "wall-force", "wall-fire", "field-antimagic", "cloud-stink", "wall-ice", "wall-blades", "cloud-sleep",
|
||||||
"obj-block", "spec-spot", "field-web", "obj-crate", "obj-barrel", "barr-fire", "barr-force", "field-quickfire",
|
"obj-block", "spec-spot", "field-web", "obj-crate", "obj-barrel", "barr-fire", "barr-force", "field-quickfire",
|
||||||
"sfx-sm-bld", "sfx-med-bld", "sfx-lg-bld", "sfx-sm-slm", "sfx-lg-slm", "sfx-ash", "sfx-bone", "sfx-rock",
|
"sfx-sm-bld", "sfx-med-bld", "sfx-lg-bld", "sfx-sm-slm", "sfx-lg-slm", "sfx-ash", "sfx-bone", "sfx-rock",
|
||||||
"barr-cage", "", "", "", "", "", "", "",
|
"barr-cage", "spec-road", "", "", "", "", "", "",
|
||||||
"dispel", "smash",
|
"dispel", "smash",
|
||||||
};
|
};
|
||||||
|
// Field names that can be printed in the editor
|
||||||
|
cEnumLookup field_names_editor = {
|
||||||
|
"", "", "", "", "", "", "", "",
|
||||||
|
"Block", "Special Spot", "Web", "Crate", "Barrel", "Fire Barrier", "Force Barrier", "Quickfire",
|
||||||
|
"Small Blood", "Medium Blood", "Large Blood", "Small Slime", "Large Slime", "Ash", "Bones", "Rocks",
|
||||||
|
"", "Road", "", "", "", "", "", "",
|
||||||
|
"", "",
|
||||||
|
};
|
||||||
|
|
||||||
std::ostream& operator << (std::ostream& out, eFieldType e) {
|
std::ostream& operator << (std::ostream& out, eFieldType e) {
|
||||||
writeEnum(out, e, field_names, "dispel");
|
writeEnum(out, e, field_names, "dispel");
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string get_editor_field_name(eFieldType e){
|
||||||
|
std::ostringstream sstr;
|
||||||
|
writeEnum(sstr, e, field_names_editor);
|
||||||
|
return sstr.str();
|
||||||
|
}
|
||||||
|
|
||||||
std::istream& operator >> (std::istream& in, eFieldType& e) {
|
std::istream& operator >> (std::istream& in, eFieldType& e) {
|
||||||
if(!readEnum(in, e, field_names, FIELD_DISPEL))
|
if(!readEnum(in, e, field_names, FIELD_DISPEL))
|
||||||
in.setstate(std::ios::failbit);
|
in.setstate(std::ios::failbit);
|
||||||
|
@@ -702,6 +702,11 @@ std::string current_stroke_type;
|
|||||||
item_changes_t current_items_placed;
|
item_changes_t current_items_placed;
|
||||||
creature_changes_t current_creatures_placed;
|
creature_changes_t current_creatures_placed;
|
||||||
|
|
||||||
|
clear_field_stroke_t current_fields_cleared;
|
||||||
|
field_stroke_t current_fields_placed;
|
||||||
|
field_stroke_t current_fields_toggled;
|
||||||
|
eFieldType current_field_type;
|
||||||
|
|
||||||
void commit_stroke() {
|
void commit_stroke() {
|
||||||
if(!current_stroke_changes.empty()){
|
if(!current_stroke_changes.empty()){
|
||||||
undo_list.add(action_ptr(new aDrawTerrain("Draw Terrain (" + current_stroke_type + ")", current_stroke_changes)));
|
undo_list.add(action_ptr(new aDrawTerrain("Draw Terrain (" + current_stroke_type + ")", current_stroke_changes)));
|
||||||
@@ -716,6 +721,18 @@ void commit_stroke() {
|
|||||||
undo_list.add(action_ptr(new aPlaceEraseCreature(current_creatures_placed.size() > 1 ? "Place Creatures" : "Place Creature", true, current_creatures_placed)));
|
undo_list.add(action_ptr(new aPlaceEraseCreature(current_creatures_placed.size() > 1 ? "Place Creatures" : "Place Creature", true, current_creatures_placed)));
|
||||||
update_edit_menu();
|
update_edit_menu();
|
||||||
current_items_placed.clear();
|
current_items_placed.clear();
|
||||||
|
}else if(!current_fields_cleared.empty()){
|
||||||
|
undo_list.add(action_ptr(new aClearFields(current_fields_cleared)));
|
||||||
|
update_edit_menu();
|
||||||
|
current_fields_cleared.clear();
|
||||||
|
}else if(!current_fields_placed.empty()){
|
||||||
|
undo_list.add(action_ptr(new aPlaceFields(current_field_type, current_fields_placed)));
|
||||||
|
update_edit_menu();
|
||||||
|
current_fields_placed.clear();
|
||||||
|
}else if(!current_fields_toggled.empty()){
|
||||||
|
undo_list.add(action_ptr(new aToggleOutFields(current_field_type == SPECIAL_ROAD, mode_count, current_fields_toggled)));
|
||||||
|
update_edit_menu();
|
||||||
|
current_fields_toggled.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -984,66 +1001,83 @@ static bool handle_terrain_action(location the_point, bool ctrl_hit) {
|
|||||||
overall_mode = MODE_DRAWING;
|
overall_mode = MODE_DRAWING;
|
||||||
break;
|
break;
|
||||||
case MODE_PLACE_WEB:
|
case MODE_PLACE_WEB:
|
||||||
make_field_type(spot_hit.x,spot_hit.y,FIELD_WEB);
|
current_field_type = FIELD_WEB;
|
||||||
|
make_field_type(spot_hit.x,spot_hit.y,FIELD_WEB,current_fields_placed);
|
||||||
mouse_button_held = true;
|
mouse_button_held = true;
|
||||||
break;
|
break;
|
||||||
case MODE_PLACE_CRATE:
|
case MODE_PLACE_CRATE:
|
||||||
make_field_type(spot_hit.x,spot_hit.y,OBJECT_CRATE);
|
current_field_type = OBJECT_CRATE;
|
||||||
|
make_field_type(spot_hit.x,spot_hit.y,OBJECT_CRATE,current_fields_placed);
|
||||||
mouse_button_held = true;
|
mouse_button_held = true;
|
||||||
break;
|
break;
|
||||||
case MODE_PLACE_BARREL:
|
case MODE_PLACE_BARREL:
|
||||||
make_field_type(spot_hit.x,spot_hit.y,OBJECT_BARREL);
|
current_field_type = OBJECT_BARREL;
|
||||||
|
make_field_type(spot_hit.x,spot_hit.y,OBJECT_BARREL,current_fields_placed);
|
||||||
mouse_button_held = true;
|
mouse_button_held = true;
|
||||||
break;
|
break;
|
||||||
case MODE_PLACE_FIRE_BARRIER:
|
case MODE_PLACE_FIRE_BARRIER:
|
||||||
make_field_type(spot_hit.x,spot_hit.y,BARRIER_FIRE);
|
current_field_type = BARRIER_FIRE;
|
||||||
|
make_field_type(spot_hit.x,spot_hit.y,BARRIER_FIRE,current_fields_placed);
|
||||||
mouse_button_held = true;
|
mouse_button_held = true;
|
||||||
break;
|
break;
|
||||||
case MODE_PLACE_FORCE_BARRIER:
|
case MODE_PLACE_FORCE_BARRIER:
|
||||||
make_field_type(spot_hit.x,spot_hit.y,BARRIER_FORCE);
|
current_field_type = BARRIER_FORCE;
|
||||||
|
make_field_type(spot_hit.x,spot_hit.y,BARRIER_FORCE,current_fields_placed);
|
||||||
mouse_button_held = true;
|
mouse_button_held = true;
|
||||||
break;
|
break;
|
||||||
case MODE_PLACE_QUICKFIRE:
|
case MODE_PLACE_QUICKFIRE:
|
||||||
make_field_type(spot_hit.x,spot_hit.y,FIELD_QUICKFIRE);
|
current_field_type = FIELD_QUICKFIRE;
|
||||||
|
make_field_type(spot_hit.x,spot_hit.y,FIELD_QUICKFIRE,current_fields_placed);
|
||||||
mouse_button_held = true;
|
mouse_button_held = true;
|
||||||
break;
|
break;
|
||||||
case MODE_PLACE_STONE_BLOCK:
|
case MODE_PLACE_STONE_BLOCK:
|
||||||
make_field_type(spot_hit.x,spot_hit.y,OBJECT_BLOCK);
|
current_field_type = OBJECT_BLOCK;
|
||||||
|
make_field_type(spot_hit.x,spot_hit.y,OBJECT_BLOCK,current_fields_placed);
|
||||||
mouse_button_held = true;
|
mouse_button_held = true;
|
||||||
break;
|
break;
|
||||||
case MODE_PLACE_FORCECAGE:
|
case MODE_PLACE_FORCECAGE:
|
||||||
make_field_type(spot_hit.x,spot_hit.y,BARRIER_CAGE);
|
current_field_type = BARRIER_CAGE;
|
||||||
|
make_field_type(spot_hit.x,spot_hit.y,BARRIER_CAGE,current_fields_placed);
|
||||||
mouse_button_held = true;
|
mouse_button_held = true;
|
||||||
break;
|
break;
|
||||||
case MODE_TOGGLE_SPECIAL_DOT:
|
case MODE_TOGGLE_SPECIAL_DOT:
|
||||||
|
current_field_type = SPECIAL_SPOT;
|
||||||
if(editing_town){
|
if(editing_town){
|
||||||
make_field_type(spot_hit.x, spot_hit.y, SPECIAL_SPOT);
|
make_field_type(spot_hit.x, spot_hit.y, SPECIAL_SPOT,current_fields_placed);
|
||||||
mouse_button_held = true;
|
mouse_button_held = true;
|
||||||
} else {
|
} else {
|
||||||
if(!mouse_button_held)
|
if(!mouse_button_held)
|
||||||
mode_count = !current_terrain->special_spot[spot_hit.x][spot_hit.y];
|
mode_count = !current_terrain->special_spot[spot_hit.x][spot_hit.y];
|
||||||
|
|
||||||
|
current_fields_toggled.insert(spot_hit);
|
||||||
|
|
||||||
current_terrain->special_spot[spot_hit.x][spot_hit.y] = mode_count;
|
current_terrain->special_spot[spot_hit.x][spot_hit.y] = mode_count;
|
||||||
mouse_button_held = true;
|
mouse_button_held = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MODE_TOGGLE_ROAD:
|
case MODE_TOGGLE_ROAD:
|
||||||
|
current_field_type = SPECIAL_ROAD;
|
||||||
if(editing_town){
|
if(editing_town){
|
||||||
make_field_type(spot_hit.x, spot_hit.y, SPECIAL_ROAD);
|
make_field_type(spot_hit.x, spot_hit.y, SPECIAL_ROAD, current_fields_placed);
|
||||||
mouse_button_held = true;
|
mouse_button_held = true;
|
||||||
} else {
|
} else {
|
||||||
if(!mouse_button_held)
|
if(!mouse_button_held)
|
||||||
mode_count = !current_terrain->roads[spot_hit.x][spot_hit.y];
|
mode_count = !current_terrain->roads[spot_hit.x][spot_hit.y];
|
||||||
|
|
||||||
|
current_fields_toggled.insert(spot_hit);
|
||||||
|
|
||||||
current_terrain->roads[spot_hit.x][spot_hit.y] = mode_count;
|
current_terrain->roads[spot_hit.x][spot_hit.y] = mode_count;
|
||||||
mouse_button_held = true;
|
mouse_button_held = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MODE_CLEAR_FIELDS:
|
case MODE_CLEAR_FIELDS:
|
||||||
for(int i = 8; i <= SPECIAL_ROAD; i++)
|
for(int i = 8; i <= SPECIAL_ROAD; i++)
|
||||||
take_field_type(spot_hit.x,spot_hit.y, eFieldType(i));
|
take_field_type(spot_hit.x,spot_hit.y, eFieldType(i), current_fields_cleared);
|
||||||
mouse_button_held = true;
|
mouse_button_held = true;
|
||||||
break;
|
break;
|
||||||
case MODE_PLACE_SFX:
|
case MODE_PLACE_SFX:
|
||||||
make_field_type(spot_hit.x,spot_hit.y,eFieldType(SFX_SMALL_BLOOD + mode_count));
|
current_field_type = eFieldType(SFX_SMALL_BLOOD + mode_count);
|
||||||
|
make_field_type(spot_hit.x,spot_hit.y, current_field_type, current_fields_placed);
|
||||||
mouse_button_held = true;
|
mouse_button_held = true;
|
||||||
break;
|
break;
|
||||||
case MODE_EYEDROPPER:
|
case MODE_EYEDROPPER:
|
||||||
|
@@ -1893,9 +1893,11 @@ bool is_field_type(short i,short j,eFieldType field_type) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void make_field_type(short i,short j,eFieldType field_type) {
|
void make_field_type(short i,short j,eFieldType field_type,field_stroke_t& stroke) {
|
||||||
if(is_field_type(i,j,field_type))
|
if(is_field_type(i,j,field_type))
|
||||||
return;
|
return;
|
||||||
|
stroke.insert(loc(i,j));
|
||||||
|
|
||||||
for(short k = 0; k < town->preset_fields.size(); k++)
|
for(short k = 0; k < town->preset_fields.size(); k++)
|
||||||
if(town->preset_fields[k].type == 0) {
|
if(town->preset_fields[k].type == 0) {
|
||||||
town->preset_fields[k].loc.x = i;
|
town->preset_fields[k].loc.x = i;
|
||||||
@@ -1911,12 +1913,13 @@ void make_field_type(short i,short j,eFieldType field_type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void take_field_type(short i,short j,eFieldType field_type) {
|
void take_field_type(short i,short j,eFieldType field_type,clear_field_stroke_t& stroke) {
|
||||||
for(short k = 0; k < town->preset_fields.size(); k++)
|
for(short k = 0; k < town->preset_fields.size(); k++)
|
||||||
if((town->preset_fields[k].type == field_type) &&
|
if((town->preset_fields[k].type == field_type) &&
|
||||||
(town->preset_fields[k].loc.x == i) &&
|
(town->preset_fields[k].loc.x == i) &&
|
||||||
(town->preset_fields[k].loc.y == j)) {
|
(town->preset_fields[k].loc.y == j)) {
|
||||||
town->preset_fields[k].type = FIELD_DISPEL;
|
town->preset_fields[k].type = FIELD_DISPEL;
|
||||||
|
stroke[loc(i,j)].push_back(field_type);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
#include <SFML/Graphics/RenderWindow.hpp>
|
#include <SFML/Graphics/RenderWindow.hpp>
|
||||||
#include "fields.hpp"
|
#include "fields.hpp"
|
||||||
#include "location.hpp"
|
#include "location.hpp"
|
||||||
|
#include "scen.undo.hpp"
|
||||||
|
|
||||||
void Set_up_win ();
|
void Set_up_win ();
|
||||||
void run_startup_g();
|
void run_startup_g();
|
||||||
@@ -29,8 +30,8 @@ void take_special(short i,short j);
|
|||||||
void make_special(short i,short j);
|
void make_special(short i,short j);
|
||||||
void sort_specials();
|
void sort_specials();
|
||||||
bool is_field_type(short i,short j,eFieldType field_type);
|
bool is_field_type(short i,short j,eFieldType field_type);
|
||||||
void make_field_type(short i,short j,eFieldType field_type);
|
void make_field_type(short i,short j,eFieldType field_type,field_stroke_t& stroke);
|
||||||
void take_field_type(short i,short j,eFieldType field_type);
|
void take_field_type(short i,short j,eFieldType field_type,clear_field_stroke_t& stroke);
|
||||||
bool container_there(location l);
|
bool container_there(location l);
|
||||||
bool is_spot(short i,short j);
|
bool is_spot(short i,short j);
|
||||||
bool is_road(short i,short j);
|
bool is_road(short i,short j);
|
||||||
|
@@ -12,6 +12,7 @@ extern short cen_x;
|
|||||||
extern short cen_y;
|
extern short cen_y;
|
||||||
extern cScenario scenario;
|
extern cScenario scenario;
|
||||||
extern cArea* get_current_area();
|
extern cArea* get_current_area();
|
||||||
|
extern cOutdoors* current_terrain;
|
||||||
extern void start_town_edit();
|
extern void start_town_edit();
|
||||||
extern void start_out_edit();
|
extern void start_out_edit();
|
||||||
extern void redraw_screen();
|
extern void redraw_screen();
|
||||||
@@ -22,6 +23,8 @@ extern eDrawMode draw_mode;
|
|||||||
extern void apply_outdoor_shift(rectangle mod);
|
extern void apply_outdoor_shift(rectangle mod);
|
||||||
extern void clamp_current_section();
|
extern void clamp_current_section();
|
||||||
extern void clamp_view_center(cTown* town);
|
extern void clamp_view_center(cTown* town);
|
||||||
|
extern void make_field_type(short i,short j,eFieldType field_type,field_stroke_t& stroke);
|
||||||
|
extern void take_field_type(short i,short j,eFieldType field_type,clear_field_stroke_t& stroke);
|
||||||
|
|
||||||
cTerrainAction::cTerrainAction(std::string name, area_ref_t area, bool reversed) : cAction(name, reversed),
|
cTerrainAction::cTerrainAction(std::string name, area_ref_t area, bool reversed) : cAction(name, reversed),
|
||||||
area(area) {}
|
area(area) {}
|
||||||
@@ -638,4 +641,62 @@ bool aPlaceStartLocation::redo_me() {
|
|||||||
scenario.out_start = area.where;
|
scenario.out_start = area.where;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool aClearFields::undo_me() {
|
||||||
|
field_stroke_t discard_stroke;
|
||||||
|
for(auto& space : stroke){
|
||||||
|
for(eFieldType field_type : space.second){
|
||||||
|
make_field_type(space.first.x, space.first.y, field_type, discard_stroke);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool aClearFields::redo_me() {
|
||||||
|
clear_field_stroke_t discard_stroke;
|
||||||
|
for(auto& space : stroke){
|
||||||
|
for(eFieldType field_type : space.second){
|
||||||
|
take_field_type(space.first.x, space.first.y, field_type, discard_stroke);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool aPlaceFields::undo_me() {
|
||||||
|
clear_field_stroke_t discard_stroke;
|
||||||
|
for(auto& space : stroke){
|
||||||
|
take_field_type(space.x, space.y, type, discard_stroke);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool aPlaceFields::redo_me() {
|
||||||
|
field_stroke_t discard_stroke;
|
||||||
|
for(auto& space : stroke){
|
||||||
|
make_field_type(space.x, space.y, type, discard_stroke);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool aToggleOutFields::undo_me() {
|
||||||
|
for(auto& space : stroke){
|
||||||
|
if(is_road)
|
||||||
|
current_terrain->roads[space.x][space.y] = !on;
|
||||||
|
else
|
||||||
|
current_terrain->special_spot[space.x][space.y] = !on;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool aToggleOutFields::redo_me() {
|
||||||
|
for(auto& space : stroke){
|
||||||
|
if(is_road)
|
||||||
|
current_terrain->roads[space.x][space.y] = on;
|
||||||
|
else
|
||||||
|
current_terrain->special_spot[space.x][space.y] = on;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
@@ -33,6 +33,8 @@ typedef std::vector<cTerrain> terrain_type_changes_t;
|
|||||||
typedef std::vector<cMonster> monst_type_changes_t;
|
typedef std::vector<cMonster> monst_type_changes_t;
|
||||||
typedef std::vector<class cItem> item_type_changes_t;
|
typedef std::vector<class cItem> item_type_changes_t;
|
||||||
typedef std::map<location,cOutdoors*,loc_compare> outdoor_sections_t;
|
typedef std::map<location,cOutdoors*,loc_compare> outdoor_sections_t;
|
||||||
|
typedef std::map<location,std::vector<eFieldType>,loc_compare> clear_field_stroke_t;
|
||||||
|
typedef std::set<location,loc_compare> field_stroke_t;
|
||||||
|
|
||||||
// Action that modified something in town or outdoor terrain, so we should show the modified area when undoing or redoing
|
// Action that modified something in town or outdoor terrain, so we should show the modified area when undoing or redoing
|
||||||
class cTerrainAction : public cAction {
|
class cTerrainAction : public cAction {
|
||||||
@@ -403,4 +405,39 @@ public:
|
|||||||
cTerrainAction(std::string { "Place Scenario Start Loc " } + (old_where.is_town ? "(Town)" : "(Outdoors)"), new_where), old_where(old_where) {}
|
cTerrainAction(std::string { "Place Scenario Start Loc " } + (old_where.is_town ? "(Town)" : "(Outdoors)"), new_where), old_where(old_where) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Action that clears all fields from tiles within a stroke
|
||||||
|
class aClearFields : public cTerrainAction {
|
||||||
|
clear_field_stroke_t stroke;
|
||||||
|
bool undo_me() override;
|
||||||
|
bool redo_me() override;
|
||||||
|
public:
|
||||||
|
aClearFields(clear_field_stroke_t stroke) : cTerrainAction("Clear Fields", stroke.begin()->first),
|
||||||
|
stroke(stroke) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
extern std::string get_editor_field_name(eFieldType e);
|
||||||
|
|
||||||
|
/// Action that draws a field on tiles within a stroke
|
||||||
|
class aPlaceFields : public cTerrainAction {
|
||||||
|
eFieldType type;
|
||||||
|
field_stroke_t stroke;
|
||||||
|
bool undo_me() override;
|
||||||
|
bool redo_me() override;
|
||||||
|
public:
|
||||||
|
aPlaceFields(eFieldType type, field_stroke_t stroke) : cTerrainAction(std::string{"Place "} + get_editor_field_name(type), *(stroke.begin())),
|
||||||
|
type(type), stroke(stroke) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Action that toggles a road or special spot outdoors within a stroke
|
||||||
|
class aToggleOutFields : public cTerrainAction {
|
||||||
|
bool is_road;
|
||||||
|
bool on;
|
||||||
|
field_stroke_t stroke;
|
||||||
|
bool undo_me() override;
|
||||||
|
bool redo_me() override;
|
||||||
|
public:
|
||||||
|
aToggleOutFields(bool is_road, bool on, field_stroke_t stroke): cTerrainAction(std::string{"Toggle "} + (is_road ? "Road" : "Special Spot"), *(stroke.begin())),
|
||||||
|
is_road(is_road), on(on), stroke(stroke) {}
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
Reference in New Issue
Block a user