1 /*
2 Copyright (C) 1997-2001 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 #include "client.h"
23 
24 cvar_t	*cl_nodelta;
25 
26 extern	unsigned	sys_frame_time;
27 unsigned	frame_msec;
28 unsigned	old_sys_frame_time;
29 
30 /*
31 ===============================================================================
32 
33 KEY BUTTONS
34 
35 Continuous button event tracking is complicated by the fact that two different
36 input sources (say, mouse button 1 and the control key) can both press the
37 same button, but the button should only be released when both of the
38 pressing key have been released.
39 
40 When a key event issues a button command (+forward, +attack, etc), it appends
41 its key number as a parameter to the command so it can be matched up with
42 the release.
43 
44 state bit 0 is the current state of the key
45 state bit 1 is edge triggered on the up to down transition
46 state bit 2 is edge triggered on the down to up transition
47 
48 
49 Key_Event (int key, qboolean down, unsigned time);
50 
51   +mlook src time
52 
53 ===============================================================================
54 */
55 
56 
57 kbutton_t	in_klook;
58 kbutton_t	in_left, in_right, in_forward, in_back;
59 kbutton_t	in_lookup, in_lookdown, in_moveleft, in_moveright;
60 kbutton_t	in_strafe, in_speed, in_use, in_attack;
61 kbutton_t	in_up, in_down;
62 
63 int			in_impulse;
64 
65 
KeyDown(kbutton_t * b)66 void KeyDown (kbutton_t *b)
67 {
68 	int		k;
69 	char	*c;
70 
71 	c = Cmd_Argv(1);
72 	if (c[0])
73 		k = atoi(c);
74 	else
75 		k = -1;		// typed manually at the console for continuous down
76 
77 	if (k == b->down[0] || k == b->down[1])
78 		return;		// repeating key
79 
80 	if (!b->down[0])
81 		b->down[0] = k;
82 	else if (!b->down[1])
83 		b->down[1] = k;
84 	else
85 	{
86 		Com_Printf ("Three keys down for a button!\n");
87 		return;
88 	}
89 
90 	if (b->state & 1)
91 		return;		// still down
92 
93 	// save timestamp
94 	c = Cmd_Argv(2);
95 	b->downtime = atoi(c);
96 	if (!b->downtime)
97 		b->downtime = sys_frame_time - 100;
98 
99 	b->state |= 1 + 2;	// down + impulse down
100 }
101 
KeyUp(kbutton_t * b)102 void KeyUp (kbutton_t *b)
103 {
104 	int		k;
105 	char	*c;
106 	unsigned	uptime;
107 
108 	c = Cmd_Argv(1);
109 	if (c[0])
110 		k = atoi(c);
111 	else
112 	{ // typed manually at the console, assume for unsticking, so clear all
113 		b->down[0] = b->down[1] = 0;
114 		b->state = 4;	// impulse up
115 		return;
116 	}
117 
118 	if (b->down[0] == k)
119 		b->down[0] = 0;
120 	else if (b->down[1] == k)
121 		b->down[1] = 0;
122 	else
123 		return;		// key up without coresponding down (menu pass through)
124 	if (b->down[0] || b->down[1])
125 		return;		// some other key is still holding it down
126 
127 	if (!(b->state & 1))
128 		return;		// still up (this should not happen)
129 
130 	// save timestamp
131 	c = Cmd_Argv(2);
132 	uptime = atoi(c);
133 	if (uptime)
134 		b->msec += uptime - b->downtime;
135 	else
136 		b->msec += 10;
137 
138 	b->state &= ~1;		// now up
139 	b->state |= 4; 		// impulse up
140 }
141 
IN_KLookDown(void)142 void IN_KLookDown (void) {KeyDown(&in_klook);}
IN_KLookUp(void)143 void IN_KLookUp (void) {KeyUp(&in_klook);}
IN_UpDown(void)144 void IN_UpDown(void) {KeyDown(&in_up);}
IN_UpUp(void)145 void IN_UpUp(void) {KeyUp(&in_up);}
IN_DownDown(void)146 void IN_DownDown(void) {KeyDown(&in_down);}
IN_DownUp(void)147 void IN_DownUp(void) {KeyUp(&in_down);}
IN_LeftDown(void)148 void IN_LeftDown(void) {KeyDown(&in_left);}
IN_LeftUp(void)149 void IN_LeftUp(void) {KeyUp(&in_left);}
IN_RightDown(void)150 void IN_RightDown(void) {KeyDown(&in_right);}
IN_RightUp(void)151 void IN_RightUp(void) {KeyUp(&in_right);}
IN_ForwardDown(void)152 void IN_ForwardDown(void) {KeyDown(&in_forward);}
IN_ForwardUp(void)153 void IN_ForwardUp(void) {KeyUp(&in_forward);}
IN_BackDown(void)154 void IN_BackDown(void) {KeyDown(&in_back);}
IN_BackUp(void)155 void IN_BackUp(void) {KeyUp(&in_back);}
IN_LookupDown(void)156 void IN_LookupDown(void) {KeyDown(&in_lookup);}
IN_LookupUp(void)157 void IN_LookupUp(void) {KeyUp(&in_lookup);}
IN_LookdownDown(void)158 void IN_LookdownDown(void) {KeyDown(&in_lookdown);}
IN_LookdownUp(void)159 void IN_LookdownUp(void) {KeyUp(&in_lookdown);}
IN_MoveleftDown(void)160 void IN_MoveleftDown(void) {KeyDown(&in_moveleft);}
IN_MoveleftUp(void)161 void IN_MoveleftUp(void) {KeyUp(&in_moveleft);}
IN_MoverightDown(void)162 void IN_MoverightDown(void) {KeyDown(&in_moveright);}
IN_MoverightUp(void)163 void IN_MoverightUp(void) {KeyUp(&in_moveright);}
164 
IN_SpeedDown(void)165 void IN_SpeedDown(void) {KeyDown(&in_speed);}
IN_SpeedUp(void)166 void IN_SpeedUp(void) {KeyUp(&in_speed);}
IN_StrafeDown(void)167 void IN_StrafeDown(void) {KeyDown(&in_strafe);}
IN_StrafeUp(void)168 void IN_StrafeUp(void) {KeyUp(&in_strafe);}
169 
IN_AttackDown(void)170 void IN_AttackDown(void) {KeyDown(&in_attack);}
IN_AttackUp(void)171 void IN_AttackUp(void) {KeyUp(&in_attack);}
172 
IN_UseDown(void)173 void IN_UseDown (void) {KeyDown(&in_use);}
IN_UseUp(void)174 void IN_UseUp (void) {KeyUp(&in_use);}
175 
IN_Impulse(void)176 void IN_Impulse (void) {in_impulse=atoi(Cmd_Argv(1));}
177 
178 /*
179 ===============
180 CL_KeyState
181 
182 Returns the fraction of the frame that the key was down
183 ===============
184 */
CL_KeyState(kbutton_t * key)185 float CL_KeyState (kbutton_t *key)
186 {
187 	float		val;
188 	int			msec;
189 
190 	key->state &= 1;		// clear impulses
191 
192 	msec = key->msec;
193 	key->msec = 0;
194 
195 	if (key->state)
196 	{	// still down
197 		msec += sys_frame_time - key->downtime;
198 		key->downtime = sys_frame_time;
199 	}
200 
201 #if 0
202 	if (msec)
203 	{
204 		Com_Printf ("%i ", msec);
205 	}
206 #endif
207 
208 	val = (float)msec / frame_msec;
209 	if (val < 0)
210 		val = 0;
211 	if (val > 1)
212 		val = 1;
213 
214 	return val;
215 }
216 
217 
218 
219 
220 //==========================================================================
221 
222 cvar_t	*cl_upspeed;
223 cvar_t	*cl_forwardspeed;
224 cvar_t	*cl_sidespeed;
225 
226 cvar_t	*cl_yawspeed;
227 cvar_t	*cl_pitchspeed;
228 
229 cvar_t	*cl_run;
230 
231 cvar_t	*cl_anglespeedkey;
232 
233 
234 /*
235 ================
236 CL_AdjustAngles
237 
238 Moves the local angle positions
239 ================
240 */
CL_AdjustAngles(void)241 void CL_AdjustAngles (void)
242 {
243 	float	speed;
244 	float	up, down;
245 
246 	if (in_speed.state & 1)
247 		speed = cls.frametime * cl_anglespeedkey->value;
248 	else
249 		speed = cls.frametime;
250 
251 	if (!(in_strafe.state & 1))
252 	{
253 		cl.viewangles[YAW] -= speed*cl_yawspeed->value*CL_KeyState (&in_right);
254 		cl.viewangles[YAW] += speed*cl_yawspeed->value*CL_KeyState (&in_left);
255 	}
256 	if (in_klook.state & 1)
257 	{
258 		cl.viewangles[PITCH] -= speed*cl_pitchspeed->value * CL_KeyState (&in_forward);
259 		cl.viewangles[PITCH] += speed*cl_pitchspeed->value * CL_KeyState (&in_back);
260 	}
261 
262 	up = CL_KeyState (&in_lookup);
263 	down = CL_KeyState(&in_lookdown);
264 
265 	cl.viewangles[PITCH] -= speed*cl_pitchspeed->value * up;
266 	cl.viewangles[PITCH] += speed*cl_pitchspeed->value * down;
267 }
268 
269 /*
270 ================
271 CL_BaseMove
272 
273 Send the intended movement message to the server
274 ================
275 */
CL_BaseMove(usercmd_t * cmd)276 void CL_BaseMove (usercmd_t *cmd)
277 {
278 	CL_AdjustAngles ();
279 
280 	memset (cmd, 0, sizeof(*cmd));
281 
282 	VectorCopy (cl.viewangles, cmd->angles);
283 	if (in_strafe.state & 1)
284 	{
285 		cmd->sidemove += cl_sidespeed->value * CL_KeyState (&in_right);
286 		cmd->sidemove -= cl_sidespeed->value * CL_KeyState (&in_left);
287 	}
288 
289 	cmd->sidemove += cl_sidespeed->value * CL_KeyState (&in_moveright);
290 	cmd->sidemove -= cl_sidespeed->value * CL_KeyState (&in_moveleft);
291 
292 	cmd->upmove += cl_upspeed->value * CL_KeyState (&in_up);
293 	cmd->upmove -= cl_upspeed->value * CL_KeyState (&in_down);
294 
295 	if (! (in_klook.state & 1) )
296 	{
297 		cmd->forwardmove += cl_forwardspeed->value * CL_KeyState (&in_forward);
298 		cmd->forwardmove -= cl_forwardspeed->value * CL_KeyState (&in_back);
299 	}
300 
301 //
302 // adjust for speed key / running
303 //
304 	if ( (in_speed.state & 1) ^ (int)(cl_run->value) )
305 	{
306 		cmd->forwardmove *= 2;
307 		cmd->sidemove *= 2;
308 		cmd->upmove *= 2;
309 	}
310 }
311 
CL_ClampPitch(void)312 void CL_ClampPitch (void)
313 {
314 	float	pitch;
315 
316 	pitch = SHORT2ANGLE(cl.frame.playerstate.pmove.delta_angles[PITCH]);
317 	if (pitch > 180)
318 		pitch -= 360;
319 
320 	if (cl.viewangles[PITCH] + pitch < -360)
321 		cl.viewangles[PITCH] += 360; // wrapped
322 	if (cl.viewangles[PITCH] + pitch > 360)
323 		cl.viewangles[PITCH] -= 360; // wrapped
324 
325 	if (cl.viewangles[PITCH] + pitch > 89)
326 		cl.viewangles[PITCH] = 89 - pitch;
327 	if (cl.viewangles[PITCH] + pitch < -89)
328 		cl.viewangles[PITCH] = -89 - pitch;
329 }
330 
331 /*
332 ==============
333 CL_FinishMove
334 ==============
335 */
CL_FinishMove(usercmd_t * cmd)336 void CL_FinishMove (usercmd_t *cmd)
337 {
338 	int		ms;
339 	int		i;
340 
341 //
342 // figure button bits
343 //
344 	if ( in_attack.state & 3 )
345 		cmd->buttons |= BUTTON_ATTACK;
346 	in_attack.state &= ~2;
347 
348 	if (in_use.state & 3)
349 		cmd->buttons |= BUTTON_USE;
350 	in_use.state &= ~2;
351 
352 	if (anykeydown && cls.key_dest == key_game)
353 		cmd->buttons |= BUTTON_ANY;
354 
355 	// send milliseconds of time to apply the move
356 	ms = cls.frametime * 1000;
357 	if (ms > 250)
358 		ms = 100;		// time was unreasonable
359 	cmd->msec = ms;
360 
361 	CL_ClampPitch ();
362 	for (i=0 ; i<3 ; i++)
363 		cmd->angles[i] = ANGLE2SHORT(cl.viewangles[i]);
364 
365 	cmd->impulse = in_impulse;
366 	in_impulse = 0;
367 
368 // send the ambient light level at the player's current position
369 	cmd->lightlevel = (byte)cl_lightlevel->value;
370 }
371 
372 /*
373 =================
374 CL_CreateCmd
375 =================
376 */
CL_CreateCmd(void)377 usercmd_t CL_CreateCmd (void)
378 {
379 	usercmd_t	cmd;
380 
381 	frame_msec = sys_frame_time - old_sys_frame_time;
382 	if (frame_msec < 1)
383 		frame_msec = 1;
384 	if (frame_msec > 200)
385 		frame_msec = 200;
386 
387 	// get basic movement from keyboard
388 	CL_BaseMove (&cmd);
389 
390 	// allow mice or other external controllers to add to the move
391 	IN_Move (&cmd);
392 
393 	CL_FinishMove (&cmd);
394 
395 	old_sys_frame_time = sys_frame_time;
396 
397 //cmd.impulse = cls.framecount;
398 
399 	return cmd;
400 }
401 
402 
IN_CenterView(void)403 void IN_CenterView (void)
404 {
405 	cl.viewangles[PITCH] = -SHORT2ANGLE(cl.frame.playerstate.pmove.delta_angles[PITCH]);
406 }
407 
408 /*
409 ============
410 CL_InitInput
411 ============
412 */
CL_InitInput(void)413 void CL_InitInput (void)
414 {
415 	Cmd_AddCommand ("centerview",IN_CenterView);
416 
417 	Cmd_AddCommand ("+moveup",IN_UpDown);
418 	Cmd_AddCommand ("-moveup",IN_UpUp);
419 	Cmd_AddCommand ("+movedown",IN_DownDown);
420 	Cmd_AddCommand ("-movedown",IN_DownUp);
421 	Cmd_AddCommand ("+left",IN_LeftDown);
422 	Cmd_AddCommand ("-left",IN_LeftUp);
423 	Cmd_AddCommand ("+right",IN_RightDown);
424 	Cmd_AddCommand ("-right",IN_RightUp);
425 	Cmd_AddCommand ("+forward",IN_ForwardDown);
426 	Cmd_AddCommand ("-forward",IN_ForwardUp);
427 	Cmd_AddCommand ("+back",IN_BackDown);
428 	Cmd_AddCommand ("-back",IN_BackUp);
429 	Cmd_AddCommand ("+lookup", IN_LookupDown);
430 	Cmd_AddCommand ("-lookup", IN_LookupUp);
431 	Cmd_AddCommand ("+lookdown", IN_LookdownDown);
432 	Cmd_AddCommand ("-lookdown", IN_LookdownUp);
433 	Cmd_AddCommand ("+strafe", IN_StrafeDown);
434 	Cmd_AddCommand ("-strafe", IN_StrafeUp);
435 	Cmd_AddCommand ("+moveleft", IN_MoveleftDown);
436 	Cmd_AddCommand ("-moveleft", IN_MoveleftUp);
437 	Cmd_AddCommand ("+moveright", IN_MoverightDown);
438 	Cmd_AddCommand ("-moveright", IN_MoverightUp);
439 	Cmd_AddCommand ("+speed", IN_SpeedDown);
440 	Cmd_AddCommand ("-speed", IN_SpeedUp);
441 	Cmd_AddCommand ("+attack", IN_AttackDown);
442 	Cmd_AddCommand ("-attack", IN_AttackUp);
443 	Cmd_AddCommand ("+use", IN_UseDown);
444 	Cmd_AddCommand ("-use", IN_UseUp);
445 	Cmd_AddCommand ("impulse", IN_Impulse);
446 	Cmd_AddCommand ("+klook", IN_KLookDown);
447 	Cmd_AddCommand ("-klook", IN_KLookUp);
448 
449 	cl_nodelta = Cvar_Get ("cl_nodelta", "0", 0);
450 }
451 
452 
453 
454 /*
455 =================
456 CL_SendCmd
457 =================
458 */
CL_SendCmd(void)459 void CL_SendCmd (void)
460 {
461 	sizebuf_t	buf;
462 	byte		data[128];
463 	int			i;
464 	usercmd_t	*cmd, *oldcmd;
465 	usercmd_t	nullcmd;
466 	int			checksumIndex;
467 
468 	// build a command even if not connected
469 
470 	// save this command off for prediction
471 	i = cls.netchan.outgoing_sequence & (CMD_BACKUP-1);
472 	cmd = &cl.cmds[i];
473 	cl.cmd_time[i] = cls.realtime;	// for netgraph ping calculation
474 
475 	*cmd = CL_CreateCmd ();
476 
477 	cl.cmd = *cmd;
478 
479 	if (cls.state == ca_disconnected || cls.state == ca_connecting)
480 		return;
481 
482 	if ( cls.state == ca_connected)
483 	{
484 		if (cls.netchan.message.cursize	|| curtime - cls.netchan.last_sent > 1000 )
485 			Netchan_Transmit (&cls.netchan, 0, buf.data);
486 		return;
487 	}
488 
489 	// send a userinfo update if needed
490 	if (userinfo_modified)
491 	{
492 		CL_FixUpGender();
493 		userinfo_modified = false;
494 		MSG_WriteByte (&cls.netchan.message, clc_userinfo);
495 		MSG_WriteString (&cls.netchan.message, Cvar_Userinfo() );
496 	}
497 
498 	SZ_Init (&buf, data, sizeof(data));
499 
500 	if (cmd->buttons && cl.cinematictime > 0 && !cl.attractloop
501 		&& cls.realtime - cl.cinematictime > 1000)
502 	{	// skip the rest of the cinematic
503 		SCR_FinishCinematic ();
504 	}
505 
506 	// begin a client move command
507 	MSG_WriteByte (&buf, clc_move);
508 
509 	// save the position for a checksum byte
510 	checksumIndex = buf.cursize;
511 	MSG_WriteByte (&buf, 0);
512 
513 	// let the server know what the last frame we
514 	// got was, so the next message can be delta compressed
515 	if (cl_nodelta->value || !cl.frame.valid || cls.demowaiting)
516 		MSG_WriteLong (&buf, -1);	// no compression
517 	else
518 		MSG_WriteLong (&buf, cl.frame.serverframe);
519 
520 	// send this and the previous cmds in the message, so
521 	// if the last packet was dropped, it can be recovered
522 	i = (cls.netchan.outgoing_sequence-2) & (CMD_BACKUP-1);
523 	cmd = &cl.cmds[i];
524 	memset (&nullcmd, 0, sizeof(nullcmd));
525 	MSG_WriteDeltaUsercmd (&buf, &nullcmd, cmd);
526 	oldcmd = cmd;
527 
528 	i = (cls.netchan.outgoing_sequence-1) & (CMD_BACKUP-1);
529 	cmd = &cl.cmds[i];
530 	MSG_WriteDeltaUsercmd (&buf, oldcmd, cmd);
531 	oldcmd = cmd;
532 
533 	i = (cls.netchan.outgoing_sequence) & (CMD_BACKUP-1);
534 	cmd = &cl.cmds[i];
535 	MSG_WriteDeltaUsercmd (&buf, oldcmd, cmd);
536 
537 	// calculate a checksum over the move commands
538 	buf.data[checksumIndex] = COM_BlockSequenceCRCByte(
539 		buf.data + checksumIndex + 1, buf.cursize - checksumIndex - 1,
540 		cls.netchan.outgoing_sequence);
541 
542 	//
543 	// deliver the message
544 	//
545 	Netchan_Transmit (&cls.netchan, buf.cursize, buf.data);
546 }
547 
548 
549