//----------------------------------------------------------------------------- // // $Id$ // //----------------------------------------------------------------------------- // // $Log$ // Revision 1.196 2007/02/03 15:02:21 jbravo // Renamed RQ3 to Reaction, Dropped loading of various baseq3 media, disabled the follow command, fixed grenades killing teammates and some cleanups // // Revision 1.195 2005/09/13 03:32:03 jbravo // added a Youve started bandaging message at the right time. // // Revision 1.194 2005/09/13 03:11:08 jbravo // Dead players cant use unzoom or weapon commands // // Revision 1.193 2005/09/13 02:33:17 jbravo // Adding new callvote gametype:map // // Revision 1.192 2005/09/07 20:27:41 makro // Entity attachment trees // // Revision 1.191 2005/02/15 16:33:39 makro // Tons of updates (entity tree attachment system, UI vectors) // // Revision 1.190 2004/01/26 21:26:08 makro // no message // // Revision 1.189 2003/09/16 23:25:32 makro // trigger_multiple - new spawnflag, 3 new keys // // Revision 1.188 2003/04/26 22:33:06 jbravo // Wratted all calls to G_FreeEnt() to avoid crashing and provide debugging // // Revision 1.187 2003/04/19 15:27:30 jbravo // Backing out of most of unlagged. Only optimized prediction and smooth clients // remains. // // Revision 1.186 2003/04/09 20:57:21 jbravo // DM team none was missing a suicide. // // Revision 1.185 2003/04/09 02:00:43 jbravo // Fixed team none in DM and some final cleanups for the 3.0 release // // Revision 1.184 2003/03/30 00:36:03 jbravo // Grenadebug when dropping bandolier // // Revision 1.183 2003/03/29 18:53:41 jbravo // Fixed ammo bug when dropping bandolier. Added color to more errormessages // // Revision 1.182 2003/03/28 13:10:36 jbravo // *** empty log message *** // // Revision 1.181 2003/03/28 13:05:18 jbravo // No nickchanging to avoid getting votekicked. // // Revision 1.180 2003/03/28 10:36:02 jbravo // Tweaking the replacement system a bit. Reactionmale now the default model // // Revision 1.179 2003/03/10 07:07:58 jbravo // Small unlagged fixes and voting delay added. // // Revision 1.178 2003/03/09 21:30:38 jbravo // Adding unlagged. Still needs work. // // Revision 1.177 2003/02/27 03:58:35 jbravo // Fixed the FF system after adding TDM broke it. Added color to error messages // // Revision 1.176 2002/11/13 00:50:38 jbravo // Fixed item dropping, specmode selection on death and helmet probs. // // Revision 1.175 2002/10/30 20:04:34 jbravo // Adding helmet // // Revision 1.174 2002/10/26 22:03:43 jbravo // Made TeamDM work RQ3 style. // // Revision 1.173 2002/10/26 18:29:17 jbravo // Added allweap and allitem funtionality. // // Revision 1.172 2002/10/26 00:37:18 jbravo // New multiple item code and added PB support to the UI // // Revision 1.171 2002/10/21 21:00:39 slicer // New MM features and bug fixes // // Revision 1.170 2002/09/30 01:32:30 jbravo // Fixing the vote and callvote cmd's so dead players in CTB can use them. // // Revision 1.169 2002/09/29 16:06:44 jbravo // Work done at the HPWorld expo // // Revision 1.168 2002/09/24 05:06:17 blaze // fixed spectating so ref\'s can now use all the chasecam modes. // // Revision 1.167 2002/09/07 22:40:01 jbravo // Added a scaling ctb respawn system. Fixed a bug that allowed players to // spawn before their team respawn with the team command. // // Revision 1.166 2002/09/04 00:16:17 makro // Fixed 'unselectable grenade shown in the inventory if you switch weapons // after pulling the pin' bug // // Revision 1.165 2002/08/27 04:53:46 niceass // one more woops // // Revision 1.164 2002/08/27 04:50:54 niceass // woops // // Revision 1.163 2002/08/27 04:48:13 niceass // ref say added // // Revision 1.162 2002/08/24 07:58:49 niceass // moved sanitizestring to g_util // // Revision 1.161 2002/08/23 14:25:05 slicer // Added a new Referee System with multiple ref support // // Revision 1.160 2002/08/21 07:00:07 jbravo // Added CTB respawn queue and fixed game <-> cgame synch problem in CTB // // Revision 1.159 2002/08/17 01:56:31 jbravo // % vars now only work in say_team mode. // // Revision 1.158 2002/08/13 16:59:16 makro // Fixed per-client callvote limit; added a new cvar - g_RQ3_maxClientVotes // // Revision 1.157 2002/08/07 20:49:21 slicer // Adapted Vote system to Matchmode // // Revision 1.156 2002/07/16 04:08:28 niceass // temporary hack solution for map rotation and ctb // // Revision 1.155 2002/07/11 04:31:07 niceass // removed team leaders in all gametypes and changed some text from centerprint to console print // // Revision 1.154 2002/07/09 05:41:48 niceass // scoreboard fix // // Revision 1.153 2002/07/09 03:24:00 niceass // fixed team talk // // Revision 1.152 2002/07/08 04:25:02 niceass // changes to who can be seen dead. removed deaths // // Revision 1.151 2002/07/07 23:54:57 jbravo // Say and Say_team now resets the idle timer // // Revision 1.150 2002/07/04 04:20:41 jbravo // Fixed my weaponchange cancel in the Use cmd, and fixed the bug where players // that where in eye spectating someone moved on to another player instantly on death. // // Revision 1.149 2002/07/02 20:22:35 jbravo // Changed the files to use the right ui. // // Revision 1.148 2002/07/02 19:15:17 jbravo // Drop weapon with akimbos now behaves like AQ, plus no suicides during LCA // // Revision 1.147 2002/06/29 04:15:15 jbravo // CTF is now CTB. no weapons while the case is in hand other than pistol or knife // // Revision 1.146 2002/06/28 21:41:12 jbravo // Gawd damn buglet // // Revision 1.145 2002/06/24 05:51:51 jbravo // CTF mode is now semi working // // Revision 1.144 2002/06/23 23:32:29 jbravo // Fixed logging of clients IP addresses. // // Revision 1.143 2002/06/23 21:44:08 jbravo // Fixed shots fired stats for non TP modes and some cleanups // // Revision 1.142 2002/06/23 19:27:18 niceass // bandage bug fix // // Revision 1.141 2002/06/23 15:22:53 slicer // Future 2.1 Matchmode Features - PART II // // Revision 1.140 2002/06/23 03:04:09 assimon // Added suport for callvote map and ref map . // // Revision 1.139 2002/06/21 00:05:55 slicer // Spectators can now use say_team to communicate among each others, DM and TP // // Revision 1.138 2002/06/20 22:32:43 jbravo // Added last damaged player and fixed a test2 model problem (atrimum my ass :) // Changed g_RQ3_printOwnObits to g_RQ3_showOwnKills and it also controls $K // // Revision 1.137 2002/06/20 21:06:07 freud // Added playing of lens.wav when using the unzoom command // // Revision 1.136 2002/06/20 18:40:17 slicer // Future 2.1 Matchmode Features - PART I // // Revision 1.135 2002/06/20 02:27:30 jbravo // Now the scoreboard doesnt show whos alive and whos not when you are alive // // Revision 1.134 2002/06/19 18:18:09 jbravo // Small cleanups for compiler warnings // // Revision 1.133 2002/06/18 09:23:23 niceass // small callvote upgrade // // Revision 1.132 2002/06/18 03:57:38 jbravo // Committing for aasimon. Callvote nextmap removed and replaced with cyclemap for .ini // // Revision 1.131 2002/06/17 16:57:39 jbravo // Items can now be dropped during bandaging // // Revision 1.130 2002/06/17 03:22:58 jbravo // Base voting system is now fixed. // // Revision 1.129 2002/06/16 20:06:14 jbravo // Reindented all the source files with "indent -kr -ut -i8 -l120 -lc120 -sob -bad -bap" // // Revision 1.128 2002/06/13 17:01:30 slicer // Radio Gender changes according to model gender // // Revision 1.127 2002/06/12 22:32:24 slicer // Even better way to improve the Cvar Anti-Cheat System // // Revision 1.126 2002/06/10 23:32:03 slicer // Tweaked the weapon queuing // // Revision 1.125 2002/06/10 19:10:59 jbravo // Voting system fixed for TP // // Revision 1.124 2002/06/10 19:04:21 slicer // Passing RefID trought scoreboard, forgot to add // // Revision 1.123 2002/06/10 03:26:04 jbravo // Fixed two small errors // // Revision 1.122 2002/06/09 18:58:00 makro // no message // // Revision 1.121 2002/06/07 19:07:08 slicer // removed cvars for teamXready, replaced by level.teamXready // // Revision 1.120 2002/06/05 23:53:05 jbravo // Color fixes for player names // // Revision 1.119 2002/06/05 04:57:50 niceass // "team" command fix // // Revision 1.118 2002/06/03 19:21:16 niceass // matchmode scoreboard changes. untested // // Revision 1.117 2002/06/03 05:56:04 jbravo // server used say for cmds issued during intermission // // Revision 1.116 2002/06/03 00:46:08 niceass // match scoreboard changes // // Revision 1.115 2002/06/02 00:13:39 makro // Spectators can vote in TP, not just call a vote // // Revision 1.114 2002/05/31 18:17:10 makro // Bot stuff. Added a server command that prints a line to a client // and everyone who is spectating him // // Revision 1.113 2002/05/21 04:58:28 blaze // kicked the reload bugs ass! // // Revision 1.112 2002/05/19 17:50:33 jbravo // Team fixes for DM // // Revision 1.111 2002/05/15 12:46:32 makro // Small func_static change.Give ammo should now give grenades/knives // // Revision 1.110 2002/05/12 19:15:47 jbravo // Added playerlist, did some cleanup on votes. // // Revision 1.109 2002/05/12 16:10:19 jbravo // Added unignore // // Revision 1.108 2002/05/12 12:15:05 slicer // Added Referee command for captains // // Revision 1.107 2002/05/11 19:52:09 slicer // Added sub and captain to the scoreboard function // // Revision 1.106 2002/05/10 08:10:18 jbravo // Fixed a bug in the Obit system and a give (weapons or all) bug // // Revision 1.105 2002/05/10 04:06:27 jbravo // Added Ignore // // Revision 1.104 2002/05/06 00:35:49 jbravo // Small fixes to misc stuff // // Revision 1.103 2002/05/05 15:51:16 slicer // Captain and subs get saved on map_restarts ( moved to "sess" ) // // Revision 1.102 2002/05/04 16:19:02 jbravo // Fixing the stuff cmd to work on dedicated servers. // // Revision 1.101 2002/05/01 18:44:36 jbravo // Added a stuff command. Needed for misc things. See bottum of cmd_use in // g_teamplay.c // // Revision 1.100 2002/04/30 11:54:37 makro // Bots rule ! Also, added clips to give all. Maybe some other things // // Revision 1.99 2002/04/30 01:23:05 jbravo // Changed the server logging to be more like a normal AQ server. Cleaned // all colors from the logs. // // Revision 1.98 2002/04/28 11:03:46 slicer // Added "teammodel" for Matchmode, Referee "pause" command // // Revision 1.97 2002/04/26 03:39:34 jbravo // added tkok, fixed players always leaving zcam modes when player thats // beeing tracked dies // // Revision 1.96 2002/04/23 06:03:05 niceass // scoreboard stuff // // Revision 1.95 2002/04/18 16:13:23 jbravo // Scoreboard now shows green for live players and white for dead. // Time should not get reset on deaths any more. // // Revision 1.94 2002/04/14 12:55:03 jbravo // Cleaned up cmd_reload while hunting for the m3 reload bug // // Revision 1.93 2002/04/13 15:37:53 jbravo // limchasecam has been redone with new spec system // // Revision 1.92 2002/04/07 12:57:36 slicer // Small fix on Matchmode Captain system // // Revision 1.90 2002/04/07 03:22:48 jbravo // Tweaks and crashbug fixes // // Revision 1.89 2002/04/05 18:53:26 jbravo // Warning fixes // // Revision 1.88 2002/04/01 22:23:14 slicer // Added "weapon" command buffering | Solved Gren Mode Bug // // Revision 1.87 2002/03/31 23:41:45 jbravo // Added the use command // // Revision 1.86 2002/03/31 03:31:24 jbravo // Compiler warning cleanups // // Revision 1.85 2002/03/30 23:20:10 jbravo // Added damage in scoreboard. // // Revision 1.84 2002/03/30 21:51:42 jbravo // Removed all those ifdefs for zcam. // // Revision 1.83 2002/03/30 02:29:43 jbravo // Lots of spectator code updates. Removed debugshit, added some color. // // Revision 1.82 2002/03/26 11:32:05 jbravo // Remember specstate between rounds. // // Revision 1.81 2002/03/23 05:17:42 jbravo // Major cleanup of game -> cgame communication with LCA vars. // // Revision 1.80 2002/03/21 19:22:12 jbravo // Bando now adds extra ammo to the special weaps, and when its dropped it goes // away again. // // Revision 1.79 2002/03/18 19:18:39 slicer // Fixed bandage bugs ( i hope ) // // Revision 1.78 2002/03/18 13:39:24 jbravo // Spectators in TP can now use callvote // // Revision 1.77 2002/03/18 13:32:53 jbravo // Fixed the fraglines for sniper head kills and twekaed bandaging a bit for // testing // // Revision 1.76 2002/03/18 04:37:10 jbravo // Removing locations from teamtalk on dead players. // // Revision 1.75 2002/03/18 01:22:35 slicer // bandage bleedtick back to original // // Revision 1.74 2002/03/17 15:18:55 jbravo // Added 2 checks to debugshit // // Revision 1.73 2002/03/17 13:41:28 jbravo // Added a debug cmd to print out stuff when bugs occor // // Revision 1.72 2002/03/17 00:40:23 jbravo // Adding variable team names. g_RQ3_team1name and g_RQ3_team2name. Fixed // Slicers fraglimit check. // // Revision 1.71 2002/03/14 23:54:12 jbravo // Added a variable system from AQ. Works the same except it uses $ for % // // Revision 1.70 2002/03/14 02:24:39 jbravo // Adding radio :) // // Revision 1.69 2002/03/13 18:40:52 slicer // Adjusted some of elder's unzoom code for the new sniper system ( server side ) // // Revision 1.68 2002/03/07 19:46:47 jbravo // No dropping weapons or items if bandaging // // Revision 1.67 2002/03/07 00:00:54 assimon // Added a skeleton referee suport, with some functional commands (map_restart and kick) // // Revision 1.66 2002/03/03 21:46:26 blaze // weapon stats, done, beta test for bugs // // Revision 1.65 2002/03/03 02:04:14 jbravo // Zcam tewaking // // Revision 1.64 2002/03/02 14:54:24 jbravo // Added the skin and model names to the name of the player thats being // followed, as in AQ // // Revision 1.63 2002/03/01 20:22:31 jbravo // Fixed a small booboo with the teamcounts // // Revision 1.62 2002/03/01 20:02:34 jbravo // Added ui_RQ3_teamCount1, ui_RQ3_teamCount2 and ui_RQ3_numSpectators for // makro // // Revision 1.61 2002/02/28 05:42:31 blaze // weapons stats on server side // // Revision 1.60 2002/02/27 01:54:29 jbravo // More spectatorfixes and finally stopped all fallingdamage anims and // sounds during LCA. // // Revision 1.59 2002/02/26 21:59:10 jbravo // Fixed death on switching teams while dead // // Revision 1.58 2002/02/26 04:59:10 jbravo // Fixed teamswitching and team broadcasting // // Revision 1.57 2002/02/26 03:46:53 jbravo // Range can now be set on grenades. // // Revision 1.56 2002/02/26 01:10:19 jbravo // Dead people cant speak to the living any more. // // Revision 1.55 2002/02/25 19:41:53 jbravo // Fixed the use ESC and join menu to join teams when dead players are // spectating in TP mode. // Tuned the autorespawn system a bit. Now dead ppl. are dead for a very // small time before they are made into spectators. // // Revision 1.54 2002/02/25 17:54:57 jbravo // Added [DEAD] tags infront of players names where appropriate and made // the server log conversation like AQ does. // // Revision 1.53 2002/02/23 18:07:18 slicer // Changed Sniper code and Cam code // // Revision 1.52 2002/02/22 02:13:13 jbravo // Fixed a few bugs and did some cleanups // // Revision 1.51 2002/02/10 22:02:06 niceass // took some debug info out // // Revision 1.50 2002/02/10 08:16:12 niceass // added deaths to the scoreboard // // Revision 1.49 2002/02/09 00:10:12 jbravo // Fixed spectator follow and free and updated zcam to 1.04 and added the // missing zcam files. // // Revision 1.48 2002/02/05 23:41:27 slicer // More on matchmode.. // // Revision 1.47 2002/02/04 00:10:49 slicer // Matchmode: Teams Ready/Not Ready goes by cvar MM_team1/2 // // Revision 1.43 2002/02/02 16:34:02 slicer // Matchmode.. // // Revision 1.42 2002/01/31 02:53:28 blaze // err, make that playerstats command // // Revision 1.41 2002/01/31 02:52:49 blaze // some basic work on the trains/elevators // // Revision 1.40 2002/01/31 02:25:31 jbravo // Adding limchasecam. // // Revision 1.39 2002/01/11 20:20:58 jbravo // Adding TP to main branch // // Revision 1.38 2002/01/11 19:48:30 jbravo // Formatted the source in non DOS format. // // Revision 1.37 2001/12/31 16:28:42 jbravo // I made a Booboo with the Log tag. // // //----------------------------------------------------------------------------- // Copyright (C) 1999-2000 Id Software, Inc. // #include "g_local.h" #include "zcam.h" //Blaze: was there a extra ../ here? #include "../ui/menudef.h" // for the voice chats //Blaze for door code void Use_BinaryMover(gentity_t * ent, gentity_t * other, gentity_t * activator); // JBravo: for kicking code gentity_t *getEntByName(char *name); /* ================== DeathmatchScoreboardMessage ================== */ void DeathmatchScoreboardMessage(gentity_t * ent) { char entry[1024], string[1400]; int stringlength, i, j; gclient_t *cl; int numSorted, scoreFlags, accuracy; int alive; // send the latest information on all clients string[0] = 0; stringlength = 0; scoreFlags = 0; numSorted = level.numConnectedClients; for (i = 0; i < numSorted; i++) { int ping; cl = &level.clients[level.sortedClients[i]]; if (cl->pers.connected == CON_CONNECTING) { ping = -1; } else { ping = cl->ps.ping < 999 ? cl->ps.ping : 999; //ping = cl->pers.realPing < 999 ? cl->pers.realPing : 999; } if (cl->accuracy_shots) { accuracy = cl->accuracy_hits * 100 / cl->accuracy_shots; } else { accuracy = 0; } // NiceAss: No one can see who is dead or alive in matchmode when alive. // But if you are dead, you can see who is dead/alive on your team. alive = cl->sess.sessionTeam != TEAM_SPECTATOR; if (g_gametype.integer >= GT_TEAM && level.team_round_going) { if (ent->client->sess.sessionTeam != TEAM_SPECTATOR) alive = qtrue; if (g_RQ3_matchmode.integer && ent->client->sess.sessionTeam == TEAM_SPECTATOR && ent->client->sess.savedTeam != cl->sess.savedTeam) alive = qtrue; } //Blaze: Prit out some Debug info if (&g_entities[level.sortedClients[i]] == NULL) G_Printf("Ln 1399\n"); Com_sprintf(entry, sizeof(entry), " %i %i %i %i %i %i %i %i %i %i %i %i", level.sortedClients[i], cl->ps.persistant[PERS_SCORE], ping, (level.time - cl->pers.enterTime) / 60000, scoreFlags, g_entities[level.sortedClients[i]].s.powerups, accuracy, cl->ps.persistant[PERS_DAMAGE_DELT], // JBravo: Added for scoreboard alive, // JBravo: Added for TP scoreboard cl->sess.captain, // Slicer: Added for Matchmode Scoreboard cl->sess.sub, // Slicer: Added for Matchmode Scoreboard cl->sess.referee ); j = strlen(entry); if (stringlength + j >= sizeof(string)) break; strcpy(string + stringlength, entry); stringlength += j; } trap_SendServerCommand(ent - g_entities, va("scores %i %i %i %i %i %i %i%s", i, level.teamScores[TEAM_RED], level.teamScores[TEAM_BLUE], level.team1ready, level.team2ready, (int) level.matchTime, level.refAmmount ? level.refStatus : -1,string)); } /* ================== Cmd_Score_f Request current scoreboard information ================== */ void Cmd_Score_f(gentity_t * ent) { DeathmatchScoreboardMessage(ent); } /* ================== Cmd_WeaponStats_f Request current weaponstats information ================== */ void Cmd_WeaponStats_f(gentity_t * ent) { trap_SendServerCommand(ent - g_entities, va("wstats %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i", ent->client->pers.records[REC_KNIFETHROWSHOTS], ent->client->pers.records[REC_KNIFETHROWHITS], ent->client->pers.records[REC_KNIFESLASHSHOTS], ent->client->pers.records[REC_KNIFESLASHHITS], ent->client->pers.records[REC_MK23SHOTS], ent->client->pers.records[REC_MK23HITS], ent->client->pers.records[REC_M3SHOTS], ent->client->pers.records[REC_M3HITS], ent->client->pers.records[REC_MP5SHOTS], ent->client->pers.records[REC_MP5HITS], ent->client->pers.records[REC_M4SHOTS], ent->client->pers.records[REC_M4HITS], ent->client->pers.records[REC_SSG3000SHOTS], ent->client->pers.records[REC_SSG3000HITS], ent->client->pers.records[REC_HANDCANNONSHOTS], ent->client->pers.records[REC_HANDCANNONHITS], ent->client->pers.records[REC_AKIMBOSHOTS], ent->client->pers.records[REC_AKIMBOHITS], ent->client->pers.records[REC_GRENADESHOTS], ent->client->pers.records[REC_GRENADEHITS], ent->client->pers.records[REC_KICKHITS], ent->client->pers.records[REC_KILLS], ent->client->pers.records[REC_TEAMKILLS], ent->client->pers.records[REC_SUICIDES])); trap_SendServerCommand(ent - g_entities, va("wstats2 %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i", ent->client->pers.records[REC_KNIFETHROWDEATHS], ent->client->pers.records[REC_KNIFETHROWKILLS], ent->client->pers.records[REC_KNIFESLASHDEATHS], ent->client->pers.records[REC_KNIFESLASHKILLS], ent->client->pers.records[REC_MK23DEATHS], ent->client->pers.records[REC_MK23KILLS], ent->client->pers.records[REC_M3DEATHS], ent->client->pers.records[REC_M3KILLS], ent->client->pers.records[REC_MP5DEATHS], ent->client->pers.records[REC_MP5KILLS], ent->client->pers.records[REC_M4DEATHS], ent->client->pers.records[REC_M4KILLS], ent->client->pers.records[REC_SSG3000DEATHS], ent->client->pers.records[REC_SSG3000KILLS], ent->client->pers.records[REC_HANDCANNONDEATHS], ent->client->pers.records[REC_HANDCANNONKILLS], ent->client->pers.records[REC_AKIMBODEATHS], ent->client->pers.records[REC_AKIMBOKILLS], ent->client->pers.records[REC_GRENADEDEATHS], ent->client->pers.records[REC_GRENADEKILLS], ent->client->pers.records[REC_KICKDEATHS], ent->client->pers.records[REC_KICKKILLS])); } /* ================== CheatsOk ================== */ qboolean CheatsOk(gentity_t * ent) { if (!g_cheats.integer) { trap_SendServerCommand(ent - g_entities, va("print \"^1Cheats are not enabled on this server.\n\"")); return qfalse; } if (ent->health <= 0) { trap_SendServerCommand(ent - g_entities, va("print \"^1You must be alive to use this command.\n\"")); return qfalse; } return qtrue; } /* ================== ConcatArgs ================== */ char *ConcatArgs(int start) { int i, c, tlen; static char line[MAX_STRING_CHARS]; int len; char arg[MAX_STRING_CHARS]; len = 0; c = trap_Argc(); for (i = start; i < c; i++) { trap_Argv(i, arg, sizeof(arg)); tlen = strlen(arg); if (len + tlen >= MAX_STRING_CHARS - 1) { break; } memcpy(line + len, arg, tlen); len += tlen; if (i != c - 1) { line[len] = ' '; len++; } } line[len] = 0; return line; } /* ================== ClientNumberFromString Returns a player number for either a number or name string Returns -1 if invalid ================== */ int ClientNumberFromString(gentity_t * to, char *s) { gclient_t *cl; int idnum; char s2[MAX_STRING_CHARS]; char n2[MAX_STRING_CHARS]; // numeric values are just slot numbers if (s[0] >= '0' && s[0] <= '9') { idnum = atoi(s); if (idnum < 0 || idnum >= level.maxclients) { trap_SendServerCommand(to - g_entities, va("print \"^1Bad client slot: %i\n\"", idnum)); return -1; } cl = &level.clients[idnum]; if (cl->pers.connected != CON_CONNECTED) { trap_SendServerCommand(to - g_entities, va("print \"^1Client %i is not active\n\"", idnum)); return -1; } return idnum; } // check for a name match SanitizeString(s, s2); for (idnum = 0, cl = level.clients; idnum < level.maxclients; idnum++, cl++) { if (cl->pers.connected != CON_CONNECTED) { continue; } SanitizeString(cl->pers.netname, n2); if (!strcmp(n2, s2)) { return idnum; } } trap_SendServerCommand(to - g_entities, va("print \"^1User %s is not on the server\n\"", s)); return -1; } /* ================== Cmd_Give_f Give items to a client ================== */ void Cmd_Give_f(gentity_t * ent) { char *name; gitem_t *it; int i; qboolean give_all; gentity_t *it_ent; trace_t trace; if (!CheatsOk(ent)) { return; } name = ConcatArgs(1); if (Q_stricmp(name, "all") == 0) give_all = qtrue; else give_all = qfalse; if (give_all || Q_stricmp(name, "health") == 0) { ent->health = 100; // 100 max health ent->client->ps.stats[STAT_MAX_HEALTH]; if (!give_all) return; } if (give_all || Q_stricmp(name, "weapons") == 0) { //Elder: basically it sets all the STAT_WEAPONS bits to 1 EXCEPT for WP_NONE and //the initial bit (I don't know what that is) //http://www.iota-six.freeserve.co.uk/c/bitwise.htm ent->client->ps.stats[STAT_WEAPONS] = (1 << WP_NUM_WEAPONS) - 1 - (1 << WP_NONE); // JBravo: so we can drop any of these weapons ent->client->uniqueWeapons = 5; ent->client->weaponCount[WP_SSG3000] = 1; ent->client->weaponCount[WP_MP5] = 1; ent->client->weaponCount[WP_M3] = 1; ent->client->weaponCount[WP_M4] = 1; ent->client->weaponCount[WP_AKIMBO] = 1; ent->client->weaponCount[WP_HANDCANNON] = 1; //Makro - added knives ent->client->weaponCount[WP_KNIFE] = 1; if (!give_all) return; } if (give_all || Q_stricmp(name, "items") == 0) { // JBravo: for the new items system ent->client->ps.stats[STAT_HOLDABLE_ITEM] = (1 << HI_KEVLAR); ent->client->ps.stats[STAT_HOLDABLE_ITEM] |= (1 << HI_LASER); ent->client->ps.stats[STAT_HOLDABLE_ITEM] |= (1 << HI_SILENCER); ent->client->ps.stats[STAT_HOLDABLE_ITEM] |= (1 << HI_BANDOLIER); ent->client->ps.stats[STAT_HOLDABLE_ITEM] |= (1 << HI_SLIPPERS); if (g_RQ3_haveHelmet.integer) { ent->client->ps.stats[STAT_HOLDABLE_ITEM] |= (1 << HI_HELMET); ent->client->uniqueItems = 6; } else { ent->client->uniqueItems = 5; } } if (give_all || Q_stricmp(name, "ammo") == 0) { for (i = 0; i < MAX_WEAPONS; i++) { //Blaze: Give right amount of shots to each gun if (i == WP_KNIFE) ent->client->ps.ammo[i] = 10; else ent->client->ps.ammo[i] = ClipAmountForAmmo(i); //Makro - I want clips, dammit ! Add_Ammo(ent, i, 100, 1); } //Makro - added grenade + knife ent->client->ps.stats[STAT_WEAPONS] |= (1 << WP_GRENADE) | (1 << WP_KNIFE); if (!give_all) return; } // spawn a specific item right on the player if (!give_all) { it = BG_FindItem(name); if (!it) { return; } it_ent = G_Spawn(); VectorCopy(ent->r.currentOrigin, it_ent->s.origin); it_ent->classname = it->classname; G_SpawnItem(it_ent, it); FinishSpawningItem(it_ent); memset(&trace, 0, sizeof(trace)); Touch_Item(it_ent, ent, &trace); if (it_ent->inuse) { G_FreeEntity(it_ent); } } } /*void RQ3_Cmd_debugshit (gentity_t * ent) { G_Printf("STAT_HOLDABLE_ITEM is %d, uniqueItems is %d\n", ent->client->ps.stats[STAT_HOLDABLE_ITEM], ent->client->uniqueItems); } */ /* ================== Cmd_God_f Sets client to godmode argv(0) god ================== */ void Cmd_God_f(gentity_t * ent) { char *msg; if (!CheatsOk(ent)) { return; } ent->flags ^= FL_GODMODE; if (!(ent->flags & FL_GODMODE)) msg = "godmode OFF\n"; else msg = "godmode ON\n"; trap_SendServerCommand(ent - g_entities, va("print \"%s\"", msg)); } /* ================== Cmd_Notarget_f Sets client to notarget argv(0) notarget ================== */ void Cmd_Notarget_f(gentity_t * ent) { char *msg; if (!CheatsOk(ent)) { return; } ent->flags ^= FL_NOTARGET; if (!(ent->flags & FL_NOTARGET)) msg = "notarget OFF\n"; else msg = "notarget ON\n"; trap_SendServerCommand(ent - g_entities, va("print \"%s\"", msg)); } /* ================== Cmd_Noclip_f argv(0) noclip ================== */ void Cmd_Noclip_f(gentity_t * ent) { char *msg; if (!CheatsOk(ent)) { return; } if (ent->client->noclip) { msg = "noclip OFF\n"; } else { msg = "noclip ON\n"; } ent->client->noclip = !ent->client->noclip; trap_SendServerCommand(ent - g_entities, va("print \"%s\"", msg)); } /* ================== Cmd_LevelShot_f This is just to help generate the level pictures for the menus. It goes to the intermission immediately and sends over a command to the client to resize the view, hide the scoreboard, and take a special screenshot ================== */ void Cmd_LevelShot_f(gentity_t * ent) { if (!CheatsOk(ent)) { return; } // doesn't work in single player if (g_gametype.integer != 0) { trap_SendServerCommand(ent - g_entities, "print \"^1Must be in g_gametype 0 for levelshot\n\""); return; } if(!ent->client->pers.localClient) { trap_SendServerCommand(ent-g_entities, "print \"The levelshot command must be executed by a local client\n\""); return; } BeginIntermission(); trap_SendServerCommand(ent - g_entities, "clientLevelShot"); } /* ================== Elder: WTF... this is the wrong description, but it was like this in the 1.29h source Cmd_LevelShot_f This is just to help generate the level pictures for the menus. It goes to the intermission immediately and sends over a command to the client to resize the view, hide the scoreboard, and take a special screenshot ================== */ void Cmd_TeamTask_f(gentity_t * ent) { char userinfo[MAX_INFO_STRING]; char arg[MAX_TOKEN_CHARS]; int task; int client = ent->client - level.clients; if (trap_Argc() != 2) { return; } trap_Argv(1, arg, sizeof(arg)); task = atoi(arg); trap_GetUserinfo(client, userinfo, sizeof(userinfo)); Info_SetValueForKey(userinfo, "teamtask", va("%d", task)); trap_SetUserinfo(client, userinfo); ClientUserinfoChanged(client); } /* ================= Cmd_Kill_f ================= */ void Cmd_Kill_f(gentity_t * ent) { if (ent->client->sess.sessionTeam == TEAM_SPECTATOR) { return; } if (ent->health <= 0) { return; } if (g_gametype.integer == GT_TEAMPLAY && level.lights_camera_action) { return; } //Makro - lamer protection //(if you suicide while hit, the attacker still receives his/her/its frag) if (g_RQ3_giveMeWhatsMine.integer) { if (ent->client->lasthurt_mod != 0) { gentity_t *attacker = &g_entities[ent->client->lasthurt_client]; if (attacker != NULL) { AddScore(attacker, ent->r.currentOrigin, 1); trap_SendServerCommand(-1, va("%s tried to steal a frag from %s. And failed\n", ent->client->pers.netname, attacker->client->pers.netname)); } } } ent->flags &= ~FL_GODMODE; ent->client->ps.stats[STAT_HEALTH] = ent->health = -999; player_die(ent, ent, ent, 100000, MOD_SUICIDE); } /* ================= BroadcastTeamChange Let everyone know about a team change ================= */ void BroadcastTeamChange(gclient_t * client, int oldTeam) { // JBravo: change team names if teamplay if (g_gametype.integer >= GT_TEAM) { if (client->sess.savedTeam == TEAM_RED) { trap_SendServerCommand(-1, va("print \"%s" S_COLOR_WHITE " joined %s.\n\"", client->pers.netname, g_RQ3_team1name.string)); } else if (client->sess.savedTeam == TEAM_BLUE) { trap_SendServerCommand(-1, va("print \"%s" S_COLOR_WHITE " joined %s.\n\"", client->pers.netname, g_RQ3_team2name.string)); } else if (client->sess.savedTeam == TEAM_SPECTATOR && oldTeam != TEAM_SPECTATOR) { trap_SendServerCommand(-1, va("print \"%s" S_COLOR_WHITE " left his team.\n\"", client->pers.netname)); } } else { if (client->sess.sessionTeam == TEAM_RED) { trap_SendServerCommand(-1, va("print \"%s" S_COLOR_WHITE " joined the red team.\n\"", client->pers.netname)); } else if (client->sess.sessionTeam == TEAM_BLUE) { trap_SendServerCommand(-1, va("print \"%s" S_COLOR_WHITE " joined the blue team.\n\"", client->pers.netname)); } else if (client->sess.sessionTeam == TEAM_SPECTATOR && oldTeam != TEAM_SPECTATOR) { trap_SendServerCommand(-1, va("print \"%s" S_COLOR_WHITE " joined the spectators.\n\"", client->pers.netname)); } else if (client->sess.sessionTeam == TEAM_FREE) { trap_SendServerCommand(-1, va("print \"%s" S_COLOR_WHITE " joined the battle.\n\"", client->pers.netname)); } } } /* ================= SetTeam ================= */ void SetTeam(gentity_t * ent, char *s) { int team, oldTeam, clientNum; gclient_t *client; spectatorState_t specState = 0; int specClient = 0, teamsave; //int teamLeader; // // see what change is requested // client = ent->client; clientNum = client - level.clients; if (g_gametype.integer < GT_TEAM) { specClient = 0; specState = SPECTATOR_NOT; } if (!Q_stricmp(s, "scoreboard") || !Q_stricmp(s, "score")) { team = TEAM_SPECTATOR; specState = SPECTATOR_SCOREBOARD; } else if (!Q_stricmp(s, "follow1")) { team = TEAM_SPECTATOR; specState = SPECTATOR_FOLLOW; specClient = -1; } else if (!Q_stricmp(s, "follow2")) { team = TEAM_SPECTATOR; specState = SPECTATOR_FOLLOW; specClient = -2; // JBravo: adding aliases none for spectator, 1 for team red and 2 for team blue. } else if (!Q_stricmp(s, "spectator") || !Q_stricmp(s, "s") || !Q_stricmp(s, "none")) { team = TEAM_SPECTATOR; specState = SPECTATOR_FREE; } else if (g_gametype.integer >= GT_TEAM) { // if running a team game, assign player to one of the teams if (g_gametype.integer < GT_TEAM) { specState = SPECTATOR_NOT; } if (!Q_stricmp(s, "red") || !Q_stricmp(s, "r") || !Q_stricmp(s, "1")) { team = TEAM_RED; } else if (!Q_stricmp(s, "blue") || !Q_stricmp(s, "b") || !Q_stricmp(s, "2")) { team = TEAM_BLUE; } else if (!Q_stricmp(s, "auto")) { team = PickTeam(clientNum); } else { // pick the team with the least number of players trap_SendServerCommand(ent->client->ps.clientNum, va("print \"^1%s is an illegal team. Putting you on the team with the least number of players.\n\"", s)); team = PickTeam(clientNum); } if (g_teamForceBalance.integer) { int counts[TEAM_NUM_TEAMS]; counts[TEAM_BLUE] = TeamCount( clientNum, TEAM_BLUE ); counts[TEAM_RED] = TeamCount( clientNum, TEAM_RED ); // We allow a spread of two if (team == TEAM_RED && counts[TEAM_RED] - counts[TEAM_BLUE] > 1) { trap_SendServerCommand(clientNum, "cp \"Red team has too many players.\n\""); return; // ignore the request } if (team == TEAM_BLUE && counts[TEAM_BLUE] - counts[TEAM_RED] > 1) { trap_SendServerCommand(clientNum, "cp \"Blue team has too many players.\n\""); return; // ignore the request } // It's ok, the team we are switching to has less or same number of players } } else { // force them to spectators if there aren't any spots free team = TEAM_FREE; } // override decision if limiting the players if ((g_gametype.integer == GT_TOURNAMENT) && level.numNonSpectatorClients >= 2) { team = TEAM_SPECTATOR; } else if (g_maxGameClients.integer > 0 && level.numNonSpectatorClients >= g_maxGameClients.integer) { team = TEAM_SPECTATOR; } // // decide if we will allow the change // // JBravo: we use the savedTeam var because the player meight be dead. if (g_gametype.integer >= GT_TEAM) { oldTeam = client->sess.savedTeam; } else { oldTeam = client->sess.sessionTeam; } if (team == oldTeam) { return; } // JBravo: we want it to be OK to change from FREE to SPECTATOR without dieing. if (g_gametype.integer == GT_TEAMPLAY && oldTeam == TEAM_FREE && team == TEAM_SPECTATOR) { return; } // // execute the team change // //sLiCeR: Matchmode referee hear all protection if(g_gametype.integer >= GT_TEAM && (oldTeam == TEAM_FREE || oldTeam == TEAM_SPECTATOR) && (team == TEAM_RED || team == TEAM_BLUE)) ent->client->sess.refHear = qfalse; // if the player was dead leave the body if (client->ps.stats[STAT_HEALTH] <= 0) { CopyToBodyQue(ent); } // he starts at 'base' client->pers.teamState.state = TEAM_BEGIN; // JBravo: if player is changing from FREE or SPECT. there is no need for violence. if (client->sess.sessionTeam != TEAM_SPECTATOR && client->sess.sessionTeam != TEAM_FREE && client->ps.pm_type == PM_NORMAL) { // Kill him (makes sure he loses flags, etc) ent->flags &= ~FL_GODMODE; ent->client->ps.stats[STAT_HEALTH] = ent->health = 0; player_die(ent, ent, ent, 100000, MOD_SUICIDE); } // JBravo: DM players switching to spectators should also die. if (g_gametype.integer == GT_FFA && team == TEAM_SPECTATOR && client->ps.pm_type == PM_NORMAL) { ent->flags &= ~FL_GODMODE; ent->client->ps.stats[STAT_HEALTH] = ent->health = 0; player_die(ent, ent, ent, 100000, MOD_SUICIDE); } // JBravo: lets set the correct var here. if (g_gametype.integer >= GT_TEAM) { client->sess.savedTeam = team; client->ps.persistant[PERS_SAVEDTEAM] = team; } else { client->sess.sessionTeam = team; } //Slicer : Matchmode - If a captain changes team , that team is no longer ready if (g_RQ3_matchmode.integer) { switch (ent->client->sess.captain) { case TEAM_RED: level.team1ready = qfalse; break; case TEAM_BLUE: level.team2ready = qfalse; break; default: break; } ent->client->sess.captain = TEAM_FREE; ent->client->sess.sub = TEAM_FREE; } // they go to the end of the line for tournements if (team == TEAM_SPECTATOR && oldTeam != team) { client->sess.spectatorTime = level.time; } // JBravo: not messing with spec system in TP during teamswitches if (g_gametype.integer < GT_TEAM) { client->sess.spectatorState = specState; client->sess.spectatorClient = specClient; } client->sess.teamLeader = qfalse; BroadcastTeamChange(client, oldTeam); // get and distribute relevent paramters // JBravo: save sessionTeam and then set it correctly for the call to ClientUserinfoChanged // so the scoreboard will be correct. Also check for uneven teams. if (g_gametype.integer >= GT_TEAM) { if (g_RQ3_matchmode.integer && g_RQ3_maxplayers.integer > 0) { if (RQ3TeamCount(-1, client->sess.savedTeam) > g_RQ3_maxplayers.integer) // If it overflows max players //Make him a sub immeadiatly. ent->client->sess.sub = client->sess.savedTeam; } CheckForUnevenTeams(ent); teamsave = client->sess.sessionTeam; client->sess.sessionTeam = client->sess.savedTeam; ClientUserinfoChanged(clientNum); CalculateRanks(); client->sess.sessionTeam = teamsave; ResetKills(ent); client->last_damaged_players[0] = '\0'; //Slicer: Changing radio gender according to models if (client->sess.savedTeam == TEAM_RED) client->radioGender = level.team1gender; else if (client->sess.savedTeam == TEAM_BLUE) client->radioGender = level.team2gender; } else { ClientUserinfoChanged(clientNum); ClientBegin(clientNum); } if (g_gametype.integer == GT_CTF || (g_gametype.integer == GT_TEAM && client->sess.savedTeam == TEAM_SPECTATOR)) MakeSpectator (ent); // JBravo: If the game is in progress, lets spawn players joining. if (g_gametype.integer == GT_TEAM && level.team_round_going && (client->sess.savedTeam == TEAM_RED || client->sess.savedTeam == TEAM_BLUE)) { client->ps.persistant[PERS_SAVEDTEAM] = client->sess.savedTeam; client->ps.persistant[PERS_TEAM] = client->sess.savedTeam; client->sess.sessionTeam = client->sess.savedTeam; respawn (ent); } } /* ================= StopFollowing If the client being followed leaves the game, or you just want to drop to free floating spectator mode ================= */ void StopFollowing(gentity_t * ent) { vec3_t angle; ent->client->ps.persistant[PERS_TEAM] = TEAM_SPECTATOR; ent->client->sess.sessionTeam = TEAM_SPECTATOR; ent->client->sess.spectatorState = SPECTATOR_FREE; ent->client->specMode = SPECTATOR_FREE; // JBravo: clear zcam also ent->client->ps.stats[STAT_RQ3] &= ~RQ3_ZCAM; //Slicer - Removing any zoom bits he might have gainned Cmd_Unzoom(ent); ent->client->ps.pm_flags &= ~PMF_FOLLOW; ent->r.svFlags &= ~SVF_BOT; ent->client->ps.clientNum = ent - g_entities; angle[0] = angle[1] = angle[2] = 0.0; SetClientViewAngle(ent, angle); } /* ================= Cmd_Team_f ================= */ void Cmd_Team_f(gentity_t * ent) { int oldTeam; char s[MAX_TOKEN_CHARS]; //Makro - moved here if (g_gametype.integer >= GT_TEAM) { oldTeam = ent->client->sess.savedTeam; } else { oldTeam = ent->client->sess.sessionTeam; } if (trap_Argc() != 2) { // JBravo: lets keep the teamnames right. if (g_gametype.integer == GT_TEAMPLAY || g_gametype.integer == GT_TEAM) { //oldTeam = ent->client->sess.savedTeam; switch (oldTeam) { case TEAM_RED: trap_SendServerCommand(ent - g_entities, va("print \"You are a member of %s\n\"", g_RQ3_team1name.string)); break; case TEAM_BLUE: trap_SendServerCommand(ent - g_entities, va("print \"You are a member of %s\n\"", g_RQ3_team2name.string)); break; case TEAM_SPECTATOR: case TEAM_FREE: trap_SendServerCommand(ent - g_entities, "print \"You have not joined a team.\n\""); break; } } else if (g_gametype.integer == GT_CTF) { switch (oldTeam) { case TEAM_RED: trap_SendServerCommand(ent - g_entities, va("print \"You are a member of the SILVER team.\n\"")); break; case TEAM_BLUE: trap_SendServerCommand(ent - g_entities, va("print \"You are a member of the BLACK team.\n\"")); break; case TEAM_SPECTATOR: case TEAM_FREE: trap_SendServerCommand(ent - g_entities, "print \"You have not joined a team.\n\""); break; } } else { //oldTeam = ent->client->sess.sessionTeam; switch (oldTeam) { case TEAM_BLUE: trap_SendServerCommand(ent - g_entities, "print \"Blue team\n\""); break; case TEAM_RED: trap_SendServerCommand(ent - g_entities, "print \"Red team\n\""); break; case TEAM_FREE: trap_SendServerCommand(ent - g_entities, "print \"Free team\n\""); break; case TEAM_SPECTATOR: trap_SendServerCommand(ent - g_entities, "print \"Spectator team\n\""); break; } } return; } if (ent->client->switchTeamTime > level.time) { trap_SendServerCommand(ent - g_entities, "print \"^1May not switch teams more than once per 5 seconds.\n\""); return; } // if they are playing a tournement game, count as a loss if ((g_gametype.integer == GT_TOURNAMENT) && ent->client->sess.sessionTeam == TEAM_FREE) { ent->client->sess.losses++; } trap_Argv(1, s, sizeof(s)); SetTeam(ent, s); ent->client->switchTeamTime = level.time + 5000; } /* ================= Cmd_Follow_f ================= // JBravo: This cmd is only used for limchasecam cheating void Cmd_Follow_f(gentity_t * ent) { int i; char arg[MAX_TOKEN_CHARS]; if (trap_Argc() != 2) { if (ent->client->sess.spectatorState == SPECTATOR_FOLLOW) { StopFollowing(ent); } return; } trap_Argv(1, arg, sizeof(arg)); i = ClientNumberFromString(ent, arg); if (i == -1) { return; } // can't follow self if (&level.clients[i] == ent->client) { return; } // can't follow another spectator if (level.clients[i].sess.sessionTeam == TEAM_SPECTATOR) { return; } // if they are playing a tournement game, count as a loss if ((g_gametype.integer == GT_TOURNAMENT) && ent->client->sess.sessionTeam == TEAM_FREE) { ent->client->sess.losses++; } // first set them to spectator if (ent->client->sess.sessionTeam != TEAM_SPECTATOR) { SetTeam(ent, "spectator"); } ent->client->sess.spectatorState = SPECTATOR_FOLLOW; ent->client->specMode = SPECTATOR_FOLLOW; ent->client->sess.spectatorClient = i; } */ /* ================= Cmd_FollowCycle_f ================= */ void Cmd_FollowCycle_f(gentity_t * ent, int dir) { int clientnum; int original; // if they are playing a tournement game, count as a loss if ((g_gametype.integer == GT_TOURNAMENT) && ent->client->sess.sessionTeam == TEAM_FREE) { ent->client->sess.losses++; } // first set them to spectator // JBravo: Unless we are in teamplay. No need to mess with teams. if (ent->client->sess.spectatorState == SPECTATOR_NOT && g_gametype.integer < GT_TEAM) { SetTeam(ent, "spectator"); } if (dir != 1 && dir != -1) { G_Error("Cmd_FollowCycle_f: bad dir %i", dir); } clientnum = ent->client->sess.spectatorClient; original = clientnum; do { clientnum += dir; if (clientnum >= level.maxclients) { clientnum = 0; } if (clientnum < 0) { clientnum = level.maxclients - 1; } // can only follow connected clients if (level.clients[clientnum].pers.connected != CON_CONNECTED) { continue; } // can't follow another spectator if (level.clients[clientnum].sess.sessionTeam == TEAM_SPECTATOR) { continue; } // JBravo: limchasecam if (g_gametype.integer >= GT_TEAM && g_RQ3_limchasecam.integer != 0 && ent->client->sess.savedTeam != level.clients[clientnum].sess.sessionTeam && ent->client->sess.referee == 0) { G_Printf("SavedTeam = (%d)\n",ent->client->sess.savedTeam); continue; } // this is good, we can use it ent->client->sess.spectatorClient = clientnum; ent->client->sess.spectatorState = SPECTATOR_FOLLOW; ent->client->specMode = SPECTATOR_FOLLOW; return; } while (clientnum != original); // leave it where it was } /* ================== G_Say ================== */ static void G_SayTo(gentity_t * ent, gentity_t * other, int mode, int color, const char *name, const char *message) { if (!other) { return; } if (!other->inuse) { return; } if (!other->client) { return; } if (other->client->pers.connected != CON_CONNECTED) { return; } if (mode == SAY_TEAM && !OnSameTeam(ent, other) && !ent->client->sess.refHear) { //Slicer: This makes referees read say_team stuff if Hear all return; } // no chatting to players in tournements if ((g_gametype.integer == GT_TOURNAMENT) && other->client->sess.sessionTeam == TEAM_FREE && ent->client->sess.sessionTeam != TEAM_FREE && mode != SAY_REF) { return; } // JBravo: is the guy ignored ? if (IsInIgnoreList(other, ent) && mode != SAY_REF) return; // JBravo: Dead people dont speak to the living... or so Im told. if (!G_PlayerAlive(ent) && G_PlayerAlive(other) && g_gametype.integer == GT_TEAMPLAY && level.team_round_going && mode != SAY_REF) return; trap_SendServerCommand(other - g_entities, va("%s \"%s%c%c%s\n\"", mode == SAY_TEAM ? "tchat" : "chat", name, Q_COLOR_ESCAPE, color, message)); } #define EC "\x19" void G_Say(gentity_t * ent, gentity_t * target, int mode, const char *chatText) { int j; gentity_t *other; int color; char name[64]; // don't let text be too long for malicious reasons char text[MAX_SAY_TEXT]; char location[64]; int validation; // Elder: validate the client validation = RQ3_ValidateSay(ent); ent->client->idletime = 0; if (validation != SAY_OK) { // Only send one message for the initial offense if (ent->client->pers.sayMuteTime == level.time && ent->client->pers.sayModerated == qfalse) { ent->client->pers.sayModerated = qtrue; if (validation == SAY_WARNING) { trap_SendServerCommand(ent - g_entities, va ("print \"Exceeded message limit - ^3WARNING ^7(%i seconds).\n\"", g_RQ3_messageWarnTime.integer)); G_LogPrintf("Server: %s received a message protect warning (offense %i)\n", ent->client->pers.netname, ent->client->pers.sayWarnings); } else if (validation == SAY_BAN) { // Don't bother printing if kicked if (g_RQ3_messageBanTime.integer > 0) trap_SendServerCommand(ent - g_entities, va ("print \"Exceeded message limit - ^1BAN ^7(%i seconds).\n\"", g_RQ3_messageBanTime.integer)); G_LogPrintf("Server: %s received a message protect ban (offense %i)\n", ent->client->pers.netname, ent->client->pers.sayBans); } } return; } // Slicer: DM now has [SPECTATOR] // JBravo: adding below the [DEAD] tag infront of dead players names. switch (mode) { default: case SAY_ALL: if (ent->client->sess.sessionTeam == TEAM_SPECTATOR) { if (g_gametype.integer < GT_TEAM) // DM, SPECTATOR Com_sprintf(name, sizeof(name), "[SPECTATOR] %s%c%c" EC ": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE); else { if (ent->client->sess.savedTeam == TEAM_SPECTATOR) Com_sprintf(name, sizeof(name), "%s %s%c%c" EC ": ",ent->client->sess.referee ? "[REFEREE]":"[SPECTATOR]", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE); else Com_sprintf(name, sizeof(name), "%s %s%c%c" EC ": ",ent->client->sess.referee ? "[REFEREE]":"[DEAD]", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE); } } else { Com_sprintf(name, sizeof(name), "%s%c%c" EC ": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE); } color = COLOR_GREEN; break; case SAY_TEAM: if (ent->client->sess.sessionTeam == TEAM_SPECTATOR) { if(g_gametype.integer < GT_TEAM) // DM, SPECTATOR Com_sprintf(name, sizeof(name), EC "[SPECTATOR] (%s%c%c" EC ")" EC ": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE); else { if (ent->client->sess.savedTeam == TEAM_SPECTATOR) Com_sprintf(name, sizeof(name), EC "%s (%s%c%c" EC ")" EC ": ",ent->client->sess.referee ? "[REFEREE]":"[SPECTATOR]", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE); else Com_sprintf(name, sizeof(name), EC "[DEAD] (%s%c%c" EC ")" EC ": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE); } } else { if (Team_GetLocationMsg(ent, location, sizeof(location))) Com_sprintf(name, sizeof(name), EC "(%s%c%c" EC ") (%s)" EC ": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, location); else Com_sprintf(name, sizeof(name), EC "(%s%c%c" EC ")" EC ": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE); } color = COLOR_CYAN; break; case SAY_TELL: if (target && g_gametype.integer == GT_TEAMPLAY && target->client->sess.savedTeam == ent->client->sess.savedTeam && Team_GetLocationMsg(ent, location, sizeof(location))) Com_sprintf(name, sizeof(name), EC "[%s%c%c" EC "] (%s)" EC ": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, location); else if (target && g_gametype.integer >= GT_TEAM && target->client->sess.sessionTeam == ent->client->sess.sessionTeam && Team_GetLocationMsg(ent, location, sizeof(location))) Com_sprintf(name, sizeof(name), EC "[%s%c%c" EC "] (%s)" EC ": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, location); else Com_sprintf(name, sizeof(name), EC "[%s%c%c" EC "]" EC ": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE); color = COLOR_MAGENTA; break; case SAY_REF: Com_sprintf(name, sizeof(name), "[REFEREE] %s%c%c" EC ": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE); color = COLOR_WHITE; break; } // JBravo: Parsing % vars here if (ent->client->sess.sessionTeam != TEAM_SPECTATOR && mode == SAY_TEAM) { ParseSayText(ent, (char *) chatText); } Q_strncpyz(text, chatText, sizeof(text)); if (target) { G_SayTo(ent, target, mode, color, name, text); return; } // echo the text to the console if (g_dedicated.integer) { G_Printf("%s%s\n", name, text); } // JBravo: Log it like AQ does G_LogPrintf("%s%s\n", name, text); // send it to all the apropriate clients for (j = 0; j < level.maxclients; j++) { //Blaze: Prit out some Debug info if (&g_entities[j] == NULL) G_Printf("Ln 1532\n"); other = &g_entities[j]; G_SayTo(ent, other, mode, color, name, text); } } /* ================== Cmd_ay_f ================== */ static void Cmd_Say_f(gentity_t * ent, int mode, qboolean arg0) { char *p; qboolean normaluser; if (trap_Argc() < 2 && !arg0) { return; } if (arg0) { p = ConcatArgs(0); } else { p = ConcatArgs(1); } //Slicer Matchmode if (g_RQ3_matchmode.integer) { normaluser = (ent->client->sess.captain == TEAM_FREE && !ent->client->sess.referee); switch (g_RQ3_forceteamtalk.integer) { case 1: //Only allow say_team when the game hasn't started if (level.inGame && normaluser) mode = SAY_TEAM; break; case 2: if (normaluser) mode = SAY_TEAM; break; default: break; } } G_Say(ent, NULL, mode, p); } /* ================== Cmd_Tell_f ================== */ static void Cmd_Tell_f(gentity_t * ent) { int targetNum; gentity_t *target; char *p; char arg[MAX_TOKEN_CHARS]; if (trap_Argc() < 2) { return; } //Slicer : no TELL FOR TP if (!g_gametype.integer < GT_TEAM) return; trap_Argv(1, arg, sizeof(arg)); targetNum = atoi(arg); if (targetNum < 0 || targetNum >= level.maxclients) { return; } //Blaze: Prit out some Debug info if (&g_entities[targetNum] == NULL) G_Printf("Ln 1608\n"); target = &g_entities[targetNum]; if (!target || !target->inuse || !target->client) { return; } p = ConcatArgs(2); G_LogPrintf("tell: %s to %s: %s\n", ent->client->pers.netname, target->client->pers.netname, p); G_Say(ent, target, SAY_TELL, p); // don't tell to the player self if it was already directed to this player // also don't send the chat back to a bot if (ent != target && !(ent->r.svFlags & SVF_BOT)) { G_Say(ent, ent, SAY_TELL, p); } } static void G_VoiceTo(gentity_t * ent, gentity_t * other, int mode, const char *id, qboolean voiceonly) { int color; char *cmd; if (!other) { return; } if (!other->inuse) { return; } if (!other->client) { return; } if (mode == SAY_TEAM && !OnSameTeam(ent, other)) { return; } // no chatting to players in tournements if ((g_gametype.integer == GT_TOURNAMENT)) { return; } if (mode == SAY_TEAM) { color = COLOR_CYAN; cmd = "vtchat"; } else if (mode == SAY_TELL) { color = COLOR_MAGENTA; cmd = "vtell"; } else { color = COLOR_GREEN; cmd = "vchat"; } trap_SendServerCommand(other - g_entities, va("%s %d %d %d %s", cmd, voiceonly, ent->s.number, color, id)); } void G_Voice(gentity_t * ent, gentity_t * target, int mode, const char *id, qboolean voiceonly) { int j; gentity_t *other; if (g_gametype.integer < GT_TEAM && mode == SAY_TEAM) { mode = SAY_ALL; } if (target) { G_VoiceTo(ent, target, mode, id, voiceonly); return; } // echo the text to the console if (g_dedicated.integer) { G_Printf("voice: %s %s\n", ent->client->pers.netname, id); } // send it to all the apropriate clients for (j = 0; j < level.maxclients; j++) { //Blaze: Prit out some Debug info if (&g_entities[j] == NULL) G_Printf("Ln 1682\n"); other = &g_entities[j]; G_VoiceTo(ent, other, mode, id, voiceonly); } } /* ================== Cmd_Voice_f ================== */ static void Cmd_Voice_f(gentity_t * ent, int mode, qboolean arg0, qboolean voiceonly) { char *p; if (trap_Argc() < 2 && !arg0) { return; } if (arg0) { p = ConcatArgs(0); } else { p = ConcatArgs(1); } G_Voice(ent, NULL, mode, p, voiceonly); } /* ================== Cmd_VoiceTell_f ================== */ static void Cmd_VoiceTell_f(gentity_t * ent, qboolean voiceonly) { int targetNum; gentity_t *target; char *id; char arg[MAX_TOKEN_CHARS]; if (trap_Argc() < 2) { return; } trap_Argv(1, arg, sizeof(arg)); targetNum = atoi(arg); if (targetNum < 0 || targetNum >= level.maxclients) { return; } //Blaze: Prit out some Debug info if (&g_entities[targetNum] == NULL) G_Printf("Ln 1733\n"); target = &g_entities[targetNum]; if (!target || !target->inuse || !target->client) { return; } id = ConcatArgs(2); G_LogPrintf("vtell: %s to %s: %s\n", ent->client->pers.netname, target->client->pers.netname, id); G_Voice(ent, target, SAY_TELL, id, voiceonly); // don't tell to the player self if it was already directed to this player // also don't send the chat back to a bot if (ent != target && !(ent->r.svFlags & SVF_BOT)) { G_Voice(ent, ent, SAY_TELL, id, voiceonly); } } /* ================== Cmd_VoiceTaunt_f ================== */ static void Cmd_VoiceTaunt_f(gentity_t * ent) { gentity_t *who; int i; if (!ent->client) { return; } // insult someone who just killed you // JBravo: adding the multiple killed system. if (ent->enemy && ent->enemy->client && ent->enemy->client->lastkilled_client[0] == ent) { // i am a dead corpse if (!(ent->enemy->r.svFlags & SVF_BOT)) { G_Voice(ent, ent->enemy, SAY_TELL, VOICECHAT_DEATHINSULT, qfalse); } if (!(ent->r.svFlags & SVF_BOT)) { G_Voice(ent, ent, SAY_TELL, VOICECHAT_DEATHINSULT, qfalse); } ent->enemy = NULL; return; } // insult someone you just killed // JBravo: adding the multiple killed system. if (ent->client->lastkilled_client[0] != NULL && ent->client->lastkilled_client[0]->s.number >= 0 && ent->client->lastkilled_client[0]->s.number != ent->s.number) { who = ent->client->lastkilled_client[0]; if (who->client) { // who is the person I just killed if (who->client->lasthurt_mod == MOD_GAUNTLET) { if (!(who->r.svFlags & SVF_BOT)) { G_Voice(ent, who, SAY_TELL, VOICECHAT_KILLGAUNTLET, qfalse); // and I killed them with a gauntlet } if (!(ent->r.svFlags & SVF_BOT)) { G_Voice(ent, ent, SAY_TELL, VOICECHAT_KILLGAUNTLET, qfalse); } } else { if (!(who->r.svFlags & SVF_BOT)) { G_Voice(ent, who, SAY_TELL, VOICECHAT_KILLINSULT, qfalse); // and I killed them with something else } if (!(ent->r.svFlags & SVF_BOT)) { G_Voice(ent, ent, SAY_TELL, VOICECHAT_KILLINSULT, qfalse); } } // JBravo: adding the multiple killed system. ent->client->lastkilled_client[0] = NULL; return; } } if (g_gametype.integer >= GT_TEAM) { // praise a team mate who just got a reward for (i = 0; i < MAX_CLIENTS; i++) { who = g_entities + i; if (who->client && who != ent && who->client->sess.sessionTeam == ent->client->sess.sessionTeam) { if (who->client->rewardTime > level.time) { if (!(who->r.svFlags & SVF_BOT)) { G_Voice(ent, who, SAY_TELL, VOICECHAT_PRAISE, qfalse); } if (!(ent->r.svFlags & SVF_BOT)) { G_Voice(ent, ent, SAY_TELL, VOICECHAT_PRAISE, qfalse); } return; } } } } // just say something G_Voice(ent, NULL, SAY_ALL, VOICECHAT_TAUNT, qfalse); } static char *gc_orders[] = { "hold your position", "hold this position", "come here", "cover me", "guard location", "search and destroy", "report" }; void Cmd_GameCommand_f(gentity_t * ent) { int player; int order; char str[MAX_TOKEN_CHARS]; trap_Argv(1, str, sizeof(str)); player = atoi(str); trap_Argv(2, str, sizeof(str)); order = atoi(str); if (player < 0 || player >= MAX_CLIENTS) { return; } if (order < 0 || order > ARRAY_LEN(gc_orders)) { return; } //Blaze: Prit out some Debug info if (&g_entities[player] == NULL) G_Printf("Ln 1854\n"); G_Say(ent, &g_entities[player], SAY_TELL, gc_orders[order]); G_Say(ent, ent, SAY_TELL, gc_orders[order]); } /* ================== Cmd_Where_f ================== */ void Cmd_Where_f(gentity_t * ent) { trap_SendServerCommand(ent-g_entities, va("print \"%s\n\"", vtos(ent->r.currentOrigin))); } static const char *gameNames[] = { "Free For All", "Tournament", "Single Player", "Team Deathmatch", // JBravo: duh ;) "RQ3 teamplay", "Capture the Briefcase", "One Flag CTF", "Overload", "Harvester" }; /* ================== Cmd_CallVote_f ================== */ void Cmd_CallVote_f(gentity_t * ent) { int i, kickNum; float delay; char arg1[MAX_STRING_TOKENS]; char arg2[MAX_STRING_TOKENS]; char *v_gametype, *v_map; gentity_t *kicked; if (!g_allowVote.integer) { trap_SendServerCommand(ent - g_entities, "print \"^1Voting not allowed here.\n\""); return; } if (level.voteTime) { trap_SendServerCommand(ent - g_entities, "print \"^1A vote is already in progress.\n\""); return; } // JBravo: delay added after mapchanges if (level.time - level.startTime < (float)g_RQ3_vote_waittime.integer * 1000) { delay = ((((float)g_RQ3_vote_waittime.integer * 1000) + level.startTime) - level.time) / 1000; if (delay < 10.0) trap_SendServerCommand(ent - g_entities, va("print \"^1Voting currently blocked - Please vote again in^2 %1.2f ^1seconds\n\"", delay)); else trap_SendServerCommand(ent - g_entities, va("print \"^1Voting currently blocked - Please vote again in^2 %2.2f ^1seconds\n\"", delay)); return; } //Makro - replaced the constant with a cvar if (ent->client->pers.voteCount >= g_RQ3_maxClientVotes.integer) { //Makro - added cvar info trap_SendServerCommand(ent - g_entities, va("print \"^1You have called the maximum number of votes (%i).\n\"", g_RQ3_maxClientVotes.integer)); return; } // JBravo: Lets allow spectators to vote in TP if (ent->client->sess.sessionTeam == TEAM_SPECTATOR && g_gametype.integer < GT_TEAM) { trap_SendServerCommand(ent - g_entities, "print \"^1Not allowed to call a vote as spectator.\n\""); return; } //Slicer Matchmode if(g_RQ3_matchmode.integer && ent->client->sess.captain == TEAM_FREE) { trap_SendServerCommand(ent - g_entities, "print \"^1Only team Captains can start a vote.\n\""); return; } // make sure it is a valid command to vote on trap_Argv(1, arg1, sizeof(arg1)); trap_Argv(2, arg2, sizeof(arg2)); if (strchr(arg1, ';') || strchr(arg2, ';')) { trap_SendServerCommand(ent - g_entities, "print \"^1Invalid vote string.\n\""); return; } if (!Q_stricmp(arg1, "cyclemap")) { //TTI - adding "map_restart" } else if (!Q_stricmp(arg1, "map_restart")) { //Makro - adding "timelimit X" } else if (!Q_stricmp(arg1, "timelimit")) { } else if (!Q_stricmp(arg1, "map")) { } else if (!Q_stricmp(arg1, "g_gametype")) { } else if (!Q_stricmp(arg1, "kick")) { } else if (!Q_stricmp(arg1, "clientkick")) { // JBravo: adding game, a gametype:map combo } else if (!Q_stricmp(arg1, "game")) { } else { if(g_RQ3_matchmode.integer) { if (!Q_stricmp(arg1, "resetmatch")) { } else if (!Q_stricmp(arg1, "clearscores")) { } else { trap_SendServerCommand(ent - g_entities, "print \"^1Invalid vote command.\n\""); trap_SendServerCommand(ent - g_entities,"print \"Valid vote commands are: cyclemap, map_restart, map , g_gametype , game , kick , clientkick , clearscores, resetmatch and timelimit .\n\""); return; } } else { trap_SendServerCommand(ent - g_entities, "print \"^1Invalid vote command.\n\""); trap_SendServerCommand(ent - g_entities, "print \"Valid vote commands are: cyclemap, map_restart, map , g_gametype , game , kick , clientkick and timelimit .\n\""); return; } } // special case for g_gametype, check for bad values if (!Q_stricmp(arg1, "g_gametype")) { // JBravo: Adding the digit check to catch errors // JBravo: Im cloning this code in the callvote game section. // Changes here must be done there also. //Makro - added short gametype names if (is_digit(arg2[0])) { i = atoi(arg2); } else { if (!Q_stricmp(arg2, "dm")) { i = GT_FFA; } else if (!Q_stricmp(arg2, "tp")) { i = GT_TEAMPLAY; } else if (!Q_stricmp(arg2, "tdm")) { i = GT_TEAM; } else if (!Q_stricmp(arg2, "ctb")) { i = GT_CTF; } else { //if not a preset name, consider it an error i = 999; } } if (i != GT_FFA && i != GT_TEAMPLAY && i != GT_CTF && i != GT_TEAM) { trap_SendServerCommand(ent - g_entities, "print \"^1Invalid gametype. Valid gametypes are 0(dm), 3(tdm), 4(tp) and 5(ctb).\n\""); return; } Com_sprintf(level.voteString, sizeof(level.voteString), "%s %d", arg1, i); Com_sprintf(level.voteDisplayString, sizeof(level.voteDisplayString), "%s %s", arg1, gameNames[i]); } else if (!Q_stricmp(arg1, "map")) { // special case for map changes, we want to reset the nextmap setting // this allows a player to change maps, but not upset the map rotation if (!G_FileExists(va("maps/%s.bsp", arg2))) { trap_SendServerCommand(ent - g_entities, va("print \"^1The map %s does not exist.\n\"", arg2)); return; } // NiceAss: Talk to NiceAss before you fix this crappy hack =) if (!G_FileSearch( va("scripts/%s.arena", arg2), "rq3ctb" ) && g_gametype.integer == GT_CTF) { trap_SendServerCommand(ent - g_entities, va("print \"^1The map %s does not support CTB.\n\"", arg2)); return; } Com_sprintf(level.voteString, sizeof(level.voteString), "%s", arg1); Com_sprintf(level.voteMap, sizeof(level.voteMap), "%s", arg2); Com_sprintf(level.voteDisplayString, sizeof(level.voteDisplayString), "%s %s", level.voteString, level.voteMap); } else if (!Q_stricmp(arg1, "game")) { // JBravo: adding new game vote. Syntax: gametype:map if (!strchr(arg2, ':')) { trap_SendServerCommand(ent - g_entities, "print \"^1Invalid vote string. Usage: gametype:map\n\""); return; } v_gametype = strtok(arg2, ":"); v_map = strtok(NULL, ":"); if (is_digit(v_gametype[0])) { i = atoi(v_gametype); } else { if (!Q_stricmp(v_gametype, "dm")) { i = GT_FFA; } else if (!Q_stricmp(v_gametype, "tp")) { i = GT_TEAMPLAY; } else if (!Q_stricmp(v_gametype, "tdm")) { i = GT_TEAM; } else if (!Q_stricmp(v_gametype, "ctb")) { i = GT_CTF; } else { //if not a preset name, consider it an error i = 999; } } trap_SendServerCommand(ent - g_entities, va("print \"^1v_map = %s, v_gametype = %s, i = %d\n\"", v_map, v_gametype, i)); if (i != GT_FFA && i != GT_TEAMPLAY && i != GT_CTF && i != GT_TEAM) { trap_SendServerCommand(ent - g_entities, "print \"^1Invalid gametype. Valid gametypes are 0(dm), 3(tdm), 4(tp) and 5(ctb).\n\""); return; } if (!G_FileExists(va("maps/%s.bsp", v_map))) { trap_SendServerCommand(ent - g_entities, va("print \"^1The map %s does not exist.\n\"", v_map)); return; } switch (i) { case GT_FFA: if (!G_FileSearch(va("scripts/%s.arena", v_map), "rq3dm")) { trap_SendServerCommand(ent - g_entities, va("print \"^1The map %s does not support DM.\n\"", v_map)); return; } break; case GT_TEAMPLAY: if (!G_FileSearch(va("scripts/%s.arena", v_map), "rq3tp")) { trap_SendServerCommand(ent - g_entities, va("print \"^1The map %s does not support TP.\n\"", v_map)); return; } break; case GT_TEAM: if (!G_FileSearch(va("scripts/%s.arena", v_map), "rq3tdm")) { trap_SendServerCommand(ent - g_entities, va("print \"^1The map %s does not support TDM.\n\"", v_map)); return; } break; case GT_CTF: if (!G_FileSearch(va("scripts/%s.arena", v_map), "rq3ctb")) { trap_SendServerCommand(ent - g_entities, va("print \"^1The map %s does not support CTB.\n\"", v_map)); return; } break; default: trap_SendServerCommand(ent - g_entities, "print \"^1Invalid gametype. Valid gametypes are 0(dm), 3(tdm), 4(tp) and 5(ctb).\n\""); return; } Com_sprintf(level.voteString, sizeof(level.voteString), "%s", arg1); Com_sprintf(level.voteMap, sizeof(level.voteMap), "%s", v_map); level.voteGametype = i; Com_sprintf(level.voteDisplayString, sizeof(level.voteDisplayString), "%s on %s", gameNames[i], level.voteMap); } else if (!Q_stricmp(arg1, "cyclemap")) { Com_sprintf(level.voteString, sizeof(level.voteString), "cyclemap"); Com_sprintf(level.voteDisplayString, sizeof(level.voteDisplayString), "%s", level.voteString); } else if (!Q_strncmp(arg1, "kick", 4)) { kicked = getEntByName(arg2); if (kicked && kicked->client) { kickNum = kicked->client - level.clients; Com_sprintf(level.voteString, sizeof(level.voteString), "clientkick \"%i\"", kickNum); Com_sprintf(level.voteDisplayString, sizeof(level.voteDisplayString), "%s \"%s\"", arg1, arg2); } else { Com_sprintf(level.voteString, sizeof(level.voteString), "%s \"%s\"", arg1, arg2); Com_sprintf(level.voteDisplayString, sizeof(level.voteDisplayString), "%s", level.voteString); } } else { Com_sprintf(level.voteString, sizeof(level.voteString), "%s \"%s\"", arg1, arg2); Com_sprintf(level.voteDisplayString, sizeof(level.voteDisplayString), "%s", level.voteString); } trap_SendServerCommand(-1, va("print \"%s^7 called a vote.\n\"", ent->client->pers.netname)); // start the voting, the caller automatically votes yes level.voteTime = level.time; level.voteYes = 1; level.voteNo = 0; for (i = 0; i < level.maxclients; i++) { level.clients[i].ps.eFlags &= ~EF_VOTED; } ent->client->ps.eFlags |= EF_VOTED; //Makro - added ent->client->pers.voteCount++; trap_SetConfigstring(CS_VOTE_TIME, va("%i", level.voteTime)); trap_SetConfigstring(CS_VOTE_STRING, level.voteDisplayString); trap_SetConfigstring(CS_VOTE_YES, va("%i", level.voteYes)); trap_SetConfigstring(CS_VOTE_NO, va("%i", level.voteNo)); } /* ================== Cmd_Vote_f ================== */ void Cmd_Vote_f(gentity_t * ent) { char msg[64]; if (!level.voteTime) { trap_SendServerCommand(ent - g_entities, "print \"No vote in progress.\n\""); return; } if (ent->client->ps.eFlags & EF_VOTED) { trap_SendServerCommand(ent - g_entities, "print \"^1Vote already cast.\n\""); return; } //Makro - allow spectators to vote in TP if (ent->client->sess.sessionTeam == TEAM_SPECTATOR && g_gametype.integer < GT_TEAM) { trap_SendServerCommand(ent - g_entities, "print \"^1Not allowed to vote as spectator.\n\""); return; } //Slicer Matchmode if(g_RQ3_matchmode.integer && ent->client->sess.captain == TEAM_FREE) { trap_SendServerCommand(ent - g_entities, "print \"^1Only team Captains vote.\n\""); return; } trap_SendServerCommand(ent - g_entities, "print \"Vote cast.\n\""); ent->client->ps.eFlags |= EF_VOTED; trap_Argv(1, msg, sizeof(msg)); if ( tolower( msg[0] ) == 'y' || msg[0] == '1' ) { level.voteYes++; trap_SetConfigstring(CS_VOTE_YES, va("%i", level.voteYes)); } else { level.voteNo++; trap_SetConfigstring(CS_VOTE_NO, va("%i", level.voteNo)); } // a majority will be determined in CheckVote, which will also account // for players entering or leaving } /* ================== Cmd_CallTeamVote_f ================== */ void Cmd_CallTeamVote_f(gentity_t * ent) { int i, team, cs_offset; char arg1[MAX_STRING_TOKENS]; char arg2[MAX_STRING_TOKENS]; // JBravo: not wanted for TP if (g_gametype.integer >= GT_TEAM) return; team = ent->client->sess.sessionTeam; if (team == TEAM_RED) cs_offset = 0; else if (team == TEAM_BLUE) cs_offset = 1; else return; if (!g_allowVote.integer) { trap_SendServerCommand(ent - g_entities, "print \"^1Voting not allowed here.\n\""); return; } if (level.teamVoteTime[cs_offset]) { trap_SendServerCommand(ent - g_entities, "print \"^1A team vote is already in progress.\n\""); return; } //Makro - replaced the constant with a cvar if (ent->client->pers.teamVoteCount >= g_RQ3_maxClientVotes.integer) { //Makro - added cvar info trap_SendServerCommand(ent - g_entities, va("print \"^1You have called the maximum number of team votes (%i).\n\"", g_RQ3_maxClientVotes.integer)); return; } if (ent->client->sess.sessionTeam == TEAM_SPECTATOR) { trap_SendServerCommand(ent - g_entities, "print \"^1Not allowed to call a vote as spectator.\n\""); return; } // make sure it is a valid command to vote on trap_Argv(1, arg1, sizeof(arg1)); arg2[0] = '\0'; for (i = 2; i < trap_Argc(); i++) { if (i > 2) strcat(arg2, " "); trap_Argv(i, &arg2[strlen(arg2)], sizeof(arg2) - strlen(arg2)); } if (strchr(arg1, ';') || strchr(arg2, ';')) { trap_SendServerCommand(ent - g_entities, "print \"^1Invalid vote string.\n\""); return; } if (!Q_stricmp(arg1, "leader")) { char netname[MAX_NETNAME], leader[MAX_NETNAME]; if (!arg2[0]) { i = ent->client->ps.clientNum; } else { // numeric values are just slot numbers for (i = 0; i < 3; i++) { if (!arg2[i] || arg2[i] < '0' || arg2[i] > '9') break; } if (i >= 3 || !arg2[i]) { i = atoi(arg2); if (i < 0 || i >= level.maxclients) { trap_SendServerCommand(ent - g_entities, va("print \"^1Bad client slot: %i\n\"", i)); return; } if (!g_entities[i].inuse) { trap_SendServerCommand(ent - g_entities, va("print \"^1Client %i is not active\n\"", i)); return; } } else { Q_strncpyz(leader, arg2, sizeof(leader)); Q_CleanStr(leader); for (i = 0; i < level.maxclients; i++) { if (level.clients[i].pers.connected == CON_DISCONNECTED) continue; if (level.clients[i].sess.sessionTeam != team) continue; Q_strncpyz(netname, level.clients[i].pers.netname, sizeof(netname)); Q_CleanStr(netname); if (!Q_stricmp(netname, leader)) { break; } } if (i >= level.maxclients) { trap_SendServerCommand(ent - g_entities, va("print \"^1%s is not a valid player on your team.\n\"", arg2)); return; } } } Com_sprintf(arg2, sizeof(arg2), "%d", i); } else { trap_SendServerCommand(ent - g_entities, "print \"^1Invalid vote string.\n\""); trap_SendServerCommand(ent - g_entities, "print \"Team vote commands are: leader .\n\""); return; } Com_sprintf(level.teamVoteString[cs_offset], sizeof(level.teamVoteString[cs_offset]), "%s %s", arg1, arg2); for (i = 0; i < level.maxclients; i++) { if (level.clients[i].pers.connected == CON_DISCONNECTED) continue; if (level.clients[i].sess.sessionTeam == team) trap_SendServerCommand(i, va("print \"%s^7 called a team vote.\n\"", ent->client->pers.netname)); } // start the voting, the caller automatically votes yes level.teamVoteTime[cs_offset] = level.time; level.teamVoteYes[cs_offset] = 1; level.teamVoteNo[cs_offset] = 0; for (i = 0; i < level.maxclients; i++) { if (level.clients[i].sess.sessionTeam == team) level.clients[i].ps.eFlags &= ~EF_TEAMVOTED; } ent->client->ps.eFlags |= EF_TEAMVOTED; //Makro - added ent->client->pers.teamVoteCount++; trap_SetConfigstring(CS_TEAMVOTE_TIME + cs_offset, va("%i", level.teamVoteTime[cs_offset])); trap_SetConfigstring(CS_TEAMVOTE_STRING + cs_offset, level.teamVoteString[cs_offset]); trap_SetConfigstring(CS_TEAMVOTE_YES + cs_offset, va("%i", level.teamVoteYes[cs_offset])); trap_SetConfigstring(CS_TEAMVOTE_NO + cs_offset, va("%i", level.teamVoteNo[cs_offset])); } /* ================== Cmd_TeamVote_f ================== */ void Cmd_TeamVote_f(gentity_t * ent) { int team, cs_offset; char msg[64]; team = ent->client->sess.sessionTeam; if (team == TEAM_RED) cs_offset = 0; else if (team == TEAM_BLUE) cs_offset = 1; else return; if (!level.teamVoteTime[cs_offset]) { trap_SendServerCommand(ent - g_entities, "print \"No team vote in progress.\n\""); return; } if (ent->client->ps.eFlags & EF_TEAMVOTED) { trap_SendServerCommand(ent - g_entities, "print \"Team vote already cast.\n\""); return; } if (ent->client->sess.sessionTeam == TEAM_SPECTATOR) { trap_SendServerCommand(ent - g_entities, "print \"Not allowed to vote as spectator.\n\""); return; } trap_SendServerCommand(ent - g_entities, "print \"Team vote cast.\n\""); ent->client->ps.eFlags |= EF_TEAMVOTED; trap_Argv(1, msg, sizeof(msg)); if (msg[0] == 'y' || msg[1] == 'Y' || msg[1] == '1') { level.teamVoteYes[cs_offset]++; trap_SetConfigstring(CS_TEAMVOTE_YES + cs_offset, va("%i", level.teamVoteYes[cs_offset])); } else { level.teamVoteNo[cs_offset]++; trap_SetConfigstring(CS_TEAMVOTE_NO + cs_offset, va("%i", level.teamVoteNo[cs_offset])); } // a majority will be determined in TeamCheckVote, which will also account // for players entering or leaving } /* ================= Cmd_SetViewpos_f ================= */ void Cmd_SetViewpos_f(gentity_t * ent) { vec3_t origin, angles; char buffer[MAX_TOKEN_CHARS]; int i; if (!g_cheats.integer) { trap_SendServerCommand(ent - g_entities, va("print \"^1Cheats are not enabled on this server.\n\"")); return; } if (trap_Argc() != 5) { trap_SendServerCommand(ent - g_entities, va("print \"usage: setviewpos x y z yaw\n\"")); return; } VectorClear(angles); for (i = 0; i < 3; i++) { trap_Argv(i + 1, buffer, sizeof(buffer)); origin[i] = atof(buffer); } trap_Argv(4, buffer, sizeof(buffer)); angles[YAW] = atof(buffer); TeleportPlayer(ent, origin, angles); } /* ================= Cmd_Stats_f ================= */ void Cmd_Stats_f(gentity_t * ent) { } /* ================= Cmd_Bandage Added by Blaze ================= */ void Cmd_Bandage(gentity_t * ent) { if (ent->client->ps.pm_type == PM_SPECTATOR) return; //Elder: added so you can't "rebandage" if (ent->client->ps.weaponstate == WEAPON_BANDAGING) { trap_SendServerCommand(ent - g_entities, va("print \"You are already bandaging!\n\"")); return; } if ((ent->client->ps.stats[STAT_RQ3] & RQ3_BANDAGE_NEED) == RQ3_BANDAGE_NEED || (ent->client->ps.stats[STAT_RQ3] & RQ3_LEGDAMAGE) == RQ3_LEGDAMAGE) { //Elder: remove zoom bits Cmd_Unzoom(ent); //Elder: drop the primed grenade //Moved weapon switch to bg_pmove.c if (ent->client->ps.weapon == WP_GRENADE && ent->client->ps.weaponstate == WEAPON_COCKED) { FireWeapon(ent); ent->client->ps.ammo[WP_GRENADE]--; //Makro - if this is the last grenade, remove the weapon from the inventory if (ent->client->ps.ammo[WP_GRENADE] <= 0) { ent->client->ps.stats[STAT_WEAPONS] &= ~(1 << WP_GRENADE); } } //slicer ent->client->ps.weaponstate = WEAPON_BANDAGING; if (ent->client->ps.weapon == WP_KNIFE && !(ent->client->ps.persistant[PERS_WEAPONMODES] & RQ3_KNIFEMODE)) { ent->client->ps.generic1 = ((ent->client->ps.generic1 & ANIM_TOGGLEBIT) ^ ANIM_TOGGLEBIT) | WP_ANIM_THROWDISARM; } else { ent->client->ps.generic1 = ((ent->client->ps.generic1 & ANIM_TOGGLEBIT) ^ ANIM_TOGGLEBIT) | WP_ANIM_DISARM; } ent->client->ps.weaponTime += BLEED_BANDAGE_TIME; ent->client->bleedtick = 4; trap_SendServerCommand(ent - g_entities, va("print \"You've started bandaging.\n\"")); } else { trap_SendServerCommand(ent - g_entities, va("print \"No need to bandage.\n\"")); } } /* ================== Opendoor Function Added by Blaze Checks to see if they are near a door, if so open it ================== */ void Cmd_OpenDoor(gentity_t * ent) { //Use_BinaryMover( ent->parent, ent, other ); // gentity_t *door = NULL; //Don't open doors if dead or spectating if (ent->client->ps.stats[STAT_HEALTH] <= 0 || ent->client->ps.pm_type == PM_SPECTATOR) return; //Makro - doesn't look right to me. Doing it one single time should be enough /* while ((door = findradius(door, ent->r.currentOrigin, 100)) != NULL) { if (Q_stricmp(door->classname, "func_door_rotating") == 0) { ent->client->openDoor = qtrue; ent->client->openDoorTime = level.time; } else if (Q_stricmp(door->classname, "func_door") == 0) { ent->client->openDoor = qtrue; ent->client->openDoorTime = level.time; } } */ ent->client->openDoor = qtrue; ent->client->openDoorTime = level.time; } /* Hawkins. Reaction weapon command */ void Cmd_Weapon(gentity_t * ent) { //Makro - this was a few lines below if (!ent || !ent->client || ent->client->ps.pm_type == PM_SPECTATOR) return; //JBravo: lets make 100% sure no dead people unzoom if (ent->health <= 0) return; ent->client->weapon_attempts--; if (ent->client->weapon_attempts < 0) ent->client->weapon_attempts = 0; if (ent->client->ps.weaponstate == WEAPON_BANDAGING) { if (!ent->client->weapon_after_bandage_warned) { ent->client->weapon_after_bandage_warned = qtrue; G_SendClientSpec(ent, va("print \"You'll get to your weapon when you are finished bandaging!\n\"")); } ent->client->weapon_attempts++; return; } ent->client->weapon_after_bandage_warned = qfalse; if (ent->client->ps.weaponstate == WEAPON_RELOADING || ent->client->ps.weaponstate == WEAPON_FIRING || ent->client->ps.weaponstate == WEAPON_DROPPING || ent->client->ps.weaponstate == WEAPON_RAISING) { ent->client->weapon_attempts++; return; } //Elder: added brackets, and-ops and not-ops instead of logical ops switch (ent->s.weapon) { case WP_SSG3000: if ((ent->client->ps.stats[STAT_RQ3] & RQ3_ZOOM_LOW) == RQ3_ZOOM_LOW && (ent->client->ps.stats[STAT_RQ3] & RQ3_ZOOM_MED) == RQ3_ZOOM_MED) { //Elder: zoom 1x ent->client->ps.stats[STAT_RQ3] &= ~RQ3_ZOOM_LOW; ent->client->ps.stats[STAT_RQ3] &= ~RQ3_ZOOM_MED; } else if ((ent->client->ps.stats[STAT_RQ3] & RQ3_ZOOM_MED) == RQ3_ZOOM_MED) { //Elder: zoom 6x ent->client->ps.stats[STAT_RQ3] |= RQ3_ZOOM_LOW; } else if ((ent->client->ps.stats[STAT_RQ3] & RQ3_ZOOM_LOW) == RQ3_ZOOM_LOW) { //Elder: zoom 4x ent->client->ps.stats[STAT_RQ3] |= RQ3_ZOOM_MED; ent->client->ps.stats[STAT_RQ3] &= ~RQ3_ZOOM_LOW; } else { //Elder: zoom 2x ent->client->ps.stats[STAT_RQ3] |= RQ3_ZOOM_LOW; } G_Sound(ent, CHAN_ITEM, G_SoundIndex("sound/misc/lens.wav")); break; case WP_PISTOL: // semiauto toggle (increase accuracy) if ((ent->client->ps.persistant[PERS_WEAPONMODES] & RQ3_MK23MODE) == RQ3_MK23MODE) { ent->client->ps.persistant[PERS_WEAPONMODES] &= ~RQ3_MK23MODE; G_SendClientSpec(ent, va("print \"Switched to full automatic.\n\"")); } else { ent->client->ps.persistant[PERS_WEAPONMODES] |= RQ3_MK23MODE; G_SendClientSpec(ent, va("print \"Switched to semi-automatic.\n\"")); } G_Sound(ent, CHAN_ITEM, G_SoundIndex("sound/misc/click.wav")); break; case WP_M4: // 3rb/full auto toggle if ((ent->client->ps.persistant[PERS_WEAPONMODES] & RQ3_M4MODE) == RQ3_M4MODE) { ent->client->ps.persistant[PERS_WEAPONMODES] &= ~RQ3_M4MODE; G_SendClientSpec(ent, va("print \"Switched to full automatic.\n\"")); } else { ent->client->ps.persistant[PERS_WEAPONMODES] |= RQ3_M4MODE; G_SendClientSpec(ent, va("print \"Switched to 3 round burst.\n\"")); } G_Sound(ent, CHAN_ITEM, G_SoundIndex("sound/misc/click.wav")); break; case WP_MP5: // 3rb/full auto toggle if ((ent->client->ps.persistant[PERS_WEAPONMODES] & RQ3_MP5MODE) == RQ3_MP5MODE) { ent->client->ps.persistant[PERS_WEAPONMODES] &= ~RQ3_MP5MODE; G_SendClientSpec(ent, va("print \"Switched to full automatic.\n\"")); } else { ent->client->ps.persistant[PERS_WEAPONMODES] |= RQ3_MP5MODE; G_SendClientSpec(ent, va("print \"Switched to 3 round burst.\n\"")); } G_Sound(ent, CHAN_ITEM, G_SoundIndex("sound/misc/click.wav")); break; case WP_KNIFE: // NiceAss: weapon animation/state check before the mode switch. if (ent->client->ps.weaponstate != WEAPON_READY) break; // toggle throwing/slashing if ((ent->client->ps.persistant[PERS_WEAPONMODES] & RQ3_KNIFEMODE) == RQ3_KNIFEMODE) { //Elder: added ent->client->ps.persistant[PERS_WEAPONMODES] &= ~RQ3_KNIFEMODE; G_SendClientSpec(ent, va("print \"Switched to throwing.\n\"")); // Niceass: Animations added ent->client->ps.weaponstate = WEAPON_MODECHANGE; ent->client->ps.weaponTime = 550; ent->client->ps.generic1 = ((ent->client->ps.generic1 & ANIM_TOGGLEBIT) ^ ANIM_TOGGLEBIT) | WP_ANIM_EXTRA1; } else { //Elder: we're gonna use this to flag throw or slash with the knife ent->client->ps.persistant[PERS_WEAPONMODES] |= RQ3_KNIFEMODE; G_SendClientSpec(ent, va("print \"Switched to slashing.\n\"")); // Niceass: Animations added ent->client->ps.weaponstate = WEAPON_MODECHANGE; ent->client->ps.weaponTime = 550; ent->client->ps.generic1 = ((ent->client->ps.generic1 & ANIM_TOGGLEBIT) ^ ANIM_TOGGLEBIT) | WP_ANIM_EXTRA2; } break; case WP_HANDCANNON: // nothing break; case WP_M3: // nothing break; case WP_AKIMBO: // nothing break; case WP_GRENADE: // JBravo: Fix for uninitialized grenade mode if ((ent->client->ps.persistant[PERS_WEAPONMODES] & RQ3_GRENSHORT) == 0 && (ent->client->ps.persistant[PERS_WEAPONMODES] & RQ3_GRENMED) == 0) { ent->client->ps.persistant[PERS_WEAPONMODES] |= RQ3_GRENSHORT; ent->client->ps.persistant[PERS_WEAPONMODES] &= ~RQ3_GRENMED; } // short, medium, long throws if ((ent->client->ps.persistant[PERS_WEAPONMODES] & RQ3_GRENSHORT) == RQ3_GRENSHORT && (ent->client->ps.persistant[PERS_WEAPONMODES] & RQ3_GRENMED) == RQ3_GRENMED) { //Going into Short ent->client->ps.persistant[PERS_WEAPONMODES] |= RQ3_GRENSHORT; //Set the short flag ent->client->ps.persistant[PERS_WEAPONMODES] &= ~RQ3_GRENMED; //unset the med flag G_SendClientSpec(ent, va("print \"Grenade set for short range throw.\n\"")); } else if ((ent->client->ps.persistant[PERS_WEAPONMODES] & RQ3_GRENSHORT) == RQ3_GRENSHORT) { //Going into Med ent->client->ps.persistant[PERS_WEAPONMODES] &= ~RQ3_GRENSHORT; //unset the short flag ent->client->ps.persistant[PERS_WEAPONMODES] |= RQ3_GRENMED; //Set the med flag G_SendClientSpec(ent, va("print \"Grenade set for medium range throw.\n\"")); } else if ((ent->client->ps.persistant[PERS_WEAPONMODES] & RQ3_GRENMED) == RQ3_GRENMED) { //Going into Long ent->client->ps.persistant[PERS_WEAPONMODES] |= RQ3_GRENSHORT; //Set the short flag ent->client->ps.persistant[PERS_WEAPONMODES] |= RQ3_GRENMED; //Set the med flag G_SendClientSpec(ent, va("print \"Grenade set for long range throw.\n\"")); } //Elder: added else { G_Printf("Cmd_Weapon_f: Received bad grenade toggle\n"); } break; default: break; } //Makro - changed all the trap_SendServerCommand prints to G_SendClientSpec prints //so that players spectating this client get his messages, too } //Slicer void Cmd_New_Weapon(gentity_t * ent) { ent->client->weapon_attempts++; if (ent->client->weapon_attempts == 1) Cmd_Weapon(ent); } // Hawkins make sure spread comes back void Cmd_Unzoom(gentity_t * ent) { //Makro - if the client is dead or spectating, do nothing if (!ent || !ent->client || ent->client->ps.pm_type == PM_SPECTATOR) return; // Freud, play zooming sounds for unzoom if (ent->client->ps.stats[STAT_RQ3] & RQ3_ZOOM_LOW || ent->client->ps.stats[STAT_RQ3] & RQ3_ZOOM_MED) G_Sound(ent, CHAN_ITEM, G_SoundIndex("sound/misc/lens.wav")); ent->client->ps.stats[STAT_RQ3] &= ~RQ3_ZOOM_LOW; ent->client->ps.stats[STAT_RQ3] &= ~RQ3_ZOOM_MED; } /* ================= Cmd_DropWeapon_f XRAY FMJ ================= */ void Cmd_DropWeapon_f(gentity_t * ent) { if (ent->client->ps.pm_type == PM_SPECTATOR) return; // JBravo: no dropping stuff while bandaging. Fix dedicated to GoKu and JesterRace :) if (ent->client->ps.weaponstate == WEAPON_BANDAGING) { trap_SendServerCommand(ent - g_entities, va("print \"You are too busy bandaging...\n\"")); return; } //Elder: remove zoom bits Cmd_Unzoom(ent); //Throwing away return value here; high precedence weapon drop ThrowWeapon(ent, qtrue); } /* ================= Cmd_DropItem_f ================= */ void Cmd_DropItem_f(gentity_t * ent) { if (ent->client->ps.pm_type == PM_SPECTATOR) return; // JBravo: no dropping stuff while bandaging. Fix dedicated to GoKu and JesterRace :) // Turns out they are full of shit... :) /* if (ent->client->ps.weaponstate == WEAPON_BANDAGING) { trap_SendServerCommand(ent - g_entities, va("print \"You are too busy bandaging...\n\"")); return; } */ if (ent->client->ps.stats[STAT_HOLDABLE_ITEM]) { //Elder: reset item totals if using bandolier //JBravo: New multiple items code. if (ent->client->ps.stats[STAT_HOLDABLE_ITEM] & (1 << HI_BANDOLIER)) { if (ent->client->numClips[WP_SSG3000] > RQ3_SSG3000_EXTRA_AMMO) ent->client->numClips[WP_SSG3000] = RQ3_SSG3000_EXTRA_AMMO; if (ent->client->numClips[WP_M3] > RQ3_M3_MAXCLIP) ent->client->numClips[WP_M3] = RQ3_M3_MAXCLIP; if (ent->client->numClips[WP_HANDCANNON] > RQ3_M3_MAXCLIP) ent->client->numClips[WP_HANDCANNON] = RQ3_M3_MAXCLIP; if (ent->client->numClips[WP_M4] > RQ3_M4_EXTRA_AMMO) ent->client->numClips[WP_M4] = RQ3_M4_EXTRA_AMMO; if (ent->client->numClips[WP_MP5] > RQ3_MP5_EXTRA_AMMO) ent->client->numClips[WP_MP5] = RQ3_MP5_EXTRA_AMMO; if (ent->client->ps.ammo[WP_KNIFE] > RQ3_KNIVES_EXTRA_AMMO) { ent->client->ps.ammo[WP_KNIFE] = RQ3_KNIVES_EXTRA_AMMO; ent->client->weaponCount[WP_KNIFE] = RQ3_KNIVES_EXTRA_AMMO; } if (ent->client->numClips[WP_AKIMBO] > RQ3_AKIMBO_EXTRA_AMMO) ent->client->numClips[WP_AKIMBO] = RQ3_AKIMBO_EXTRA_AMMO; if (ent->client->ps.ammo[WP_GRENADE] > 2) ent->client->ps.ammo[WP_GRENADE] = 2; if (ent->client->numClips[WP_PISTOL] > 1) ent->client->numClips[WP_PISTOL] = 1; if (ent->client->uniqueWeapons > g_RQ3_maxWeapons.integer) { Cmd_Unzoom(ent); ThrowWeapon(ent, qtrue); } } //Force laser off else if (ent->client->ps.stats[STAT_HOLDABLE_ITEM] & (1 << HI_LASER)) Laser_Gen(ent, qfalse); ThrowItem(ent); } } /* ================= PlayerStats ================= */ void Cmd_PlayerStats_f(gentity_t * ent) { trap_SendServerCommand(ent - g_entities, va("print \"%s^7:\n\"", ent->client->pers.netname)); trap_SendServerCommand(ent - g_entities, va("print \"----------------------------------\n\"")); trap_SendServerCommand(ent - g_entities, va("print \"| Weapon | Accuracy | Hits/Shots |\n\"")); trap_SendServerCommand(ent - g_entities, va("print \"----------------------------------\n\"")); trap_SendServerCommand(ent - g_entities, va("print \"| Knife(T)| %.0f | %.0f/%.0f \n\"", (100 * (float) ent->client->pers.records[REC_KNIFETHROWHITS] / (ent->client->pers.records[REC_KNIFETHROWSHOTS] ? ent->client->pers. records[REC_KNIFETHROWSHOTS] : 1)), (float) ent->client->pers.records[REC_KNIFETHROWHITS], (float) ent->client->pers.records[REC_KNIFETHROWSHOTS])); trap_SendServerCommand(ent - g_entities, va("print \"| MK23 | %.0f | %.0f/%.0f \n\"", (100 * (float) ent->client->pers.records[REC_MK23HITS] / (ent->client->pers.records[REC_MK23SHOTS] ? ent->client->pers. records[REC_MK23SHOTS] : 1)), (float) ent->client->pers.records[REC_MK23HITS], (float) ent->client->pers.records[REC_MK23SHOTS])); trap_SendServerCommand(ent - g_entities, va("print \"| Akimbo | %.0f | %.0f/%.0f \n\"", (100 * (float) ent->client->pers.records[REC_AKIMBOHITS] / (ent->client->pers.records[REC_AKIMBOSHOTS] ? ent->client->pers. records[REC_AKIMBOSHOTS] : 1)), (float) ent->client->pers.records[REC_AKIMBOHITS], (float) ent->client->pers.records[REC_AKIMBOSHOTS])); trap_SendServerCommand(ent - g_entities, va("print \"| M4 | %.0f | %.0f/%.0f \n\"", (100 * (float) ent->client->pers.records[REC_M4HITS] / (ent->client->pers.records[REC_M4SHOTS] ? ent->client->pers. records[REC_M4SHOTS] : 1)), (float) ent->client->pers.records[REC_M4HITS], (float) ent->client->pers.records[REC_M4SHOTS])); trap_SendServerCommand(ent - g_entities, va("print \"| MP5 | %.0f | %.0f/%.0f \n\"", (100 * (float) ent->client->pers.records[REC_MP5HITS] / (ent->client->pers.records[REC_MP5SHOTS] ? ent->client->pers. records[REC_MP5SHOTS] : 1)), (float) ent->client->pers.records[REC_MP5HITS], (float) ent->client->pers.records[REC_MP5SHOTS])); trap_SendServerCommand(ent - g_entities, va("print \"| M3 | %.0f | %.0f/%.0f \n\"", (100 * (float) ent->client->pers.records[REC_M3HITS] / (ent->client->pers.records[REC_M3SHOTS] ? ent->client->pers. records[REC_M3SHOTS] : 1)), (float) ent->client->pers.records[REC_M3HITS], (float) ent->client->pers.records[REC_M3SHOTS])); trap_SendServerCommand(ent - g_entities, va("print \"| HC | %.0f | %.0f/%.0f \n\"", (100 * (float) ent->client->pers.records[REC_HANDCANNONHITS] / (ent->client->pers.records[REC_HANDCANNONSHOTS] ? ent->client->pers. records[REC_HANDCANNONSHOTS] : 1)), (float) ent->client->pers.records[REC_HANDCANNONHITS], (float) ent->client->pers.records[REC_HANDCANNONSHOTS])); trap_SendServerCommand(ent - g_entities, va("print \"| SSG | %.0f | %.0f/%.0f \n\"", (100 * (float) ent->client->pers.records[REC_SSG3000HITS] / (ent->client->pers.records[REC_SSG3000SHOTS] ? ent->client->pers. records[REC_SSG3000SHOTS] : 1)), (float) ent->client->pers.records[REC_SSG3000HITS], (float) ent->client->pers.records[REC_SSG3000SHOTS])); trap_SendServerCommand(ent - g_entities, va("print \"----------------------------------\n\"")); } void G_SetSunFlare(const vec3_t dir, int size, float alpha); void Cmd_TestSunFlare_f(gentity_t *ent) { vec3_t dir; int size = 0; float alpha = 0.f; char arg[64]; if (!CheatsOk(ent) || !ent->client || !ent->client->pers.localClient) return; if (trap_Argc() >= 2) { trap_Argv(1, arg, sizeof(arg)); size = atoi(arg); } if (trap_Argc() >= 3) { trap_Argv(2, arg, sizeof(arg)); alpha = atof(arg); } if (size <= 0) size = 256; if (alpha <= 0.f) alpha = 0.75f; AngleVectors(ent->client->ps.viewangles, dir, NULL, NULL); G_SetSunFlare(dir, size, alpha); } /* ================= ClientCommand ================= */ void ClientCommand(int clientNum) { gentity_t *ent; char cmd[MAX_TOKEN_CHARS]; ent = g_entities + clientNum; if (!ent->client || ent->client->pers.connected != CON_CONNECTED) { return; // not fully in game yet } trap_Argv(0, cmd, sizeof(cmd)); if (Q_stricmp(cmd, "say") == 0) { Cmd_Say_f(ent, SAY_ALL, qfalse); return; } if (Q_stricmp(cmd, "say_team") == 0) { Cmd_Say_f(ent, SAY_TEAM, qfalse); return; } if (Q_stricmp(cmd, "tell") == 0) { Cmd_Tell_f(ent); return; } if (Q_stricmp(cmd, "vsay") == 0) { Cmd_Voice_f(ent, SAY_ALL, qfalse, qfalse); return; } if (Q_stricmp(cmd, "vsay_team") == 0) { Cmd_Voice_f(ent, SAY_TEAM, qfalse, qfalse); return; } if (Q_stricmp(cmd, "vtell") == 0) { Cmd_VoiceTell_f(ent, qfalse); return; } if (Q_stricmp(cmd, "vosay") == 0) { Cmd_Voice_f(ent, SAY_ALL, qfalse, qtrue); return; } if (Q_stricmp(cmd, "vosay_team") == 0) { Cmd_Voice_f(ent, SAY_TEAM, qfalse, qtrue); return; } if (Q_stricmp(cmd, "votell") == 0) { Cmd_VoiceTell_f(ent, qtrue); return; } if (Q_stricmp(cmd, "vtaunt") == 0) { Cmd_VoiceTaunt_f(ent); return; } if (Q_stricmp(cmd, "score") == 0) { Cmd_Score_f(ent); return; } if (Q_stricmp(cmd, "wstats") == 0) { Cmd_WeaponStats_f(ent); return; } // ignore all other commands when at intermission if (level.intermissiontime) { // JBravo: this is just simply way out there. // Cmd_Say_f (ent, qfalse, qtrue); return; } if (Q_stricmp(cmd, "give") == 0) Cmd_Give_f(ent); else if (Q_stricmp(cmd, "god") == 0) Cmd_God_f(ent); else if (Q_stricmp(cmd, "notarget") == 0) Cmd_Notarget_f(ent); else if (Q_stricmp(cmd, "noclip") == 0) Cmd_Noclip_f(ent); else if (Q_stricmp(cmd, "kill") == 0) Cmd_Kill_f(ent); else if (Q_stricmp(cmd, "teamtask") == 0) Cmd_TeamTask_f(ent); else if (Q_stricmp(cmd, "levelshot") == 0) Cmd_LevelShot_f(ent); // JBravo: no limchasecam cheating... // else if (Q_stricmp(cmd, "follow") == 0) // Cmd_Follow_f(ent); else if (Q_stricmp(cmd, "follownext") == 0) Cmd_FollowCycle_f(ent, 1); else if (Q_stricmp(cmd, "followprev") == 0) Cmd_FollowCycle_f(ent, -1); else if (Q_stricmp(cmd, "team") == 0) Cmd_Team_f(ent); else if (Q_stricmp(cmd, "where") == 0) Cmd_Where_f(ent); else if (Q_stricmp(cmd, "callvote") == 0) Cmd_CallVote_f(ent); else if (Q_stricmp(cmd, "vote") == 0) Cmd_Vote_f(ent); else if (Q_stricmp(cmd, "callteamvote") == 0) Cmd_CallTeamVote_f(ent); else if (Q_stricmp(cmd, "teamvote") == 0) Cmd_TeamVote_f(ent); else if (Q_stricmp(cmd, "gc") == 0) Cmd_GameCommand_f(ent); else if (Q_stricmp(cmd, "setviewpos") == 0) Cmd_SetViewpos_f(ent); else if (Q_stricmp(cmd, "stats") == 0) Cmd_Stats_f(ent); else if (Q_stricmp(cmd, "playerlist") == 0) Cmd_Playerlist_f(ent); //Slicer: matchmode else if (Q_stricmp(cmd, "captain") == 0) MM_Captain_f(ent); else if (Q_stricmp(cmd, "ready") == 0) MM_Ready_f(ent); else if (Q_stricmp(cmd, "sub") == 0) MM_Sub_f(ent); else if (Q_stricmp(cmd, "teamname") == 0) MM_TeamName_f(ent); else if (Q_stricmp(cmd, "teammodel") == 0) MM_TeamModel_f(ent); else if (Q_stricmp(cmd, "referee") == 0) MM_Referee_f(ent); else if (Q_stricmp(cmd, "settings") == 0) MM_Settings_f(ent); // aasimon: referee for MM else if (Q_stricmp(cmd, "reflogin") == 0) Ref_Auth(ent); else if (Q_stricmp(cmd, "ref") == 0) Ref_Command(ent); else if (Q_stricmp(cmd, "refresign") == 0) Ref_Resign(ent); // Begin Duffman else if (Q_stricmp(cmd, "reload") == 0) { //Cmd_Reload( ent ); // End Duffman //Blaze's Open door command } else if (Q_stricmp(cmd, "opendoor") == 0) Cmd_OpenDoor(ent); //Blaze: Bandage code else if (Q_stricmp(cmd, "bandage") == 0) Cmd_Bandage(ent); //End Blaze // Hawkins else if (Q_stricmp(cmd, "weapon") == 0) Cmd_New_Weapon(ent); else if (Q_stricmp(cmd, "unzoom") == 0) Cmd_Unzoom(ent); // end hawkins // JBravo: adding the choose and drop commands. else if (Q_stricmp(cmd, "choose") == 0) RQ3_Cmd_Choose_f(ent); else if (Q_stricmp(cmd, "drop") == 0) RQ3_Cmd_Drop_f(ent); else if (Q_stricmp(cmd, "use") == 0) RQ3_Cmd_Use_f(ent); // JBravo: adding radio else if (Q_stricmp(cmd, "radiogender") == 0) RQ3_Cmd_Radiogender_f(ent); else if (Q_stricmp(cmd, "radio_power") == 0) RQ3_Cmd_Radio_power_f(ent); else if (Q_stricmp(cmd, "radio") == 0) RQ3_Cmd_Radio_f(ent); else if (Q_stricmp(cmd, "dropweapon") == 0) // XRAY FMJ Cmd_DropWeapon_f(ent); // JBravo: adding ignore else if (Q_stricmp(cmd, "ignorenum") == 0) Cmd_Ignorenum_f(ent); else if (Q_stricmp(cmd, "unignorenum") == 0) Cmd_Ignorenum_f(ent); else if (Q_stricmp(cmd, "ignore") == 0) Cmd_Ignore_f(ent); else if (Q_stricmp(cmd, "unignore") == 0) Cmd_Unignore_f(ent); else if (Q_stricmp(cmd, "clearignorelist") == 0) Cmd_Ignoreclear_f(ent); // JBravo: adding tkok else if (Q_stricmp(cmd, "tkok") == 0) RQ3_Cmd_TKOk(ent); // else if (Q_stricmp(cmd, "debugshit") == 0) // RQ3_Cmd_debugshit(ent); //Elder: stuff for dropping items else if (Q_stricmp(cmd, "dropitem") == 0) Cmd_DropItem_f(ent); else if (Q_stricmp(cmd, "camera") == 0) camera_cmd(ent); else if (Q_stricmp(cmd, "playerstats") == 0) Cmd_PlayerStats_f(ent); else if (Q_stricmp(cmd, "testSunFlare") == 0) Cmd_TestSunFlare_f(ent); else if (Q_stricmp(cmd, "SendCheatCvars") == 0) { if (!G_SendCheatVars(clientNum)) Com_Printf("Error loading cvar cfg\n"); } else trap_SendServerCommand(clientNum, va("print \"^1unknown cmd ^7%s\n\"", cmd)); } /* =============== RQ3_ValidateSay Added by Elder Ensure that the client is not spamming because we need sv_floodProtect off for fast reloads and what-not. It's not perfect (ideally we'd cut them off in cgame but messagemodes by-pass it), but at least it prevents spam from reaching other clients. =============== */ int RQ3_ValidateSay(gentity_t * ent) { int timeCheck, warnTime, banTime; int intervalTime, maxWarnings, maxMessages; if (g_RQ3_messageProtect.integer == 0) return SAY_OK; // Check for good cvar values and set them to defaults if bad // We use local vars because the cvars may not update in time for use // message count if (g_RQ3_messageMaxCount.integer < 0) { maxMessages = atoi(SAY_MAX_NUMBER); trap_Cvar_Set("sv_RQ3_messageMaxCount", SAY_MAX_NUMBER); } else maxMessages = g_RQ3_messageMaxCount.integer; // warning time if (g_RQ3_messageWarnTime.integer < 0) { warnTime = atoi(SAY_WARNING_TIME); trap_Cvar_Set("sv_RQ3_messageWarnTime", SAY_WARNING_TIME); } else warnTime = g_RQ3_messageWarnTime.integer; // max warnings if (g_RQ3_messageMaxWarnings.integer < 0) { maxWarnings = atoi(SAY_MAX_WARNINGS); trap_Cvar_Set("sv_RQ3_messageMaxWarnings", SAY_MAX_WARNINGS); } else maxWarnings = g_RQ3_messageMaxWarnings.integer; // ban time if (g_RQ3_messageBanTime.integer < 0) { banTime = atoi(SAY_BAN_TIME); trap_Cvar_Set("sv_RQ3_messageBanTime", SAY_BAN_TIME); } else banTime = g_RQ3_messageBanTime.integer; // interval time if (g_RQ3_messageInterval.integer < 0) { intervalTime = atoi(SAY_PERIOD_TIME); trap_Cvar_Set("sv_RQ3_messageInterval", SAY_PERIOD_TIME); } else intervalTime = g_RQ3_messageInterval.integer; // seconds to milliseconds if (ent->client->pers.sayWarnings) { timeCheck = warnTime * 1000; } else { timeCheck = banTime * 1000; } // check if already warned/banned if (ent->client->pers.sayMuteTime && level.time - ent->client->pers.sayMuteTime < timeCheck) { if (ent->client->pers.sayWarnings) return SAY_WARNING; else return SAY_BAN; } // check if a flooder if (ent->client->pers.sayCount >= maxMessages && level.time - ent->client->pers.sayTime < intervalTime * 1000) { ent->client->pers.sayMuteTime = level.time; // determine penalty level if (ent->client->pers.sayWarnings >= maxWarnings) { // bans never reset, but warnings do ent->client->pers.sayBans++; ent->client->pers.sayWarnings = 0; // kick if no ban time is set if (banTime == 0) trap_DropClient(ent->s.clientNum, "Dropped due to chat abuse"); return SAY_BAN; } else { ent->client->pers.sayWarnings++; return SAY_WARNING; } } // regular say check if (level.time - ent->client->pers.sayTime > intervalTime * 1000) { ent->client->pers.sayCount = 0; ent->client->pers.sayTime = level.time; ent->client->pers.sayMuteTime = 0; ent->client->pers.sayModerated = qfalse; } ent->client->pers.sayCount++; return SAY_OK; } //Makro - this function sends print commands to the client and the ones spectating him void G_SendClientSpec(gentity_t * ent, const char *msg) { int i; if (!ent) return; if (!ent->client) return; trap_SendServerCommand(ent - g_entities, msg); for (i = 0; i < level.maxclients; i++) { gclient_t *follower = &level.clients[i]; //no need to send the message twice if (i == ent - g_entities) continue; if (follower->pers.connected != CON_CONNECTED) continue; if (follower->sess.sessionTeam != TEAM_SPECTATOR) continue; if (follower->sess.spectatorState != SPECTATOR_FOLLOW) continue; //gotcha ! if (follower->sess.spectatorClient == ent - g_entities) trap_SendServerCommand(i, msg); } }