1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (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.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 */
20 // cl.input.c -- builds an intended movement command to send to the server
21
22 // Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. All
23 // rights reserved.
24
25 #include "quakedef.h"
26
27 /*
28 ===============================================================================
29
30 KEY BUTTONS
31
32 Continuous button event tracking is complicated by the fact that two different
33 input sources (say, mouse button 1 and the control key) can both press the
34 same button, but the button should only be released when both of the
35 pressing key have been released.
36
37 When a key event issues a button command (+forward, +attack, etc), it appends
38 its key number as a parameter to the command so it can be matched up with
39 the release.
40
41 state bit 0 is the current state of the key
42 state bit 1 is edge triggered on the up to down transition
43 state bit 2 is edge triggered on the down to up transition
44
45 ===============================================================================
46 */
47
48
49 kbutton_t in_mlook, in_klook;
50 kbutton_t in_left, in_right, in_forward, in_back;
51 kbutton_t in_lookup, in_lookdown, in_moveleft, in_moveright;
52 kbutton_t in_strafe, in_speed, in_use, in_jump, in_attack;
53 kbutton_t in_up, in_down;
54
55 int in_impulse;
56
57
KeyDown(kbutton_t * b)58 void KeyDown (kbutton_t *b)
59 {
60 int k;
61 const char *c;
62
63 c = Cmd_Argv(1);
64 if (c[0])
65 k = atoi(c);
66 else
67 k = -1; // typed manually at the console for continuous down
68
69 if (k == b->down[0] || k == b->down[1])
70 return; // repeating key
71
72 if (!b->down[0])
73 b->down[0] = k;
74 else if (!b->down[1])
75 b->down[1] = k;
76 else
77 {
78 Con_Printf ("Three keys down for a button!\n");
79 return;
80 }
81
82 if (b->state & 1)
83 return; // still down
84 b->state |= 1 + 2; // down + impulse down
85 }
86
KeyUp(kbutton_t * b)87 void KeyUp (kbutton_t *b)
88 {
89 int k;
90 const char *c;
91
92 c = Cmd_Argv(1);
93 if (c[0])
94 k = atoi(c);
95 else
96 { // typed manually at the console, assume for unsticking, so clear all
97 b->down[0] = b->down[1] = 0;
98 b->state = 4; // impulse up
99 return;
100 }
101
102 if (b->down[0] == k)
103 b->down[0] = 0;
104 else if (b->down[1] == k)
105 b->down[1] = 0;
106 else
107 return; // key up without coresponding down (menu pass through)
108 if (b->down[0] || b->down[1])
109 return; // some other key is still holding it down
110
111 if (!(b->state & 1))
112 return; // still up (this should not happen)
113 b->state &= ~1; // now up
114 b->state |= 4; // impulse up
115 }
116
IN_KLookDown(void)117 void IN_KLookDown (void) {KeyDown(&in_klook);}
IN_KLookUp(void)118 void IN_KLookUp (void) {KeyUp(&in_klook);}
IN_MLookDown(void)119 void IN_MLookDown (void) {KeyDown(&in_mlook);}
IN_MLookUp(void)120 void IN_MLookUp (void) {
121 KeyUp(&in_mlook);
122 if ( !(in_mlook.state&1) && lookspring.value)
123 V_StartPitchDrift();
124 }
IN_UpDown(void)125 void IN_UpDown(void) {KeyDown(&in_up);}
IN_UpUp(void)126 void IN_UpUp(void) {KeyUp(&in_up);}
IN_DownDown(void)127 void IN_DownDown(void) {KeyDown(&in_down);}
IN_DownUp(void)128 void IN_DownUp(void) {KeyUp(&in_down);}
IN_LeftDown(void)129 void IN_LeftDown(void) {KeyDown(&in_left);}
IN_LeftUp(void)130 void IN_LeftUp(void) {KeyUp(&in_left);}
IN_RightDown(void)131 void IN_RightDown(void) {KeyDown(&in_right);}
IN_RightUp(void)132 void IN_RightUp(void) {KeyUp(&in_right);}
IN_ForwardDown(void)133 void IN_ForwardDown(void) {KeyDown(&in_forward);}
IN_ForwardUp(void)134 void IN_ForwardUp(void) {KeyUp(&in_forward);}
IN_BackDown(void)135 void IN_BackDown(void) {KeyDown(&in_back);}
IN_BackUp(void)136 void IN_BackUp(void) {KeyUp(&in_back);}
IN_LookupDown(void)137 void IN_LookupDown(void) {KeyDown(&in_lookup);}
IN_LookupUp(void)138 void IN_LookupUp(void) {KeyUp(&in_lookup);}
IN_LookdownDown(void)139 void IN_LookdownDown(void) {KeyDown(&in_lookdown);}
IN_LookdownUp(void)140 void IN_LookdownUp(void) {KeyUp(&in_lookdown);}
IN_MoveleftDown(void)141 void IN_MoveleftDown(void) {KeyDown(&in_moveleft);}
IN_MoveleftUp(void)142 void IN_MoveleftUp(void) {KeyUp(&in_moveleft);}
IN_MoverightDown(void)143 void IN_MoverightDown(void) {KeyDown(&in_moveright);}
IN_MoverightUp(void)144 void IN_MoverightUp(void) {KeyUp(&in_moveright);}
145
IN_SpeedDown(void)146 void IN_SpeedDown(void) {KeyDown(&in_speed);}
IN_SpeedUp(void)147 void IN_SpeedUp(void) {KeyUp(&in_speed);}
IN_StrafeDown(void)148 void IN_StrafeDown(void) {KeyDown(&in_strafe);}
IN_StrafeUp(void)149 void IN_StrafeUp(void) {KeyUp(&in_strafe);}
150
IN_AttackDown(void)151 void IN_AttackDown(void) {KeyDown(&in_attack);}
IN_AttackUp(void)152 void IN_AttackUp(void) {KeyUp(&in_attack);}
153
IN_UseDown(void)154 void IN_UseDown (void) {KeyDown(&in_use);}
IN_UseUp(void)155 void IN_UseUp (void) {KeyUp(&in_use);}
IN_JumpDown(void)156 void IN_JumpDown (void) {KeyDown(&in_jump);}
IN_JumpUp(void)157 void IN_JumpUp (void) {KeyUp(&in_jump);}
158
IN_Impulse(void)159 void IN_Impulse (void) {in_impulse=Q_atoi(Cmd_Argv(1));}
160
161 /*
162 ===============
163 CL_KeyState
164
165 Returns 0.25 if a key was pressed and released during the frame,
166 0.5 if it was pressed and held
167 0 if held then released, and
168 1.0 if held for the entire time
169 ===============
170 */
CL_KeyState(kbutton_t * key)171 float CL_KeyState (kbutton_t *key)
172 {
173 float val;
174 qboolean impulsedown, impulseup, down;
175
176 impulsedown = key->state & 2;
177 impulseup = key->state & 4;
178 down = key->state & 1;
179 val = 0;
180
181 if (impulsedown && !impulseup)
182 {
183 if (down)
184 val = 0.5; // pressed and held this frame
185 else
186 val = 0; // I_Error ();
187 }
188
189 if (impulseup && !impulsedown)
190 {
191 if (down)
192 val = 0; // I_Error ();
193 else
194 val = 0; // released this frame
195 }
196
197 if (!impulsedown && !impulseup)
198 {
199 if (down)
200 val = 1.0; // held the entire frame
201 else
202 val = 0; // up the entire frame
203 }
204
205 if (impulsedown && impulseup)
206 {
207 if (down)
208 val = 0.75; // released and re-pressed this frame
209 else
210 val = 0.25; // pressed and released this frame
211 }
212
213 key->state &= 1; // clear impulses
214
215 return val;
216 }
217
218
219
220
221 //==========================================================================
222
223 cvar_t cl_upspeed = CVAR2("cl_upspeed","200");
224 cvar_t cl_forwardspeed = CVAR3("cl_forwardspeed","200", true);
225 cvar_t cl_backspeed = CVAR3("cl_backspeed","200", true);
226 cvar_t cl_sidespeed = CVAR2("cl_sidespeed","350");
227
228 cvar_t cl_movespeedkey = CVAR2("cl_movespeedkey","2.0");
229
230 cvar_t cl_yawspeed = CVAR2("cl_yawspeed","140");
231 cvar_t cl_pitchspeed = CVAR2("cl_pitchspeed","150");
232
233 cvar_t cl_anglespeedkey = CVAR2("cl_anglespeedkey","1.5");
234
235
236 /*
237 ================
238 CL_AdjustAngles
239
240 Moves the local angle positions
241 ================
242 */
CL_AdjustAngles(void)243 void CL_AdjustAngles (void)
244 {
245 float speed;
246 float up, down;
247
248 if (in_speed.state & 1)
249 speed = host_frametime * cl_anglespeedkey.value;
250 else
251 speed = host_frametime;
252
253 if (!(in_strafe.state & 1))
254 {
255 cl.viewangles[YAW] -= speed*cl_yawspeed.value*CL_KeyState (&in_right);
256 cl.viewangles[YAW] += speed*cl_yawspeed.value*CL_KeyState (&in_left);
257 cl.viewangles[YAW] = anglemod(cl.viewangles[YAW]);
258 }
259 if (in_klook.state & 1)
260 {
261 V_StopPitchDrift ();
262 cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * CL_KeyState (&in_forward);
263 cl.viewangles[PITCH] += speed*cl_pitchspeed.value * CL_KeyState (&in_back);
264 }
265
266 up = CL_KeyState (&in_lookup);
267 down = CL_KeyState(&in_lookdown);
268
269 cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * up;
270 cl.viewangles[PITCH] += speed*cl_pitchspeed.value * down;
271
272 if (up || down)
273 V_StopPitchDrift ();
274
275 if (cl.viewangles[PITCH] > 80)
276 cl.viewangles[PITCH] = 80;
277 if (cl.viewangles[PITCH] < -70)
278 cl.viewangles[PITCH] = -70;
279
280 if (cl.viewangles[ROLL] > 50)
281 cl.viewangles[ROLL] = 50;
282 if (cl.viewangles[ROLL] < -50)
283 cl.viewangles[ROLL] = -50;
284
285 }
286
287 /*
288 ================
289 CL_BaseMove
290
291 Send the intended movement message to the server
292 ================
293 */
CL_BaseMove(usercmd_t * cmd)294 void CL_BaseMove (usercmd_t *cmd)
295 {
296 if (cls.signon != SIGNONS)
297 return;
298
299 CL_AdjustAngles ();
300
301 Q_memset (cmd, 0, sizeof(*cmd));
302
303 if (in_strafe.state & 1)
304 {
305 cmd->sidemove += cl_sidespeed.value * CL_KeyState (&in_right);
306 cmd->sidemove -= cl_sidespeed.value * CL_KeyState (&in_left);
307 }
308
309 cmd->sidemove += cl_sidespeed.value * CL_KeyState (&in_moveright);
310 cmd->sidemove -= cl_sidespeed.value * CL_KeyState (&in_moveleft);
311
312 cmd->upmove += cl_upspeed.value * CL_KeyState (&in_up);
313 cmd->upmove -= cl_upspeed.value * CL_KeyState (&in_down);
314
315 if (! (in_klook.state & 1) )
316 {
317 cmd->forwardmove += cl_forwardspeed.value * CL_KeyState (&in_forward);
318 cmd->forwardmove -= cl_backspeed.value * CL_KeyState (&in_back);
319 }
320
321 //
322 // adjust for speed key
323 //
324 if (in_speed.state & 1)
325 {
326 cmd->forwardmove *= cl_movespeedkey.value;
327 cmd->sidemove *= cl_movespeedkey.value;
328 cmd->upmove *= cl_movespeedkey.value;
329 }
330
331 #ifdef QUAKE2
332 cmd->lightlevel = cl.light_level;
333 #endif
334 }
335
336
337
338 /*
339 ==============
340 CL_SendMove
341 ==============
342 */
CL_SendMove(usercmd_t * cmd)343 void CL_SendMove (usercmd_t *cmd)
344 {
345 int i;
346 int bits;
347 sizebuf_t buf;
348 byte data[128];
349
350 buf.maxsize = 128;
351 buf.cursize = 0;
352 buf.data = data;
353
354 cl.cmd = *cmd;
355
356 //
357 // send the movement message
358 //
359 MSG_WriteByte (&buf, clc_move);
360
361 MSG_WriteFloat (&buf, cl.mtime[0]); // so server can get ping times
362
363 for (i=0 ; i<3 ; i++)
364 MSG_WriteAngle (&buf, cl.viewangles[i]);
365
366 MSG_WriteShort (&buf, (short) cmd->forwardmove);
367 MSG_WriteShort (&buf, (short) cmd->sidemove);
368 MSG_WriteShort (&buf, (short) cmd->upmove);
369
370 //
371 // send button bits
372 //
373 bits = 0;
374
375 if ( in_attack.state & 3 )
376 bits |= 1;
377 in_attack.state &= ~2;
378
379 if (in_jump.state & 3)
380 bits |= 2;
381 in_jump.state &= ~2;
382
383 MSG_WriteByte (&buf, bits);
384
385 MSG_WriteByte (&buf, in_impulse);
386 in_impulse = 0;
387
388 #ifdef QUAKE2
389 //
390 // light level
391 //
392 MSG_WriteByte (&buf, cmd->lightlevel);
393 #endif
394
395 //
396 // deliver the message
397 //
398 if (cls.demoplayback)
399 return;
400
401 //
402 // allways dump the first two message, because it may contain leftover inputs
403 // from the last level
404 //
405 if (++cl.movemessages <= 2)
406 return;
407
408 if (NET_SendUnreliableMessage (cls.netcon, &buf) == -1)
409 {
410 Con_Printf ("CL_SendMove: lost server connection\n");
411 CL_Disconnect ();
412 }
413 }
414
415 /*
416 ============
417 CL_InitInput
418 ============
419 */
CL_InitInput(void)420 void CL_InitInput (void)
421 {
422 Cmd_AddCommand ("+moveup",IN_UpDown);
423 Cmd_AddCommand ("-moveup",IN_UpUp);
424 Cmd_AddCommand ("+movedown",IN_DownDown);
425 Cmd_AddCommand ("-movedown",IN_DownUp);
426 Cmd_AddCommand ("+left",IN_LeftDown);
427 Cmd_AddCommand ("-left",IN_LeftUp);
428 Cmd_AddCommand ("+right",IN_RightDown);
429 Cmd_AddCommand ("-right",IN_RightUp);
430 Cmd_AddCommand ("+forward",IN_ForwardDown);
431 Cmd_AddCommand ("-forward",IN_ForwardUp);
432 Cmd_AddCommand ("+back",IN_BackDown);
433 Cmd_AddCommand ("-back",IN_BackUp);
434 Cmd_AddCommand ("+lookup", IN_LookupDown);
435 Cmd_AddCommand ("-lookup", IN_LookupUp);
436 Cmd_AddCommand ("+lookdown", IN_LookdownDown);
437 Cmd_AddCommand ("-lookdown", IN_LookdownUp);
438 Cmd_AddCommand ("+strafe", IN_StrafeDown);
439 Cmd_AddCommand ("-strafe", IN_StrafeUp);
440 Cmd_AddCommand ("+moveleft", IN_MoveleftDown);
441 Cmd_AddCommand ("-moveleft", IN_MoveleftUp);
442 Cmd_AddCommand ("+moveright", IN_MoverightDown);
443 Cmd_AddCommand ("-moveright", IN_MoverightUp);
444 Cmd_AddCommand ("+speed", IN_SpeedDown);
445 Cmd_AddCommand ("-speed", IN_SpeedUp);
446 Cmd_AddCommand ("+attack", IN_AttackDown);
447 Cmd_AddCommand ("-attack", IN_AttackUp);
448 Cmd_AddCommand ("+use", IN_UseDown);
449 Cmd_AddCommand ("-use", IN_UseUp);
450 Cmd_AddCommand ("+jump", IN_JumpDown);
451 Cmd_AddCommand ("-jump", IN_JumpUp);
452 Cmd_AddCommand ("impulse", IN_Impulse);
453 Cmd_AddCommand ("+klook", IN_KLookDown);
454 Cmd_AddCommand ("-klook", IN_KLookUp);
455 Cmd_AddCommand ("+mlook", IN_MLookDown);
456 Cmd_AddCommand ("-mlook", IN_MLookUp);
457
458 }
459
460