File: input.cpp

package info (click to toggle)
postal1 2015.git20250526%2Bds-2
  • links: PTS, VCS
  • area: contrib
  • in suites: forky, sid
  • size: 14,024 kB
  • sloc: cpp: 130,877; ansic: 38,942; python: 874; makefile: 351; sh: 61
file content (1334 lines) | stat: -rw-r--r-- 42,681 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 RWS Inc, All Rights Reserved
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as published by
// the Free Software Foundation
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
// input.cpp
// Project: Postal
//
// This module deals with getting user input.
//
// History:
//		12/05/96 MJR	Started.
//
//		03/24/97	JMI	Changed m_aInputs[] from long to UINPUT.
//
//		03/31/97	JMI	Added g_InputSettings instantiation.
//
//		04/11/97 MJR	Moved lots of stuff into input.cpp and added support for
//							recording and playing back inputs for self-playing mode.
//
//		04/11/97	JMI	Fixed typo with comparison in SetInputMode().
//							Also, added event driven keys.
//							Also, no user input allowed in release mode **this is temp
//							for ABC demo**.
//
//		04/24/97	JMI	Moved around INPUT_JUMP, INPUT_SUICIDE, and 
//							INPUT_NEXT_LEVEL.
//							Also, added INPUT_WEAPON_6...9 and INPUT_EXECUTE.  
//							Consequently, we're out of inputs (and we still don't have
//							INPUT_TOGGLE_HUMANSHIELD).
//
//		04/25/97	JMI	Forgot to add check for INPUT_EXECUTE on last update.
//
//		05/02/97	JMI	Increased number of available input bits by reducing
//							the weapon changing bits to 4 bits (now a number instead of
//							bit fields).
//
//		05/13/97	JMI	Changed INPUT_RUN to INPUT_WALK.
//							Changed Suicide and NextLevel to polling instead of event
//							driven.
//
//		05/14/97	JMI	Added INPUT_PICKUP.
//
//		06/06/97	JMI	Now uses rspGetKeyStatusArray() instead of rspScanKeys().
//							The rspGetKeyStatusArray() method is much more likely to
//							catch keys that get missed by polling, but, better than
//							rpsGetKey(), this function works on a key basis rather than
//							a key combo basis (i.e., SHIFT-1 is viewed as SHIFT and '1'
//							with rspGetKeyStatusArray() (like rspScanKeys()) but with
//							rspGetKey() it is seen as '!').
//
//		06/09/97	JMI	Changed from IS_INPUT to WAS_INPUT for Jump, 
//							Execute, Suicide, NextLevel, and PickUp.
//
//		06/09/97	JMI	Added tap rotation when standing.
//
//		06/09/97	JMI	Changed 'Walk' to 'Run'.
//
//		06/10/97	JMI	Changed to use all the new variations of user settable
//							rotation.
//
//		06/12/97	JMI	Added temp version of cheat keys that utilizes backspace
//							in combination with the normal weapon keys 1 - 5.
//
//		06/15/97 MJR	Removed INPUT_NEXT_LEVEL, which has now been replaced by
//							an client/server mechanism.
//							Changed the input modes from macros to enums.
//							Added function to delete demo data buffer.
//
//		06/15/97 MJR	Changed to separate InputDemoInit() and InputDemoKill()
//							functions to make it real obvious and clean.
//							If you fill the entire record buffer, the dude will
//							automatically commit suicide as the last thing he does.
//
//		07/06/97	JMI	Changed m_au8PlayKeys[] from a U8 array to a short array,
//							m_asPlayKeys[].
//							Also, changed m_asPlayButtons to m_asPlayMouseButtons.
//
//		07/07/97	JMI	Now uses g_InputSettings.m_dMouseSensitivityX,Y to tweak
//							the mouse input values before interpretation by the normal 
//							input rotational logic.
//
//		07/16/97	JMI	Added input for weapon ten reducing the number of cheats to
//							4.
//
//		07/19/97	JMI	Added InitLocalInput().  Also, changed inputLast which was
//							a static local to GetLocalInput() to file scope 
//							(ms_inputLastLocal) so it could be altered by other 
//							functions.
//
//		07/23/97	JMI	Changed cheats to key combos.  See ms_acheats to find out
//							what you have to type.
//
//		07/24/97	JMI	Now the cheat keys are NOT stored in the EXE as exactly
//							as they are typed to discourage the possibility people can
//							find them easily by viewing/searching the EXE.
//
//		07/25/97	JMI	Added more INPUT_*'s and changed INPUT_WEAPON_11,12,13,14
//							to INPUT_CHEAT_11,12,13,14.
//							Also, removed INPUT_PICKUP.  There are now 20 possible
//							cheat inputs.
//
//		07/30/97	JMI	Added INPUT_CHEAT_16.
//
//		08/04/97	JMI	Originally setup the Y mouse sensitivity so it work 
//							backwards (i.e., a higher number was less sensitive).
//							Fixed.
//
//		08/05/97	JMI	Modified FindCheatCombos() to search until no cheat key has
//							been found.
//
//		08/06/97	JMI	Changed InitLocalInput() to ClearLocalInput().
//
//		08/07/97	JMI	Added INPUT_CHEAT_17.
//
//		08/10/97	JMI	Added StrafeLeft and StrafeRight inputs.
//							Also, changed Jump to Revive and INPUT_JUMP to INPUT_REVIVE.
//
//		08/12/97	JMI	Now requires an input event for cheats for GetLocalInput().
//							Anagrams will no longer work and it will not be liberal
//							about additional keys mixed in (e.g., HONC would not work
//							for HOC).
//
//		08/12/97	JMI	Now the run key becomes the walk key when the caps lock
//							is on.
//							Also added support for Run2 and Strafe2 and removed 
//							StrafeLeft and StrafeRight.
//
//		08/17/97	JMI	Changed MAX_CONSEQ_CHEAT_KEY_LAG from 1000 to 2000.
//
//		08/18/97	JMI	Renamed almost all existing cheats and added new ones.
//							There is one empty cheat slot.
//
//		08/18/97	JMI	Added cheat code for sales people.
//
//		08/18/97	JMI	Added filler for non-sales cheat code so it wouldn't make
//							a cheat feedback sound when you simply typed 'sell'.
//
//		08/20/97	JMI	Changed cheat 29 for advance level.
//
//		08/26/97 BRH	Added a query function to see if the demo is done.
//
//		08/27/97	JMI	Added Fire2.
//
//		08/28/97	JMI	Added safety initializations for dRate in GetLocalInput().
//							Should not be necessary if no logic errors, but what the
//							hey?!
//
//		09/03/97	JMI	Now compiles in the new or the old cheats based on 
//							USE_NEW_CHEATS.
//
//		09/06/97 MJR	Changed ClearLocalInput() to use INPUT_IDLE.
//
//		09/17/97	JMI	Added DOS-for-Dummies (or 'Paloma') cheats for the editors'
//							demo as a conditional compile option.
//
//		09/24/97	JMI	Now does not set INPUT_EXECUTE when LOCALE == UK.
//
//		09/24/97	JMI	Now only sets INPUT_EXECUTE when LOCALE == US.
//
//		10/10/97	JMI	Now uses joystick settings if ALLOW_JOYSTICK is defined.
//
//		11/05/97	JMI	Converted to new joystick API.
//
//		12/04/97	JMI	Reduced horizontal mouse sensitivity by a factor of 3.
//
//		12/08/97	JMI	Casting order was causing a rightward motion bias that was
//							previously only notable on the Alpha at high frame rates
//							but, now that we exaggerated the mouse sensitivity values,
//							it is more obvious at lower frame rates.
//
//		09/27/99	JMI	Changed to allow execution only in any locale 
//							satisfying the CompilerOptions macro VIOLENT_LOCALE.
//
////////////////////////////////////////////////////////////////////////////////

