Well, here's a work-in-progress undo button and a small change to the brush to fix a bug (not the zero height one). The undo button currently only undoes one left-click worth of work so don't try and undo multiple clicks with it, and the redo button hasn't been implemented yet. But, I planned for both a length of undo history and a redo button in the code, I just got tired of coding for today.
@titi: I'll store the water level on/off switch in the back of my mind and see if I can add that in later. I don't know if it's possible to color the menu fonts, though I don't really have much experience with wx so I couldn't say for sure. Maybe there would be another way to make the colors more obvious? Also my brush isn't replacing the old one, it's in a new menu in the svn, so don't worry, your old brush is safe.
@-Archmage- and wciow: sorry no shortcut key yet, but the button is there.
Index: source/map_editor/map.h
===================================================================
--- source/map_editor/map.h (revision 498)
+++ source/map_editor/map.h (working copy)
@@ -105,6 +105,11 @@
void changeResource(int x, int y, int resource, int radius);
void changeStartLocation(int x, int y, int player);
+ void setHeight(int x, int y, float height);
+ void setSurface(int x, int y, int surface);
+ void setObject(int x, int y, int object);
+ void setResource(int x, int y, int resource);
+
void flipX();
void flipY();
void reset(int w, int h, float alt, int surf);
Index: source/map_editor/main.cpp
===================================================================
--- source/map_editor/main.cpp (revision 498)
+++ source/map_editor/main.cpp (working copy)
@@ -52,7 +52,7 @@
object = 0;
resource = 0;
startLocation = 1;
- enabledGroup = 0;
+ enabledGroup = ctLocation;
//gl canvas
@@ -73,6 +73,8 @@
//edit
menuEdit = new wxMenu();
+ menuEdit->Append(miEditUndo, wxT("Undo"));
+ menuEdit->Append(miEditRedo, wxT("Redo"));
menuEdit->Append(miEditReset, wxT("Reset"));
menuEdit->Append(miEditResetPlayers, wxT("Reset Players"));
menuEdit->Append(miEditResize, wxT("Resize"));
@@ -189,6 +191,7 @@
void MainWindow::onMouseDown(wxMouseEvent &event) {
if (event.LeftIsDown()) {
+ program->setUndoPoint(enabledGroup);
program->setRefAlt(event.GetX(), event.GetY());
change(event.GetX(), event.GetY());
}
@@ -265,6 +268,16 @@
Close();
}
+void MainWindow::onMenuEditUndo(wxCommandEvent &event) {
+ std::cout << "Undo Pressed" << std::endl;
+ program->undo();
+}
+
+void MainWindow::onMenuEditRedo(wxCommandEvent &event) {
+ std::cout << "Redo Pressed" << std::endl;
+ program->redo();
+}
+
void MainWindow::onMenuEditReset(wxCommandEvent &event) {
SimpleDialog simpleDialog;
simpleDialog.addValue("Altitude", "10");
@@ -395,14 +408,14 @@
uncheckBrush();
menuBrushHeight->Check(event.GetId(), true);
height = event.GetId() - miBrushHeight - heightCount / 2 - 1;
- enabledGroup = 0;
+ enabledGroup = ctGlestHeight;
}
void MainWindow::onMenuPirateBrushHeight(wxCommandEvent &event) {
uncheckBrush();
menuPirateBrushHeight->Check(event.GetId(), true);
height = event.GetId() - miPirateBrushHeight - heightCount / 2 - 1;
- enabledGroup = 5;
+ enabledGroup = ctPirateHeight;
}
@@ -410,28 +423,28 @@
uncheckBrush();
menuBrushSurface->Check(event.GetId(), true);
surface = event.GetId() - miBrushSurface;
- enabledGroup = 1;
+ enabledGroup = ctSurface;
}
void MainWindow::onMenuBrushObject(wxCommandEvent &event) {
uncheckBrush();
menuBrushObject->Check(event.GetId(), true);
object = event.GetId() - miBrushObject - 1;
- enabledGroup = 2;
+ enabledGroup = ctObject;
}
void MainWindow::onMenuBrushResource(wxCommandEvent &event) {
uncheckBrush();
menuBrushResource->Check(event.GetId(), true);
resource = event.GetId() - miBrushResource - 1;
- enabledGroup = 3;
+ enabledGroup = ctResource;
}
void MainWindow::onMenuBrushStartLocation(wxCommandEvent &event) {
uncheckBrush();
menuBrushStartLocation->Check(event.GetId(), true);
startLocation = event.GetId() - miBrushStartLocation - 1;
- enabledGroup = 4;
+ enabledGroup = ctLocation;
}
void MainWindow::onMenuRadius(wxCommandEvent &event) {
@@ -442,22 +455,22 @@
void MainWindow::change(int x, int y) {
switch (enabledGroup) {
- case 0:
+ case ctGlestHeight:
program->glestChangeMapHeight(x, y, height, radius);
break;
- case 1:
+ case ctSurface:
program->changeMapSurface(x, y, surface, radius);
break;
- case 2:
+ case ctObject:
program->changeMapObject(x, y, object, radius);
break;
- case 3:
+ case ctResource:
program->changeMapResource(x, y, resource, radius);
break;
- case 4:
+ case ctLocation:
program->changeStartLocation(x, y, startLocation);
break;
- case 5:
+ case ctPirateHeight:
program->pirateChangeMapHeight(x, y, height, radius);
break;
}
@@ -500,6 +513,8 @@
EVT_MENU(miFileSaveAs, MainWindow::onMenuFileSaveAs)
EVT_MENU(miFileExit, MainWindow::onMenuFileExit)
+ EVT_MENU(miEditUndo, MainWindow::onMenuEditUndo)
+ EVT_MENU(miEditRedo, MainWindow::onMenuEditRedo)
EVT_MENU(miEditReset, MainWindow::onMenuEditReset)
EVT_MENU(miEditResetPlayers, MainWindow::onMenuEditResetPlayers)
EVT_MENU(miEditResize, MainWindow::onMenuEditResize)
Index: source/map_editor/program.cpp
===================================================================
--- source/map_editor/program.cpp (revision 498)
+++ source/map_editor/program.cpp (working copy)
@@ -18,16 +18,168 @@
namespace MapEditor {
+////////////////////////////
+// class UndoPoint
+////////////////////////////
+int UndoPoint::undoCount = 0;
+int UndoPoint::w = 0;
+int UndoPoint::h = 0;
+UndoPoint *UndoPoint::current = NULL;
+
+UndoPoint::UndoPoint(ChangeType change) {
+ w = Program::map->getW();
+ h = Program::map->getH();
+
+ height = NULL;
+ surface = NULL;
+ object = NULL;
+ resource = NULL;
+
+ switch (change) {
+ case ctGlestHeight:
+ case ctPirateHeight:
+ // Build an array of heights from the map
+ std::cout << "Building an array of heights to use for our UndoPoint" << std::endl;
+ height = new float*[w];
+ for (int i = 0; i < w; i++) {
+ height[i] = new float [h];
+ for (int j = 0; j < h; j++) {
+ height[i][j] = Program::map->getHeight(i, j);
+ }
+ }
+ std::cout << "Built the array" << std::endl;
+ break;
+ case ctSurface:
+ surface = new int*[w];
+ for (int i = 0; i < w; i++) {
+ surface[i] = new int [h];
+ for (int j = 0; j < h; j++) {
+ surface[i][j] = Program::map->getSurface(i, j);
+ }
+ }
+ break;
+ case ctObject:
+ object = new int*[w];
+ for (int i = 0; i < w; i++) {
+ object[i] = new int [h];
+ for (int j = 0; j < h; j++) {
+ object[i][j] = Program::map->getObject(i, j);
+ }
+ }
+ break;
+ case ctResource:
+ resource = new int*[w];
+ for (int i = 0; i < w; i++) {
+ resource[i] = new int [h];
+ for (int j = 0; j < h; j++) {
+ resource[i][j] = Program::map->getResource(i, j);
+ }
+ }
+ break;
+ case ctLocation:
+ break;
+ case ctAll:
+ break;
+ }
+
+ this->change = change;
+
+ undoCount++;
+ std::cout << "Increased undoCount to " << undoCount << std::endl;
+ if (current != NULL) {
+ current->setNext(this);
+ previous = current;
+ }
+ current = this;
+ next = NULL;
+}
+
+UndoPoint::~UndoPoint() {
+ std::cout << "attempting to delete an UndoPoint" << std::endl;
+ if (height != NULL) {
+ std::cout << "deleting heights" << std::endl;
+ for (int i = 0; i < w; i++) {
+ delete height[i];
+ }
+ }
+ if (resource != NULL) {
+ std::cout << "deleting resources" << std::endl;
+ for (int i = 0; i < w; i++) {
+ delete resource[i];
+ }
+ }
+ if (object != NULL) {
+ std::cout << "deleting objects" << std::endl;
+ for (int i = 0; i < w; i++) {
+ delete object[i];
+ }
+ }
+ if (surface != NULL) {
+ std::cout << "deleting surfaces" << std::endl;
+ for (int i = 0; i < w; i++) {
+ delete surface[i];
+ }
+ }
+ // Make sure our links don't break
+ //std::cout << "fixing the list" << std::endl;
+ //if (previous != NULL) previous->setNext(next->getPrevious());
+ //if (next != NULL) next->setPrevious(previous->getNext());
+}
+
+void UndoPoint::revert() {
+ std::cout << "attempting to revert last changes to " << change << std::endl;
+ switch (change) {
+ case ctGlestHeight:
+ case ctPirateHeight:
+ // Restore the array of heights to the map
+ std::cout << "attempting to restore the height array" << std::endl;
+ for (int i = 0; i < w; i++) {
+ for (int j = 0; j < h; j++) {
+ Program::map->setHeight(i, j, height[i][j]);
+ }
+ }
+ break;
+ case ctSurface:
+ std::cout << "attempting to restore the surface array" << std::endl;
+ for (int i = 0; i < w; i++) {
+ for (int j = 0; j < h; j++) {
+ Program::map->setSurface(i, j, surface[i][j]);
+ }
+ }
+ break;
+ case ctObject:
+ for (int i = 0; i < w; i++) {
+ for (int j = 0; j < h; j++) {
+ Program::map->setObject(i, j, object[i][j]);
+ }
+ }
+ break;
+ case ctResource:
+ for (int i = 0; i < w; i++) {
+ for (int j = 0; j < h; j++) {
+ Program::map->setResource(i, j, resource[i][j]);
+ }
+ }
+ break;
+ case ctLocation:
+ break;
+ }
+ std::cout << "reverted changes (we hope)" << std::endl;
+}
+
// ===============================================
// class Program
// ===============================================
+Map *Program::map = NULL;
+
Program::Program(int w, int h) {
cellSize = 6;
ofsetX = 0;
ofsetY = 0;
map = new Map();
renderer.init(w, h);
+ undoIterator = NULL;
}
Program::~Program() {
@@ -58,6 +210,21 @@
map->changeStartLocation((x - ofsetX) / cellSize, (y + ofsetY) / cellSize, player);
}
+void Program::setUndoPoint(ChangeType change) {
+ std::cout << "attempting to set a new UndoPoint from change " << change << std::endl;
+ if (undoIterator != NULL )delete undoIterator; // TODO change... this is just for testing (we want multiple undo points)
+ undoIterator = new UndoPoint(change);
+ std::cout << "set a new UndoPoint" << std::endl;
+}
+
+void Program::undo() {
+ undoIterator->revert();
+}
+
+void Program::redo() {
+ std::cout << "not yet implamented" << std::endl;
+}
+
void Program::renderMap(int w, int h) {
renderer.renderMap(map, ofsetX, ofsetY, w, h, cellSize);
}
Index: source/map_editor/main.h
===================================================================
--- source/map_editor/main.h (revision 498)
+++ source/map_editor/main.h (working copy)
@@ -53,6 +53,8 @@
miFileSaveAs,
miFileExit,
+ miEditUndo,
+ miEditRedo,
miEditReset,
miEditResetPlayers,
miEditResize,
@@ -105,7 +107,7 @@
int object;
int resource;
int startLocation;
- int enabledGroup;
+ ChangeType enabledGroup;
public:
MainWindow();
@@ -123,6 +125,8 @@
void onMenuFileSaveAs(wxCommandEvent &event);
void onMenuFileExit(wxCommandEvent &event);
+ void onMenuEditUndo(wxCommandEvent &event);
+ void onMenuEditRedo(wxCommandEvent &event);
void onMenuEditReset(wxCommandEvent &event);
void onMenuEditResetPlayers(wxCommandEvent &event);
void onMenuEditResize(wxCommandEvent &event);
Index: source/map_editor/program.h
===================================================================
--- source/map_editor/program.h (revision 498)
+++ source/map_editor/program.h (working copy)
@@ -19,6 +19,56 @@
class MainWindow;
+enum ChangeType {
+ ctGlestHeight,
+ ctSurface,
+ ctObject,
+ ctResource,
+ ctLocation,
+ ctPirateHeight,
+ ctAll
+};
+
+// =============================================
+// class Undo Point
+// A linked list class that is more of an extension / modification on
+// the already existing Cell struct in map.h
+// Provides the ability to only specify a certain property of the map to change
+// =============================================
+class UndoPoint {
+ private:
+ // Only keep a certain number of undo points in memory otherwise
+ // Big projects could hog a lot of memory
+ const static int MAX_UNDO_LIST_SIZE = 100; // TODO get feedback on this value
+ static int undoCount;
+
+ ChangeType change;
+
+ // Pointers to arrays of each property
+ int **surface;
+ int **object;
+ int **resource;
+ float **height;
+
+ // Map width and height
+ static int w;
+ static int h;
+
+ UndoPoint *next;
+ UndoPoint *previous;
+ static UndoPoint *current;
+
+ public:
+ UndoPoint(ChangeType change);
+ ~UndoPoint();
+ void revert();
+
+ inline UndoPoint* getNext() const { return next; }
+ inline UndoPoint* getPrevious() const { return previous; }
+ inline void setNext(UndoPoint *n) { this->next = n; }
+ inline void setPrevious(UndoPoint *p) { this->previous = p; }
+};
+
// ===============================================
// class Program
// ===============================================
@@ -28,8 +78,12 @@
Renderer renderer;
int ofsetX, ofsetY;
int cellSize;
- Map *map;
+ static Map *map;
+ friend class UndoPoint;
+// Every mouse click this will be changed
+ UndoPoint *undoIterator;
+
public:
Program(int w, int h);
~Program();
@@ -42,6 +96,10 @@
void changeMapResource(int x, int y, int resource, int radius);
void changeStartLocation(int x, int y, int player);
+ void setUndoPoint(ChangeType change);
+ void undo();
+ void redo();
+
//map ops
void reset(int w, int h, int alt, int surf);
void resize(int w, int h, int alt, int surf);
@@ -67,7 +125,7 @@
void incCellSize(int i);
void resetOfset();
- const Map *getMap() {return map;}
+ static const Map *getMap() {return map;}
};
}// end namespace
Index: source/map_editor/map.cpp
===================================================================
--- source/map_editor/map.cpp (revision 498)
+++ source/map_editor/map.cpp (working copy)
@@ -120,6 +120,12 @@
goalAlt = overBounds;
}
+ // If the radius is 1 don't bother doing any calculations
+ if (radius == 1) {
+ cells[x][y].height = goalAlt;
+ return;
+ }
+
// Get Old height reference points and compute gradients
// from the heights of the sides and corners of the brush to the centre goal height
float gradient[3][3]; // [i][j]
@@ -156,7 +162,8 @@
radius -= 1;
for (int i = x - radius; i <= x + radius; i++) {
for (int j = y - radius; j <= y + radius; j++) {
- if (inside(i, j)) {
+ int dist = get_dist(i - x, j - y);
+ if (inside(i, j) && dist < radius) {
// Normalize di and dj and round them to an int so they can be used as indicies
float normIf = (float(i - x)/ radius);
float normJf = (float(j - y)/ radius);
@@ -231,7 +238,7 @@
}
- float newAlt = usedGrad * get_dist(i - x, j - y) + goalAlt;
+ float newAlt = usedGrad * dist + goalAlt;
// if the change in height and what is supposed to be the change in height
// are the same sign then we can change the height
@@ -245,6 +252,10 @@
}
}
+void Map::setHeight(int x, int y, float height) {
+ cells[x][y].height = height;
+}
+
void Map::setRefAlt(int x, int y) {
if (inside(x, y)) {
refAlt = static_cast<int>(cells[x][y].height);
@@ -315,6 +326,10 @@
}
}
+void Map::setSurface(int x, int y, int surface) {
+ cells[x][y].surface = surface;
+}
+
void Map::changeObject(int x, int y, int object, int radius) {
int i, j;
int dist;
@@ -332,6 +347,11 @@
}
}
+void Map::setObject(int x, int y, int object) {
+ cells[x][y].object = object;
+ cells[x][y].resource = 0;
+}
+
void Map::changeResource(int x, int y, int resource, int radius) {
int i, j;
int dist;
@@ -349,6 +369,11 @@
}
}
+void Map::setResource(int x, int y, int resource) {
+ cells[x][y].resource = resource;
+ cells[x][y].object = 0;
+}
+
void Map::changeStartLocation(int x, int y, int faction) {
if ((faction - 1) < maxFactions && inside(x, y)) {
startLocations[faction].x = x;
As always, I'd love any feedback about the features, or my code/implementation, etc.
EDIT: This patch is obsolete... see my next post for the actual release candidate undo/redo buttons.