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