#include "RSPiX.h"
#include "input.h"
#include "CompileOptions.h"

#ifdef MOBILE
#include "android/android.h"
#endif

////////////////////////////////////////////////////////////////////////////////
// Macros/types/etc.
////////////////////////////////////////////////////////////////////////////////

// Location to set mouse to
#define MOUSE_RESET_X		320
#define MOUSE_RESET_Y		240

// Threshold of mouse Y movement that is unnoticed by the input module.
// More movement than this is considered an indication to move the dude
// forward or backward.
#define MOUSE_Y_THRESH		40

// Check for the specified input.
// This equates to:  is the key for this input pressed or, IF there are mouse 
// buttons for this input, are the mouse buttons in that combination?
#define IS_INPUT(input)																														\
	(	(pu8KeyStatus[g_InputSettings.m_asPlayKeys[input]]	& 1)																	\
	||	((sButtons & g_InputSettings.m_asPlayMouseButtons[input]) == g_InputSettings.m_asPlayMouseButtons[input]	\
		&& g_InputSettings.m_asPlayMouseButtons[input]	!= 0) IS_JOY_INPUT(input) )

// This equates to:  was the key for this input pressed or, IF there are mouse 
// buttons for this input, are the mouse buttons in that combination?
// You should use CLEAR_INPUT(input) with this version.
#define WAS_INPUT(input)																													\
	(	pu8KeyStatus[g_InputSettings.m_asPlayKeys[input]]																			\
	||	((sButtons & g_InputSettings.m_asPlayMouseButtons[input]) == g_InputSettings.m_asPlayMouseButtons[input]	\
		&& g_InputSettings.m_asPlayMouseButtons[input]	!= 0) IS_JOY_INPUT(input) )

// Clear the specified input.
#define CLEAR_INPUT(input)																													\
	pu8KeyStatus[g_InputSettings.m_asPlayKeys[input]] = 0

#if defined(ALLOW_JOYSTICK)
	#define IS_JOY_INPUT(input)																											\
		||	((u32Buttons & g_InputSettings.m_asPlayJoyButtons[input]) == g_InputSettings.m_asPlayJoyButtons[input]	\
			&& g_InputSettings.m_asPlayJoyButtons[input]	!= 0)
#else
	#define IS_JOY_INPUT(input)
#endif	// defined(ALLOW_JOYSTICK)

// Minimum memory buffer to use for recording input data.  To figure out a
// reasonable size, multiply a high number of frames per second by a large
// number of total seconds.
#define BUF_MAX_ENTRIES		(30 * (5 * 60))

// Determines the number of elements in the passed array at compile time.
#define NUM_ELEMENTS(a)		(sizeof(a) / sizeof(a[0]) )

#define MAX_CONSEQ_CHEAT_KEY_LAG	2000	// In ms.

// Tweak characters of a string so they are tough to find in the exe.
// Use CHAR_UNTWEAK() to get a specific char back.
#define STR_TWEAK(c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19)	\
	{								\
	!c0 ? 0 : c0 + 1,			\
	!c1 ? 0 : c1 + 2,			\
	!c2 ? 0 : c2 + 3,			\
	!c3 ? 0 : c3 + 4,			\
	!c4 ? 0 : c4 + 5,			\
	!c5 ? 0 : c5 + 6,			\
	!c6 ? 0 : c6 + 7,			\
	!c7 ? 0 : c7 + 8,			\
	!c8 ? 0 : c8 + 9,			\
	!c9 ? 0 : c9 + 10,		\
	!c10 ? 0 : c10 + 11,		\
	!c11 ? 0 : c11 + 12,		\
	!c12 ? 0 : c12 + 13,		\
	!c13 ? 0 : c13 + 14,		\
	!c14 ? 0 : c14 + 15,		\
	!c15 ? 0 : c15 + 16,		\
	!c16 ? 0 : c16 + 17,		\
	!c17 ? 0 : c17 + 18,		\
	!c18 ? 0 : c18 + 19,		\
	!c19 ? 0 : c19 + 20,		\
	'\0'							\
	}

// Detweak a char from a string previously tweaked by STR_TWEAK().
#define DETWEAK_CHAR(str, i)	(str[i] - (i + 1) )

#define SET(ptr, val)		( ((ptr) != NULL) ? *(ptr) = (val) : 0)

typedef struct
	{
	char	szCheat[21];
	UINPUT	input;
	int32_t	lLastValidInputTime;
	int16_t	sCurrentIndex;
	} Cheat;

////////////////////////////////////////////////////////////////////////////////
// Variables/data
////////////////////////////////////////////////////////////////////////////////

// Data for each dude
UINPUT m_aInputs[INPUT_MAX_DUDES];

// Last input for the local dude.
UINPUT	ms_inputLastLocal	= 0;

// Global input settings
CInputSettings	g_InputSettings;

// Current mode
INPUT_MODE m_mode;

// Buffer-related stuff
U32* m_pBuf = 0;				// Pointer to buffer. Must be a U32 to maintain demo compatibility!
int32_t m_lBufIndex;					// Current index into buffer
int32_t m_lBufEntries;				// Total entries in buffer

