]> icculus.org git repositories - divverent/darkplaces.git/blob - cl_input.c
Made ValidateState warn and not error out about colormap > maxclients.
[divverent/darkplaces.git] / cl_input.c
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_jump, in_attack;
53 kbutton_t       in_up, in_down;
54 // LordHavoc: added 6 new buttons
55 kbutton_t       in_button3, in_button4, in_button5, in_button6, in_button7, in_button8;
56
57 int                     in_impulse;
58
59
60 void KeyDown (kbutton_t *b)
61 {
62         int k;
63         const char *c;
64
65         c = Cmd_Argv(1);
66         if (c[0])
67                 k = atoi(c);
68         else
69                 k = -1;         // typed manually at the console for continuous down
70
71         if (k == b->down[0] || k == b->down[1])
72                 return;         // repeating key
73
74         if (!b->down[0])
75                 b->down[0] = k;
76         else if (!b->down[1])
77                 b->down[1] = k;
78         else
79         {
80                 Con_Print("Three keys down for a button!\n");
81                 return;
82         }
83
84         if (b->state & 1)
85                 return;         // still down
86         b->state |= 1 + 2;      // down + impulse down
87 }
88
89 void KeyUp (kbutton_t *b)
90 {
91         int k;
92         const char *c;
93
94         c = Cmd_Argv(1);
95         if (c[0])
96                 k = atoi(c);
97         else
98         { // typed manually at the console, assume for unsticking, so clear all
99                 b->down[0] = b->down[1] = 0;
100                 b->state = 4;   // impulse up
101                 return;
102         }
103
104         if (b->down[0] == k)
105                 b->down[0] = 0;
106         else if (b->down[1] == k)
107                 b->down[1] = 0;
108         else
109                 return;         // key up without coresponding down (menu pass through)
110         if (b->down[0] || b->down[1])
111                 return;         // some other key is still holding it down
112
113         if (!(b->state & 1))
114                 return;         // still up (this should not happen)
115         b->state &= ~1;         // now up
116         b->state |= 4;          // impulse up
117 }
118
119 void IN_KLookDown (void) {KeyDown(&in_klook);}
120 void IN_KLookUp (void) {KeyUp(&in_klook);}
121 void IN_MLookDown (void) {KeyDown(&in_mlook);}
122 void IN_MLookUp (void)
123 {
124         KeyUp(&in_mlook);
125         if ( !(in_mlook.state&1) && lookspring.value)
126                 V_StartPitchDrift();
127 }
128 void IN_UpDown(void) {KeyDown(&in_up);}
129 void IN_UpUp(void) {KeyUp(&in_up);}
130 void IN_DownDown(void) {KeyDown(&in_down);}
131 void IN_DownUp(void) {KeyUp(&in_down);}
132 void IN_LeftDown(void) {KeyDown(&in_left);}
133 void IN_LeftUp(void) {KeyUp(&in_left);}
134 void IN_RightDown(void) {KeyDown(&in_right);}
135 void IN_RightUp(void) {KeyUp(&in_right);}
136 void IN_ForwardDown(void) {KeyDown(&in_forward);}
137 void IN_ForwardUp(void) {KeyUp(&in_forward);}
138 void IN_BackDown(void) {KeyDown(&in_back);}
139 void IN_BackUp(void) {KeyUp(&in_back);}
140 void IN_LookupDown(void) {KeyDown(&in_lookup);}
141 void IN_LookupUp(void) {KeyUp(&in_lookup);}
142 void IN_LookdownDown(void) {KeyDown(&in_lookdown);}
143 void IN_LookdownUp(void) {KeyUp(&in_lookdown);}
144 void IN_MoveleftDown(void) {KeyDown(&in_moveleft);}
145 void IN_MoveleftUp(void) {KeyUp(&in_moveleft);}
146 void IN_MoverightDown(void) {KeyDown(&in_moveright);}
147 void IN_MoverightUp(void) {KeyUp(&in_moveright);}
148
149 void IN_SpeedDown(void) {KeyDown(&in_speed);}
150 void IN_SpeedUp(void) {KeyUp(&in_speed);}
151 void IN_StrafeDown(void) {KeyDown(&in_strafe);}
152 void IN_StrafeUp(void) {KeyUp(&in_strafe);}
153
154 void IN_AttackDown(void) {KeyDown(&in_attack);}
155 void IN_AttackUp(void) {KeyUp(&in_attack);}
156
157 // LordHavoc: added 6 new buttons
158 void IN_Button3Down(void) {KeyDown(&in_button3);}
159 void IN_Button3Up(void) {KeyUp(&in_button3);}
160 void IN_Button4Down(void) {KeyDown(&in_button4);}
161 void IN_Button4Up(void) {KeyUp(&in_button4);}
162 void IN_Button5Down(void) {KeyDown(&in_button5);}
163 void IN_Button5Up(void) {KeyUp(&in_button5);}
164 void IN_Button6Down(void) {KeyDown(&in_button6);}
165 void IN_Button6Up(void) {KeyUp(&in_button6);}
166 void IN_Button7Down(void) {KeyDown(&in_button7);}
167 void IN_Button7Up(void) {KeyUp(&in_button7);}
168 void IN_Button8Down(void) {KeyDown(&in_button8);}
169 void IN_Button8Up(void) {KeyUp(&in_button8);}
170
171 void IN_JumpDown (void) {KeyDown(&in_jump);}
172 void IN_JumpUp (void) {KeyUp(&in_jump);}
173
174 void IN_Impulse (void) {in_impulse=atoi(Cmd_Argv(1));}
175
176 /*
177 ===============
178 CL_KeyState
179
180 Returns 0.25 if a key was pressed and released during the frame,
181 0.5 if it was pressed and held
182 0 if held then released, and
183 1.0 if held for the entire time
184 ===============
185 */
186 float CL_KeyState (kbutton_t *key)
187 {
188         float           val;
189         qboolean        impulsedown, impulseup, down;
190
191         impulsedown = key->state & 2;
192         impulseup = key->state & 4;
193         down = key->state & 1;
194         val = 0;
195
196         if (impulsedown && !impulseup)
197         {
198                 if (down)
199                         val = 0.5;      // pressed and held this frame
200                 else
201                         val = 0;        //      I_Error ();
202         }
203         if (impulseup && !impulsedown)
204         {
205                 if (down)
206                         val = 0;        //      I_Error ();
207                 else
208                         val = 0;        // released this frame
209         }
210         if (!impulsedown && !impulseup)
211         {
212                 if (down)
213                         val = 1.0;      // held the entire frame
214                 else
215                         val = 0;        // up the entire frame
216         }
217         if (impulsedown && impulseup)
218         {
219                 if (down)
220                         val = 0.75;     // released and re-pressed this frame
221                 else
222                         val = 0.25;     // pressed and released this frame
223         }
224
225         key->state &= 1;                // clear impulses
226
227         return val;
228 }
229
230
231
232
233 //==========================================================================
234
235 cvar_t cl_upspeed = {CVAR_SAVE, "cl_upspeed","400"};
236 cvar_t cl_forwardspeed = {CVAR_SAVE, "cl_forwardspeed","400"};
237 cvar_t cl_backspeed = {CVAR_SAVE, "cl_backspeed","400"};
238 cvar_t cl_sidespeed = {CVAR_SAVE, "cl_sidespeed","350"};
239
240 cvar_t cl_movespeedkey = {CVAR_SAVE, "cl_movespeedkey","2.0"};
241
242 cvar_t cl_yawspeed = {CVAR_SAVE, "cl_yawspeed","140"};
243 cvar_t cl_pitchspeed = {CVAR_SAVE, "cl_pitchspeed","150"};
244
245 cvar_t cl_anglespeedkey = {CVAR_SAVE, "cl_anglespeedkey","1.5"};
246
247 cvar_t cl_nodelta = {0, "cl_nodelta", "0"};
248
249 /*
250 ================
251 CL_AdjustAngles
252
253 Moves the local angle positions
254 ================
255 */
256 void CL_AdjustAngles (void)
257 {
258         float   speed;
259         float   up, down;
260
261         if (in_speed.state & 1)
262                 speed = host_realframetime * cl_anglespeedkey.value;
263         else
264                 speed = host_realframetime;
265
266         if (!(in_strafe.state & 1))
267         {
268                 cl.viewangles[YAW] -= speed*cl_yawspeed.value*CL_KeyState (&in_right);
269                 cl.viewangles[YAW] += speed*cl_yawspeed.value*CL_KeyState (&in_left);
270         }
271         if (in_klook.state & 1)
272         {
273                 V_StopPitchDrift ();
274                 cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * CL_KeyState (&in_forward);
275                 cl.viewangles[PITCH] += speed*cl_pitchspeed.value * CL_KeyState (&in_back);
276         }
277
278         up = CL_KeyState (&in_lookup);
279         down = CL_KeyState(&in_lookdown);
280
281         cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * up;
282         cl.viewangles[PITCH] += speed*cl_pitchspeed.value * down;
283
284         if (up || down)
285                 V_StopPitchDrift ();
286
287         cl.viewangles[YAW] = ANGLEMOD(cl.viewangles[YAW]);
288         cl.viewangles[PITCH] = ANGLEMOD(cl.viewangles[PITCH]);
289         cl.viewangles[ROLL] = ANGLEMOD(cl.viewangles[ROLL]);
290         if (cl.viewangles[YAW] >= 180)
291                 cl.viewangles[YAW] -= 360;
292         if (cl.viewangles[PITCH] >= 180)
293                 cl.viewangles[PITCH] -= 360;
294         if (cl.viewangles[ROLL] >= 180)
295                 cl.viewangles[ROLL] -= 360;
296
297         cl.viewangles[PITCH] = bound (in_pitch_min.value, cl.viewangles[PITCH], in_pitch_max.value);
298         cl.viewangles[ROLL] = bound(-50, cl.viewangles[ROLL], 50);
299 }
300
301 /*
302 ================
303 CL_BaseMove
304
305 Send the intended movement message to the server
306 ================
307 */
308 void CL_BaseMove (usercmd_t *cmd)
309 {
310         if (cls.signon != SIGNONS)
311                 return;
312
313         CL_AdjustAngles ();
314
315         memset (cmd, 0, sizeof(*cmd));
316
317         if (in_strafe.state & 1)
318         {
319                 cmd->sidemove += cl_sidespeed.value * CL_KeyState (&in_right);
320                 cmd->sidemove -= cl_sidespeed.value * CL_KeyState (&in_left);
321         }
322
323         cmd->sidemove += cl_sidespeed.value * CL_KeyState (&in_moveright);
324         cmd->sidemove -= cl_sidespeed.value * CL_KeyState (&in_moveleft);
325
326         cmd->upmove += cl_upspeed.value * CL_KeyState (&in_up);
327         cmd->upmove -= cl_upspeed.value * CL_KeyState (&in_down);
328
329         if (! (in_klook.state & 1) )
330         {
331                 cmd->forwardmove += cl_forwardspeed.value * CL_KeyState (&in_forward);
332                 cmd->forwardmove -= cl_backspeed.value * CL_KeyState (&in_back);
333         }
334
335 //
336 // adjust for speed key
337 //
338         if (in_speed.state & 1)
339         {
340                 cmd->forwardmove *= cl_movespeedkey.value;
341                 cmd->sidemove *= cl_movespeedkey.value;
342                 cmd->upmove *= cl_movespeedkey.value;
343         }
344 }
345
346
347
348 /*
349 ==============
350 CL_SendMove
351 ==============
352 */
353 void CL_SendMove(usercmd_t *cmd)
354 {
355         int i;
356         int bits;
357         sizebuf_t buf;
358         qbyte data[128];
359         static double lastmovetime;
360         static float forwardmove, sidemove, upmove, total; // accumulation
361
362         forwardmove += cmd->forwardmove;
363         sidemove += cmd->sidemove;
364         upmove += cmd->upmove;
365         total++;
366         // LordHavoc: cap outgoing movement messages to sys_ticrate
367         if (!cl.islocalgame && (realtime - lastmovetime < sys_ticrate.value))
368                 return;
369         lastmovetime = realtime;
370         // average what has happened during this time
371         total = 1.0f / total;
372         forwardmove *= total;
373         sidemove *= total;
374         upmove *= total;
375         total = 0;
376
377         buf.maxsize = 128;
378         buf.cursize = 0;
379         buf.data = data;
380
381         cl.cmd = *cmd;
382
383         // send the movement message
384         MSG_WriteByte (&buf, clc_move);
385
386         MSG_WriteFloat (&buf, cl.mtime[0]);     // so server can get ping times
387
388         if (cl.protocol == PROTOCOL_DARKPLACES2 || cl.protocol == PROTOCOL_DARKPLACES3)
389         {
390                 for (i = 0;i < 3;i++)
391                         MSG_WriteFloat (&buf, cl.viewangles[i]);
392         }
393         else if (cl.protocol == PROTOCOL_DARKPLACES1 || cl.protocol == PROTOCOL_DARKPLACES4 || cl.protocol == PROTOCOL_DARKPLACES5)
394         {
395                 for (i=0 ; i<3 ; i++)
396                         MSG_WritePreciseAngle (&buf, cl.viewangles[i]);
397         }
398         else
399         {
400                 for (i=0 ; i<3 ; i++)
401                         MSG_WriteAngle (&buf, cl.viewangles[i]);
402         }
403
404         MSG_WriteShort (&buf, forwardmove);
405         MSG_WriteShort (&buf, sidemove);
406         MSG_WriteShort (&buf, upmove);
407
408         forwardmove = sidemove = upmove = 0;
409         // send button bits
410         bits = 0;
411
412         if ( in_attack.state & 3 )
413                 bits |= 1;
414         in_attack.state &= ~2;
415
416         if (in_jump.state & 3)
417                 bits |= 2;
418         in_jump.state &= ~2;
419         // LordHavoc: added 6 new buttons
420         if (in_button3.state & 3) bits |=   4;in_button3.state &= ~2;
421         if (in_button4.state & 3) bits |=   8;in_button4.state &= ~2;
422         if (in_button5.state & 3) bits |=  16;in_button5.state &= ~2;
423         if (in_button6.state & 3) bits |=  32;in_button6.state &= ~2;
424         if (in_button7.state & 3) bits |=  64;in_button7.state &= ~2;
425         if (in_button8.state & 3) bits |= 128;in_button8.state &= ~2;
426
427         MSG_WriteByte (&buf, bits);
428
429         MSG_WriteByte (&buf, in_impulse);
430         in_impulse = 0;
431
432         if (cl.protocol == PROTOCOL_DARKPLACES1 || cl.protocol == PROTOCOL_DARKPLACES2 || cl.protocol == PROTOCOL_DARKPLACES3)
433         {
434                 // LordHavoc: should we ack this on receipt instead?  would waste net bandwidth though
435                 i = EntityFrame_MostRecentlyRecievedFrameNum(&cl.entitydatabase);
436                 if (i > 0)
437                 {
438                         MSG_WriteByte(&buf, clc_ackentities);
439                         MSG_WriteLong(&buf, i);
440                 }
441         }
442         else
443         {
444                 if (cl.entitydatabase4)
445                 {
446                         i = cl.entitydatabase4->ackframenum;
447                         if (cl_nodelta.integer)
448                                 i = -1;
449                         if (developer_networkentities.integer >= 1)
450                                 Con_Printf("send clc_ackentities %i\n", i);
451                         MSG_WriteByte(&buf, clc_ackentities);
452                         MSG_WriteLong(&buf, i);
453                 }
454         }
455
456         // deliver the message
457         if (cls.demoplayback)
458                 return;
459
460         // always dump the first two messages, because they may contain leftover inputs from the last level
461         if (++cl.movemessages <= 2)
462                 return;
463
464         if (NetConn_SendUnreliableMessage(cls.netcon, &buf) == -1)
465         {
466                 Con_Print("CL_SendMove: lost server connection\n");
467                 CL_Disconnect();
468                 Host_ShutdownServer(false);
469         }
470 }
471
472 /*
473 ============
474 CL_InitInput
475 ============
476 */
477 void CL_InitInput (void)
478 {
479         Cmd_AddCommand ("+moveup",IN_UpDown);
480         Cmd_AddCommand ("-moveup",IN_UpUp);
481         Cmd_AddCommand ("+movedown",IN_DownDown);
482         Cmd_AddCommand ("-movedown",IN_DownUp);
483         Cmd_AddCommand ("+left",IN_LeftDown);
484         Cmd_AddCommand ("-left",IN_LeftUp);
485         Cmd_AddCommand ("+right",IN_RightDown);
486         Cmd_AddCommand ("-right",IN_RightUp);
487         Cmd_AddCommand ("+forward",IN_ForwardDown);
488         Cmd_AddCommand ("-forward",IN_ForwardUp);
489         Cmd_AddCommand ("+back",IN_BackDown);
490         Cmd_AddCommand ("-back",IN_BackUp);
491         Cmd_AddCommand ("+lookup", IN_LookupDown);
492         Cmd_AddCommand ("-lookup", IN_LookupUp);
493         Cmd_AddCommand ("+lookdown", IN_LookdownDown);
494         Cmd_AddCommand ("-lookdown", IN_LookdownUp);
495         Cmd_AddCommand ("+strafe", IN_StrafeDown);
496         Cmd_AddCommand ("-strafe", IN_StrafeUp);
497         Cmd_AddCommand ("+moveleft", IN_MoveleftDown);
498         Cmd_AddCommand ("-moveleft", IN_MoveleftUp);
499         Cmd_AddCommand ("+moveright", IN_MoverightDown);
500         Cmd_AddCommand ("-moveright", IN_MoverightUp);
501         Cmd_AddCommand ("+speed", IN_SpeedDown);
502         Cmd_AddCommand ("-speed", IN_SpeedUp);
503         Cmd_AddCommand ("+attack", IN_AttackDown);
504         Cmd_AddCommand ("-attack", IN_AttackUp);
505         Cmd_AddCommand ("+jump", IN_JumpDown);
506         Cmd_AddCommand ("-jump", IN_JumpUp);
507         Cmd_AddCommand ("impulse", IN_Impulse);
508         Cmd_AddCommand ("+klook", IN_KLookDown);
509         Cmd_AddCommand ("-klook", IN_KLookUp);
510         Cmd_AddCommand ("+mlook", IN_MLookDown);
511         Cmd_AddCommand ("-mlook", IN_MLookUp);
512
513         // LordHavoc: added 6 new buttons
514         Cmd_AddCommand ("+button3", IN_Button3Down);
515         Cmd_AddCommand ("-button3", IN_Button3Up);
516         Cmd_AddCommand ("+button4", IN_Button4Down);
517         Cmd_AddCommand ("-button4", IN_Button4Up);
518         Cmd_AddCommand ("+button5", IN_Button5Down);
519         Cmd_AddCommand ("-button5", IN_Button5Up);
520         Cmd_AddCommand ("+button6", IN_Button6Down);
521         Cmd_AddCommand ("-button6", IN_Button6Up);
522         Cmd_AddCommand ("+button7", IN_Button7Down);
523         Cmd_AddCommand ("-button7", IN_Button7Up);
524         Cmd_AddCommand ("+button8", IN_Button8Down);
525         Cmd_AddCommand ("-button8", IN_Button8Up);
526
527         Cvar_RegisterVariable(&cl_nodelta);
528 }
529