mirror of
https://github.com/ReactionQuake3/reaction.git
synced 2024-11-29 07:22:47 +00:00
3327 lines
100 KiB
C
3327 lines
100 KiB
C
//-----------------------------------------------------------------------------
|
|
//
|
|
// $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 <map> and ref map <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 <mapname>, g_gametype <n>, game <gametype:map>, kick <player>, clientkick <clientnum>, clearscores, resetmatch and timelimit <minutes>.\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 <mapname>, g_gametype <n>, game <gametype:map>, kick <player>, clientkick <clientnum> and timelimit <minutes>.\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 autoamtically 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 <player>.\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 autoamtically 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);
|
|
}
|
|
}
|