1 /*
2 ===========================================================================
3
4 Return to Castle Wolfenstein multiplayer GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6
7 This file is part of the Return to Castle Wolfenstein multiplayer GPL Source Code (RTCW MP Source Code).
8
9 RTCW MP Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 RTCW MP Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with RTCW MP Source Code. If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the RTCW MP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW MP Source Code. If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29
30
31 // cg_servercmds.c -- reliably sequenced text commands sent by the server
32 // these are processed at snapshot transition time, so there will definately
33 // be a valid snapshot this frame
34
35 #include "cg_local.h"
36 #include "../ui/ui_shared.h"
37
38 void CG_StartShakeCamera( float param ); // NERVE - SMF
39
40 /*
41 =================
42 CG_ParseScores
43
44 =================
45 */
CG_ParseScores(void)46 static void CG_ParseScores( void ) {
47 int i, powerups;
48
49 cg.numScores = atoi( CG_Argv( 1 ) );
50 if ( cg.numScores > MAX_CLIENTS ) {
51 cg.numScores = MAX_CLIENTS;
52 }
53
54 cg.teamScores[0] = atoi( CG_Argv( 2 ) );
55 cg.teamScores[1] = atoi( CG_Argv( 3 ) );
56
57 memset( cg.scores, 0, sizeof( cg.scores ) );
58 for ( i = 0 ; i < cg.numScores ; i++ ) {
59 //
60 cg.scores[i].client = atoi( CG_Argv( i * 8 + 4 ) );
61 cg.scores[i].score = atoi( CG_Argv( i * 8 + 5 ) );
62 cg.scores[i].ping = atoi( CG_Argv( i * 8 + 6 ) );
63 cg.scores[i].time = atoi( CG_Argv( i * 8 + 7 ) );
64 cg.scores[i].scoreFlags = atoi( CG_Argv( i * 8 + 8 ) );
65 powerups = atoi( CG_Argv( i * 8 + 9 ) );
66 cg.scores[i].playerClass = atoi( CG_Argv( i * 8 + 10 ) ); // NERVE - SMF
67 cg.scores[i].respawnsLeft = atoi( CG_Argv( i * 8 + 11 ) ); // NERVE - SMF
68 // DHM - Nerve :: the following parameters are not sent by server
69 /*
70 cg.scores[i].accuracy = atoi(CG_Argv(i * 14 + 10));
71 cg.scores[i].impressiveCount = atoi(CG_Argv(i * 14 + 11));
72 cg.scores[i].excellentCount = atoi(CG_Argv(i * 14 + 12));
73 cg.scores[i].guantletCount = atoi(CG_Argv(i * 14 + 13));
74 cg.scores[i].defendCount = atoi(CG_Argv(i * 14 + 14));
75 cg.scores[i].assistCount = atoi(CG_Argv(i * 14 + 15));
76 cg.scores[i].perfect = atoi(CG_Argv(i * 14 + 16));
77 cg.scores[i].captures = atoi(CG_Argv(i * 14 + 17));
78 */
79
80 if ( cg.scores[i].client < 0 || cg.scores[i].client >= MAX_CLIENTS ) {
81 cg.scores[i].client = 0;
82 }
83 cgs.clientinfo[ cg.scores[i].client ].score = cg.scores[i].score;
84 cgs.clientinfo[ cg.scores[i].client ].powerups = powerups;
85
86 cg.scores[i].team = cgs.clientinfo[cg.scores[i].client].team;
87 }
88 #ifdef MISSIONPACK
89 CG_SetScoreSelection( NULL );
90 #endif
91
92 }
93
94 /*
95 =================
96 CG_ParseTeamInfo
97
98 =================
99 */
CG_ParseTeamInfo(void)100 static void CG_ParseTeamInfo( void ) {
101 int i;
102 int client;
103
104 // NERVE - SMF
105 cg.identifyClientNum = atoi( CG_Argv( 1 ) );
106 cg.identifyClientHealth = atoi( CG_Argv( 2 ) );
107 // -NERVE - SMF
108
109 numSortedTeamPlayers = atoi( CG_Argv( 3 ) );
110 if( numSortedTeamPlayers < 0 || numSortedTeamPlayers > TEAM_MAXOVERLAY )
111 {
112 CG_Error( "CG_ParseTeamInfo: numSortedTeamPlayers out of range (%d)",
113 numSortedTeamPlayers );
114 return;
115 }
116
117 for ( i = 0 ; i < numSortedTeamPlayers ; i++ ) {
118 client = atoi( CG_Argv( i * 5 + 4 ) );
119 if( client < 0 || client >= MAX_CLIENTS )
120 {
121 CG_Error( "CG_ParseTeamInfo: bad client number: %d", client );
122 return;
123 }
124
125 sortedTeamPlayers[i] = client;
126
127 cgs.clientinfo[ client ].location = atoi( CG_Argv( i * 5 + 5 ) );
128 cgs.clientinfo[ client ].health = atoi( CG_Argv( i * 5 + 6 ) );
129 cgs.clientinfo[ client ].powerups = atoi( CG_Argv( i * 5 + 7 ) );
130
131 cg_entities[ client ].currentState.teamNum = atoi( CG_Argv( i * 5 + 8 ) );
132 }
133 }
134
135
136 /*
137 ================
138 CG_ParseServerinfo
139
140 This is called explicitly when the gamestate is first received,
141 and whenever the server updates any serverinfo flagged cvars
142 ================
143 */
CG_ParseServerinfo(void)144 void CG_ParseServerinfo( void ) {
145 const char *info;
146 char *mapname;
147
148 info = CG_ConfigString( CS_SERVERINFO );
149 cgs.gametype = atoi( Info_ValueForKey( info, "g_gametype" ) );
150 cgs.antilag = atoi( Info_ValueForKey( info, "g_antilag" ) );
151 if ( !cgs.localServer ) {
152 trap_Cvar_Set( "g_gametype", va( "%i", cgs.gametype ) );
153 trap_Cvar_Set( "g_antilag", va( "%i", cgs.antilag ) );
154 }
155 cgs.dmflags = 0; //atoi( Info_ValueForKey( info, "dmflags" ) ); // NERVE - SMF - no longer serverinfo
156 cgs.teamflags = 0; //atoi( Info_ValueForKey( info, "teamflags" ) );
157 cgs.fraglimit = 0; //atoi( Info_ValueForKey( info, "fraglimit" ) ); // NERVE - SMF - no longer serverinfo
158 cgs.capturelimit = 0; //atoi( Info_ValueForKey( info, "capturelimit" ) ); // NERVE - SMF - no longer serverinfo
159 cgs.timelimit = atof( Info_ValueForKey( info, "timelimit" ) );
160 cgs.maxclients = atoi( Info_ValueForKey( info, "sv_maxclients" ) );
161 mapname = Info_ValueForKey( info, "mapname" );
162 Com_sprintf( cgs.mapname, sizeof( cgs.mapname ), "maps/%s.bsp", mapname );
163
164 // JPW NERVE
165 // prolly should parse all CS_SERVERINFO keys automagically, but I don't want to break anything that might be improperly set for wolf SP, so I'm just parsing MP relevant stuff here
166 trap_Cvar_Set( "g_medicChargeTime",Info_ValueForKey( info,"g_medicChargeTime" ) );
167 trap_Cvar_Set( "g_engineerChargeTime",Info_ValueForKey( info,"g_engineerChargeTime" ) );
168 trap_Cvar_Set( "g_soldierChargeTime",Info_ValueForKey( info,"g_soldierChargeTime" ) );
169 trap_Cvar_Set( "g_LTChargeTime",Info_ValueForKey( info,"g_LTChargeTime" ) );
170 trap_Cvar_Set( "g_redlimbotime",Info_ValueForKey( info,"g_redlimbotime" ) );
171 cg_redlimbotime.integer = atoi( Info_ValueForKey( info, "g_redlimbotime" ) );
172 cg_bluelimbotime.integer = atoi( Info_ValueForKey( info, "g_bluelimbotime" ) );
173 // jpw
174
175 cgs.minclients = atoi( Info_ValueForKey( info, "g_minGameClients" ) ); // NERVE - SMF
176
177 // TTimo - make this available for ingame_callvote
178 trap_Cvar_Set( "cg_ui_voteFlags", Info_ValueForKey( info, "g_voteFlags" ) );
179 }
180
181 /*
182 ==================
183 CG_ParseWolfinfo
184
185 NERVE - SMF
186 ==================
187 */
CG_ParseWolfinfo(void)188 void CG_ParseWolfinfo( void ) {
189 const char *info;
190
191 info = CG_ConfigString( CS_WOLFINFO );
192
193 cgs.currentRound = atoi( Info_ValueForKey( info, "g_currentRound" ) );
194 cgs.nextTimeLimit = atof( Info_ValueForKey( info, "g_nextTimeLimit" ) );
195 cgs.gamestate = atoi( Info_ValueForKey( info, "gamestate" ) );
196 if ( !cgs.localServer ) {
197 trap_Cvar_Set( "gamestate", va( "%i", cgs.gamestate ) );
198 }
199 }
200
201 /*
202 ==================
203 CG_ParseWarmup
204 ==================
205 */
CG_ParseWarmup(void)206 static void CG_ParseWarmup( void ) {
207 const char *info;
208 int warmup;
209
210 info = CG_ConfigString( CS_WARMUP );
211
212 warmup = atoi( info );
213 cg.warmupCount = -1;
214
215 if ( warmup == 0 && cg.warmup ) {
216
217 } else if ( warmup > 0 && cg.warmup <= 0 ) {
218 trap_S_StartLocalSound( cgs.media.countPrepareSound, CHAN_ANNOUNCER );
219 }
220
221 cg.warmup = warmup;
222 }
223
224 /*
225 =====================
226 CG_ParseScreenFade
227 =====================
228 */
CG_ParseScreenFade(void)229 static void CG_ParseScreenFade( void ) {
230 const char *info;
231 char *token;
232
233 info = CG_ConfigString( CS_SCREENFADE );
234 token = COM_Parse( (char **)&info );
235 cgs.fadeAlpha = atof( token );
236 token = COM_Parse( (char **)&info );
237 cgs.fadeStartTime = atoi( token );
238 token = COM_Parse( (char **)&info );
239 cgs.fadeDuration = atoi( token );
240
241 if ( cgs.fadeStartTime + cgs.fadeDuration < cg.time ) {
242 cgs.fadeAlphaCurrent = cgs.fadeAlpha;
243 }
244 }
245
246
247 /*
248 ==============
249 CG_ParseFog
250 float near dist
251 float far dist
252 float density
253 float[3] r,g,b
254 int time
255 ==============
256 */
CG_ParseFog(void)257 static void CG_ParseFog( void ) {
258 const char *info;
259 char *token;
260 float ne, fa, r, g, b, density;
261 int time;
262
263 info = CG_ConfigString( CS_FOGVARS );
264 token = COM_Parse( (char **)&info ); ne = atof( token );
265 token = COM_Parse( (char **)&info ); fa = atof( token );
266 token = COM_Parse( (char **)&info ); density = atof( token );
267 token = COM_Parse( (char **)&info ); r = atof( token );
268 token = COM_Parse( (char **)&info ); g = atof( token );
269 token = COM_Parse( (char **)&info ); b = atof( token );
270 token = COM_Parse( (char **)&info ); time = atoi( token );
271
272 if ( fa ) { // far of '0' from a target_fog means "return to map fog"
273 trap_R_SetFog( FOG_SERVER, (int)ne, (int)fa, r, g, b, density + .1 );
274 trap_R_SetFog( FOG_CMD_SWITCHFOG, FOG_SERVER, time, 0, 0, 0, 0 );
275 } else {
276 trap_R_SetFog( FOG_CMD_SWITCHFOG, FOG_MAP, time, 0, 0, 0, 0 );
277 }
278 }
279
280 /*
281 ================
282 CG_SetConfigValues
283
284 Called on load to set the initial values from configure strings
285 ================
286 */
CG_SetConfigValues(void)287 void CG_SetConfigValues( void ) {
288 #ifdef MISSIONPACK
289 const char *s;
290 #endif
291
292 cgs.scores1 = atoi( CG_ConfigString( CS_SCORES1 ) );
293 cgs.scores2 = atoi( CG_ConfigString( CS_SCORES2 ) );
294 cgs.levelStartTime = atoi( CG_ConfigString( CS_LEVEL_START_TIME ) );
295 #ifdef MISSIONPACK
296 if ( cgs.gametype == GT_CTF ) {
297 s = CG_ConfigString( CS_FLAGSTATUS );
298 cgs.redflag = s[0] - '0';
299 cgs.blueflag = s[1] - '0';
300 } else if ( cgs.gametype == GT_1FCTF ) {
301 s = CG_ConfigString( CS_FLAGSTATUS );
302 cgs.flagStatus = s[0] - '0';
303 }
304 #endif
305 cg.warmup = atoi( CG_ConfigString( CS_WARMUP ) );
306 }
307
308 /*
309 =====================
310 CG_ShaderStateChanged
311 =====================
312 */
CG_ShaderStateChanged(void)313 void CG_ShaderStateChanged( void ) {
314 char originalShader[MAX_QPATH];
315 char newShader[MAX_QPATH];
316 char timeOffset[16];
317 const char *o;
318 char *n,*t;
319
320 o = CG_ConfigString( CS_SHADERSTATE );
321 while ( o && *o ) {
322 n = strstr( o, "=" );
323 if ( n && *n ) {
324 strncpy( originalShader, o, n - o );
325 originalShader[n - o] = 0;
326 n++;
327 t = strstr( n, ":" );
328 if ( t && *t ) {
329 strncpy( newShader, n, t - n );
330 newShader[t - n] = 0;
331 } else {
332 break;
333 }
334 t++;
335 o = strstr( t, "@" );
336 if ( o ) {
337 strncpy( timeOffset, t, o - t );
338 timeOffset[o - t] = 0;
339 o++;
340 trap_R_RemapShader( originalShader, newShader, timeOffset );
341 }
342 } else {
343 break;
344 }
345 }
346 }
347
348 /*
349 ================
350 CG_ConfigStringModified
351
352 ================
353 */
CG_ConfigStringModified(void)354 static void CG_ConfigStringModified( void ) {
355 const char *str;
356 int num;
357
358 num = atoi( CG_Argv( 1 ) );
359
360 // get the gamestate from the client system, which will have the
361 // new configstring already integrated
362 trap_GetGameState( &cgs.gameState );
363
364 // look up the individual string that was modified
365 str = CG_ConfigString( num );
366
367 // do something with it if necessary
368 if ( num == CS_MUSIC ) {
369 CG_StartMusic();
370 } else if ( num == CS_SERVERINFO ) {
371 CG_ParseServerinfo();
372 } else if ( num == CS_WARMUP ) {
373 CG_ParseWarmup();
374 } else if ( num == CS_WOLFINFO ) { // NERVE - SMF
375 CG_ParseWolfinfo();
376 } else if ( num == CS_SCORES1 ) {
377 cgs.scores1 = atoi( str );
378 } else if ( num == CS_SCORES2 ) {
379 cgs.scores2 = atoi( str );
380 } else if ( num == CS_LEVEL_START_TIME ) {
381 cgs.levelStartTime = atoi( str );
382 } else if ( num == CS_VOTE_TIME ) {
383 cgs.voteTime = atoi( str );
384 cgs.voteModified = qtrue;
385 } else if ( num == CS_VOTE_YES ) {
386 cgs.voteYes = atoi( str );
387 cgs.voteModified = qtrue;
388 } else if ( num == CS_VOTE_NO ) {
389 cgs.voteNo = atoi( str );
390 cgs.voteModified = qtrue;
391 } else if ( num == CS_VOTE_STRING ) {
392 Q_strncpyz( cgs.voteString, str, sizeof( cgs.voteString ) );
393 #if 0
394 trap_S_StartLocalSound( cgs.media.voteNow, CHAN_ANNOUNCER );
395 } else if ( num >= CS_TEAMVOTE_TIME && num <= CS_TEAMVOTE_TIME + 1 ) {
396 cgs.teamVoteTime[num - CS_TEAMVOTE_TIME] = atoi( str );
397 cgs.teamVoteModified[num - CS_TEAMVOTE_TIME] = qtrue;
398 } else if ( num >= CS_TEAMVOTE_YES && num <= CS_TEAMVOTE_YES + 1 ) {
399 cgs.teamVoteYes[num - CS_TEAMVOTE_YES] = atoi( str );
400 cgs.teamVoteModified[num - CS_TEAMVOTE_YES] = qtrue;
401 } else if ( num >= CS_TEAMVOTE_NO && num <= CS_TEAMVOTE_NO + 1 ) {
402 cgs.teamVoteNo[num - CS_TEAMVOTE_NO] = atoi( str );
403 cgs.teamVoteModified[num - CS_TEAMVOTE_NO] = qtrue;
404 } else if ( num >= CS_TEAMVOTE_STRING && num <= CS_TEAMVOTE_STRING + 1 ) {
405 Q_strncpyz( cgs.teamVoteString[num - CS_TEAMVOTE_STRING], str, sizeof( cgs.teamVoteString[0] ) );
406 trap_S_StartLocalSound( cgs.media.voteNow, CHAN_ANNOUNCER );
407 #endif
408 } else if ( num == CS_INTERMISSION ) {
409 cg.intermissionStarted = atoi( str );
410 } else if ( num == CS_SCREENFADE ) {
411 CG_ParseScreenFade();
412 } else if ( num == CS_FOGVARS ) {
413 CG_ParseFog();
414 } else if ( num >= CS_MODELS && num < CS_MODELS + MAX_MODELS ) {
415 cgs.gameModels[ num - CS_MODELS ] = trap_R_RegisterModel( str );
416 } else if ( num >= CS_SOUNDS && num < CS_SOUNDS + MAX_SOUNDS ) {
417 if ( str[0] != '*' ) { // player specific sounds don't register here
418
419 // Ridah, register sound scripts seperately
420 if ( !strstr( str, ".wav" ) ) {
421 CG_SoundScriptPrecache( str );
422 } else {
423 cgs.gameSounds[ num - CS_SOUNDS] = trap_S_RegisterSound( str );
424 }
425
426 }
427 } else if ( num >= CS_PLAYERS && num < CS_PLAYERS + MAX_CLIENTS ) {
428 CG_NewClientInfo( num - CS_PLAYERS );
429 }
430 // Rafael particle configstring
431 else if ( num >= CS_PARTICLES && num < CS_PARTICLES + MAX_PARTICLES_AREAS ) {
432 CG_NewParticleArea( num );
433 }
434 //----(SA) have not reached this code yet so I don't know if I really need this here
435 else if ( num >= CS_DLIGHTS && num < CS_DLIGHTS + MAX_DLIGHTS ) {
436 // CG_Printf( ">>>>>>>>>>>got configstring for dlight: %d\ntell Sherman!!!!!!!!!!", num - CS_DLIGHTS );
437 //----(SA)
438 } else if ( num == CS_SHADERSTATE ) {
439 CG_ShaderStateChanged();
440 }
441 }
442
443
444 /*
445 =======================
446 CG_AddToTeamChat
447
448 =======================
449 */
CG_AddToTeamChat(const char * str)450 static void CG_AddToTeamChat( const char *str ) {
451 int len;
452 char *p, *ls;
453 int lastcolor;
454 int chatHeight;
455
456 if ( cg_teamChatHeight.integer < TEAMCHAT_HEIGHT ) {
457 chatHeight = cg_teamChatHeight.integer;
458 } else {
459 chatHeight = TEAMCHAT_HEIGHT;
460 }
461
462 if ( chatHeight <= 0 || cg_teamChatTime.integer <= 0 ) {
463 // team chat disabled, dump into normal chat
464 cgs.teamChatPos = cgs.teamLastChatPos = 0;
465 return;
466 }
467
468 len = 0;
469
470 p = cgs.teamChatMsgs[cgs.teamChatPos % chatHeight];
471 *p = 0;
472
473 lastcolor = '7';
474
475 ls = NULL;
476 while ( *str ) {
477 if ( len > TEAMCHAT_WIDTH - 1 ) {
478 if ( ls ) {
479 str -= ( p - ls );
480 str++;
481 p -= ( p - ls );
482 }
483 *p = 0;
484
485 cgs.teamChatMsgTimes[cgs.teamChatPos % chatHeight] = cg.time;
486
487 cgs.teamChatPos++;
488 p = cgs.teamChatMsgs[cgs.teamChatPos % chatHeight];
489 *p = 0;
490 *p++ = Q_COLOR_ESCAPE;
491 *p++ = lastcolor;
492 len = 0;
493 ls = NULL;
494 }
495
496 if ( Q_IsColorString( str ) ) {
497 *p++ = *str++;
498 lastcolor = *str;
499 *p++ = *str++;
500 continue;
501 }
502 if ( *str == ' ' ) {
503 ls = p;
504 }
505 *p++ = *str++;
506 len++;
507 }
508 *p = 0;
509
510 cgs.teamChatMsgTimes[cgs.teamChatPos % chatHeight] = cg.time;
511 cgs.teamChatPos++;
512
513 if ( cgs.teamChatPos - cgs.teamLastChatPos > chatHeight ) {
514 cgs.teamLastChatPos = cgs.teamChatPos - chatHeight;
515 }
516 }
517
518 /*
519 =======================
520 CG_AddToNotify
521
522 =======================
523 */
CG_AddToNotify(const char * str)524 void CG_AddToNotify( const char *str ) {
525 int len;
526 char *p, *ls;
527 int lastcolor;
528 int chatHeight;
529 float notifytime;
530 char var[MAX_TOKEN_CHARS];
531
532 trap_Cvar_VariableStringBuffer( "con_notifytime", var, sizeof( var ) );
533 notifytime = atof( var ) * 1000;
534
535 chatHeight = NOTIFY_HEIGHT;
536
537 if ( chatHeight <= 0 || notifytime <= 0 ) {
538 // team chat disabled, dump into normal chat
539 cgs.notifyPos = cgs.notifyLastPos = 0;
540 return;
541 }
542
543 len = 0;
544
545 p = cgs.notifyMsgs[cgs.notifyPos % chatHeight];
546 *p = 0;
547
548 lastcolor = '7';
549
550 ls = NULL;
551 while ( *str ) {
552 if ( len > NOTIFY_WIDTH - 1 || ( *str == '\n' && ( *( str + 1 ) != 0 ) ) ) {
553 if ( ls ) {
554 str -= ( p - ls );
555 str++;
556 p -= ( p - ls );
557 }
558 *p = 0;
559
560 cgs.notifyMsgTimes[cgs.notifyPos % chatHeight] = cg.time;
561
562 cgs.notifyPos++;
563 p = cgs.notifyMsgs[cgs.notifyPos % chatHeight];
564 *p = 0;
565 *p++ = Q_COLOR_ESCAPE;
566 *p++ = lastcolor;
567 len = 0;
568 ls = NULL;
569 }
570
571 if ( Q_IsColorString( str ) ) {
572 *p++ = *str++;
573 lastcolor = *str;
574 *p++ = *str++;
575 continue;
576 }
577 if ( *str == ' ' ) {
578 ls = p;
579 }
580 while ( *str == '\n' ) {
581 // TTimo gcc warning: value computed is not used
582 // was *str++;
583 str++;
584 }
585
586 if ( *str ) {
587 *p++ = *str++;
588 len++;
589 }
590 }
591 *p = 0;
592
593 cgs.notifyMsgTimes[cgs.notifyPos % chatHeight] = cg.time;
594 cgs.notifyPos++;
595
596 if ( cgs.notifyPos - cgs.notifyLastPos > chatHeight ) {
597 cgs.notifyLastPos = cgs.notifyPos - chatHeight;
598 }
599 }
600
601 /*
602 ===============
603 CG_SendMoveSpeed
604 ===============
605 */
CG_SendMoveSpeed(animation_t * animList,int numAnims,char * modelName)606 void CG_SendMoveSpeed( animation_t *animList, int numAnims, char *modelName ) {
607 animation_t *anim;
608 int i;
609 char text[10000];
610
611 if ( !cgs.localServer ) {
612 return;
613 }
614
615 text[0] = 0;
616 Q_strcat( text, sizeof( text ), modelName );
617
618 for ( i = 0, anim = animList; i < numAnims; i++, anim++ ) {
619 if ( anim->moveSpeed <= 0 ) {
620 continue;
621 }
622
623 // add this to the list
624 Q_strcat( text, sizeof( text ), va( " %s %i", anim->name, anim->moveSpeed ) );
625 }
626
627 // send the movespeeds to the server
628 trap_SendMoveSpeedsToGame( 0, text );
629 }
630
631 /*
632 ===============
633 CG_SendMoveSpeeds
634
635 send moveSpeeds for all unique models
636 ===============
637 */
CG_SendMoveSpeeds(void)638 void CG_SendMoveSpeeds( void ) {
639 int i;
640 animModelInfo_t *modelInfo;
641
642 for ( i = 0, modelInfo = cgs.animScriptData.modelInfo; i < MAX_ANIMSCRIPT_MODELS; i++, modelInfo++ ) {
643 if ( !modelInfo->modelname[0] ) {
644 continue;
645 }
646
647 // send this model
648 CG_SendMoveSpeed( modelInfo->animations, modelInfo->numAnimations, modelInfo->modelname );
649 }
650 }
651
652
653 /*
654 ===============
655 CG_MapRestart
656
657 The server has issued a map_restart, so the next snapshot
658 is completely new and should not be interpolated to.
659
660 A tournement restart will clear everything, but doesn't
661 require a reload of all the media
662 ===============
663 */
CG_MapRestart(void)664 static void CG_MapRestart( void ) {
665 int i;
666 if ( cg_showmiss.integer ) {
667 CG_Printf( "CG_MapRestart\n" );
668 }
669
670 memset( &cg.lastWeapSelInBank[0], 0, MAX_WEAP_BANKS * sizeof( int ) ); // clear weapon bank selections
671
672 cg.centerPrintTime = 0; // reset centerprint counter so previous messages don't re-appear
673 cg.itemPickupTime = 0; // reset item pickup counter so previous messages don't re-appear
674 cg.cursorHintFade = 0; // reset cursor hint timer
675
676 // DHM - Nerve :: Reset complaint system
677 cgs.complaintClient = -1;
678 cgs.complaintEndTime = 0;
679
680 // (SA) clear zoom (so no warpies)
681 cg.zoomedBinoc = qfalse;
682 cg.zoomedBinoc = cg.zoomedScope = qfalse;
683 cg.zoomTime = 0;
684 cg.zoomval = 0;
685
686 // reset fog to world fog (if present)
687 trap_R_SetFog( FOG_CMD_SWITCHFOG, FOG_MAP,20,0,0,0,0 );
688
689 CG_InitLocalEntities();
690 CG_InitMarkPolys();
691
692 //Rafael particles
693 CG_ClearParticles();
694 // done.
695
696 for ( i = 1; i < MAX_PARTICLES_AREAS; i++ )
697 {
698 {
699 int rval;
700
701 rval = CG_NewParticleArea( CS_PARTICLES + i );
702 if ( !rval ) {
703 break;
704 }
705 }
706 }
707
708
709 // Ridah, trails
710 // CG_ClearTrails();
711 // done.
712
713 // Ridah
714 CG_ClearFlameChunks();
715 CG_SoundInit();
716 // done.
717
718 // make sure the "3 frags left" warnings play again
719 cg.fraglimitWarnings = 0;
720
721 cg.timelimitWarnings = 0;
722
723 cg.rewardTime = 0;
724 cg.intermissionStarted = qfalse;
725 cg.levelShot = qfalse;
726
727 cgs.voteTime = 0;
728
729 cg.lightstylesInited = qfalse;
730
731 cg.mapRestart = qtrue;
732
733 CG_StartMusic();
734
735 trap_S_ClearLoopingSounds( qtrue );
736
737 cg.latchVictorySound = qfalse; // NERVE - SMF
738 // JPW NERVE -- reset render flags
739 cg_fxflags = 0;
740 // jpw
741
742
743 // we really should clear more parts of cg here and stop sounds
744 cg.v_dmg_time = 0;
745 cg.v_noFireTime = 0;
746 cg.v_fireTime = 0;
747
748 // inform LOCAL server of animationSpeeds for AI use (ONLY)
749 // if ( cgs.localServer ) {
750 // CG_SendMoveSpeeds();
751 // }
752
753 // play the "fight" sound if this is a restart without warmup
754 if ( cg.warmup == 0 && cgs.gametype == GT_TOURNAMENT ) {
755 trap_S_StartLocalSound( cgs.media.countFightSound, CHAN_ANNOUNCER );
756 CG_CenterPrint( "FIGHT!", 120, GIANTCHAR_WIDTH * 2 );
757 }
758 #ifdef MISSIONPACK
759 if ( cg_singlePlayerActive.integer ) {
760 trap_Cvar_Set( "ui_matchStartTime", va( "%i", cg.time ) );
761 if ( cg_recordSPDemo.integer && *cg_recordSPDemoName.string ) {
762 trap_SendConsoleCommand( va( "set g_synchronousclients 1 ; record %s \n", cg_recordSPDemoName.string ) );
763 }
764 }
765 #endif
766 trap_Cvar_Set( "cg_thirdPerson", "0" );
767 }
768
769 /*
770 =================
771 CG_RequestMoveSpeed
772 =================
773 */
CG_RequestMoveSpeed(const char * modelname)774 void CG_RequestMoveSpeed( const char *modelname ) {
775 animModelInfo_t *modelInfo;
776
777 modelInfo = BG_ModelInfoForModelname( (char *)modelname );
778
779 if ( !modelInfo ) {
780 // ignore it
781 return;
782 }
783
784 // send it
785 CG_SendMoveSpeed( modelInfo->animations, modelInfo->numAnimations, (char *)modelname );
786 }
787
788 // NERVE - SMF
789 #define MAX_VOICEFILESIZE 16384
790 #define MAX_VOICEFILES 8
791 #define MAX_VOICECHATS 64
792 #define MAX_VOICESOUNDS 64
793 #define MAX_CHATSIZE 64
794 #define MAX_HEADMODELS 64
795
796 typedef struct voiceChat_s
797 {
798 char id[64];
799 int numSounds;
800 sfxHandle_t sounds[MAX_VOICESOUNDS];
801 char chats[MAX_VOICESOUNDS][MAX_CHATSIZE];
802 qhandle_t sprite[MAX_VOICESOUNDS]; // DHM - Nerve
803 } voiceChat_t;
804
805 typedef struct voiceChatList_s
806 {
807 char name[64];
808 int gender;
809 int numVoiceChats;
810 voiceChat_t voiceChats[MAX_VOICECHATS];
811 } voiceChatList_t;
812
813 typedef struct headModelVoiceChat_s
814 {
815 char headmodel[64];
816 int voiceChatNum;
817 } headModelVoiceChat_t;
818
819 voiceChatList_t voiceChatLists[MAX_VOICEFILES];
820 headModelVoiceChat_t headModelVoiceChat[MAX_HEADMODELS];
821
822 /*
823 =================
824 CG_ParseVoiceChats
825 =================
826 */
CG_ParseVoiceChats(const char * filename,voiceChatList_t * voiceChatList,int maxVoiceChats)827 int CG_ParseVoiceChats( const char *filename, voiceChatList_t *voiceChatList, int maxVoiceChats ) {
828 int len, i;
829 int current = 0;
830 fileHandle_t f;
831 char buf[MAX_VOICEFILESIZE];
832 char **p, *ptr;
833 char *token;
834 voiceChat_t *voiceChats;
835
836 len = trap_FS_FOpenFile( filename, &f, FS_READ );
837 if ( !f ) {
838 trap_Print( va( S_COLOR_RED "voice chat file not found: %s\n", filename ) );
839 return qfalse;
840 }
841 if ( len >= MAX_VOICEFILESIZE ) {
842 trap_Print( va( S_COLOR_RED "voice chat file too large: %s is %i, max allowed is %i\n", filename, len, MAX_VOICEFILESIZE ) );
843 trap_FS_FCloseFile( f );
844 return qfalse;
845 }
846
847 trap_FS_Read( buf, len, f );
848 buf[len] = 0;
849 trap_FS_FCloseFile( f );
850
851 ptr = buf;
852 p = &ptr;
853
854 Com_sprintf( voiceChatList->name, sizeof( voiceChatList->name ), "%s", filename );
855 voiceChats = voiceChatList->voiceChats;
856 for ( i = 0; i < maxVoiceChats; i++ ) {
857 voiceChats[i].id[0] = 0;
858 }
859 token = COM_ParseExt( p, qtrue );
860 if ( !token[0] ) {
861 return qtrue;
862 }
863 if ( !Q_stricmp( token, "female" ) ) {
864 voiceChatList->gender = GENDER_FEMALE;
865 } else if ( !Q_stricmp( token, "male" ) ) {
866 voiceChatList->gender = GENDER_MALE;
867 } else if ( !Q_stricmp( token, "neuter" ) ) {
868 voiceChatList->gender = GENDER_NEUTER;
869 } else {
870 trap_Print( va( S_COLOR_RED "expected gender not found in voice chat file: %s\n", filename ) );
871 return qfalse;
872 }
873
874 voiceChatList->numVoiceChats = 0;
875 while ( 1 ) {
876 token = COM_ParseExt( p, qtrue );
877 if ( !token[0] ) {
878 return qtrue;
879 }
880 Com_sprintf( voiceChats[voiceChatList->numVoiceChats].id, sizeof( voiceChats[voiceChatList->numVoiceChats].id ), "%s", token );
881 token = COM_ParseExt( p, qtrue );
882 if ( Q_stricmp( token, "{" ) ) {
883 trap_Print( va( S_COLOR_RED "expected { found %s in voice chat file: %s\n", token, filename ) );
884 return qfalse;
885 }
886 voiceChats[voiceChatList->numVoiceChats].numSounds = 0;
887 current = voiceChats[voiceChatList->numVoiceChats].numSounds;
888
889 while ( 1 ) {
890 token = COM_ParseExt( p, qtrue );
891 if ( !token[0] ) {
892 return qtrue;
893 }
894 if ( !Q_stricmp( token, "}" ) ) {
895 break;
896 }
897 voiceChats[voiceChatList->numVoiceChats].sounds[current] = trap_S_RegisterSound( token );
898 token = COM_ParseExt( p, qtrue );
899 if ( !token[0] ) {
900 return qtrue;
901 }
902 Com_sprintf( voiceChats[voiceChatList->numVoiceChats].chats[current], MAX_CHATSIZE, "%s", token );
903
904 // DHM - Nerve :: Specify sprite shader to show above player's head
905 token = COM_ParseExt( p, qfalse );
906 if ( !token[0] || !Q_stricmp( token, "}" ) ) {
907 voiceChats[voiceChatList->numVoiceChats].sprite[current] = trap_R_RegisterShader( "sprites/voiceChat" );
908 COM_RestoreParseSession( p );
909 } else {
910 voiceChats[voiceChatList->numVoiceChats].sprite[current] = trap_R_RegisterShader( token );
911 if ( voiceChats[voiceChatList->numVoiceChats].sprite[current] == 0 ) {
912 voiceChats[voiceChatList->numVoiceChats].sprite[current] = trap_R_RegisterShader( "sprites/voiceChat" );
913 }
914 }
915 // dhm - end
916
917 voiceChats[voiceChatList->numVoiceChats].numSounds++;
918 current = voiceChats[voiceChatList->numVoiceChats].numSounds;
919
920 if ( voiceChats[voiceChatList->numVoiceChats].numSounds >= MAX_VOICESOUNDS ) {
921 break;
922 }
923 }
924
925 voiceChatList->numVoiceChats++;
926 if ( voiceChatList->numVoiceChats >= maxVoiceChats ) {
927 return qtrue;
928 }
929 }
930 return qtrue;
931 }
932
933 /*
934 =================
935 CG_LoadVoiceChats
936 =================
937 */
CG_LoadVoiceChats(void)938 void CG_LoadVoiceChats( void ) {
939 int size;
940
941 size = trap_MemoryRemaining();
942 CG_ParseVoiceChats( "scripts/wm_axis_chat.voice", &voiceChatLists[0], MAX_VOICECHATS );
943 CG_ParseVoiceChats( "scripts/wm_allies_chat.voice", &voiceChatLists[1], MAX_VOICECHATS );
944 // CG_ParseVoiceChats( "scripts/female2.voice", &voiceChatLists[1], MAX_VOICECHATS );
945 // CG_ParseVoiceChats( "scripts/female3.voice", &voiceChatLists[2], MAX_VOICECHATS );
946 // CG_ParseVoiceChats( "scripts/male1.voice", &voiceChatLists[3], MAX_VOICECHATS );
947 // CG_ParseVoiceChats( "scripts/male2.voice", &voiceChatLists[4], MAX_VOICECHATS );
948 // CG_ParseVoiceChats( "scripts/male3.voice", &voiceChatLists[5], MAX_VOICECHATS );
949 // CG_ParseVoiceChats( "scripts/male4.voice", &voiceChatLists[6], MAX_VOICECHATS );
950 // CG_ParseVoiceChats( "scripts/male5.voice", &voiceChatLists[7], MAX_VOICECHATS );
951 CG_Printf( "voice chat memory size = %d\n", size - trap_MemoryRemaining() );
952 }
953
954 /*
955 =================
956 CG_HeadModelVoiceChats
957 =================
958 */
CG_HeadModelVoiceChats(char * filename)959 int CG_HeadModelVoiceChats( char *filename ) {
960 int len, i;
961 fileHandle_t f;
962 char buf[MAX_VOICEFILESIZE];
963 char **p, *ptr;
964 char *token;
965
966 len = trap_FS_FOpenFile( filename, &f, FS_READ );
967 if ( !f ) {
968 trap_Print( va( "voice chat file not found: %s\n", filename ) );
969 return -1;
970 }
971 if ( len >= MAX_VOICEFILESIZE ) {
972 trap_Print( va( S_COLOR_RED "voice chat file too large: %s is %i, max allowed is %i\n", filename, len, MAX_VOICEFILESIZE ) );
973 trap_FS_FCloseFile( f );
974 return -1;
975 }
976
977 trap_FS_Read( buf, len, f );
978 buf[len] = 0;
979 trap_FS_FCloseFile( f );
980
981 ptr = buf;
982 p = &ptr;
983
984 token = COM_ParseExt( p, qtrue );
985 if ( !token[0] ) {
986 return -1;
987 }
988
989 for ( i = 0; i < MAX_VOICEFILES; i++ ) {
990 if ( !Q_stricmp( token, voiceChatLists[i].name ) ) {
991 return i;
992 }
993 }
994
995 //FIXME: maybe try to load the .voice file which name is stored in token?
996
997 return -1;
998 }
999
1000
1001 /*
1002 =================
1003 CG_GetVoiceChat
1004 =================
1005 */
CG_GetVoiceChat(voiceChatList_t * voiceChatList,const char * id,sfxHandle_t * snd,qhandle_t * sprite,char ** chat)1006 int CG_GetVoiceChat( voiceChatList_t *voiceChatList, const char *id, sfxHandle_t *snd, qhandle_t *sprite, char **chat ) {
1007 int i, rnd;
1008
1009 for ( i = 0; i < voiceChatList->numVoiceChats; i++ ) {
1010 if ( !Q_stricmp( id, voiceChatList->voiceChats[i].id ) ) {
1011 rnd = random() * voiceChatList->voiceChats[i].numSounds;
1012 *snd = voiceChatList->voiceChats[i].sounds[rnd];
1013 *sprite = voiceChatList->voiceChats[i].sprite[rnd];
1014 *chat = voiceChatList->voiceChats[i].chats[rnd];
1015 return qtrue;
1016 }
1017 }
1018 return qfalse;
1019 }
1020
1021 /*
1022 =================
1023 CG_VoiceChatListForClient
1024 =================
1025 */
CG_VoiceChatListForClient(int clientNum)1026 voiceChatList_t *CG_VoiceChatListForClient( int clientNum ) {
1027 clientInfo_t *ci;
1028 int voiceChatNum, i, j, k, gender;
1029 char filename[128], *headModelName;
1030
1031 // NERVE - SMF
1032 if ( cgs.gametype >= GT_WOLF ) {
1033 if ( cgs.clientinfo[ clientNum ].team == TEAM_RED ) {
1034 return &voiceChatLists[0];
1035 } else {
1036 return &voiceChatLists[1];
1037 }
1038 }
1039 // -NERVE - SMF
1040
1041 if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) {
1042 clientNum = 0;
1043 }
1044 ci = &cgs.clientinfo[ clientNum ];
1045
1046 headModelName = ci->headModelName;
1047 if ( headModelName[0] == '*' ) {
1048 headModelName++;
1049 }
1050 // find the voice file for the head model the client uses
1051 for ( i = 0; i < MAX_HEADMODELS; i++ ) {
1052 if ( !Q_stricmp( headModelVoiceChat[i].headmodel, headModelName ) ) {
1053 break;
1054 }
1055 }
1056 if ( i < MAX_HEADMODELS ) {
1057 return &voiceChatLists[headModelVoiceChat[i].voiceChatNum];
1058 }
1059 // find a <headmodelname>.vc file
1060 for ( i = 0; i < MAX_HEADMODELS; i++ ) {
1061 if ( !strlen( headModelVoiceChat[i].headmodel ) ) {
1062 Com_sprintf( filename, sizeof( filename ), "scripts/%s.vc", headModelName );
1063 voiceChatNum = CG_HeadModelVoiceChats( filename );
1064 if ( voiceChatNum == -1 ) {
1065 break;
1066 }
1067 Com_sprintf( headModelVoiceChat[i].headmodel, sizeof( headModelVoiceChat[i].headmodel ),
1068 "%s", headModelName );
1069 headModelVoiceChat[i].voiceChatNum = voiceChatNum;
1070 return &voiceChatLists[headModelVoiceChat[i].voiceChatNum];
1071 }
1072 }
1073 gender = ci->gender;
1074 for ( k = 0; k < 2; k++ ) {
1075 // just pick the first with the right gender
1076 for ( i = 0; i < MAX_VOICEFILES; i++ ) {
1077 if ( strlen( voiceChatLists[i].name ) ) {
1078 if ( voiceChatLists[i].gender == gender ) {
1079 // store this head model with voice chat for future reference
1080 for ( j = 0; j < MAX_HEADMODELS; j++ ) {
1081 if ( !strlen( headModelVoiceChat[j].headmodel ) ) {
1082 Com_sprintf( headModelVoiceChat[j].headmodel, sizeof( headModelVoiceChat[j].headmodel ),
1083 "%s", headModelName );
1084 headModelVoiceChat[j].voiceChatNum = i;
1085 break;
1086 }
1087 }
1088 return &voiceChatLists[i];
1089 }
1090 }
1091 }
1092 // fall back to male gender because we don't have neuter in the mission pack
1093 if ( gender == GENDER_MALE ) {
1094 break;
1095 }
1096 gender = GENDER_MALE;
1097 }
1098 // store this head model with voice chat for future reference
1099 for ( j = 0; j < MAX_HEADMODELS; j++ ) {
1100 if ( !strlen( headModelVoiceChat[j].headmodel ) ) {
1101 Com_sprintf( headModelVoiceChat[j].headmodel, sizeof( headModelVoiceChat[j].headmodel ),
1102 "%s", headModelName );
1103 headModelVoiceChat[j].voiceChatNum = 0;
1104 break;
1105 }
1106 }
1107 // just return the first voice chat list
1108 return &voiceChatLists[0];
1109 }
1110
1111 #define MAX_VOICECHATBUFFER 32
1112
1113 typedef struct bufferedVoiceChat_s
1114 {
1115 int clientNum;
1116 sfxHandle_t snd;
1117 qhandle_t sprite;
1118 int voiceOnly;
1119 char cmd[MAX_SAY_TEXT];
1120 char message[MAX_SAY_TEXT];
1121 vec3_t origin; // NERVE - SMF
1122 } bufferedVoiceChat_t;
1123
1124 bufferedVoiceChat_t voiceChatBuffer[MAX_VOICECHATBUFFER];
1125
1126 /*
1127 =================
1128 CG_PlayVoiceChat
1129 =================
1130 */
CG_PlayVoiceChat(bufferedVoiceChat_t * vchat)1131 void CG_PlayVoiceChat( bufferedVoiceChat_t *vchat ) {
1132 // if we are going into the intermission, don't start any voices
1133 /* // NERVE - SMF - don't do this in wolfMP
1134 if ( cg.intermissionStarted ) {
1135 return;
1136 }
1137 */
1138
1139 if ( !cg_noVoiceChats.integer ) {
1140 trap_S_StartLocalSound( vchat->snd, CHAN_VOICE );
1141
1142 // DHM - Nerve :: Show icon above head
1143 if ( vchat->clientNum == cg.snap->ps.clientNum ) {
1144 cg.predictedPlayerEntity.voiceChatSprite = vchat->sprite;
1145 if ( vchat->sprite == cgs.media.voiceChatShader ) {
1146 cg.predictedPlayerEntity.voiceChatSpriteTime = cg.time + cg_voiceSpriteTime.integer;
1147 } else {
1148 cg.predictedPlayerEntity.voiceChatSpriteTime = cg.time + cg_voiceSpriteTime.integer * 2;
1149 }
1150 } else {
1151 cg_entities[ vchat->clientNum ].voiceChatSprite = vchat->sprite;
1152 VectorCopy( vchat->origin, cg_entities[ vchat->clientNum ].lerpOrigin ); // NERVE - SMF
1153 if ( vchat->sprite == cgs.media.voiceChatShader ) {
1154 cg_entities[ vchat->clientNum ].voiceChatSpriteTime = cg.time + cg_voiceSpriteTime.integer;
1155 } else {
1156 cg_entities[ vchat->clientNum ].voiceChatSpriteTime = cg.time + cg_voiceSpriteTime.integer * 2;
1157 }
1158 }
1159 // dhm - end
1160
1161 #ifdef MISSIONPACK
1162 if ( vchat->clientNum != cg.snap->ps.clientNum ) {
1163 int orderTask = CG_ValidOrder( vchat->cmd );
1164 if ( orderTask > 0 ) {
1165 cgs.acceptOrderTime = cg.time + 5000;
1166 Q_strncpyz( cgs.acceptVoice, vchat->cmd, sizeof( cgs.acceptVoice ) );
1167 cgs.acceptTask = orderTask;
1168 cgs.acceptLeader = vchat->clientNum;
1169 }
1170 // see if this was an order
1171 CG_ShowResponseHead();
1172 }
1173 #endif
1174 }
1175 if ( !vchat->voiceOnly && !cg_noVoiceText.integer ) {
1176 CG_AddToTeamChat( vchat->message );
1177 CG_Printf( "%s", va( "[skipnotify]: %s\n", vchat->message ) ); // JPW NERVE
1178 }
1179 voiceChatBuffer[cg.voiceChatBufferOut].snd = 0;
1180 }
1181
1182 /*
1183 =====================
1184 CG_PlayBufferedVoieChats
1185 =====================
1186 */
CG_PlayBufferedVoiceChats(void)1187 void CG_PlayBufferedVoiceChats( void ) {
1188 if ( cg.voiceChatTime < cg.time ) {
1189 if ( cg.voiceChatBufferOut != cg.voiceChatBufferIn && voiceChatBuffer[cg.voiceChatBufferOut].snd ) {
1190 //
1191 CG_PlayVoiceChat( &voiceChatBuffer[cg.voiceChatBufferOut] );
1192 //
1193 cg.voiceChatBufferOut = ( cg.voiceChatBufferOut + 1 ) % MAX_VOICECHATBUFFER;
1194 cg.voiceChatTime = cg.time + 1000;
1195 }
1196 }
1197 }
1198
1199 /*
1200 =====================
1201 CG_AddBufferedVoiceChat
1202 =====================
1203 */
CG_AddBufferedVoiceChat(bufferedVoiceChat_t * vchat)1204 void CG_AddBufferedVoiceChat( bufferedVoiceChat_t *vchat ) {
1205 // if we are going into the intermission, don't start any voices
1206 /* // NERVE - SMF - don't do this in wolfMP
1207 if ( cg.intermissionStarted ) {
1208 return;
1209 }
1210 */
1211
1212 // JPW NERVE new system doesn't buffer but overwrites vchats FIXME put this on a cvar to choose which to use
1213 memcpy( &voiceChatBuffer[0],vchat,sizeof( bufferedVoiceChat_t ) );
1214 cg.voiceChatBufferIn = 0;
1215 CG_PlayVoiceChat( &voiceChatBuffer[0] );
1216
1217 /* JPW NERVE pulled this
1218 memcpy(&voiceChatBuffer[cg.voiceChatBufferIn], vchat, sizeof(bufferedVoiceChat_t));
1219 cg.voiceChatBufferIn = (cg.voiceChatBufferIn + 1) % MAX_VOICECHATBUFFER;
1220 if (cg.voiceChatBufferIn == cg.voiceChatBufferOut) {
1221 CG_PlayVoiceChat( &voiceChatBuffer[cg.voiceChatBufferOut] );
1222 cg.voiceChatBufferOut++;
1223 }
1224 */
1225 }
1226
1227 /*
1228 =================
1229 CG_VoiceChatLocal
1230 =================
1231 */
CG_VoiceChatLocal(int mode,qboolean voiceOnly,int clientNum,int color,const char * cmd,vec3_t origin)1232 void CG_VoiceChatLocal( int mode, qboolean voiceOnly, int clientNum, int color, const char *cmd, vec3_t origin ) {
1233 char *chat;
1234 voiceChatList_t *voiceChatList;
1235 clientInfo_t *ci;
1236 sfxHandle_t snd;
1237 qhandle_t sprite;
1238 bufferedVoiceChat_t vchat;
1239 const char *loc; // NERVE - SMF
1240
1241 /* // NERVE - SMF - don't do this in wolfMP
1242 // if we are going into the intermission, don't start any voices
1243 if ( cg.intermissionStarted ) {
1244 return;
1245 }
1246 */
1247
1248 if ( mode == SAY_ALL && cgs.gametype >= GT_TEAM && cg_teamChatsOnly.integer ) {
1249 return;
1250 }
1251
1252 if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) {
1253 clientNum = 0;
1254 }
1255 ci = &cgs.clientinfo[ clientNum ];
1256
1257 cgs.currentVoiceClient = clientNum;
1258
1259 voiceChatList = CG_VoiceChatListForClient( clientNum );
1260
1261 if ( CG_GetVoiceChat( voiceChatList, cmd, &snd, &sprite, &chat ) ) {
1262 vchat.clientNum = clientNum;
1263 vchat.snd = snd;
1264 vchat.sprite = sprite;
1265 vchat.voiceOnly = voiceOnly;
1266 VectorCopy( origin, vchat.origin ); // NERVE - SMF
1267 Q_strncpyz( vchat.cmd, cmd, sizeof( vchat.cmd ) );
1268
1269 // NERVE - SMF - get location
1270 loc = CG_ConfigString( CS_LOCATIONS + ci->location );
1271 if ( !loc || !*loc ) {
1272 loc = " ";
1273 }
1274 // -NERVE - SMF
1275
1276 if ( mode == SAY_TELL ) {
1277 Com_sprintf( vchat.message, sizeof( vchat.message ), "[%s]%c%c[%s]: %c%c%s",
1278 ci->name, Q_COLOR_ESCAPE, COLOR_YELLOW, CG_TranslateString( loc ), Q_COLOR_ESCAPE, color, CG_TranslateString( chat ) );
1279 } else if ( mode == SAY_TEAM ) {
1280 Com_sprintf( vchat.message, sizeof( vchat.message ), "(%s)%c%c(%s): %c%c%s",
1281 ci->name, Q_COLOR_ESCAPE, COLOR_YELLOW, CG_TranslateString( loc ), Q_COLOR_ESCAPE, color, CG_TranslateString( chat ) );
1282 } else {
1283 Com_sprintf( vchat.message, sizeof( vchat.message ), "%s %c%c(%s): %c%c%s",
1284 ci->name, Q_COLOR_ESCAPE, COLOR_YELLOW, CG_TranslateString( loc ), Q_COLOR_ESCAPE, color, CG_TranslateString( chat ) );
1285 }
1286 CG_AddBufferedVoiceChat( &vchat );
1287 }
1288 }
1289
1290 /*
1291 =================
1292 CG_VoiceChat
1293 =================
1294 */
CG_VoiceChat(int mode)1295 void CG_VoiceChat( int mode ) {
1296 const char *cmd;
1297 int clientNum, color;
1298 qboolean voiceOnly;
1299 vec3_t origin; // NERVE - SMF
1300
1301 voiceOnly = atoi( CG_Argv( 1 ) );
1302 clientNum = atoi( CG_Argv( 2 ) );
1303 color = atoi( CG_Argv( 3 ) );
1304
1305 // NERVE - SMF - added origin
1306 origin[0] = atoi( CG_Argv( 5 ) );
1307 origin[1] = atoi( CG_Argv( 6 ) );
1308 origin[2] = atoi( CG_Argv( 7 ) );
1309
1310 cmd = CG_Argv( 4 );
1311
1312 if ( cg_noTaunt.integer != 0 ) {
1313 if ( !strcmp( cmd, VOICECHAT_KILLINSULT ) || !strcmp( cmd, VOICECHAT_TAUNT ) || \
1314 !strcmp( cmd, VOICECHAT_DEATHINSULT ) || !strcmp( cmd, VOICECHAT_KILLGAUNTLET ) || \
1315 !strcmp( cmd, VOICECHAT_PRAISE ) ) {
1316 return;
1317 }
1318 }
1319
1320 CG_VoiceChatLocal( mode, voiceOnly, clientNum, color, cmd, origin );
1321 }
1322 // -NERVE - SMF
1323
1324 /*
1325 =================
1326 CG_RemoveChatEscapeChar
1327 =================
1328 */
CG_RemoveChatEscapeChar(char * text)1329 static void CG_RemoveChatEscapeChar( char *text ) {
1330 int i, l;
1331
1332 l = 0;
1333 for ( i = 0; text[i]; i++ ) {
1334 if ( text[i] == '\x19' ) {
1335 continue;
1336 }
1337 text[l++] = text[i];
1338 }
1339 text[l] = '\0';
1340 }
1341
1342 /*
1343 =================
1344 CG_LocalizeServerCommand
1345
1346 NERVE - SMF - localize string sent from server
1347
1348 - localization is ON by default.
1349 - use [lof] in string to turn OFF
1350 - use [lon] in string to turn back ON
1351 =================
1352 */
CG_LocalizeServerCommand(const char * buf)1353 const char* CG_LocalizeServerCommand( const char *buf ) {
1354 static char token[MAX_TOKEN_CHARS];
1355 char temp[MAX_TOKEN_CHARS];
1356 qboolean togloc = qtrue;
1357 const char *s;
1358 int i, prev;
1359
1360 memset( token, 0, sizeof( token ) );
1361 s = buf;
1362 prev = 0;
1363
1364 for ( i = 0; *s; i++, s++ ) {
1365 // TTimo:
1366 // line was: if ( *s == '[' && !Q_strncmp( s, "[lon]", 5 ) || !Q_strncmp( s, "[lof]", 5 ) ) {
1367 // || prevails on &&, gcc warning was 'suggest parentheses around && within ||'
1368 // modified to the correct behaviour
1369 if ( *s == '[' && ( !Q_strncmp( s, "[lon]", 5 ) || !Q_strncmp( s, "[lof]", 5 ) ) ) {
1370
1371 if ( togloc ) {
1372 memset( temp, 0, sizeof( temp ) );
1373 strncpy( temp, buf + prev, i - prev );
1374 strcat( token, CG_TranslateString( temp ) );
1375 } else {
1376 strncat( token, buf + prev, i - prev );
1377 }
1378
1379 if ( s[3] == 'n' ) {
1380 togloc = qtrue;
1381 } else {
1382 togloc = qfalse;
1383 }
1384
1385 i += 5;
1386 s += 5;
1387 prev = i;
1388 }
1389 }
1390
1391 if ( togloc ) {
1392 memset( temp, 0, sizeof( temp ) );
1393 strncpy( temp, buf + prev, i - prev );
1394 strcat( token, CG_TranslateString( temp ) );
1395 } else {
1396 strncat( token, buf + prev, i - prev );
1397 }
1398
1399 return token;
1400 }
1401 // -NERVE - SMF
1402
1403 /*
1404 =================
1405 CG_ServerCommand
1406
1407 The string has been tokenized and can be retrieved with
1408 Cmd_Argc() / Cmd_Argv()
1409 =================
1410 */
CG_ServerCommand(void)1411 static void CG_ServerCommand( void ) {
1412 const char *cmd;
1413 char text[MAX_SAY_TEXT];
1414
1415 cmd = CG_Argv( 0 );
1416
1417 if ( !cmd[0] ) {
1418 // server claimed the command
1419 return;
1420 }
1421
1422 // if ( !strcmp( cmd, "startCam" ) ) {
1423 // CG_StartCamera( CG_Argv( 1 ), atoi( CG_Argv( 2 ) ) );
1424 // return;
1425 // }
1426
1427 if ( !strcmp( cmd, "mvspd" ) ) {
1428 CG_RequestMoveSpeed( CG_Argv( 1 ) );
1429 return;
1430 }
1431
1432 if ( !strcmp( cmd, "tinfo" ) ) {
1433 CG_ParseTeamInfo();
1434 return;
1435 }
1436
1437 if ( !strcmp( cmd, "scores" ) ) {
1438 CG_ParseScores();
1439 return;
1440 }
1441
1442 if ( !strcmp( cmd, "cp" ) ) {
1443 // NERVE - SMF
1444 int args = trap_Argc();
1445 char *s;
1446
1447 if ( args >= 3 ) {
1448 s = CG_TranslateString( CG_Argv( 1 ) );
1449
1450 if ( args == 4 ) {
1451 s = va( "%s%s", CG_Argv( 3 ), s );
1452 }
1453
1454 CG_PriorityCenterPrint( s, SCREEN_HEIGHT - ( SCREEN_HEIGHT * 0.25 ), SMALLCHAR_WIDTH, atoi( CG_Argv( 2 ) ) );
1455 } else {
1456 CG_CenterPrint( CG_LocalizeServerCommand( CG_Argv( 1 ) ), SCREEN_HEIGHT - ( SCREEN_HEIGHT * 0.25 ), SMALLCHAR_WIDTH ); //----(SA) modified
1457 }
1458 return;
1459 }
1460
1461 if ( !strcmp( cmd, "cs" ) ) {
1462 CG_ConfigStringModified();
1463 return;
1464 }
1465
1466 // NERVE - SMF
1467 if ( !strcmp( cmd, "shake" ) ) {
1468 CG_StartShakeCamera( atof( CG_Argv( 1 ) ) );
1469 return;
1470 }
1471 // -NERVE - SMF
1472
1473 if ( !strcmp( cmd, "print" ) ) {
1474 CG_Printf( "[cgnotify]%s", CG_LocalizeServerCommand( CG_Argv( 1 ) ) );
1475 #ifdef MISSIONPACK
1476 cmd = CG_Argv( 1 ); // yes, this is obviously a hack, but so is the way we hear about
1477 // votes passing or failing
1478 if ( !Q_stricmpn( cmd, "vote failed", 11 ) || !Q_stricmpn( cmd, "team vote failed", 16 ) ) {
1479 trap_S_StartLocalSound( cgs.media.voteFailed, CHAN_ANNOUNCER );
1480 } else if ( !Q_stricmpn( cmd, "vote passed", 11 ) || !Q_stricmpn( cmd, "team vote passed", 16 ) ) {
1481 trap_S_StartLocalSound( cgs.media.votePassed, CHAN_ANNOUNCER );
1482 }
1483 #endif
1484 return;
1485 }
1486
1487 if ( !strcmp( cmd, "chat" ) ) {
1488 const char *s;
1489
1490 if ( cgs.gametype >= GT_TEAM && cg_teamChatsOnly.integer ) {
1491 return;
1492 }
1493
1494 if ( atoi( CG_Argv( 2 ) ) ) {
1495 s = CG_LocalizeServerCommand( CG_Argv( 1 ) );
1496 } else {
1497 s = CG_Argv( 1 );
1498 }
1499
1500 trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND );
1501 Q_strncpyz( text, s, MAX_SAY_TEXT );
1502 CG_RemoveChatEscapeChar( text );
1503 CG_AddToTeamChat( text ); // JPW NERVE
1504 CG_Printf( "[skipnotify]%s\n", text ); // JPW NERVE
1505
1506 // NERVE - SMF - we also want this to display in limbo chat
1507 if ( cgs.gametype >= GT_WOLF ) {
1508 trap_UI_LimboChat( text );
1509 }
1510
1511 return;
1512 }
1513
1514 if ( !strcmp( cmd, "tchat" ) ) {
1515 const char *s;
1516
1517 if ( atoi( CG_Argv( 2 ) ) ) {
1518 s = CG_LocalizeServerCommand( CG_Argv( 1 ) );
1519 } else {
1520 s = CG_Argv( 1 );
1521 }
1522
1523 trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND );
1524 Q_strncpyz( text, s, MAX_SAY_TEXT );
1525 CG_RemoveChatEscapeChar( text );
1526 CG_AddToTeamChat( text );
1527 CG_Printf( "[skipnotify]%s\n", text ); // JPW NERVE
1528
1529 // NERVE - SMF - we also want this to display in limbo chat
1530 if ( cgs.gametype >= GT_WOLF ) {
1531 trap_UI_LimboChat( text );
1532 }
1533
1534 return;
1535 }
1536
1537 if ( !strcmp( cmd, "vchat" ) ) {
1538 CG_VoiceChat( SAY_ALL ); // NERVE - SMF - enabled support
1539 return;
1540 }
1541
1542 if ( !strcmp( cmd, "vtchat" ) ) {
1543 CG_VoiceChat( SAY_TEAM ); // NERVE - SMF - enabled support
1544 return;
1545 }
1546
1547 if ( !strcmp( cmd, "vtell" ) ) {
1548 CG_VoiceChat( SAY_TELL ); // NERVE - SMF - enabled support
1549 return;
1550 }
1551
1552 // NERVE - SMF - limbo chat
1553 if ( !strcmp( cmd, "lchat" ) ) {
1554 trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND );
1555 Q_strncpyz( text, CG_Argv( 1 ), MAX_SAY_TEXT );
1556 CG_RemoveChatEscapeChar( text );
1557 trap_UI_LimboChat( text );
1558 CG_Printf( "[skipnotify]%s\n", text ); // JPW NERVE
1559 return;
1560 }
1561 // -NERVE - SMF
1562
1563 // NERVE - SMF
1564 if ( !Q_stricmp( cmd, "oid" ) ) {
1565 CG_ObjectivePrint( CG_Argv( 1 ), SMALLCHAR_WIDTH );
1566 return;
1567 }
1568 // -NERVE - SMF
1569
1570 // DHM - Nerve :: Allow client to lodge a complaing
1571 if ( !Q_stricmp( cmd, "complaint" ) ) {
1572 cgs.complaintEndTime = cg.time + 20000;
1573 cgs.complaintClient = atoi( CG_Argv( 1 ) );
1574
1575 if ( cgs.complaintClient < 0 ) {
1576 cgs.complaintEndTime = cg.time + 10000;
1577 }
1578
1579 return;
1580 }
1581 // dhm
1582
1583 if ( !strcmp( cmd, "map_restart" ) ) {
1584 CG_MapRestart();
1585 return;
1586 }
1587
1588 // if ( !strcmp( cmd, "startCam" ) ) {
1589 // CG_StartCamera( CG_Argv( 1 ), atoi( CG_Argv( 2 ) ) );
1590 // return;
1591 // }
1592
1593 if ( !strcmp( cmd, "mvspd" ) ) {
1594 CG_RequestMoveSpeed( CG_Argv( 1 ) );
1595 return;
1596 }
1597
1598 if ( Q_stricmp (cmd, "remapShader") == 0 )
1599 {
1600 if (trap_Argc() == 4)
1601 {
1602 char shader1[MAX_QPATH];
1603 char shader2[MAX_QPATH];
1604 char shader3[MAX_QPATH];
1605
1606 Q_strncpyz(shader1, CG_Argv(1), sizeof(shader1));
1607 Q_strncpyz(shader2, CG_Argv(2), sizeof(shader2));
1608 Q_strncpyz(shader3, CG_Argv(3), sizeof(shader3));
1609
1610 trap_R_RemapShader(shader1, shader2, shader3);
1611 }
1612
1613 return;
1614 }
1615
1616 // loaddeferred can be both a servercmd and a consolecmd
1617 if ( !strcmp( cmd, "loaddeferred" ) ) { // spelling fixed (SA)
1618 CG_LoadDeferredPlayers();
1619 return;
1620 }
1621
1622 // clientLevelShot is sent before taking a special screenshot for
1623 // the menu system during development
1624 if ( !strcmp( cmd, "clientLevelShot" ) ) {
1625 cg.levelShot = qtrue;
1626 return;
1627 }
1628
1629 CG_Printf( "Unknown client game command: %s\n", cmd );
1630 }
1631
1632
1633 /*
1634 ====================
1635 CG_ExecuteNewServerCommands
1636
1637 Execute all of the server commands that were received along
1638 with this this snapshot.
1639 ====================
1640 */
CG_ExecuteNewServerCommands(int latestSequence)1641 void CG_ExecuteNewServerCommands( int latestSequence ) {
1642 while ( cgs.serverCommandSequence < latestSequence ) {
1643 if ( trap_GetServerCommand( ++cgs.serverCommandSequence ) ) {
1644 CG_ServerCommand();
1645 }
1646 }
1647 }
1648