1 //-------------------------------------------------------------------------
2 /*
3 Copyright (C) 2010-2019 EDuke32 developers and contributors
4 Copyright (C) 2019 Nuke.YKT
5
6 This file is part of NBlood.
7
8 NBlood is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License version 2
10 as published by the Free Software Foundation.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15
16 See the GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 */
22 //-------------------------------------------------------------------------
23 #include "compat.h"
24 #include "baselayer.h"
25 #include "keyboard.h"
26 #include "mouse.h"
27 #include "joystick.h"
28 #include "control.h"
29 #include "function.h"
30 #include "common_game.h"
31 #include "blood.h"
32 #include "config.h"
33 #include "controls.h"
34 #include "globals.h"
35 #include "levels.h"
36 #include "map2d.h"
37 #include "view.h"
38
39
ctrlCheckAllInput(void)40 int32_t ctrlCheckAllInput(void)
41 {
42 return (
43 KB_KeyWaiting() ||
44 MOUSE_GetButtons() ||
45 JOYSTICK_GetButtons()
46 );
47 }
48
ctrlClearAllInput(void)49 void ctrlClearAllInput(void)
50 {
51 KB_FlushKeyboardQueue();
52 KB_ClearKeysDown();
53 MOUSE_ClearAllButtons();
54 JOYSTICK_ClearAllButtons();
55 }
56
57 GINPUT gInput, gNetInput;
58 bool bSilentAim = false;
59
GetTime(void)60 int32_t GetTime(void)
61 {
62 return (int32_t)totalclock;
63 }
64
ctrlInit(void)65 void ctrlInit(void)
66 {
67 KB_ClearKeysDown();
68 KB_FlushKeyboardQueue();
69 KB_FlushKeyboardQueueScans();
70 CONTROL_Startup(controltype_keyboardandmouse, &GetTime, 120);
71 CONFIG_SetupMouse();
72 CONFIG_SetupJoystick();
73
74 CONTROL_JoystickEnabled = (gSetup.usejoystick && CONTROL_JoyPresent);
75 CONTROL_MouseEnabled = (gSetup.usemouse && CONTROL_MousePresent);
76
77 // JBF 20040215: evil and nasty place to do this, but joysticks are evil and nasty too
78 for (int i = 0; i < joystick.numAxes; i++)
79 joySetDeadZone(i, JoystickAnalogueDead[i], JoystickAnalogueSaturate[i]);
80 CONTROL_DefineFlag(gamefunc_Move_Forward, false);
81 CONTROL_DefineFlag(gamefunc_Move_Backward, false);
82 CONTROL_DefineFlag(gamefunc_Turn_Left, false);
83 CONTROL_DefineFlag(gamefunc_Turn_Right, false);
84 CONTROL_DefineFlag(gamefunc_Turn_Around, false);
85 CONTROL_DefineFlag(gamefunc_Strafe, false);
86 CONTROL_DefineFlag(gamefunc_Strafe_Left, false);
87 CONTROL_DefineFlag(gamefunc_Strafe_Right, false);
88 CONTROL_DefineFlag(gamefunc_Jump, false);
89 CONTROL_DefineFlag(gamefunc_Crouch, false);
90 CONTROL_DefineFlag(gamefunc_Run, false);
91 CONTROL_DefineFlag(gamefunc_AutoRun, false);
92 CONTROL_DefineFlag(gamefunc_Open, false);
93 CONTROL_DefineFlag(gamefunc_Weapon_Fire, false);
94 CONTROL_DefineFlag(gamefunc_Weapon_Special_Fire, false);
95 CONTROL_DefineFlag(gamefunc_Aim_Up, false);
96 CONTROL_DefineFlag(gamefunc_Aim_Down, false);
97 CONTROL_DefineFlag(gamefunc_Aim_Center, false);
98 CONTROL_DefineFlag(gamefunc_Look_Up, false);
99 CONTROL_DefineFlag(gamefunc_Look_Down, false);
100 CONTROL_DefineFlag(gamefunc_Tilt_Left, false);
101 CONTROL_DefineFlag(gamefunc_Tilt_Right, false);
102 CONTROL_DefineFlag(gamefunc_Weapon_1, false);
103 CONTROL_DefineFlag(gamefunc_Weapon_2, false);
104 CONTROL_DefineFlag(gamefunc_Weapon_3, false);
105 CONTROL_DefineFlag(gamefunc_Weapon_4, false);
106 CONTROL_DefineFlag(gamefunc_Weapon_5, false);
107 CONTROL_DefineFlag(gamefunc_Weapon_6, false);
108 CONTROL_DefineFlag(gamefunc_Weapon_7, false);
109 CONTROL_DefineFlag(gamefunc_Weapon_8, false);
110 CONTROL_DefineFlag(gamefunc_Weapon_9, false);
111 CONTROL_DefineFlag(gamefunc_Weapon_10, false);
112 CONTROL_DefineFlag(gamefunc_Inventory_Use, false);
113 CONTROL_DefineFlag(gamefunc_Inventory_Left, false);
114 CONTROL_DefineFlag(gamefunc_Inventory_Right, false);
115 CONTROL_DefineFlag(gamefunc_Map_Toggle, false);
116 CONTROL_DefineFlag(gamefunc_Map_Follow_Mode, false);
117 CONTROL_DefineFlag(gamefunc_Shrink_Screen, false);
118 CONTROL_DefineFlag(gamefunc_Enlarge_Screen, false);
119 CONTROL_DefineFlag(gamefunc_Send_Message, false);
120 CONTROL_DefineFlag(gamefunc_See_Coop_View, false);
121 CONTROL_DefineFlag(gamefunc_See_Chase_View, false);
122 CONTROL_DefineFlag(gamefunc_Mouse_Aiming, false);
123 CONTROL_DefineFlag(gamefunc_Toggle_Crosshair, false);
124 CONTROL_DefineFlag(gamefunc_Next_Weapon, false);
125 CONTROL_DefineFlag(gamefunc_Previous_Weapon, false);
126 CONTROL_DefineFlag(gamefunc_Holster_Weapon, false);
127 CONTROL_DefineFlag(gamefunc_Show_Opponents_Weapon, false);
128 CONTROL_DefineFlag(gamefunc_BeastVision, false);
129 CONTROL_DefineFlag(gamefunc_CrystalBall, false);
130 CONTROL_DefineFlag(gamefunc_JumpBoots, false);
131 CONTROL_DefineFlag(gamefunc_MedKit, false);
132 CONTROL_DefineFlag(gamefunc_ProximityBombs, false);
133 CONTROL_DefineFlag(gamefunc_RemoteBombs, false);
134 }
135
ctrlTerm(void)136 void ctrlTerm(void)
137 {
138 CONTROL_Shutdown();
139 }
140
141 int32_t mouseyaxismode = -1;
142
143 fix16_t gViewLook, gViewAngle;
144 float gViewAngleAdjust;
145 float gViewLookAdjust;
146 int gViewLookRecenter;
147
ctrlGetInput(void)148 void ctrlGetInput(void)
149 {
150 ControlInfo info;
151
152 if (!gGameStarted || gInputMode != INPUT_MODE_0)
153 {
154 gInput = {};
155 CONTROL_GetInput(&info);
156 return;
157 }
158
159 GINPUT input = {};
160 static double lastInputTicks;
161 auto const currentHiTicks = timerGetHiTicks();
162 double const elapsedInputTicks = currentHiTicks - lastInputTicks;
163
164 lastInputTicks = currentHiTicks;
165
166 auto scaleAdjustmentToInterval = [=](double x) { return x * kTicsPerSec / (1000.0 / elapsedInputTicks); };
167
168 CONTROL_ProcessBinds();
169
170 if (gMouseAiming)
171 gMouseAim = 0;
172
173 if (BUTTON(gamefunc_Mouse_Aiming))
174 {
175 if (gMouseAiming)
176 gMouseAim = 1;
177 else
178 {
179 CONTROL_ClearButton(gamefunc_Mouse_Aiming);
180 gMouseAim = !gMouseAim;
181 if (gMouseAim)
182 {
183 if (!bSilentAim)
184 viewSetMessage("Mouse aiming ON");
185 }
186 else
187 {
188 if (!bSilentAim)
189 viewSetMessage("Mouse aiming OFF");
190 gInput.keyFlags.lookCenter = 1;
191 }
192 }
193 }
194 else if (gMouseAiming)
195 gInput.keyFlags.lookCenter = 1;
196
197 CONTROL_GetInput(&info);
198
199 if (MouseDeadZone)
200 {
201 if (info.mousey > 0)
202 info.mousey = max(info.mousey - MouseDeadZone, 0);
203 else if (info.mousey < 0)
204 info.mousey = min(info.mousey + MouseDeadZone, 0);
205
206 if (info.mousex > 0)
207 info.mousex = max(info.mousex - MouseDeadZone, 0);
208 else if (info.mousex < 0)
209 info.mousex = min(info.mousex + MouseDeadZone, 0);
210 }
211
212 if (MouseBias)
213 {
214 if (klabs(info.mousex) > klabs(info.mousey))
215 info.mousey = tabledivide32_noinline(info.mousey, MouseBias);
216 else
217 info.mousex = tabledivide32_noinline(info.mousex, MouseBias);
218 }
219
220 if (gQuitRequest)
221 gInput.keyFlags.quit = 1;
222
223 if (gGameStarted && gInputMode != INPUT_MODE_2 && gInputMode != INPUT_MODE_1
224 && BUTTON(gamefunc_Send_Message))
225 {
226 CONTROL_ClearButton(gamefunc_Send_Message);
227 keyFlushScans();
228 gInputMode = INPUT_MODE_2;
229 }
230
231 if (BUTTON(gamefunc_AutoRun))
232 {
233 CONTROL_ClearButton(gamefunc_AutoRun);
234 gAutoRun = !gAutoRun;
235 if (gAutoRun)
236 viewSetMessage("Auto run ON");
237 else
238 viewSetMessage("Auto run OFF");
239 }
240
241 if (BUTTON(gamefunc_Map_Toggle))
242 {
243 CONTROL_ClearButton(gamefunc_Map_Toggle);
244 viewToggle(gViewMode);
245 }
246
247 if (BUTTON(gamefunc_Map_Follow_Mode))
248 {
249 CONTROL_ClearButton(gamefunc_Map_Follow_Mode);
250 gFollowMap = !gFollowMap;
251 gViewMap.FollowMode(gFollowMap);
252 }
253
254 if (BUTTON(gamefunc_Shrink_Screen))
255 {
256 if (gViewMode == 3)
257 {
258 CONTROL_ClearButton(gamefunc_Shrink_Screen);
259 viewResizeView(gViewSize + 1);
260 }
261 if (gViewMode == 2 || gViewMode == 4)
262 {
263 gZoom = ClipLow(gZoom - (gZoom >> 4), 64);
264 gViewMap.nZoom = gZoom;
265 }
266 }
267
268 if (BUTTON(gamefunc_Enlarge_Screen))
269 {
270 if (gViewMode == 3)
271 {
272 CONTROL_ClearButton(gamefunc_Enlarge_Screen);
273 viewResizeView(gViewSize - 1);
274 }
275 if (gViewMode == 2 || gViewMode == 4)
276 {
277 gZoom = ClipHigh(gZoom + (gZoom >> 4), 4096);
278 gViewMap.nZoom = gZoom;
279 }
280 }
281
282 if (BUTTON(gamefunc_Toggle_Crosshair))
283 {
284 CONTROL_ClearButton(gamefunc_Toggle_Crosshair);
285 gAimReticle = !gAimReticle;
286 }
287
288 if (BUTTON(gamefunc_Next_Weapon))
289 {
290 CONTROL_ClearButton(gamefunc_Next_Weapon);
291 gInput.keyFlags.nextWeapon = 1;
292 }
293
294 if (BUTTON(gamefunc_Previous_Weapon))
295 {
296 CONTROL_ClearButton(gamefunc_Previous_Weapon);
297 gInput.keyFlags.prevWeapon = 1;
298 }
299
300 if (BUTTON(gamefunc_Show_Opponents_Weapon))
301 {
302 CONTROL_ClearButton(gamefunc_Show_Opponents_Weapon);
303 gShowWeapon = (gShowWeapon+1)%3;
304 }
305
306 if (BUTTON(gamefunc_Jump))
307 gInput.buttonFlags.jump = 1;
308
309 if (BUTTON(gamefunc_Crouch))
310 gInput.buttonFlags.crouch = 1;
311
312 if (BUTTON(gamefunc_Weapon_Fire))
313 gInput.buttonFlags.shoot = 1;
314
315 if (BUTTON(gamefunc_Weapon_Special_Fire))
316 gInput.buttonFlags.shoot2 = 1;
317
318 if (BUTTON(gamefunc_Open))
319 {
320 CONTROL_ClearButton(gamefunc_Open);
321 gInput.keyFlags.action = 1;
322 }
323
324 gInput.buttonFlags.lookUp |= BUTTON(gamefunc_Look_Up);
325 gInput.buttonFlags.lookDown |= BUTTON(gamefunc_Look_Down);
326
327 if (BUTTON(gamefunc_Look_Up) || BUTTON(gamefunc_Look_Down))
328 gInput.keyFlags.lookCenter = 1;
329 else
330 {
331 gInput.buttonFlags.lookUp |= BUTTON(gamefunc_Aim_Up);
332 gInput.buttonFlags.lookDown |= BUTTON(gamefunc_Aim_Down);
333 }
334
335 if (BUTTON(gamefunc_Aim_Center))
336 {
337 CONTROL_ClearButton(gamefunc_Aim_Center);
338 gInput.keyFlags.lookCenter = 1;
339 }
340
341 gInput.keyFlags.spin180 |= BUTTON(gamefunc_Turn_Around);
342
343 if (BUTTON(gamefunc_Inventory_Left))
344 {
345 CONTROL_ClearButton(gamefunc_Inventory_Left);
346 gInput.keyFlags.prevItem = 1;
347 }
348
349 if (BUTTON(gamefunc_Inventory_Right))
350 {
351 CONTROL_ClearButton(gamefunc_Inventory_Right);
352 gInput.keyFlags.nextItem = 1;
353 }
354
355 if (BUTTON(gamefunc_Inventory_Use))
356 {
357 CONTROL_ClearButton(gamefunc_Inventory_Use);
358 gInput.keyFlags.useItem = 1;
359 }
360
361 if (BUTTON(gamefunc_BeastVision))
362 {
363 CONTROL_ClearButton(gamefunc_BeastVision);
364 gInput.useFlags.useBeastVision = 1;
365 }
366
367 if (BUTTON(gamefunc_CrystalBall))
368 {
369 CONTROL_ClearButton(gamefunc_CrystalBall);
370 gInput.useFlags.useCrystalBall = 1;
371 }
372
373 if (BUTTON(gamefunc_JumpBoots))
374 {
375 CONTROL_ClearButton(gamefunc_JumpBoots);
376 gInput.useFlags.useJumpBoots = 1;
377 }
378
379 if (BUTTON(gamefunc_MedKit))
380 {
381 CONTROL_ClearButton(gamefunc_MedKit);
382 gInput.useFlags.useMedKit = 1;
383 }
384
385 for (int i = 0; i < 10; i++)
386 {
387 if (BUTTON(gamefunc_Weapon_1 + i))
388 {
389 CONTROL_ClearButton(gamefunc_Weapon_1 + i);
390 gInput.newWeapon = 1 + i;
391 }
392 }
393
394 if (BUTTON(gamefunc_ProximityBombs))
395 {
396 CONTROL_ClearButton(gamefunc_ProximityBombs);
397 gInput.newWeapon = 11;
398 }
399
400 if (BUTTON(gamefunc_RemoteBombs))
401 {
402 CONTROL_ClearButton(gamefunc_RemoteBombs);
403 gInput.newWeapon = 12;
404 }
405
406 if (BUTTON(gamefunc_Holster_Weapon))
407 {
408 CONTROL_ClearButton(gamefunc_Holster_Weapon);
409 gInput.keyFlags.holsterWeapon = 1;
410 }
411
412 int const run = gRunKeyMode ? (BUTTON(gamefunc_Run) | gAutoRun) : (BUTTON(gamefunc_Run) ^ gAutoRun);
413 int const run2 = BUTTON(gamefunc_Run);
414 int const keyMove = (1 + run) << 10;
415
416 gInput.syncFlags.run |= run;
417
418 if (gInput.forward < keyMove && gInput.forward > -keyMove)
419 {
420 if (BUTTON(gamefunc_Move_Forward))
421 gInput.forward += keyMove;
422
423 if (BUTTON(gamefunc_Move_Backward))
424 gInput.forward -= keyMove;
425 }
426
427 if (gInput.strafe < keyMove && gInput.strafe > -keyMove)
428 {
429 if (BUTTON(gamefunc_Strafe_Left))
430 input.strafe += keyMove;
431 if (BUTTON(gamefunc_Strafe_Right))
432 input.strafe -= keyMove;
433 }
434
435
436 char turnLeft = 0, turnRight = 0;
437
438 if (BUTTON(gamefunc_Strafe))
439 {
440 if (gInput.strafe < keyMove && gInput.strafe > -keyMove)
441 {
442 if (BUTTON(gamefunc_Turn_Left))
443 input.strafe += keyMove;
444 if (BUTTON(gamefunc_Turn_Right))
445 input.strafe -= keyMove;
446 }
447 }
448 else
449 {
450 if (BUTTON(gamefunc_Turn_Left))
451 turnLeft = 1;
452 if (BUTTON(gamefunc_Turn_Right))
453 turnRight = 1;
454 }
455
456 static int32_t turnHeldTime;
457 static int32_t lastInputClock; // MED
458 int32_t const elapsedTics = (int32_t)totalclock - lastInputClock;
459
460 lastInputClock = (int32_t) totalclock;
461
462 if (turnLeft || turnRight)
463 turnHeldTime += elapsedTics;
464 else
465 turnHeldTime = 0;
466
467 if (turnLeft)
468 input.q16turn = fix16_ssub(input.q16turn, fix16_from_float(scaleAdjustmentToInterval(ClipHigh(12 * turnHeldTime, gTurnSpeed)>>2)));
469 if (turnRight)
470 input.q16turn = fix16_sadd(input.q16turn, fix16_from_float(scaleAdjustmentToInterval(ClipHigh(12 * turnHeldTime, gTurnSpeed)>>2)));
471
472 if ((run2 || run) && turnHeldTime > 24)
473 input.q16turn <<= 1;
474
475 if (BUTTON(gamefunc_Strafe))
476 input.strafe -= info.mousex;
477 else
478 input.q16turn = fix16_sadd(input.q16turn, fix16_sdiv(fix16_from_int(info.mousex), F16(32)));
479
480 input.strafe -= -(info.dx<<5);
481
482 #if 0
483 if (info.dz < 0)
484 gInput.mlook = ClipRange((info.dz+127)>>7, -127, 127);
485 else
486 gInput.mlook = ClipRange(info.dz>>7, -127, 127);
487 #endif
488 if (gMouseAim)
489 input.q16mlook = fix16_sadd(input.q16mlook, fix16_sdiv(fix16_from_int(info.mousey), F16(128)));
490 else
491 input.forward -= info.mousey;
492 if (!gMouseAimingFlipped)
493 input.q16mlook = -input.q16mlook;
494
495 if (KB_KeyPressed(sc_Pause)) // 0xc5 in disassembly
496 {
497 gInput.keyFlags.pause = 1;
498 KB_ClearKeyDown(sc_Pause);
499 }
500
501 if (!gViewMap.bFollowMode && gViewMode == 4)
502 {
503 gViewMap.turn += input.q16turn<<2;
504 gViewMap.forward += gMouseAim ? gInput.forward : clamp(gInput.forward + fix16_sadd(input.forward, fix16_sdiv(fix16_from_int(-info.mousey), F16(8192))), -2048, 2048);
505 gViewMap.strafe += input.strafe;
506 input.q16turn = 0;
507 input.forward = 0;
508 input.strafe = 0;
509 }
510 gInput.forward = clamp(gInput.forward + input.forward, -2048, 2048);
511 gInput.strafe = clamp(gInput.strafe + input.strafe, -2048, 2048);
512 gInput.q16turn = fix16_sadd(gInput.q16turn, input.q16turn);
513 gInput.q16mlook = fix16_clamp(fix16_sadd(gInput.q16mlook, input.q16mlook), F16(-127)>>2, F16(127)>>2);
514 if (gMe && gMe->pXSprite->health != 0 && !gPaused)
515 {
516 CONSTEXPR int upAngle = 289;
517 CONSTEXPR int downAngle = -347;
518 CONSTEXPR double lookStepUp = 4.0*upAngle/60.0;
519 CONSTEXPR double lookStepDown = -4.0*downAngle/60.0;
520 gViewAngle = (gViewAngle + input.q16turn + fix16_from_float(scaleAdjustmentToInterval(gViewAngleAdjust))) & 0x7ffffff;
521 if (gViewLookRecenter)
522 {
523 if (gViewLook < 0)
524 gViewLook = fix16_min(gViewLook+fix16_from_float(scaleAdjustmentToInterval(lookStepDown)), F16(0));
525 if (gViewLook > 0)
526 gViewLook = fix16_max(gViewLook-fix16_from_float(scaleAdjustmentToInterval(lookStepUp)), F16(0));
527 }
528 else
529 {
530 gViewLook = fix16_clamp(gViewLook+fix16_from_float(scaleAdjustmentToInterval(gViewLookAdjust)), F16(downAngle), F16(upAngle));
531 }
532 gViewLook = fix16_clamp(gViewLook+(input.q16mlook << 3), F16(downAngle), F16(upAngle));
533 }
534 }
535