@@ -28,12 +28,23 @@ public partial class MainWindow : Window
2828 private Tileset _tileset ;
2929 private Map _map ;
3030
31- private int _selectedTile = - 1 ;
31+ private byte _selectedTile ;
3232 private GeometryDrawing _selectedTileDrawing = new GeometryDrawing ( ) ;
3333 private WriteableBitmap [ ] _rowBitmaps ;
3434
3535 private bool _painting = false ;
3636
37+ private struct PaintedTile
38+ {
39+ public int X , Y ;
40+ public byte OldTile ;
41+ public byte NewTile ;
42+ }
43+
44+ private List < PaintedTile > _paintingTiles = new List < PaintedTile > ( ) ;
45+ private readonly Stack < List < PaintedTile > > _undoStack = new Stack < List < PaintedTile > > ( ) ;
46+ private readonly Stack < List < PaintedTile > > _redoStack = new Stack < List < PaintedTile > > ( ) ;
47+
3748 private int SpaceUsed
3849 {
3950 set
@@ -220,7 +231,7 @@ private void Tileset_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
220231
221232 private void SelectTile ( int x , int y )
222233 {
223- _selectedTile = 16 * y + x ;
234+ _selectedTile = ( byte ) ( 16 * y + x ) ;
224235
225236 HighlightSelectedTile ( x , y ) ;
226237
@@ -264,44 +275,127 @@ private void CheckTilePropertyBoxes()
264275
265276 private void Map_MouseLeftButtonDown ( object sender , MouseButtonEventArgs e )
266277 {
267- if ( _selectedTile == - 1 )
278+ GetClickedTile ( sender , e , out int x , out int y ) ;
279+ if ( x < 0 || x >= _map . Width || y < 0 || y >= _map . Height )
268280 {
269281 return ;
270282 }
271283
272- GetClickedTile ( sender , e , out int x , out int y ) ;
273- if ( x < 0 || x >= _map . Width || y < 0 || y >= _map . Height )
284+ if ( FloodFillButton . IsChecked == true )
274285 {
275- return ;
286+ FloodFill ( x , y ) ;
287+ }
288+ else
289+ {
290+ Paint ( x , y ) ;
291+ _painting = true ;
276292 }
277-
278- Paint ( x , y ) ;
279- _painting = true ;
280293 }
281294
282295 private void Map_MouseLeftButtonUp ( object sender , MouseButtonEventArgs e )
283296 {
284297 _painting = false ;
298+
299+ if ( _paintingTiles . Count > 0 )
300+ {
301+ RecompressMap ( _paintingTiles ) ;
302+
303+ _undoStack . Push ( _paintingTiles ) ;
304+ _redoStack . Clear ( ) ;
305+ _paintingTiles = new List < PaintedTile > ( ) ;
306+ }
307+ }
308+
309+ private void RecompressMap ( List < PaintedTile > paintingTiles )
310+ {
311+ var rowsToCompress = paintingTiles . Select ( tile => tile . Y ) . Distinct ( ) ;
312+ foreach ( var row in rowsToCompress )
313+ {
314+ _map . CompressRow ( row ) ;
315+ }
316+
317+ SpaceUsed = _map . CompressedSize ;
318+ }
319+
320+ private void Map_MouseRightButtonDown ( object sender , MouseButtonEventArgs e )
321+ {
322+ GetClickedTile ( sender , e , out int x , out int y ) ;
323+ if ( x < 0 || x >= _map . Width || y < 0 || y >= _map . Height || _map [ y , x ] == _selectedTile )
324+ {
325+ return ;
326+ }
327+
328+ var selectedTile = _map [ y , x ] ;
329+ SelectTile ( selectedTile % 16 , selectedTile / 16 ) ;
285330 }
286331
287332 private void Map_MouseMove ( object sender , MouseEventArgs e )
288333 {
289- if ( _painting )
334+ GetClickedTile ( sender , e , out int x , out int y ) ;
335+ CoordinatesLabel . Content = $ "Coordinates: ({ x : X2} , { y : X2} )";
336+
337+ if ( _painting && ! ( x < 0 || x >= _map . Width || y < 0 || y >= _map . Height ) && _map [ y , x ] != _selectedTile )
290338 {
291- GetClickedTile ( sender , e , out int x , out int y ) ;
292339 Paint ( x , y ) ;
293340 }
294341 }
295342
296343 private void Paint ( int x , int y )
297344 {
298- _map [ y , x ] = ( byte ) _selectedTile ;
345+ _paintingTiles . Add ( new PaintedTile
346+ {
347+ X = x ,
348+ Y = y ,
349+ OldTile = _map [ y , x ] ,
350+ NewTile = _selectedTile
351+ } ) ;
352+
353+ SetTile ( x , y , _selectedTile ) ;
354+ }
355+
356+ private void FloodFill ( int x , int y )
357+ {
358+ var queue = new Queue < ( int x , int y ) > ( ) ;
359+ var fillingTile = _map [ y , x ] ;
360+ if ( fillingTile != _selectedTile )
361+ {
362+ Paint ( x , y ) ;
363+ queue . Enqueue ( ( x , y ) ) ;
364+ }
365+
366+ while ( queue . Count > 0 )
367+ {
368+ ( x , y ) = queue . Dequeue ( ) ;
369+ if ( x > 0 && _map [ y , x - 1 ] == fillingTile )
370+ {
371+ Paint ( x - 1 , y ) ;
372+ queue . Enqueue ( ( x - 1 , y ) ) ;
373+ }
374+ if ( x < _map . Width - 1 && _map [ y , x + 1 ] == fillingTile )
375+ {
376+ Paint ( x + 1 , y ) ;
377+ queue . Enqueue ( ( x + 1 , y ) ) ;
378+ }
379+ if ( y > 0 && _map [ y - 1 , x ] == fillingTile )
380+ {
381+ Paint ( x , y - 1 ) ;
382+ queue . Enqueue ( ( x , y - 1 ) ) ;
383+ }
384+ if ( y < _map . Height - 1 && _map [ y + 1 , x ] == fillingTile )
385+ {
386+ Paint ( x , y + 1 ) ;
387+ queue . Enqueue ( ( x , y + 1 ) ) ;
388+ }
389+ }
390+ }
391+
392+ private void SetTile ( int x , int y , byte tile )
393+ {
394+ _map [ y , x ] = tile ;
299395
300396 _rowBitmaps [ y ] . Lock ( ) ;
301- _rowBitmaps [ y ] . WritePixels ( new Int32Rect ( 16 * x , 0 , 16 , 16 ) , _tileset [ _selectedTile ] , 16 * 2 , 0 ) ;
397+ _rowBitmaps [ y ] . WritePixels ( new Int32Rect ( 16 * x , 0 , 16 , 16 ) , _tileset [ tile ] , 16 * 2 , 0 ) ;
302398 _rowBitmaps [ y ] . Unlock ( ) ;
303-
304- SpaceUsed = _map . CompressedSize ;
305399 }
306400
307401 private void GetClickedTile ( object sender , MouseButtonEventArgs e , out int x , out int y )
@@ -324,6 +418,58 @@ private static void PixelsToTile(Point position, out int x, out int y)
324418 y /= 16 ;
325419 }
326420
421+ private void Undo_Executed ( object sender , ExecutedRoutedEventArgs args )
422+ {
423+ Undo ( ) ;
424+ }
425+
426+ private void Redo_Executed ( object sender , ExecutedRoutedEventArgs args )
427+ {
428+ Redo ( ) ;
429+ }
430+
431+ private void UndoButton_OnClick ( object sender , RoutedEventArgs e )
432+ {
433+ Undo ( ) ;
434+ }
435+
436+ private void RedoButton_OnClick ( object sender , RoutedEventArgs e )
437+ {
438+ Redo ( ) ;
439+ }
440+
441+ private void Undo ( )
442+ {
443+ if ( _undoStack . Count > 0 )
444+ {
445+ var undo = _undoStack . Pop ( ) ;
446+ foreach ( var painting in undo )
447+ {
448+ SetTile ( painting . X , painting . Y , painting . OldTile ) ;
449+ }
450+
451+ _redoStack . Push ( undo ) ;
452+
453+ RecompressMap ( undo ) ;
454+ }
455+ }
456+
457+ private void Redo ( )
458+ {
459+ if ( _redoStack . Count > 0 )
460+ {
461+ var redo = _redoStack . Pop ( ) ;
462+ foreach ( var painting in redo )
463+ {
464+ SetTile ( painting . X , painting . Y , painting . NewTile ) ;
465+ }
466+
467+ _undoStack . Push ( redo ) ;
468+
469+ RecompressMap ( redo ) ;
470+ }
471+ }
472+
327473 private void ScrollViewer_ScrollChanged ( object sender , ScrollChangedEventArgs e )
328474 {
329475 MapScrollViewer . ScrollToHorizontalOffset ( Math . Round ( MapScrollViewer . HorizontalOffset ) ) ;
0 commit comments