Skip to content

Commit 2b819f0

Browse files
committed
Add Undo and Backup features
1 parent ca1deb0 commit 2b819f0

File tree

7 files changed

+223
-15
lines changed

7 files changed

+223
-15
lines changed

Content/tools.png

204 Bytes
Loading

FileManager.cs

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ public struct Setting
3333
public class SettingsManager
3434
{
3535
public List<Setting> Settings { get; set; }
36+
private int defaultUndoDepth = 4;
37+
private Point defaultResolution = new Point(1200, 800);
38+
private int defaultDelayForBackup = 5;
3639
public SettingsManager()
3740
{
3841
Settings = new();
@@ -60,6 +63,42 @@ public void SetResolution(Point resolution)
6063
Settings.Add(new Setting() { Name = "Resolution X", Value = resolution.X });
6164
Settings.Add(new Setting() { Name = "Resolution Y", Value = resolution.Y });
6265
}
66+
public int GetUndoDepth()
67+
{
68+
var depth = Settings.Where(s => s.Name == "Undo Depth").ToList();
69+
if (depth.Any())
70+
{
71+
return depth.First().Value;
72+
}
73+
else
74+
{
75+
SetUndoDepth(defaultUndoDepth);
76+
return defaultUndoDepth;
77+
}
78+
}
79+
public void SetUndoDepth(int depth)
80+
{
81+
Settings.RemoveAll(s => s.Name == "Undo Depth");
82+
Settings.Add(new Setting() { Name = "Undo Depth", Value = depth });
83+
}
84+
public int GetBackupDelay()
85+
{
86+
var minutes = Settings.Where(s => s.Name == "Backup Delay").ToList();
87+
if (minutes.Any())
88+
{
89+
return minutes.First().Value * 60 * 60;
90+
}
91+
else
92+
{
93+
SetBackupDelay(defaultUndoDepth);
94+
return defaultUndoDepth * 60 * 60;
95+
}
96+
}
97+
public void SetBackupDelay(int minutes)
98+
{
99+
Settings.RemoveAll(s => s.Name == "Backup Delay");
100+
Settings.Add(new Setting() { Name = "Backup Delay", Value = minutes });
101+
}
63102
}
64103

65104
public class FileManager
@@ -98,7 +137,7 @@ public void ProcessTasks(Overworld overworld, List<string> missingTiles, List<Ed
98137
LoadedMapPath = "";
99138
tasks.Remove(task);
100139
tasks.Add(new EditorTask() { Type = EditorTasks.OverworldBlueMap });
101-
140+
tasks.Add(new EditorTask() { Type = EditorTasks.ResetBackupCounter });
102141
}
103142
}
104143
else if (task.Type == EditorTasks.FileLoadMap)
@@ -109,11 +148,12 @@ public void ProcessTasks(Overworld overworld, List<string> missingTiles, List<Ed
109148
tasks.Remove(task);
110149
}
111150
else
112-
{
151+
{
113152
bool fileLoaded = OpenFile();
114153
if (fileLoaded)
115154
{
116155
tasks.Add(new EditorTask() { Type = EditorTasks.OverworldLoadMap });
156+
tasks.Add(new EditorTask() { Type = EditorTasks.ResetBackupCounter });
117157
}
118158
tasks.Remove(task);
119159
}
@@ -153,6 +193,12 @@ public void ProcessTasks(Overworld overworld, List<string> missingTiles, List<Ed
153193
filesaved = SaveFileAs();
154194
tasks.Remove(task);
155195
}
196+
else if (task.Type == EditorTasks.SaveBackupMap)
197+
{
198+
OverworldData.EncodeMap(overworld);
199+
SaveBackup();
200+
tasks.Remove(task);
201+
}
156202
}
157203

158204
if (filesaved)
@@ -271,6 +317,14 @@ public bool SaveFile()
271317