// Cheat structs.
// Add one plus the index of each string item so it's not recognizable when 
// searching/viewing the exe.  The chars will be untweaked when the cheat is 
// evaluated at runtime.
static Cheat	ms_acheats[]	=
	{
#if defined(USE_LA_PALOMA_CHEATS)
						 
		{ STR_TWEAK('B', 'E', 'E', 'R', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'), INPUT_CHEAT_11 },
		{ STR_TWEAK('V', 'E', 'S', 'T', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'), INPUT_CHEAT_12 },
		{ STR_TWEAK('P', 'A', 'C', 'K', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'), INPUT_CHEAT_13 },
		{ STR_TWEAK('S', 'T', 'U', 'F', 'F', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'), INPUT_CHEAT_14 },
		{ STR_TWEAK('R', 'E', 'V', 'I', 'V', 'E', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'), INPUT_CHEAT_15 },
		{ { 'D'+1, 'A'+2, 'W'+3, 'H'+4, 'O'+5, 'L'+6, 'E'+7, 'E'+8, 'N'+9, 'C'+10, 'H'+11, 'I'+12, 'L'+13, 'A'+14, 'D'+15, 'A'+16, }, INPUT_CHEAT_16, },
		{ { 'B'+1, 'R'+2, 'E'+3, 'A'+4, 'K'+5, 'Y'+6, 'O'+7, 'S'+8, 'A'+9, 'K'+10,				}, INPUT_CHEAT_17, },
		{ STR_TWEAK('M', 'Y', 'T', 'E', 'A', 'M', 'O', 'U', 'S', 'E', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'), INPUT_CHEAT_18 },
		{ STR_TWEAK('S', 'H', 'E', 'L', 'L', 'S', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'), INPUT_CHEAT_19 },
		{ STR_TWEAK('B', 'O', 'O', 'M', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'), INPUT_CHEAT_20 },
		{ STR_TWEAK('F', 'L', 'A', 'M', 'A', 'G', 'E', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'), INPUT_CHEAT_21 },
		{ STR_TWEAK('S', 'H', 'O', 'T', 'G', 'U', 'N', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'), INPUT_CHEAT_22 },
		{ STR_TWEAK('C', 'A', 'N', 'N', 'O', 'N', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'), INPUT_CHEAT_23 },
		{ STR_TWEAK('L', 'O', 'B', 'B', 'E', 'R', 'S', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'), INPUT_CHEAT_24 },
		{ STR_TWEAK('M', 'I', 'S', 'S', 'I', 'L', 'E', 'S', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'), INPUT_CHEAT_25 },
		{ STR_TWEAK('N', 'A', 'P', 'A', 'L', 'M', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'), INPUT_CHEAT_26 },
		{ STR_TWEAK('F', 'L', 'A', 'M', 'E', 'R', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'), INPUT_CHEAT_27 },
		{ STR_TWEAK('M', 'I', 'N', 'E', 'S', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'), INPUT_CHEAT_28 },
		{ STR_TWEAK('N', 'E', 'X', 'T', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'), INPUT_CHEAT_29 },
		{ STR_TWEAK('G', 'A', 'W', 'D', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'), INPUT_CHEAT_30 },
	

#else
			{ { 'H'+1, 'E'+2, 'A'+3, 'L'+4, 'T'+5, 'H'+6, 'F'+7, 'U'+8, 'L'+9,						},	INPUT_CHEAT_11, },
			{ { 'T'+1, 'H'+2, 'I'+3, 'C'+4, 'K'+5, 'S'+6, 'K'+7, 'I'+8, 'N'+9,						},	INPUT_CHEAT_12, },
			{ { 'C'+1, 'A'+2, 'R'+3, 'R'+4, 'Y'+5, 'M'+6, 'O'+7, 'R'+8, 'E'+9,						},	INPUT_CHEAT_13, },
	#if defined(USE_NEW_CHEATS)
 			{ { 'G'+1, 'I'+2, 'M'+3, 'M'+4, 'E'+5, 'D'+6, 'A'+7, 'T'+8,									},	INPUT_CHEAT_14, },
			{ { 'H'+1, 'E'+2, 'S'+3, 'S'+4, 'T'+5, 'I'+6, 'L'+7, 'L'+8, 'G'+9, 'O'+10,	'O'+11, 'D'+12,	},	INPUT_CHEAT_15, },
	#else
 			{ { 'M'+1, 'E'+2, 'G'+3, 'A'+4, 'S'+5, 'T'+6, 'U'+7, 'F'+8, 'F'+9,						},	INPUT_CHEAT_14, },
			{ { 'G'+1, 'E'+2, 'T'+3, 'U'+4, 'P'+5, 'R'+6, 'O'+7, 'G'+8, 'U'+9, 'E'+10,				},	INPUT_CHEAT_15, },
	#endif
			{ { 'D'+1, 'A'+2, 'W'+3, 'H'+4, 'O'+5, 'L'+6, 'E'+7, 'E'+8, 'N'+9, 'C'+10, 'H'+11, 'I'+12, 'L'+13, 'A'+14, 'D'+15, 'A'+16, }, INPUT_CHEAT_16, },
			{ { 'B'+1, 'R'+2, 'E'+3, 'A'+4, 'K'+5, 'Y'+6, 'O'+7, 'S'+8, 'A'+9, 'K'+10,				}, INPUT_CHEAT_17, },
			{ { 'M'+1, 'Y'+2, 'T'+3, 'E'+4, 'A'+5, 'M'+6, 'O'+7, 'U'+8, 'S'+9, 'E'+10,				}, INPUT_CHEAT_18, },
			{ { 'S'+1, 'H'+2, 'E'+3, 'L'+4, 'L'+5, 'F'+6, 'E'+7, 'S'+8, 'T'+9,						}, INPUT_CHEAT_19, },
			{ { 'E'+1, 'X'+2, 'P'+3, 'L'+4, 'O'+5, 'D'+6, 'A'+7, 'R'+8, 'A'+9, 'M'+10, 'A'+11,	}, INPUT_CHEAT_20, },
			{ { 'F'+1, 'L'+2, 'A'+3, 'M'+4, 'E'+5, 'N'+6, 'S'+7, 'T'+8, 'E'+9, 'I'+10, 'N'+11,	}, INPUT_CHEAT_21, },
			{ { 'S'+1, 'H'+2, 'O'+3, 'T'+4, 'G'+5, 'U'+6, 'N'+7,											}, INPUT_CHEAT_22, },
			{ { 'T'+1, 'H'+2, 'E'+3, 'B'+4, 'E'+5, 'S'+6, 'T'+7, 'G'+8, 'U'+9, 'N'+10,				}, INPUT_CHEAT_23, },
			{ { 'L'+1, 'O'+2, 'B'+3, 'I'+4, 'T'+5, 'F'+6, 'A'+7, 'R'+8,									}, INPUT_CHEAT_24, },
			{ { 'T'+1, 'I'+2, 'T'+3, 'A'+4, 'N'+5, 'I'+6, 'I'+7, 'I'+8,									}, INPUT_CHEAT_25, },
			{ { 'S'+1, 'T'+2, 'E'+3, 'R'+4, 'N'+5, 'O'+6, 'M'+7, 'A'+8, 'T'+9,						}, INPUT_CHEAT_26, },
			{ { 'F'+1, 'I'+2, 'R'+3, 'E'+4, 'H'+5, 'U'+6, 'R'+7, 'L'+8, 'E'+9, 'R'+10,				}, INPUT_CHEAT_27, },
			{ { 'C'+1, 'R'+2, 'O'+3, 'T'+4, 'C'+5, 'H'+6, 'B'+7, 'O'+8, 'M'+9, 'B'+10,				}, INPUT_CHEAT_28, },
	#ifdef SALES_DEMO
			{ { 'S'+1, 'E'+2, 'L'+3, 'L'+4, 																		}, INPUT_CHEAT_29, },	// Only used when SALES_DEMO defined
	#else
			{ { 'T'+1, 'H'+2, 'E'+3, 'R'+4, 'E'+5, 'S'+6, 'N'+7, 'O'+8, 'P'+9, 'L'+10, 'A'+11, 'C'+12, 'E'+13, 'L'+14, 'I'+15, 'K'+16,  'E'+17, 'O'+18, 'Z'+19, }, INPUT_CHEAT_29, },
	#endif
			{ { 'I'+1, 'A'+2, 'M'+3, 'S'+4, 'O'+5, 'L'+6, 'A'+7, 'M'+8, 'E'+9,						}, INPUT_CHEAT_30, },

#endif	// USE_LA_PALOMA_CHEATS
	};


////////////////////////////////////////////////////////////////////////////////
// Function prototypes
////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////
//
// Set input mode.
//
// InputDemoInit() and InputDemoLoad() must be called before setting playback
// mode.
//
// InputDemoInit() must be called before setting record mode.
//
////////////////////////////////////////////////////////////////////////////////
extern void SetInputMode(
	INPUT_MODE mode)										// In:  Input mode
	{
	// Start new mode
	m_mode = mode;
	switch (m_mode)
		{
		case INPUT_MODE_LIVE:
			break;

		case INPUT_MODE_RECORD:
			// Reset index and number of entries
			m_lBufIndex = 0;
			m_lBufEntries = 0;
			break;

		case INPUT_MODE_PLAYBACK:
			// Reset index (retain whatever number of entries there currently are)
			m_lBufIndex = 0;
			break;

		default:
			// Change to known mode
			m_mode = INPUT_MODE_LIVE;
			TRACE("SetInputMode(): Unknown mode -- defaulting to INPUT_MODE_LIVE!\n");
			break;
		}
	}


////////////////////////////////////////////////////////////////////////////////
//
// Get current input mode
//
////////////////////////////////////////////////////////////////////////////////
extern INPUT_MODE GetInputMode(void)				// Returns current mode
	{
	return m_mode;
	}


////////////////////////////////////////////////////////////////////////////////
//
// Init demo mode.  Must be called before setting playback or record modes.
//
////////////////////////////////////////////////////////////////////////////////
extern int16_t InputDemoInit(void)
	{
	int16_t sResult = 0;

	// Reset index and number of entries
	m_lBufIndex = 0;
	m_lBufEntries = 0;

	// Allocate buffer
	if (m_pBuf == 0)
		{
		m_pBuf = new U32[BUF_MAX_ENTRIES];
		if (m_pBuf == 0)
			{
			sResult = -1;
			TRACE("InputDemoInit(): Error allocating buffer!\n");
			}
		}

	return sResult;
	}


////////////////////////////////////////////////////////////////////////////////
//
// Kill demo mode.  Must be called if InputDemoInit() was successfull (safe
// to call even if it wasn't.)
//
////////////////////////////////////////////////////////////////////////////////
void InputDemoKill(void)
	{
	// Delete buffer
	delete []m_pBuf;
	m_pBuf = 0;
	}


////////////////////////////////////////////////////////////////////////////////
//
// Load previously saved input demo data
//
////////////////////////////////////////////////////////////////////////////////
extern int16_t InputDemoLoad(							// Returns 0 if successfull, non-zero otherwise
	RFile* pFile)											// In:  RFile to load from
	{
	int16_t sResult = 0;

	ASSERT(m_pBuf);
	if (m_pBuf)
		{
		// Get number of entries
		if (pFile->Read(&m_lBufEntries) == 1)
			{
			if (m_lBufEntries <= BUF_MAX_ENTRIES)
				{

				// Load all entries
				for (int32_t l = 0; l < m_lBufEntries; l++)
					pFile->Read(&m_pBuf[l]);
				
				// Check for errors
				if (pFile->Error())
					{
					sResult = -1;
					TRACE("InputDemoLoad(): Error reading data!\n");
					}
				}
			else
				{
				sResult = -1;
				TRACE("InputDemoLoad(): Too many entries to fit into current buffer size!\n");
				}
			}
		else
			{
			sResult = -1;
			TRACE("InputDemoLoad(): Error reading number of entries!\n");
			}
		}
	else
		{
		sResult = -1;
		TRACE("InputDemoLoad(): No buffer!\n");
		}

	return sResult;
	}


////////////////////////////////////////////////////////////////////////////////
//
// Save current input demo data
//
////////////////////////////////////////////////////////////////////////////////
extern int16_t InputDemoSave(							// Returns 0 if successfull, non-zero otherwise
	RFile* pFile)											// In:  RFile to save to
	{
	int16_t sResult = 0;

	ASSERT(m_pBuf);
	if (m_pBuf)
		{
		// Save number of entries
		pFile->Write(m_lBufEntries);

		// Save all entries
		for (int32_t l = 0; l < m_lBufEntries; l++)
			pFile->Write(m_pBuf[l]);

		// Check for errors
		if (pFile->Error())
			{
			sResult = -1;
			TRACE("InputDemoSave(): Error saving data!\n");
			}
		}
	else
		{
		sResult = -1;
		TRACE("InputDemoSave(): No buffer!\n");
		}

	return sResult;
	}


////////////////////////////////////////////////////////////////////////////////
//
// Reset/Clear/Initialize local input.  This should be done just prior to 
// iteratively calling GetLocalInput().  It resets the last input storage and 
// positions the mouse, if mouse input is active.
//
////////////////////////////////////////////////////////////////////////////////
extern void ClearLocalInput(void)
	{
	// Reset last local input to nothing.
	ms_inputLastLocal	= INPUT_IDLE;

	// If mouse use is specified (be nice about background state) . . .
	if (g_InputSettings.m_sUseMouse != FALSE && rspIsBackground() == FALSE)
		{
		rspSetMouse(MOUSE_RESET_X, MOUSE_RESET_Y);
		}

	// Clear the keys' statuses.
	memset(rspGetKeyStatusArray(), 0, 128);

	// Clear cheats.
	int16_t	i;
	for (i = 0; i < NUM_ELEMENTS(ms_acheats); i++)
		{
		ms_acheats[i].sCurrentIndex	= 0;
		}
	}

////////////////////////////////////////////////////////////////////////////////
//
// Look for cheat combos.
//
////////////////////////////////////////////////////////////////////////////////
static void FindCheatCombos(	// Returns nothing.
	UINPUT*	pinput,				// In:  Input to augment.
										// Out: Input with cheats.
	RInputEvent* pie)				// In:  Latest input event or NULL.
	{
	int32_t		lNow		= rspGetMilliseconds();
	int16_t	i;

	if (pie)
		{
		if (pie->type == RInputEvent::Key)
			{
			int32_t	lKey	= (pie->lKey & 0x00FF);
			// Force alpha lower keys to upper keys
			if (islower(lKey))
				lKey	= toupper(lKey);

			Cheat*	pcheat	= ms_acheats;
			for (i = 0; i < NUM_ELEMENTS(ms_acheats); i++, pcheat++)
				{
				// Been a while since last input?  We should/need to use rspGetMiliseconds() for
				// consistency.  This should offer no danger to synchronization as this is part
				// of the input structure which is transmitted to all machines in multiplayer and
				// loaded from disk in demos.
				if (pcheat->lLastValidInputTime + MAX_CONSEQ_CHEAT_KEY_LAG < lNow )
					{
					// Been too long -- Reset this cheat.
					pcheat->sCurrentIndex	= 0;
					}

				// Get current key.  Note that we detweak the strings so we 
				// can store the strings not as they are used to avoid these being
				// obvious when searching/viewing the exe.
				char	c = DETWEAK_CHAR(pcheat->szCheat, pcheat->sCurrentIndex);
				// If current key is hit . . .
				if ( lKey == (int32_t)c && c != 0)
					{
					// Remember time of this key.
					pcheat->lLastValidInputTime				= lNow;
					// Advance index.
					(pcheat->sCurrentIndex)++;
					// Was that the last char . . . ?
					if (pcheat->szCheat[pcheat->sCurrentIndex] == '\0')
						{
						// Set input.
						(*pinput)	|= pcheat->input;
						// Reset.
						pcheat->sCurrentIndex	= 0;
						}
					}
				else
					{
					// Does not match -- Reset this cheat.
					pcheat->sCurrentIndex	= 0;
					}
				}
			}
		}
	}

////////////////////////////////////////////////////////////////////////////////
// 
// Check to see if demo is over.  If this is not demo playback mode, then it
// will always return true.  If it is demo playback mode, then it will return
//	true if there is no more input.
//
////////////////////////////////////////////////////////////////////////////////

extern bool InputIsDemoOver(void)
{
	bool bOver = true;

	if (m_mode == INPUT_MODE_PLAYBACK && m_pBuf && m_lBufIndex < m_lBufEntries)
		bOver = false;

	return bOver;
}

// Simply returns TRUE or FALSE to say whether we should throw out a NextWeapon/PrevWeapon input or not.
bool CanCycleThroughWeapons()
{
#define WEAPON_SWITCH_HOLD_TIME 750
#define WEAPON_SWITCH_CYCLE_TIME 350
	static int32_t lLastWeaponSwitchTime = 0;
	static bool bFastWeaponSwitching = false;
	int32_t lCurTime = rspGetMilliseconds();
	bool bResult = false;

	if (lLastWeaponSwitchTime == 0)
		bResult = true;
	else if (bFastWeaponSwitching && lCurTime - lLastWeaponSwitchTime > WEAPON_SWITCH_HOLD_TIME)
	{
		bResult = true;
		bFastWeaponSwitching = false;
	}
	else if (bFastWeaponSwitching && lCurTime - lLastWeaponSwitchTime > WEAPON_SWITCH_CYCLE_TIME)
		bResult = true;
	else if (lCurTime - lLastWeaponSwitchTime > WEAPON_SWITCH_HOLD_TIME)
	{
		bResult = true;
		bFastWeaponSwitching = true;
	}
	else
		bResult = false;

	TRACE("CanCycleThroughWeapons Time %i %i %i Result %i", lCurTime, lLastWeaponSwitchTime, lCurTime - lLastWeaponSwitchTime, bResult);

	if (bResult)
		lLastWeaponSwitchTime = lCurTime;

	return bResult;
}

double m_ResModifierX = NULL;
double m_ResModifierY = NULL;

//Get the Modifier to Cram mouse pos in the resolution the game expects
void GetResModifer(
	double* modiferX,
	double* modiferY)
	{ 
		//Do this only for initialisation
		if (m_ResModifierX == NULL)
		{
			int screen_width = 640;
			int screen_height = 480;
			int16_t render_width = 640;
			int16_t render_height = 480;
			SDL_DisplayMode dm_Mode;

			//Returns 0 on success...
			int i_Result = SDL_GetCurrentDisplayMode(0, &dm_Mode);

			if (i_Result == 0) {
				screen_width = dm_Mode.w;
				screen_height = dm_Mode.h;
			}

			//Get rendered resolution (not alway 640x480)
			rspGetVideoMode(NULL, NULL, NULL, NULL, &render_width, &render_height, NULL, NULL);

			m_ResModifierX = (double)render_width / (double)screen_width;
			m_ResModifierY = (double)render_height / (double)screen_height;

			printf("ScreenRes: %i %i\n", screen_width, screen_height);
			printf("RenderRes: %i %i\n", render_width, render_height);
			printf("Modifier: %f %f\n", m_ResModifierX, m_ResModifierY);		
		}
		SET(modiferX, m_ResModifierX);
		SET(modiferY, m_ResModifierY);
		
	}

////////////////////////////////////////////////////////////////////////////////
//
// Get local input
//
////////////////////////////////////////////////////////////////////////////////
extern UINPUT GetLocalInput(				// Returns local input.
	CRealm* prealm,                         // In: Realm (used to access realm timer)
	CCamera* pcamera,                       // In: Camera (used for mouse input)
	U16 idLocalDude,						// In: Local dude id (used for mouse input)
	RInputEvent* pie	/*= NULL*/,			// In:  Latest input event.  NULL to
	bool isMP /*= false*/)						//	disable cheats in a way that will be
													// harder to hack.
													
	{
	// Default to nothing
	UINPUT input = 0;

	if (m_mode == INPUT_MODE_PLAYBACK)
		{

		// Get input from buffer
		if (m_pBuf && (m_lBufIndex < m_lBufEntries))
			input = m_pBuf[m_lBufIndex++];

		}
	else
		{
// Set this to 0 to disable all possibility of any user input during the game
#if 1
		int32_t				lCurTime			= prealm->m_time.GetGameTime();
		static int32_t		lPrevTime		= lCurTime;
		// Get ptr to Blue's key status array.  Only need to do this
		// once.
		static U8*		pu8KeyStatus	= rspGetKeyStatusArray();

		int16_t	sButtons	= 0;
		int16_t	sDeltaX	= 360;

		//Overrides twinstick keyboard controls
		// If utilizing mouse input . . .
		if (g_InputSettings.m_sUseNewMouse == FALSE && g_InputSettings.m_sUseMouse != FALSE && rspIsBackground() == FALSE)
		{
			int16_t	sPosX, sPosY;
			int16_t	sThreshY;
			rspGetMouse(&sPosX, &sPosY, &sButtons);
			rspSetMouse(MOUSE_RESET_X, MOUSE_RESET_Y);

			// Tweak input values.  We reduce the sensitivity by a factor of 3 to make
			// up for increased frame rates.
			double	dDeltaRot = (MOUSE_RESET_X - sPosX) * (g_InputSettings.m_dMouseSensitivityX / 3.0);
#if 1
			// If positive round up . . .
			if (dDeltaRot >= 0.0)
			{
				dDeltaRot += 0.5;
			}
			else	// Negative round down.
			{
				dDeltaRot -= 0.5;
			}
#endif
			//TRACE("sDif = %d, dDeltaRot = %g\n", (MOUSE_RESET_X - sPosX), dDeltaRot);

			// Must cast to short before subtracting b/c this statement is really:
			// sDeltaX = sDeltaX + dDeltaRot which became promoted to float before
			// it was added and then truncated causing a bias in degree toward
			// negative or rightward rotations.
			sDeltaX += (int16_t)dDeltaRot;

			sThreshY = MOUSE_Y_THRESH;
			if (g_InputSettings.m_dMouseSensitivityY > 0.0)
			{
				sThreshY = int16_t(float(sThreshY) / g_InputSettings.m_dMouseSensitivityY);
			}
			else
			{
				// Infiniti.
				sThreshY *= 100;
			}

			// If less than last time . . .
			if (sDeltaX < 360)
				input |= INPUT_RIGHT;
			else if (sDeltaX > 360)
				input |= INPUT_LEFT;

			if (sPosY < MOUSE_RESET_Y - sThreshY)
				input |= INPUT_FORWARD;
			else if (sPosY > MOUSE_RESET_Y + sThreshY)
				input |= INPUT_BACKWARD;
		}

		//New mouse input
		if (g_InputSettings.m_sUseNewMouse == TRUE && rspIsBackground() == FALSE) {

			CDude* pdude = NULL;

			//Actually get a local dude
			prealm->m_idbank.GetThingByID((CThing**)&pdude, idLocalDude);

			/*     Mouse impl      */
			double dudePosX = 0;
			double dudePosY = 0;

			int16_t mousePosX = 0;
			int16_t mousePosY = 0;

			rspGetMouse(&mousePosX, &mousePosY, &sButtons);

			//printf("Mouse Pos: %i %i\n", mousePosX, mousePosY);
			double modifierX = 0;
			double modifierY = 0;

			GetResModifer(&modifierX, &modifierY);

			//Cram mouse pos in 640x480 resolution which game expects
			double adjMousePosX = mousePosX * modifierX;
			double adjMousePosY = mousePosY * modifierY;

			//printf("Crammed Mouse Pos: %f %f\n", adjMousePosX, adjMousePosY);

			/*     Trying to do calculation in 'global'  3d     */

			double mouseX_3d = 0;
			double mouseY_3d = 0;
			double mouseZ_3d = 0;

			MapScreen2Realm(prealm, pcamera, adjMousePosX, adjMousePosY, &mouseX_3d, &mouseY_3d, &mouseZ_3d);

			dudePosX = pdude->m_dX;
			dudePosY = pdude->m_dZ;

			/*     Trying to do calculation in 'screen' 2d      */

			// Map coordinate onto 2D screen coords

			//Maprealm2Screen(m_pRealm, Camera(), m_dX, m_dY, m_dZ, &dudePosX, &dudePosY);


			double deltaX = mouseX_3d - dudePosX;  //In either screen coords or in global 3d coords
			double deltaY = dudePosY - mouseZ_3d;

			//For crosshair to scale in mouse pointer direction
			pdude->m_dMousePosX = mouseX_3d;
			pdude->m_dMousePosY = mouseZ_3d;

			//printf("Scale X: %.2f, Scale Y: %.2f\n", pdude->m_dCrossScaleX, pdude->m_dCrossScaleY);

			int16_t rotateToAngle = 0;
			int16_t rot = (int16_t)pdude->m_dRot; //Never a true double, because assigned from an int16_t in dude class

			//Account for game actually going ahead and 
			//collecting mutiple inputs without processing them
			if (isMP) {

				rot += pdude->m_sDeltaRot;  //Adjust local variable by previous delta 
				rspMod360(&rot);			//if we haven't processed input it's not going to change
				                            //Kinda psudo-process it
			}

			rotateToAngle = (int16_t)(atan2(deltaY, deltaX) * (180 / M_PI));
			if (rotateToAngle < 0) rotateToAngle += 360;

			/* Trying to make dude gradually rotate torwards the mouse pointer */
			int16_t rotStep = (int16_t)(g_InputSettings.m_dMouseSensitivityX * 10.0 * ((lCurTime - lPrevTime)/20.0)); //The constant is arbitrary and can be experimented on 
			int16_t deltaDiff = rotateToAngle - rot;

			if (abs(deltaDiff) > 180) {
				//Clockwise rotation should be negative
				if (deltaDiff < 0) {
					deltaDiff = (360 - rot) + rotateToAngle;
				}
				else if (deltaDiff > 0) {
					deltaDiff = (rotateToAngle - 360) - rot;
				}

			}

			//Direct rot assignment impl 
			//It won't work for mutiplayer until appropriate value is assigned to sDeltaX

			//if (abs(deltaDiff) < rotStep) {
			//	rot = rotateToAngle;
			//}
			//else if (deltaDiff > 0) {
			//	rot += rotStep;
			//}
			//else if (deltaDiff < 0) {
			//	rot -= rotStep;
			//}

			////Keep rotation from going negative 
			//if (rot < 0) 
			//	rot += 360;

			////Keep rotation from going over 360
			//if (rot >= 360)
			//	rot -= 360;

			//pdude->m_dRotateToAngle = rotateToAngle;
			//pdude->m_dRot = rot;

			//Using sDeltaX for rotation

			if (abs(deltaDiff) < rotStep) {
				sDeltaX += deltaDiff;
			}
			else if (deltaDiff > 0) {
				sDeltaX += rotStep;
			}
			else if (deltaDiff < 0) {
				sDeltaX -= rotStep;
			}
			
			pdude->m_sDeltaRot = sDeltaX - 360;

		}


#if defined(ALLOW_JOYSTICK)
		U32	u32Buttons	= 0;

		// If utilizing joystick input . . .
		if (g_InputSettings.m_sUseJoy)
			{
			// Only need to update joystick 1.
			rspUpdateJoy(0);

			U32	u32Axes	= 0;
			rspGetJoyState(0, &u32Buttons, &u32Axes);	

#if defined(ALLOW_TWINSTICK)
			//if ((u32Axes & RSP_JOY_Y_NEG) || (u32Axes & RSP_JOY_Y_POS) || (u32Axes & RSP_JOY_X_NEG) || (u32Axes & RSP_JOY_X_POS))
				//input |= INPUT_FORWARD;

				//All the new mouse stuff here
			/*if (g_InputSettings.m_sUseNewMouse != FALSE && rspIsBackground() == FALSE) {

			}*/
#else
			if (u32Axes & RSP_JOY_Y_NEG)
				{
				input	|= INPUT_FORWARD;
				}
			if (u32Axes & RSP_JOY_Y_POS)
				{
				input	|= INPUT_MOVE_DOWN;
				}
			if (u32Axes & RSP_JOY_X_NEG)
				{
				input	|= INPUT_LEFT;
				}
			if (u32Axes & RSP_JOY_X_POS)
				{
				input	|= INPUT_RIGHT;
				}
			if (u32Axes & RSP_JOY_Z_NEG)
				{
				input	|= (INPUT_STRAFE | INPUT_LEFT);
				}
			if (u32Axes & RSP_JOY_Z_POS)
				{
				input	|= (INPUT_STRAFE | INPUT_RIGHT);
				}
#endif	// ALLOW_TWINSTICK
			}
#endif	// defined(ALLOW_JOYSTICK)

		if (IS_INPUT(CInputSettings::Forward))
			input |= INPUT_FORWARD;

		if (IS_INPUT(CInputSettings::Backward))
			input |= INPUT_BACKWARD;

		// The run key function is toggled by the caps lock key.
		// If the caps lock is on, run is walk.
		if ( (rspGetToggleKeyStates() & RSP_CAPS_LOCK_ON) == 0)
			{
			if (IS_INPUT(CInputSettings::Run) || IS_INPUT(CInputSettings::Run2) )
				input |= INPUT_RUN;
			}
		else
			{
			if ( (IS_INPUT(CInputSettings::Run) == 0) && (IS_INPUT(CInputSettings::Run2) == 0) )
				input |= INPUT_RUN;
			}

		// 2015 update: have the run button function as a walk button instead
		input ^= INPUT_RUN;

		if (IS_INPUT(CInputSettings::Strafe) || IS_INPUT(CInputSettings::Strafe2) )
			input |= INPUT_STRAFE;

		if (IS_INPUT(CInputSettings::StrafeLeft))
			input |= INPUT_STRAFE_LEFT;

		if (IS_INPUT(CInputSettings::StrafeRight))
			input |= INPUT_STRAFE_RIGHT;

		if (IS_INPUT(CInputSettings::Left))
			{
			input	|= INPUT_LEFT;
			}

		if (IS_INPUT(CInputSettings::Right))
			{
			input	|= INPUT_RIGHT;
			}
		            
		if (input & INPUT_LEFT)
			{
			input |= INPUT_LEFT;
			// If last input had left rotation or this one has forward or reverse . . .
			if (	(ms_inputLastLocal & INPUT_LEFT)
				||	(input & INPUT_FORWARD)
				||	(input & INPUT_BACKWARD) )
				{
				// Adjust rotation.
				double	dRate	= 0.0;	// Initialized for safety only.
				// If fast specified . . .
				switch (input & (INPUT_RUN | INPUT_FORWARD | INPUT_BACKWARD) )
					{
					case (INPUT_RUN | INPUT_FORWARD):						// Forward fast.
					case (INPUT_RUN | INPUT_BACKWARD):						// Backward fast.
					case (INPUT_RUN | INPUT_FORWARD | INPUT_BACKWARD):	// Some fucked up fast case.
						dRate	= g_InputSettings.m_dMovingFastDegreesPerSec;
						break;
					case INPUT_FORWARD:						// Forward normal.
					case INPUT_BACKWARD:						// Backward normal.
					case (INPUT_FORWARD | INPUT_BACKWARD):	// Some fucked up normal case.
						dRate	= g_InputSettings.m_dMovingSlowDegreesPerSec;
						break;
					case INPUT_RUN:							// Fast turn only.
						dRate	= g_InputSettings.m_dStillFastDegreesPerSec;
						break;
					case 0:										// Normal turn only.
						dRate	= g_InputSettings.m_dStillSlowDegreesPerSec;
						break;
					default:
						TRACE("GetLocalInput(): Unhandled input case.\n");
						break;
					}

				sDeltaX	+= ((lCurTime - lPrevTime) * dRate) / 1000UL;
                //sDeltaX++;  // !!! FIXME: Not sure why this is needed. --ryan.
				}
			else
				{
				// Slight adjustment.
				sDeltaX	+= g_InputSettings.m_sTapRotationDegrees;
				}

			// Range check occurs later.
			}

		if (input & INPUT_RIGHT)
			{
			input |= INPUT_RIGHT;
			// If last input had right rotation or this one has forward or reverse . . .
			if (	(ms_inputLastLocal & INPUT_RIGHT)
				||	(input & INPUT_FORWARD)
				||	(input & INPUT_BACKWARD) )
				{
				// Adjust rotation.
				double	dRate	= 0.0;	// Initialized for safety only.
				// If fast specified . . .
				switch (input & (INPUT_RUN | INPUT_FORWARD | INPUT_BACKWARD) )
					{
					case (INPUT_RUN | INPUT_FORWARD):						// Forward fast.
					case (INPUT_RUN | INPUT_BACKWARD):						// Backward fast.
					case (INPUT_RUN | INPUT_FORWARD | INPUT_BACKWARD):	// Some fucked up fast case.
						dRate	= g_InputSettings.m_dMovingFastDegreesPerSec;
						break;
					case INPUT_FORWARD:						// Forward normal.
					case INPUT_BACKWARD:						// Backward normal.
					case (INPUT_FORWARD | INPUT_BACKWARD):	// Some fucked up normal case.
						dRate	= g_InputSettings.m_dMovingSlowDegreesPerSec;
						break;
					case INPUT_RUN:							// Fast turn only.
						dRate	= g_InputSettings.m_dStillFastDegreesPerSec;
						break;
					case 0:										// Normal turn only.
						dRate	= g_InputSettings.m_dStillSlowDegreesPerSec;
						break;
					default:
						TRACE("GetLocalInput(): Unhandled input case.\n");
						break;
					}

				sDeltaX	-= ((lCurTime - lPrevTime) * dRate) / 1000UL;
				}
			else
				{
				// Slight adjustment.
				sDeltaX	-= g_InputSettings.m_sTapRotationDegrees;
				}

			// Range check occurs later.
			}

		if (IS_INPUT(CInputSettings::Fire) || IS_INPUT(CInputSettings::Fire2) )
			{
			input |= INPUT_FIRE;
			}

		if (IS_INPUT(CInputSettings::Duck))
			{
			input	|= INPUT_DUCK;
			}

		if (IS_INPUT(CInputSettings::Up))
			{
			input	|= INPUT_MOVE_UP;
			}
		if (IS_INPUT(CInputSettings::Down))
			{
			input	|= INPUT_MOVE_DOWN;
			}
		if (IS_INPUT(CInputSettings::MoveLeft))
			{
			input	|= INPUT_MOVE_LEFT;
			}
		if (IS_INPUT(CInputSettings::MoveRight))
			{
			input	|= INPUT_MOVE_RIGHT;
			}
		if (IS_INPUT(CInputSettings::FireUp))
		{
			input |= INPUT_FIRE_UP;
		}
		if (IS_INPUT(CInputSettings::FireDown))
		{
			input |= INPUT_FIRE_DOWN;
		}
		if (IS_INPUT(CInputSettings::FireLeft))
		{
			input |= INPUT_FIRE_LEFT;
		}
		if (IS_INPUT(CInputSettings::FireRight))
		{
			input |= INPUT_FIRE_RIGHT;
		}

		if (WAS_INPUT(CInputSettings::Revive))
			{
			input |= INPUT_REVIVE;
			CLEAR_INPUT(CInputSettings::Revive);
			}

		// Find deluxe cheat combos.
		FindCheatCombos(&input, pie);

		if (WAS_INPUT(CInputSettings::Weapon0))
			{
			input |= INPUT_WEAPON_0;
			CLEAR_INPUT(CInputSettings::Weapon0);
			}
		else if (WAS_INPUT(CInputSettings::Weapon1))
			{
			input |= INPUT_WEAPON_1;
			CLEAR_INPUT(CInputSettings::Weapon1);
			}
		else if (WAS_INPUT(CInputSettings::Weapon2))
			{
			input |= INPUT_WEAPON_2;
			CLEAR_INPUT(CInputSettings::Weapon2);
			}
		else if (WAS_INPUT(CInputSettings::Weapon3))
			{
			input |= INPUT_WEAPON_3;
			CLEAR_INPUT(CInputSettings::Weapon3);
			}
		else if (WAS_INPUT(CInputSettings::Weapon4))
			{
			input |= INPUT_WEAPON_4;
			CLEAR_INPUT(CInputSettings::Weapon4);
			}
		else if (WAS_INPUT(CInputSettings::Weapon5))
			{
			input |= INPUT_WEAPON_5;
			CLEAR_INPUT(CInputSettings::Weapon5);
			}
		else if (WAS_INPUT(CInputSettings::Weapon6))
			{
			input |= INPUT_WEAPON_6;
			CLEAR_INPUT(CInputSettings::Weapon6);
			}
		else if (WAS_INPUT(CInputSettings::Weapon7))
			{
			input |= INPUT_WEAPON_7;
			CLEAR_INPUT(CInputSettings::Weapon7);
			}
		else if (WAS_INPUT(CInputSettings::Weapon8))
			{
			input |= INPUT_WEAPON_8;
			CLEAR_INPUT(CInputSettings::Weapon8);
			}
		else if (WAS_INPUT(CInputSettings::Weapon9))
			{
			input |= INPUT_WEAPON_9;
			CLEAR_INPUT(CInputSettings::Weapon9);
			}
		else if (WAS_INPUT(CInputSettings::Weapon10))
			{
			input |= INPUT_WEAPON_10;
			CLEAR_INPUT(CInputSettings::Weapon10);
			}
		else if (WAS_INPUT(CInputSettings::NextWeapon))
			{
			if (CanCycleThroughWeapons())
				input |= INPUT_WEAPON_NEXT;
			CLEAR_INPUT(CInputSettings::NextWeapon);
			}
		else if (WAS_INPUT(CInputSettings::PrevWeapon))
			{
			if (CanCycleThroughWeapons())
				input |= INPUT_WEAPON_PREV;
			CLEAR_INPUT(CInputSettings::PrevWeapon);
			}

		if (WAS_INPUT(CInputSettings::Execute))
			{
// It is not proper to execute people in non-violent countries.
#if VIOLENT_LOCALE
			input |= INPUT_EXECUTE;
#endif
			CLEAR_INPUT(CInputSettings::Execute);
			}

		if (WAS_INPUT(CInputSettings::Suicide))
			{
			input |= INPUT_SUICIDE;
			CLEAR_INPUT(CInputSettings::Suicide);
			}

		if (IS_INPUT(CInputSettings::NextMap))
		{
			prealm->m_bPressedEndLevelKey = true;
		}

		// Keep in range.
		if (sDeltaX > 720)
			sDeltaX	= 720;
		else if (sDeltaX < 0)
			sDeltaX	= 0;

		// Set only the 10 bits of the delta (as unsigned value).
		// As long as the above range check is used, we don't need
		// to & with the rotation mask.
		input		|=	(U32)sDeltaX;

#ifdef MOBILE
		input = AndroidGetInput();
#endif

		// Store time for next call.
		lPrevTime	= lCurTime;

		// Store input for next call.
		ms_inputLastLocal	= input;

		// If in record mode, save input to buffer
		if (m_mode == INPUT_MODE_RECORD)
			{
			if (m_pBuf && (m_lBufIndex < BUF_MAX_ENTRIES))
				{
				// If this is the last entry that will fit, then commit suicide
				if (m_lBufIndex == (BUF_MAX_ENTRIES - 1))
					input |= INPUT_SUICIDE;

				m_pBuf[m_lBufIndex++] = input;
				m_lBufEntries++;
				}
			else
				{
				}
			}
#endif
		}

	return input;
	}


////////////////////////////////////////////////////////////////////////////////
// EOF
////////////////////////////////////////////////////////////////////////////////