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