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