1 //Copyright Paul Reiche, Fred Ford. 1992-2002
2 
3 /*
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 
19 #include "controls.h"
20 #include "battlecontrols.h"
21 #include "init.h"
22 #include "intel.h"
23 		// For computer_intelligence
24 #ifdef NETPLAY
25 #	include "supermelee/netplay/netmelee.h"
26 #endif
27 #include "settings.h"
28 #include "sounds.h"
29 #include "tactrans.h"
30 #include "uqmdebug.h"
31 #include "libs/async.h"
32 #include "libs/inplib.h"
33 #include "libs/timelib.h"
34 #include "libs/threadlib.h"
35 
36 
37 #define ACCELERATION_INCREMENT (ONE_SECOND / 12)
38 #define MENU_REPEAT_DELAY (ONE_SECOND / 2)
39 
40 
41 typedef struct
42 {
43 	BOOLEAN (*InputFunc) (void *pInputState);
44 } INPUT_STATE_DESC;
45 
46 /* These static variables are the values that are set by the controllers. */
47 
48 typedef struct
49 {
50 	DWORD key [NUM_TEMPLATES][NUM_KEYS];
51 	DWORD menu [NUM_MENU_KEYS];
52 } MENU_ANNOTATIONS;
53 
54 
55 CONTROL_TEMPLATE PlayerControls[NUM_PLAYERS];
56 CONTROLLER_INPUT_STATE CurrentInputState, PulsedInputState;
57 static CONTROLLER_INPUT_STATE CachedInputState, OldInputState;
58 static MENU_ANNOTATIONS RepeatDelays, Times;
59 static DWORD GestaltRepeatDelay, GestaltTime;
60 static BOOLEAN OldGestalt, CachedGestalt;
61 static DWORD _max_accel, _min_accel, _step_accel;
62 static BOOLEAN _gestalt_keys;
63 
64 static MENU_SOUND_FLAGS sound_0, sound_1;
65 
66 volatile CONTROLLER_INPUT_STATE ImmediateInputState;
67 
68 volatile BOOLEAN ExitRequested;
69 volatile BOOLEAN GamePaused;
70 
71 static InputFrameCallback *inputCallback;
72 
73 static void
_clear_menu_state(void)74 _clear_menu_state (void)
75 {
76 	int i, j;
77 	for (i = 0; i < NUM_TEMPLATES; i++)
78 	{
79 		for (j = 0; j < NUM_KEYS; j++)
80 		{
81 			PulsedInputState.key[i][j] = 0;
82 			CachedInputState.key[i][j] = 0;
83 		}
84 	}
85 	for (i = 0; i < NUM_MENU_KEYS; i++)
86 	{
87 		PulsedInputState.menu[i] = 0;
88 		CachedInputState.menu[i] = 0;
89 	}
90 	CachedGestalt = FALSE;
91 }
92 
93 void
ResetKeyRepeat(void)94 ResetKeyRepeat (void)
95 {
96 	DWORD initTime = GetTimeCounter ();
97 	int i, j;
98 	for (i = 0; i < NUM_TEMPLATES; i++)
99 	{
100 		for (j = 0; j < NUM_KEYS; j++)
101 		{
102 			RepeatDelays.key[i][j] = _max_accel;
103 			Times.key[i][j] = initTime;
104 		}
105 	}
106 	for (i = 0; i < NUM_MENU_KEYS; i++)
107 	{
108 		RepeatDelays.menu[i] = _max_accel;
109 		Times.menu[i] = initTime;
110 	}
111 	GestaltRepeatDelay = _max_accel;
112 	GestaltTime = initTime;
113 }
114 
115 static void
_check_for_pulse(int * current,int * cached,int * old,DWORD * accel,DWORD * newtime,DWORD * oldtime)116 _check_for_pulse (int *current, int *cached, int *old, DWORD *accel,
117 		DWORD *newtime, DWORD *oldtime)
118 {
119 	if (*cached && *old)
120 	{
121 		if (*newtime - *oldtime < *accel)
122 		{
123 			*current = 0;
124 		}
125 		else
126 		{
127 			*current = *cached;
128 			if (*accel > _min_accel)
129 				*accel -= _step_accel;
130 			if (*accel < _min_accel)
131 				*accel = _min_accel;
132 			*oldtime = *newtime;
133 		}
134 	}
135 	else
136 	{
137 		*current = *cached;
138 		*oldtime = *newtime;
139 		*accel = _max_accel;
140 	}
141 }
142 
143 /* BUG: If a key from a currently unused control template is held,
144  * this will affect the gestalt repeat rate.  This isn't a problem
145  * *yet*, but it will be once the user gets to define control
146  * templates on his own --McM */
147 static void
_check_gestalt(DWORD NewTime)148 _check_gestalt (DWORD NewTime)
149 {
150 	BOOLEAN CurrentGestalt;
151 	int i, j;
152 	OldGestalt = CachedGestalt;
153 
154 	CachedGestalt = 0;
155 	CurrentGestalt = 0;
156 	for (i = 0; i < NUM_TEMPLATES; i++)
157 	{
158 		for (j = 0; j < NUM_KEYS; j++)
159 		{
160 			CachedGestalt |= ImmediateInputState.key[i][j];
161 			CurrentGestalt |= PulsedInputState.key[i][j];
162 		}
163 	}
164 	for (i = 0; i < NUM_MENU_KEYS; i++)
165 	{
166 		CachedGestalt |= ImmediateInputState.menu[i];
167 		CurrentGestalt |= PulsedInputState.menu[i];
168 	}
169 
170 	if (OldGestalt && CachedGestalt)
171 	{
172 		if (NewTime - GestaltTime < GestaltRepeatDelay)
173 		{
174 			for (i = 0; i < NUM_TEMPLATES; i++)
175 			{
176 				for (j = 0; j < NUM_KEYS; j++)
177 				{
178 					PulsedInputState.key[i][j] = 0;
179 				}
180 			}
181 			for (i = 0; i < NUM_MENU_KEYS; i++)
182 			{
183 				PulsedInputState.menu[i] = 0;
184 			}
185 		}
186 		else
187 		{
188 			for (i = 0; i < NUM_TEMPLATES; i++)
189 			{
190 				for (j = 0; j < NUM_KEYS; j++)
191 				{
192 					PulsedInputState.key[i][j] = CachedInputState.key[i][j];
193 				}
194 			}
195 			for (i = 0; i < NUM_MENU_KEYS; i++)
196 			{
197 				PulsedInputState.menu[i] = CachedInputState.menu[i];
198 			}
199 			if (GestaltRepeatDelay > _min_accel)
200 				GestaltRepeatDelay -= _step_accel;
201 			if (GestaltRepeatDelay < _min_accel)
202 				GestaltRepeatDelay = _min_accel;
203 			GestaltTime = NewTime;
204 		}
205 	}
206 	else
207 	{
208 		for (i = 0; i < NUM_TEMPLATES; i++)
209 		{
210 			for (j = 0; j < NUM_KEYS; j++)
211 			{
212 				PulsedInputState.key[i][j] = CachedInputState.key[i][j];
213 			}
214 		}
215 		for (i = 0; i < NUM_MENU_KEYS; i++)
216 		{
217 			PulsedInputState.menu[i] = CachedInputState.menu[i];
218 		}
219 		GestaltTime = NewTime;
220 		GestaltRepeatDelay = _max_accel;
221 	}
222 }
223 
224 void
UpdateInputState(void)225 UpdateInputState (void)
226 {
227 	DWORD NewTime;
228 	/* First, if the game is, in fact, paused, we stall until
229 	 * unpaused.  Every thread with control over game logic calls
230 	 * UpdateInputState routinely, so we handle pause and exit
231 	 * state updates here. */
232 
233 	// Automatically pause and enter low-activity state while inactive,
234 	// for example, window minimized.
235 	if (!GameActive)
236 		SleepGame ();
237 
238 	if (GamePaused)
239 		PauseGame ();
240 
241 	if (ExitRequested)
242 		ConfirmExit ();
243 
244 	CurrentInputState = ImmediateInputState;
245 	OldInputState = CachedInputState;
246 	CachedInputState = ImmediateInputState;
247 	BeginInputFrame ();
248 	NewTime = GetTimeCounter ();
249 	if (_gestalt_keys)
250 	{
251 		_check_gestalt (NewTime);
252 	}
253 	else
254 	{
255 		int i, j;
256 		for (i = 0; i < NUM_TEMPLATES; i++)
257 		{
258 			for (j = 0; j < NUM_KEYS; j++)
259 			{
260 				_check_for_pulse (&PulsedInputState.key[i][j],
261 						&CachedInputState.key[i][j], &OldInputState.key[i][j],
262 						&RepeatDelays.key[i][j], &NewTime, &Times.key[i][j]);
263 			}
264 		}
265 		for (i = 0; i < NUM_MENU_KEYS; i++)
266 		{
267 			_check_for_pulse (&PulsedInputState.menu[i],
268 					&CachedInputState.menu[i], &OldInputState.menu[i],
269 					&RepeatDelays.menu[i], &NewTime, &Times.menu[i]);
270 		}
271 	}
272 
273 	if (CurrentInputState.menu[KEY_PAUSE])
274 		GamePaused = TRUE;
275 
276 	if (CurrentInputState.menu[KEY_EXIT])
277 		ExitRequested = TRUE;
278 
279 #if defined(DEBUG) || defined(USE_DEBUG_KEY)
280 	if (PulsedInputState.menu[KEY_DEBUG])
281 		debugKeyPressedSynchronous ();
282 #endif
283 }
284 
285 InputFrameCallback *
SetInputCallback(InputFrameCallback * callback)286 SetInputCallback (InputFrameCallback *callback)
287 {
288 	InputFrameCallback *old = inputCallback;
289 
290 	// Replacing an existing callback with another is not a problem,
291 	// but currently this should never happen, which is why the assert.
292 	assert (!old || !callback);
293 	inputCallback = callback;
294 
295 	return old;
296 }
297 
298 void
SetMenuRepeatDelay(DWORD min,DWORD max,DWORD step,BOOLEAN gestalt)299 SetMenuRepeatDelay (DWORD min, DWORD max, DWORD step, BOOLEAN gestalt)
300 {
301 	_min_accel = min;
302 	_max_accel = max;
303 	_step_accel = step;
304 	_gestalt_keys = gestalt;
305 	//_clear_menu_state ();
306 	ResetKeyRepeat ();
307 }
308 
309 void
SetDefaultMenuRepeatDelay(void)310 SetDefaultMenuRepeatDelay (void)
311 {
312 	_min_accel = ACCELERATION_INCREMENT;
313 	_max_accel = MENU_REPEAT_DELAY;
314 	_step_accel = ACCELERATION_INCREMENT;
315 	_gestalt_keys = FALSE;
316 	//_clear_menu_state ();
317 	ResetKeyRepeat ();
318 }
319 
320 void
FlushInput(void)321 FlushInput (void)
322 {
323 	TFB_ResetControls ();
324 	_clear_menu_state ();
325 }
326 
327 static MENU_SOUND_FLAGS
MenuKeysToSoundFlags(const CONTROLLER_INPUT_STATE * state)328 MenuKeysToSoundFlags (const CONTROLLER_INPUT_STATE *state)
329 {
330 	MENU_SOUND_FLAGS soundFlags;
331 
332 	soundFlags = MENU_SOUND_NONE;
333 	if (state->menu[KEY_MENU_UP])
334 		soundFlags |= MENU_SOUND_UP;
335 	if (state->menu[KEY_MENU_DOWN])
336 		soundFlags |= MENU_SOUND_DOWN;
337 	if (state->menu[KEY_MENU_LEFT])
338 		soundFlags |= MENU_SOUND_LEFT;
339 	if (state->menu[KEY_MENU_RIGHT])
340 		soundFlags |= MENU_SOUND_RIGHT;
341 	if (state->menu[KEY_MENU_SELECT])
342 		soundFlags |= MENU_SOUND_SELECT;
343 	if (state->menu[KEY_MENU_CANCEL])
344 		soundFlags |= MENU_SOUND_CANCEL;
345 	if (state->menu[KEY_MENU_SPECIAL])
346 		soundFlags |= MENU_SOUND_SPECIAL;
347 	if (state->menu[KEY_MENU_PAGE_UP])
348 		soundFlags |= MENU_SOUND_PAGEUP;
349 	if (state->menu[KEY_MENU_PAGE_DOWN])
350 		soundFlags |= MENU_SOUND_PAGEDOWN;
351 	if (state->menu[KEY_MENU_DELETE])
352 		soundFlags |= MENU_SOUND_DELETE;
353 	if (state->menu[KEY_MENU_BACKSPACE])
354 		soundFlags |= MENU_SOUND_DELETE;
355 
356 	return soundFlags;
357 }
358 
359 void
DoInput(void * pInputState,BOOLEAN resetInput)360 DoInput (void *pInputState, BOOLEAN resetInput)
361 {
362 	if (resetInput)
363 		FlushInput ();
364 
365 	do
366 	{
367 		MENU_SOUND_FLAGS soundFlags;
368 		Async_process ();
369 		TaskSwitch ();
370 
371 		UpdateInputState ();
372 
373 #if DEMO_MODE || CREATE_JOURNAL
374 		if (ArrowInput != DemoInput)
375 #endif
376 		{
377 #if CREATE_JOURNAL
378 			JournalInput (InputState);
379 #endif /* CREATE_JOURNAL */
380 		}
381 
382 		soundFlags = MenuKeysToSoundFlags (&PulsedInputState);
383 
384 		if (MenuSounds && (soundFlags & (sound_0 | sound_1)))
385 		{
386 			SOUND S;
387 
388 			S = MenuSounds;
389 			if (soundFlags & sound_1)
390 				S = SetAbsSoundIndex (S, MENU_SOUND_SUCCESS);
391 
392 			PlaySoundEffect (S, 0, NotPositional (), NULL, 0);
393 		}
394 
395 		if (inputCallback)
396 			inputCallback ();
397 
398 	} while (((INPUT_STATE_DESC*)pInputState)->InputFunc (pInputState));
399 
400 	if (resetInput)
401 		FlushInput ();
402 }
403 
404 void
SetMenuSounds(MENU_SOUND_FLAGS s0,MENU_SOUND_FLAGS s1)405 SetMenuSounds (MENU_SOUND_FLAGS s0, MENU_SOUND_FLAGS s1)
406 {
407 	sound_0 = s0;
408 	sound_1 = s1;
409 }
410 
411 void
GetMenuSounds(MENU_SOUND_FLAGS * s0,MENU_SOUND_FLAGS * s1)412 GetMenuSounds (MENU_SOUND_FLAGS *s0, MENU_SOUND_FLAGS *s1)
413 {
414 	*s0 = sound_0;
415 	*s1 = sound_1;
416 }
417 
418 static BATTLE_INPUT_STATE
ControlInputToBattleInput(const int * keyState)419 ControlInputToBattleInput (const int *keyState)
420 {
421 	BATTLE_INPUT_STATE InputState = 0;
422 
423 	if (keyState[KEY_UP])
424 		InputState |= BATTLE_THRUST;
425 	if (keyState[KEY_LEFT])
426 		InputState |= BATTLE_LEFT;
427 	if (keyState[KEY_RIGHT])
428 		InputState |= BATTLE_RIGHT;
429 	if (keyState[KEY_WEAPON])
430 		InputState |= BATTLE_WEAPON;
431 	if (keyState[KEY_SPECIAL])
432 		InputState |= BATTLE_SPECIAL;
433 	if (keyState[KEY_ESCAPE])
434 		InputState |= BATTLE_ESCAPE;
435 	if (keyState[KEY_DOWN])
436 		InputState |= BATTLE_DOWN;
437 
438 	return InputState;
439 }
440 
441 BATTLE_INPUT_STATE
CurrentInputToBattleInput(COUNT player)442 CurrentInputToBattleInput (COUNT player)
443 {
444 	return ControlInputToBattleInput(
445 			CurrentInputState.key[PlayerControls[player]]);
446 }
447 
448 BATTLE_INPUT_STATE
PulsedInputToBattleInput(COUNT player)449 PulsedInputToBattleInput (COUNT player)
450 {
451 	return ControlInputToBattleInput(
452 			PulsedInputState.key[PlayerControls[player]]);
453 }
454 
455 BOOLEAN
AnyButtonPress(BOOLEAN CheckSpecial)456 AnyButtonPress (BOOLEAN CheckSpecial)
457 {
458 	int i, j;
459 	(void) CheckSpecial;   // Ignored
460 	UpdateInputState ();
461 	for (i = 0; i < NUM_TEMPLATES; i++)
462 	{
463 		for (j = 0; j < NUM_KEYS; j++)
464 		{
465 			if (CurrentInputState.key[i][j])
466 				return TRUE;
467 		}
468 	}
469 	for (i = 0; i < NUM_MENU_KEYS; i++)
470 	{
471 		if (CurrentInputState.menu[i])
472 			return TRUE;
473 	}
474 	return FALSE;
475 }
476 
477 BOOLEAN
ConfirmExit(void)478 ConfirmExit (void)
479 {
480 	DWORD old_max_accel, old_min_accel, old_step_accel;
481 	BOOLEAN old_gestalt_keys, result;
482 
483 	old_max_accel = _max_accel;
484 	old_min_accel = _min_accel;
485 	old_step_accel = _step_accel;
486 	old_gestalt_keys = _gestalt_keys;
487 
488 	SetDefaultMenuRepeatDelay ();
489 
490 	result = DoConfirmExit ();
491 
492 	SetMenuRepeatDelay (old_min_accel, old_max_accel, old_step_accel,
493 			old_gestalt_keys);
494 	return result;
495 }
496 
497