272318
return true;
273319
}
320+
public bool SaveBackup()
321+
{
322+
string serializedOwData = JsonSerializer.Serialize<OwMapExchangeData>(OverworldData, new JsonSerializerOptions { WriteIndented = true });
323+
using var stream = new StreamWriter("backupowmap.json");
324+
stream.Write(serializedOwData);
325+
326+
return true;
327+
}
274328
public bool FileSelected()
275329
{
276330
return LoadedMapName != "";

Game1.cs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ public class Game1 : Game
3535
private WindowsManager windowsManager;
3636
private bool LastActiveState = true;
3737
private List<string> unplacedTiles;
38+
private int suspendKeyboard = 0;
39+
private int timeToBackup;
3840
public Game1()
3941
{
4042
_graphics = new GraphicsDeviceManager(this);
@@ -61,6 +63,7 @@ protected override void Initialize()
6163
fileManager = new FileManager();
6264
fileManager.LoadSettings();
6365
windowSize = fileManager.Settings.GetResolution();
66+
timeToBackup = fileManager.Settings.GetBackupDelay();
6467
_graphics.PreferredBackBufferWidth = windowSize.X;
6568
_graphics.PreferredBackBufferHeight = windowSize.Y;
6669
_graphics.ApplyChanges();
@@ -89,7 +92,7 @@ protected override void LoadContent()
8992
font = Content.Load<SpriteFont>("File");
9093

9194
// Create overworld
92-
overworld = new(tileSetTexture, font, domainGroupsIcons, docksIcons, mapobjectsIcons, GraphicsDevice, _spriteBatch);
95+
overworld = new(tileSetTexture, font, domainGroupsIcons, docksIcons, mapobjectsIcons, GraphicsDevice, _spriteBatch, fileManager.Settings.GetUndoDepth());
9396

9497
// Create menus
9598
toolsMenu = new(toolsTexture, selectors32, font);
@@ -118,6 +121,21 @@ protected override void LoadContent()
118121
}
119122
protected override void Update(GameTime gameTime)
120123
{
124+
if (editorTasks.Contains(new EditorTask() { Type = EditorTasks.ResetBackupCounter }))
125+
{
126+
editorTasks.RemoveAll(t => t.Type == EditorTasks.ResetBackupCounter);
127+
timeToBackup = fileManager.Settings.GetBackupDelay();
128+
}
129+
else if (timeToBackup > 0)
130+
{
131+
timeToBackup--;
132+
}
133+
else
134+
{
135+
editorTasks.Add(new EditorTask() { Type = EditorTasks.SaveBackupMap });
136+
timeToBackup = fileManager.Settings.GetBackupDelay();
137+
}
138+
121139
if (Keyboard.GetState().IsKeyDown(Keys.Escape) || editorTasks.Contains(new EditorTask() { Type = EditorTasks.ExitProgram }))
122140
{
123141
if (overworld.UnsavedChanges)
@@ -147,6 +165,25 @@ protected override void Update(GameTime gameTime)
147165
infoWindow.UpdatePosition(windowSize);
148166
}
149167

168+
if (suspendKeyboard > 0)
169+
{
170+
suspendKeyboard--;
171+
}
172+
else if(Keyboard.GetState().IsKeyDown(Keys.LeftControl) && Keyboard.GetState().IsKeyDown(Keys.Z))
173+
{
174+
if (Keyboard.GetState().IsKeyDown(Keys.LeftShift))
175+
{
176+
overworld.Redo();
177+
suspendKeyboard = 20;
178+
}
179+
else
180+
{
181+
overworld.Undo();
182+
suspendKeyboard = 20;
183+
}
184+
}
185+
186+
150187
// Remove None tasks
151188
editorTasks.RemoveAll(t => t.Type == EditorTasks.None);
152189

Overworld.cs

Lines changed: 100 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public class Overworld
5555
private Texture2D docksIcons;
5656
private Texture2D mapObjectsIcons;
5757
private RenderTarget2D mapTexture;
58-
private byte[] overworldMap;
58+
private byte[] overworldMap { get => targetMaps[owMapCurrentTarget]; }
5959
private List<(int id, OwEncounterGroup group)> domains;
6060
private List<(OverworldTeleportIndex location, SCCoords coord)> docks;
6161
private List<(MapObject mapobject, SCCoords coord)> mapObjects;
@@ -65,20 +65,32 @@ public class Overworld
6565
private GraphicsDevice graphicsDevice;
6666
private SpriteBatch spriteBatch;
6767
private List<SmartBrush> smartBrushes;
68+
private int owMapCurrentTarget;
69+
private int owMapBackSteps;
70+
private int owMapForwardSteps;
71+
private int owMapUndoDepth;
72+
private bool currentlyDrawing;
73+
private List<byte[]> targetMaps;
74+
private List<List<(int id, OwEncounterGroup group)>> targetDomains;
6875

6976
public bool UpdatePlacedMapObjects { get; set; }
7077
public bool UpdatePlacedDocks { get; set; }
7178
public bool UpdatePlacedRequiredTiles { get; set; }
7279
public bool UnsavedChanges { get; set; }
73-
public Overworld(Texture2D _tileset, SpriteFont _font, Texture2D _domaingroups, Texture2D _docks, Texture2D _mapobjects, GraphicsDevice _graphicsDevice, SpriteBatch _spriteBatch)
80+
public Overworld(Texture2D _tileset, SpriteFont _font, Texture2D _domaingroups, Texture2D _docks, Texture2D _mapobjects, GraphicsDevice _graphicsDevice, SpriteBatch _spriteBatch, int _undodepth)
7481
{
7582
graphicsDevice = _graphicsDevice;
7683
spriteBatch = _spriteBatch;
7784
tileSet = _tileset;
78-
overworldMap = new byte[256 * 256];
85+
owMapCurrentTarget = 0;
86+
owMapBackSteps = 0;
87+
owMapForwardSteps = 0;
88+
currentlyDrawing = false;
7989
domainGroupIcons = _domaingroups;
8090
docksIcons = _docks;
8191
mapObjectsIcons = _mapobjects;
92+
owMapUndoDepth = _undodepth;
93+
targetMaps = Enumerable.Range(0, owMapUndoDepth + 1).Select(i => new byte[256 * 256]).ToList();
8294
docks = new();
8395
mapObjects = new();
8496
mapTexture = new(graphicsDevice, 4096, 4096, true, SurfaceFormat.Color, DepthFormat.Depth24, 0, RenderTargetUsage.PreserveContents);
@@ -94,6 +106,57 @@ public Overworld(Texture2D _tileset, SpriteFont _font, Texture2D _domaingroups,
94106
UpdatePlacedRequiredTiles = true;
95107
UnsavedChanges = false;
96108
}
109+
private void CreateBackup()
110+
{
111+
int nextTarget = owMapCurrentTarget < owMapUndoDepth ? owMapCurrentTarget + 1 : 0;
112+
if (owMapBackSteps < owMapUndoDepth)
113+
{
114+
owMapBackSteps++;
115+
}
116+
117+
owMapForwardSteps = 0;
118+
targetMaps[owMapCurrentTarget].CopyTo(targetMaps[nextTarget], 0);
119+
owMapCurrentTarget = nextTarget;
120+
}
121+
public void Undo()
122+
{
123+
if (owMapBackSteps == 0)
124+
{
125+
return;
126+
}
127+
128+
int previousTarget = owMapCurrentTarget > 0 ? owMapCurrentTarget - 1 : owMapUndoDepth;
129+
owMapBackSteps--;
130+
if (owMapForwardSteps < owMapUndoDepth)
131+
{
132+
owMapForwardSteps++;
133+
}
134+
135+
owMapCurrentTarget = previousTarget;
136+
GenerateInitialMap();
137+
UpdatePlacedMapObjects = true;
138+
UpdatePlacedDocks = true;
139+
UpdatePlacedRequiredTiles = true;
140+
}
141+
public void Redo()
142+
{
143+
if (owMapForwardSteps == 0)
144+
{
145+
return;
146+
}
147+
owMapForwardSteps--;
148+
if (owMapBackSteps < owMapUndoDepth)
149+
{
150+
owMapBackSteps++;
151+
}
152+
153+
int nextTarget = owMapCurrentTarget < owMapUndoDepth ? owMapCurrentTarget + 1 : 0;
154+
owMapCurrentTarget = nextTarget;
155+
GenerateInitialMap();
156+
UpdatePlacedMapObjects = true;
157+
UpdatePlacedDocks = true;
158+
UpdatePlacedRequiredTiles = true;
159+
}
97160
private void DefineSmarthBrushes()
98161
{
99162
smartBrushes = new()
@@ -110,7 +173,7 @@ private void DefineSmarthBrushes()
110173
}
111174
public void LoadData(OwMapExchangeData mapdata)
112175
{
113-
overworldMap = mapdata.DecodeMap();
176+
mapdata.DecodeMap().CopyTo(overworldMap, 0);
114177
domains = mapdata.DomainUpdates.Select(d => ((int)d.To, OwDataGroup.OwDomainsGroup[d.From])).ToList();
115178
for (int i = 0; i < 64; i++)
116179
{
@@ -139,9 +202,7 @@ public void LoadData(OwMapExchangeData mapdata)
139202
}
140203
public void ProcessTasks(OwMapExchangeData mapdata, List<EditorTask> tasks)
141204
{
142-
List<EditorTasks> managedTasks = new() { EditorTasks.OverworldLoadMap, EditorTasks.OverworldBlueMap };
143-
144-
var validtask = tasks.Where(t => managedTasks.Contains(t.Type)).ToList();
205+
var validtask = tasks.ToList();
145206

146207
foreach (var task in validtask)
147208
{
@@ -160,7 +221,16 @@ public void ProcessTasks(OwMapExchangeData mapdata, List<EditorTask> tasks)
160221
UpdatePlacedDocks = true;
161222
UpdatePlacedRequiredTiles = true;
162223
tasks.Remove(task);
163-
224+
}
225+
else if (task.Type == EditorTasks.PaintingUndo)
226+
{
227+
Undo();
228+
tasks.Remove(task);
229+
}
230+
else if (task.Type == EditorTasks.PaintingRedo)
231+
{
232+
Redo();
233+
tasks.Remove(task);
164234
}
165235
}
166236
}
@@ -284,6 +354,11 @@ public void UpdateTile(GraphicsDevice graphicsDevice, SpriteBatch spriteBatch, M
284354
{
285355
if ((!mouse.LeftDown && !mouse.LeftClick) || ((tool.Tool != ToolAction.Pencil && tool.Tool != ToolAction.Brush)))
286356
{
357+
if (!mouse.LeftDown && currentlyDrawing && (tool.Tool == ToolAction.Pencil || tool.Tool == ToolAction.Brush))
358+
{
359+
currentlyDrawing = false;
360+
}
361+
287362
return;
288363
}
289364

@@ -300,6 +375,12 @@ public void UpdateTile(GraphicsDevice graphicsDevice, SpriteBatch spriteBatch, M
300375
return;
301376
}
302377

378+
if (currentlyDrawing == false)
379+
{
380+
CreateBackup();
381+
currentlyDrawing = true;
382+
}
383+
303384
int minsize = 0;
304385
int maxsize = 0;
305386

@@ -346,6 +427,11 @@ public void PlaceTemplate(GraphicsDevice graphicsDevice, SpriteBatch spriteBatch
346427
{
347428
if ((!mouse.LeftDown && !mouse.LeftClick) || ((tool.Tool != ToolAction.Templates)))
348429
{
430+
if (!mouse.LeftDown && currentlyDrawing && tool.Tool == ToolAction.Templates)
431+
{
432+
currentlyDrawing = false;
433+
}
434+
349435
return;
350436
}
351437

@@ -362,6 +448,12 @@ public void PlaceTemplate(GraphicsDevice graphicsDevice, SpriteBatch spriteBatch
362448
return;
363449
}
364450

451+
if (currentlyDrawing == false)
452+
{
453+
CreateBackup();
454+
currentlyDrawing = true;
455+
}
456+
365457
int sizex = tool.Template.GetLength(1);
366458
int sizey = tool.Template.GetLength(0);
367459

Tools.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,8 @@ public void UpdateBrushSize(int size)
200200
("Domains", new() { new EditorTask() { Type = EditorTasks.DomainsOpen }, new EditorTask() { Type = EditorTasks.DomainsSetTool } }, new() { new EditorTask() { Type = EditorTasks.None } }),
201201
("Docks", new() { new EditorTask() { Type = EditorTasks.DocksOpen }, new EditorTask() { Type = EditorTasks.DocksSetTool } }, new() { new EditorTask() { Type = EditorTasks.None } }),
202202
("MapObjects", new() { new EditorTask() { Type = EditorTasks.MapObjectsOpen }, new EditorTask() { Type = EditorTasks.MapObjectsSetTool } }, new() { new EditorTask() { Type = EditorTasks.None } }),
203+
("Undo", new() { new EditorTask() { Type = EditorTasks.PaintingUndo } }, new() ),
204+
("Redo", new() { new EditorTask() { Type = EditorTasks.PaintingRedo } }, new() ),
203205
("", new(), new() ),
204206
("", new(), new() ),
205207
("Exit", new() { new EditorTask() { Type = EditorTasks.ExitProgram } }, new() { new EditorTask() { Type = EditorTasks.None } }),

WindowsManager.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ public enum EditorTasks
7171
LoadMapWarningOpen,
7272
LoadMapWarningClose,
7373

74+
PaintingUndo,
75+
PaintingRedo,
76+
77+
SaveBackupMap,
78+
ResetBackupCounter,
79+
7480
ExitProgramHard,
7581
ExitProgram
7682

0 commit comments

Comments
 (0)