1 // This code is D 1.0
2 
3 /**
4 	Part of my old D 1.0 game helper code that used SDL. I keep it compiling on new D compilers too, but it is not meant to be used in new projects.
5 
6 	Version: 1.0
7 	License: GNU General Public License
8 */
9 module arsd.engine; //@-L-lSDL -L-lSDL_mixer -L-lSDL_ttf -L-lSDL_image -L-lGL -L-lSDL_net
10 
11 pragma(lib, "SDL");
12 pragma(lib, "SDL_mixer");
13 pragma(lib, "SDL_ttf");
14 pragma(lib, "SDL_image");
15 pragma(lib, "SDL_net");
16 pragma(lib, "GL");
17 
18 // FIXME: the difference between directions and buttons should be removed
19 
20 
21 import sdl.SDL;
22 import sdl.SDL_net;
23 
24 import std..string;
25 version(D_Version2) {
26 	import random = core.stdc.stdlib;
27 	alias random.srand srand;
28 
29 	import std.conv;
30 	char[] convToString(T)(T t) { return to!(char[])(t); }
31 	string immutableString(in char[] a) { return a.idup; }
32 } else {
33 	import random = std.random;
34 	void srand(uint a) {
35 		random.rand_seed(a, 0);
36 	}
37 	alias std..string.toString convToString;
38 
39 	char[] immutableString(in char[] a) { return a; }
40 }
41 import std.math;
42 public import arsd.screen;
43 public import arsd.audio;
44 
45 public import sdl.SDL_keysym_;
46 
47 version(D_Version2)
48 	import core.stdc.stdarg;
49 else
50 	import std.stdarg;
51 
52 import std.stdio;
53 //version(linux) pragma(lib, "kbhit.o");
54 
55 int randomNumber(int min, int max){
56 	if(min == max)
57 		return min;
58 	max++; // make it inclusive
59 
60 	uint a = random.rand();
61 	a %= (max - min);
62 	a += min;
63 	return a;
64 }
65 
66 
67 // HACK!
68 	bool waiting;
69 	uint globalTimer;
70 class Callable{
71   public:
72 	  abstract void run(int timer);
73 	  // after is the other thing that is calling this to be paused and then unpaused
74 	  final void start(Callable after = null){
75 		After = after;
76 		if(After !is null)
77 			After.paused = true;
78 		paused = false;
79 		for(int a = 0; a < objs.length; a++)
80 			if(objs[a] is null){
81 				objs[a] = this;
82 				goto done;
83 
84 		}
85 		objs.length = objs.length + 1;
86 		objs[objs.length - 1] = this;
87 
88 		done:
89 		frame();
90 	}
91 
92 	  final void queue(){
93 	  	int a;
94 		paused = true;
95 		for(a = 0; a < objs.length; a++)
96 			if(objs[a] is null){
97 				objs[a] = this;
98 				goto done;
99 
100 		}
101 		objs.length = a + 1;
102 		objs[a] = this;
103 
104 		done:
105 		if(a == 1){
106 			objs[0].paused = true;
107 			paused = false;
108 		} else {
109 			objs[a - 1].After = this;
110 		}
111 		After = objs[0];
112 
113 		frame();
114 	}
115 
116 
117 
118 
119 	final void terminate(){
120 		for(int a = 0; a < objs.length; a++)
121 			if(objs[a] !is null && objs[a] == this)
122 				objs[a] = null;
123 		if(After !is null){
124 			After.paused = false;
125 			After.frame();
126 		}
127 	}
128 
129 	bool paused;
130   private:
131 	void frame(){
132 		if(!paused){
133 			if(globalTimer > lastFrame){
134 				lastFrame = globalTimer;
135 				run(Timer);
136 				Timer++;
137 			}
138 		}
139 	}
140 	int Timer;
141 	uint lastFrame;
142 	Callable After;
143 }
144 
145 Callable[] objs;
146 // end HACK
147 
148 //	enum {up = 0, down, left, right};
149 	enum {select = 8, start = 9, square = 3, cross = 2, circle = 1, triangle = 0,
150 		R1 = 7, R2 = 5, L2 = 4, L1 = 6, L3 = 10, R3 = 11, special = 12,
151 		up = 13, down = 14, left = 15, right = 16,  // dpad and left stick
152 		up2 = 17, down2 = 18, left2 = 19, right2 = 20}; // right stick
153 	const int NUM_BUTTONS = 21;
154 class Engine{
155 	static const int NoVideo = 0;
156 	static const int Video1024x768 = 1;
157 	static const int Video640x480 = 2;
158 	static const int Video800x600 = 3;
159 	static const int Video320x200 = 4;
160 	static const int Video512x512 = 5;
161 
162 	static const int VideoFullScreen = 32;
163 
164 
165 	alias int Direction;
166 	alias int Buttons;
167 
168 	static const int MAX_NET = 8;
169 	static const int NET_PORT = 7777;
170 
171 	// For being a network server.....
172 	bool isServer;
173 	struct NetClient {
174 		TCPsocket sock;
175 		int numPlayers;
176 		int startingPlayer;
177 		int latency; // in milliseconds
178 
179 		int state; // Only valid if sock is non null.
180 				// 0: waiting on initial timestamp
181 				// 1: waiting on lag response
182 				// 2: ready for starting
183 	}
184 	NetClient[MAX_NET] clients;
185 	int numberOfClients;
186 
187 	int maxLatency;
188 
189 
190 	// For being a network client
191 	TCPsocket clientsock;
192 
193 	// All done.
194 
195 	void beginServing(){
196 		TCPsocket serversock;
197 
198 		socketset = SDLNet_AllocSocketSet(MAX_NET+1);
199 		if(socketset is null)
200 			throw new Exception("AllocSocketSet");
201 		scope(failure)
202 			SDLNet_FreeSocketSet(socketset);
203 
204 		IPaddress serverIP;
205 
206 		SDLNet_ResolveHost(&serverIP, null, NET_PORT);
207 		serversock = SDLNet_TCP_Open(&serverIP);
208 		if(serversock is null)
209 			throw new Exception("Server sock");
210 		scope(exit)
211 			SDLNet_TCP_Close(serversock);
212 
213 		if(SDLNet_AddSocket(socketset, cast(SDLNet_GenericSocket) serversock) < 0)
214 			throw new Exception("addsocket");
215 		scope(exit)
216 			SDLNet_DelSocket(socketset, cast(SDLNet_GenericSocket) serversock);
217 
218 		writefln("Waiting for players to join the game.\nPress enter when everyone has joined to start the game.");
219 
220 
221 		uint randomSeed = random.rand();
222 		srand(randomSeed);
223 		writefln("TEST: %d", randomNumber(0, 100));
224 
225 
226 		bool loopingNeeeded = false; // potential FIXME for later
227 		while(!kbhit() || loopingNeeeded){
228 			int n = SDLNet_CheckSockets(socketset, 10);
229 			if(n < 0)
230 				throw new Exception("Check sockets");
231 			if(n == 0)
232 				continue;
233 			if(SDLNet_SocketReady(serversock)){
234 				TCPsocket newsock;
235 
236 				newsock = SDLNet_TCP_Accept(serversock);
237 				if(newsock is null){
238 					continue;
239 				}
240 
241 
242 				SDLNet_AddSocket(socketset, cast(SDLNet_GenericSocket) newsock);
243 
244 				// accept the connection
245 
246 				writefln("New player:");
247 
248 				clients[numberOfClients].sock = newsock;
249 				numberOfClients++;
250 			}
251 
252 			// Check the rest of our sockets for data
253 
254 			for(int a = 0; a < numberOfClients; a++){
255 				if(SDLNet_SocketReady(clients[a].sock)){
256 					byte[16] data; // this needs to be EXACTLY the size we are actually going to get.
257 					if(SDLNet_TCP_Recv(clients[a].sock, data.ptr, 16) <= 0){
258 						// the connection was closed
259 						for(int b = a; b < numberOfClients; b++)
260 							clients[b] = clients[b+1];
261 						clients[numberOfClients] = NetClient.init;
262 						numberOfClients--;
263 						a--;
264 						continue;
265 					}
266 
267 					// And handle the data here.
268 					switch(clients[a].state){
269 					  default: assert(0);
270 					  case 0: // this is the timestamp and stuff
271 						int ts = SDLNet_Read32(data.ptr);
272 						clients[a].numPlayers = SDLNet_Read32(data.ptr+4);
273 
274 						int startingPlayer = numberOfPlayers;
275 						numberOfPlayers+= clients[a].numPlayers;
276 						clients[a].startingPlayer = startingPlayer;
277 
278 						SDLNet_Write32(ts, data.ptr);
279 						SDLNet_Write32(SDL_GetTicks(), data.ptr+4);
280 						SDLNet_Write32(randomSeed, data.ptr+8);
281 						SDLNet_Write32(startingPlayer, data.ptr+12);
282 
283 						if(SDLNet_TCP_Send(clients[a].sock, data.ptr, 16) <= 0)
284 							throw new Exception("TCP send");
285 
286 						clients[a].state++;
287 					  break;
288 					  case 1: // this is telling of the latency
289 
290 						clients[a].latency = SDLNet_Read32(data.ptr);
291 
292 						if(clients[a].latency > maxLatency)
293 							maxLatency = clients[a].latency;
294 
295 						writefln("Latency: %d", clients[a].latency);
296 
297 						clients[a].state++;
298 					  break;
299 					  case 2:
300 					  	throw new Exception("unknown data came in");
301 					}
302 				}
303 			}
304 		}
305 
306 
307 
308 		isServer = true;
309 	}
310 
311 	void connectTo(in char[] whom){
312 		socketset = SDLNet_AllocSocketSet(1);
313 		if(socketset is null)
314 			throw new Exception("AllocSocketSet");
315 		scope(failure)
316 			SDLNet_FreeSocketSet(socketset);
317 
318 		IPaddress ip;
319 
320 		if(SDLNet_ResolveHost(&ip, std..string.toStringz(whom), NET_PORT) == -1)
321 			throw new Exception("Resolve host");
322 
323 		clientsock = SDLNet_TCP_Open(&ip);
324 
325 		if(clientsock is null)
326 			throw new Exception("open socket");
327 
328 		if(SDLNet_AddSocket(socketset, cast(SDLNet_GenericSocket) clientsock) < 0)
329 			throw new Exception("addsocket");
330 		scope(failure) SDLNet_DelSocket(socketset, cast(SDLNet_GenericSocket) clientsock);
331 
332 		byte[16] data;
333 
334 		int timeStamp = SDL_GetTicks();
335 		SDLNet_Write32(timeStamp, data.ptr);
336 		SDLNet_Write32(numberOfLocalPlayers, data.ptr+4);
337 		if(SDLNet_TCP_Send(clientsock, data.ptr, 16) <= 0)
338 			throw new Exception("TCP send");
339 
340 		if(SDLNet_TCP_Recv(clientsock, data.ptr, 16) <= 0)
341 			throw new Exception("TCP recv");
342 
343 		int receivedTimeStamp = SDLNet_Read32(data.ptr);
344 		int serverTimeStamp = SDLNet_Read32(data.ptr+4);
345 		uint randomSeed = SDLNet_Read32(data.ptr+8);
346 		firstLocalPlayer = SDLNet_Read32(data.ptr+12);
347 		writefln("First local player = %d", firstLocalPlayer);
348 
349 		srand(randomSeed);
350 		writefln("TEST: %d", randomNumber(0, 100));
351 
352 		int ourLatency = SDL_GetTicks() - receivedTimeStamp;
353 
354 		SDLNet_Write32(ourLatency, data.ptr);
355 		SDLNet_Write32(serverTimeStamp, data.ptr+4);
356 
357 		if(SDLNet_TCP_Send(clientsock, data.ptr, 16) <= 0)
358 			throw new Exception("TCP send 2");
359 
360 		waiting = true;
361 	}
362 
363 	// This should be called AFTER most initialization, but BEFORE you initialize your players; you don't
364 	// know the number of players for sure until this call returns.
365 	void waitOnNetwork(){
366 		if(!net)
367 			return;
368 
369 		if(isServer){
370 
371 			// Calculate when to start, then send the signal to everyone.
372 			int desiredLag = cast(int) round(cast(float) maxLatency / msPerTick) + 2;//1;
373 			lag = desiredLag;
374 			writefln("Lag = %d", lag);
375 
376 			for(int a = 0; a < numberOfClients; a++){
377 				int delay = maxLatency - clients[a].latency;
378 
379 				byte[16] data;
380 
381 				// FIXME: We need to send all player data here!
382 
383 				SDLNet_Write32(desiredLag, data.ptr);
384 				SDLNet_Write32(delay, data.ptr + 4);
385 				SDLNet_Write32(numberOfPlayers, data.ptr + 8);
386 
387 
388 				if(SDLNet_TCP_Send(clients[a].sock, data.ptr, 16) < 16)
389 					throw new Exception("Sending failed");
390 			}
391 
392 			SDL_Delay(maxLatency); // After waiting for the signal to reach everyone, we can now begin the game!
393 			return;
394 		} else {
395 			// handle the data
396 			byte[16] data;
397 
398 			// FIXME: we need to read special per game player data here!
399 
400 			if(SDLNet_TCP_Recv(clientsock, data.ptr, 16) <= 0)
401 				throw new Exception("Server closed the connection");
402 
403 			int lagAmount   = SDLNet_Read32(data.ptr); 
404 			int delayAmount = SDLNet_Read32(data.ptr + 4);
405 			numberOfPlayers = SDLNet_Read32(data.ptr+8);
406 
407 
408 			lag = lagAmount;
409 			writefln("Lag = %d", lag);
410 
411 			SDL_Delay(delayAmount);
412 			// And finally, we're done, and the game can begin.
413 			waiting = false;
414 			return;
415 		}
416 	}
417 
418 	SDLNet_SocketSet socketset;
419 
420 	int msPerTick;
421 
422 	int numberOfPlayers;
423 	int numberOfLocalPlayers;
424 	int firstLocalPlayer;
425 
426   public:
427 	int getNumberOfPlayers(){ // good for main looping and controls and such
428 		return numberOfPlayers;
429 	}
430 
431 	// returns < 0 if the player is not local
432 	int getLocalPlayerNumber(int absolutePlayerNumber){ // useful for split screening
433 		if(absolutePlayerNumber >= firstLocalPlayer && absolutePlayerNumber < firstLocalPlayer + numberOfLocalPlayers)
434 			return  absolutePlayerNumber - firstLocalPlayer;
435 
436 		return -1;
437 	}
438 
439 	int getNumberOfLocalPlayers(){ // only useful for deciding how to split the screen
440 		return numberOfLocalPlayers;
441 	}
442 
443 	int getFirstLocalPlayerNumber(){
444 		return firstLocalPlayer;
445 	}
446 
447 
448 	this(int graphics = NoVideo, bool sound = false, int timerClick = 0, int numOfLocalPlayers = 1, in char[] network = null){
449 		bool joystick = true;
450 
451 		int init = 0;
452 
453 		numberOfPlayers = numberOfLocalPlayers = numOfLocalPlayers;
454 
455 
456 
457 		if(graphics)
458 			init |= SDL_INIT_VIDEO;
459 		if(timerClick)
460 			init |= SDL_INIT_TIMER;
461 		if(sound)
462 			init |= SDL_INIT_AUDIO;
463 		if(joystick)
464 			init |= SDL_INIT_JOYSTICK;
465 
466 		msPerTick = timerClick;
467 
468 		if(SDL_Init(init) == -1 ){
469 			throw new Exception("SDL_Init");
470 		}
471 		scope(failure) SDL_Quit();
472 
473 
474 // SDL_WM_SetIcon(SDL_LoadBMP("icon.bmp"), NULL);
475 
476 
477 		if(network !is null){
478 			if(SDLNet_Init() < 0)
479 				throw new Exception("SDLNet_Init");
480 			scope(failure) SDLNet_Quit();
481 
482 
483 			if(network == "SERVER")
484 				beginServing();
485 			else
486 				connectTo(network);
487 
488 			net = true;
489 		}
490 
491 		switch(graphics & ~32){
492 			case NoVideo:
493 				screen = null;
494 			break;
495 			case Video1024x768:
496 				screen = new Screen(1024, 768, 24, true, (graphics & 32) ? true : false);
497 			break;
498 			case Video640x480:
499 				screen = new Screen(640, 480);
500 			break;
501 			case Video800x600:
502 				screen = new Screen(800, 600);
503 			break;
504 			case Video512x512:
505 				screen = new Screen(512, 512);
506 			break;
507 			case Video320x200:
508 				screen = new Screen(320, 200);
509 			break;
510 			default:
511 				throw new Exception("Invalid screen type");
512 		}
513 		scope(failure) delete screen;
514 
515 		if(timerClick)
516 			SDL_AddTimer(timerClick, &tcallback, null);
517 
518 		if(sound)
519 			audio = new Audio;
520 		else
521 			audio = new Audio(false);
522 
523 		scope(failure) delete audio;
524 
525 		if(joystick && SDL_NumJoysticks() > 0){
526 			SDL_JoystickEventState(SDL_ENABLE);
527 			for(int a = 0; a < SDL_NumJoysticks(); a++)
528 				joyStick[a] = SDL_JoystickOpen(a);
529 		}
530 		else
531 			joyStick[0] = null;
532 
533 		scope(failure){	for(int a = 0; a < 16; a++) if(joyStick[a]) SDL_JoystickClose(joyStick[a]); }
534 
535 
536 		//SDL_ShowCursor(SDL_DISABLE); // FIXME: make this a call
537 
538 //***********************************************************************
539 	// FIXME: it should load controller maps from a config file
540 
541 		// My playstation controller
542 		for(int a = 0; a < 13; a++)
543 			mapJoystickKeyToButton(a, cast(Buttons) a, 0, firstLocalPlayer);
544 
545 		leftStickXAxis[0] = 0;
546 		leftStickYAxis[0] = 1;
547 		dpadXAxis[0] = 4;
548 		dpadYAxis[0] = 5;
549 		rightStickXAxis[0] = 2;
550 		rightStickYAxis[0] = 3;
551 		leftTriggerAxis[0] = -1;
552 		rightTriggerAxis[0] = -1;
553 
554 		// 360 controllers
555 		for(int b = 1; b < 16; b++){
556 			mapJoystickKeyToButton(1, circle, b, firstLocalPlayer + b);
557 			mapJoystickKeyToButton(0, cross, b, firstLocalPlayer + b);
558 			mapJoystickKeyToButton(4, square, b, firstLocalPlayer + b);
559 			mapJoystickKeyToButton(3, triangle, b, firstLocalPlayer + b);
560 
561 			mapJoystickKeyToButton(16, select, b, firstLocalPlayer + b);
562 			mapJoystickKeyToButton(8, start, b, firstLocalPlayer + b);
563 
564 			mapJoystickKeyToButton(10, L3, b, firstLocalPlayer + b);
565 			mapJoystickKeyToButton(11, R3, b, firstLocalPlayer + b);
566 
567 			mapJoystickKeyToButton(6, L1, b, firstLocalPlayer + b);
568 			mapJoystickKeyToButton(7, R1, b, firstLocalPlayer + b);
569 
570 			mapJoystickKeyToButton(9, special, b, firstLocalPlayer + b);
571 
572 			mapJoystickKeyToButton(15, left, b, firstLocalPlayer + b);
573 			mapJoystickKeyToButton(12, up, b, firstLocalPlayer + b);
574 			mapJoystickKeyToButton(13, right, b, firstLocalPlayer + b);
575 			mapJoystickKeyToButton(14, down, b, firstLocalPlayer + b);
576 
577 			leftStickXAxis[b] = 0;
578 			dpadXAxis[b] = -1;
579 			leftStickYAxis[b] = 1;
580 			dpadYAxis[b] = -1;
581 			rightStickXAxis[b] = 4;
582 			rightStickYAxis[b] = 3;
583 			leftTriggerAxis[b] = 2;
584 			rightTriggerAxis[b] = 5;
585 		}
586 
587 
588 
589 		// Some sane keyboard defaults
590 
591 		keyboardMap['s'] = InputMap(circle, firstLocalPlayer);
592 		keyboardMap['x'] = InputMap(cross, firstLocalPlayer);
593 		keyboardMap['w'] = InputMap(triangle, firstLocalPlayer);
594 		keyboardMap['a'] = InputMap(square, firstLocalPlayer);
595 		keyboardMap[' '] = InputMap(circle, firstLocalPlayer);
596 		keyboardMap['d'] = InputMap(L1, firstLocalPlayer);
597 		keyboardMap['f'] = InputMap(R1, firstLocalPlayer);
598 		keyboardMap['e'] = InputMap(L2, firstLocalPlayer);
599 		keyboardMap['r'] = InputMap(R2, firstLocalPlayer);
600 		keyboardMap['c'] = InputMap(L3, firstLocalPlayer);
601 		keyboardMap['v'] = InputMap(R3, firstLocalPlayer);
602 		keyboardMap['['] = InputMap(start, firstLocalPlayer);
603 		keyboardMap[']'] = InputMap(select, firstLocalPlayer);
604 		keyboardMap['='] = InputMap(special, firstLocalPlayer);
605 
606 		keyboardMap[SDLK_UP] = InputMap(up, firstLocalPlayer);
607 		keyboardMap[SDLK_DOWN] = InputMap(down, firstLocalPlayer);
608 		keyboardMap[SDLK_LEFT] = InputMap(left, firstLocalPlayer);
609 		keyboardMap[SDLK_RIGHT] = InputMap(right, firstLocalPlayer);
610 
611 		keyboardMap['k'] = InputMap(up, firstLocalPlayer);
612 		keyboardMap['j'] = InputMap(down, firstLocalPlayer);
613 		keyboardMap['h'] = InputMap(left, firstLocalPlayer);
614 		keyboardMap['l'] = InputMap(right, firstLocalPlayer);
615 
616 
617 
618 	}
619 
620 	void moveMouse(Point pos){
621 		SDL_WarpMouse(cast(ushort) pos.x, cast(ushort) pos.y);
622 
623 	}
624 
625 	bool capturedInput = false;
626 
627 	void captureInput(){
628 		if(capturedInput)
629 			return;
630 		SDL_WM_GrabInput(1);
631 		capturedInput = true;
632 	}
633 
634 	void unCaptureInput(){
635 		if(!capturedInput)
636 			return;
637 		SDL_WM_GrabInput(0);
638 		capturedInput = false;
639 	}
640 
641 	~this(){
642 		unCaptureInput();
643 		if(net){
644 			SDLNet_FreeSocketSet(socketset);
645 			SDLNet_Quit();
646 		}
647 		for(int a = 0; a < 16; a++)
648 		if(joyStick[a])
649 			SDL_JoystickClose(joyStick[a]);
650 		delete audio;
651 		delete screen;
652 
653 		foreach(a; objs)
654 			if(a !is null)
655 				delete a;
656 		SDL_Quit();
657 	}
658 
659 	void run(){
660 		eventLoop();
661 	}
662 
663 	void setTitle(in char[] title){
664 		SDL_WM_SetCaption(std..string.toStringz(title), null);
665 	}
666 
667 	bool buttonWasPressed(Buttons button, int which = 0){
668 		if(!buttonsChecked[button][which] && buttonsDown[button][which]){
669 			buttonsChecked[button][which] = true;
670 			return true;
671 		}
672 		return false;
673 	}
674 
675 	bool buttonIsDown(Buttons button, int which = 0){
676 		if(button < NUM_BUTTONS && button >= 0)
677 			return buttonsDown[button][which];
678 		return false;
679 	}
680 	
681 	bool keyWasPressed(int button){
682 		if(button < 400 && button >= 0)
683 		if(!keysChecked[button] && keysDown[button]){
684 			keysChecked[button] = true;
685 			return true;
686 		}
687 		return false;
688 	}
689 
690 	bool keyIsDown(int button){
691 		if(button < 400 && button >= 0)
692 			return keysDown[button];
693 		assert(0);
694 	}
695 
696 	int getStickX(int stick, int which = 0){
697 		if( stick >= 0 && stick < 2)
698 			return stickX[stick][which];
699 		else
700 			return 0;
701 	}
702 
703 	int getStickY(int stick, int which = 0){
704 		if( stick >= 0 && stick < 2)
705 			return stickY[stick][which];
706 		else
707 			return 0;
708 	}
709 
710 	int getMouseX(){
711 		return mouseX;
712 	}
713 
714 	int getMouseY(){
715 		return mouseY;
716 	}
717 
718 	int getMouseChangeX(){
719 		int a = mousedx;
720 		mousedx = 0;
721 		return a;
722 	}
723 
724 	int getMouseChangeY(){
725 		int a = mousedy;
726 		mousedy = 0;
727 		return a;
728 	}
729 
730 	bool mouseHasMoved(){
731 		return (getMouseChangeY != 0 || getMouseChangeX != 0);
732 	}
733 
734 	bool mouseButtonWasPressed(int button){
735 		if(!mouseButtonsChecked[button] && mouseButtonsDown[button]){
736 			mouseButtonsChecked[button] = true;
737 			return true;
738 		}
739 		return false;
740 	}
741 
742 	bool mouseButtonIsDown(int button){
743 		if(button < 8 && button >= 0)
744 			return mouseButtonsDown[button];
745 		return false;
746 	}
747 
748 	Point mouseLocation(){
749 		return XY(mouseX, mouseY);
750 	}
751 
752 	void quit(){
753 		wantToQuit = true;
754 	}
755 
756 	bool isAltDown(){
757 		return (SDL_GetModState() & KMOD_ALT) ? true : false;
758 	}
759 	bool isControlDown(){
760 		return  (SDL_GetModState() & KMOD_CTRL) ? true : false;
761 	}
762 	bool isShiftDown(){
763 		return  (SDL_GetModState() & KMOD_SHIFT) ? true : false;
764 	}
765 
766   protected:
767 	const int BUTTONDOWN = 0;
768 	const int BUTTONUP   = 1;
769 	const int MOTION     = 2;
770 
771 	void keyEvent(int type, int keyCode, int character, int modifiers){
772 		defaultKeyEvent(type, keyCode, character, modifiers);
773 	}
774 
775 	void defaultKeyEvent(int type, int keyCode, int character, int modifiers){
776 		if(character == 'q' || keyCode == 'q')
777 			quit();
778 		if(type == BUTTONUP && keyCode == SDLK_F3){
779 			if(capturedInput)
780 				unCaptureInput();
781 			else
782 				captureInput();
783 		}
784 	}
785 
786 	void mouseEvent(int type, int x, int y, int xrel, int yrel, int button, int flags){
787 		defaultMouseEvent(type, x, y, xrel, yrel, button, flags);
788 	}
789 
790 	final void defaultMouseEvent(int type, int x, int y, int xrel, int yrel, int button, int flags){
791 
792 	}
793 
794 	void joystickEvent(int type, int whichStick, int button, int state){
795 		defaultJoystickEvent(type, whichStick, button, state);
796 	}
797 
798 	final void defaultJoystickEvent(int type, int whichStick, int button, int state){
799 
800 	}
801 
802 	bool quitEvent(){
803 		return true;
804 	}
805 
806 	void timerEvent(){
807 
808 		// Need to add network receives and the lag timer loops
809 		if(net)
810 			getNetworkControllerData();
811 
812 		// do we actually need to lag here? hmmm.....
813 
814 		globalTimer++;
815 		foreach(a; objs){//copy){
816 			if(a is null)
817 				continue;
818 			a.frame();
819 		}
820 
821 		if(lag)
822 			updateControllers();
823 
824 	}
825 
826 	public Screen screen;
827 	public Audio audio;
828 
829   private:
830 
831 	bool net;
832 
833 
834 	SDL_Joystick*[16] joyStick;
835 		struct InputMap{
836 			int button; // or direction
837 			int which; // which player
838 		}
839 	InputMap[int] keyboardMap;
840 	InputMap[int][16] joystickMap; // joystickMap[which][button] = translated val
841 
842 	int[16] leftStickXAxis;
843 	int[16] dpadXAxis;
844 	int[16] leftStickYAxis;
845 	int[16] dpadYAxis;
846 	int[16] rightStickXAxis;
847 	int[16] rightStickYAxis;
848 	int[16] leftTriggerAxis;
849 	int[16] rightTriggerAxis;
850 
851 
852 
853 
854 	bool[400] keysDown;
855 	bool[400] keysChecked;
856 
857 	bool wantToQuit;
858 
859 	bool[NUM_BUTTONS][16] buttonsDown;
860 	bool[NUM_BUTTONS][16] buttonsChecked;
861 
862 	const int LAG_QUEUE_SIZE = 10;
863 	// This lag is used for network games. It sends you old data until the lag time is up,
864 	// to try and keep all the players synchronized.
865 	int[NUM_BUTTONS][16][LAG_QUEUE_SIZE] buttonLagRemaining;
866 
867 	// This way we can queue up activities happening while the lag is waiting
868 	int[NUM_BUTTONS][16] buttonLagQueueStart;
869 	int[NUM_BUTTONS][16] buttonLagQueueEnd;
870 	int[NUM_BUTTONS][16] buttonLagQueueLength;
871 
872 	// These store what the state was before the lag began; it is what is returned while
873 	// waiting on the lag to complete
874 	bool[NUM_BUTTONS][16][LAG_QUEUE_SIZE] lagbuttonsDown;
875 
876 
877 
878 	int[3][16] stickX;
879 	int[3][16] stickY;
880 
881 	bool[8] mouseButtonsDown;
882 	bool[8] mouseButtonsChecked;
883 	const int LEFT = SDL_BUTTON_LEFT;//1;
884 	const int MIDDLE = SDL_BUTTON_MIDDLE;//2;
885 	const int RIGHT = SDL_BUTTON_RIGHT;//3;
886 	const int SCROLL_UP = 4;
887 	const int SCROLL_DOWN = 5;
888 
889 	int mouseX;
890 	int mouseY;
891 	int mousedx;
892 	int mousedy;
893 
894 	bool altDown;
895 	bool controlDown;
896 	bool shiftDown;
897 
898 	void mapJoystickKeyToButton(int a, Buttons b, int whichJoystick, int whichPlayer){
899 		if(b > NUM_BUTTONS)
900 			return;
901 		joystickMap[whichJoystick][a] = InputMap(cast(int) b, whichPlayer);
902 	}
903 
904 	/*
905 		How does this work?
906 
907 		when and local are the fancy ones.
908 
909 		Maybe when should always be globalTimer + 1. This way, you have a local wait of 1 frame
910 		and the remote ones are set to go off one frame later, which gives them time to get down the wire.
911 
912 		I think that works.
913 
914 	*/
915 
916 
917 	uint lag = 0; // should not be > 10 XXX
918 
919 	void getNetworkControllerData(){
920 		int type, when, which, button;
921 
922 		if(!net) return;
923 
924 			int n = SDLNet_CheckSockets(socketset, 0); // timeout of 1000 might be good too
925 			if(n < 0)
926 				throw new Exception("Check sockets");
927 			if(n == 0)
928 				return;
929 
930 			if(isServer){
931 			for(int a = 0; a < numberOfClients; a++){
932 				if(SDLNet_SocketReady(clients[a].sock)){
933 					byte[16] data;
934 					if(SDLNet_TCP_Recv(clients[a].sock, data.ptr, 16) <= 0){
935 						throw new Exception("someone closed");
936 					}
937 
938 					type = SDLNet_Read32(data.ptr);
939 					when = SDLNet_Read32(data.ptr+4);
940 					which = SDLNet_Read32(data.ptr+8);
941 					button = SDLNet_Read32(data.ptr+12);
942 
943 					changeButtonState(cast(Buttons) button, type == 0 ? true : false, which, when);
944 
945 					// don't forget to forward the data to everyone else
946 					for(int b = 0; b< numberOfClients; b++)
947 						if(b != a)
948 						if(SDLNet_TCP_Send(clients[b].sock, data.ptr, 16) < 16)
949 							throw new Exception("network send failure");
950 
951 				}
952 			}
953 			} else if(SDLNet_SocketReady(clientsock)) {
954 				byte[16] data;
955 				if(SDLNet_TCP_Recv(clientsock, data.ptr, 16) <= 0){
956 					throw new Exception("connection closed");
957 				}
958 				type = SDLNet_Read32(data.ptr);
959 				when = SDLNet_Read32(data.ptr+4);
960 				which = SDLNet_Read32(data.ptr+8);
961 				button = SDLNet_Read32(data.ptr+12);
962 
963 				changeButtonState(cast(Buttons) button, type == 0 ? true : false, which, when);
964 			}
965 
966 
967 	}
968 
969 	void changeButtonState(Buttons button, bool type, int which, uint when, bool sendToNet = false){
970 	if(when  <= lag)
971 		return;
972 		if(when > globalTimer){
973 			lagbuttonsDown[button][which][buttonLagQueueEnd[button][which]] = type;
974 			buttonLagRemaining[button][which][buttonLagQueueEnd[button][which]] = when - globalTimer;
975 
976 			buttonLagQueueLength[button][which]++;
977 			buttonLagQueueEnd[button][which]++;
978 			if(buttonLagQueueEnd[button][which] == LAG_QUEUE_SIZE)
979 				buttonLagQueueEnd[button][which] = 0;
980 		} else {
981 			if(when < globalTimer)
982 				throw new Exception(immutableString("Impossible control timing " ~ convToString(when) ~ " @ " ~ convToString(globalTimer)));
983 			buttonsDown[button][which] = type;
984 			buttonsChecked[button][which] = false;
985 		}
986 
987 		if(net && sendToNet){
988 			byte[16] data;
989 			SDLNet_Write32(type ? 0 : 1, data.ptr);
990 			SDLNet_Write32(when, data.ptr+4);
991 			SDLNet_Write32(which, data.ptr+8);
992 			SDLNet_Write32(button, data.ptr+12);
993 			if(isServer){
994 				for(int a = 0; a< numberOfClients; a++)
995 					if(SDLNet_TCP_Send(clients[a].sock, data.ptr, 16) < 16)
996 						throw new Exception("network send failure");
997 
998 			} else {
999 				if(SDLNet_TCP_Send(clientsock, data.ptr, 16) < 16)
1000 					throw new Exception("network send failure");
1001 			}
1002 		}
1003 	}
1004 
1005 	void updateControllers(){
1006 		for(int a = 0; a < 16; a++){ // FIXME: should be changed to number of players
1007 			for(int b = 0; b < NUM_BUTTONS; b++)
1008 				for(int co = 0, q = buttonLagQueueStart[b][a]; co < buttonLagQueueLength[b][a]; q++, co++){
1009 				if(q == LAG_QUEUE_SIZE)
1010 					q = 0;
1011 				if(buttonLagRemaining[b][a][q]){
1012 					buttonLagRemaining[b][a][q]--;
1013 					if(!buttonLagRemaining[b][a][q]){
1014 						changeButtonState(cast(Buttons) b, lagbuttonsDown[b][a][q], a, globalTimer);
1015 						buttonLagQueueStart[b][a]++;
1016 						buttonLagQueueLength[b][a]--;
1017 						if(buttonLagQueueStart[b][a] == LAG_QUEUE_SIZE)
1018 							buttonLagQueueStart[b][a] = 0;
1019 					}
1020 				}
1021 				}
1022 		}
1023 	}
1024 
1025 	int eventLoop(){
1026 		SDL_Event event;
1027 	  while(SDL_WaitEvent(&event) >= 0 && !wantToQuit){
1028 	    switch(event.type){
1029 		case SDL_KEYUP:
1030 		case SDL_KEYDOWN:
1031 			bool type = event.key.type == SDL_KEYDOWN ? true : false;
1032 				if(event.key.keysym.sym in keyboardMap){
1033 					int button = keyboardMap[event.key.keysym.sym].button;
1034 					int which = keyboardMap[event.key.keysym.sym].which;
1035 					changeButtonState(cast(Buttons) button, type, which, globalTimer + lag, true);
1036 				}
1037 
1038 			if(event.key.keysym.sym < 400){
1039 				keysDown[event.key.keysym.sym] = (event.key.type == SDL_KEYDOWN) ? true : false;
1040 				keysChecked[event.key.keysym.sym] = false;
1041 			}
1042 
1043 
1044 			keyEvent(
1045 				event.key.type == SDL_KEYDOWN ? BUTTONDOWN : BUTTONUP,
1046 				event.key.keysym.sym,
1047 				event.key.keysym.unicode,
1048 				event.key.keysym.mod
1049 			);
1050 		break;
1051 		case SDL_JOYAXISMOTION:
1052 		// the things here are to avoid little changes around the center if the stick isn't perfect
1053 		if ( ( event.jaxis.value < -3200 ) || (event.jaxis.value > 3200 ) || event.jaxis.value == 0){
1054 			int stick;
1055 			if(event.jaxis.axis >= 0 && event.jaxis.axis < 6)
1056 
1057 				stick = event.jaxis.axis;
1058 			else
1059 				break;
1060 
1061 			int which = event.jaxis.which;
1062 			if(stick == leftStickXAxis[which] || stick == dpadXAxis[which]){
1063 				changeButtonState(left, event.jaxis.value < -28000, which, globalTimer + lag, true);
1064 				changeButtonState(right, event.jaxis.value > 28000, which, globalTimer + lag, true);
1065 
1066 				stickX[0][which] = event.jaxis.value;
1067 			}
1068 			if(stick == leftStickYAxis[which] || stick == dpadYAxis[which]){
1069 				changeButtonState(up, event.jaxis.value < -28000, which, globalTimer + lag, true);
1070 				changeButtonState(down, event.jaxis.value > 28000, which, globalTimer + lag, true);
1071 
1072 				stickY[0][which] = event.jaxis.value;
1073 			}
1074 			if(stick == rightStickXAxis[which]){
1075 				stickX[1][which] = event.jaxis.value;
1076 				changeButtonState(left2, event.jaxis.value < -28000, which, globalTimer + lag, true);
1077 				changeButtonState(right2, event.jaxis.value > 28000, which, globalTimer + lag, true);
1078 			}
1079 			if(stick == rightStickYAxis[which]){
1080 				stickY[1][which] = event.jaxis.value;
1081 				changeButtonState(up2, event.jaxis.value < -28000, which, globalTimer + lag, true);
1082 				changeButtonState(down2, event.jaxis.value > 28000, which, globalTimer + lag, true);
1083 			}
1084 			// x-box 360 controller stuff
1085 			if(stick == leftTriggerAxis[which]){
1086 				stickX[2][which] = event.jaxis.value;
1087 
1088 				changeButtonState(L2, event.jaxis.value > 32000, which, globalTimer + lag, true);
1089 			}
1090 			if(stick == rightTriggerAxis[which]){
1091 				stickY[2][which] = event.jaxis.value;
1092 				changeButtonState(R2, event.jaxis.value > 32000, which, globalTimer + lag, true);
1093 			}
1094 
1095 
1096 			joystickEvent(
1097 				MOTION,
1098 				event.jaxis.which,
1099 				event.jaxis.axis,
1100 				event.jaxis.value
1101 			);
1102 		}
1103 		break;
1104 		case SDL_JOYHATMOTION:
1105 		/+
1106 			joystickEvent(
1107 				HATMOTION,
1108 				event.jhat.which,
1109 				event.jhat.hat,
1110 				event.jhat.value
1111 			);
1112 		+/
1113 		break;
1114 		case SDL_JOYBUTTONDOWN:
1115 		case SDL_JOYBUTTONUP:
1116 			if(event.jbutton.button in joystickMap[event.jbutton.which]){
1117 				int which = joystickMap[event.jbutton.which][event.jbutton.button].which;
1118 				int button = joystickMap[event.jbutton.which][event.jbutton.button].button;
1119 
1120 				changeButtonState(cast(Buttons) button, event.jbutton.type == SDL_JOYBUTTONDOWN ? true : false, which, globalTimer + lag, true);
1121 			}
1122 
1123 			joystickEvent(
1124 				event.jbutton.type == SDL_JOYBUTTONDOWN ? BUTTONDOWN : BUTTONUP,
1125 				event.jbutton.which,
1126 				event.jbutton.button,
1127 				event.jbutton.state
1128 			);
1129 		break;
1130 
1131 		case SDL_MOUSEBUTTONDOWN:
1132 		case SDL_MOUSEBUTTONUP:
1133 			mouseButtonsDown[event.button.button] = event.button.type == SDL_MOUSEBUTTONDOWN ? true : false;
1134 			mouseButtonsChecked[event.button.button] = false;
1135 			mouseEvent(
1136 				event.button.type == SDL_MOUSEBUTTONDOWN ? BUTTONDOWN : BUTTONUP,
1137 				event.button.x,
1138 				event.button.y,
1139 				0,		// xrel
1140 				0,		// yrel
1141 				event.button.button,
1142 				0		//state
1143 			);
1144 		break;
1145 		case SDL_MOUSEMOTION:
1146 			mouseX = event.motion.x;
1147 			mouseY = event.motion.y;
1148 			mousedx += event.motion.xrel;
1149 			mousedy += event.motion.yrel;
1150 		
1151 			mouseEvent(
1152 				MOTION,
1153 				event.motion.x,
1154 				event.motion.y,
1155 				event.motion.xrel,
1156 				event.motion.yrel,
1157 				0,
1158 				event.motion.state
1159 			);
1160 		break;
1161 
1162 		case SDL_USEREVENT:
1163 			timerEvent();
1164 		break;
1165 		case SDL_QUIT:
1166 			if(quitEvent() == true)
1167 				quit();
1168 		break;
1169 		default:
1170 	    }
1171 	  }
1172 		return 0;
1173 	}
1174 
1175 }
1176 extern(C){
1177 	Uint32 tcallback(Uint32 interval, void* param){
1178 		if(waiting)
1179 			return interval;
1180 		SDL_Event event;
1181 
1182 		event.type = SDL_USEREVENT;
1183 		event.user.code = 0;
1184 		event.user.data1 = null;
1185 		event.user.data2 = null;
1186 		SDL_PushEvent(&event);
1187 
1188 		return interval;
1189 	}
1190 }
1191 
1192 int SDLNet_SocketReady(void* sock) {
1193         SDLNet_GenericSocket s = cast(SDLNet_GenericSocket)sock;
1194         return sock != cast(TCPsocket)0 && s.ready;             
1195 }
1196 
1197 
1198 
1199 Engine engine;
1200 
1201 bool buttonWasPressed(Engine.Buttons button, int which = 0){
1202 	return engine.buttonWasPressed(button, which);
1203 }
1204 
1205 bool buttonIsDown(Engine.Buttons button, int which = 0){
1206 	return engine.buttonIsDown(button, which);
1207 }
1208 /*
1209 bool directionWasPressed(Engine.Direction direction, int which = 0){
1210 	return engine.directionWasPressed(direction, which);
1211 }
1212 
1213 bool directionIsDown(Engine.Direction d, int which = 0){
1214 	return engine.directionIsDown(d, which);
1215 }
1216 */
1217 
1218 
1219 
1220 version(linux) {
1221 	version(D_Version2) {
1222 		import sys = core.sys.posix.sys.select;
1223 		version=CustomKbhit;
1224 
1225 		int kbhit()
1226 		{
1227 			sys.timeval tv;
1228 			sys.fd_set read_fd;
1229 
1230 			tv.tv_sec=0;
1231 			tv.tv_usec=0;
1232 			sys.FD_ZERO(&read_fd);
1233 			sys.FD_SET(0,&read_fd);
1234 
1235 			if(sys.select(1, &read_fd, null, null, &tv) == -1)
1236 				return 0;
1237 
1238 			if(sys.FD_ISSET(0,&read_fd))
1239 				return 1;
1240 
1241 			return 0;
1242 		}
1243 	}
1244 	
1245 	// else, use kbhit.o from the C file
1246 }
1247 
1248 version(CustomKbhit) {} else
1249 	extern(C) bool kbhit();
1250 
1251 
1252 
Suggestion Box / Bug Report