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