Advertisement
Guest User

JailBreamTeamBalance

a guest
Jun 26th, 2017
81
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 39.49 KB | None | 0 0
  1. /* CSS Jailbreak Team Balance
  2. by: databomb
  3. Original Compile Date: 12.26.2010
  4.  
  5. Description/Features:
  6.  
  7. This plugin is designed to automate the team switching that inundate many admins in the CSS Jailbreak community. A configurable ratio will specify how many Ts for each CT. The ratio is matched everytime the round ends. While the ratio is usually higher or lower than what is requested, the plugin will find the closest match possible at the end of every round. One advantage of this plugin is the separate algorithms for T and CT switching. When a player is switched off CT team a LIFO routine (last person to join is moved off first) is specified while (optionally) only those with a mic are radonmly switched to CT absent any guard requests which are pending.
  8.  
  9. Prisoners may type '!guard' in chat or use the sm_guard command to join a queue of those requesting a transfer to CT. The queue is processed in normal FIFO fashion (the first to join the queue is the first to get a transfer.) Admins may say '!clearguards' or use the sm_clearguards command to manually clear the guard queue completely. Admins may also target a single unwanted individual with '!removeguard <player|#userid>' or just sm_removeguard which will bring up the menu. While anyone is waiting in the queue for a CT position, team switching to CT is blocked and instead handled by the queue.
  10.  
  11. When you join the server and haven't yet selected a team, you're required by default to pick either Terrorist team or Spectate. By default auto-join is disabled and CT is only available after you played on the Terorrist team. Often times the teams will become stacked at the beginning of a map resulting in the often mandated 'first-round free-day.' This behavior is compensated for by allowing the first person access to the CT team without any of the previous restrictions but enforcing it thereafter. Also, teams are locked during round transitions to make it impossible for players to switch back to CT without suiciding after they spawn in the following round. The exception to this is at the beginning of the game when you hear "Round Draw" it will still allow you to join teams.
  12.  
  13. Optional Plugins:
  14.  
  15. If you have asherkin's VoiceHook installed then this plugin will determine which Ts used their mics each round and exclude those that did not use their mic from being switched to CT. This has built in integration with a few other plugins such as Azelphur's and my own team bans plugin in addition to my voicecomm plugin for dead all talk.
  16.  
  17. Usage/Cvar List:
  18. sm_jbtb_ratio, [0.1-10.0]: Sets the ratio of Ts to CTs (e.g. 3.0 will match 12Ts & 4CTs with 16 players.)
  19. sm_jbtb, [0,1]: Turns the plugin functionality on or off.
  20. sm_jbtb_soundfile, [path]: The path to the soundfile to play when denying a team-change request. Set to "" to disable.
  21. sm_jbtb_showqueue, [0,1]: Sets whether the position in the queue is revealed to players when they use the guard command.
  22. sm_jbtb_showclasses, [0,1]: Sets whether to show the classes screen after the team selection screen. Useful for servers that use custom models or model menus.
  23. sm_jbtb_blockct, [0,1]: This requires everyone to play as T before joining CT. This may frusturate potential free-killers and/or your regulars.
  24.  
  25. Installation:
  26. .smx file goes in addons/sourcemod/plugins/
  27. .phrases.txt file goes in addons/sourcemod/translations/
  28.  
  29. Future Considerations:
  30. - Overhaul the team size matching algorithm and T->CT selection for efficiency
  31. - Additional integrations with VoiceHook such as informational notifications
  32. - Optimize code further
  33.  
  34. Special Thanks:
  35. psychonic who helped me with many general SM issues
  36. dalto who wrote the deny sound code which I've used in this plugin
  37. asherkin who has developed the VoiceHook MetaMod:Source plugin
  38. Donkey Kong who has helped to re-add CS:GO support to the plugin
  39. */
  40.  
  41. #include <sourcemod>
  42. #include <sdktools>
  43. #include <clientprefs>
  44. #include <adminmenu>
  45. #include <cstrike>
  46.  
  47. #define VERSION "3.0.7.2"
  48. #define REASON_ROUND_DRAW 9
  49. #define REASON_GAME_COMMENCING 15
  50. #define REASON_INVALID 20
  51. #define CHAT_BANNER " \x03[Csapat-Elosztó] \x01%t"
  52. #define DEBUG 0
  53.  
  54. // here we'll listen for asherkin's voicehook mm:s plugin
  55. forward OnClientSpeaking(client);
  56. new Bool:g_bTalkedThisRound[MAXPLAYERS+1];
  57.  
  58. new Handle:gH_LimitTeams = INVALID_HANDLE;
  59. new Handle:gH_AzelphurTeamBanStatus = INVALID_HANDLE;
  60. new Handle:gH_AzlBanCookie = INVALID_HANDLE;
  61. new Handle:gH_BanCookie = INVALID_HANDLE;
  62. new Handle:gH_CTBansStatus = INVALID_HANDLE;
  63. new gShadow_CTBan;
  64. new Handle:gH_Cvar_Enabled = INVALID_HANDLE;
  65. new Bool:gShadow_Cvar_Enabled;
  66. new Handle:gH_Cvar_RatioGoal = INVALID_HANDLE;
  67. new Float:gShadow_Cvar_RatioGoal;
  68. new Handle:gH_Cvar_SoundName = INVALID_HANDLE;
  69. new String:gShadow_Cvar_SoundName[PLATFORM_MAX_PATH];
  70. new Handle:gH_Cvar_ShowQueuePosition = INVALID_HANDLE;
  71. new Bool:gShadow_Cvar_ShowQueuePosition;
  72. new Handle:gH_Cvar_ShowClassPanel = INVALID_HANDLE;
  73. new Bool:gShadow_Cvar_ShowClassPanel;
  74. new Handle:gH_Cvar_BlockCTatJoin = INVALID_HANDLE;
  75. new Bool:gShadow_Cvar_BlockCTatJoin;
  76. new Handle:gH_TopMenu = INVALID_HANDLE;
  77. new Handle:gH_CTStack = INVALID_HANDLE;
  78. new Handle:gH_TempStack = INVALID_HANDLE;
  79. new Handle:gA_GuardRequest = INVALID_HANDLE;
  80. new Handle:gA_Terrorists = INVALID_HANDLE;
  81.  
  82. new gLastRoundEndReason = REASON_INVALID;
  83. new g_iActivePlayers = 0;
  84. new g_iNumCTsDuringRound;
  85. new Bool:gTeamsLocked;
  86. new Bool:gOneJoined;
  87. new Bool:gOneRoundPlayed;
  88.  
  89. public Plugin:myinfo =
  90. {
  91. name = "Jailbreak Team Balance",
  92. author = "databomb",
  93. description = "Matches teams to a requested ratio every round",
  94. version = VERSION,
  95. url = "vintagejailbreak.org"
  96. }
  97.  
  98. public OnPluginStart()
  99. {
  100. // Load translations files needed
  101. LoadTranslations("jailbreak-tb.phrases");
  102. LoadTranslations("common.phrases");
  103.  
  104. // Register console variables
  105. CreateConVar("sm_jbtb_version",VERSION,"Jailbreak Team Balance Version",FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_DONTRECORD|FCVAR_NOTIFY);
  106. gH_Cvar_Enabled = CreateConVar("sm_jbtb","1","Enables the jailbreak team balance system", FCVAR_PLUGIN, true, 0.0, true, 1.0);
  107. gShadow_Cvar_Enabled = Bool:true;
  108. gH_Cvar_RatioGoal = CreateConVar("sm_jbtb_ratio","3.0","Sets the requested ratio of how many Ts per each CT", FCVAR_PLUGIN, true, 0.1, true, 10.0);
  109. gShadow_Cvar_RatioGoal = 2.75;
  110. gH_Cvar_SoundName = CreateConVar("sm_jbtb_soundfile", "buttons/button11.wav", "The name of the sound to play when an action is denied",FCVAR_PLUGIN);
  111. strcopy(gShadow_Cvar_SoundName, PLATFORM_MAX_PATH, "buttons/button11.wav");
  112. gH_Cvar_ShowQueuePosition = CreateConVar("sm_jbtb_showqueue", "0", "Specifies whether clients see their queue position when using the guard command.", FCVAR_PLUGIN, true, 0.0, true, 1.0);
  113. gShadow_Cvar_ShowQueuePosition = Bool:true;
  114. gH_Cvar_ShowClassPanel = CreateConVar("sm_jbtb_showclasses", "0", "Sets whether the class selection screen will be shown to players. 1-shows class menu, 0-stops menu from appearing.", FCVAR_PLUGIN, true, 0.0, true, 1.0);
  115. gShadow_Cvar_ShowClassPanel = Bool:true;
  116. gH_Cvar_BlockCTatJoin = CreateConVar("sm_jbtb_blockct", "0", "Sets whether joining CT is blocked when a player first joins the server. 1-require player be a T first, 0-feature disabled.", FCVAR_PLUGIN, true, 0.0, true, 1.0);
  117. gShadow_Cvar_BlockCTatJoin = Bool:true;
  118.  
  119. // create handles to the two global stacks & arrays we'll use
  120. gH_CTStack = CreateStack(1);
  121. gH_TempStack = CreateStack(1);
  122. gA_Terrorists = CreateArray(1);
  123. gA_GuardRequest = CreateArray(1);
  124.  
  125. // Auto generates a config file, JailbreakTeamBalance.cfg, with default values
  126. // Old file 'jailbreak-teambalance.cfg' may be safely deleted
  127. AutoExecConfig(true, "JBTeamBalance");
  128.  
  129. // Register the plugin's own commands
  130. RegConsoleCmd("sm_glajsklg", Command_Guard);
  131. RegAdminCmd("sm_tiasjgksa", Command_ClearGuards, ADMFLAG_CUSTOM4, "sm_clearguards - Resets the guard queue");
  132. RegAdminCmd("sm_tolakslg", Command_RemoveGuard, ADMFLAG_CUSTOM4, "sm_removeguard <player|#userid> - Removes target player from the guard queue");
  133.  
  134. // Look for changes to act on
  135. HookUserMessage(GetUserMessageId("VGUIMenu"),Hook_VGUIMenu,true);
  136.  
  137. HookConVarChange(gH_Cvar_Enabled, ConVarChanged_Global);
  138. HookConVarChange(gH_Cvar_RatioGoal, ConVarChanged_Global);
  139. HookConVarChange(gH_Cvar_SoundName, ConVarChanged_Global);
  140. HookConVarChange(gH_Cvar_ShowQueuePosition, ConVarChanged_Global);
  141. HookConVarChange(gH_Cvar_ShowClassPanel, ConVarChanged_Global);
  142. HookConVarChange(gH_Cvar_BlockCTatJoin, ConVarChanged_Global);
  143.  
  144. HookEvent("round_end",Event_RoundEnded,EventHookMode_Post);
  145. HookEvent("round_start",Event_RoundStarted,EventHookMode_Post);
  146. HookEvent("player_team",Event_PlayerTeamSwitch,EventHookMode_Post);
  147. HookEvent("player_disconnect",Event_PlayerDisconnect,EventHookMode_Post);
  148.  
  149. // Hook join & team change commands
  150. AddCommandListener(Command_JoinTeam, "jointeam");
  151.  
  152. // Hook joinclass for debugging purposes
  153. #if DEBUG == 1
  154. AddCommandListener(Command_JoinClass, "joinclass");
  155. #endif
  156.  
  157. // Zero out array
  158. for (new idx = 1; idx <= MaxClients; idx++)
  159. {
  160. g_bTalkedThisRound[idx] = Bool:false;
  161. }
  162.  
  163. // Add menu integration for removeguard command
  164. new Handle:topmenu;
  165. if (LibraryExists("adminmenu") && ((topmenu = GetAdminTopMenu()) != INVALID_HANDLE))
  166. {
  167. OnAdminMenuReady(topmenu);
  168. }
  169. } // end OnPluginStart
  170.  
  171. public OnConfigsExecuted()
  172. {
  173. // Update the shadow variables from the exec'd configs
  174. gShadow_Cvar_Enabled = Bool:GetConVarBool(gH_Cvar_Enabled);
  175. GetConVarString(gH_Cvar_SoundName, gShadow_Cvar_SoundName, sizeof(gShadow_Cvar_SoundName));
  176. gShadow_Cvar_RatioGoal = GetConVarFloat(gH_Cvar_RatioGoal);
  177. gShadow_Cvar_ShowQueuePosition = Bool:GetConVarBool(gH_Cvar_ShowQueuePosition);
  178. gShadow_Cvar_ShowClassPanel = Bool:GetConVarBool(gH_Cvar_ShowClassPanel);
  179. gShadow_Cvar_BlockCTatJoin = Bool:GetConVarBool(gH_Cvar_BlockCTatJoin);
  180.  
  181. // Check for the prescence of other plugins
  182. gH_CTBansStatus = FindConVar("sm_ctban_enable");
  183. gH_AzelphurTeamBanStatus = FindConVar("sm_teambans_version");
  184.  
  185. if (gH_CTBansStatus != INVALID_HANDLE)
  186. {
  187. HookConVarChange(gH_CTBansStatus, ConVarChanged_Global);
  188. gShadow_CTBan = GetConVarInt(gH_CTBansStatus);
  189. if (gShadow_CTBan)
  190. {
  191. gH_BanCookie = RegClientCookie("Banned_From_CT", "Tells if you are restricted from joining the CT team", CookieAccess_Protected);
  192. }
  193. }
  194.  
  195. if (gH_AzelphurTeamBanStatus != INVALID_HANDLE)
  196. {
  197. gH_AzlBanCookie = RegClientCookie("TeamBan_BanMask", "The team banmask.", CookieAccess_Private);
  198. }
  199.  
  200. // Make sure we enforce the limitteams value if owner is running vanilla config
  201. gH_LimitTeams = FindConVar("mp_limitteams");
  202. if (GetConVarInt(gH_LimitTeams) > 0)
  203. {
  204. SetConVarInt(gH_LimitTeams, 0);
  205. }
  206. }
  207.  
  208. public OnLibraryRemoved(const String:name[])
  209. {
  210. if (StrEqual(name, "adminmenu"))
  211. {
  212. gH_TopMenu = INVALID_HANDLE;
  213. }
  214. }
  215.  
  216. public Action:Command_ClearGuards(client, args)
  217. {
  218. if (args)
  219. {
  220. ReplyToCommand(client, CHAT_BANNER, "ClearGuards Usage");
  221. return Plugin_Handled;
  222. }
  223.  
  224. ClearArray(gA_GuardRequest);
  225. ReplyToCommand(client, CHAT_BANNER, "Clear Guards");
  226.  
  227. return Plugin_Handled;
  228. }
  229.  
  230. public OnAdminMenuReady(Handle:topmenu)
  231. {
  232. /* Block us from being called twice */
  233. if (topmenu == gH_TopMenu)
  234. {
  235. return;
  236. }
  237.  
  238. /* Save the Handle */
  239. gH_TopMenu = topmenu;
  240.  
  241. /* Build the "Player Commands" category */
  242. new TopMenuObject:player_commands = FindTopMenuCategory(gH_TopMenu, ADMINMENU_PLAYERCOMMANDS);
  243.  
  244. if (player_commands != INVALID_TOPMENUOBJECT)
  245. {
  246. AddToTopMenu(gH_TopMenu,
  247. "sm_removeguard",
  248. TopMenuObject_Item,
  249. AdminMenu_RemoveGuard,
  250. player_commands,
  251. "sm_removeguard",
  252. ADMFLAG_CUSTOM4);
  253. }
  254. }
  255.  
  256. public AdminMenu_RemoveGuard(Handle:topmenu, TopMenuAction:action, TopMenuObject:object_id, param, String:buffer[], maxlength)
  257. {
  258. if (action == TopMenuAction_DisplayOption)
  259. {
  260. Format(buffer, maxlength, "Remove from Queue");
  261. }
  262. else if (action == TopMenuAction_SelectOption)
  263. {
  264. DisplayRemoveGuardMenu(param, GetArraySize(gA_GuardRequest));
  265. }
  266. }
  267.  
  268. DisplayRemoveGuardMenu(Client, QueueSize)
  269. {
  270. if (QueueSize == 0)
  271. {
  272. PrintToChat(Client, CHAT_BANNER, "Queue Empty");
  273. }
  274. else
  275. {
  276. new Handle:menu = CreateMenu(MenuHandler_RemoveGuard);
  277.  
  278. SetMenuTitle(menu, "Remove Player from Guard Queue:");
  279. SetMenuExitBackButton(menu, true);
  280.  
  281. new IndexPlayer = 0;
  282. for (new QueueIndex = 0; QueueIndex < QueueSize; QueueIndex++)
  283. {
  284. IndexPlayer = GetArrayCell(gA_GuardRequest, QueueIndex);
  285. decl String:sName[MAX_NAME_LENGTH];
  286. GetClientName(IndexPlayer, sName, sizeof(sName));
  287. decl String:sPlayerIndex[5];
  288. IntToString(IndexPlayer, sPlayerIndex, sizeof(sPlayerIndex));
  289. AddMenuItem(menu, sPlayerIndex, sName);
  290. }
  291.  
  292. DisplayMenu(menu, Client, 15);
  293. }
  294. }
  295.  
  296. public MenuHandler_RemoveGuard(Handle:menu, MenuAction:action, param1, param2)
  297. {
  298. if (action == MenuAction_End)
  299. {
  300. CloseHandle(menu);
  301. }
  302. else if (action == MenuAction_Cancel)
  303. {
  304. if ((param2 == MenuCancel_ExitBack) && (gH_TopMenu != INVALID_HANDLE))
  305. {
  306. DisplayTopMenu(gH_TopMenu, param1, TopMenuPosition_LastCategory);
  307. }
  308. }
  309. else if (action == MenuAction_Select)
  310. {
  311. decl String:sInfo[5];
  312. new target;
  313. GetMenuItem(menu, param2, sInfo, sizeof(sInfo));
  314. target = StringToInt(sInfo);
  315.  
  316. if (target == 0)
  317. {
  318. PrintToChat(param1, CHAT_BANNER, "Player no longer available");
  319. }
  320. else if (!CanUserTarget(param1, target))
  321. {
  322. PrintToChat(param1, CHAT_BANNER, "Unable to target");
  323. }
  324. else
  325. {
  326. // try to find target in guard queue
  327. new RemoveeIndex = FindValueInArray(gA_GuardRequest, target);
  328.  
  329. if (RemoveeIndex == -1)
  330. {
  331. PrintToChat(param1, CHAT_BANNER, "Queue Not Found");
  332. }
  333. else
  334. {
  335. RemoveFromArray(gA_GuardRequest, RemoveeIndex);
  336. PrintToChat(param1, CHAT_BANNER, "Queue Removed Guard", target);
  337. }
  338. }
  339. }
  340. }
  341.  
  342. public Action:Command_RemoveGuard(client, args)
  343. {
  344. new iQueueSize = GetArraySize(gA_GuardRequest);
  345. if (iQueueSize == 0)
  346. {
  347. ReplyToCommand(client, CHAT_BANNER, "Queue Empty");
  348. return Plugin_Handled;
  349. }
  350.  
  351. if (!args)
  352. {
  353. if (client)
  354. {
  355. DisplayRemoveGuardMenu(client, iQueueSize);
  356. }
  357. else
  358. {
  359. ReplyToCommand(client, CHAT_BANNER, "Not Available from Server");
  360. }
  361. return Plugin_Handled;
  362. }
  363.  
  364. decl String:sArgument[65];
  365. GetCmdArg(1, sArgument, sizeof(sArgument));
  366.  
  367. decl String:target_name[MAX_TARGET_LENGTH];
  368. decl target_list[MAXPLAYERS], target_count, bool:tn_is_ml;
  369.  
  370. target_count = ProcessTargetString(
  371. sArgument,
  372. client,
  373. target_list,
  374. MAXPLAYERS,
  375. COMMAND_FILTER_CONNECTED,
  376. target_name,
  377. sizeof(target_name),
  378. tn_is_ml);
  379.  
  380. if (target_count != 1)
  381. {
  382. ReplyToTargetError(client, target_count);
  383. return Plugin_Handled;
  384. }
  385.  
  386. // try to find target in guard queue
  387. new RemoveeIndex = FindValueInArray(gA_GuardRequest, target_list[0]);
  388.  
  389. if (RemoveeIndex == -1)
  390. {
  391. ReplyToCommand(client, CHAT_BANNER, "Queue Not Found");
  392. return Plugin_Handled;
  393. }
  394.  
  395. RemoveFromArray(gA_GuardRequest, RemoveeIndex);
  396. ReplyToCommand(client, CHAT_BANNER, "Queue Removed Guard", target_list[0]);
  397.  
  398. return Plugin_Handled;
  399. }
  400.  
  401. public ConVarChanged_Global(Handle:cvar, const String:oldValue[], const String:newValue[])
  402. {
  403. // Ignore changes which result in the same value being set
  404. if (StrEqual(oldValue, newValue, true))
  405. {
  406. return;
  407. }
  408.  
  409. // Perform separate integer checking for booleans
  410. new iNewValue = StringToInt(newValue);
  411. new iOldValue = StringToInt(oldValue);
  412. new Bool:b_iNoChange = Bool:false;
  413. if (iNewValue == iOldValue)
  414. {
  415. b_iNoChange = Bool:true;
  416. }
  417.  
  418. if (!b_iNoChange && (cvar == gH_Cvar_Enabled))
  419. {
  420. if (iNewValue != 1)
  421. {
  422. UnhookEvent("round_end",Event_RoundEnded,EventHookMode_Post);
  423. UnhookEvent("round_start",Event_RoundStarted,EventHookMode_Post);
  424. UnhookEvent("player_team",Event_PlayerTeamSwitch,EventHookMode_Post);
  425. UnhookEvent("player_disconnect",Event_PlayerDisconnect,EventHookMode_Post);
  426. }
  427. else
  428. {
  429. HookEvent("round_end",Event_RoundEnded,EventHookMode_Post);
  430. HookEvent("round_start",Event_RoundStarted,EventHookMode_Post);
  431. HookEvent("player_team",Event_PlayerTeamSwitch,EventHookMode_Post);
  432. HookEvent("player_disconnect",Event_PlayerDisconnect,EventHookMode_Post);
  433. }
  434.  
  435. gShadow_Cvar_Enabled = Bool:iNewValue;
  436. }
  437. else if (cvar == gH_Cvar_RatioGoal)
  438. {
  439. gShadow_Cvar_RatioGoal = StringToFloat(newValue);
  440. }
  441. else if (cvar == gH_Cvar_SoundName)
  442. {
  443. strcopy(gShadow_Cvar_SoundName, PLATFORM_MAX_PATH, newValue);
  444. }
  445. else if (!b_iNoChange && (cvar == gH_Cvar_ShowQueuePosition))
  446. {
  447. gShadow_Cvar_ShowQueuePosition = Bool:iNewValue;
  448. }
  449. else if (!b_iNoChange && (cvar == gH_Cvar_ShowClassPanel))
  450. {
  451. if (Bool:iNewValue != Bool:true)
  452. {
  453. HookUserMessage(GetUserMessageId("VGUIMenu"),Hook_VGUIMenu,true);
  454. }
  455. else
  456. {
  457. UnhookUserMessage(GetUserMessageId("VGUIMenu"),Hook_VGUIMenu,true);
  458. }
  459. gShadow_Cvar_ShowClassPanel = Bool:iNewValue;
  460. }
  461. else if (!b_iNoChange && (cvar == gH_Cvar_BlockCTatJoin))
  462. {
  463. gShadow_Cvar_BlockCTatJoin = Bool:iNewValue;
  464. }
  465. else if (cvar == gH_CTBansStatus)
  466. {
  467. gShadow_CTBan = iNewValue;
  468. }
  469. }
  470.  
  471. public Action:Command_Guard(client, args)
  472. {
  473. // check to make sure the client isn't already a CT
  474. if (GetClientTeam(client) != CS_TEAM_CT)
  475. {
  476. // check for Team Bans
  477. if (gH_CTBansStatus != INVALID_HANDLE)
  478. {
  479. // check if client cookie is loaded (if not, Team Bans will take care of it)
  480. if (AreClientCookiesCached(client) && gShadow_CTBan)
  481. {
  482. decl String:cookie[5];
  483. GetClientCookie(client, gH_BanCookie, cookie, sizeof(cookie));
  484.  
  485. if (StrEqual(cookie, "1"))
  486. {
  487. if(strcmp(gShadow_Cvar_SoundName, ""))
  488. {
  489. decl String:buffer[PLATFORM_MAX_PATH + 5];
  490. Format(buffer, sizeof(buffer), "play %s", gShadow_Cvar_SoundName);
  491. ClientCommand(client, buffer);
  492. }
  493. PrintToChat(client, CHAT_BANNER, "CT Bans Denied");
  494. return Plugin_Handled;
  495. } // end If CT Banned
  496. } // end Are Cookies Cached?
  497. } // end Team Bans check
  498. else if (gH_AzelphurTeamBanStatus != INVALID_HANDLE)
  499. {
  500. decl String:sCookie[5];
  501. GetClientCookie(client, gH_AzlBanCookie, sCookie, sizeof(sCookie));
  502. new iBanMask = StringToInt(sCookie);
  503. if (1<<CS_TEAM_CT & iBanMask)
  504. {
  505. if (strcmp(gShadow_Cvar_SoundName, ""))
  506. {
  507. decl String:buffer[PLATFORM_MAX_PATH + 5];
  508. Format(buffer, sizeof(buffer), "play %s", gShadow_Cvar_SoundName);
  509. ClientCommand(client, buffer);
  510. }
  511. PrintToChat(client, CHAT_BANNER, "Azelphur Ban");
  512. return Plugin_Handled;
  513. }
  514. } // end Azelphur Team Bans check
  515.  
  516. // count people on CT
  517. new numCTs = GetTeamClientCount(CS_TEAM_CT);
  518.  
  519. // check if requester is already in the queue
  520. new QueuePosition = FindValueInArray(gA_GuardRequest, client);
  521. if (QueuePosition == -1)
  522. {
  523. if (numCTs != 0)
  524. {
  525. new GRindex = PushArrayCell(gA_GuardRequest, client) + 1;
  526. if (gShadow_Cvar_ShowQueuePosition)
  527. {
  528. PrintToChat(client, CHAT_BANNER, "Queue Added Position", GRindex);
  529. }
  530. else
  531. {
  532. PrintToChat(client, CHAT_BANNER, "Queue No Position Added");
  533. }
  534. }
  535. else
  536. {
  537. // this often occurs at the beginning of a map
  538. PrintToChat(client, CHAT_BANNER, "Queue Manual Join");
  539. }
  540. }
  541. else
  542. {
  543. if (gShadow_Cvar_ShowQueuePosition)
  544. {
  545. PrintToChat(client, CHAT_BANNER, "Queue Position", QueuePosition+1);
  546. }
  547. else
  548. {
  549. PrintToChat(client, CHAT_BANNER, "Queue InQueue");
  550. }
  551. }
  552. }
  553. else
  554. {
  555. PrintToChat(client, CHAT_BANNER, "Queue CT");
  556. }
  557.  
  558. return Plugin_Handled;
  559. } //end Command_Guard
  560.  
  561. public Action:Event_PlayerDisconnect(Handle:event, const String:name[], bool:dontBroadcast)
  562. {
  563. new clientID = GetClientOfUserId(GetEventInt(event, "userid"));
  564.  
  565. // remove from guard request list if they were in it
  566. new FindValueIndex = FindValueInArray(gA_GuardRequest, clientID);
  567. if (FindValueIndex != -1)
  568. {
  569. RemoveFromArray(gA_GuardRequest, FindValueIndex);
  570. }
  571.  
  572. return Plugin_Handled;
  573. }
  574.  
  575. public Action:Event_RoundEnded(Handle:event, const String:name[], bool:dontBroadcast)
  576. {
  577. gLastRoundEndReason = GetEventInt(event, "reason");
  578.  
  579. // lock team changes
  580. gTeamsLocked = Bool:true;
  581.  
  582. gOneRoundPlayed = Bool:true;
  583.  
  584. // clear T array
  585. ClearArray(gA_Terrorists);
  586.  
  587. // consider making global variables and moving this to jointeam cmd
  588. // count people in T and CT teams
  589. new numTs = 0;
  590. new numCTs = 0;
  591.  
  592. // check if VoiceHook is installed (crude method but effective in JB servers)
  593. // it's preferable to check a cvar or file but this is the cheapest method
  594. new Bool:bVoiceHook = Bool:false;
  595. for (new Pidx = 1; Pidx <= MaxClients; Pidx++)
  596. {
  597. if (g_bTalkedThisRound[Pidx])
  598. {
  599. bVoiceHook = Bool:true;
  600. }
  601. }
  602.  
  603. for (new idx = 1; idx <= MaxClients; idx++)
  604. {
  605. // check if person is in game and not in spec
  606. if (IsClientInGame(idx))
  607. {
  608. new indexTeam = GetClientTeam(idx);
  609. if (indexTeam == CS_TEAM_T)
  610. {
  611. if (bVoiceHook)
  612. {
  613. if (g_bTalkedThisRound[idx])
  614. {
  615. PushArrayCell(gA_Terrorists, idx);
  616. }
  617. // debug the voicehook mm:s plugin/extension
  618. #if DEBUG == 1
  619. else
  620. {
  621. LogMessage("VH: excluded T %N for not using their mic this round", idx);
  622. }
  623. #endif
  624. }
  625. else
  626. {
  627. PushArrayCell(gA_Terrorists, idx);
  628. }
  629. numTs++;
  630. }
  631. else if (indexTeam == CS_TEAM_CT)
  632. {
  633. numCTs++;
  634. }
  635. } // end if client in game
  636.  
  637. // reset global voicehook bools
  638. g_bTalkedThisRound[idx] = Bool:false;
  639. } // end for idx
  640.  
  641. // check for empty CT team
  642. if (numCTs == 0)
  643. {
  644. gOneRoundPlayed = Bool:false;
  645. gOneJoined = Bool:false;
  646. }
  647.  
  648. g_iActivePlayers = numTs + numCTs;
  649. // make sure server isn't empty
  650. if ((numTs == 0) || (numCTs == 0))
  651. {
  652. return Plugin_Continue;
  653. }
  654.  
  655. // we should be able to do some better guessing of the ratio here but for now...
  656. // find the closest arrangement to the requested ratio
  657. new TargetNumTs = 0;
  658. new Float:Ratio = 0.0;
  659. new Float:tempBest = 10.1; // 0.1 above max ratio
  660. // find ideal target teams (leave 1 person on each team for the calculations)
  661. for (new t = 1; t <= (g_iActivePlayers-1); t++)
  662. {
  663. // check for divide by zero (this should never happen but catch it if it does)
  664. if ((g_iActivePlayers - t) == 0)
  665. {
  666. LogError("Error: Divide by zero in round_end");
  667. }
  668. else
  669. {
  670. Ratio = float(t)/float(g_iActivePlayers - t);//FloatDiv(Float:t,(Float:g_iActivePlayers - Float:t));
  671. // CTs are (g_iActivePlayers - t)
  672.  
  673. new Float:fRatioToTry = FloatAbs(gShadow_Cvar_RatioGoal - Ratio);
  674. if (fRatioToTry < tempBest)
  675. {
  676. tempBest = fRatioToTry;
  677. TargetNumTs = t;
  678. }
  679. }
  680. } // end for t
  681.  
  682. new numToMove = 0;
  683. new Switch_ID = 0;
  684.  
  685. // find if changes need to be made
  686. if (numTs > TargetNumTs)
  687. {
  688. // move Ts to CT
  689. numToMove = numTs - TargetNumTs;
  690.  
  691. // this is a bad method of doing it, in the future we should
  692. // check each clients cookie setting and build an array of
  693. // possible candidates for the random swap.
  694. new RetriesRemaining = 3;
  695. for (new t = 0; t <= (numToMove-1); t++)
  696. {
  697. // check if there is anyone in the request queue or terrorist array
  698. new iTerrorSize = GetArraySize(gA_Terrorists);
  699. if ((GetArraySize(gA_GuardRequest) == 0) && (iTerrorSize != 0))
  700. {
  701. // grab random T
  702. new RandomIndex = GetRandomInt(0,iTerrorSize - 1);
  703.  
  704. Switch_ID = GetArrayCell(gA_Terrorists, RandomIndex);
  705. RemoveFromArray(gA_Terrorists, RandomIndex);
  706.  
  707. // check if player is CT banned
  708. // check for Team Bans
  709. if (gH_CTBansStatus != INVALID_HANDLE)
  710. {
  711. // check if client cookie is loaded (if not, Team Bans will take care of it)
  712. if (AreClientCookiesCached(Switch_ID) && gShadow_CTBan)
  713. {
  714. decl String:cookie[5];
  715. GetClientCookie(Switch_ID, gH_BanCookie, cookie, sizeof(cookie));
  716.  
  717. if (StrEqual(cookie, "1"))
  718. {
  719. // decrement the loop counter so we can try again
  720. if (RetriesRemaining > 0)
  721. {
  722. // redo the loop iteration
  723. t--;
  724. RetriesRemaining--;
  725. }
  726. } // end If CT Banned
  727. else
  728. {
  729. // player isn't banned so switch normally
  730. if (IsClientInGame(Switch_ID))
  731. {
  732. // let them know they were changed
  733. if (!IsFakeClient(Switch_ID))
  734. {
  735. PrintToChat(Switch_ID, CHAT_BANNER, "Random to CT");
  736. }
  737. CS_SwitchTeam(Switch_ID,CS_TEAM_CT);
  738. numTs--;
  739. }
  740. }
  741. } // end Are Cookies Cached?
  742. } // end Team Bans check
  743. else if (gH_AzelphurTeamBanStatus != INVALID_HANDLE)
  744. {
  745. decl String:sCookie[5];
  746. GetClientCookie(Switch_ID, gH_AzlBanCookie, sCookie, sizeof(sCookie));
  747. new iBanMask = StringToInt(sCookie);
  748. if (1<<CS_TEAM_CT & iBanMask)
  749. {
  750. // decrement the loop counter so we can try again
  751. if (RetriesRemaining > 0)
  752. {
  753. // redo the loop iteration
  754. t--;
  755. RetriesRemaining--;
  756. }
  757. } // end If CT Banned
  758. else
  759. {
  760. // player isn't banned so switch normally
  761. if (IsClientInGame(Switch_ID))
  762. {
  763. // let them know they were changed
  764. if (!IsFakeClient(Switch_ID))
  765. {
  766. PrintToChat(Switch_ID, CHAT_BANNER, "Random to CT");
  767. }
  768. CS_SwitchTeam(Switch_ID,CS_TEAM_CT);
  769. numTs--;
  770. }
  771. }
  772. } // end Team Bans check
  773. else
  774. {
  775. if (IsClientInGame(Switch_ID))
  776. {
  777. // let them know they were changed
  778. if (!IsFakeClient(Switch_ID))
  779. {
  780. PrintToChat(Switch_ID, CHAT_BANNER, "Random to CT");
  781. }
  782. CS_SwitchTeam(Switch_ID,CS_TEAM_CT);
  783. numTs--;
  784. }
  785. }
  786. }
  787. // or else someone has requested a CT position
  788. else if (GetArraySize(gA_GuardRequest) != 0)
  789. {
  790. // act like a queue
  791. Switch_ID = GetArrayCell(gA_GuardRequest,0);
  792. RemoveFromArray(gA_GuardRequest, 0);
  793.  
  794. if (IsClientInGame(Switch_ID))
  795. {
  796. if (!IsFakeClient(Switch_ID))
  797. {
  798. PrintToChat(Switch_ID, CHAT_BANNER, "Request Processed");
  799. }
  800. CS_SwitchTeam(Switch_ID,CS_TEAM_CT);
  801.  
  802. // remove them from terrorists array (consider they might have been spectator)
  803. new FindValueIndex = FindValueInArray(gA_Terrorists, Switch_ID);
  804. if (FindValueIndex != -1)
  805. {
  806. RemoveFromArray(gA_Terrorists, FindValueIndex);
  807. }
  808. }
  809. }
  810. }
  811. }
  812. else if (numTs < TargetNumTs)
  813. {
  814. // move CTs to T
  815. numToMove = TargetNumTs - numTs;
  816.  
  817. for (new t = 0; t <= (numToMove-1); t++)
  818. {
  819. // check if the stack is empty before we pop a value
  820. if (!IsStackEmpty(gH_CTStack))
  821. {
  822. PopStackCell(gH_CTStack,Switch_ID);
  823. // push it back on the stack so the change team can pop it
  824. PushStackCell(gH_CTStack,Switch_ID);
  825.  
  826. if (!IsFakeClient(Switch_ID))
  827. {
  828. PrintToChat(Switch_ID, CHAT_BANNER, "Stacked to T");
  829. }
  830. CS_SwitchTeam(Switch_ID,CS_TEAM_T);
  831. }
  832. }
  833. }
  834. return Plugin_Continue;
  835. } // end Event_RoundEnded
  836.  
  837. public OnMapStart()
  838. {
  839. gLastRoundEndReason = REASON_INVALID;
  840.  
  841. gOneJoined = Bool:false;
  842. gOneRoundPlayed = Bool:false;
  843.  
  844. // clear the stacks
  845. for (;;)
  846. {
  847. if (IsStackEmpty(gH_CTStack))
  848. {
  849. break;
  850. }
  851. PopStack(gH_CTStack);
  852. }
  853. for (;;)
  854. {
  855. if (IsStackEmpty(gH_TempStack))
  856. {
  857. break;
  858. }
  859. PopStack(gH_TempStack);
  860. }
  861.  
  862. ClearArray(gA_GuardRequest);
  863.  
  864. // pre-cache deny sound
  865. if(strcmp(gShadow_Cvar_SoundName, ""))
  866. {
  867. decl String:sBuffer[PLATFORM_MAX_PATH];
  868. PrecacheSound(gShadow_Cvar_SoundName, true);
  869. Format(sBuffer, sizeof(sBuffer), "sound/%s", gShadow_Cvar_SoundName);
  870. AddFileToDownloadsTable(sBuffer);
  871. }
  872. }
  873.  
  874. public Event_PlayerTeamSwitch(Handle:event, const String:name[], bool:dontBroadcast)
  875. {
  876. new NewTeam = GetEventInt(event, "team");
  877. new OldTeam = GetEventInt(event, "oldteam");
  878. new Bool:Disconnect = Bool:GetEventBool(event, "disconnect");
  879. new UserID = GetEventInt(event, "userid");
  880. new clientID = GetClientOfUserId(UserID);
  881.  
  882. // remove userid from old team
  883. if (OldTeam == CS_TEAM_CT)
  884. {
  885. g_iNumCTsDuringRound--;
  886.  
  887. // check if there's no more CTs
  888. if (g_iNumCTsDuringRound <= 0)
  889. {
  890. gOneJoined = Bool:false;
  891. gOneRoundPlayed = Bool:false;
  892. }
  893.  
  894. // find the CT in the stack and remove her/him
  895. new TempStackSize = 0;
  896. new TempClient = 0;
  897. for(;;)
  898. {
  899. PopStackCell(gH_CTStack, TempClient);
  900.  
  901. if (TempClient == clientID)
  902. {
  903. // rebuild CT stack
  904. for (new tsi = 0; tsi < TempStackSize; tsi++)
  905. {
  906. PopStackCell(gH_TempStack,TempClient);
  907. PushStackCell(gH_CTStack,TempClient);
  908. }
  909. break;
  910. }
  911.  
  912. if (IsStackEmpty(gH_CTStack))
  913. {
  914. break;
  915. }
  916.  
  917. // insert it into the temp stack
  918. PushStackCell(gH_TempStack,TempClient);
  919. TempStackSize++;
  920. } // 'end' infinite loop
  921. }
  922.  
  923. if (Bool:Disconnect == Bool:true)
  924. {
  925. // remove from guard request list if they were in it
  926. new FindValueIndex = FindValueInArray(gA_GuardRequest, clientID);
  927. if (FindValueIndex != -1)
  928. {
  929. RemoveFromArray(gA_GuardRequest, FindValueIndex);
  930. }
  931. }
  932. else
  933. {
  934. // find new team
  935. if (NewTeam == CS_TEAM_CT)
  936. {
  937. PushStackCell(gH_CTStack,clientID);
  938. }
  939. }
  940. } // end Event_PlayerTeamSwitch
  941.  
  942. /*
  943. public Action:Timer_RespawnFirstRound(Handle:timer)
  944. {
  945. new Bool:bCTAlive = Bool:false;
  946. new Bool:bCTPresent = Bool:false;
  947. // check if CT team is all dead
  948. for (new idx = 1; idx <= MaxClients; idx++)
  949. {
  950. if (IsClientInGame(idx))
  951. {
  952. if (GetClientTeam(idx) == CS_TEAM_CT)
  953. {
  954. bCTPresent = Bool:true;
  955. if (IsPlayerAlive(idx))
  956. {
  957. bCTAlive = Bool:true;
  958. }
  959. }
  960. }
  961. }
  962. if (!bCTAlive && !gTeamsLocked)
  963. {
  964. // respawn everyone
  965. for (new idx = 1; idx <= MaxClients; idx++)
  966. {
  967. if (IsClientInGame(idx))
  968. {
  969. if (!IsPlayerAlive(idx) && (GetClientTeam(idx) > 1))
  970. {
  971. CS_RespawnPlayer(idx);
  972. }
  973. }
  974. }
  975. }
  976. if (!bCTPresent)
  977. {
  978. // allow next person to join
  979. gOneJoined = Bool:false;
  980. gOneRoundPlayed = Bool:false;
  981. }
  982. }
  983. */
  984.  
  985. public Action:Event_RoundStarted(Handle:event, const String:name[], bool:dontBroadcast)
  986. {
  987. // count and track people on CT before teams are unlocked
  988. new numCTs = 0;
  989. for (new idx = 1; idx <= MaxClients; idx++)
  990. {
  991. if (IsClientInGame(idx))
  992. {
  993. if (GetClientTeam(idx) == CS_TEAM_CT)
  994. {
  995. PushStackCell(gH_TempStack, idx);
  996. numCTs++;
  997. }
  998. }
  999. } // end for idx
  1000.  
  1001. // fire a timer to handle any respawns that don't occur
  1002. // or those who want to swap back to T after a random switch
  1003. CreateTimer(2.5, Timer_RespawnSwapped, numCTs, TIMER_FLAG_NO_MAPCHANGE);
  1004.  
  1005. // keep track of number of CTs during a round, starting now
  1006. g_iNumCTsDuringRound = numCTs;
  1007.  
  1008. if (numCTs == 0)
  1009. {
  1010. gOneJoined = Bool:false;
  1011. gOneRoundPlayed = Bool:false;
  1012. }
  1013.  
  1014. // unlock team changes
  1015. gTeamsLocked = Bool:false;
  1016.  
  1017. return Plugin_Handled;
  1018. } // end Event_RoundStarted
  1019.  
  1020. public Action:Timer_RespawnSwapped(Handle:timer, any:numberCTs)
  1021. {
  1022. // only respawn those CTs who were there before teams were locked
  1023. new TempClient = 0;
  1024. for (new CTidx = 0; CTidx < numberCTs; CTidx++)
  1025. {
  1026. PopStackCell(gH_TempStack,TempClient);
  1027. if (IsClientInGame(TempClient))
  1028. {
  1029. if (!IsPlayerAlive(TempClient) && (GetClientTeam(TempClient) == CS_TEAM_CT))
  1030. {
  1031. CS_RespawnPlayer(TempClient);
  1032. }
  1033. }
  1034. } // end for CTidx
  1035. // respawn any T who is dead
  1036. for (new idx = 1; idx <= MaxClients; idx++)
  1037. {
  1038. if (IsClientInGame(idx))
  1039. {
  1040. if ((GetClientTeam(idx) == CS_TEAM_T) && !IsPlayerAlive(idx))
  1041. {
  1042. CS_RespawnPlayer(idx);
  1043. }
  1044. }
  1045. } // end for idx
  1046. }
  1047.  
  1048. // this function is present for debugging purposes
  1049. #if DEBUG == 1
  1050. public Action:Command_JoinClass(client, const String:command[], args)
  1051. {
  1052. decl String:classString[5];
  1053. GetCmdArg(1, classString, sizeof(classString));
  1054. new Target_Class = StringToInt(classString);
  1055. if ((Target_Class != 3) && (Target_Class != 5))
  1056. {
  1057. LogMessage("%N did joinclass: %d", client, Target_Class);
  1058. }
  1059. return Plugin_Continue;
  1060. }
  1061. #endif
  1062.  
  1063. public Action:Timer_DetermineRoundDraw(Handle:timer)
  1064. {
  1065. // if the we're NOT inbetween round_start and round_end
  1066. if (!gTeamsLocked)
  1067. {
  1068. // count alive players and total players on each team
  1069. new CTs = GetTeamClientCount(CS_TEAM_CT);
  1070. new Ts = GetTeamClientCount(CS_TEAM_T);
  1071. new AliveCTs = 0;
  1072. new AliveTs = 0;
  1073.  
  1074. for (new idx = 1; idx <= MaxClients; idx++)
  1075. {
  1076. if (IsClientInGame(idx) && IsPlayerAlive(idx))
  1077. {
  1078. switch (GetClientTeam(idx))
  1079. {
  1080. case CS_TEAM_CT:
  1081. {
  1082. AliveCTs++;
  1083. }
  1084. case CS_TEAM_T:
  1085. {
  1086. AliveTs++;
  1087. }
  1088. }
  1089. }
  1090. }
  1091.  
  1092. if ((CTs > 0 && AliveCTs == 0) || (Ts >0 && AliveTs == 0))
  1093. {
  1094. CS_TerminateRound(0.0, CSRoundEnd_Draw, true);
  1095. }
  1096. }
  1097.  
  1098. return Plugin_Handled;
  1099. }
  1100.  
  1101. public Action:Command_JoinTeam(client, const String:command[], args)
  1102. {
  1103. // Check to see if the client is valid and JBTB is enabled
  1104. if(!client || !IsClientInGame(client) || IsFakeClient(client) || !gShadow_Cvar_Enabled)
  1105. {
  1106. return Plugin_Continue;
  1107. }
  1108.  
  1109. // Create timer to determine if we should fire a round draw manually
  1110. CreateTimer(0.1, Timer_DetermineRoundDraw, _, TIMER_FLAG_NO_MAPCHANGE);
  1111.  
  1112. // Get the target team
  1113. decl String:teamString[3];
  1114. GetCmdArg(1, teamString, sizeof(teamString));
  1115. new Target_Team = StringToInt(teamString);
  1116. // Get the players current team
  1117. new Current_Team = GetClientTeam(client);
  1118.  
  1119. // Check to see if the team request is valid
  1120. if (Current_Team == Target_Team)
  1121. {
  1122. PrintCenterText(client, "%t", "Invalid Team Selection");
  1123. return Plugin_Handled;
  1124. }
  1125.  
  1126. // check if teams are currently locked and it's not the beginning of the game
  1127. if (gTeamsLocked && (gLastRoundEndReason != REASON_INVALID) && (gLastRoundEndReason != REASON_GAME_COMMENCING) && (gLastRoundEndReason != REASON_ROUND_DRAW))
  1128. {
  1129. if(strcmp(gShadow_Cvar_SoundName, ""))
  1130. {
  1131. decl String:buffer[PLATFORM_MAX_PATH + 5];
  1132. Format(buffer, sizeof(buffer), "play %s", gShadow_Cvar_SoundName);
  1133. ClientCommand(client, buffer);
  1134. }
  1135. PrintCenterText(client, "%t", "Center Teams Locked");
  1136. UTIL_TeamMenu(client);
  1137. return Plugin_Handled;
  1138. } // end if teams locked
  1139.  
  1140. // allow one person to join CT when map first starts or CT team is empty
  1141. if ((Target_Team == CS_TEAM_CT) && (gOneJoined == Bool:false) && (gOneRoundPlayed == Bool:false))
  1142. {
  1143. gOneJoined = Bool:true;
  1144. return Plugin_Continue;
  1145. }
  1146.  
  1147. // disable auto-join
  1148. if (!((Target_Team == CS_TEAM_T) || (Target_Team == CS_TEAM_CT) || (Target_Team == CS_TEAM_SPECTATOR)))
  1149. {
  1150. if(strcmp(gShadow_Cvar_SoundName, ""))
  1151. {
  1152. decl String:buffer[PLATFORM_MAX_PATH + 5];
  1153. Format(buffer, sizeof(buffer), "play %s", gShadow_Cvar_SoundName);
  1154. ClientCommand(client, buffer);
  1155. }
  1156. PrintCenterText(client, "%t", "Center Auto-Join Disabled");
  1157. UTIL_TeamMenu(client);
  1158. return Plugin_Handled;
  1159. }
  1160.  
  1161. // disable joining CT with people waiting in queue
  1162. if (Target_Team == CS_TEAM_CT)
  1163. {
  1164. // check if there are people waiting in the queue
  1165. if (GetArraySize(gA_GuardRequest) != 0)
  1166. {
  1167. // there are people waiting, so deny them
  1168. if(strcmp(gShadow_Cvar_SoundName, ""))
  1169. {
  1170. decl String:buffer[PLATFORM_MAX_PATH + 5];
  1171. Format(buffer, sizeof(buffer), "play %s", gShadow_Cvar_SoundName);
  1172. ClientCommand(client, buffer);
  1173. }
  1174. PrintCenterText(client, "%t", "Center InQueue");
  1175. UTIL_TeamMenu(client);
  1176.  
  1177. return Plugin_Handled;
  1178. }
  1179. }
  1180.  
  1181. // check if player is trying to join CT just after just joining
  1182. if (gShadow_Cvar_BlockCTatJoin)
  1183. {
  1184. if ((Target_Team == CS_TEAM_CT) && (Current_Team != CS_TEAM_T))
  1185. {
  1186. if(strcmp(gShadow_Cvar_SoundName, ""))
  1187. {
  1188. decl String:buffer[PLATFORM_MAX_PATH + 5];
  1189. Format(buffer, sizeof(buffer), "play %s", gShadow_Cvar_SoundName);
  1190. ClientCommand(client, buffer);
  1191. }
  1192. PrintCenterText(client, "%t", "Center Must Play T First");
  1193. UTIL_TeamMenu(client);
  1194.  
  1195. return Plugin_Handled;
  1196. }
  1197. }
  1198.  
  1199. // we've passed all the checks, now get ready to call joinclass if we're not showing the classes screen
  1200. if (!gShadow_Cvar_ShowClassPanel)
  1201. {
  1202. new Handle:JoinClassPack = CreateDataPack();
  1203. WritePackCell(JoinClassPack, client);
  1204. WritePackCell(JoinClassPack, Target_Team);
  1205. CreateTimer(0.0, Timer_ForceJoinClass, JoinClassPack);
  1206. }
  1207.  
  1208. // If we get to here then all is for the good
  1209. return Plugin_Continue;
  1210. } // end Command_JoinTeam
  1211.  
  1212. public Action:Timer_ForceJoinClass(Handle:timer, Handle:JoinClassPack)
  1213. {
  1214. ResetPack(JoinClassPack);
  1215. new client = ReadPackCell(JoinClassPack);
  1216. new Team = ReadPackCell(JoinClassPack);
  1217.  
  1218. // these models here were chosen so they support attachables
  1219. // in case the server doesn't run an SM model menu and has classes disabled
  1220. if (Team == CS_TEAM_T)
  1221. {
  1222. // arctic model
  1223. FakeClientCommand(client, "joinclass 3");
  1224. }
  1225. else if (Team == CS_TEAM_CT)
  1226. {
  1227. // urban model
  1228. FakeClientCommand(client, "joinclass 5");
  1229. }
  1230.  
  1231. CloseHandle(JoinClassPack);
  1232. }
  1233.  
  1234. public Action:Hook_VGUIMenu(UserMsg:msg_id, Handle:bf, const players[], playersNum, bool:reliable, bool:init)
  1235. {
  1236. new String:sPanelName[10];
  1237.  
  1238. if (GetUserMessageType() == UM_Protobuf)
  1239. {
  1240. PbReadString(bf, "name", sPanelName, sizeof(sPanelName));
  1241. }
  1242. else
  1243. {
  1244. BfReadString(bf, sPanelName, sizeof(sPanelName));
  1245. }
  1246.  
  1247. // find any class panels
  1248. if(StrContains(sPanelName, "class") != -1)
  1249. {
  1250. new bShow = BfReadByte(bf);
  1251. if(bShow)
  1252. {
  1253. // hide class screen
  1254. return Plugin_Handled;
  1255. }
  1256. }
  1257.  
  1258. return Plugin_Continue;
  1259. }
  1260.  
  1261. // This helper procedure will re-display the team join menu
  1262. // and is equivalent to what ClientCommand(client, "chooseteam") did in the past
  1263. UTIL_TeamMenu(client)
  1264. {
  1265. new clients[1];
  1266. new Handle:bf;
  1267. clients[0] = client;
  1268. bf = StartMessage("VGUIMenu", clients, 1);
  1269.  
  1270. if (GetUserMessageType() == UM_Protobuf)
  1271. {
  1272. PbSetString(bf, "name", "team");
  1273. PbSetBool(bf, "show", true);
  1274. }
  1275. else
  1276. {
  1277. BfWriteString(bf, "team"); // panel name
  1278. BfWriteByte(bf, 1); // bShow
  1279. BfWriteByte(bf, 0); // count
  1280. }
  1281.  
  1282. EndMessage();
  1283. }
  1284.  
  1285. public OnClientSpeaking(client)
  1286. {
  1287. g_bTalkedThisRound[client] = Bool:true;
  1288. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement