1 module editorevents; 2 3 import document; 4 5 public import PixelPerfectEngine.concrete.eventChainSystem; 6 public import PixelPerfectEngine.graphics.layers; 7 public import PixelPerfectEngine.map.mapformat; 8 9 import PixelPerfectEngine.system.file; 10 11 import std.stdio; 12 import std.conv : to; 13 import sdlang; 14 15 public class WriteToMapVoidFill : UndoableEvent { 16 ITileLayer target; 17 Coordinate area; 18 MappingElement me; 19 MappingElement[] original; 20 public this(ITileLayer target, Coordinate area, MappingElement me){ 21 this.target = target; 22 this.area = area; 23 this.me = me; 24 //mask.length = area.area; 25 } 26 public void redo() { 27 for(int y = area.top ; y <= area.bottom ; y++){ 28 for(int x = area.left ; x <= area.right ; x++){ 29 MappingElement o = target.readMapping(x,y); 30 original ~= o; 31 if (o.tileID == 0xFFFF) 32 target.writeMapping(x,y,me); 33 } 34 } 35 } 36 public void undo() { 37 size_t pos; 38 for(int y = area.top ; y <= area.bottom ; y++){ 39 for(int x = area.left ; x <= area.right ; x++){ 40 target.writeMapping(x,y,original[pos]); 41 pos++; 42 } 43 } 44 } 45 } 46 47 public class WriteToMapOverwrite : UndoableEvent { 48 ITileLayer target; 49 Coordinate area; 50 MappingElement me; 51 MappingElement[] original; 52 public this(ITileLayer target, Coordinate area, MappingElement me){ 53 this.target = target; 54 this.area = area; 55 this.me = me; 56 //original.length = (area.width + 1) * (area.height + 1); 57 } 58 public void redo() { 59 for(int y = area.top ; y <= area.bottom ; y++){ 60 for(int x = area.left ; x <= area.right ; x++){ 61 original ~= target.readMapping(x,y); 62 target.writeMapping(x,y,me); 63 } 64 } 65 } 66 public void undo() { 67 size_t pos; 68 for(int y = area.top ; y <= area.bottom ; y++){ 69 for(int x = area.left ; x <= area.right ; x++){ 70 target.writeMapping(x,y,original[pos]); 71 pos++; 72 } 73 } 74 } 75 } 76 77 public class WriteToMapSingle : UndoableEvent { 78 ITileLayer target; 79 int x; 80 int y; 81 MappingElement me; 82 MappingElement original; 83 public this(ITileLayer target, int x, int y, MappingElement me) { 84 this.target = target; 85 this.x = x; 86 this.y = y; 87 this.me = me; 88 } 89 public void redo() { 90 original = target.readMapping(x,y); 91 target.writeMapping(x,y,me); 92 /*debug { 93 import std.stdio : writeln; 94 writeln("Layer was written at position ", x, ";", y," with values ", target.readMapping(x,y)); 95 }*/ 96 } 97 public void undo() { 98 target.writeMapping(x,y,original); 99 } 100 } 101 102 public class CreateTileLayerEvent : UndoableEvent { 103 TileLayer creation; 104 MapDocument target; 105 int tX; 106 int tY; 107 int mX; 108 int mY; 109 int pri; 110 string name; 111 string file; 112 bool embed; 113 Tag backup; 114 115 public this(MapDocument target, int tX, int tY, int mX, int mY, dstring name, string file, bool embed) { 116 import std.utf : toUTF8; 117 creation = new TileLayer(tX, tY); 118 creation.setRenderingMode(RenderingMode.Copy); 119 this.target = target; 120 //this.md = md; 121 this.tX = tX; 122 this.tY = tY; 123 this.mX = mX; 124 this.mY = mY; 125 this.name = toUTF8(name); 126 this.file = file; 127 this.embed = embed; 128 //this.imageReturnFunc = imageReturnFunc; 129 } 130 public void redo() { 131 import std.file : exists, isFile; 132 import std.path : baseName; 133 import std.utf : toUTF8; 134 import PixelPerfectEngine.system.etc : intToHex; 135 if (backup) { //If a backup exists, then re-add that to the document, then return. 136 target.mainDoc.addNewLayer(pri, backup, creation); 137 target.outputWindow.addLayer(pri); 138 return; 139 } 140 try { 141 const int nextLayer = target.nextLayerNumber; 142 143 //handle the following instances for mapping: 144 //file == null AND embed 145 //file == existing file AND embed 146 //file == existing file AND !embed 147 //file == nonexisting file 148 if ((!exists(file) || !isFile(file)) && embed) { //create new instance for the map by embedding data into the SDLang file 149 //selDoc.mainDoc.tld[nextLayer] = new 150 MappingElement[] me; 151 me.length = mX * mY; 152 creation.loadMapping(mX, mY, me); 153 target.mainDoc.addNewTileLayer(nextLayer, tX, tY, mX, mY, name, creation); 154 target.mainDoc.addEmbeddedMapData(nextLayer, me); 155 } else if (!exists(file)) { //Create empty file 156 File f = File(file, "wb"); 157 MappingElement[] me; 158 me.length = mX * mY; 159 creation.loadMapping(mX, mY, me); 160 target.mainDoc.addNewTileLayer(nextLayer, tX, tY, mX, mY, name, creation); 161 saveMapFile(MapDataHeader(mX, mY), me, f); 162 target.mainDoc.addMapDataFile(nextLayer, file); 163 } else { //load mapping, embed data into current file if needed 164 MapDataHeader mdh; 165 MappingElement[] me = loadMapFile(File(file), mdh); 166 creation.loadMapping(mdh.sizeX, mdh.sizeY, me); 167 target.mainDoc.addNewTileLayer(nextLayer, tX, tY, mX, mY, name, creation); 168 if (embed) 169 target.mainDoc.addEmbeddedMapData(nextLayer, me); 170 else 171 target.mainDoc.addMapDataFile(nextLayer, file); 172 } 173 174 //handle the following instances for materials: 175 //res == image file 176 //TODO: check if material resource file has any embedded resource data 177 //TODO: enable importing from SDLang map files (*.xmf) 178 //TODO: generate dummy tiles for nonexistent material 179 /+if (exists(res)) { 180 //load the resource file and test if it's the correct size (through an exception) 181 source = loadImage(File(res)); 182 ABitmap[] tilesheet; 183 switch (source.getBitdepth()) { 184 case 4: 185 Bitmap4Bit[] output = loadBitmapSheetFromImage!Bitmap4Bit(source, tX, tY); 186 foreach(p; output) 187 tilesheet ~= p; 188 break; 189 case 8: 190 Bitmap8Bit[] output = loadBitmapSheetFromImage!Bitmap8Bit(source, tX, tY); 191 foreach(p; output) 192 tilesheet ~= p; 193 break; 194 case 16: 195 Bitmap16Bit[] output = loadBitmapSheetFromImage!Bitmap16Bit(source, tX, tY); 196 foreach(p; output) 197 tilesheet ~= p; 198 break; 199 case 32: 200 Bitmap32Bit[] output = loadBitmapSheetFromImage!Bitmap32Bit(source, tX, tY); 201 foreach(p; output) 202 tilesheet ~= p; 203 break; 204 default: 205 throw new Exception("Unsupported bitdepth!"); 206 207 } 208 if (tilesheet.length == 0) throw new Exception("No tiles were imported!"); 209 target.addTileSet(nextLayer, tilesheet); 210 target.mainDoc.addsourceFile(nextLayer, res); 211 { 212 TileInfo[] idList; 213 string nameBase = baseName(res); 214 for (int id ; id < tilesheet.length ; id++) { 215 idList ~= TileInfo(cast(wchar)id, id, nameBase ~ "0x" ~ intToHex(id, 4)); 216 //writeln(idList); 217 } 218 target.mainDoc.addTileInfo(nextLayer, idList, res); 219 } 220 if (source.isIndexed) { 221 Color[] palette; 222 /*foreach (color ; source.palette) { 223 palette ~= Color(color.a, color.r, color.g, color.b); 224 debug writeln(color); 225 }*/ 226 auto sourcePalette = source.palette; 227 palette.reserve(sourcePalette.length); 228 for (ushort i ; i < sourcePalette.length ; i++){ 229 const auto origC = sourcePalette[i]; 230 const Color c = Color(origC.a, origC.r, origC.g, origC.b); 231 palette ~= c; 232 } 233 target.mainDoc.addPaletteFile(res, "", cast(int)target.outputWindow.palette.length); 234 target.outputWindow.palette = target.outputWindow.palette ~ palette; 235 //debug writeln(target.outputWindow.palette); 236 } 237 238 }+/ 239 target.outputWindow.addLayer(nextLayer); 240 target.selectedLayer = nextLayer; 241 pri = nextLayer; 242 target.updateLayerList(); 243 target.updateMaterialList(); 244 } catch (Exception e) { 245 writeln(e); 246 } 247 } 248 public void undo() { 249 //Just remove the added layer from the layerlists 250 target.outputWindow.removeLayer(pri); 251 backup = target.mainDoc.removeLayer(pri); 252 target.updateLayerList(); 253 target.updateMaterialList(); 254 } 255 } 256 public class ResizeTileMapEvent : UndoableEvent { 257 MappingElement[] backup, destMap; 258 int mX, mY, offsetX, offsetY, newX, newY; 259 MapDocument targetDoc; 260 int layer; 261 bool patternRepeat; 262 this(int[6] params, MapDocument targetDoc, int layer, bool patternRepeat) { 263 mX = params[0]; 264 mY = params[1]; 265 offsetX = params[2]; 266 offsetY = params[3]; 267 newX = params[4]; 268 newY = params[5]; 269 this.targetDoc = targetDoc; 270 this.layer = layer; 271 this.patternRepeat = patternRepeat; 272 } 273 public void redo() { 274 //backup current layer data 275 ITileLayer targetLayer = cast(ITileLayer)targetDoc.mainDoc.layeroutput[layer]; 276 backup = targetLayer.getMapping(); 277 destMap.length = newX * newY; 278 //writeln(destMap.length); 279 if(patternRepeat) { 280 int sX = offsetX % mX, sY = offsetY % mY; 281 for (int iY ; iY < newY ; iY++) { 282 for (int iX ; iX < newX ; iX++) { 283 destMap[iX + (iY * newX)] = backup[sX + (sY * mX)]; 284 sX++; 285 if (sX >= mX) sX = 0; 286 } 287 sY++; 288 if (sY >= mY) sY = 0; 289 } 290 } else { 291 for (int iY ; iY < mY ; iY++) { 292 //Do a boundscheck, if falls outside of it do nothing. If inside, copy. 293 if (iY + offsetY < newY && iY + offsetY >= 0) { 294 for (int iX ; iX < mX ; iX++) { 295 //Do a boundscheck, if falls outside of it do nothing. If inside, copy. 296 if (iX + offsetX < newX && iX + offsetX >= 0) { 297 destMap[iX + offsetX + ((iY + offsetY) * newY)] = backup[iX + (iY * mY)]; 298 } else if (iX + offsetX >= newX) break; 299 } 300 } else if (iY + offsetY >= newY) break; 301 } 302 } 303 targetLayer.loadMapping(newX, newY, destMap); 304 targetDoc.mainDoc.alterTileLayerInfo(layer, 4, newX); 305 targetDoc.mainDoc.alterTileLayerInfo(layer, 5, newY); 306 } 307 public void undo() { 308 ITileLayer targetLayer = cast(ITileLayer)targetDoc.mainDoc.layeroutput[layer]; 309 targetLayer.loadMapping(mX, mY, backup); 310 targetDoc.mainDoc.alterTileLayerInfo(layer, 4, mX); 311 targetDoc.mainDoc.alterTileLayerInfo(layer, 5, mY); 312 } 313 } 314 public class AddTileSheetEvent : UndoableEvent { 315 Image source; 316 MapDocument targetDoc; 317 int layer; 318 int paletteOffset; 319 int paletteShift; 320 string preName, afterName, fileName; 321 int numFrom; 322 uint numStyle; ///0: decimal, 1: hex, 2: octal 323 Tag backup; 324 this(Image source, MapDocument targetDoc, int layer, int paletteOffset, int paletteShift, string[3] name, int numFrom, 325 uint numStyle) { 326 this.source = source; 327 this.targetDoc = targetDoc; 328 this.layer = layer; 329 this.paletteOffset = paletteOffset; 330 this.paletteShift = paletteShift; 331 preName = name[0]; 332 afterName = name[1]; 333 fileName = name[2]; 334 this.numFrom = numFrom; 335 this.numStyle = numStyle; 336 } 337 public void redo() { 338 import PixelPerfectEngine.system.etc : intToHex, intToOct; 339 //Copy palette if exists (-1 means no palette or do not import palette) 340 if (paletteOffset >= 0) { 341 //Color[] targetPalette = targetDoc.outputWindow.paletteLocal; 342 Color[] importedPalette = loadPaletteFromImage(source); 343 assert(importedPalette.length); 344 const ushort offset = cast(ushort)(paletteOffset << paletteShift); 345 targetDoc.outputWindow.loadPaletteChunk(importedPalette, offset); 346 targetDoc.mainDoc.addPaletteFile(fileName, "", paletteOffset << paletteShift, paletteShift); 347 } 348 if (backup is null) { 349 //TODO: check if material resource file has any embedded resource data 350 //TODO: enable importing from SDLang map files (*.xmf) 351 //TODO: generate dummy tiles for nonexistent material 352 353 //load the resource file and test if it's the correct size (through an exception) 354 //source = loadImage(File(res)); 355 ITileLayer itl = cast(ITileLayer)targetDoc.mainDoc.layeroutput[layer]; 356 const int tX = itl.getTileWidth, tY = itl.getTileHeight; 357 ABitmap[] tilesheet; 358 switch (source.getBitdepth()) { 359 case 4: 360 Bitmap4Bit[] output = loadBitmapSheetFromImage!Bitmap4Bit(source, tX, tY); 361 foreach(p; output) 362 tilesheet ~= p; 363 break; 364 case 8: 365 Bitmap8Bit[] output = loadBitmapSheetFromImage!Bitmap8Bit(source, tX, tY); 366 foreach(p; output) 367 tilesheet ~= p; 368 break; 369 case 16: 370 Bitmap16Bit[] output = loadBitmapSheetFromImage!Bitmap16Bit(source, tX, tY); 371 foreach(p; output) 372 tilesheet ~= p; 373 break; 374 case 32: 375 Bitmap32Bit[] output = loadBitmapSheetFromImage!Bitmap32Bit(source, tX, tY); 376 foreach(p; output) 377 tilesheet ~= p; 378 break; 379 default: 380 throw new Exception("Unsupported bitdepth!"); 381 } 382 if (tilesheet.length == 0) throw new Exception("No tiles were imported!"); 383 targetDoc.addTileSet(layer, tilesheet); 384 targetDoc.mainDoc.addTileSourceFile(layer, fileName, null, 0); 385 { 386 TileInfo[] idList; 387 for (int id ; id < tilesheet.length ; id++) { 388 //idList ~= TileInfo(cast(wchar)id, id, nameBase ~ "0x" ~ intToHex(id, 4)); 389 string tilename = preName; 390 switch(numStyle & 0x3) { 391 case 1: 392 tilename ~= intToHex(id + numFrom, numStyle>>>8); 393 break; 394 case 2: 395 tilename ~= intToOct(id + numFrom, numStyle>>>8); 396 break; 397 default: 398 string num = to!string(id); 399 for (int i ; i < (numStyle>>>8) - num.length ; i++) { 400 tilename ~= '0'; 401 } 402 tilename ~= num; 403 break; 404 } 405 tilename ~= afterName; 406 idList ~= TileInfo(cast(wchar)id, cast(ushort)paletteShift, id, tilename); 407 //writeln(idList); 408 } 409 targetDoc.mainDoc.addTileInfo(layer, idList, fileName, null); 410 } 411 /+if (source.isIndexed) { 412 Color[] palette; 413 /*foreach (color ; source.palette) { 414 palette ~= Color(color.a, color.r, color.g, color.b); 415 debug writeln(color); 416 }*/ 417 auto sourcePalette = source.palette; 418 palette.reserve(sourcePalette.length); 419 for (ushort i ; i < sourcePalette.length ; i++){ 420 const auto origC = sourcePalette[i]; 421 const Color c = Color(origC.a, origC.r, origC.g, origC.b); 422 palette ~= c; 423 } 424 target.mainDoc.addPaletteFile(res, "", cast(int)target.outputWindow.palette.length); 425 target.outputWindow.palette = target.outputWindow.palette ~ palette; 426 //debug writeln(target.outputWindow.palette); 427 }+/ 428 429 430 } else { 431 432 } 433 } 434 public void undo() { 435 436 } 437 }