1 /* 2 Copyright (C) 2015-2021, by Laszlo Szeremi under the Boost license. 3 4 PixelPerfectEditor 5 */ 6 7 8 module app; 9 10 import std.stdio; 11 import std.string; 12 import std.conv; 13 import std.format; 14 import std.random; 15 16 import bindbc.sdl; 17 //import derelict.freeimage.freeimage; 18 19 //import system.config; 20 21 import pixelperfectengine.graphics.outputscreen; 22 import pixelperfectengine.graphics.raster; 23 import pixelperfectengine.graphics.layers; 24 25 import pixelperfectengine.graphics.bitmap; 26 27 import pixelperfectengine.collision.common; 28 import pixelperfectengine.collision.objectcollision; 29 30 import pixelperfectengine.system.input; 31 import pixelperfectengine.system.file; 32 import pixelperfectengine.system.etc; 33 import pixelperfectengine.system.config; 34 //import pixelperfectengine.system.binarySearchTree; 35 import pixelperfectengine.system.common; 36 import core.memory; 37 38 public import editor; 39 //import pixelperfectengine.extbmp.extbmp; 40 41 public Editor prg; 42 43 int main(string[] args){ 44 initialzeSDL(); 45 GC.disable(); 46 if (args.length > 1) { 47 if (args[1] == "--test") { 48 int mapWidth = 8, mapHeight = 8; 49 if (args.length == 4) { 50 mapWidth = to!int(args[2]); 51 mapHeight = to!int(args[3]); 52 } 53 TileLayerTest lprg = new TileLayerTest(mapWidth, mapHeight); 54 lprg.whereTheMagicHappens; 55 return 0; 56 } 57 } 58 59 prg = new Editor(args); 60 prg.whereTheMagicHappens; 61 return 0; 62 } 63 /** 64 * Tests graphics output, input events, collision, etc. 65 */ 66 class TileLayerTest : SystemEventListener, InputListener { 67 bool isRunning, up, down, left, right, scrup, scrdown, scrleft, scrright; 68 OutputScreen output; 69 Raster r; 70 TileLayer t; 71 TileLayer textLayer; 72 TransformableTileLayer!(Bitmap8Bit,16,16) tt; 73 Bitmap8Bit[] tiles; 74 Bitmap8Bit dlangMan; 75 Bitmap1bit dlangManCS; 76 SpriteLayer s; 77 InputHandler ih; 78 ObjectCollisionDetector ocd; 79 float theta; 80 int framecounter; 81 this (int mapWidth, int mapHeight) { 82 theta = 0; 83 isRunning = true; 84 Image tileSource = loadImage(File("../assets/sci-fi-tileset.png")); 85 //Image tileSource = loadImage(File("../assets/_system/concreteGUIE0.tga")); 86 Image spriteSource = loadImage(File("../assets/d-man.tga")); 87 Image fontSource = loadImage(File("../system/codepage_8_8.png")); 88 output = new OutputScreen("TileLayer test", 424 * 4, 240 * 4); 89 r = new Raster(424,240,output,0); 90 output.setMainRaster(r); 91 t = new TileLayer(16,16, RenderingMode.Copy); 92 textLayer = new TileLayer(8,8, RenderingMode.AlphaBlend); 93 textLayer.paletteOffset = 512; 94 textLayer.masterVal = 127; 95 textLayer.loadMapping(53, 30, new MappingElement[](53 * 30)); 96 tt = new TransformableTileLayer!(Bitmap8Bit,16,16)(RenderingMode.AlphaBlend); 97 s = new SpriteLayer(RenderingMode.AlphaBlend); 98 r.addLayer(tt, 1); 99 r.addLayer(t, 0); 100 r.addLayer(s, 2); 101 r.addLayer(textLayer, 65_536); 102 103 Color[] localPal = loadPaletteFromImage(tileSource); 104 localPal.length = 256; 105 r.addPaletteChunk(localPal); 106 localPal = loadPaletteFromImage(spriteSource); 107 localPal.length = 256; 108 r.addPaletteChunk(localPal); 109 r.addPaletteChunk([Color(0x00,0x00,0x00,0xFF),Color(0xff,0xff,0xff,0xFF),Color(0x00,0x00,0x00,0xFF), 110 Color(0xff,0x00,0x00,0xFF),Color(0x00,0x00,0x00,0xFF),Color(0x00,0xff,0x00,0xFF),Color(0x00,0x00,0x00,0xFF), 111 Color(0x00,0x00,0xff,0xFF)]); 112 113 //writeln(r.layerMap); 114 //c = new CollisionDetector(); 115 dlangMan = loadBitmapFromImage!Bitmap8Bit(spriteSource); 116 dlangManCS = dlangMan.generateStandardCollisionModel(); 117 ocd = new ObjectCollisionDetector(&onCollision, 0); 118 s.addSprite(dlangMan, 65_536, 0, 0, 1); 119 ocd.objects[65_536] = CollisionShape(Box(0, 0, 31, 31), dlangManCS); 120 121 for(int i = 1 ; i < 10 ; i++){ 122 const int x = uniform(0,320), y = uniform(0,240); 123 s.addSprite(dlangMan, i, x, y, 1); 124 ocd.objects[i] = CollisionShape(Box(x, y, x + 31, y + 31), dlangManCS); 125 } 126 127 tiles = loadBitmapSheetFromImage!Bitmap8Bit(tileSource, 16, 16);//loadBitmapSheetFromFile!Bitmap8Bit("../assets/sci-fi-tileset.png",16,16); 128 129 for (int i; i < tiles.length; i++) { 130 tt.addTile(tiles[i], cast(wchar)i); 131 } 132 133 for (int i; i < tiles.length; i++) { 134 t.addTile(tiles[i], cast(wchar)i); 135 } 136 137 { 138 Bitmap8Bit[] fontSet = loadBitmapSheetFromImage!Bitmap8Bit(fontSource, 8, 8); 139 for (ushort i; i < fontSet.length; i++) { 140 textLayer.addTile(fontSet[i], i, 1); 141 } 142 } 143 //wchar[] mapping; 144 MappingElement[] mapping; 145 mapping.length = mapWidth * mapHeight;//64*64; 146 //attrMapping.length = 256*256; 147 for(int i; i < mapping.length; i++){ 148 //mapping[i] = to!wchar(uniform(0x0000,0x00AA)); 149 const int rnd = uniform(0,1024); 150 //attrMapping[i] = BitmapAttrib(rnd & 1 ? true : false, rnd & 2 ? true : false); 151 mapping[i] = MappingElement(cast(wchar)(rnd & 63), BitmapAttrib(rnd & 1024 ? true : false, rnd & 512 ? true : false)); 152 //mapping[i] = MappingElement(0x0, BitmapAttrib(false,false)); 153 } 154 ih = new InputHandler(); 155 ih.systemEventListener = this; 156 ih.inputListener = this; 157 158 { 159 import pixelperfectengine.system.input.scancode; 160 ih.addBinding(BindingCode(ScanCode.UP, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("up")); 161 ih.addBinding(BindingCode(ScanCode.DOWN, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("down")); 162 ih.addBinding(BindingCode(ScanCode.LEFT, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("left")); 163 ih.addBinding(BindingCode(ScanCode.RIGHT, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("right")); 164 ih.addBinding(BindingCode(ScanCode.np8, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("scrup")); 165 ih.addBinding(BindingCode(ScanCode.np2, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("scrdown")); 166 ih.addBinding(BindingCode(ScanCode.np4, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("scrleft")); 167 ih.addBinding(BindingCode(ScanCode.np6, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("scrright")); 168 ih.addBinding(BindingCode(ScanCode.Q, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("A+")); 169 ih.addBinding(BindingCode(ScanCode.A, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("A-")); 170 ih.addBinding(BindingCode(ScanCode.W, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("B+")); 171 ih.addBinding(BindingCode(ScanCode.S, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("B-")); 172 ih.addBinding(BindingCode(ScanCode.E, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("C+")); 173 ih.addBinding(BindingCode(ScanCode.D, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("C-")); 174 ih.addBinding(BindingCode(ScanCode.R, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("D+")); 175 ih.addBinding(BindingCode(ScanCode.F, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("D-")); 176 ih.addBinding(BindingCode(ScanCode.T, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("x0+")); 177 ih.addBinding(BindingCode(ScanCode.G, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("x0-")); 178 ih.addBinding(BindingCode(ScanCode.Y, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("y0+")); 179 ih.addBinding(BindingCode(ScanCode.H, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("y0-")); 180 ih.addBinding(BindingCode(ScanCode.PAGEUP, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("alpha+")); 181 ih.addBinding(BindingCode(ScanCode.PAGEDOWN, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("alpha-")); 182 ih.addBinding(BindingCode(ScanCode.HOME, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("hidettl")); 183 ih.addBinding(BindingCode(ScanCode.END, 0, Devicetype.Keyboard, 0, KeyModifier.All), InputBinding("unhidettl")); 184 } 185 186 tt.loadMapping(mapWidth, mapHeight, mapping); 187 tt.warpMode = WarpMode.Off; 188 189 t.loadMapping(mapWidth, mapHeight, mapping); 190 t.warpMode = WarpMode.TileRepeat; 191 192 //t.setWrapMode(true); 193 //tt.D = -256; 194 //loadPaletteFromXMP(tileSource, "default", r); 195 196 /*for(int y ; y < 240 ; y++){ 197 for(int x ; x < 240 ; x++){ 198 writeln('[',x,',',y,"] : ", t.transformFunc([x,y])); 199 } 200 }*/ 201 202 //writeln(r.palette); 203 //r.palette[0].alpha = 255; 204 //r.palette[256].base = 0; 205 //textLayer.writeTextToMap(2,2,0,"Hello world!",BitmapAttrib(true, false)); 206 textLayer.writeTextToMap(0, 0, 0, "Framerate:", BitmapAttrib(true, false)); 207 textLayer.writeTextToMap(0, 1, 0, "Collision:", BitmapAttrib(true, false)); 208 textLayer.writeTextToMap(0, 2, 0, "Col. type:", BitmapAttrib(true, false)); 209 //writeln(tt); 210 //r.palette[0] = 255; 211 //r.addRefreshListener(output, 0); 212 213 } 214 private @nogc void ttlHBlankInterrupt(ref short[4] localABCD, ref short[2] localsXsY, ref short[2] localx0y0, short y){ 215 localABCD[0]++; 216 } 217 public void whereTheMagicHappens(){ 218 while(isRunning){ 219 r.refresh(); 220 ih.test(); 221 if(up) { 222 s.relMoveSprite(65_536,0,-1); 223 } 224 if(down) { 225 s.relMoveSprite(65_536,0,1); 226 } 227 if(left) { 228 s.relMoveSprite(65_536,-1,0); 229 } 230 if(right) { 231 s.relMoveSprite(65_536,1,0); 232 } 233 ocd.objects.ptrOf(65_536).position = s.getSpriteCoordinate(65_536); 234 ocd.testSingle(65_536); 235 if(scrup) { 236 t.relScroll(0,-1); 237 tt.relScroll(0,-1); 238 s.relScroll(0,-1); 239 } 240 if(scrdown) { 241 t.relScroll(0,1); 242 tt.relScroll(0,1); 243 s.relScroll(0,1); 244 } 245 if(scrleft) { 246 t.relScroll(-1,0); 247 tt.relScroll(-1,0); 248 s.relScroll(-1,0); 249 } 250 if(scrright) { 251 t.relScroll(1,0); 252 tt.relScroll(1,0); 253 s.relScroll(1,0); 254 } 255 256 framecounter++; 257 if(framecounter == 10){ 258 float avgFPS = r.avgfps; 259 wstring fpsCounter = format(" %3.3f"w, avgFPS); 260 textLayer.writeTextToMap(10,0,0,fpsCounter,BitmapAttrib(true, false)); 261 framecounter = 0; 262 } 263 //t.relScroll(1,0); 264 } 265 } 266 public void onCollision(ObjectCollisionEvent event) { 267 textLayer.writeTextToMap(10,1,0,format("%8X"w,event.idB),BitmapAttrib(true, false)); 268 final switch (event.type) with (ObjectCollisionEvent.Type) { 269 case None: 270 textLayer.writeTextToMap(10,2,0," None",BitmapAttrib(true, false)); 271 break; 272 case BoxEdge: 273 textLayer.writeTextToMap(10,2,0," BoxEdge",BitmapAttrib(true, false)); 274 break; 275 case BoxOverlap: 276 textLayer.writeTextToMap(10,2,0," BoxOverlap",BitmapAttrib(true, false)); 277 break; 278 case ShapeOverlap: 279 textLayer.writeTextToMap(10,2,0,"ShapeOverlap",BitmapAttrib(true, false)); 280 break; 281 } 282 } 283 override public void onQuit() { 284 isRunning = false; 285 } 286 public void controllerAdded(uint id) { 287 288 } 289 public void controllerRemoved(uint id) { 290 291 } 292 /+override public void keyPressed(string ID,uint timestamp,uint devicenumber,uint devicetype) { 293 //writeln(ID); 294 import pixelperfectengine.graphics.transformFunctions; 295 switch(ID){ 296 case "up": up = true; break; 297 case "down": down = true; break; 298 case "left": left = true; break; 299 case "right": right = true; break; 300 case "scrup": scrup = true; break; 301 case "scrdown": scrdown = true; break; 302 case "scrleft": scrleft = true; break; 303 case "scrright": scrright = true; break; 304 case "A+": tt.A = cast(short)(tt.A + 16); break; 305 case "A-": tt.A = cast(short)(tt.A - 16); break; 306 case "B+": tt.B = cast(short)(tt.B + 16); break; 307 case "B-": tt.B = cast(short)(tt.B - 16); break; 308 case "C+": tt.C = cast(short)(tt.C + 16); break; 309 case "C-": tt.C = cast(short)(tt.C - 16); break; 310 case "D+": tt.D = cast(short)(tt.D + 16); break; 311 case "D-": tt.D = cast(short)(tt.D - 16); break; 312 case "x0+": tt.x_0 = cast(short)(tt.x_0 + 1); break; 313 case "x0-": tt.x_0 = cast(short)(tt.x_0 - 1); break; 314 case "y0+": tt.y_0 = cast(short)(tt.y_0 + 1); break; 315 case "y0-": tt.y_0 = cast(short)(tt.y_0 - 1); break; 316 case "sH-": 317 if(s.getScaleSpriteHoriz(0) == 16){ 318 s.scaleSpriteHoriz(0,-16); 319 return; 320 } 321 s.scaleSpriteHoriz(0,s.getScaleSpriteHoriz(0) - 16); 322 //writeln(s.getScaleSpriteHoriz(0)); 323 break; 324 case "sH+": 325 if(s.getScaleSpriteHoriz(0) == -16){ 326 s.scaleSpriteHoriz(0,16); 327 return; 328 } 329 s.scaleSpriteHoriz(0,s.getScaleSpriteHoriz(0) + 16); 330 //writeln(s.getScaleSpriteHoriz(0)); 331 break; 332 case "sV-": 333 if(s.getScaleSpriteVert(0) == 16){ 334 s.scaleSpriteVert(0,-16); 335 return; 336 } 337 s.scaleSpriteVert(0,s.getScaleSpriteVert(0) - 16); 338 break; 339 case "sV+": 340 if(s.getScaleSpriteVert(0) == -16){ 341 s.scaleSpriteVert(0,16); 342 return; 343 } 344 s.scaleSpriteVert(0,s.getScaleSpriteVert(0) + 16); 345 break; 346 case "HM": 347 s.scaleSpriteHoriz(0,s.getScaleSpriteHoriz(0) * -1); 348 break; 349 case "VM": 350 s.scaleSpriteVert(0,s.getScaleSpriteVert(0) * -1); 351 break; 352 case "theta+": 353 theta += 1; 354 short[4] newTP = rotateFunction(theta); 355 tt.A = newTP[0]; 356 tt.B = newTP[1]; 357 tt.C = newTP[2]; 358 tt.D = newTP[3]; 359 break; 360 case "theta-": 361 theta -= 1; 362 short[4] newTP = rotateFunction(theta); 363 tt.A = newTP[0]; 364 tt.B = newTP[1]; 365 tt.C = newTP[2]; 366 tt.D = newTP[3]; 367 break; 368 default: break; 369 } 370 } 371 override public void keyReleased(string ID,uint timestamp,uint devicenumber,uint devicetype) { 372 switch(ID){ 373 case "up": up = false; break; 374 case "down": down = false; break; 375 case "left": left = false; break; 376 case "right": right = false; break; 377 case "scrup": scrup = false; break; 378 case "scrdown": scrdown = false; break; 379 case "scrleft": scrleft = false; break; 380 case "scrright": scrright = false; break; 381 default: break; 382 } 383 }+/ 384 /*public void spriteCollision(CollisionEvent ce){ 385 writeln("COLLISION!!!!11!1111!!!ONEONEONE!!!"); 386 } 387 388 public void backgroundCollision(CollisionEvent ce){}*/ 389 /** 390 * Called when a keybinding event is generated. 391 * The `id` should be generated from a string, usually the name of the binding. 392 * `code` is a duplicate of the code used for fast lookup of the binding, which also contains other info (deviceID, etc). 393 * `timestamp` is the time lapsed since the start of the program, can be used to measure time between keypresses. 394 * NOTE: Hat events on joysticks don't generate keyReleased events, instead they generate keyPressed events on release. 395 */ 396 public void keyEvent(uint id, BindingCode code, uint timestamp, bool isPressed) { 397 //writeln(id, ";", code, ";",timestamp, ";",isPressed, ";"); 398 switch (id) { 399 case 1720810685: //up 400 up = isPressed; 401 break; 402 case 1672064345: //down 403 down = isPressed; 404 break; 405 case 2840212248: //left 406 left = isPressed; 407 break; 408 case 1786548735: //right 409 right = isPressed; 410 break; 411 case 3938104347: //scrup 412 scrup = isPressed; 413 break; 414 case 131561283: //scrdown 415 scrdown = isPressed; 416 break; 417 case 4011913815: //scrleft 418 scrleft = isPressed; 419 break; 420 case 2073272778: //scrright 421 scrright = isPressed; 422 break; 423 case 4284782897: //A+ 424 tt.A = cast(short)(tt.A + 16); 425 break; 426 case 142754382: //A- 427 tt.A = cast(short)(tt.A - 16); 428 break; 429 case 2060572171: //B+ 430 tt.B = cast(short)(tt.B + 16); 431 break; 432 case 919786464: //B- 433 tt.B = cast(short)(tt.B - 16); 434 break; 435 case 2857229774: //C+ 436 tt.C = cast(short)(tt.C + 16); 437 break; 438 case 1598464886: //C- 439 tt.C = cast(short)(tt.C - 16); 440 break; 441 case 2476135441: //D+ 442 tt.D = cast(short)(tt.D + 16); 443 break; 444 case 3708187064: //D- 445 tt.D = cast(short)(tt.D - 16); 446 break; 447 case 3238134781: //x0+ 448 tt.x_0 = cast(short)(tt.x_0 + 1); 449 break; 450 case 135027337: //x0- 451 tt.x_0 = cast(short)(tt.x_0 - 1); 452 break; 453 case 983492653: //y0+ 454 tt.y_0 = cast(short)(tt.y_0 + 1); 455 break; 456 case 2733639921: //y0- 457 tt.y_0 = cast(short)(tt.y_0 - 1); 458 break; 459 case hashCalc("hidettl"): 460 r.removeLayer(0); 461 break; 462 case hashCalc("unhidettl"): 463 r.addLayer(tt, 0); 464 break; 465 default: 466 break; 467 } 468 } 469 /** 470 * Called when an axis is being operated. 471 * The `id` should be generated from a string, usually the name of the binding. 472 * `code` is a duplicate of the code used for fast lookup of the binding, which also contains other info (deviceID, etc). 473 * `timestamp` is the time lapsed since the start of the program, can be used to measure time between keypresses. 474 * `value` is the current position of the axis normalized between -1.0 and +1.0 for joysticks, and 0.0 and +1.0 for analog 475 * triggers. 476 */ 477 public void axisEvent(uint id, BindingCode code, uint timestamp, float value) { 478 479 } 480 }