//----------------------------------------------------------------------------- // // $Id$ // //----------------------------------------------------------------------------- // // $Log$ // Revision 1.155 2005/09/08 19:34:47 makro // Removed " turned the other cheek" message // // Revision 1.154 2005/09/07 20:27:42 makro // Entity attachment trees // // Revision 1.153 2003/09/08 19:19:20 makro // New code for respawning entities in TP // // Revision 1.152 2003/04/26 22:33:06 jbravo // Wratted all calls to G_FreeEnt() to avoid crashing and provide debugging // // Revision 1.151 2003/04/26 02:03:51 jbravo // Helmet fixes // // Revision 1.150 2003/03/28 10:36:02 jbravo // Tweaking the replacement system a bit. Reactionmale now the default model // // Revision 1.149 2003/03/22 20:19:20 jbravo // Item replacement fixes, tmp ban after votekicks and ignore now works on // players with colors. // // Revision 1.148 2003/03/21 11:32:04 jbravo // Added debugging to locate the bad gEnt crashes, added timelimit support // for CTB and TDM and fixed some typos // // Revision 1.147 2003/02/27 03:58:35 jbravo // Fixed the FF system after adding TDM broke it. Added color to error messages // // Revision 1.146 2002/11/17 20:14:15 jbravo // Itembanning added // // Revision 1.145 2002/11/13 00:50:38 jbravo // Fixed item dropping, specmode selection on death and helmet probs. // // Revision 1.144 2002/10/30 20:04:34 jbravo // Adding helmet // // Revision 1.143 2002/10/29 01:34:52 jbravo // Added g_RQ3_tdmMode (0 = TP style, 1 = DM style) including UI support. // // Revision 1.142 2002/10/26 22:03:43 jbravo // Made TeamDM work RQ3 style. // // Revision 1.141 2002/10/26 00:37:18 jbravo // New multiple item code and added PB support to the UI // // Revision 1.140 2002/10/21 21:00:39 slicer // New MM features and bug fixes // // Revision 1.139 2002/09/29 16:06:45 jbravo // Work done at the HPWorld expo // // Revision 1.138 2002/09/24 05:06:17 blaze // fixed spectating so ref\'s can now use all the chasecam modes. // // Revision 1.137 2002/09/09 02:26:55 niceass // enabled drop case // // Revision 1.136 2002/09/08 12:50:52 jbravo // Tuned the scaling ctb respawn system a bit and defaulted g_RQ3_ctb_respawndelay // to 0 // // Revision 1.135 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.134 2002/08/30 15:27:10 jbravo // One more extra body fix // // Revision 1.133 2002/08/30 01:09:06 jbravo // Semi fixed the bodies thing in CTB // // Revision 1.132 2002/08/29 23:47:10 jbravo // Disabled drop case and fixed a padding problem in the date code. // // Revision 1.131 2002/08/24 22:46:50 niceass // roundtimelimit bug fixed // // Revision 1.130 2002/08/22 03:29:07 niceass // bug fix in countdown code // // Revision 1.129 2002/08/21 07:00:07 jbravo // Added CTB respawn queue and fixed game <-> cgame synch problem in CTB // // Revision 1.128 2002/08/07 16:13:33 jbravo // Case carrier glowing removed. Ignorenum bug fixed // // Revision 1.127 2002/08/07 03:49:44 jbravo // No rcon stuff use allowed in matchmode // // Revision 1.126 2002/08/07 03:35:57 jbravo // Added dynamic radio and stopped all radio usage during lca // // Revision 1.125 2002/08/03 06:21:04 jbravo // Fixed the Akimbo ammo when akimbos are not the primary weapon // // Revision 1.124 2002/07/19 04:40:56 jbravo // Added 2 new radio sets and ctb radio sound support // // Revision 1.123 2002/07/11 04:27:29 niceass // crash bug fix for CTB & not selecting an item // // Revision 1.122 2002/07/07 18:36:13 jbravo // Added an AntiIdle system. Can play insane sounds for idle players, drop them // from teams or kick them. Upped version to Beta 2.1 // // Revision 1.121 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.120 2002/07/02 18:30:53 makro // Fixed bug with akimbo/pistol clips in TP // // Revision 1.119 2002/07/02 03:41:59 jbravo // Fixed a 2 frags pr kill bug, the use cmd now cancels weaponchanges in progress // and fixed the captain status lingering on people after switching from MM // // Revision 1.118 2002/07/01 02:56:15 blaze // moved the start demo code to where it prints the 20 seconds till match start // // Revision 1.117 2002/07/01 02:18:42 jbravo // Small fixes to CTB and possible fix for subs and limchasecam // // Revision 1.116 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.115 2002/06/26 03:28:36 niceass // upper right HUD scores updated quickly // // Revision 1.114 2002/06/24 05:51:51 jbravo // CTF mode is now semi working // // Revision 1.113 2002/06/22 00:19:57 jbravo // Cleanups for colors and stopped bots looking for team leaders in TP // // Revision 1.112 2002/06/21 15:04:55 makro // Health functionality for movers should be complete now // // Revision 1.111 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.110 2002/06/20 02:27:30 jbravo // Now the scoreboard doesnt show whos alive and whos not when you are alive // // Revision 1.109 2002/06/19 18:13:57 jbravo // New TNG spawning system :) // // Revision 1.108 2002/06/17 03:30:33 jbravo // More color fixes // // Revision 1.107 2002/06/16 20:06:14 jbravo // Reindented all the source files with "indent -kr -ut -i8 -l120 -lc120 -sob -bad -bap" // // Revision 1.106 2002/06/12 01:23:36 blaze // fix starting demos with autoaction // // Revision 1.105 2002/06/10 14:02:52 slicer // Removed rq3_cmd : ROUND // // Revision 1.104 2002/06/08 10:51:42 slicer // Added a better end message for MM matches // // Revision 1.103 2002/06/07 19:07:08 slicer // removed cvars for teamXready, replaced by level.teamXready // // Revision 1.102 2002/06/05 23:39:40 blaze // unbreakables work properly. Though I already commited this. // // Revision 1.101 2002/06/04 07:20:48 niceass // no moreTKing in MM // // Revision 1.100 2002/05/31 17:32:11 jbravo // HC gibs almost working :) // // Revision 1.99 2002/05/31 00:17:06 jbravo // Slicers fix for the weaponswitching issue // // Revision 1.98 2002/05/30 18:22:20 jbravo // Misc fixes // // Revision 1.97 2002/05/23 04:53:41 blaze // some func_breakable fixes. Explosives respawn on new rounds now . // // Revision 1.96 2002/05/23 03:07:10 blaze // Some changes to autoaction, still need to fix up a little bit more stuff relating to getting sent 2 screenshot requests // // Revision 1.95 2002/05/20 16:23:44 jbravo // Fixed spec problem when noone is alive. Fixed kicking teammates bug // // Revision 1.94 2002/05/20 05:11:56 jbravo // Fixed specmodes when nobody is alive // // Revision 1.93 2002/05/20 04:59:33 jbravo // Lots of small fixes. // // Revision 1.92 2002/05/18 21:57:54 blaze // Transmit round start to clients // used for cg_rq3_autoaction // // Revision 1.91 2002/05/13 06:17:20 jbravo // Ignore fix // // Revision 1.90 2002/05/12 19:15:47 jbravo // Added playerlist, did some cleanup on votes. // // Revision 1.89 2002/05/12 16:10:19 jbravo // Added unignore // // Revision 1.88 2002/05/12 00:07:47 slicer // Added Normal Radio Flood Protection // // Revision 1.87 2002/05/11 16:22:38 slicer // Added a Repeat Flood Protection to Radio // // Revision 1.86 2002/05/11 14:22:06 makro // Func_statics now reset at the beginning of each round // // Revision 1.85 2002/05/10 04:06:27 jbravo // Added Ignore // // Revision 1.84 2002/05/09 02:43:12 jbravo // Fixing stuff and use cmd's // // Revision 1.83 2002/05/07 13:35:45 jbravo // Fixed the double lights for spectators and made the use cmd use rq3_cmd // and made scoreboard not revieal whos alive or dead to live players. // // Revision 1.82 2002/05/06 21:41:01 slicer // Added rq3_cmd // // Revision 1.81 2002/05/06 00:35:49 jbravo // Small fixes to misc stuff // // Revision 1.80 2002/05/05 16:51:36 slicer // Fixed a problem on MakeAllPlayersObservers() // // Revision 1.78 2002/05/05 04:23:00 jbravo // Some MM fixes and cleanups // // Revision 1.77 2002/05/05 01:20:50 jbravo // Delay the lights sound 5 server frames. // // Revision 1.76 2002/05/04 16:19:02 jbravo // Fixing the stuff cmd to work on dedicated servers. // // Revision 1.75 2002/05/04 16:13:05 makro // Bots // // Revision 1.74 2002/05/04 01:03:43 makro // Bots // // Revision 1.73 2002/05/02 00:02:19 jbravo // Added a fix for the incorrect weapon at spawns // // Revision 1.72 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.71 2002/04/30 11:54:37 makro // Bots rule ! Also, added clips to give all. Maybe some other things // // Revision 1.70 2002/04/30 11:20:12 jbravo // Redid the teamcount cvars. // // Revision 1.69 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.68 2002/04/29 10:58:07 jbravo // My lights sound fix broke the scoreboard removal on round begins. // // Revision 1.67 2002/04/29 01:15:07 jbravo // Think I fixed the lights.wav thing // // Revision 1.66 2002/04/28 11:03:46 slicer // Added "teammodel" for Matchmode, Referee "pause" command // // Revision 1.65 2002/04/26 03:39:34 jbravo // added tkok, fixed players always leaving zcam modes when player thats // beeing tracked dies // // Revision 1.64 2002/04/23 11:24:06 jbravo // Removed a debug message and did some cleanups // // Revision 1.63 2002/04/13 15:37:54 jbravo // limchasecam has been redone with new spec system // // Revision 1.62 2002/04/08 20:14:34 blaze // func_breakable explode fix // // Revision 1.61 2002/04/07 12:49:10 slicer // Added 'teamname' command for MM, and tweaked the cvar system. // // Revision 1.60 2002/04/07 03:22:48 jbravo // Tweaks and crashbug fixes // // Revision 1.59 2002/04/06 21:42:20 makro // Changes to bot code. New surfaceparm system. // // Revision 1.58 2002/04/05 06:50:25 blaze // breakables should now respawn when the round restarts( when g_teamplay:SpawnPlayers() is called to be exact) // // Revision 1.57 2002/04/03 15:51:01 jbravo // Small warning fixes // // Revision 1.56 2002/04/03 09:26:47 jbravo // New FF system. Warns and then finally kickbans teamwounders and // teamkillers // // Revision 1.55 2002/04/02 21:45:15 jbravo // change for makro // // Revision 1.54 2002/04/02 21:41:03 jbravo // But in new bot radio code. // // Revision 1.53 2002/04/02 20:23:12 jbravo // Bots dont get to use any specmode other than FREE and the recive radio cmds // as text and not sounds. // // Revision 1.52 2002/04/02 04:18:58 jbravo // Made the TP scoreboard go down at round beginig (not for spectators) and // pop up at intermission. Also added special to the use command // // Revision 1.51 2002/03/31 23:41:45 jbravo // Added the use command // // Revision 1.50 2002/03/31 18:36:27 jbravo // Added $T (Near by teammates) // // Revision 1.49 2002/03/31 03:31:24 jbravo // Compiler warning cleanups // // Revision 1.48 2002/03/30 17:37:49 jbravo // Added damage tracking to the server. Added zcam flic mode. cleaned up g_damage. // // Revision 1.47 2002/03/30 02:29:43 jbravo // Lots of spectator code updates. Removed debugshit, added some color. // // Revision 1.46 2002/03/26 11:32:05 jbravo // Remember specstate between rounds. // // Revision 1.45 2002/03/26 10:32:52 jbravo // Bye bye LCA lag // // Revision 1.44 2002/03/25 14:55:01 jbravo // teamCount cvars for Makro // // Revision 1.43 2002/03/24 06:06:53 jbravo // Some tweaks and cleanups // // Revision 1.42 2002/03/23 21:29:42 jbravo // I finally fixed snipers spawning with pistol up. g_RQ3_sniperup has been // reinstated. // // Revision 1.41 2002/03/23 05:17:43 jbravo // Major cleanup of game -> cgame communication with LCA vars. // // Revision 1.40 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.39 2002/03/21 15:02:05 jbravo // More teamname cleanups and fix for fraglines. // // Revision 1.38 2002/03/18 12:25:10 jbravo // Live players dont get fraglines, except their own. Cleanups and some // hacks to get bots to stop using knives only. // // Revision 1.37 2002/03/17 16:38:57 jbravo // Colored nicks fucked up the string in $K messages. // // Revision 1.36 2002/03/17 03:35:29 jbravo // More radio tewaks and cleanups. // // Revision 1.35 2002/03/17 01:55:43 jbravo // Fixed a small bug in radio enemyd that made it say "one enemu down" // // Revision 1.34 2002/03/17 00:40:23 jbravo // Adding variable team names. g_RQ3_team1name and g_RQ3_team2name. Fixed // Slicers fraglimit check. // // Revision 1.33 2002/03/14 23:54:12 jbravo // Added a variable system from AQ. Works the same except it uses $ for % // // Revision 1.32 2002/03/14 02:24:39 jbravo // Adding radio :) // // Revision 1.31 2002/03/11 18:02:33 slicer // Fixed team changes and scoreboard bugs // // Revision 1.30 2002/03/07 14:29:12 slicer // Intermission ala aq2, when timelimit/roundlimit hits. // // Revision 1.29 2002/03/05 02:08:16 jbravo // Fixed Unknown client command: Roundlimit bug // // Revision 1.28 2002/03/03 23:51:45 jbravo // Final fixes for anims at spawn and a knife bug. // // Revision 1.27 2002/03/03 18:00:26 jbravo // More Anim fixes. Knives still broken // // Revision 1.26 2002/03/03 03:11:37 jbravo // Use propper weapon anims on TP spawns // // Revision 1.25 2002/03/02 16:16:54 jbravo // Fixed the uneven team calculations // // Revision 1.24 2002/03/01 20:02:34 jbravo // Added ui_RQ3_teamCount1, ui_RQ3_teamCount2 and ui_RQ3_numSpectators for // makro // // Revision 1.23 2002/03/01 18:21:26 jbravo // Cleanups and removal of g_RQ3_sniperup // // Revision 1.22 2002/02/27 01:54:29 jbravo // More spectatorfixes and finally stopped all fallingdamage anims and // sounds during LCA. // // Revision 1.21 2002/02/25 19:51:26 jbravo // Fixed a bug that made it impossible to pick up special weapons when player // had selected akimbos or knives // // Revision 1.20 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.19 2002/02/24 18:12:19 jbravo // Added a cvar to control sniper behavior g_RQ3_sniperup. Def 0. if set yo 1 // it makes players spawn with the sniper up. // // Revision 1.18 2002/02/24 17:56:44 jbravo // Cleaned up EquipPlayer() and made snipers spawn with pistol in hand // // Revision 1.17 2002/02/24 16:40:35 jbravo // Fixed a bug where the mode of knifes and grenades where reset to slashing // and short throw. // // Revision 1.16 2002/02/22 02:13:13 jbravo // Fixed a few bugs and did some cleanups // // Revision 1.15 2002/02/11 00:30:23 niceass // LCA fix // // Revision 1.14 2002/02/10 04:55:28 jbravo // Fix #1 to zcam jitter. More is probably needed. // // Revision 1.13 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.12 2002/02/06 03:10:43 jbravo // Fix the instant spectate on death and an attempt to fix the scores // // Revision 1.11 2002/02/05 23:41:27 slicer // More on matchmode.. // // Revision 1.10 2002/02/05 10:04:41 jbravo // Debugging message gone and cleanup of Antistick routinr // // Revision 1.9 2002/02/04 05:44:00 jbravo // Fixed a typo in knives name // // Revision 1.8 2002/02/03 21:23:51 slicer // More Matchmode code and fixed 2 bugs in TP // // Revision 1.7 2002/01/29 03:13:45 jbravo // Further fixes to antistick // // Revision 1.6 2002/01/27 13:42:19 jbravo // temp hack removed // // Revision 1.5 2002/01/27 13:33:28 jbravo // Teamplay antistick system. // // Revision 1.4 2002/01/23 15:59:43 jbravo // Make use of NiceAsses ClearBodyQue() between rounds // // Revision 1.3 2002/01/21 20:03:15 jbravo // Changed the knifes fix a bit // // Revision 1.2 2002/01/21 19:52:31 jbravo // Fixed a bug that didnt allow knifes to be selected as primary weapon // // Revision 1.1 2002/01/11 20:23:41 jbravo // Added the two new files for TP I forgot during the main TP commit // // //----------------------------------------------------------------------------- #include "g_local.h" #include "zcam.h" void RQ3_SetupTeamSpawnPoints(void); gitem_t *BG_FindItemForHoldable(holdable_t pw); char *ConcatArgs(int start); int touch[MAX_GENTITIES]; void ClearBodyQue(void); void Cmd_DropItem_f(gentity_t * ent); void Cmd_DropWeapon_f(gentity_t * ent); void AddIP(char *str); gentity_t *FindClientByPersName(char *name) { int i; gentity_t *other, *found; char cleanname[MAX_NETNAME]; found = NULL; cleanname[0] = '\0'; for (i = 0; i <= level.maxclients; i++) { other = &g_entities[i]; if (!other->inuse) continue; if (other && other->client) { strcpy(cleanname, other->client->pers.netname); Q_CleanStr(cleanname); if (Q_stricmp(name, cleanname) == 0) { found = other; break; } } } return (found); } void CheckTeamRules() { int winner, checked_tie = 0; //Slicer if (level.intermissiontime) return; level.fps = trap_Cvar_VariableIntegerValue("sv_fps"); if (level.lights_delay) { level.lights_delay--; if (level.lights_delay == 1) { level.lights_delay = 0; trap_SendServerCommand(-1, va("rq3_cmd %i", LIGHTS)); } } if (level.lights_camera_action) { ContinueLCA(); return; } if (level.team_round_going) level.current_round_length++; if (level.holding_on_tie_check) { level.holding_on_tie_check--; if (level.holding_on_tie_check > 0) return; level.holding_on_tie_check = 0; checked_tie = 1; } if (level.team_round_countdown == 1) { level.team_round_countdown = 0; if (BothTeamsHavePlayers()) { level.team_game_going = 1; level.inGame = qtrue; StartLCA(); } else { //Slicer: Adding Matchmode if (g_RQ3_matchmode.integer) { if (level.paused) trap_SendServerCommand(-1, "cp \"Referee has paused the Game!\n\""); else if (level.team1ready && level.team2ready) trap_SendServerCommand(-1, "cp \"Not enough players to play!\n\""); else if (level.refAmmount && !level.refStatus) trap_SendServerCommand(-1, "cp \"At least one Referee needs to be ready!\n\""); else trap_SendServerCommand(-1, "cp \"Both Teams Must Be Ready!\n\""); } else trap_SendServerCommand(-1, "cp \"Not enough players to play!\n\""); MakeAllLivePlayersObservers(); level.inGame = qfalse; } } else { if (level.team_round_countdown) level.team_round_countdown--; if (g_RQ3_tpcountdown.integer && level.team_round_countdown == (101 * level.fps) / 10) { trap_SendServerCommand(-1, va("rq3_cmd %i", TPCOUNTDOWN)); trap_SendServerCommand(-1, va("rq3_cmd %i 0", MAPSTART)); } } level.rulecheckfrequency++; if (level.rulecheckfrequency % 30 && !checked_tie) return; if (!level.team_round_going) { if (g_timelimit.integer) { //Slicer : Matchmode if (g_RQ3_matchmode.integer) { if (level.matchTime >= g_timelimit.integer * 60) { level.inGame = level.team_round_going = level.team_round_countdown = level.team_game_going = level.matchTime = 0; level.team1ready = qfalse; level.team2ready = qfalse; MakeAllLivePlayersObservers(); trap_SendServerCommand(-1, "cp \"Match is OVER !!!.\n\""); return; } } else { if (level.time - level.startTime >= g_timelimit.integer * 60000) { //Slicer : Let's do a normal console print instead.. trap_SendServerCommand(-1, "print \"^1Timelimit hit.\n\""); level.team_round_going = level.team_round_countdown = level.team_game_going = 0; //Slicer: Start Intermission BeginIntermission(); return; } } } if (!level.team_round_countdown) { if (BothTeamsHavePlayers()) { //Slicer let's print to the console too if (g_gametype.integer == GT_TEAMPLAY) { trap_SendServerCommand(-1, "print \"The round will begin in 20 seconds!\n\""); trap_SendServerCommand(-1, va("cp \"The round will begin in 20 seconds!\n\"")); } else { trap_SendServerCommand(-1, "print \"The game will begin in 20 seconds!\n\""); trap_SendServerCommand(-1, va("cp \"The game will begin in 20 seconds!\n\"")); } level.team_round_countdown = (201 * level.fps) / 10; //Blaze: moved from below the G_AddEvent trap_SendServerCommand(-1, va("rq3_cmd %i 0", STARTDEMO)); } } } else { if (g_gametype.integer == GT_TEAMPLAY && ((winner = CheckForWinner()) != WINNER_NONE)) { if (!checked_tie) { level.holding_on_tie_check = (50 * level.fps) / 10; return; } if (WonGame(winner)) return; level.team_round_going = 0; level.lights_camera_action = 0; level.holding_on_tie_check = 0; level.team_round_countdown = (71 * level.fps) / 10; return; } if (g_gametype.integer == GT_TEAMPLAY && g_RQ3_roundtimelimit.integer && (level.current_round_length > g_RQ3_roundtimelimit.integer * 60 * level.fps)) { trap_SendServerCommand(-1, va("cp \"Round timelimit hit.\n\"")); winner = CheckForForcedWinner(); if (WonGame(winner)) return; level.team_round_going = 0; level.lights_camera_action = 0; level.holding_on_tie_check = 0; level.team_round_countdown = (71 * level.fps) / 10; return; } if (g_gametype.integer == GT_CTF || g_gametype.integer == GT_TEAM) { qboolean matchOver = qfalse; if ( g_RQ3_matchmode.integer && g_RQ3_roundtimelimit.integer && (level.current_round_length > g_RQ3_roundtimelimit.integer * 60 * level.fps)) { matchOver = qtrue; } else if ( !g_RQ3_matchmode.integer && level.time - level.startTime >= g_timelimit.integer * 60000) { matchOver = qtrue; } if ( matchOver ) { trap_SendServerCommand(-1, "print \"^1Timelimit hit.\n\""); trap_SendServerCommand(-1, va("print \"^6Scores:^7 %s [^3%d^7] - %s [^3%d^7]\n\"", g_RQ3_team1name.string, level.teamScores[TEAM_RED], g_RQ3_team2name.string, level.teamScores[TEAM_BLUE])); if (level.teamScores[TEAM_BLUE] < level.teamScores[TEAM_RED]) trap_SendServerCommand(-1, va("print \"^1%s WIN!\n\"", g_RQ3_team1name.string)); else if (level.teamScores[TEAM_RED] < level.teamScores[TEAM_BLUE]) trap_SendServerCommand(-1, va("print \"^1%s WIN!\n\"", g_RQ3_team2name.string)); else trap_SendServerCommand(-1, va("print \"^1It was a TIE. No team wins!\n\"")); level.team_round_going = level.team_round_countdown = level.team_game_going = 0; BeginIntermission(); return; } } } } void StartLCA() { int i; gentity_t *player; CleanLevel(); trap_Cvar_Set("g_RQ3_lca", "1"); level.lights_camera_action = (41 * level.fps) / 10; G_LogPrintf("LIGHTS...\n"); if (g_gametype.integer == GT_TEAMPLAY || g_gametype.integer == GT_TEAM) { SpawnPlayers(); } else if (g_gametype.integer == GT_CTF) { RQ3_Respawn_CTB_players(TEAM_RED); RQ3_Respawn_CTB_players(TEAM_BLUE); if (g_RQ3_limchasecam.integer != 0 && g_RQ3_matchmode.integer ) { for (i = 0; i < level.maxclients; i++) { player = &g_entities[i]; if (!player->inuse || !player->client) continue; if (player->client->sess.sub != TEAM_FREE) Cmd_FollowCycle_f(player, 1); } } } level.lights_delay = 4; } void ContinueLCA() { if (level.lights_camera_action == (21 * level.fps) / 10) { G_LogPrintf("CAMERA...\n"); trap_SendServerCommand(-1, va("rq3_cmd %i", CAMERA)); } else if (level.lights_camera_action == 1) { G_LogPrintf("ACTION!\n"); trap_SendServerCommand(-1, va("rq3_cmd %i", ACTION)); trap_Cvar_Set("g_RQ3_lca", "0"); level.team_round_going = 1; level.current_round_length = 0; if ( level.startTime == 0 ) { level.startTime = level.time; } } level.lights_camera_action--; } qboolean BothTeamsHavePlayers() { int onteam1 = 0, onteam2 = 0; //Slicer: Matchmode if (g_RQ3_matchmode.integer && ((level.refAmmount && !level.refStatus) || !level.team1ready || !level.team2ready || level.paused)) return 0; onteam1 = RQ3TeamCount(-1, TEAM_RED); onteam2 = RQ3TeamCount(-1, TEAM_BLUE); return (onteam1 > 0 && onteam2 > 0); } void MakeAllLivePlayersObservers() { gentity_t *player; int i; for (i = 0; i < level.maxclients; i++) { player = &g_entities[i]; if (!player->inuse || !player->client) continue; if (level.clients[i].sess.sessionTeam == TEAM_SPECTATOR) { StopFollowing(player); } else { //Slicer Adding this.. level.clients[i].ps.pm_type = PM_DEAD; level.clients[i].ps.weapon = WP_NONE; level.clients[i].sess.sessionTeam = TEAM_SPECTATOR; StopFollowing(player); } } } void CleanLevel() { gentity_t *ent; int i; if (g_gametype.integer == GT_TEAM && g_RQ3_tdmMode.integer) return; ClearBodyQue(); ent = &g_entities[MAX_CLIENTS]; //Makro - call the reset function for all the entities in the map for (i = MAX_CLIENTS; i < level.num_entities; i++, ent++) { if (ent->reset && !ent->noreset) ent->reset(ent); } } int CheckForWinner() { int onteam1 = 0, onteam2 = 0, i; gentity_t *player; for (i = 0; i < level.maxclients; i++) { player = &g_entities[i]; if (!player->inuse) continue; if (level.clients[i].sess.sessionTeam == TEAM_RED && player->client->ps.stats[STAT_HEALTH] > 0) onteam1++; else if (level.clients[i].sess.sessionTeam == TEAM_BLUE && player->client->ps.stats[STAT_HEALTH] > 0) onteam2++; } if (onteam1 > 0 && onteam2 > 0) return WINNER_NONE; else if (onteam1 == 0 && onteam2 == 0) return WINNER_TIE; else if (onteam1 > 0 && onteam2 == 0) return WINNER_TEAM1; else return WINNER_TEAM2; } int CheckForForcedWinner() { int onteam1 = 0, onteam2 = 0, i; int health1 = 0, health2 = 0; gentity_t *player; for (i = 0; i < level.maxclients; i++) { player = &g_entities[i]; if (!player->inuse) continue; if (level.clients[i].sess.savedTeam == TEAM_RED && player->client->ps.stats[STAT_HEALTH] > 0) { onteam1++; health1 += player->client->ps.stats[STAT_HEALTH]; } else if (level.clients[i].sess.savedTeam == TEAM_BLUE && player->client->ps.stats[STAT_HEALTH] > 0) { onteam2++; health2 += player->client->ps.stats[STAT_HEALTH]; } } if (onteam1 > onteam2) { return WINNER_TEAM1; } else if (onteam2 > onteam1) { return WINNER_TEAM2; } else { if (health1 > health2) return WINNER_TEAM1; else if (health2 > health1) return WINNER_TEAM2; else return WINNER_TIE; } } int WonGame(int winner) { trap_SendServerCommand(-1, "print \"The round is over:\n\""); G_LogPrintf("The round is over:\n"); if (winner == WINNER_TIE) { trap_SendServerCommand(-1, "print \"It was a tie, no points awarded!\n\""); G_LogPrintf("It was a tie, no points awarded!\n"); } else { if (winner == WINNER_TEAM1) { trap_SendServerCommand(-1, va("print \"%s won!\n\"", g_RQ3_team1name.string)); G_LogPrintf("%s won!\n", g_RQ3_team1name.string); level.teamScores[TEAM_RED]++; } else { trap_SendServerCommand(-1, va("print \"%s won!\n\"", g_RQ3_team2name.string)); G_LogPrintf("%s won!\n", g_RQ3_team2name.string); level.teamScores[TEAM_BLUE]++; } } // NiceAss: This should keep the client updated with teamscores promptly. CalculateRanks(); if (g_timelimit.integer) { //Slicer : Matchmode if (g_RQ3_matchmode.integer) { if (level.matchTime >= g_timelimit.integer * 60) { SendEndMessage(); level.inGame = level.team_round_going = level.team_round_countdown = level.team_game_going = level.matchTime = 0; level.team1ready = qfalse; level.team2ready = qfalse; MakeAllLivePlayersObservers(); return 1; } } else { if (level.time - level.startTime >= g_timelimit.integer * 60000) { trap_SendServerCommand(-1, "print \"Timelimit hit.\n\""); level.team_round_going = level.team_round_countdown = level.team_game_going = 0; //Slicer: Start Intermission BeginIntermission(); return 1; } } } if (g_RQ3_roundlimit.integer) { if (level.teamScores[TEAM_RED] >= g_RQ3_roundlimit.integer || level.teamScores[TEAM_BLUE] >= g_RQ3_roundlimit.integer) { //Slicer : Matchmode if (g_RQ3_matchmode.integer) { SendEndMessage(); level.inGame = level.team_round_going = level.team_round_countdown = level.team_game_going = level.matchTime = 0; level.team1ready = qfalse; level.team2ready = qfalse; MakeAllLivePlayersObservers(); return 1; } else { //Slicer: Adding a normal console print.. trap_SendServerCommand(-1, "print \"Roundlimit hit.\n\""); trap_SendServerCommand(-1, va("cp \"Roundlimit hit.\n\"")); level.team_round_going = level.team_round_countdown = level.team_game_going = 0; //Slicer: Start Intermission BeginIntermission(); return 1; } } } return 0; } team_t RQ3TeamCount(int ignoreClientNum, int team) { int i, count = 0; for (i = 0; i < level.maxclients; i++) { if (i == ignoreClientNum) { continue; } if (level.clients[i].pers.connected == CON_DISCONNECTED) { continue; } //Slicer: Matchmode - Subs don't count if (g_RQ3_matchmode.integer && level.clients[i].sess.sub != TEAM_FREE) continue; if (level.clients[i].sess.savedTeam == team) { count++; } } return count; } void CheckForUnevenTeams(gentity_t * player) { int i, onteam1 = 0, onteam2 = 0; gentity_t *ent; for (i = 0; i < level.maxclients; i++) { ent = &g_entities[i]; if (ent->inuse) { if (ent->client->sess.savedTeam == TEAM_RED) onteam1++; else if (ent->client->sess.savedTeam == TEAM_BLUE) onteam2++; } } if (player->client->sess.savedTeam == TEAM_RED) { if (onteam1 > onteam2) trap_SendServerCommand(player - g_entities, va("print \"Your team now has %d more player%s than %s\n\"", onteam1 - onteam2, onteam1 - onteam2 == 1 ? "" : "s", g_RQ3_team2name.string)); } else if (player->client->sess.savedTeam == TEAM_BLUE) { if (onteam2 > onteam1) trap_SendServerCommand(player - g_entities, va("print \"Your team now has %d more player%s than %s\n\"", onteam2 - onteam1, onteam2 - onteam1 == 1 ? "" : "s", g_RQ3_team1name.string)); } } void SpawnPlayers() { gentity_t *player; gclient_t *client; int clientNum, i; level.spawnPointsLocated = qfalse; RQ3_SetupTeamSpawnPoints(); for (i = 0; i < level.maxclients; i++) { player = &g_entities[i]; if (!player->inuse || !player->client) continue; //Slicer: Matchmode - Subs don't spawn if (g_RQ3_matchmode.integer && player->client->sess.sub != TEAM_FREE) continue; // JBravo: lets not respawn spectators in free floating mode if (player->client->sess.savedTeam == TEAM_SPECTATOR && player->client->specMode == SPECTATOR_FREE) { continue; } client = player->client; clientNum = client - level.clients; client->sess.teamSpawn = qtrue; client->idletime = 0; if (client->sess.savedTeam == TEAM_RED) { client->sess.sessionTeam = TEAM_RED; client->ps.persistant[PERS_TEAM] = TEAM_RED; client->sess.spectatorState = SPECTATOR_NOT; } else if (client->sess.savedTeam == TEAM_BLUE) { client->sess.sessionTeam = TEAM_BLUE; client->ps.persistant[PERS_TEAM] = TEAM_BLUE; client->sess.spectatorState = SPECTATOR_NOT; } client->ps.stats[STAT_RQ3] &= ~RQ3_PLAYERSOLID; ResetKills(player); client->last_damaged_players[0] = '\0'; ClientSpawn(player); ClientUserinfoChanged(clientNum); client->sess.teamSpawn = qfalse; } // JBravo: lets make those pesky subs follow live players. if (g_RQ3_limchasecam.integer != 0 && g_RQ3_matchmode.integer) { for (i = 0; i < level.maxclients; i++) { player = &g_entities[i]; if (!player->inuse || !player->client) continue; if (player->client->sess.sub != TEAM_FREE) Cmd_FollowCycle_f(player, 1); } } //Blaze: May aswell respawn breakables here //Makro - using a generic respawn function for all the entities now /* for (i = 0; i < level.num_entities; i++) { ent = &g_entities[i]; if (ent != NULL && ent->classname != NULL && !strcmp(ent->classname, "func_breakable")) { //re-link all unlinked breakables trap_LinkEntity(ent); ent->exploded = qfalse; ent->takedamage = qtrue; ent->s.eType = ET_BREAKABLE; ent->health = ent->health_saved; } } */ } /* Let the player Choose the weapon and/or item he wants */ void RQ3_Cmd_Choose_f(gentity_t * ent) { char *cmd; if (!ent->client) { return; // not fully in game yet } if (g_gametype.integer == GT_TEAM && g_RQ3_tdmMode.integer) { trap_SendServerCommand(ent - g_entities, va("print \"^1This Team DM mode does not allow you to choose weapons or items.\n\"")); return; } cmd = ConcatArgs(1); if (Q_stricmp(cmd, RQ3_MP5_NAME) == 0 || Q_stricmp(cmd, "mp5") == 0) { if ((int) g_RQ3_weaponban.integer & WPF_MP5) { ent->client->teamplayWeapon = WP_MP5; trap_SendServerCommand(ent - g_entities, va("print \"Weapon selected: %s\n\"", RQ3_MP5_NAME)); } else { trap_SendServerCommand(ent - g_entities, va("print \"^1%s is disabled on this server.\n\"", RQ3_MP5_NAME)); } } else if (Q_stricmp(cmd, RQ3_M3_NAME) == 0 || Q_stricmp(cmd, "m3") == 0) { if ((int) g_RQ3_weaponban.integer & WPF_M3) { ent->client->teamplayWeapon = WP_M3; trap_SendServerCommand(ent - g_entities, va("print \"Weapon selected: %s\n\"", RQ3_M3_NAME)); } else { trap_SendServerCommand(ent - g_entities, va("print \"^1%s is disabled on this server.\n\"", RQ3_M3_NAME)); } } else if (Q_stricmp(cmd, RQ3_M4_NAME) == 0 || Q_stricmp(cmd, "m4") == 0) { if ((int) g_RQ3_weaponban.integer & WPF_M4) { ent->client->teamplayWeapon = WP_M4; trap_SendServerCommand(ent - g_entities, va("print \"Weapon selected: %s\n\"", RQ3_M4_NAME)); } else { trap_SendServerCommand(ent - g_entities, va("print \"^1%s is disabled on this server.\n\"", RQ3_M4_NAME)); } } else if (Q_stricmp(cmd, RQ3_HANDCANNON_NAME) == 0 || Q_stricmp(cmd, "hc") == 0) { if ((int) g_RQ3_weaponban.integer & WPF_HC) { ent->client->teamplayWeapon = WP_HANDCANNON; trap_SendServerCommand(ent - g_entities, va("print \"Weapon selected: %s\n\"", RQ3_HANDCANNON_NAME)); } else { trap_SendServerCommand(ent - g_entities, va("print \"^1%s is disabled on this server.\n\"", RQ3_HANDCANNON_NAME)); } } else if (Q_stricmp(cmd, RQ3_SSG3000_NAME) == 0 || Q_stricmp(cmd, "sniper") == 0) { if ((int) g_RQ3_weaponban.integer & WPF_SNIPER) { ent->client->teamplayWeapon = WP_SSG3000; trap_SendServerCommand(ent - g_entities, va("print \"Weapon selected: %s\n\"", RQ3_SSG3000_NAME)); } else { trap_SendServerCommand(ent - g_entities, va("print \"^1%s is disabled on this server.\n\"", RQ3_SSG3000_NAME)); } } else if (Q_stricmp(cmd, RQ3_KNIFE_NAME) == 0 || Q_stricmp(cmd, "knives") == 0) { if ((int) g_RQ3_weaponban.integer & WPF_KNIFE) { ent->client->teamplayWeapon = WP_KNIFE; trap_SendServerCommand(ent - g_entities, va("print \"Weapon selected: %s\n\"", RQ3_KNIFE_NAME)); } else { trap_SendServerCommand(ent - g_entities, va("print \"^1%s is disabled on this server.\n\"", RQ3_KNIFE_NAME)); } } else if (Q_stricmp(cmd, RQ3_AKIMBO_NAME) == 0 || Q_stricmp(cmd, "akimbo") == 0) { if ((int) g_RQ3_weaponban.integer & WPF_DUAL) { ent->client->teamplayWeapon = WP_AKIMBO; trap_SendServerCommand(ent - g_entities, va("print \"Weapon selected: %s\n\"", RQ3_AKIMBO_NAME)); } else { trap_SendServerCommand(ent - g_entities, va("print \"^1%s is disabled on this server.\n\"", RQ3_AKIMBO_NAME)); } } else if (Q_stricmp(cmd, RQ3_KEVLAR_NAME) == 0 || Q_stricmp(cmd, "kevlar") == 0) { if ((int) g_RQ3_weaponban.integer & ITF_KEVLAR) { ent->client->teamplayItem = HI_KEVLAR; trap_SendServerCommand(ent - g_entities, va("print \"Item selected: %s\n\"", RQ3_KEVLAR_NAME)); } else { trap_SendServerCommand(ent - g_entities, va("print \"^1%s is disabled on this server.\n\"", RQ3_KEVLAR_NAME)); } } else if (Q_stricmp(cmd, RQ3_LASER_NAME) == 0 || Q_stricmp(cmd, "laser") == 0) { if ((int) g_RQ3_weaponban.integer & ITF_LASER) { ent->client->teamplayItem = HI_LASER; trap_SendServerCommand(ent - g_entities, va("print \"Item selected: %s\n\"", RQ3_LASER_NAME)); } else { trap_SendServerCommand(ent - g_entities, va("print \"^1%s is disabled on this server.\n\"", RQ3_LASER_NAME)); } } else if (Q_stricmp(cmd, RQ3_SLIPPERS_NAME) == 0 || Q_stricmp(cmd, "slippers") == 0) { if ((int) g_RQ3_weaponban.integer & ITF_SLIPPERS) { ent->client->teamplayItem = HI_SLIPPERS; trap_SendServerCommand(ent - g_entities, va("print \"Item selected: %s\n\"", RQ3_SLIPPERS_NAME)); } else { trap_SendServerCommand(ent - g_entities, va("print \"^1%s is disabled on this server.\n\"", RQ3_SLIPPERS_NAME)); } } else if (Q_stricmp(cmd, RQ3_SILENCER_NAME) == 0 || Q_stricmp(cmd, "silencer") == 0) { if ((int) g_RQ3_weaponban.integer & ITF_SILENCER) { ent->client->teamplayItem = HI_SILENCER; trap_SendServerCommand(ent - g_entities, va("print \"Item selected: %s\n\"", RQ3_SILENCER_NAME)); } else { trap_SendServerCommand(ent - g_entities, va("print \"^1%s is disabled on this server.\n\"", RQ3_SILENCER_NAME)); } } else if (Q_stricmp(cmd, RQ3_BANDOLIER_NAME) == 0 || Q_stricmp(cmd, "bandolier") == 0) { if ((int) g_RQ3_weaponban.integer & ITF_BANDOLIER) { ent->client->teamplayItem = HI_BANDOLIER; trap_SendServerCommand(ent - g_entities, va("print \"Item selected: %s\n\"", RQ3_BANDOLIER_NAME)); } else { trap_SendServerCommand(ent - g_entities, va("print \"^1%s is disabled on this server.\n\"", RQ3_BANDOLIER_NAME)); } } else if (Q_stricmp(cmd, RQ3_HELMET_NAME) == 0 || Q_stricmp(cmd, "helmet") == 0) { if (g_RQ3_haveHelmet.integer) { if ((int) g_RQ3_weaponban.integer & ITF_HELMET) { ent->client->teamplayItem = HI_HELMET; trap_SendServerCommand(ent - g_entities, va("print \"Item selected: %s\n\"", RQ3_HELMET_NAME)); } else { trap_SendServerCommand(ent - g_entities, va("print \"^1%s is disabled on this server.\n\"", RQ3_HELMET_NAME)); } } else { trap_SendServerCommand(ent - g_entities, va("print \"^1%s is disabled on this server.\n\"", RQ3_HELMET_NAME)); } } else { trap_SendServerCommand(ent - g_entities, va("print \"^1Invalid weapon or item choice.\n\"")); return; } } /* Drop weapon, Item or case ala. AQ */ void Cmd_Dropcase_f(gentity_t * ent) { gitem_t *item; if (!ent->client) return; if (!ent->client->ps.powerups[PW_REDFLAG] && !ent->client->ps.powerups[PW_BLUEFLAG]) { trap_SendServerCommand(ent - g_entities, va("print \"^1Go get the enemy case and try again.\n\"")); return; } item = NULL; if (ent->client->sess.sessionTeam == TEAM_RED) { item = BG_FindItemForPowerup(PW_BLUEFLAG); if (item) { ent->client->ps.powerups[PW_BLUEFLAG] = 0; dropWeapon(ent, item, 0, FL_DROPPED_ITEM | FL_THROWN_ITEM); ent->client->uniqueItems--; } } else if (ent->client->sess.sessionTeam == TEAM_BLUE) { item = BG_FindItemForPowerup(PW_REDFLAG); if (item) { ent->client->ps.powerups[PW_REDFLAG] = 0; dropWeapon(ent, item, 0, FL_DROPPED_ITEM | FL_THROWN_ITEM); ent->client->uniqueItems--; } } else { trap_SendServerCommand(ent - g_entities, va("print \"^1Huh? You dont have a flag to drop!\n\"")); } } void RQ3_Cmd_Drop_f(gentity_t * ent) { char cmd[MAX_TOKEN_CHARS]; if (!ent->client) { return; // not fully in game yet } trap_Argv(1, cmd, sizeof(cmd)); if (Q_stricmp(cmd, "item") == 0) { Cmd_DropItem_f(ent); } else if (Q_stricmp(cmd, "weapon") == 0) { Cmd_DropWeapon_f(ent); } else if (Q_stricmp(cmd, "case") == 0) { Cmd_Dropcase_f(ent); } else { trap_SendServerCommand(ent - g_entities, va("print \"^1unknown item: %s\n\"", cmd)); } } /* Equip the player with the weapons and items he needs for the round. */ void EquipPlayer(gentity_t * ent) { int bandolierFactor, grenades; if (!ent->client || ent->client->sess.sessionTeam == TEAM_SPECTATOR) return; bandolierFactor = grenades = 0; ent->client->ps.stats[STAT_WEAPONS] = 0; if (ent->client->teamplayItem == HI_BANDOLIER) { bandolierFactor = 2; grenades = trap_Cvar_VariableIntegerValue("g_RQ3_tgren"); } else { bandolierFactor = 1; } if ((int) g_RQ3_weaponban.integer & WPF_MK23) { ent->client->ps.stats[STAT_WEAPONS] = (1 << WP_PISTOL); ent->client->numClips[WP_PISTOL] = ent->client->numClips[WP_AKIMBO] = 1; // extra clip of ammo for pistol ent->client->ps.ammo[WP_PISTOL] = RQ3_PISTOL_AMMO; } if ((int) g_RQ3_weaponban.integer & WPF_KNIFE) { ent->client->ps.stats[STAT_WEAPONS] |= (1 << WP_KNIFE); ent->client->weaponCount[WP_KNIFE] = 1; ent->client->ps.ammo[WP_KNIFE] = 1; } switch (ent->client->teamplayWeapon) { case WP_SSG3000: ent->client->ps.stats[STAT_WEAPONS] |= (1 << WP_SSG3000); ent->client->numClips[WP_SSG3000] = RQ3_SSG3000_EXTRA_AMMO * bandolierFactor; ent->client->ps.ammo[WP_SSG3000] = RQ3_SSG3000_AMMO; if (g_RQ3_sniperup.integer == 1) { ent->client->ps.weapon = WP_SSG3000; ent->client->ps.weaponTime = RQ3_SSG3000_ACTIVATE_DELAY; } else { ent->client->ps.weapon = WP_PISTOL; ent->client->ps.weaponTime = RQ3_PISTOL_ACTIVATE_DELAY; } ent->client->weaponCount[WP_SSG3000] = 1; ent->client->uniqueWeapons = 1; break; case WP_MP5: ent->client->ps.stats[STAT_WEAPONS] |= (1 << WP_MP5); ent->client->numClips[WP_MP5] = RQ3_MP5_EXTRA_AMMO * bandolierFactor; ent->client->ps.ammo[WP_MP5] = RQ3_MP5_AMMO; ent->client->ps.weapon = WP_MP5; ent->client->ps.weaponTime = RQ3_MP5_ACTIVATE_DELAY; ent->client->weaponCount[WP_MP5] = 1; ent->client->uniqueWeapons = 1; break; case WP_M3: ent->client->ps.stats[STAT_WEAPONS] |= (1 << WP_M3); ent->client->numClips[WP_M3] = RQ3_M3_EXTRA_AMMO * bandolierFactor; ent->client->ps.ammo[WP_M3] = RQ3_M3_AMMO; ent->client->ps.weapon = WP_M3; ent->client->ps.weaponTime = RQ3_M3_ACTIVATE_DELAY; ent->client->weaponCount[WP_M3] = 1; ent->client->uniqueWeapons = 1; break; case WP_M4: ent->client->ps.stats[STAT_WEAPONS] |= (1 << WP_M4); ent->client->numClips[WP_M4] = RQ3_M4_EXTRA_AMMO * bandolierFactor; ent->client->ps.ammo[WP_M4] = RQ3_M4_AMMO; ent->client->ps.weapon = WP_M4; ent->client->ps.weaponTime = RQ3_M4_ACTIVATE_DELAY; ent->client->weaponCount[WP_M4] = 1; ent->client->uniqueWeapons = 1; break; case WP_AKIMBO: ent->client->ps.stats[STAT_WEAPONS] |= (1 << WP_AKIMBO); //Makro - added pistol clips ent->client->numClips[WP_PISTOL] = ent->client->numClips[WP_AKIMBO] = RQ3_AKIMBO_EXTRA_AMMO * bandolierFactor; ent->client->ps.ammo[WP_AKIMBO] = RQ3_AKIMBO_AMMO; ent->client->ps.weapon = WP_AKIMBO; ent->client->ps.weaponTime = RQ3_AKIMBO_ACTIVATE_DELAY; ent->client->weaponCount[WP_AKIMBO] = 1; ent->client->uniqueWeapons = 0; break; case WP_HANDCANNON: ent->client->ps.stats[STAT_WEAPONS] |= (1 << WP_HANDCANNON); ent->client->numClips[WP_HANDCANNON] = RQ3_HANDCANNON_EXTRA_AMMO * bandolierFactor; ent->client->ps.ammo[WP_HANDCANNON] = RQ3_HANDCANNON_AMMO; ent->client->ps.weapon = WP_HANDCANNON; ent->client->ps.weaponTime = RQ3_HANDCANNON_ACTIVATE_DELAY; ent->client->weaponCount[WP_HANDCANNON] = 1; ent->client->uniqueWeapons = 1; break; case WP_KNIFE: ent->client->ps.ammo[WP_KNIFE] = RQ3_KNIVES_EXTRA_AMMO * bandolierFactor; ent->client->ps.weapon = WP_KNIFE; ent->client->ps.weaponTime = RQ3_KNIFE_ACTIVATE_DELAY; ent->client->weaponCount[WP_KNIFE] = 1; ent->client->uniqueWeapons = 0; break; default: G_Printf("%s had an illegal teamplay weapon [%i]!\n", ent->client->pers.netname, ent->client->teamplayWeapon); break; } if (grenades > 0) { ent->client->ps.stats[STAT_WEAPONS] |= (1 << WP_GRENADE); ent->client->ps.ammo[WP_GRENADE] = grenades; } if (ent->client->teamplayWeapon == WP_KNIFE && !(ent->client->ps.persistant[PERS_WEAPONMODES] & RQ3_KNIFEMODE)) { ent->client->ps.generic1 = ((ent->client->ps.generic1 & ANIM_TOGGLEBIT) ^ ANIM_TOGGLEBIT) | WP_ANIM_THROWACTIVATE; } else { ent->client->ps.generic1 = ((ent->client->ps.generic1 & ANIM_TOGGLEBIT) ^ ANIM_TOGGLEBIT) | WP_ANIM_ACTIVATE; } if (!(ent->r.svFlags & SVF_BOT)) { trap_SendServerCommand(ent - g_entities, va("rq3_cmd %i %i", SETWEAPON, ent->client->ps.weapon)); } ent->client->ps.weaponstate = WEAPON_RAISING; if (ent->client->teamplayItem) { ent->client->ps.stats[STAT_HOLDABLE_ITEM] = (1 << ent->client->teamplayItem); ent->client->uniqueItems = 1; } else { G_Printf("%s had an illegal teamplay item [%i]!\n", ent->client->pers.netname, ent->client->teamplayItem); } } void UnstickPlayer(gentity_t * ent) { int i, num, count; gentity_t *hit; vec3_t mins, maxs; count = 0; VectorAdd(ent->client->ps.origin, ent->r.mins, mins); VectorAdd(ent->client->ps.origin, ent->r.maxs, maxs); num = trap_EntitiesInBox(mins, maxs, touch, MAX_GENTITIES); for (i = 0; i < num; i++) { hit = &g_entities[touch[i]]; if (hit->client && hit != ent) { count++; } } if (count == 0) { ent->client->ps.stats[STAT_RQ3] |= RQ3_PLAYERSOLID; } } void RQ3_StartTimer(int team, int delay) { int i; gentity_t *ent; for (i = 0; i < level.maxclients; i++) { ent = &g_entities[i]; if (!ent->inuse || !ent->client) continue; if (ent->client->pers.connected != CON_CONNECTED) continue; if (ent->client->sess.savedTeam == team) { trap_SendServerCommand(ent - g_entities, va("rq3_cmd %i %i", CTBCOUNTDOWN, delay)); } } } void RQ3_Respawn_CTB_players(int team) { int i; gentity_t *ent; for (i = 0; i < level.maxclients; i++) { ent = &g_entities[i]; if (!ent->inuse || !ent->client) continue; if (g_RQ3_matchmode.integer && ent->client->sess.sub != TEAM_FREE) continue; if (ent->client->sess.savedTeam == TEAM_SPECTATOR && ent->client->specMode == SPECTATOR_FREE) continue; if (ent->client->sess.savedTeam == team && (ent->client->sess.spectatorState == SPECTATOR_FREE || level.lights_camera_action)) { ent->client->weaponCount[ent->client->ps.weapon] = ent->client->savedPSweapon; ent->client->ps.stats[STAT_WEAPONS] = ent->client->savedSTAT; ent->client->sess.sessionTeam = team; ent->client->ps.persistant[PERS_TEAM] = team; ent->client->sess.spectatorState = SPECTATOR_NOT; ent->client->specMode = SPECTATOR_NOT; ent->client->idletime = 0; ResetKills(ent); ent->client->last_damaged_players[0] = '\0'; if (level.lights_camera_action) ent->client->pers.teamState.state = TEAM_BEGIN; else ent->client->pers.teamState.state = TEAM_ACTIVE; ClientSpawn(ent); } } } void MakeSpectator(gentity_t * ent) { gclient_t *client; gentity_t *follower; int i; client = ent->client; if (g_gametype.integer == GT_TEAMPLAY && client->sess.sessionTeam == TEAM_SPECTATOR) return; if (!client->gibbed || ent->s.eType != ET_INVISIBLE) CopyToBodyQue(ent); if (g_gametype.integer >= GT_TEAM) { for (i = 0; i < level.maxclients; i++) { follower = &g_entities[i]; if (!follower->inuse || !follower->client) continue; if (follower->client->pers.connected != CON_CONNECTED) continue; if (follower->client->sess.sessionTeam != TEAM_SPECTATOR) continue; if (follower->client->sess.spectatorClient == ent->s.number && follower->client->sess.spectatorState == SPECTATOR_FOLLOW) Cmd_FollowCycle_f(follower, 1); } } if (g_gametype.integer == GT_CTF) { client->savedPSweapon = client->weaponCount[ent->client->ps.weapon]; client->savedSTAT = client->ps.stats[STAT_WEAPONS]; } client->weaponCount[ent->client->ps.weapon] = 0; client->ps.stats[STAT_WEAPONS] = 0; client->sess.sessionTeam = TEAM_SPECTATOR; client->ps.persistant[PERS_TEAM] = TEAM_SPECTATOR; if (g_gametype.integer == GT_CTF && level.team_round_going) { client->sess.spectatorState = SPECTATOR_FREE; client->specMode = SPECTATOR_FREE; client->ps.pm_flags &= ~PMF_FOLLOW; client->ps.stats[STAT_RQ3] &= ~RQ3_ZCAM; client->ps.pm_type = PM_FREEZE; if (g_RQ3_ctb_respawndelay.integer == 0) { if (level.numPlayingClients <= 4) level.ctb_respawndelay = 5; else if (level.numPlayingClients <= 6) level.ctb_respawndelay = 6; else if (level.numPlayingClients <= 8) level.ctb_respawndelay = 7; else if (level.numPlayingClients <= 10) level.ctb_respawndelay = 8; else level.ctb_respawndelay = 10; } else { level.ctb_respawndelay = g_RQ3_ctb_respawndelay.integer; } if (client->sess.savedTeam == TEAM_RED) { if (level.team1respawn == 0) { level.team1respawn = level.time + (level.ctb_respawndelay * 1000); RQ3_StartTimer(TEAM_RED, level.ctb_respawndelay); } } else if (client->sess.savedTeam == TEAM_BLUE) { if (level.team2respawn == 0) { level.team2respawn = level.time + (level.ctb_respawndelay * 1000); RQ3_StartTimer(TEAM_BLUE, level.ctb_respawndelay); } } ClientSpawn(ent); ent->client->gibbed = qtrue; trap_UnlinkEntity(ent); return; } if (ent->r.svFlags & SVF_BOT) client->sess.spectatorState = SPECTATOR_FREE; else if (g_RQ3_limchasecam.integer != 0 ) { if (OKtoFollow(ent - g_entities)) { client->sess.spectatorState = SPECTATOR_FOLLOW; client->specMode = SPECTATOR_FOLLOW; client->ps.pm_flags |= PMF_FOLLOW; client->ps.stats[STAT_RQ3] &= ~RQ3_ZCAM; Cmd_FollowCycle_f(ent, 1); } else { client->sess.spectatorState = SPECTATOR_FREE; client->specMode = SPECTATOR_FREE; client->ps.pm_flags &= ~PMF_FOLLOW; client->ps.stats[STAT_RQ3] &= ~RQ3_ZCAM; } } else { if (client->specMode == SPECTATOR_NOT) { client->ps.pm_flags &= ~PMF_FOLLOW; client->ps.stats[STAT_RQ3] &= ~RQ3_ZCAM; client->sess.spectatorState = SPECTATOR_FREE; client->specMode = SPECTATOR_FREE; } if (client->specMode == SPECTATOR_FOLLOW) { if (OKtoFollow(ent - g_entities)) { client->sess.spectatorState = SPECTATOR_FOLLOW; client->ps.pm_flags |= PMF_FOLLOW; client->ps.stats[STAT_RQ3] &= ~RQ3_ZCAM; ClientSpawn(ent); Cmd_FollowCycle_f(ent, 1); return; } else { client->sess.spectatorState = SPECTATOR_FREE; client->ps.pm_flags &= ~PMF_FOLLOW; client->ps.stats[STAT_RQ3] &= ~RQ3_ZCAM; } } if (client->specMode == SPECTATOR_ZCAM) { if (OKtoFollow(ent - g_entities)) { client->sess.spectatorState = SPECTATOR_ZCAM; client->ps.pm_flags &= ~PMF_FOLLOW; client->ps.stats[STAT_RQ3] |= RQ3_ZCAM; ClientSpawn(ent); if (client->camera->mode == CAMERA_MODE_SWING) { CameraSwingCycle (ent, 1); } else if (client->camera->mode == CAMERA_MODE_FLIC) { CameraFlicBegin (ent); } else { client->camera->mode = CAMERA_MODE_SWING; CameraSwingCycle (ent, 1); } return; } else { client->sess.spectatorState = SPECTATOR_FREE; client->ps.pm_flags &= ~PMF_FOLLOW; client->ps.stats[STAT_RQ3] &= ~RQ3_ZCAM; } } } ClientSpawn(ent); } qboolean OKtoFollow(int clientnum) { int i, x; x = 0; for (i = 0; i < level.maxclients; i++) { if (i == clientnum) { continue; } if (level.clients[i].pers.connected != CON_CONNECTED) { continue; } if (level.clients[i].sess.sessionTeam == TEAM_SPECTATOR) { continue; } if (level.clients[clientnum].sess.referee == 1 && level.clients[clientnum].sess.savedTeam == TEAM_SPECTATOR) { x++; } if (g_gametype.integer == GT_TEAMPLAY && g_RQ3_limchasecam.integer != 0 && level.clients[i].sess.sessionTeam != level.clients[clientnum].sess.savedTeam) { continue; } return qtrue; } return qfalse; } void RQ3_Cmd_Radio_power_f(gentity_t * ent) { if (ent->client->radioOff == qfalse) { ent->client->radioOff = qtrue; trap_SendServerCommand(ent - g_entities, "cp \"Radio switched off\n\""); trap_SendServerCommand(ent - g_entities, va("rq3_cmd %i 25 0 0", RADIO)); } else { ent->client->radioOff = qfalse; trap_SendServerCommand(ent - g_entities, "cp \"Radio switched on\n\""); trap_SendServerCommand(ent - g_entities, va("rq3_cmd %i 25 0 0", RADIO)); } } void RQ3_Cmd_Radiogender_f(gentity_t * ent) { char arg[MAX_TOKEN_CHARS]; if (trap_Argc() == 1) { if (ent->client->radioGender == 0) { trap_SendServerCommand(ent - g_entities, "print \"Radio gender currently set to male\n\""); return; } else { trap_SendServerCommand(ent - g_entities, "print \"Radio gender currently set to female\n\""); return; } } trap_Argv(1, arg, sizeof(arg)); if (Q_stricmp(arg, "male") == 0) { trap_SendServerCommand(ent - g_entities, "print \"Radio gender set to male\n\""); ent->client->radioGender = 0; } else if (Q_stricmp(arg, "female") == 0) { trap_SendServerCommand(ent - g_entities, "print \"Radio gender set to female\n\""); ent->client->radioGender = 1; } else { trap_SendServerCommand(ent - g_entities, "print \"^1Invalid gender selection, try 'male' or 'female'\n\""); } } radio_msg_t male_radio_msgs[] = { {"1", 6}, {"2", 6}, {"3", 8}, {"4", 7}, {"5", 8}, {"6", 9}, {"7", 8}, {"8", 7}, {"9", 7}, {"10", 6}, {"back", 6}, {"cover", 7}, {"down", 13}, {"enemyd", 10}, {"enemys", 9}, {"forward", 6}, {"go", 6}, {"im_hit", 7}, {"left", 7}, {"reportin", 9}, {"right", 6}, {"taking_f", 22}, {"teamdown", 13}, {"treport", 12}, {"up", 4}, {"click", 4}, // JBravo: CTB sounds {"backup1", 1}, {"backup2", 1}, {"backup3", 1}, {"chit1", 1}, {"chit2", 1}, {"chit3", 1}, {"deliv1", 1}, {"deliv2", 1}, {"deliv3", 1}, {"ecdown1", 1}, {"ecdown2", 1}, {"ecdown3", 1}, {"enepak1", 1}, {"enepak2", 1}, {"enepak3", 1}, {"escort1", 1}, {"escort2", 1}, {"escort3", 1}, {"gotpak1", 1}, {"gotpak2", 1}, {"gotpak3", 1}, {"guard1", 1}, {"guard2", 1}, {"guard3", 1}, {"incoming1", 1}, {"incoming2", 1}, {"incoming3", 1}, {"plost1", 1}, {"plost2", 1}, {"plost3", 1}, {"END", 0}, // end of list delimiter }; radio_msg_t female_radio_msgs[] = { {"1", 5}, {"2", 5}, {"3", 5}, {"4", 5}, {"5", 5}, {"6", 8}, {"7", 7}, {"8", 5}, {"9", 5}, {"10", 5}, {"back", 6}, {"cover", 5}, {"down", 6}, {"enemyd", 9}, {"enemys", 9}, {"forward", 8}, {"go", 6}, {"im_hit", 7}, {"left", 8}, {"reportin", 9}, {"right", 5}, {"taking_f", 22}, {"teamdown", 10}, {"treport", 12}, {"up", 6}, {"click", 6}, // JBravo: CTB sounds {"backup1", 1}, {"backup2", 1}, {"backup3", 1}, {"chit1", 1}, {"chit2", 1}, {"chit3", 1}, {"deliv1", 1}, {"deliv2", 1}, {"deliv3", 1}, {"ecdown1", 1}, {"ecdown2", 1}, {"ecdown3", 1}, {"enepak1", 1}, {"enepak2", 1}, {"enepak3", 1}, {"escort1", 1}, {"escort2", 1}, {"escort3", 1}, {"gotpak1", 1}, {"gotpak2", 1}, {"gotpak3", 1}, {"guard1", 1}, {"guard2", 1}, {"guard3", 1}, {"incoming1", 1}, {"incoming2", 1}, {"incoming3", 1}, {"plost1", 1}, {"plost2", 1}, {"plost3", 1}, {"END", 0}, // end of list delimiter }; //Slicer Adding Flood Protection Functions qboolean CheckForFlood(gentity_t * ent) { //If he's muted.. if (ent->client->rd_mute) { if (ent->client->rd_mute > level.time) // Still muted.. return qfalse; else ent->client->rd_mute = 0; // No longer muted.. } if (!ent->client->rd_Count) { ent->client->rd_time = level.time; ++ent->client->rd_Count; } else { ++ent->client->rd_Count; if (level.time - ent->client->rd_time < g_RQ3_radioFloodTime.integer) { if (ent->client->rd_Count >= g_RQ3_radioFlood.integer) { trap_SendServerCommand(ent - g_entities, va ("print \"^1Radio Flood Detected, you are silenced for %i secs\n\"", (int) g_RQ3_radioBan.integer)); ent->client->rd_mute = level.time + g_RQ3_radioBan.integer * 1000; return qfalse; } } else { ent->client->rd_Count = 0; } } return qtrue; } qboolean CheckForRepeat(gentity_t * ent, int radioCode) { int lastRadioCode; //If he's muted.. if (ent->client->rd_mute) { if (ent->client->rd_mute > level.time) // Still muted.. return qfalse; else ent->client->rd_mute = 0; // No longer muted.. } lastRadioCode = ent->client->rd_lastRadio; if (lastRadioCode == radioCode) { //He's trying to repeat it.. if ((level.time - ent->client->rd_repTime) < g_RQ3_radioRepeatTime.integer * 1000) { if (++ent->client->rd_repCount >= g_RQ3_radioRepeat.integer) { //Busted if (ent->client->rd_repCount == g_RQ3_radioRepeat.integer) { trap_SendServerCommand(ent - g_entities, va ("print \"^1Radio Repeat Flood Detected, you are silenced for %i secs\n\"", (int) g_RQ3_radioBan.integer)); ent->client->rd_mute = level.time + g_RQ3_radioBan.integer * 1000; } return qfalse; } } else ent->client->rd_repCount = 0; ent->client->rd_repTime = level.time; } ent->client->rd_lastRadio = radioCode; ent->client->rd_repTime = level.time; return qtrue; } void RQ3_Cmd_Radio_f(gentity_t * ent) { char msg[MAX_TOKEN_CHARS]; radio_msg_t *radio_msgs; gentity_t *player; int i, x, kills, set; if (ent->client->sess.sessionTeam == TEAM_SPECTATOR || ent->health <= 0) { trap_SendServerCommand(ent - g_entities, "print \"^1It's kind of hard to talk in the radio when you can't breathe anymore...\n\""); return; } if (trap_Argc() < 2) { trap_SendServerCommand(ent - g_entities, "print \"^1Say again ? Radio command missing!\n\""); return; } if (g_RQ3_lca.integer) { trap_SendServerCommand(ent - g_entities, "print \"^1Announcer is busy talkin' now!\n\""); return; } if (ent->client->radioOff == qtrue) { trap_SendServerCommand(ent - g_entities, "print \"^1Your radio is off!\n\""); return; } if (ent->client->radioGender == 0) { radio_msgs = male_radio_msgs; set = ent->client->radioSetMale; } else { radio_msgs = female_radio_msgs; set = ent->client->radioSetFemale; } x = 0; trap_Argv(1, msg, sizeof(msg)); while (Q_stricmp(radio_msgs[x].msg, "END")) { if (!Q_stricmp(radio_msgs[x].msg, msg)) { //Slicer Checking for flood protections if (!CheckForRepeat(ent, x) || !CheckForFlood(ent)) return; if (!Q_stricmp(radio_msgs[x].msg, "enemyd")) { kills = ent->client->killStreak; ent->client->killStreak = 0; if (kills > 1 && kills < 10) { for (i = 0; i < level.maxclients; i++) { player = &g_entities[i]; if (!player->inuse) continue; if (IsInIgnoreList(player, ent)) continue; if (player->client->sess.savedTeam == ent->client->sess.savedTeam && player->client->radioOff == qfalse) trap_SendServerCommand(player - g_entities, va("rq3_cmd %i %i %i %i\n", RADIO, kills - 1, ent->client->radioGender, set)); } } } for (i = 0; i < level.maxclients; i++) { player = &g_entities[i]; if (!player->inuse) continue; if (IsInIgnoreList(player, ent)) continue; if (player->client->sess.savedTeam == ent->client->sess.savedTeam && player->client->radioOff == qfalse) { if (player->r.svFlags & SVF_BOT) trap_SendServerCommand(player - g_entities, va("print \"radio %s %s\n\"", ent->client->pers.netname, radio_msgs[x].msg)); else trap_SendServerCommand(player - g_entities, va("rq3_cmd %i %i %i %i\n", RADIO, x, ent->client->radioGender, set)); } } //Slicer lets return to stop the while if found.. return; } x++; // JBravo: no CTB sounds unless CTB mode is on. // JBravo: no, now they want CTB sounds in all gametypes. //if (g_gametype.integer != GT_CTF && x > 25) // return; } trap_SendServerCommand(ent - g_entities, "print \"^1Where'd you train? You can't say that over the radio!\n\""); } char *SeekBufEnd(char *buf) { while (*buf != 0) buf++; return buf; } void GetWeaponName(gentity_t * ent, char *buf) { if (ent->client->ps.weapon == WP_AKIMBO) { strcpy(buf, RQ3_AKIMBO_NAME); return; } else if (ent->client->ps.weapon == WP_PISTOL) { strcpy(buf, RQ3_PISTOL_NAME); return; } else if (ent->client->ps.weapon == WP_MP5) { strcpy(buf, RQ3_MP5_NAME); return; } else if (ent->client->ps.weapon == WP_M4) { strcpy(buf, RQ3_M4_NAME); return; } else if (ent->client->ps.weapon == WP_M3) { strcpy(buf, RQ3_M3_NAME); return; } else if (ent->client->ps.weapon == WP_HANDCANNON) { strcpy(buf, RQ3_HANDCANNON_NAME); return; } else if (ent->client->ps.weapon == WP_SSG3000) { strcpy(buf, RQ3_SSG3000_NAME); return; } else if (ent->client->ps.weapon == WP_KNIFE) { strcpy(buf, RQ3_KNIFE_NAME); return; } else if (ent->client->ps.weapon == WP_GRENADE) { strcpy(buf, RQ3_GRENADE_NAME); return; } else { strcpy(buf, "No Weapon"); } } void GetItemName(gentity_t * ent, char *buf) { if (ent->client->ps.stats[STAT_HOLDABLE_ITEM] & (1 << HI_KEVLAR)) { strcpy(buf, RQ3_KEVLAR_NAME); return; } else if (ent->client->ps.stats[STAT_HOLDABLE_ITEM] & (1 << HI_SILENCER)) { strcpy(buf, RQ3_SILENCER_NAME); return; } else if (ent->client->ps.stats[STAT_HOLDABLE_ITEM] & (1 << HI_SLIPPERS)) { strcpy(buf, RQ3_SLIPPERS_NAME); return; } else if (ent->client->ps.stats[STAT_HOLDABLE_ITEM] & (1 << HI_BANDOLIER)) { strcpy(buf, RQ3_BANDOLIER_NAME); return; } else if (ent->client->ps.stats[STAT_HOLDABLE_ITEM] & (1 << HI_LASER)) { strcpy(buf, RQ3_LASER_NAME); return; } else if (ent->client->ps.stats[STAT_HOLDABLE_ITEM] & (1 << HI_HELMET)) { strcpy(buf, RQ3_HELMET_NAME); return; } else { strcpy(buf, "No Item"); } } void GetAmmo(gentity_t * ent, char *buf) { int ammo; char temp[1024]; switch (ent->client->ps.weapon) { case WP_PISTOL: Com_sprintf(temp, sizeof(temp), "%d rounds (%d extra clips)", ent->client->ps.ammo[WP_PISTOL], ent->client->numClips[WP_PISTOL]); strcpy(buf, temp); return; case WP_SSG3000: Com_sprintf(temp, sizeof(temp), "%d rounds (%d extra rounds)", ent->client->ps.ammo[WP_SSG3000], ent->client->numClips[WP_SSG3000]); strcpy(buf, temp); return; case WP_MP5: Com_sprintf(temp, sizeof(temp), "%i rounds (%i extra clips)", ent->client->ps.ammo[WP_MP5], ent->client->numClips[WP_MP5]); strcpy(buf, temp); return; case WP_M4: Com_sprintf(temp, sizeof(temp), "%d rounds (%d extra clips)", ent->client->ps.ammo[WP_M4], ent->client->numClips[WP_M4]); strcpy(buf, temp); return; case WP_M3: Com_sprintf(temp, sizeof(temp), "%d rounds (%d extra shells)", ent->client->ps.ammo[WP_M3], ent->client->numClips[WP_M3]); strcpy(buf, temp); return; case WP_HANDCANNON: Com_sprintf(temp, sizeof(temp), "%d rounds (%d extra shells)", ent->client->ps.ammo[WP_HANDCANNON], ent->client->numClips[WP_HANDCANNON]); strcpy(buf, temp); return; case WP_AKIMBO: Com_sprintf(temp, sizeof(temp), "%d rounds (%d extra clips)", ent->client->ps.ammo[WP_AKIMBO], ent->client->numClips[WP_AKIMBO]); strcpy(buf, temp); return; case WP_KNIFE: ammo = ent->client->ps.ammo[WP_KNIFE]; Com_sprintf(temp, sizeof(temp), "%d kni%s", ammo, (ammo == 1) ? "fe" : "ves"); strcpy(buf, temp); return; case WP_GRENADE: ammo = ent->client->ps.ammo[WP_GRENADE]; Com_sprintf(temp, sizeof(temp), "%d grenade%s", ammo, (ammo == 1) ? "" : "s"); strcpy(buf, temp); return; } } void ResetKills(gentity_t * ent) { int i; for (i = 0; i < RQ3_MAXKILLS; i++) { ent->client->lastkilled_client[i] = NULL; } } int ReadKilledPlayers(gentity_t * ent) { int results, i; results = -1; if (ent->client->lastkilled_client[0] != NULL) { for (i = 0; i < RQ3_MAXKILLS; i++) { if (ent->client->lastkilled_client[i] != NULL) { results++; } } return results; } else return -1; } void AddKilledPlayer(gentity_t * self, gentity_t * ent) { int kills; kills = ReadKilledPlayers(self); if (kills == RQ3_MAXKILLS || kills == -1) { self->client->lastkilled_client[0] = ent; } else { self->client->lastkilled_client[kills + 1] = ent; } } void GetLastKilledTarget(gentity_t * self, char *buf) { int kills, i; kills = ReadKilledPlayers(self); if (kills >= 0) { strcpy(buf, self->client->lastkilled_client[0]->client->pers.netname); strcat(buf, "^5"); if (kills > 0) { for (i = 1; i < kills + 1; i++) { if (i == kills) { strcat(buf, "^5 and "); strcat(buf, self->client->lastkilled_client[i]->client->pers.netname); strcat(buf, "^5"); } else { strcat(buf, "^5, "); strcat(buf, self->client->lastkilled_client[i]->client->pers.netname); strcat(buf, "^5"); } } } ResetKills(self); } else { strcpy(buf, "nobody"); } } void GetLastDamagedPart(gentity_t * self, char *buf) { switch (self->client->lasthurt_location & ~(LOCATION_BACK | LOCATION_LEFT | LOCATION_RIGHT | LOCATION_FRONT)) { case LOCATION_HEAD: case LOCATION_FACE: strcpy(buf, "head"); self->client->lasthurt_location = 0; return; case LOCATION_SHOULDER: case LOCATION_CHEST: strcpy(buf, "chest"); self->client->lasthurt_location = 0; return; case LOCATION_STOMACH: case LOCATION_GROIN: strcpy(buf, "stomach"); self->client->lasthurt_location = 0; return; case LOCATION_LEG: case LOCATION_FOOT: strcpy(buf, "legs"); self->client->lasthurt_location = 0; return; case LOCATION_NONE: default: strcpy(buf, "nothing"); } } #define MAXNEAR 10 void GetNearbyTeammates(gentity_t * self, char *buf) { char nearby_teammates[MAXNEAR][MAX_NAME_LENGTH]; int nearby_teammates_num, l; gentity_t *ent = NULL; nearby_teammates_num = 0; while ((ent = findradius(ent, self->s.origin, 1500)) != NULL) { if (ent == self || !ent->client || !CanDamage(self, ent->r.currentOrigin) || (ent->client->sess.sessionTeam != self->client->sess.sessionTeam)) continue; strncpy(nearby_teammates[nearby_teammates_num], ent->client->pers.netname, MAX_NAME_LENGTH - 1); nearby_teammates[nearby_teammates_num][MAX_NAME_LENGTH - 1] = 0; nearby_teammates_num++; if (nearby_teammates_num >= MAXNEAR) break; } if (nearby_teammates_num == 0) { strcpy(buf, "nobody"); return; } for (l = 0; l < nearby_teammates_num; l++) { if (l == 0) { strcpy(buf, nearby_teammates[l]); } else { if (nearby_teammates_num == 2) { strcat(buf, "^5 and "); strcat(buf, nearby_teammates[l]); strcat(buf, "^5"); } else { if (l == (nearby_teammates_num - 1)) { strcat(buf, "^5, and "); strcat(buf, nearby_teammates[l]); strcat(buf, "^5"); } else { strcat(buf, "^5, "); strcat(buf, nearby_teammates[l]); strcat(buf, "^5, "); } } } } } void GetLastDamagedPlayers(gentity_t * ent, char *buf) { if (ent->client->last_damaged_players[0] == '\0') strcpy(buf, "nobody"); else strcpy(buf, ent->client->last_damaged_players); ent->client->last_damaged_players[0] = '\0'; } void ParseSayText(gentity_t * ent, char *text) { static char buf[1024], infobuf[1024]; char *p, *pbuf; p = text; pbuf = buf; *pbuf = 0; while (*p != 0) { if ((pbuf - buf) > 225) { break; } if (*p == '$') { switch (*(p + 1)) { case 'H': Com_sprintf(infobuf, sizeof(infobuf), "%d", ent->health); strcpy(pbuf, infobuf); pbuf = SeekBufEnd(pbuf); p += 2; continue; case 'A': GetAmmo(ent, infobuf); strcpy(pbuf, infobuf); pbuf = SeekBufEnd(pbuf); p += 2; continue; case 'W': GetWeaponName(ent, infobuf); strcpy(pbuf, infobuf); pbuf = SeekBufEnd(pbuf); p += 2; continue; case 'I': GetItemName(ent, infobuf); strcpy(pbuf, infobuf); pbuf = SeekBufEnd(pbuf); p += 2; continue; case 'K': if (g_RQ3_showOwnKills.integer) GetLastKilledTarget(ent, infobuf); else GetLastDamagedPlayers(ent, infobuf); strcpy(pbuf, infobuf); pbuf = SeekBufEnd(pbuf); p += 2; continue; case 'D': GetLastDamagedPart(ent, infobuf); strcpy(pbuf, infobuf); pbuf = SeekBufEnd(pbuf); p += 2; continue; case 'T': GetNearbyTeammates(ent, infobuf); strcpy(pbuf, infobuf); pbuf = SeekBufEnd(pbuf); p += 2; continue; /* case 'S': GetSightedLocation (ent, infobuf); strcpy (pbuf, infobuf); pbuf = SeekBufEnd (pbuf); p += 2; continue; */ case 'P': GetLastDamagedPlayers(ent, infobuf); strcpy(pbuf, infobuf); pbuf = SeekBufEnd(pbuf); p += 2; continue; } } *pbuf++ = *p++; } *pbuf = 0; strncpy(text, buf, 225); text[225] = 0; } void RQ3_SpectatorMode(gentity_t * ent) { if (ent->r.svFlags & SVF_BOT) return; if (ent->client->sess.spectatorState == SPECTATOR_ZCAM) trap_SendServerCommand(ent->client->ps.clientNum, va("print \"\n" S_COLOR_MAGENTA "Spectator Mode-" S_COLOR_YELLOW "ZCAM-%s\n", (ent->client->camera->mode == CAMERA_MODE_SWING) ? "SWING" : "FLIC")); else trap_SendServerCommand(ent->client->ps.clientNum, va("print \"\n" S_COLOR_MAGENTA "Spectator Mode-" S_COLOR_YELLOW "%s\n", (ent->client->sess.spectatorState == SPECTATOR_FREE) ? "FREE" : "FOLLOW")); } static qboolean RQ3_IsSpecialWeapon(int weapon) { switch (weapon) { case WP_M3: case WP_MP5: case WP_HANDCANNON: case WP_SSG3000: case WP_M4: return qtrue; default: return qfalse; } } static weapon_t RQ3_CycleSpecialWeapon(gentity_t* ent) { int i; for (i=ent->client->ps.weapon-1; i>WP_NONE; --i) { int mask = (1 << i); if ((ent->client->ps.stats[STAT_WEAPONS] & mask) == mask && RQ3_IsSpecialWeapon(i)) return (weapon_t) i; } for (i=WP_NUM_WEAPONS-1; i>ent->client->ps.weapon; --i) { int mask = (1 << i); if ((ent->client->ps.stats[STAT_WEAPONS] & mask) == mask && RQ3_IsSpecialWeapon(i)) return (weapon_t) i; } if (RQ3_IsSpecialWeapon(ent->client->ps.weapon)) return (weapon_t) ent->client->ps.weapon; return WP_NONE; } void RQ3_Cmd_Use_f(gentity_t * ent) { char *cmd, buf[128]; int weapon; if (!ent->client) { return; // not fully in game yet } cmd = ConcatArgs(1); weapon = WP_NONE; if (Q_stricmp(cmd, RQ3_PISTOL_NAME) == 0 || Q_stricmp(cmd, "pistol") == 0) { if ((ent->client->ps.stats[STAT_WEAPONS] & (1 << WP_PISTOL)) == (1 << WP_PISTOL)) { weapon = WP_PISTOL; } else { trap_SendServerCommand(ent - g_entities, va("print \"Out of item: %s\n\"", RQ3_AKIMBO_NAME)); return; } } else if (Q_stricmp(cmd, "throwing combat knife") == 0) { if ((ent->client->ps.stats[STAT_WEAPONS] & (1 << WP_KNIFE)) == (1 << WP_KNIFE)) { weapon = WP_KNIFE; //Makro - if we already have the knife, but it's in the wrong mode, we don't just set the mode //instead, we call Cmd_Weapon so that the right animation gets played if (ent->client->ps.weapon == WP_KNIFE) { if ( (ent->client->ps.persistant[PERS_WEAPONMODES] & RQ3_KNIFEMODE) == RQ3_KNIFEMODE ) { Cmd_Weapon(ent); } } else { ent->client->ps.persistant[PERS_WEAPONMODES] &= ~RQ3_KNIFEMODE; } } else { trap_SendServerCommand(ent - g_entities, va("print \"Out of item: %s\n\"", RQ3_KNIFE_NAME)); return; } } else if (Q_stricmp(cmd, "slashing combat knife") == 0) { if ((ent->client->ps.stats[STAT_WEAPONS] & (1 << WP_KNIFE)) == (1 << WP_KNIFE)) { weapon = WP_KNIFE; //Makro - if we already have the knife, but it's in the wrong mode, we don't just set the mode //instead, we call Cmd_Weapon so that the right animation gets played if (ent->client->ps.weapon == WP_KNIFE) { if ( (ent->client->ps.persistant[PERS_WEAPONMODES] & RQ3_KNIFEMODE) == 0 ) { Cmd_Weapon(ent); } } else { ent->client->ps.persistant[PERS_WEAPONMODES] |= RQ3_KNIFEMODE; } } else { trap_SendServerCommand(ent - g_entities, va("print \"Out of item: %s\n\"", RQ3_KNIFE_NAME)); return; } } else if (Q_stricmp(cmd, RQ3_KNIFE_NAME) == 0 || Q_stricmp(cmd, "knife") == 0 || Q_stricmp(cmd, "knives") == 0) { if ((ent->client->ps.stats[STAT_WEAPONS] & (1 << WP_KNIFE)) == (1 << WP_KNIFE)) { weapon = WP_KNIFE; } else { trap_SendServerCommand(ent - g_entities, va("print \"Out of item: %s\n\"", RQ3_KNIFE_NAME)); return; } } if (weapon != WP_NONE) { if (weapon == ent->client->ps.weapon) return; if (ent->client->ps.weaponTime == 0) trap_SendServerCommand(ent - g_entities, va("rq3_cmd %i %i", SETWEAPON, weapon)); Com_sprintf(buf, sizeof(buf), "stuff weapon %d\n", weapon); trap_SendServerCommand(ent - g_entities, buf); return; } if (g_gametype.integer == GT_CTF && (ent->client->ps.powerups[PW_REDFLAG] || ent->client->ps.powerups[PW_BLUEFLAG])) return; if (Q_stricmp(cmd, RQ3_MP5_NAME) == 0 || Q_stricmp(cmd, "mp5") == 0) { if ((ent->client->ps.stats[STAT_WEAPONS] & (1 << WP_MP5)) == (1 << WP_MP5)) { weapon = WP_MP5; } else { trap_SendServerCommand(ent - g_entities, va("print \"Out of item: %s\n\"", RQ3_MP5_NAME)); return; } } else if (Q_stricmp(cmd, RQ3_M3_NAME) == 0 || Q_stricmp(cmd, "m3") == 0) { if ((ent->client->ps.stats[STAT_WEAPONS] & (1 << WP_M3)) == (1 << WP_M3)) { weapon = WP_M3; } else { trap_SendServerCommand(ent - g_entities, va("print \"Out of item: %s\n\"", RQ3_M3_NAME)); return; } } else if (Q_stricmp(cmd, RQ3_M4_NAME) == 0 || Q_stricmp(cmd, "m4") == 0) { if ((ent->client->ps.stats[STAT_WEAPONS] & (1 << WP_M4)) == (1 << WP_M4)) { weapon = WP_M4; } else { trap_SendServerCommand(ent - g_entities, va("print \"Out of item: %s\n\"", RQ3_M4_NAME)); return; } } else if (Q_stricmp(cmd, RQ3_HANDCANNON_NAME) == 0 || Q_stricmp(cmd, "hc") == 0) { if ((ent->client->ps.stats[STAT_WEAPONS] & (1 << WP_HANDCANNON)) == (1 << WP_HANDCANNON)) { weapon = WP_HANDCANNON; } else { trap_SendServerCommand(ent - g_entities, va("print \"Out of item: %s\n\"", RQ3_HANDCANNON_NAME)); return; } } else if (Q_stricmp(cmd, RQ3_SSG3000_NAME) == 0 || Q_stricmp(cmd, "sniper") == 0) { if ((ent->client->ps.stats[STAT_WEAPONS] & (1 << WP_SSG3000)) == (1 << WP_SSG3000)) { weapon = WP_SSG3000; } else { trap_SendServerCommand(ent - g_entities, va("print \"Out of item: %s\n\"", RQ3_SSG3000_NAME)); return; } } else if (Q_stricmp(cmd, RQ3_AKIMBO_NAME) == 0 || Q_stricmp(cmd, "akimbo") == 0) { if ((ent->client->ps.stats[STAT_WEAPONS] & (1 << WP_AKIMBO)) == (1 << WP_AKIMBO)) { weapon = WP_AKIMBO; } else { trap_SendServerCommand(ent - g_entities, va("print \"Out of item: %s\n\"", RQ3_AKIMBO_NAME)); return; } } else if (Q_stricmp(cmd, RQ3_GRENADE_NAME) == 0 || Q_stricmp(cmd, "grenade") == 0) { if ((ent->client->ps.stats[STAT_WEAPONS] & (1 << WP_GRENADE)) == (1 << WP_GRENADE)) { weapon = WP_GRENADE; } else { trap_SendServerCommand(ent - g_entities, va("print \"Out of item: %s\n\"", RQ3_GRENADE_NAME)); return; } } else if (Q_stricmp(cmd, "special") == 0) { weapon = RQ3_CycleSpecialWeapon(ent); if (weapon == WP_NONE) { trap_SendServerCommand(ent - g_entities, va("print \"You dont have a special weapon!\n\"")); return; } } if (weapon == WP_NONE) { trap_SendServerCommand(ent - g_entities, va("print \"Unknown item: %s\n\"", cmd)); return; } if (weapon == ent->client->ps.weapon) return; if (ent->client->ps.weaponTime == 0) trap_SendServerCommand(ent - g_entities, va("rq3_cmd %i %i", SETWEAPON, weapon)); Com_sprintf(buf, sizeof(buf), "stuff weapon %d\n", weapon); trap_SendServerCommand(ent - g_entities, buf); } void Add_TeamWound(gentity_t * attacker, gentity_t * victim, int mod) { char userinfo[MAX_INFO_STRING]; char *value; if (g_gametype.integer < GT_TEAM || !attacker->client || !victim->client) return; attacker->client->team_wounds++; if (attacker->client->ff_warning == 0) { attacker->client->ff_warning++; trap_SendServerCommand(victim - g_entities, va("print \"You were hit by %s^7, your ^1TEAMMATE!\n\"", attacker->client->pers.netname)); trap_SendServerCommand(attacker - g_entities, va("print \"You hit your ^1TEAMMATE^7 %s^7!\n\"", victim->client->pers.netname)); } attacker->client->team_wounds = (attacker->client->team_wounds_before + 1); if (g_RQ3_maxteamkills.integer < 1) return; if (attacker->client->team_wounds < (g_RQ3_maxteamkills.integer * 3)) { return; } else if (attacker->client->team_wounds < (g_RQ3_maxteamkills.integer * 4)) { trap_SendServerCommand(-1, va("print \"%s^7 is in danger of being banned for wounding teammates\n\"", attacker->client->pers.netname)); trap_SendServerCommand(attacker - g_entities, va ("print \"^1WARNING:^7 You'll be temporarily banned if you continue wounding teammates!\n\"")); return; } else { trap_SendServerCommand(-1, va("print \"^1Banning^7 %s^7 for team wounding\n\"", attacker->client->pers.netname)); trap_SendServerCommand(attacker - g_entities, va ("print \"^1You've wounded teammates too many times, and are banned for %d %s.\n\"", g_RQ3_twbanrounds.integer, ((g_RQ3_twbanrounds.integer > 1) ? "games" : "game"))); trap_GetUserinfo(attacker - g_entities, userinfo, sizeof(userinfo)); value = Info_ValueForKey(userinfo, "ip"); AddIP(value); trap_DropClient(attacker - g_entities, "Dropped due to team wounding"); } } void Add_TeamKill(gentity_t * attacker) { char userinfo[MAX_INFO_STRING]; char *value; // NiceAss: No TKing in matchmode if (g_gametype.integer < GT_TEAM || !attacker->client || g_RQ3_matchmode.integer) return; attacker->client->team_kills++; if (attacker->client->team_wounds > attacker->client->team_wounds_before) attacker->client->team_wounds = attacker->client->team_wounds_before; if ((g_RQ3_maxteamkills.integer < 1) || (attacker->client->team_kills < ((g_RQ3_maxteamkills.integer % 2) + g_RQ3_maxteamkills.integer / 2))) { trap_SendServerCommand(attacker - g_entities, va("print \"You killed your ^1TEAMMATE!\n\"")); return; } else if (attacker->client->team_kills < g_RQ3_maxteamkills.integer) { trap_SendServerCommand(-1, va("print \"%s^7 is in danger of being banned for killing teammates\n\"", attacker->client->pers.netname)); trap_SendServerCommand(attacker - g_entities, va ("print \"^1WARNING:^7 You'll be temporarily banned if you continue killing teammates!\n\"")); return; } else { trap_SendServerCommand(-1, va("print \"^1Banning^7 %s^7 for team killing\n\"", attacker->client->pers.netname)); trap_SendServerCommand(attacker - g_entities, va("print \"^1You've killed too many teammates, and are banned for %d %s.\n", g_RQ3_tkbanrounds.integer, ((g_RQ3_tkbanrounds.integer > 1) ? "games" : "game"))); trap_GetUserinfo(attacker - g_entities, userinfo, sizeof(userinfo)); value = Info_ValueForKey(userinfo, "ip"); AddIP(value); trap_DropClient(attacker - g_entities, "Dropped due to team killing"); } } void setFFState(gentity_t * ent) { if (ent && ent->client) { ent->client->team_wounds_before = ent->client->team_wounds; ent->client->ff_warning = 0; } } void RQ3_Cmd_TKOk(gentity_t * ent) { if (!ent->enemy || !ent->enemy->inuse || !ent->enemy->client || (ent == ent->enemy)) { trap_SendServerCommand(ent - g_entities, va("print \"Nothing to forgive\n\"")); } else if (ent->client->sess.savedTeam == ent->enemy->client->sess.sessionTeam) { if (ent->enemy->client->team_kills) { trap_SendServerCommand(ent - g_entities, va("print \"You forgave %s\n\"", ent->enemy->client->pers.netname)); trap_SendServerCommand(ent->enemy - g_entities, va("print \"%s^7 forgave you\n", ent->client->pers.netname)); ent->enemy->client->team_kills--; if (ent->enemy->client->team_wounds) ent->enemy->client->team_wounds /= 2; ent->enemy = NULL; } } else { trap_SendServerCommand(ent - g_entities, va("print \"That's very noble of you...\n\"")); //Makro - commented out, at people's request //http://team.reactionquake3.com/ubb/ultimatebb.cgi?ubb=get_topic&f=8&t=000928#000034 //trap_SendServerCommand(-1, va("print \"%s^7 turned the other cheek\n\"", ent->client->pers.netname)); } ent->enemy = NULL; } void RQ3_Cmd_Stuff(void) { char *cmd, user[128]; int len, client; if (g_RQ3_matchmode.integer) return; len = trap_Argc(); if (len < 3) { G_Printf("Usage: stuff \n"); return; } trap_Argv(1, user, sizeof(user)); if (user[0] < '0' || user[0] > '9') { G_Printf("Usage: stuff \n"); return; } client = atoi(user); cmd = ConcatArgs(2); trap_SendServerCommand(client, va("stuff %s\n", cmd)); } int RQ3_FindFreeIgnoreListEntry(gentity_t * source) { return (IsInIgnoreList(source, NULL)); } void RQ3_ClearIgnoreList(gentity_t * ent) { int i; if (!ent->client) return; for (i = 0; i < MAXIGNORE; i++) ent->client->sess.ignorelist[i] = NULL; } int IsInIgnoreList(gentity_t * source, gentity_t * subject) { int i; if (!source || !source->client) return 0; for (i = 1; i < MAXIGNORE; i++) if (source->client->sess.ignorelist[i] == subject) return i; return 0; } void RQ3_AddOrDelIgnoreSubject(gentity_t * source, gentity_t * subject, qboolean silent) { int i; if (!source->client) return; if (!subject->client || !subject->inuse) { trap_SendServerCommand(source - g_entities, va("print \"^1Only valid clients may be added to ignore list!\n\"")); return; } i = IsInIgnoreList(source, subject); if (i) { //subject is in ignore list, so delete it source->client->sess.ignorelist[i] = NULL; if (!silent) { trap_SendServerCommand(source - g_entities, va("print \"%s^7 was removed from ignore list.\n\"", subject->client->pers.netname)); trap_SendServerCommand(subject - g_entities, va("print \"%s^7 is no longer ignoring you.\n\"", source->client->pers.netname)); } source->client->sess.ignore_time = level.framenum; } else { //subject has to be added i = RQ3_FindFreeIgnoreListEntry(source); if (!i) { if (!silent) trap_SendServerCommand(source - g_entities, va("print \"Sorry, ignore list is full!\n\"")); } else { //we've found a place source->client->sess.ignorelist[i] = subject; if (!silent) { trap_SendServerCommand(source - g_entities, va("print \"%s^7 was added to ignore list.\n\"", subject->client->pers.netname)); trap_SendServerCommand(subject - g_entities, va("print \"%s^7 ignores you.\n\"", source->client->pers.netname)); } } } } void RQ3__ClrIgnoresOn(gentity_t * target) { gentity_t *other; int i; for (i = 1; i <= level.maxclients; i++) { other = &g_entities[i]; if (other->inuse && other->client) { if (IsInIgnoreList(other, target)) RQ3_AddOrDelIgnoreSubject(other, target, qtrue); } } } void Cmd_Ignore_f(gentity_t * self) { gentity_t *target; char s[128]; int i; i = trap_Argc(); if (i < 2) { trap_SendServerCommand(self - g_entities, va("print \"Usage: ignore .\n\"")); return; } trap_Argv(1, s, sizeof(s)); target = FindClientByPersName(s); if (target == NULL) { trap_SendServerCommand(self - g_entities, va("print \"%s: No such player..\n\"", s)); return; } i = IsInIgnoreList(self, target); if (i) { trap_SendServerCommand(self - g_entities, va("print \"%s is already on your ignorelist.\n\"", s)); return; } if (target && target != self) { if (level.framenum > (self->client->sess.ignore_time + 50)) RQ3_AddOrDelIgnoreSubject(self, target, qfalse); else { trap_SendServerCommand(self - g_entities, va("print \"^1Wait 5 seconds before ignoring again.\n\"")); return; } } else trap_SendServerCommand(self - g_entities, va("print \"Use ignorelist to see who can be ignored.\n\"")); } void Cmd_Unignore_f(gentity_t * self) { gentity_t *target; char s[128]; int i; i = trap_Argc(); if (i < 2) { trap_SendServerCommand(self - g_entities, va("print \"Usage: unignore .\n\"")); return; } trap_Argv(1, s, sizeof(s)); target = FindClientByPersName(s); if (target == NULL) { trap_SendServerCommand(self - g_entities, va("print \"%s: No such player..\n\"", s)); return; } i = IsInIgnoreList(self, target); if (!i) { trap_SendServerCommand(self - g_entities, va("print \"%s is not on your ignore list..\n\"", s)); return; } if (target && target != self) { if (level.framenum > (self->client->sess.ignore_time + 50)) RQ3_AddOrDelIgnoreSubject(self, target, qfalse); else { trap_SendServerCommand(self - g_entities, va("print \"^1Wait 5 seconds before ignoring again.\n\"")); return; } } else trap_SendServerCommand(self - g_entities, va("print \"Use ignorelist to see who can be ignored.\n\"")); } void Cmd_Ignorenum_f(gentity_t * self) { int i; gentity_t *target; char arg[128]; i = trap_Argc(); if (i < 2) { trap_SendServerCommand(self - g_entities, va("print \"Usage: ignorenum .\n\"")); return; } trap_Argv(1, arg, sizeof(arg)); i = atoi(arg); if (i <= level.maxclients) { target = &g_entities[i]; if (target && target->client && target != self && target->inuse) RQ3_AddOrDelIgnoreSubject(self, target, qfalse); else trap_SendServerCommand(self - g_entities, va("print \"Use ignorelist to see who can be ignored.\n\"")); } else trap_SendServerCommand(self - g_entities, va("print \"^1Used ignorenum with illegal number.\n\"")); } void Cmd_Ignoreclear_f(gentity_t * self) { RQ3_ClearIgnoreList(self); trap_SendServerCommand(self - g_entities, va("print \"Your ignorelist is now clear.\n\"")); } void Cmd_Playerlist_f(gentity_t * ent) { gentity_t *other; int i; for (i = 0; i <= level.maxclients; i++) { other = &g_entities[i]; if (!other->inuse || !other->client) continue; trap_SendServerCommand(ent - g_entities, va("print \"%i - %s^7\n\"", i, other->client->pers.netname)); } } // Freud: RQ3_compare_spawn_distances // // Sorting mechanism for spawn point distances feeded to qsort int QDECL RQ3_compare_spawn_distances(const void *sd1, const void *sd2) { if (((spawn_distances_t *) sd1)->distance < ((spawn_distances_t *) sd2)->distance) return -1; else if (((spawn_distances_t *) sd1)->distance > ((spawn_distances_t *) sd2)->distance) return 1; else return 0; } // Freud: SpawnPointDistance // // Returns the distance between two spawn points (or any entities, actually...) float RQ3_SpawnPointDistance(gentity_t * spot1, gentity_t * spot2) { vec3_t v; VectorSubtract(spot1->s.origin, spot2->s.origin, v); return VectorLength(v); } // RQ3_GetSpawnPoints () // // Called whenever no spawn points are available. void RQ3_GetSpawnPoints() { gentity_t *spot; int x, spawns; spot = NULL; // The team that is called with SetupRandomTeamplaySpawnPoint level.randteam = rand() % MAX_TEAMS; // Reset the spawns for each team for (x = 0; x < MAX_TEAMS; x++) { level.num_potential_spawns[x] = 0; level.num_used_farteamplay_spawns[x] = 0; } spawns = 0; // Read spawn points from the map while ((spot = G_Find(spot, FOFS(classname), "info_player_deathmatch")) != NULL) { spawns++; for (x = 0; x < MAX_TEAMS; x++) { level.potential_spawns[x][level.num_potential_spawns[x]] = spot; level.num_potential_spawns[x]++; } } G_Printf("%i spawns read from map\n", spawns); } // Freud: RQ3_SelectRandomTeamplaySpawnPoint // // Selects a random spawns point from potential team spawns. qboolean RQ3_SelectRandomTeamplaySpawnPoint(int team) { int spawn_point, y, ok, i, z; float distance; gentity_t *spot; spot = NULL; i = 0; ok = qfalse; // Done with potential spawns, re-reading and re-assigning if (level.num_potential_spawns[team] < 1) { RQ3_GetSpawnPoints(); RQ3_SetupTeamSpawnPoints(); return qfalse; } // Randomizing from potential spawn points spawn_point = rand() % level.num_potential_spawns[team]; // decrementing potential spawns counter level.num_potential_spawns[team]--; while ((ok == qfalse) && (i < MAX_TEAMS)) { ok = qtrue; for (y = 0; y < MAX_TEAMS; y++) { if (level.teams_assigned[y] == qtrue) { distance = RQ3_SpawnPointDistance(level.potential_spawns[team][spawn_point], level.teamplay_spawns[y]); if (distance == 0) { ok = qfalse; } } } if (ok == qfalse) { spawn_point++; if (spawn_point == level.num_potential_spawns[team]) { spawn_point = 0; } i++; } } // Assigning the spawn point to the team level.teamplay_spawns[team] = level.potential_spawns[team][spawn_point]; level.teams_assigned[team] = qtrue; // Removing used spawn point from potential_spawns for (z = spawn_point; z < level.num_potential_spawns[team]; z++) { level.potential_spawns[team][z] = level.potential_spawns[team][z + 1]; } if (i == MAX_TEAMS) { G_Printf("Arrrggh: More teams than potential spawnpoints!\n"); if ((spot = G_Find(spot, FOFS(classname), "info_player_start")) != NULL) { G_Printf("Well, guess I'm using info_player_start\n"); level.teamplay_spawns[team] = spot; } else return qfalse; } return qtrue; } // Freud: RQ3_SelectFarTeamplaySpawnPoint // // Selects farthest teamplay spawn point(s) from available spawns. qboolean RQ3_SelectFarTeamplaySpawnPoint(int team) { int u, x, y, z; int spawn_to_use, preferred_spawn_points, num_already_used; int total_good_spawn_points, num_usable; float closest_spawn_distance, distance; gentity_t *usable_spawns[MAX_SPAWN_POINTS]; qboolean used; spawn_distances_t spawn_distances[MAX_SPAWN_POINTS]; num_already_used = 0; // Reset the spawn_distances structure for (x = 0; x < MAX_SPAWN_POINTS; x++) { memset(&spawn_distances[x], 0, sizeof(spawn_distances[x])); } // for (x = 0; x < level.num_potential_spawns[team]; x++) { closest_spawn_distance = 2000000000; for (y = 0; y < MAX_TEAMS; y++) { if (level.teams_assigned[y] == qtrue) { distance = RQ3_SpawnPointDistance(level.potential_spawns[team][x], level.teamplay_spawns[y]); if (distance < closest_spawn_distance) { closest_spawn_distance = distance; } } } if (closest_spawn_distance == 0) num_already_used++; spawn_distances[x].s = level.potential_spawns[team][x]; spawn_distances[x].distance = closest_spawn_distance; } // Sort the farthest spawn points to the end of the array qsort(spawn_distances, MAX_SPAWN_POINTS, sizeof(spawn_distances_t), RQ3_compare_spawn_distances); total_good_spawn_points = level.num_potential_spawns[team] - num_already_used; // Support Spawn farthest and the old AQ system for spawn possibilities if (g_dmflags.integer & DF_SPAWN_FARTHEST || total_good_spawn_points <= 4) preferred_spawn_points = 1; else if (total_good_spawn_points <= 10) preferred_spawn_points = 2; else preferred_spawn_points = 3; num_usable = 0; // Now lets go through the spawn points and see if they have been used up. for (z = 0; z < preferred_spawn_points; z++) { used = qfalse; for (u = 0; u < level.num_used_farteamplay_spawns[team]; u++) { if (level.used_farteamplay_spawns[team][u] == spawn_distances[MAX_SPAWN_POINTS - z - 1].s) { used = qtrue; } } if (used == qfalse) { usable_spawns[num_usable] = spawn_distances[MAX_SPAWN_POINTS - z - 1].s; num_usable++; } } // Can't use any of the far spawn points, let's go through the whole thing again. if (num_usable < 1) { RQ3_SetupTeamSpawnPoints(); return qfalse; } // Randomize through the usable spawns. spawn_to_use = rand() % num_usable; // Marking the spawn as used. level.used_farteamplay_spawns[team][level.num_used_farteamplay_spawns[team]] = usable_spawns[spawn_to_use]; level.num_used_farteamplay_spawns[team]++; // Setting the team to assigned and assigning the spawn point. level.teams_assigned[team] = qtrue; level.teamplay_spawns[team] = usable_spawns[spawn_to_use]; if (!level.teamplay_spawns[team]) { G_Printf("Argh, Invalid Far Spawn\n"); return qfalse; } return qtrue; } // Freud: RQ3_SetupTeamSpawnPoints // // Call this for assigning the spawn points to the teams void RQ3_SetupTeamSpawnPoints() { int i; for (i = 0; i < MAX_TEAMS; i++) { level.teamplay_spawns[i] = NULL; level.teams_assigned[i] = qfalse; } if (RQ3_SelectRandomTeamplaySpawnPoint(level.randteam) == qfalse) return; // If we ever decide to have more teams then 2.. :) for (i = 0; i < MAX_TEAMS; i++) if (i != level.randteam && RQ3_SelectFarTeamplaySpawnPoint(i) == qfalse) return; }