PageRenderTime 62ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/codemp/cgame/cg_servercmds.c

https://github.com/Stoiss/jaMME
C | 1840 lines | 1390 code | 280 blank | 170 comment | 385 complexity | a77039e49a668af86d705d0a01ea5790 MD5 | raw file
Possible License(s): GPL-2.0
  1. // Copyright (C) 1999-2000 Id Software, Inc.
  2. //
  3. // cg_servercmds.c -- reliably sequenced text commands sent by the server
  4. // these are processed at snapshot transition time, so there will definately
  5. // be a valid snapshot this frame
  6. #include "cg_local.h"
  7. #include "ui/menudef.h"
  8. #include "cg_lights.h"
  9. #include "ghoul2/G2.h"
  10. #include "ui/ui_public.h"
  11. #define SSF_GRAPPLE_SWING (1<<0)
  12. #define SSF_SCOREBOARD_LARGE (1<<1)
  13. #define SSF_SCOREBOARD_KD (1<<2)
  14. #define SSF_CHAT_FILTERS (1<<3)
  15. #define SSF_FIXED_WEAP_ANIMS (1<<4)
  16. #define SSF_MERC_FLAMETHOWER (1<<5)
  17. #define JAPLUS_SERVER_FLAGS (SSF_GRAPPLE_SWING|SSF_SCOREBOARD_KD|SSF_MERC_FLAMETHOWER)
  18. /*
  19. =================
  20. CG_ParseScores
  21. =================
  22. */
  23. qboolean Server_Supports( unsigned int supportFlag )
  24. {
  25. return !!(cg.japlus.SSF & supportFlag );
  26. }
  27. static ID_INLINE int GetScoreOffset( void ) {
  28. return Server_Supports( SSF_SCOREBOARD_KD ) ? 15 : 14;
  29. }
  30. static void CG_ParseScores( void ) {
  31. if (cg.japlus.detected && cg_japlusFix.integer) {
  32. int i=0, scoreIndex=0, powerups=0, readScores=0;
  33. int scoreOffset = GetScoreOffset();
  34. if ( Server_Supports( SSF_SCOREBOARD_LARGE ) )
  35. readScores = Com_Clampi( 0, MAX_CLIENTS, atoi( CG_Argv( 1 ) ) );
  36. else
  37. readScores = Com_Clampi( 0, MAX_CLIENT_SCORE_SEND, atoi( CG_Argv( 1 ) ) );
  38. cg.numScores = readScores;
  39. cg.teamScores[0] = atoi( CG_Argv( 2 ) );
  40. cg.teamScores[1] = atoi( CG_Argv( 3 ) );
  41. memset( cg.scores, 0, sizeof( cg.scores ) );
  42. for ( i=0, scoreIndex=0; i<readScores; i++ )
  43. {
  44. cg.scores[scoreIndex].client = atoi( CG_Argv( i * scoreOffset + 4 ) );
  45. if ( cg.scores[scoreIndex].client < 0 || cg.scores[scoreIndex].client >= MAX_CLIENTS )
  46. continue;
  47. cg.scores[scoreIndex].score = atoi( CG_Argv( i * scoreOffset + 5 ) );
  48. cg.scores[scoreIndex].ping = atoi( CG_Argv( i * scoreOffset + 6 ) );
  49. cg.scores[scoreIndex].time = atoi( CG_Argv( i * scoreOffset + 7 ) );
  50. cg.scores[scoreIndex].scoreFlags = atoi( CG_Argv( i * scoreOffset + 8 ) );
  51. powerups = atoi( CG_Argv( i * scoreOffset + 9 ) );
  52. cg.scores[scoreIndex].accuracy = atoi( CG_Argv( i * scoreOffset + 10 ) );
  53. cg.scores[scoreIndex].impressiveCount = atoi( CG_Argv( i * scoreOffset + 11 ) );
  54. cg.scores[scoreIndex].excellentCount = atoi( CG_Argv( i * scoreOffset + 12 ) );
  55. cg.scores[scoreIndex].guantletCount = atoi( CG_Argv( i * scoreOffset + 13 ) );
  56. cg.scores[scoreIndex].defendCount = atoi( CG_Argv( i * scoreOffset + 14 ) );
  57. cg.scores[scoreIndex].assistCount = atoi( CG_Argv( i * scoreOffset + 15 ) );
  58. cg.scores[scoreIndex].perfect = atoi( CG_Argv( i * scoreOffset + 16 ) );
  59. cg.scores[scoreIndex].captures = atoi( CG_Argv( i * scoreOffset + 17 ) );
  60. if ( Server_Supports( SSF_SCOREBOARD_KD ) )
  61. cg.scores[scoreIndex].deaths = atoi( CG_Argv( i * scoreOffset + 18 ) );
  62. cgs.clientinfo[ cg.scores[scoreIndex].client ].score = cg.scores[scoreIndex].score;
  63. cgs.clientinfo[ cg.scores[scoreIndex].client ].powerups = powerups;
  64. cg.scores[scoreIndex].team = cgs.clientinfo[ cg.scores[scoreIndex].client ].team;
  65. scoreIndex++;
  66. }
  67. CG_SetScoreSelection( NULL );
  68. } else {
  69. int i, powerups, readScores;
  70. cg.numScores = atoi( CG_Argv( 1 ) );
  71. readScores = cg.numScores;
  72. if (readScores > MAX_CLIENT_SCORE_SEND)
  73. {
  74. readScores = MAX_CLIENT_SCORE_SEND;
  75. }
  76. if ( cg.numScores > MAX_CLIENTS ) {
  77. cg.numScores = MAX_CLIENTS;
  78. }
  79. cg.numScores = readScores;
  80. cg.teamScores[0] = atoi( CG_Argv( 2 ) );
  81. cg.teamScores[1] = atoi( CG_Argv( 3 ) );
  82. memset( cg.scores, 0, sizeof( cg.scores ) );
  83. for ( i = 0 ; i < readScores ; i++ ) {
  84. //
  85. cg.scores[i].client = atoi( CG_Argv( i * 14 + 4 ) );
  86. cg.scores[i].score = atoi( CG_Argv( i * 14 + 5 ) );
  87. cg.scores[i].ping = atoi( CG_Argv( i * 14 + 6 ) );
  88. cg.scores[i].time = atoi( CG_Argv( i * 14 + 7 ) );
  89. cg.scores[i].scoreFlags = atoi( CG_Argv( i * 14 + 8 ) );
  90. powerups = atoi( CG_Argv( i * 14 + 9 ) );
  91. cg.scores[i].accuracy = atoi(CG_Argv(i * 14 + 10));
  92. cg.scores[i].impressiveCount = atoi(CG_Argv(i * 14 + 11));
  93. cg.scores[i].excellentCount = atoi(CG_Argv(i * 14 + 12));
  94. cg.scores[i].guantletCount = atoi(CG_Argv(i * 14 + 13));
  95. cg.scores[i].defendCount = atoi(CG_Argv(i * 14 + 14));
  96. cg.scores[i].assistCount = atoi(CG_Argv(i * 14 + 15));
  97. cg.scores[i].perfect = atoi(CG_Argv(i * 14 + 16));
  98. cg.scores[i].captures = atoi(CG_Argv(i * 14 + 17));
  99. if ( cg.scores[i].client < 0 || cg.scores[i].client >= MAX_CLIENTS ) {
  100. cg.scores[i].client = 0;
  101. }
  102. cgs.clientinfo[ cg.scores[i].client ].score = cg.scores[i].score;
  103. cgs.clientinfo[ cg.scores[i].client ].powerups = powerups;
  104. cg.scores[i].team = cgs.clientinfo[cg.scores[i].client].team;
  105. }
  106. CG_SetScoreSelection(NULL);
  107. }
  108. }
  109. /*
  110. =================
  111. CG_ParseTeamInfo
  112. =================
  113. */
  114. static void CG_ParseTeamInfo( void ) {
  115. int i;
  116. int client;
  117. numSortedTeamPlayers = atoi( CG_Argv( 1 ) );
  118. if( numSortedTeamPlayers < 0 || numSortedTeamPlayers > TEAM_MAXOVERLAY )
  119. {
  120. CG_Error( "CG_ParseTeamInfo: numSortedTeamPlayers out of range (%d)", numSortedTeamPlayers );
  121. return;
  122. }
  123. for ( i = 0 ; i < numSortedTeamPlayers ; i++ ) {
  124. client = atoi( CG_Argv( i * 6 + 2 ) );
  125. if( client < 0 || client >= MAX_CLIENTS )
  126. {
  127. CG_Error( "CG_ParseTeamInfo: bad client number: %d", client );
  128. return;
  129. }
  130. sortedTeamPlayers[i] = client;
  131. cgs.clientinfo[ client ].location = atoi( CG_Argv( i * 6 + 3 ) );
  132. cgs.clientinfo[ client ].health = atoi( CG_Argv( i * 6 + 4 ) );
  133. cgs.clientinfo[ client ].armor = atoi( CG_Argv( i * 6 + 5 ) );
  134. cgs.clientinfo[ client ].curWeapon = atoi( CG_Argv( i * 6 + 6 ) );
  135. cgs.clientinfo[ client ].powerups = atoi( CG_Argv( i * 6 + 7 ) );
  136. }
  137. }
  138. /*
  139. ================
  140. CG_ParseServerinfo
  141. This is called explicitly when the gamestate is first received,
  142. and whenever the server updates any serverinfo flagged cvars
  143. ================
  144. */
  145. void CG_ParseServerinfo( void ) {
  146. const char *info = NULL, *tinfo = NULL;
  147. char *mapname;
  148. int i;
  149. info = CG_ConfigString( CS_SERVERINFO );
  150. cgs.debugMelee = atoi( Info_ValueForKey( info, "g_debugMelee" ) ); //trap_Cvar_GetHiddenVarValue("g_iknowkungfu");
  151. cgs.stepSlideFix = atoi( Info_ValueForKey( info, "g_stepSlideFix" ) );
  152. cgs.noSpecMove = atoi( Info_ValueForKey( info, "g_noSpecMove" ) );
  153. trap_Cvar_Set("bg_fighterAltControl", Info_ValueForKey( info, "bg_fighterAltControl" ));
  154. cgs.siegeTeamSwitch = atoi( Info_ValueForKey( info, "g_siegeTeamSwitch" ) );
  155. cgs.showDuelHealths = atoi( Info_ValueForKey( info, "g_showDuelHealths" ) );
  156. cgs.gametype = atoi( Info_ValueForKey( info, "g_gametype" ) );
  157. trap_Cvar_Set("g_gametype", va("%i", cgs.gametype));
  158. cgs.needpass = atoi( Info_ValueForKey( info, "needpass" ) );
  159. cgs.jediVmerc = atoi( Info_ValueForKey( info, "g_jediVmerc" ) );
  160. cgs.wDisable = atoi( Info_ValueForKey( info, "wdisable" ) );
  161. cgs.fDisable = atoi( Info_ValueForKey( info, "fdisable" ) );
  162. cgs.dmflags = atoi( Info_ValueForKey( info, "dmflags" ) );
  163. cgs.duel_fraglimit = atoi( Info_ValueForKey( info, "duel_fraglimit" ) );
  164. cgs.capturelimit = atoi( Info_ValueForKey( info, "capturelimit" ) );
  165. // reset fraglimit warnings
  166. i = atoi( Info_ValueForKey( info, "fraglimit" ) );
  167. if ( cgs.fraglimit < i )
  168. cg.fraglimitWarnings &= ~(1|2|4);
  169. cgs.fraglimit = i;
  170. // reset timelimit warnings
  171. i = atoi( Info_ValueForKey( info, "timelimit" ) );
  172. if ( cgs.timelimit != i )
  173. cg.timelimitWarnings &= ~(1|2);
  174. cgs.timelimit = i;
  175. cgs.maxclients = Com_Clampi( 0, MAX_CLIENTS, atoi( Info_ValueForKey( info, "sv_maxclients" ) ) );
  176. cg.japlus.detected = qfalse;
  177. CG_Printf("\n");
  178. if (!Q_stricmpn(Info_ValueForKey(info, "gamename"), "JA+ Mod", 7)
  179. || !Q_stricmpn(Info_ValueForKey(info, "gamename"), "^4U^3A^5Galaxy", 14)) { //uag :s
  180. cg.japlus.detected = qtrue;
  181. cg.japlus.SSF = JAPLUS_SERVER_FLAGS;
  182. CG_Printf("JA+ demo detected\n");
  183. Com_Printf("Server support hints: 0x%X\n", cg.japlus.SSF);
  184. } else if (!Q_stricmpn(Info_ValueForKey(info, "gamename"), "MakerMod", 8)) {
  185. CG_Printf("MakerMod demo detected\n");
  186. } else if (!Q_stricmpn(Info_ValueForKey(info, "gamename"), "Lugormod", 8)) {
  187. CG_Printf("Lugormod demo detected\n");
  188. } else {
  189. CG_Printf("Base/Unknown demo detected\n");
  190. }
  191. CG_Printf( "\n" );
  192. mapname = Info_ValueForKey( info, "mapname" );
  193. //rww - You must do this one here, Info_ValueForKey always uses the same memory pointer.
  194. trap_Cvar_Set ( "ui_about_mapname", mapname );
  195. Com_sprintf( cgs.mapname, sizeof( cgs.mapname ), "maps/%s.bsp", mapname );
  196. // Q_strncpyz( cgs.redTeam, Info_ValueForKey( info, "g_redTeam" ), sizeof(cgs.redTeam) );
  197. // trap_Cvar_Set("g_redTeam", cgs.redTeam);
  198. // Q_strncpyz( cgs.blueTeam, Info_ValueForKey( info, "g_blueTeam" ), sizeof(cgs.blueTeam) );
  199. // trap_Cvar_Set("g_blueTeam", cgs.blueTeam);
  200. trap_Cvar_Set ( "ui_about_gametype", va("%i", cgs.gametype ) );
  201. trap_Cvar_Set ( "ui_about_fraglimit", va("%i", cgs.fraglimit ) );
  202. trap_Cvar_Set ( "ui_about_duellimit", va("%i", cgs.duel_fraglimit ) );
  203. trap_Cvar_Set ( "ui_about_capturelimit", va("%i", cgs.capturelimit ) );
  204. trap_Cvar_Set ( "ui_about_timelimit", va("%i", cgs.timelimit ) );
  205. trap_Cvar_Set ( "ui_about_maxclients", va("%i", cgs.maxclients ) );
  206. trap_Cvar_Set ( "ui_about_dmflags", va("%i", cgs.dmflags ) );
  207. trap_Cvar_Set ( "ui_about_hostname", Info_ValueForKey( info, "sv_hostname" ) );
  208. trap_Cvar_Set ( "ui_about_needpass", Info_ValueForKey( info, "g_needpass" ) );
  209. trap_Cvar_Set ( "ui_about_botminplayers", Info_ValueForKey ( info, "bot_minplayers" ) );
  210. //Set the siege teams based on what the server has for overrides.
  211. trap_Cvar_Set("cg_siegeTeam1", Info_ValueForKey(info, "g_siegeTeam1"));
  212. trap_Cvar_Set("cg_siegeTeam2", Info_ValueForKey(info, "g_siegeTeam2"));
  213. tinfo = CG_ConfigString( CS_TERRAINS + 1 );
  214. if ( !tinfo || !*tinfo )
  215. {
  216. cg.mInRMG = qfalse;
  217. }
  218. else
  219. {
  220. int weather = 0;
  221. cg.mInRMG = qtrue;
  222. trap_Cvar_Set("RMG", "1");
  223. weather = atoi( Info_ValueForKey( info, "RMG_weather" ) );
  224. trap_Cvar_Set("RMG_weather", va("%i", weather));
  225. if (weather == 1 || weather == 2)
  226. {
  227. cg.mRMGWeather = qtrue;
  228. }
  229. else
  230. {
  231. cg.mRMGWeather = qfalse;
  232. }
  233. }
  234. //Raz: Fix bogus vote strings
  235. Q_strncpyz( cgs.voteString, CG_ConfigString( CS_VOTE_STRING ), sizeof( cgs.voteString ) );
  236. //Raz: Synchronise our expected snaps/sec with the server's framerate
  237. // OpenJK servers will try to match us to the sv_fps too (sv_client.cpp -> SV_UserinfoChanged)
  238. i = atoi( Info_ValueForKey( info, "sv_fps" ) );
  239. if ( i )
  240. trap_Cvar_Set( "snaps", va( "%i", i ) );
  241. }
  242. /*
  243. ==================
  244. CG_ParseWarmup
  245. ==================
  246. */
  247. static void CG_ParseWarmup( void ) {
  248. const char *info;
  249. int warmup;
  250. info = CG_ConfigString( CS_WARMUP );
  251. warmup = atoi( info );
  252. cg.warmupCount = -1;
  253. cg.warmup = warmup;
  254. }
  255. //Raz: This is a reverse map of flag statuses as seen in g_team.c
  256. //static char ctfFlagStatusRemap[] = { '0', '1', '*', '*', '2' };
  257. static char ctfFlagStatusRemap[] = {
  258. FLAG_ATBASE,
  259. FLAG_TAKEN, // CTF
  260. // server doesn't use FLAG_TAKEN_RED or FLAG_TAKEN_BLUE
  261. // which was originally for 1-flag CTF.
  262. FLAG_DROPPED
  263. };
  264. /*
  265. ================
  266. CG_SetConfigValues
  267. Called on load to set the initial values from configure strings
  268. ================
  269. */
  270. void CG_SetConfigValues( void ) {
  271. const char *s;
  272. const char *str;
  273. cgs.scores1 = atoi( CG_ConfigString( CS_SCORES1 ) );
  274. cgs.scores2 = atoi( CG_ConfigString( CS_SCORES2 ) );
  275. cgs.levelStartTime = atoi( CG_ConfigString( CS_LEVEL_START_TIME ) );
  276. if( cgs.gametype == GT_CTF || cgs.gametype == GT_CTY ) {
  277. int redflagId = 0, blueflagId = 0;
  278. s = CG_ConfigString( CS_FLAGSTATUS );
  279. redflagId = s[0] - '0';
  280. blueflagId = s[1] - '0';
  281. // fix: proper flag statuses mapping for dropped flag
  282. if ( redflagId >= 0 && redflagId < ARRAY_LEN( ctfFlagStatusRemap ) )
  283. cgs.redflag = ctfFlagStatusRemap[redflagId];
  284. if ( blueflagId >= 0 && blueflagId < ARRAY_LEN( ctfFlagStatusRemap ) )
  285. cgs.blueflag = ctfFlagStatusRemap[blueflagId];
  286. }
  287. cg.warmup = atoi( CG_ConfigString( CS_WARMUP ) );
  288. // Track who the jedi master is
  289. cgs.jediMaster = atoi ( CG_ConfigString ( CS_CLIENT_JEDIMASTER ) );
  290. cgs.duelWinner = atoi ( CG_ConfigString ( CS_CLIENT_DUELWINNER ) );
  291. str = CG_ConfigString(CS_CLIENT_DUELISTS);
  292. if (str && str[0])
  293. {
  294. char buf[64];
  295. int c = 0;
  296. int i = 0;
  297. while (str[i] && str[i] != '|')
  298. {
  299. buf[c] = str[i];
  300. c++;
  301. i++;
  302. }
  303. buf[c] = 0;
  304. cgs.duelist1 = atoi ( buf );
  305. c = 0;
  306. i++;
  307. while (str[i])
  308. {
  309. buf[c] = str[i];
  310. c++;
  311. i++;
  312. }
  313. buf[c] = 0;
  314. cgs.duelist2 = atoi ( buf );
  315. }
  316. }
  317. /*
  318. =====================
  319. CG_ShaderStateChanged
  320. =====================
  321. */
  322. void CG_ShaderStateChanged(void) {
  323. char originalShader[MAX_QPATH];
  324. char newShader[MAX_QPATH];
  325. char timeOffset[16];
  326. const char *o;
  327. char *n,*t;
  328. o = CG_ConfigString( CS_SHADERSTATE );
  329. while (o && *o) {
  330. n = strstr(o, "=");
  331. if (n && *n) {
  332. strncpy(originalShader, o, n-o);
  333. originalShader[n-o] = 0;
  334. n++;
  335. t = strstr(n, ":");
  336. if (t && *t) {
  337. strncpy(newShader, n, t-n);
  338. newShader[t-n] = 0;
  339. } else {
  340. break;
  341. }
  342. t++;
  343. o = strstr(t, "@");
  344. if (o) {
  345. strncpy(timeOffset, t, o-t);
  346. timeOffset[o-t] = 0;
  347. o++;
  348. trap_R_RemapShader( originalShader, newShader, timeOffset );
  349. }
  350. } else {
  351. break;
  352. }
  353. }
  354. }
  355. extern char *cg_customSoundNames[MAX_CUSTOM_SOUNDS];
  356. extern const char *cg_customCombatSoundNames[MAX_CUSTOM_COMBAT_SOUNDS];
  357. extern const char *cg_customExtraSoundNames[MAX_CUSTOM_EXTRA_SOUNDS];
  358. extern const char *cg_customJediSoundNames[MAX_CUSTOM_JEDI_SOUNDS];
  359. extern const char *cg_customDuelSoundNames[MAX_CUSTOM_DUEL_SOUNDS];
  360. static const char *GetCustomSoundForType(int setType, int index)
  361. {
  362. switch (setType)
  363. {
  364. case 1:
  365. return cg_customSoundNames[index];
  366. case 2:
  367. return cg_customCombatSoundNames[index];
  368. case 3:
  369. return cg_customExtraSoundNames[index];
  370. case 4:
  371. return cg_customJediSoundNames[index];
  372. case 5:
  373. return bg_customSiegeSoundNames[index];
  374. case 6:
  375. return cg_customDuelSoundNames[index];
  376. default:
  377. assert(0);
  378. return NULL;
  379. }
  380. }
  381. void SetCustomSoundForType(clientInfo_t *ci, int setType, int index, sfxHandle_t sfx)
  382. {
  383. switch (setType)
  384. {
  385. case 1:
  386. ci->sounds[index] = sfx;
  387. break;
  388. case 2:
  389. ci->combatSounds[index] = sfx;
  390. break;
  391. case 3:
  392. ci->extraSounds[index] = sfx;
  393. break;
  394. case 4:
  395. ci->jediSounds[index] = sfx;
  396. break;
  397. case 5:
  398. ci->siegeSounds[index] = sfx;
  399. break;
  400. case 6:
  401. ci->duelSounds[index] = sfx;
  402. break;
  403. default:
  404. assert(0);
  405. break;
  406. }
  407. }
  408. static void CG_RegisterCustomSounds(clientInfo_t *ci, int setType, const char *psDir)
  409. {
  410. int iTableEntries = 0;
  411. int i;
  412. switch (setType)
  413. {
  414. case 1:
  415. iTableEntries = MAX_CUSTOM_SOUNDS;
  416. break;
  417. case 2:
  418. iTableEntries = MAX_CUSTOM_COMBAT_SOUNDS;
  419. break;
  420. case 3:
  421. iTableEntries = MAX_CUSTOM_EXTRA_SOUNDS;
  422. break;
  423. case 4:
  424. iTableEntries = MAX_CUSTOM_JEDI_SOUNDS;
  425. break;
  426. case 5:
  427. iTableEntries = MAX_CUSTOM_SIEGE_SOUNDS;
  428. default:
  429. assert(0);
  430. return;
  431. }
  432. for ( i = 0 ; i<iTableEntries; i++ )
  433. {
  434. sfxHandle_t hSFX;
  435. const char *s = GetCustomSoundForType(setType, i);
  436. if ( !s )
  437. {
  438. break;
  439. }
  440. s++;
  441. hSFX = trap_S_RegisterSound( va("sound/chars/%s/misc/%s.mp3", psDir, s) ); //all of them have .mp3?
  442. if (hSFX == 0)
  443. {
  444. char modifiedSound[MAX_QPATH];
  445. char *p;
  446. strcpy(modifiedSound, s);
  447. p = strchr(modifiedSound,'.');
  448. if (p)
  449. {
  450. char testNumber[2];
  451. p--;
  452. //before we destroy it.. we want to see if this is actually a number.
  453. //If it isn't a number then don't try decrementing and registering as
  454. //it will only cause a disk hit (we don't try precaching such files)
  455. testNumber[0] = *p;
  456. testNumber[1] = 0;
  457. if (atoi(testNumber))
  458. {
  459. *p = 0;
  460. strcat(modifiedSound, "1.wav");
  461. hSFX = trap_S_RegisterSound( va("sound/chars/%s/misc/%s", psDir, modifiedSound) );
  462. }
  463. }
  464. }
  465. SetCustomSoundForType(ci, setType, i, hSFX);
  466. }
  467. }
  468. void CG_PrecacheNPCSounds(const char *str)
  469. {
  470. char sEnd[MAX_QPATH];
  471. char pEnd[MAX_QPATH];
  472. int i = 0;
  473. int j = 0;
  474. int k = 0;
  475. k = 2;
  476. while (str[k])
  477. {
  478. pEnd[k-2] = str[k];
  479. k++;
  480. }
  481. pEnd[k-2] = 0;
  482. while (i < 4) //4 types
  483. { //It would be better if we knew what type this actually was (extra, combat, jedi, etc).
  484. //But that would require extra configstring indexing and that is a bad thing.
  485. while (j < MAX_CUSTOM_SOUNDS)
  486. {
  487. const char *s = GetCustomSoundForType(i+1, j);
  488. if (s && s[0])
  489. { //whatever it is, try registering it under this folder.
  490. k = 1;
  491. while (s[k])
  492. {
  493. sEnd[k-1] = s[k];
  494. k++;
  495. }
  496. sEnd[k-1] = 0;
  497. trap_S_ShutUp(qtrue);
  498. trap_S_RegisterSound( va("sound/chars/%s/misc/%s.mp3", pEnd, sEnd) ); //all of them have .mp3?
  499. trap_S_ShutUp(qfalse);
  500. }
  501. else
  502. { //move onto the next set
  503. break;
  504. }
  505. j++;
  506. }
  507. j = 0;
  508. i++;
  509. }
  510. }
  511. void CG_HandleNPCSounds(centity_t *cent)
  512. {
  513. if (!cent->npcClient)
  514. {
  515. return;
  516. }
  517. //standard
  518. if (cent->currentState.csSounds_Std)
  519. {
  520. const char *s = CG_ConfigString( CS_SOUNDS + cent->currentState.csSounds_Std );
  521. if (s && s[0])
  522. {
  523. char sEnd[MAX_QPATH];
  524. int i = 2;
  525. int j = 0;
  526. //Parse past the initial "*" which indicates this is a custom sound, and the $ which indicates
  527. //it is an NPC custom sound dir.
  528. while (s[i])
  529. {
  530. sEnd[j] = s[i];
  531. j++;
  532. i++;
  533. }
  534. sEnd[j] = 0;
  535. CG_RegisterCustomSounds(cent->npcClient, 1, sEnd);
  536. }
  537. }
  538. else
  539. {
  540. memset(&cent->npcClient->sounds, 0, sizeof(cent->npcClient->sounds));
  541. }
  542. //combat
  543. if (cent->currentState.csSounds_Combat)
  544. {
  545. const char *s = CG_ConfigString( CS_SOUNDS + cent->currentState.csSounds_Combat );
  546. if (s && s[0])
  547. {
  548. char sEnd[MAX_QPATH];
  549. int i = 2;
  550. int j = 0;
  551. //Parse past the initial "*" which indicates this is a custom sound, and the $ which indicates
  552. //it is an NPC custom sound dir.
  553. while (s[i])
  554. {
  555. sEnd[j] = s[i];
  556. j++;
  557. i++;
  558. }
  559. sEnd[j] = 0;
  560. CG_RegisterCustomSounds(cent->npcClient, 2, sEnd);
  561. }
  562. }
  563. else
  564. {
  565. memset(&cent->npcClient->combatSounds, 0, sizeof(cent->npcClient->combatSounds));
  566. }
  567. //extra
  568. if (cent->currentState.csSounds_Extra)
  569. {
  570. const char *s = CG_ConfigString( CS_SOUNDS + cent->currentState.csSounds_Extra );
  571. if (s && s[0])
  572. {
  573. char sEnd[MAX_QPATH];
  574. int i = 2;
  575. int j = 0;
  576. //Parse past the initial "*" which indicates this is a custom sound, and the $ which indicates
  577. //it is an NPC custom sound dir.
  578. while (s[i])
  579. {
  580. sEnd[j] = s[i];
  581. j++;
  582. i++;
  583. }
  584. sEnd[j] = 0;
  585. CG_RegisterCustomSounds(cent->npcClient, 3, sEnd);
  586. }
  587. }
  588. else
  589. {
  590. memset(&cent->npcClient->extraSounds, 0, sizeof(cent->npcClient->extraSounds));
  591. }
  592. //jedi
  593. if (cent->currentState.csSounds_Jedi)
  594. {
  595. const char *s = CG_ConfigString( CS_SOUNDS + cent->currentState.csSounds_Jedi );
  596. if (s && s[0])
  597. {
  598. char sEnd[MAX_QPATH];
  599. int i = 2;
  600. int j = 0;
  601. //Parse past the initial "*" which indicates this is a custom sound, and the $ which indicates
  602. //it is an NPC custom sound dir.
  603. while (s[i])
  604. {
  605. sEnd[j] = s[i];
  606. j++;
  607. i++;
  608. }
  609. sEnd[j] = 0;
  610. CG_RegisterCustomSounds(cent->npcClient, 4, sEnd);
  611. }
  612. }
  613. else
  614. {
  615. memset(&cent->npcClient->jediSounds, 0, sizeof(cent->npcClient->jediSounds));
  616. }
  617. }
  618. int CG_HandleAppendedSkin(char *modelName);
  619. void CG_CacheG2AnimInfo(char *modelName);
  620. // nmckenzie: DUEL_HEALTH - fixme - we could really clean this up immensely with some helper functions.
  621. void SetDuelistHealthsFromConfigString ( const char *str ) {
  622. char buf[64];
  623. int c = 0;
  624. int i = 0;
  625. while (str[i] && str[i] != '|')
  626. {
  627. buf[c] = str[i];
  628. c++;
  629. i++;
  630. }
  631. buf[c] = 0;
  632. cgs.duelist1health = atoi ( buf );
  633. c = 0;
  634. i++;
  635. while (str[i] && str[i] != '|')
  636. {
  637. buf[c] = str[i];
  638. c++;
  639. i++;
  640. }
  641. buf[c] = 0;
  642. cgs.duelist2health = atoi ( buf );
  643. c = 0;
  644. i++;
  645. if ( str[i] == '!' )
  646. { // we only have 2 duelists, apparently.
  647. cgs.duelist3health = -1;
  648. return;
  649. }
  650. while (str[i] && str[i] != '|')
  651. {
  652. buf[c] = str[i];
  653. c++;
  654. i++;
  655. }
  656. buf[c] = 0;
  657. cgs.duelist3health = atoi ( buf );
  658. }
  659. /*
  660. ================
  661. CG_ConfigStringModified
  662. ================
  663. */
  664. extern int cgSiegeRoundState;
  665. extern int cgSiegeRoundTime;
  666. void CG_ParseSiegeObjectiveStatus(const char *str);
  667. void CG_ParseWeatherEffect(const char *str);
  668. extern void CG_ParseSiegeState(const char *str); //cg_main.c
  669. extern int cg_beatingSiegeTime;
  670. extern int cg_siegeWinTeam;
  671. static void CG_ConfigStringModified( void ) {
  672. const char *str;
  673. int num;
  674. num = atoi( CG_Argv( 1 ) );
  675. // get the gamestate from the client system, which will have the
  676. // new configstring already integrated
  677. trap_GetGameState( &cgs.gameState );
  678. // look up the individual string that was modified
  679. str = CG_ConfigString( num );
  680. // do something with it if necessary
  681. if ( num == CS_MUSIC ) {
  682. CG_StartMusic( qtrue );
  683. } else if ( num == CS_SERVERINFO ) {
  684. CG_ParseServerinfo();
  685. } else if ( num == CS_WARMUP ) {
  686. CG_ParseWarmup();
  687. } else if ( num == CS_SCORES1 ) {
  688. cgs.scores1 = atoi( str );
  689. } else if ( num == CS_SCORES2 ) {
  690. cgs.scores2 = atoi( str );
  691. } else if ( num == CS_CLIENT_JEDIMASTER ) {
  692. cgs.jediMaster = atoi ( str );
  693. }
  694. else if ( num == CS_CLIENT_DUELWINNER )
  695. {
  696. cgs.duelWinner = atoi ( str );
  697. }
  698. else if ( num == CS_CLIENT_DUELISTS )
  699. {
  700. char buf[64];
  701. int c = 0;
  702. int i = 0;
  703. while (str[i] && str[i] != '|')
  704. {
  705. buf[c] = str[i];
  706. c++;
  707. i++;
  708. }
  709. buf[c] = 0;
  710. cgs.duelist1 = atoi ( buf );
  711. c = 0;
  712. i++;
  713. while (str[i] && str[i] != '|')
  714. {
  715. buf[c] = str[i];
  716. c++;
  717. i++;
  718. }
  719. buf[c] = 0;
  720. cgs.duelist2 = atoi ( buf );
  721. if (str[i])
  722. {
  723. c = 0;
  724. i++;
  725. while (str[i])
  726. {
  727. buf[c] = str[i];
  728. c++;
  729. i++;
  730. }
  731. buf[c] = 0;
  732. cgs.duelist3 = atoi(buf);
  733. }
  734. }
  735. else if ( num == CS_CLIENT_DUELHEALTHS ) { // nmckenzie: DUEL_HEALTH
  736. SetDuelistHealthsFromConfigString(str);
  737. }
  738. else if ( num == CS_LEVEL_START_TIME ) {
  739. cgs.levelStartTime = atoi( str );
  740. } else if ( num == CS_VOTE_TIME ) {
  741. cgs.voteTime = atoi( str );
  742. cgs.voteModified = qtrue;
  743. } else if ( num == CS_VOTE_YES ) {
  744. cgs.voteYes = atoi( str );
  745. cgs.voteModified = qtrue;
  746. } else if ( num == CS_VOTE_NO ) {
  747. cgs.voteNo = atoi( str );
  748. cgs.voteModified = qtrue;
  749. } else if ( num == CS_VOTE_STRING ) {
  750. Q_strncpyz( cgs.voteString, str, sizeof( cgs.voteString ) );
  751. } else if ( num >= CS_TEAMVOTE_TIME && num <= CS_TEAMVOTE_TIME + 1) {
  752. cgs.teamVoteTime[num-CS_TEAMVOTE_TIME] = atoi( str );
  753. cgs.teamVoteModified[num-CS_TEAMVOTE_TIME] = qtrue;
  754. } else if ( num >= CS_TEAMVOTE_YES && num <= CS_TEAMVOTE_YES + 1) {
  755. cgs.teamVoteYes[num-CS_TEAMVOTE_YES] = atoi( str );
  756. cgs.teamVoteModified[num-CS_TEAMVOTE_YES] = qtrue;
  757. } else if ( num >= CS_TEAMVOTE_NO && num <= CS_TEAMVOTE_NO + 1) {
  758. cgs.teamVoteNo[num-CS_TEAMVOTE_NO] = atoi( str );
  759. cgs.teamVoteModified[num-CS_TEAMVOTE_NO] = qtrue;
  760. } else if ( num >= CS_TEAMVOTE_STRING && num <= CS_TEAMVOTE_STRING + 1) {
  761. Q_strncpyz( cgs.teamVoteString[num-CS_TEAMVOTE_STRING], str, sizeof( cgs.teamVoteString ) );
  762. } else if ( num == CS_INTERMISSION ) {
  763. cg.intermissionStarted = atoi( str );
  764. } else if ( num >= CS_MODELS && num < CS_MODELS+MAX_MODELS ) {
  765. char modelName[MAX_QPATH];
  766. strcpy(modelName, str);
  767. if (strstr(modelName, ".glm") || modelName[0] == '$')
  768. { //Check to see if it has a custom skin attached.
  769. CG_HandleAppendedSkin(modelName);
  770. CG_CacheG2AnimInfo(modelName);
  771. }
  772. if (modelName[0] != '$' && modelName[0] != '@')
  773. { //don't register vehicle names and saber names as models.
  774. cgs.gameModels[ num-CS_MODELS ] = trap_R_RegisterModel( modelName );
  775. }
  776. else
  777. {
  778. cgs.gameModels[ num-CS_MODELS ] = 0;
  779. }
  780. // GHOUL2 Insert start
  781. /*
  782. } else if ( num >= CS_CHARSKINS && num < CS_CHARSKINS+MAX_CHARSKINS ) {
  783. cgs.skins[ num-CS_CHARSKINS ] = trap_R_RegisterSkin( str );
  784. */
  785. //rww - removed and replaced with CS_G2BONES
  786. // Ghoul2 Insert end
  787. } else if ( num >= CS_SOUNDS && num < CS_SOUNDS+MAX_SOUNDS && (str[0] || str[1] == '$')) {
  788. if ( str[0] != '*' ) { // player specific sounds don't register here
  789. cgs.gameSounds[ num-CS_SOUNDS] = trap_S_RegisterSound( str );
  790. }
  791. else if (str[1] == '$')
  792. { //an NPC soundset
  793. CG_PrecacheNPCSounds(str);
  794. }
  795. } else if ( num >= CS_EFFECTS && num < CS_EFFECTS+MAX_FX ) {
  796. if (str[0] == '*')
  797. { //it's a special global weather effect
  798. CG_ParseWeatherEffect(str);
  799. cgs.gameEffects[ num-CS_EFFECTS] = 0;
  800. }
  801. else
  802. {
  803. cgs.gameEffects[ num-CS_EFFECTS] = trap_FX_RegisterEffect( str );
  804. }
  805. }
  806. else if ( num >= CS_SIEGE_STATE && num < CS_SIEGE_STATE+1 )
  807. {
  808. if (str[0])
  809. {
  810. CG_ParseSiegeState(str);
  811. }
  812. }
  813. else if ( num >= CS_SIEGE_WINTEAM && num < CS_SIEGE_WINTEAM+1 )
  814. {
  815. if (str[0])
  816. {
  817. cg_siegeWinTeam = atoi(str);
  818. }
  819. }
  820. else if ( num >= CS_SIEGE_OBJECTIVES && num < CS_SIEGE_OBJECTIVES+1 )
  821. {
  822. CG_ParseSiegeObjectiveStatus(str);
  823. }
  824. else if (num >= CS_SIEGE_TIMEOVERRIDE && num < CS_SIEGE_TIMEOVERRIDE+1)
  825. {
  826. cg_beatingSiegeTime = atoi(str);
  827. CG_SetSiegeTimerCvar ( cg_beatingSiegeTime );
  828. }
  829. else if ( num >= CS_PLAYERS && num < CS_PLAYERS+MAX_CLIENTS )
  830. {
  831. CG_NewClientInfo( num - CS_PLAYERS, qtrue);
  832. CG_BuildSpectatorString();
  833. } else if ( num == CS_FLAGSTATUS ) {
  834. if( cgs.gametype == GT_CTF || cgs.gametype == GT_CTY ) {
  835. // format is rb where its red/blue, 0 is at base, 1 is taken, 2 is dropped
  836. int redflagId = str[0] - '0', blueflagId = str[1] - '0';
  837. //Raz: improved flag status remapping
  838. if ( redflagId >= 0 && redflagId < ARRAY_LEN( ctfFlagStatusRemap ) )
  839. cgs.redflag = ctfFlagStatusRemap[redflagId];
  840. if ( blueflagId >= 0 && blueflagId < ARRAY_LEN( ctfFlagStatusRemap ) )
  841. cgs.blueflag = ctfFlagStatusRemap[blueflagId];
  842. }
  843. }
  844. else if ( num == CS_SHADERSTATE ) {
  845. CG_ShaderStateChanged();
  846. }
  847. else if ( num >= CS_LIGHT_STYLES && num < CS_LIGHT_STYLES + (MAX_LIGHT_STYLES * 3))
  848. {
  849. CG_SetLightstyle(num - CS_LIGHT_STYLES);
  850. }
  851. }
  852. //frees all ghoul2 stuff and npc stuff from a centity -rww
  853. void CG_KillCEntityG2(int entNum)
  854. {
  855. int j;
  856. clientInfo_t *ci = NULL;
  857. centity_t *cent = &cg_entities[entNum];
  858. if (entNum < MAX_CLIENTS)
  859. {
  860. ci = &cgs.clientinfo[entNum];
  861. }
  862. else
  863. {
  864. ci = cent->npcClient;
  865. }
  866. if (ci)
  867. {
  868. if (ci == cent->npcClient)
  869. { //never going to be != cent->ghoul2, unless cent->ghoul2 has already been removed (and then this ptr is not valid)
  870. ci->ghoul2Model = NULL;
  871. }
  872. else if (ci->ghoul2Model == cent->ghoul2)
  873. {
  874. ci->ghoul2Model = NULL;
  875. }
  876. else if (ci->ghoul2Model && trap_G2_HaveWeGhoul2Models(ci->ghoul2Model))
  877. {
  878. trap_G2API_CleanGhoul2Models(&ci->ghoul2Model);
  879. ci->ghoul2Model = NULL;
  880. }
  881. //Clean up any weapon instances for custom saber stuff
  882. j = 0;
  883. while (j < MAX_SABERS)
  884. {
  885. if (ci->ghoul2Weapons[j] && trap_G2_HaveWeGhoul2Models(ci->ghoul2Weapons[j]))
  886. {
  887. trap_G2API_CleanGhoul2Models(&ci->ghoul2Weapons[j]);
  888. ci->ghoul2Weapons[j] = NULL;
  889. }
  890. j++;
  891. }
  892. }
  893. if (cent->ghoul2 && trap_G2_HaveWeGhoul2Models(cent->ghoul2))
  894. {
  895. trap_G2API_CleanGhoul2Models(&cent->ghoul2);
  896. cent->ghoul2 = NULL;
  897. }
  898. if (cent->grip_arm && trap_G2_HaveWeGhoul2Models(cent->grip_arm))
  899. {
  900. trap_G2API_CleanGhoul2Models(&cent->grip_arm);
  901. cent->grip_arm = NULL;
  902. }
  903. if (cent->frame_hold && trap_G2_HaveWeGhoul2Models(cent->frame_hold))
  904. {
  905. trap_G2API_CleanGhoul2Models(&cent->frame_hold);
  906. cent->frame_hold = NULL;
  907. }
  908. if (cent->npcClient)
  909. {
  910. CG_DestroyNPCClient(&cent->npcClient);
  911. }
  912. cent->isRagging = qfalse; //just in case.
  913. cent->ikStatus = qfalse;
  914. cent->localAnimIndex = 0;
  915. }
  916. void CG_KillCEntityInstances(void)
  917. {
  918. int i = 0;
  919. centity_t *cent;
  920. while (i < MAX_GENTITIES)
  921. {
  922. cent = &cg_entities[i];
  923. if (i >= MAX_CLIENTS && cent->currentState.number == i)
  924. { //do not clear G2 instances on client ents, they are constant
  925. CG_KillCEntityG2(i);
  926. }
  927. cent->bolt1 = 0;
  928. cent->bolt2 = 0;
  929. cent->bolt3 = 0;
  930. cent->bolt4 = 0;
  931. cent->bodyHeight = 0;//SABER_LENGTH_MAX;
  932. //cent->saberExtendTime = 0;
  933. cent->boltInfo = 0;
  934. cent->frame_minus1_refreshed = 0;
  935. cent->frame_minus2_refreshed = 0;
  936. cent->dustTrailTime = 0;
  937. cent->ghoul2weapon = NULL;
  938. //cent->torsoBolt = 0;
  939. cent->trailTime = 0;
  940. cent->frame_hold_time = 0;
  941. cent->frame_hold_refreshed = 0;
  942. cent->trickAlpha = 0;
  943. cent->trickAlphaTime = 0;
  944. VectorClear(cent->turAngles);
  945. cent->weapon = 0;
  946. cent->teamPowerEffectTime = 0;
  947. cent->teamPowerType = 0;
  948. cent->numLoopingSounds = 0;
  949. cent->localAnimIndex = 0;
  950. i++;
  951. }
  952. }
  953. /*
  954. ===============
  955. CG_MapRestart
  956. The server has issued a map_restart, so the next snapshot
  957. is completely new and should not be interpolated to.
  958. A tournement restart will clear everything, but doesn't
  959. require a reload of all the media
  960. ===============
  961. */
  962. static void CG_MapRestart( void ) {
  963. if ( cg_showMiss.integer ) {
  964. CG_Printf( "CG_MapRestart\n" );
  965. }
  966. trap_R_ClearDecals ( );
  967. //FIXME: trap_FX_Reset?
  968. CG_InitLocalEntities();
  969. CG_InitMarkPolys();
  970. CG_ClearParticles ();
  971. CG_KillCEntityInstances();
  972. // make sure the "3 frags left" warnings play again
  973. cg.fraglimitWarnings = 0;
  974. cg.timelimitWarnings = 0;
  975. cg.intermissionStarted = qfalse;
  976. cgs.voteTime = 0;
  977. cg.mapRestart = qtrue;
  978. CG_StartMusic(qtrue);
  979. trap_S_ClearLoopingSounds();
  980. // we really should clear more parts of cg here and stop sounds
  981. // play the "fight" sound if this is a restart without warmup
  982. if ( cg.warmup == 0 && cgs.gametype != GT_SIEGE && cgs.gametype != GT_POWERDUEL/* && cgs.gametype == GT_DUEL */) {
  983. if (!(mov_soundDisable.integer & SDISABLE_ANNOUNCER)) {
  984. trap_S_StartLocalSound( cgs.media.countFightSound, CHAN_ANNOUNCER );
  985. }
  986. CG_CenterPrint( CG_GetStringEdString("MP_SVGAME", "BEGIN_DUEL"), 120, GIANTCHAR_WIDTH*2 );
  987. }
  988. /*
  989. if (cg_singlePlayerActive.integer) {
  990. trap_Cvar_Set("ui_matchStartTime", va("%i", cg.time));
  991. if (cg_recordSPDemo.integer && cg_recordSPDemoName.string && *cg_recordSPDemoName.string) {
  992. trap_SendConsoleCommand(va("set g_synchronousclients 1 ; record %s \n", cg_recordSPDemoName.string));
  993. }
  994. }
  995. */
  996. // trap_Cvar_Set("cg_thirdPerson", "0");
  997. }
  998. /*
  999. =================
  1000. CG_RemoveChatEscapeChar
  1001. =================
  1002. */
  1003. static void CG_RemoveChatEscapeChar( char *text ) {
  1004. int i, l;
  1005. l = 0;
  1006. for ( i = 0; text[i]; i++ ) {
  1007. if (text[i] == '\x19')
  1008. continue;
  1009. text[l++] = text[i];
  1010. }
  1011. text[l] = '\0';
  1012. }
  1013. #define MAX_STRINGED_SV_STRING 1024 // this is an quake-engine limit, not a StringEd limit
  1014. void CG_CheckSVStringEdRef(char *buf, const char *str)
  1015. { //I don't really like doing this. But it utilizes the system that was already in place.
  1016. int i = 0;
  1017. int b = 0;
  1018. int strLen = 0;
  1019. qboolean gotStrip = qfalse;
  1020. if (!str || !str[0])
  1021. {
  1022. if (str)
  1023. {
  1024. strcpy(buf, str);
  1025. }
  1026. return;
  1027. }
  1028. strcpy(buf, str);
  1029. strLen = strlen(str);
  1030. if (strLen >= MAX_STRINGED_SV_STRING)
  1031. {
  1032. return;
  1033. }
  1034. while (i < strLen && str[i])
  1035. {
  1036. gotStrip = qfalse;
  1037. if (str[i] == '@' && (i+1) < strLen)
  1038. {
  1039. if (str[i+1] == '@' && (i+2) < strLen)
  1040. {
  1041. if (str[i+2] == '@' && (i+3) < strLen)
  1042. { //@@@ should mean to insert a StringEd reference here, so insert it into buf at the current place
  1043. char stringRef[MAX_STRINGED_SV_STRING];
  1044. int r = 0;
  1045. while (i < strLen && str[i] == '@')
  1046. {
  1047. i++;
  1048. }
  1049. while (i < strLen && str[i] && str[i] != ' ' && str[i] != ':' && str[i] != '.' && str[i] != '\n')
  1050. {
  1051. stringRef[r] = str[i];
  1052. r++;
  1053. i++;
  1054. }
  1055. stringRef[r] = 0;
  1056. buf[b] = 0;
  1057. Q_strcat(buf, MAX_STRINGED_SV_STRING, CG_GetStringEdString("MP_SVGAME", stringRef));
  1058. b = strlen(buf);
  1059. }
  1060. }
  1061. }
  1062. if (!gotStrip)
  1063. {
  1064. buf[b] = str[i];
  1065. b++;
  1066. }
  1067. i++;
  1068. }
  1069. buf[b] = 0;
  1070. }
  1071. static void CG_BodyQueueCopy(centity_t *cent, int clientNum, int knownWeapon)
  1072. {
  1073. centity_t *source;
  1074. animation_t *anim;
  1075. float animSpeed;
  1076. int flags=BONE_ANIM_OVERRIDE_FREEZE;
  1077. clientInfo_t *ci;
  1078. if (cent->ghoul2)
  1079. {
  1080. trap_G2API_CleanGhoul2Models(&cent->ghoul2);
  1081. }
  1082. if (clientNum < 0 || clientNum >= MAX_CLIENTS)
  1083. {
  1084. return;
  1085. }
  1086. source = &cg_entities[ clientNum ];
  1087. ci = &cgs.clientinfo[ clientNum ];
  1088. if (!source)
  1089. {
  1090. return;
  1091. }
  1092. if (!source->ghoul2)
  1093. {
  1094. return;
  1095. }
  1096. cent->isRagging = qfalse; //reset in case it's still set from another body that was in this cent slot.
  1097. cent->ownerRagging = source->isRagging; //if the owner was in ragdoll state, then we want to go into it too right away.
  1098. #if 0
  1099. VectorCopy(source->lerpOriginOffset, cent->lerpOriginOffset);
  1100. #endif
  1101. cent->bodyFadeTime = 0;
  1102. cent->bodyHeight = 0;
  1103. cent->dustTrailTime = source->dustTrailTime;
  1104. trap_G2API_DuplicateGhoul2Instance(source->ghoul2, &cent->ghoul2);
  1105. if (source->isRagging)
  1106. { //just reset it now.
  1107. source->isRagging = qfalse;
  1108. trap_G2API_SetRagDoll(source->ghoul2, NULL); //calling with null parms resets to no ragdoll.
  1109. }
  1110. //either force the weapon from when we died or remove it if it was a dropped weapon
  1111. if (knownWeapon > WP_BRYAR_PISTOL && trap_G2API_HasGhoul2ModelOnIndex(&(cent->ghoul2), 1))
  1112. {
  1113. trap_G2API_RemoveGhoul2Model(&(cent->ghoul2), 1);
  1114. }
  1115. else if (trap_G2API_HasGhoul2ModelOnIndex(&(cent->ghoul2), 1))
  1116. {
  1117. trap_G2API_CopySpecificGhoul2Model(CG_G2WeaponInstance(cent, knownWeapon), 0, cent->ghoul2, 1);
  1118. }
  1119. if (!cent->ownerRagging)
  1120. {
  1121. int aNum;
  1122. int eFrame;
  1123. qboolean fallBack = qfalse;
  1124. //anim = &bgAllAnims[cent->localAnimIndex].anims[ cent->currentState.torsoAnim ];
  1125. if (!BG_InDeathAnim(source->currentState.torsoAnim))
  1126. { //then just snap the corpse into a default
  1127. anim = &bgAllAnims[source->localAnimIndex].anims[ BOTH_DEAD1 ];
  1128. fallBack = qtrue;
  1129. }
  1130. else
  1131. {
  1132. anim = &bgAllAnims[source->localAnimIndex].anims[ source->currentState.torsoAnim ];
  1133. }
  1134. animSpeed = 50.0f / anim->frameLerp;
  1135. if (!fallBack)
  1136. {
  1137. //this will just set us to the last frame of the animation, in theory
  1138. aNum = cgs.clientinfo[source->currentState.number].frame+1;
  1139. while (aNum >= anim->firstFrame+anim->numFrames)
  1140. {
  1141. aNum--;
  1142. }
  1143. if (aNum < anim->firstFrame-1)
  1144. { //wrong animation...?
  1145. aNum = (anim->firstFrame+anim->numFrames)-1;
  1146. }
  1147. }
  1148. else
  1149. {
  1150. aNum = anim->firstFrame;
  1151. }
  1152. eFrame = anim->firstFrame + anim->numFrames;
  1153. //if (!cgs.clientinfo[source->currentState.number].frame || (cent->currentState.torsoAnim) != (source->currentState.torsoAnim) )
  1154. //{
  1155. // aNum = (anim->firstFrame+anim->numFrames)-1;
  1156. //}
  1157. trap_G2API_SetBoneAnim(cent->ghoul2, 0, "upper_lumbar", aNum, eFrame, flags, animSpeed, cg.time, -1, 150);
  1158. trap_G2API_SetBoneAnim(cent->ghoul2, 0, "model_root", aNum, eFrame, flags, animSpeed, cg.time, -1, 150);
  1159. trap_G2API_SetBoneAnim(cent->ghoul2, 0, "Motion", aNum, eFrame, flags, animSpeed, cg.time, -1, 150);
  1160. }
  1161. //After we create the bodyqueue, regenerate any limbs on the real instance
  1162. if (source->torsoBolt)
  1163. {
  1164. CG_ReattachLimb(source);
  1165. }
  1166. }
  1167. /*
  1168. =================
  1169. CG_ServerCommand
  1170. The string has been tokenized and can be retrieved with
  1171. Cmd_Argc() / Cmd_Argv()
  1172. =================
  1173. */
  1174. void CG_SiegeBriefingDisplay(int team, int dontshow);
  1175. void CG_ParseSiegeExtendedData(void);
  1176. extern void CG_ChatBox_AddString(char *chatStr); //cg_draw.c
  1177. static void CG_ServerCommand( void ) {
  1178. const char *cmd;
  1179. char text[MAX_SAY_TEXT];
  1180. qboolean IRCG = qfalse;
  1181. cmd = CG_Argv(0);
  1182. if ( !cmd[0] ) {
  1183. // server claimed the command
  1184. return;
  1185. }
  1186. #if 0
  1187. // never seems to get used -Ste
  1188. if ( !strcmp( cmd, "spd" ) )
  1189. {
  1190. const char *ID;
  1191. int holdInt,count,i;
  1192. char string[1204];
  1193. count = trap_Argc();
  1194. ID = CG_Argv(1);
  1195. holdInt = atoi(ID);
  1196. memset( &string, 0, sizeof( string ) );
  1197. Com_sprintf( string,sizeof(string)," \"%s\"", (const char *) CG_Argv(2));
  1198. for (i=3;i<count;i++)
  1199. {
  1200. Com_sprintf( string,sizeof(string)," %s \"%s\"", string, (const char *) CG_Argv(i));
  1201. }
  1202. trap_SP_Print(holdInt, (byte *)string);
  1203. return;
  1204. }
  1205. #endif
  1206. if (!strcmp(cmd, "sxd"))
  1207. { //siege extended data, contains extra info certain classes may want to know about other clients
  1208. CG_ParseSiegeExtendedData();
  1209. return;
  1210. }
  1211. if (!strcmp(cmd, "sb"))
  1212. { //siege briefing display
  1213. CG_SiegeBriefingDisplay(atoi(CG_Argv(1)), 0);
  1214. return;
  1215. }
  1216. if ( !strcmp( cmd, "scl" ) )
  1217. {
  1218. //if (!( trap_Key_GetCatcher() & KEYCATCH_UI ))
  1219. //Well, I want it to come up even if the briefing display is up.
  1220. {
  1221. trap_OpenUIMenu(UIMENU_CLASSSEL); //UIMENU_CLASSSEL
  1222. }
  1223. return;
  1224. }
  1225. if ( !strcmp( cmd, "spc" ) )
  1226. {
  1227. if (cg.demoPlayback)
  1228. return;
  1229. trap_Cvar_Set("ui_myteam", "3");
  1230. trap_OpenUIMenu(UIMENU_PLAYERCONFIG); //UIMENU_CLASSSEL
  1231. return;
  1232. }
  1233. if ( !strcmp( cmd, "nfr" ) )
  1234. { //"nfr" == "new force rank" (want a short string)
  1235. int doMenu = 0;
  1236. int setTeam = 0;
  1237. int newRank = 0;
  1238. if (trap_Argc() < 3)
  1239. {
  1240. #ifdef _DEBUG
  1241. Com_Printf("WARNING: Invalid newForceRank string\n");
  1242. #endif
  1243. return;
  1244. }
  1245. newRank = atoi(CG_Argv(1));
  1246. doMenu = atoi(CG_Argv(2));
  1247. setTeam = atoi(CG_Argv(3));
  1248. trap_Cvar_Set("ui_rankChange", va("%i", newRank));
  1249. trap_Cvar_Set("ui_myteam", va("%i", setTeam));
  1250. if (!( trap_Key_GetCatcher() & KEYCATCH_UI ) && doMenu && !cg.demoPlayback)
  1251. {
  1252. trap_OpenUIMenu(UIMENU_PLAYERCONFIG);
  1253. }
  1254. return;
  1255. }
  1256. if ( !strcmp( cmd, "kg2" ) )
  1257. { //Kill a ghoul2 instance in this slot.
  1258. //If it has been occupied since this message was sent somehow, the worst that can (should) happen
  1259. //is the instance will have to reinit with its current info.
  1260. int indexNum = 0;
  1261. int argNum = trap_Argc();
  1262. int i = 1;
  1263. if (argNum < 1)
  1264. {
  1265. return;
  1266. }
  1267. while (i < argNum)
  1268. {
  1269. indexNum = atoi(CG_Argv(i));
  1270. if (cg_entities[indexNum].ghoul2 && trap_G2_HaveWeGhoul2Models(cg_entities[indexNum].ghoul2))
  1271. {
  1272. if (indexNum < MAX_CLIENTS)
  1273. { //You try to do very bad thing!
  1274. #ifdef _DEBUG
  1275. Com_Printf("WARNING: Tried to kill a client ghoul2 instance with a kg2 command!\n");
  1276. #endif
  1277. return;
  1278. }
  1279. CG_KillCEntityG2(indexNum);
  1280. }
  1281. i++;
  1282. }
  1283. return;
  1284. }
  1285. if (!strcmp(cmd, "kls"))
  1286. { //kill looping sounds
  1287. int indexNum = 0;
  1288. int argNum = trap_Argc();
  1289. centity_t *clent = NULL;
  1290. centity_t *trackerent = NULL;
  1291. if (argNum < 1)
  1292. {
  1293. assert(0);
  1294. return;
  1295. }
  1296. indexNum = atoi(CG_Argv(1));
  1297. if (indexNum != -1)
  1298. {
  1299. clent = &cg_entities[indexNum];
  1300. }
  1301. if (argNum >= 2)
  1302. {
  1303. indexNum = atoi(CG_Argv(2));
  1304. if (indexNum != -1)
  1305. {
  1306. trackerent = &cg_entities[indexNum];
  1307. }
  1308. }
  1309. if (clent)
  1310. {
  1311. CG_S_StopLoopingSound(clent->currentState.number, -1);
  1312. }
  1313. if (trackerent)
  1314. {
  1315. CG_S_StopLoopingSound(trackerent->currentState.number, -1);
  1316. }
  1317. return;
  1318. }
  1319. if (!strcmp(cmd, "ircg"))
  1320. { //this means param 2 is the body index and we want to copy to bodyqueue on it
  1321. IRCG = qtrue;
  1322. }
  1323. if (!strcmp(cmd, "rcg") || IRCG)
  1324. { //rcg - Restore Client Ghoul (make sure limbs are reattached and ragdoll state is reset - this must be done reliably)
  1325. int indexNum = 0;
  1326. int argNum = trap_Argc();
  1327. centity_t *clent;
  1328. if (argNum < 1)
  1329. {
  1330. assert(0);
  1331. return;
  1332. }
  1333. indexNum = atoi(CG_Argv(1));
  1334. if (indexNum < 0 || indexNum >= MAX_CLIENTS)
  1335. {
  1336. assert(0);
  1337. return;
  1338. }
  1339. clent = &cg_entities[indexNum];
  1340. //assert(clent->ghoul2);
  1341. if (!clent->ghoul2)
  1342. { //this can happen while connecting as a client
  1343. return;
  1344. }
  1345. #ifdef _DEBUG
  1346. if (!trap_G2_HaveWeGhoul2Models(clent->ghoul2))
  1347. {
  1348. assert(!"Tried to reset state on a bad instance. Crash is inevitable.");
  1349. }
  1350. #endif
  1351. if (IRCG)
  1352. {
  1353. int bodyIndex = 0;
  1354. int weaponIndex = 0;
  1355. int side = 0;
  1356. centity_t *body;
  1357. assert(argNum >= 3);
  1358. bodyIndex = atoi(CG_Argv(2));
  1359. weaponIndex = atoi(CG_Argv(3));
  1360. side = atoi(CG_Argv(4));
  1361. body = &cg_entities[bodyIndex];
  1362. if (side)
  1363. {
  1364. body->teamPowerType = qtrue; //light side
  1365. }
  1366. else
  1367. {
  1368. body->teamPowerType = qfalse; //dark side
  1369. }
  1370. CG_BodyQueueCopy(body, clent->currentState.number, weaponIndex);
  1371. }
  1372. //reattach any missing limbs
  1373. if (clent->torsoBolt)
  1374. {
  1375. CG_ReattachLimb(clent);
  1376. }
  1377. //make sure ragdoll state is reset
  1378. if (clent->isRagging)
  1379. {
  1380. clent->isRagging = qfalse;
  1381. trap_G2API_SetRagDoll(clent->ghoul2, NULL); //calling with null parms resets to no ragdoll.
  1382. }
  1383. //clear all the decals as well
  1384. trap_G2API_ClearSkinGore(clent->ghoul2);
  1385. clent->weapon = 0;
  1386. clent->ghoul2weapon = NULL; //force a weapon reinit
  1387. return;
  1388. }
  1389. if ( !strcmp( cmd, "cp" ) ) {
  1390. char strEd[MAX_STRINGED_SV_STRING];
  1391. CG_CheckSVStringEdRef(strEd, CG_Argv(1));
  1392. CG_CenterPrint( strEd, SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH );
  1393. return;
  1394. }
  1395. if ( !strcmp( cmd, "cps" ) ) {
  1396. char strEd[MAX_STRINGED_SV_STRING];
  1397. char *x = (char *)CG_Argv(1);
  1398. if (x[0] == '@')
  1399. {
  1400. x++;
  1401. }
  1402. trap_SP_GetStringTextString(x, strEd, MAX_STRINGED_SV_STRING);
  1403. CG_CenterPrint( strEd, SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH );
  1404. return;
  1405. }
  1406. if ( !strcmp( cmd, "cs" ) ) {
  1407. CG_ConfigStringModified();
  1408. return;
  1409. }
  1410. if ( !strcmp( cmd, "print" ) ) {
  1411. char strEd[MAX_STRINGED_SV_STRING];
  1412. CG_CheckSVStringEdRef(strEd, CG_Argv(1));
  1413. CG_Printf( "%s", strEd );
  1414. return;
  1415. }
  1416. if ( !strcmp( cmd, "chat" ) ) {
  1417. if ( !cg_teamChatsOnly.integer ) {
  1418. if (!(mov_soundDisable.integer & SDISABLE_CHAT) && mov_chatBeep.integer) {
  1419. trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND );
  1420. }
  1421. Q_strncpyz( text, CG_Argv(1), MAX_SAY_TEXT );
  1422. CG_RemoveChatEscapeChar( text );
  1423. CG_ChatBox_AddString(text);
  1424. CG_Printf( "*%s\n", text );
  1425. }
  1426. return;
  1427. }
  1428. if ( !strcmp( cmd, "tchat" ) ) {
  1429. if (!(mov_soundDisable.integer & SDISABLE_CHAT) && mov_chatBeep.integer) {
  1430. trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND );
  1431. }
  1432. Q_strncpyz( text, CG_Argv(1), MAX_SAY_TEXT );
  1433. CG_RemoveChatEscapeChar( text );
  1434. CG_ChatBox_AddString(text);
  1435. CG_Printf( "*%s\n", text );
  1436. return;
  1437. }
  1438. //chat with location, possibly localized.
  1439. if ( !strcmp( cmd, "lchat" ) ) {
  1440. if ( !cg_teamChatsOnly.integer ) {
  1441. char name[MAX_STRING_CHARS];
  1442. char loc[MAX_STRING_CHARS];
  1443. char color[8];
  1444. char message[MAX_STRING_CHARS];
  1445. if (trap_Argc() < 4)
  1446. {
  1447. return;
  1448. }
  1449. Q_strncpyz( name, CG_Argv( 1 ), sizeof( name ) );
  1450. Q_strncpyz( loc, CG_Argv( 2 ), sizeof( loc ) );
  1451. Q_strncpyz( color, CG_Argv( 3 ), sizeof( color ) );
  1452. Q_strncpyz( message, CG_Argv( 4 ), sizeof( message ) );
  1453. if (loc[0] == '@')
  1454. { //get localized text
  1455. trap_SP_GetStringTextString(loc+1, loc, sizeof( loc ) );
  1456. }
  1457. if (!(mov_soundDisable.integer & SDISABLE_CHAT) && mov_chatBeep.integer)
  1458. trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND );
  1459. //Q_strncpyz( text, CG_Argv(1), MAX_SAY_TEXT );
  1460. Com_sprintf(text, sizeof( text ), "%s^7<%s> ^%s%s", name, loc, color, message);
  1461. CG_RemoveChatEscapeChar( text );
  1462. CG_ChatBox_AddString(text);
  1463. CG_Printf( "*%s\n", text );
  1464. }
  1465. return;
  1466. }
  1467. if ( !strcmp( cmd, "ltchat" ) ) {
  1468. char name[MAX_STRING_CHARS];
  1469. char loc[MAX_STRING_CHARS];
  1470. char color[8];
  1471. char message[MAX_STRING_CHARS];
  1472. if (trap_Argc() < 4)
  1473. {
  1474. return;
  1475. }
  1476. Q_strncpyz( name, CG_Argv( 1 ), sizeof( name ) );
  1477. Q_strncpyz( loc, CG_Argv( 2 ), sizeof( loc ) );
  1478. Q_strncpyz( color, CG_Argv( 3 ), sizeof( color ) );
  1479. Q_strncpyz( message, CG_Argv( 4 ), sizeof( message ) );
  1480. if (loc[0] == '@')
  1481. { //get localized text
  1482. trap_SP_GetStringTextString(loc+1, loc, sizeof( loc ) );
  1483. }
  1484. if (!(mov_soundDisable.integer & SDISABLE_CHAT) && mov_chatBeep.integer)
  1485. trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND );
  1486. //Q_strncpyz( text, CG_Argv(1), MAX_SAY_TEXT );
  1487. Com_sprintf(text, sizeof( text ), "%s^7<%s> ^%s%s", name, loc, color, message);
  1488. CG_RemoveChatEscapeChar( text );
  1489. CG_ChatBox_AddString(text);
  1490. CG_Printf( "*%s\n", text );
  1491. return;
  1492. }
  1493. if ( !strcmp( cmd, "scores" ) ) {
  1494. CG_ParseScores();
  1495. return;
  1496. }
  1497. if ( !strcmp( cmd, "tinfo" ) ) {
  1498. CG_ParseTeamInfo();
  1499. return;
  1500. }
  1501. if ( !strcmp( cmd, "map_restart" ) ) {
  1502. CG_MapRestart();
  1503. return;
  1504. }
  1505. //Raz: Buffer overflow fix
  1506. #if 0
  1507. if ( Q_stricmp (cmd, "remapShader") == 0 ) {
  1508. if (trap_Argc() == 4) {
  1509. trap_R_RemapShader(CG_Argv(1), CG_Argv(2), CG_Argv(3));
  1510. }
  1511. }
  1512. #else
  1513. if ( !Q_stricmp( cmd, "remapShader" ) ) {
  1514. if ( trap_Argc() == 4 ) {
  1515. char shader1[MAX_QPATH];
  1516. char shader2[MAX_QPATH];
  1517. Q_strncpyz( shader1, CG_Argv( 1 ), sizeof( shader1 ) );
  1518. Q_strncpyz( shader2, CG_Argv( 2 ), sizeof( shader2 ) );
  1519. trap_R_RemapShader( shader1, shader2, CG_Argv( 3 ) );
  1520. return;
  1521. }
  1522. return;
  1523. }
  1524. #endif
  1525. // loaddeferred can be both a servercmd and a consolecmd
  1526. if ( !strcmp( cmd, "loaddefered" ) ) { // FIXME: spelled wrong, but not changing for demo
  1527. CG_LoadDeferredPlayers();
  1528. return;
  1529. }
  1530. // clientLevelShot is sent before taking a special screenshot for
  1531. // the menu system during development
  1532. if ( !strcmp( cmd, "clientLevelShot" ) ) {
  1533. cg.levelShot = qtrue;
  1534. return;
  1535. }
  1536. CG_Printf( "Unknown client game command: %s\n", cmd );
  1537. }
  1538. /*
  1539. ====================
  1540. CG_ExecuteNewServerCommands
  1541. Execute all of the server commands that were received along
  1542. with this this snapshot.
  1543. ====================
  1544. */
  1545. void CG_ExecuteNewServerCommands( int latestSequence ) {
  1546. while ( cgs.serverCommandSequence < latestSequence ) {
  1547. if ( trap_GetServerCommand( ++cgs.serverCommandSequence ) ) {
  1548. CG_ServerCommand();
  1549. }
  1550. }
  1551. }