1 module document; 2 3 import pixelperfectengine.map.mapdata; 4 import pixelperfectengine.map.mapformat; 5 import editorevents; 6 import clipboard; 7 import contmenu; 8 import windows.rasterwindow; 9 import pixelperfectengine.concrete.eventchainsystem; 10 import pixelperfectengine.concrete.interfaces : MouseEventReceptor; 11 import pixelperfectengine.concrete.types; 12 import pixelperfectengine.graphics.common : Color, Coordinate; 13 import pixelperfectengine.graphics.bitmap; 14 import pixelperfectengine.graphics.layers; 15 import pixelperfectengine.system.input : MouseButton, ButtonState, MouseClickEvent, MouseMotionEvent, MouseWheelEvent, 16 MouseEventCommons, MouseButtonFlags; 17 import std.stdio; 18 19 import app; 20 ///Individual document for parallel editing 21 public class MapDocument : MouseEventReceptor { 22 /** 23 * Specifies the current edit mode 24 */ 25 public enum EditMode { 26 selectDragScroll, 27 tilePlacement, 28 boxPlacement, 29 spritePlacement, 30 } 31 UndoableStack events; ///Per document event stack 32 MapFormat mainDoc; ///Used to reduce duplicate data as much as possible 33 //ABitmap[] delegate() imageReturnFunc; 34 Color[] delegate(MapDocument sender) paletteReturnFunc; ///Used for adding the palette for the document 35 int selectedLayer; ///Indicates the currently selected layer 36 Box mapSelection; ///Contains the selected map area parameters 37 Box areaSelection; ///Contains the selected layer area parameters in pixels 38 RasterWindow outputWindow; ///Window used to output the screen data 39 EditMode mode; ///Mose event mode selector 40 LayerInfo[] layerList; ///Local layerinfo for data lookup 41 string filename; ///Null if not yet saved, otherwise name of the target file 42 protected int prevMouseX; ///Previous mouse X position 43 protected int prevMouseY; ///Previous mouse Y position 44 45 public int sXAmount; ///Continuous X scroll amount 46 public int sYAmount; ///Continuous Y scroll amount 47 protected uint flags; ///Various status flags combined into one. 48 protected static enum VOIDFILL_EN = 1<<0; ///If set, tilePlacement overrides only transparent (0xFFFF) tiles. 49 protected static enum LAYER_SCROLL = 1<<1;///If set, then layer is being scrolled. 50 protected static enum PLACEMENT = 1<<2; ///To solve some "debounce" issues around mouse click releases 51 protected static enum DISPL_SELECTION = 1<<3; ///If set, then selection is shown 52 protected MappingElement selectedMappingElement; ///Currently selected mapping element to write, including mirroring properties, palette selection, and priority attributes 53 //public bool voidfill; ///If true, tilePlacement overrides only transparent (0xFFFF) tiles. 54 /** 55 * Loads the document from disk. 56 */ 57 public this(string filename) @trusted { 58 mainDoc = new MapFormat(File(filename)); 59 events = new UndoableStack(20); 60 mode = EditMode.selectDragScroll; 61 } 62 ///New from scratch 63 public this(string docName, int resX, int resY) @trusted { 64 events = new UndoableStack(20); 65 mainDoc = new MapFormat(docName, resX, resY); 66 mode = EditMode.selectDragScroll; 67 } 68 ///Returns the next available layer number. 69 public int nextLayerNumber() @safe { 70 int result = selectedLayer; 71 do { 72 if (mainDoc[result] is null) 73 return result; 74 result++; 75 } while (true); 76 } 77 ///Puts the loaded tiles onto a TileLayer 78 public void addTileSet(int layer, ABitmap[ushort] tiles) @trusted { 79 ITileLayer itl = cast(ITileLayer)mainDoc[layer]; 80 foreach (i ; tiles.byKey) { 81 itl.addTile(tiles[i], i); 82 } 83 } 84 ///Ditto 85 public void addTileSet(int layer, ABitmap[] tiles) @trusted { 86 ITileLayer itl = cast(ITileLayer)mainDoc[layer]; 87 for (ushort i ; i < tiles.length ; i++) { 88 itl.addTile(tiles[i], i); 89 } 90 } 91 /** 92 * Clears selection area. 93 */ 94 public void clearSelection() { 95 if (outputWindow) { 96 outputWindow.disarmSelection(); 97 outputWindow.showSelection = false; 98 outputWindow.updateRaster(); 99 } 100 } 101 /** 102 * Scrolls the selected layer by a given amount. 103 */ 104 public void scrollSelectedLayer (int x, int y) { 105 if (selectedLayer in mainDoc.layeroutput) { 106 mainDoc[selectedLayer].relScroll(x, y); 107 } 108 //outputWindow.updateRaster(); 109 updateSelection(); 110 } 111 /** 112 * Sets the continuous scroll amounts. 113 */ 114 public void setContScroll (int x, int y) { 115 sXAmount = x; 116 sYAmount = y; 117 } 118 /** 119 * Moves the selected area by the given amounts. 120 */ 121 public void moveSelection(int x, int y) { 122 if (selectedLayer in mainDoc.layeroutput) { 123 if (getLayerInfo(selectedLayer).type == LayerType.Tile) { 124 ITileLayer target = cast(ITileLayer)(mainDoc[selectedLayer]); 125 const int tileWidth = target.getTileWidth, tileHeight = target.getTileHeight; 126 areaSelection.relMove(x * tileWidth, y * tileHeight); 127 mapSelection.relMove(x, y); 128 updateSelection(); 129 } 130 } 131 } 132 /** 133 * Updates the selection on the raster window. 134 */ 135 public void updateSelection() { 136 if (selectedLayer in mainDoc.layeroutput) { 137 const int sX = mainDoc[selectedLayer].getSX, sY = mainDoc[selectedLayer].getSY; 138 Box onscreen = Box(sX, sY, sX + outputWindow.rasterX - 1, sY + outputWindow.rasterY - 1); 139 if (onscreen.isBetween(areaSelection.cornerUL) || onscreen.isBetween(areaSelection.cornerUR) || 140 onscreen.isBetween(areaSelection.cornerLL) || onscreen.isBetween(areaSelection.cornerLR)) { 141 outputWindow.selection = areaSelection; 142 outputWindow.selection.relMove(sX * -1, sY * -1); 143 144 outputWindow.selection.left = outputWindow.selection.left < 0 ? 0 : outputWindow.selection.left; 145 outputWindow.selection.left = outputWindow.selection.left >= outputWindow.rasterX ? outputWindow.rasterX - 1 : 146 outputWindow.selection.left; 147 outputWindow.selection.right = outputWindow.selection.right < 0 ? 0 : outputWindow.selection.right; 148 outputWindow.selection.right = outputWindow.selection.right >= outputWindow.rasterX ? outputWindow.rasterX - 1 : 149 outputWindow.selection.right; 150 151 outputWindow.selection.top = outputWindow.selection.top < 0 ? 0 : outputWindow.selection.top; 152 outputWindow.selection.top = outputWindow.selection.top >= outputWindow.rasterY ? outputWindow.rasterY - 1 : 153 outputWindow.selection.top; 154 outputWindow.selection.bottom = outputWindow.selection.bottom < 0 ? 0 : outputWindow.selection.bottom; 155 outputWindow.selection.bottom = outputWindow.selection.bottom >= outputWindow.rasterX ? outputWindow.rasterX - 1 : 156 outputWindow.selection.bottom; 157 } else { 158 outputWindow.selection = Box (0, 0, -1, -1); 159 } 160 outputWindow.updateRaster(); 161 } 162 } 163 /** 164 * Scrolls the selected layer by the amount set. 165 * Should be called for every frame. 166 */ 167 public void contScrollLayer () { 168 if(sXAmount || sYAmount){ 169 scrollSelectedLayer (sXAmount, sYAmount); 170 } 171 } 172 /** 173 * Updates the material list for the selected layer. 174 */ 175 public void updateMaterialList () { 176 if (selectedLayer in mainDoc.layeroutput) { 177 if (prg.materialList !is null) { 178 TileInfo[] list = mainDoc.getTileInfo(selectedLayer); 179 //writeln(list.length); 180 prg.materialList.updateMaterialList(list); 181 } 182 } 183 } 184 /** 185 * Updates the layers for this document. 186 */ 187 public void updateLayerList () { 188 layerList = mainDoc.getLayerInfo; 189 if (prg.layerList !is null) { 190 prg.layerList.updateLayerList(layerList); 191 //prg.wh.layerlist 192 } 193 } 194 public void onSelection () { 195 updateLayerList; 196 updateMaterialList; 197 } 198 public void tileMaterial_FlipHorizontal(bool pos) { 199 selectedMappingElement.attributes.horizMirror = pos; 200 } 201 public void tileMaterial_FlipHorizontal() { 202 selectedMappingElement.attributes.horizMirror = !selectedMappingElement.attributes.horizMirror; 203 } 204 public void tileMaterial_FlipVertical(bool pos) { 205 selectedMappingElement.attributes.vertMirror = pos; 206 } 207 public void tileMaterial_FlipVertical() { 208 selectedMappingElement.attributes.vertMirror = !selectedMappingElement.attributes.vertMirror; 209 } 210 public void tileMaterial_Select(wchar id) { 211 selectedMappingElement.tileID = id; 212 //mode = EditMode.tilePlacement; 213 } 214 public void tileMaterial_Up() { 215 selectedMappingElement.tileID++; 216 } 217 public void tileMaterial_Down() { 218 selectedMappingElement.tileID--; 219 } 220 public ushort tileMaterial_PaletteUp() { 221 selectedMappingElement.paletteSel++; 222 return selectedMappingElement.paletteSel; 223 } 224 public ushort tileMaterial_PaletteDown() { 225 selectedMappingElement.paletteSel--; 226 return selectedMappingElement.paletteSel; 227 } 228 public @property bool voidfill() @nogc @safe pure nothrow { 229 return flags & VOIDFILL_EN; 230 } 231 public @property bool voidfill(bool val) @nogc @safe pure nothrow { 232 if (val) flags |= VOIDFILL_EN; 233 else flags &= ~VOIDFILL_EN; 234 return flags & VOIDFILL_EN; 235 } 236 237 public void passMCE(MouseEventCommons mec, MouseClickEvent mce) { 238 int x = mce.x, y = mce.y; 239 final switch (mode) with (EditMode) { 240 case tilePlacement: 241 switch (mce.button) { 242 case MouseButton.Left: //Tile placement 243 //Record the first cursor position upon mouse button press, then initialize either a single or zone write for the selected tile layer. 244 if (selectedLayer !in mainDoc.layeroutput) return; //Safety protection 245 if (mce.state) { 246 prevMouseX = x; 247 prevMouseY = y; 248 flags |= PLACEMENT; 249 } else if (flags & PLACEMENT) { 250 flags &= ~PLACEMENT; 251 ITileLayer target = cast(ITileLayer)(mainDoc[selectedLayer]); 252 const int tileWidth = target.getTileWidth, tileHeight = target.getTileHeight; 253 const int hScroll = mainDoc[selectedLayer].getSX, vScroll = mainDoc[selectedLayer].getSX; 254 x = (x + hScroll) / tileWidth; 255 y = (y + vScroll) / tileHeight; 256 prevMouseX = (prevMouseX + hScroll) / tileWidth; 257 prevMouseY = (prevMouseY + vScroll) / tileHeight; 258 Box c; 259 260 if (x > prevMouseX){ 261 c.left = prevMouseX; 262 c.right = x; 263 } else { 264 c.left = x; 265 c.right = prevMouseX; 266 } 267 268 if (y > prevMouseY){ 269 c.top = prevMouseY; 270 c.bottom = y; 271 } else { 272 c.top = y; 273 c.bottom = prevMouseY; 274 } 275 276 if (voidfill) { 277 if (c.width == 1 && c.height == 1) { 278 if (target.readMapping(c.left, c.top).tileID == 0xFFFF) 279 events.addToTop(new WriteToMapSingle(target, c.left, c.top, selectedMappingElement)); 280 } else { 281 events.addToTop(new WriteToMapVoidFill(target, c, selectedMappingElement)); 282 } 283 } else { 284 if (c.width == 1 && c.height == 1) { 285 events.addToTop(new WriteToMapSingle(target, c.left, c.top, selectedMappingElement)); 286 } else { 287 events.addToTop(new WriteToMapOverwrite(target, c, selectedMappingElement)); 288 } 289 } 290 } 291 outputWindow.updateRaster(); 292 break; 293 case MouseButton.Mid: //Tile deletion 294 //Record the first cursor position upon mouse button press, then initialize either a single or zone delete for the selected tile layer. 295 if (mce.state) { 296 prevMouseX = x; 297 prevMouseY = y; 298 flags |= PLACEMENT; 299 } else if (flags & PLACEMENT) { 300 flags &= ~PLACEMENT; 301 ITileLayer target = cast(ITileLayer)(mainDoc[selectedLayer]); 302 x = (x + mainDoc[selectedLayer].getSX) / target.getTileWidth; 303 y = (y + mainDoc[selectedLayer].getSY) / target.getTileHeight; 304 prevMouseX = (prevMouseX + mainDoc[selectedLayer].getSX) / target.getTileWidth; 305 prevMouseY = (prevMouseY + mainDoc[selectedLayer].getSY) / target.getTileHeight; 306 307 Box c; 308 309 if (x > prevMouseX){ 310 c.left = prevMouseX; 311 c.right = x; 312 } else { 313 c.left = x; 314 c.right = prevMouseX; 315 } 316 317 if (y > prevMouseY){ 318 c.top = prevMouseY; 319 c.bottom = y; 320 } else { 321 c.top = y; 322 c.bottom = prevMouseY; 323 } 324 325 if (c.width == 1 && c.height == 1) { 326 events.addToTop(new WriteToMapSingle(target, c.left, c.top, MappingElement(0xFFFF))); 327 } else { 328 events.addToTop(new WriteToMapOverwrite(target, c, MappingElement(0xFFFF))); 329 } 330 } 331 outputWindow.updateRaster(); 332 break; 333 case MouseButton.Right: 334 if (mce.state) prg.wh.addPopUpElement(createTilePlacementContextMenu(&onSelectContextMenuSelect)); 335 break; 336 default: 337 break; 338 } 339 break; 340 case selectDragScroll: 341 switch (mce.button) { 342 case MouseButton.Left: 343 344 //Initialize drag select 345 if (mce.state) { 346 prevMouseX = x; 347 prevMouseY = y; 348 outputWindow.armSelection; 349 outputWindow.selection.left = x; 350 outputWindow.selection.top = y; 351 outputWindow.selection.right = x; 352 outputWindow.selection.bottom = y; 353 } else { 354 outputWindow.disarmSelection; 355 Box c = outputWindow.selection; 356 if (getLayerInfo(selectedLayer).type != LayerType.init) { 357 Layer l = mainDoc.layeroutput[selectedLayer]; 358 areaSelection = c; 359 areaSelection.relMove(l.getSX, l.getSY); 360 361 switch (getLayerInfo(selectedLayer).type) { 362 case LayerType.Tile: 363 TileLayer tl = cast(TileLayer)l; 364 const int tileWidth = tl.getTileWidth, tileHeight = tl.getTileHeight, 365 mapWidth = tl.getMX, mapHeight = tl.getMY; 366 //calculate selected map area 367 mapSelection.left = areaSelection.left / tileWidth; 368 mapSelection.right = areaSelection.right / tileWidth; /+ + (areaSelection.right % tileWidth > 0 ? 1 : 0); +/ 369 mapSelection.top = areaSelection.top / tileHeight; 370 mapSelection.bottom = areaSelection.bottom / tileHeight; /+ + (areaSelection.bottom % tileHeight > 0 ? 1 : 0); +/ 371 //Clamp map sizes between what map has 372 import pixelperfectengine.system.etc : clamp; 373 374 clamp(mapSelection.left, 0, mapWidth); 375 clamp(mapSelection.right, 0, mapHeight); 376 clamp(mapSelection.top, 0, mapWidth); 377 clamp(mapSelection.bottom, 0, mapHeight); 378 //adjust displayed selection to mapSelection 379 areaSelection.left = mapSelection.left * tileWidth; 380 areaSelection.right = (mapSelection.right + 1) * tileWidth;//areaSelection.right = mapSelection.right * tileWidth; 381 areaSelection.top = mapSelection.top * tileHeight; 382 areaSelection.bottom = (mapSelection.bottom + 1) * tileHeight;//areaSelection.bottom = mapSelection.bottom * tileHeight; 383 break; 384 385 default: 386 break; 387 } 388 } 389 outputWindow.disarmSelection(); 390 outputWindow.showSelection = true; 391 updateSelection(); 392 } 393 break; 394 case MouseButton.Mid: 395 396 if (mce.state) { 397 outputWindow.requestCursor(CursorType.Hand); 398 prevMouseX = x; 399 prevMouseY = y; 400 outputWindow.moveEn = true; 401 } else { 402 outputWindow.requestCursor(CursorType.Arrow); 403 outputWindow.moveEn = false; 404 } 405 //scrollSelectedLayer(prevMouseX - x, prevMouseY - y); 406 407 prevMouseX = x; 408 prevMouseY = y; 409 break; 410 case MouseButton.Right: 411 if (mce.state) prg.wh.addPopUpElement(createSelectContextMenu(&onSelectContextMenuSelect)); 412 break; 413 default: 414 break; 415 } 416 break; 417 case boxPlacement: 418 break; 419 case spritePlacement: 420 break; 421 } 422 } 423 424 public void passMME(MouseEventCommons mec, MouseMotionEvent mme) { 425 final switch (mode) with (EditMode) { 426 case selectDragScroll: 427 switch (mme.buttonState) { 428 case MouseButtonFlags.Left: 429 Box s; 430 if (prevMouseX < mme.x) { 431 s.left = prevMouseX; 432 s.right = mme.x; 433 } else { 434 s.left = mme.x; 435 s.right = prevMouseX; 436 } 437 if (prevMouseY < mme.y) { 438 s.top = prevMouseY; 439 s.bottom = mme.y; 440 } else { 441 s.top = mme.y; 442 s.bottom = prevMouseY; 443 } 444 outputWindow.selection = s; 445 outputWindow.updateRaster(); 446 break; 447 case MouseButtonFlags.Mid: 448 scrollSelectedLayer(prevMouseX - mme.x, prevMouseY - mme.y); 449 prevMouseX = mme.x; 450 prevMouseY = mme.y; 451 outputWindow.updateRaster(); 452 break; 453 default: 454 break; 455 } 456 break; 457 case tilePlacement: 458 break; 459 case boxPlacement: 460 break; 461 case spritePlacement: 462 break; 463 } 464 } 465 466 public void passMWE(MouseEventCommons mec, MouseWheelEvent mwe) { 467 if (outputWindow.isSelectionArmed()) 468 scrollSelectedLayer(mwe.x, mwe.y); 469 } 470 471 public LayerInfo getLayerInfo(int pri) nothrow { 472 foreach (key; layerList) { 473 if (key.pri == pri) 474 return key; 475 } 476 return LayerInfo.init; 477 } 478 /** 479 * Copies an area on a tilelayer to a MapClipboard item. 480 */ 481 protected MapClipboard.Item createMapClipboardItem() { 482 MapClipboard.Item result; 483 const LayerType lt = getLayerInfo(selectedLayer).type; 484 if (lt == LayerType.Tile || lt == LayerType.TransformableTile) { 485 result.map.length = mapSelection.area; 486 result.width = mapSelection.width; 487 result.height = mapSelection.height; 488 ITileLayer tl = cast(ITileLayer)mainDoc.layeroutput[selectedLayer]; 489 for (int y ; y < mapSelection.height ; y++) { 490 for (int x ; x < mapSelection.width; x++) { 491 result.map[x + (mapSelection.width * y)] = tl.readMapping(mapSelection.left + x, mapSelection.top + y); 492 } 493 } 494 } 495 return result; 496 } 497 /** 498 * Creates a copy event if called. 499 * Uses the internal states of this document. 500 */ 501 public void copy() { 502 switch (getLayerInfo(selectedLayer).type) { 503 case LayerType.Tile, LayerType.TransformableTile: 504 MapClipboard.Item area = createMapClipboardItem(); 505 prg.mapClipboard.addItem(area); 506 break; 507 default: 508 break; 509 } 510 outputWindow.updateRaster(); 511 } 512 /** 513 * Creates a cut event if called. 514 * Uses the internal states of this document. 515 */ 516 public void cut() { 517 switch (getLayerInfo(selectedLayer).type) { 518 case LayerType.Tile, LayerType.TransformableTile: 519 MapClipboard.Item area = createMapClipboardItem(); 520 prg.mapClipboard.addItem(area); 521 events.addToTop(new CutFromTileLayerEvent(cast(ITileLayer)mainDoc.layeroutput[selectedLayer], mapSelection)); 522 break; 523 default: 524 break; 525 } 526 outputWindow.updateRaster(); 527 } 528 /** 529 * Deletes the selected area. 530 */ 531 public void deleteArea() { 532 switch (getLayerInfo(selectedLayer).type) { 533 case LayerType.Tile, LayerType.TransformableTile: 534 events.addToTop(new CutFromTileLayerEvent(cast(ITileLayer)mainDoc.layeroutput[selectedLayer], mapSelection)); 535 break; 536 default: 537 break; 538 } 539 outputWindow.updateRaster(); 540 } 541 /** 542 * Creates a paste event if called. 543 * Uses the internal states of this document. 544 */ 545 public void paste(size_t which = 0) { 546 switch (getLayerInfo(selectedLayer).type) { 547 case LayerType.Tile, LayerType.TransformableTile: 548 events.addToTop(new PasteIntoTileLayerEvent(prg.mapClipboard.getItem(which), 549 cast(ITileLayer)(mainDoc.layeroutput[selectedLayer]), mapSelection.cornerUL, voidfill)); 550 break; 551 default: 552 break; 553 } 554 outputWindow.updateRaster(); 555 } 556 /** 557 * Context menu for selection events go here 558 */ 559 protected void onSelectContextMenuSelect (Event ev) { 560 MenuEvent me = cast(MenuEvent)ev; 561 switch (me.itemSource) { 562 case "copy": 563 copy; 564 break; 565 case "cut": 566 cut; 567 break; 568 case "paste": 569 paste; 570 break; 571 case "flph": 572 flipTilesHoriz(); 573 break; 574 case "flpv": 575 flipTilesVert(); 576 break; 577 case "mirh": 578 selMirrorHoriz(); 579 break; 580 case "mirv": 581 selMirrorVert(); 582 break; 583 case "shp": 584 break; 585 case "hm": 586 selectedMappingElement.attributes.horizMirror = !selectedMappingElement.attributes.horizMirror; 587 break; 588 case "vm": 589 selectedMappingElement.attributes.vertMirror = !selectedMappingElement.attributes.vertMirror; 590 break; 591 case "p+": 592 selectedMappingElement.paletteSel++; 593 break; 594 case "p-": 595 selectedMappingElement.paletteSel--; 596 break; 597 default: 598 break; 599 } 600 } 601 /** 602 * Removes a tile from the material list. 603 */ 604 public void removeTile(int id) { 605 if (mainDoc.layeroutput[selectedLayer]) 606 events.addToTop(new RemoveTile(id, this, selectedLayer)); 607 } 608 /** 609 * Renames a tile on the material list. 610 */ 611 public void renameTile(int id, string name) { 612 if (mainDoc.layeroutput[selectedLayer]) 613 events.addToTop(new RenameTile(id, this, selectedLayer, name)); 614 } 615 /** 616 * Removes a layer. 617 */ 618 public void removeLayer() { 619 if (mainDoc.layeroutput[selectedLayer]) 620 events.addToTop(new RemoveLayer(this, selectedLayer)); 621 } 622 /** 623 * Renames a layer. 624 */ 625 public void renameLayer(string newName) { 626 if (mainDoc.layeroutput[selectedLayer]) 627 events.addToTop(new RenameLayer(this, selectedLayer, newName)); 628 } 629 /** 630 * Moves the priority of a layer. 631 */ 632 public void changeLayerPriority(int newPri) { 633 if (mainDoc.layeroutput[newPri]) { 634 prg.wh.message("Layer edit error!", "Layer priority is already in use or invalid!"); 635 } else if (mainDoc.layeroutput[selectedLayer]) { 636 events.addToTop(new ChangeLayerPriority(this, selectedLayer, newPri)); 637 } 638 } 639 /** 640 * Mirrors selected items horizontally. 641 */ 642 public void selMirrorHoriz() { 643 switch (getLayerInfo(selectedLayer).type) { 644 case LayerType.Tile, LayerType.TransformableTile: 645 events.addToTop(new MirrorSelHTL(cast(ITileLayer)(mainDoc.layeroutput[selectedLayer]), mapSelection)); 646 break; 647 default: 648 break; 649 } 650 } 651 /** 652 * Mirrors selected items vertically. 653 */ 654 public void selMirrorVert() { 655 switch (getLayerInfo(selectedLayer).type) { 656 case LayerType.Tile, LayerType.TransformableTile: 657 events.addToTop(new MirrorSelVTL(cast(ITileLayer)(mainDoc.layeroutput[selectedLayer]), mapSelection)); 658 outputWindow.updateRaster(); 659 break; 660 default: 661 break; 662 } 663 } 664 /** 665 * Mirrors selected items horizontally and vertically. 666 */ 667 public void selMirrorBoth() { 668 switch (getLayerInfo(selectedLayer).type) { 669 case LayerType.Tile, LayerType.TransformableTile: 670 events.addToTop(new MirrorSelBTL(cast(ITileLayer)(mainDoc.layeroutput[selectedLayer]), mapSelection)); 671 outputWindow.updateRaster(); 672 break; 673 default: 674 break; 675 } 676 } 677 /** 678 * Flips selected tiles horizontally. 679 */ 680 public void flipTilesHoriz() { 681 if (getLayerInfo(selectedLayer).type == LayerType.Tile || 682 getLayerInfo(selectedLayer).type == LayerType.TransformableTile) { 683 events.addToTop(new FlipSelTilesH(cast(ITileLayer)(mainDoc.layeroutput[selectedLayer]), mapSelection)); 684 outputWindow.updateRaster(); 685 } 686 } 687 /** 688 * Flips selected tiles vertically. 689 */ 690 public void flipTilesVert() { 691 if (getLayerInfo(selectedLayer).type == LayerType.Tile || 692 getLayerInfo(selectedLayer).type == LayerType.TransformableTile) { 693 events.addToTop(new FlipSelTilesV(cast(ITileLayer)(mainDoc.layeroutput[selectedLayer]), mapSelection)); 694 outputWindow.updateRaster(); 695 } 696 } 697 /** 698 * Fills selected area with selected tile, using overwrite rules defined by the materiallist. 699 */ 700 public void fillSelectedArea() { 701 if (getLayerInfo(selectedLayer).type == LayerType.Tile || 702 getLayerInfo(selectedLayer).type == LayerType.TransformableTile) { 703 ITileLayer target = cast(ITileLayer)mainDoc.layeroutput[selectedLayer]; 704 if (voidfill) { 705 events.addToTop(new WriteToMapVoidFill(target, mapSelection, selectedMappingElement)); 706 } else { 707 events.addToTop(new WriteToMapOverwrite(target, mapSelection, selectedMappingElement)); 708 } 709 outputWindow.updateRaster(); 710 } 711 } 712 /** 713 * Assigns an imported tilemap to the currently selected layer. 714 */ 715 public void assignImportedTilemap(MappingElement[] map, int w, int h) { 716 if (getLayerInfo(selectedLayer).type == LayerType.Tile || 717 getLayerInfo(selectedLayer).type == LayerType.TransformableTile) { 718 events.addToTop(new ImportLayerData(cast(ITileLayer)mainDoc.layeroutput[selectedLayer], mainDoc.layerData[selectedLayer], map, w, 719 h)); 720 } 721 } 722 }