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",
|
||||
"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",
|
||||
"barr-cage", "", "", "", "", "", "", "",
|
||||
"barr-cage", "spec-road", "", "", "", "", "", "",
|
||||
"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) {
|
||||
writeEnum(out, e, field_names, "dispel");
|
||||
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) {
|
||||
if(!readEnum(in, e, field_names, FIELD_DISPEL))
|
||||
in.setstate(std::ios::failbit);
|
||||
|
@@ -702,6 +702,11 @@ std::string current_stroke_type;
|
||||
item_changes_t current_items_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() {
|
||||
if(!current_stroke_changes.empty()){
|
||||
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)));
|
||||
update_edit_menu();
|
||||
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;
|
||||
break;
|
||||
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;
|
||||
break;
|
||||
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;
|
||||
break;
|
||||
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;
|
||||
break;
|
||||
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;
|
||||
break;
|
||||
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;
|
||||
break;
|
||||
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;
|
||||
break;
|
||||
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;
|
||||
break;
|
||||
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;
|
||||
break;
|
||||
case MODE_TOGGLE_SPECIAL_DOT:
|
||||
current_field_type = SPECIAL_SPOT;
|
||||
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;
|
||||
} else {
|
||||
if(!mouse_button_held)
|
||||
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;
|
||||
mouse_button_held = true;
|
||||
}
|
||||
break;
|
||||
case MODE_TOGGLE_ROAD:
|
||||
current_field_type = SPECIAL_ROAD;
|
||||
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;
|
||||
} else {
|
||||
if(!mouse_button_held)
|
||||
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;
|
||||
mouse_button_held = true;
|
||||
}
|
||||
break;
|
||||
case MODE_CLEAR_FIELDS:
|
||||
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;
|
||||
break;
|
||||
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;
|
||||
break;
|
||||
case MODE_EYEDROPPER:
|
||||
|
@@ -1893,9 +1893,11 @@ bool is_field_type(short i,short j,eFieldType field_type) {
|
||||
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))
|
||||
return;
|
||||
stroke.insert(loc(i,j));
|
||||
|
||||
for(short k = 0; k < town->preset_fields.size(); k++)
|
||||
if(town->preset_fields[k].type == 0) {
|
||||
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++)
|
||||
if((town->preset_fields[k].type == field_type) &&
|
||||
(town->preset_fields[k].loc.x == i) &&
|
||||
(town->preset_fields[k].loc.y == j)) {
|
||||
town->preset_fields[k].type = FIELD_DISPEL;
|
||||
stroke[loc(i,j)].push_back(field_type);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@
|
||||
#include <SFML/Graphics/RenderWindow.hpp>
|
||||
#include "fields.hpp"
|
||||
#include "location.hpp"
|
||||
#include "scen.undo.hpp"
|
||||
|
||||
void Set_up_win ();
|
||||
void run_startup_g();
|
||||
@@ -29,8 +30,8 @@ void take_special(short i,short j);
|
||||
void make_special(short i,short j);
|
||||
void sort_specials();
|
||||
bool is_field_type(short i,short j,eFieldType field_type);
|
||||
void make_field_type(short i,short j,eFieldType field_type);
|
||||
void take_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,clear_field_stroke_t& stroke);
|
||||
bool container_there(location l);
|
||||
bool is_spot(short i,short j);
|
||||
bool is_road(short i,short j);
|
||||
|
@@ -12,6 +12,7 @@ extern short cen_x;
|
||||
extern short cen_y;
|
||||
extern cScenario scenario;
|
||||
extern cArea* get_current_area();
|
||||
extern cOutdoors* current_terrain;
|
||||
extern void start_town_edit();
|
||||
extern void start_out_edit();
|
||||
extern void redraw_screen();
|
||||
@@ -22,6 +23,8 @@ extern eDrawMode draw_mode;
|
||||
extern void apply_outdoor_shift(rectangle mod);
|
||||
extern void clamp_current_section();
|
||||
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),
|
||||
area(area) {}
|
||||
@@ -638,4 +641,62 @@ bool aPlaceStartLocation::redo_me() {
|
||||
scenario.out_start = area.where;
|
||||
}
|
||||
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<class cItem> item_type_changes_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
|
||||
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) {}
|
||||
};
|
||||
|
||||
/// 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
|
Reference in New Issue
Block a user