Advertisement
Guest User

Untitled

a guest
Jan 25th, 2015
177
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 15.98 KB | None | 0 0
  1. /**
  2. * vim: set ai et ts=4 sw=4 :
  3. * File: MaxClass.sp
  4. * Description: Set max players for each class
  5. * Author(s): Nican132
  6. * Versions:
  7. * 4.0 : by -=|JFH|=-Naris (Murray Wilson)
  8. * Use tf2_stocks
  9. * Pick random class to switch to
  10. * Use ConVars for admin settings
  11. * Display class selection menu when switching
  12. *
  13. * 4.3 : by cadavor
  14. * Add translations
  15. * 4.4 : by JameLess
  16. * Fixed skin glitching
  17. * 4.5 : by Thraka
  18. * Added the ability to load settings based on the prefix of the map. Example: pl is payload, cp is capture point.
  19. * Logs which section of the config is loading the settings.
  20. * 4.6 : by Thraka
  21. * Added the NO! class sounds when player chooses a class that is full. This was pulled from DJ Tsunami's class restriction plugin
  22. * 4.7 : by Thraka & Jameless
  23. * Request from snelvuur - Added the sm_maxclass_allow_adminsdeadclass cvar. If set to 0, this will not let admins play restricted classes
  24. * 4.8 : by Jameless
  25. * Added the sm_maxclass_admin_flag cvar. This will allow you to change what flag is immune.
  26. */
  27.  
  28. #pragma semicolon 1
  29.  
  30. #include <sourcemod>
  31. #include <sdktools>
  32. #include <tf2_stocks>
  33.  
  34. #define PL_VERSION "4.8"
  35.  
  36. #define DEBUG 0
  37. //To change the flag, look at: http://docs.sourcemod.net/api/index.php?fastload=file&id=28& for the right values
  38. #define TF2L_ADMIN_FLAG Admin_Reservation
  39.  
  40. public Plugin:myinfo =
  41. {
  42. name = "TF Max Players",
  43. author = "Nican132,cadavor,JameLess,Thraka",
  44. description = "Set max players for each class",
  45. version = PL_VERSION,
  46. url = "http://sourcemod.net/"
  47. };
  48.  
  49. //[amount of players][team][class] max amount
  50. new MaxClass[MAXPLAYERS][TFTeam + TFTeam:1][TFClassType + TFClassType:1];
  51. //[team][class] count array
  52. new CurrentCount[TFTeam + TFTeam:1][TFClassType + TFClassType:1];
  53. new bool:isrunning;
  54. new Handle:IsMaxPlayersOn;
  55. new Handle:ConfigFileName;
  56. new Handle:CheckAdmins;
  57. new Handle:CountAdmins;
  58. new Handle:CVAR_AllowAdminsDeadClass;
  59. new Handle:CVAR_Adminflag;
  60. new maxplayers;
  61.  
  62. static String:ClassNames[TFClassType][] = {"", "Scout", "Sniper", "Soldier", "Demoman", "Medic", "Heavy Guy", "Pyro", "Spy", "Engineer" };
  63. static String:TF_ClassNames[TFClassType][] = {"", "scout", "sniper", "soldier", "demoman", "medic", "heavyweapons", "pyro", "spy", "engineer" };
  64. new String:g_sSounds[10][24] = {"", "vo/scout_no03.wav", "vo/sniper_no04.wav", "vo/soldier_no01.wav",
  65. "vo/demoman_no03.wav", "vo/medic_no03.wav", "vo/heavy_no02.wav",
  66. "vo/pyro_no01.wav", "vo/spy_no02.wav", "vo/engineer_no03.wav"};
  67.  
  68. public OnPluginStart()
  69. {
  70. LoadTranslations("maxclass.phrases");
  71.  
  72. CreateConVar("sm_tf_maxclass", PL_VERSION, "TF2 max class", FCVAR_PLUGIN|FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY);
  73. IsMaxPlayersOn = CreateConVar("sm_maxclass_allow","1","Enable/Disable max class blocking");
  74. CheckAdmins = CreateConVar("sm_maxclass_exclude_admins","0","Enable/Disable admin exclusion");
  75. CountAdmins = CreateConVar("sm_maxclass_count_admins","0","Enable/Disable admins counting towards class limits");
  76. ConfigFileName = CreateConVar("sm_maxclass_config","MaxClass.txt","config file name");
  77. CVAR_AllowAdminsDeadClass = CreateConVar("sm_maxclass_allow_adminsdeadclass","1","Enable/Disable admins to choose restricted classes (0 in config)");
  78. CVAR_Adminflag = CreateConVar("sm_maxclass_admin_flag","a","Flag for admin immunity");
  79.  
  80. HookEvent("player_changeclass", PlayerChangeClass, EventHookMode_Pre);
  81. HookEvent("player_spawn", PlayerSpawn, EventHookMode_Pre);
  82. HookEvent("teamplay_teambalanced_player", PlayerTeamBalanced, EventHookMode_Pre);
  83.  
  84. RegAdminCmd("sm_classlimit", Command_PrintTable, ADMFLAG_CUSTOM4, "Re-reads and prints the limits");
  85. //RegConsoleCmd("sm_classlimit", Command_PrintTable)
  86.  
  87. AutoExecConfig(true, "maxclass");
  88. }
  89.  
  90.  
  91. public OnMapStart()
  92. {
  93. maxplayers = GetMaxClients();
  94.  
  95. StartReadingFromTable();
  96.  
  97. decl i, String:sSound[32];
  98. for(i = 1; i < sizeof(g_sSounds); i++)
  99. {
  100. Format(sSound, sizeof(sSound), "sound/%s", g_sSounds[i]);
  101. PrecacheSound(g_sSounds[i]);
  102. AddFileToDownloadsTable(sSound);
  103. }
  104. }
  105.  
  106. public Action:PlayerTeamBalanced(Handle:event, const String:name[], bool:dontBroadcast)
  107. {
  108. if (!isrunning)
  109. return;
  110.  
  111. if (!GetConVarBool(IsMaxPlayersOn))
  112. return;
  113.  
  114. new client = GetClientOfUserId(GetEventInt(event, "userid"));
  115. if (client <= 0)
  116. return;
  117.  
  118. new TFTeam:team = TFTeam:GetEventInt(event, "team");
  119. if (team < TFTeam_Red || team > TFTeam_Blue)
  120. return;
  121.  
  122. new TFClassType:class = TF2_GetPlayerClass(client);
  123. if (class == TFClass_Unknown)
  124. return;
  125.  
  126. new clientcount = GetClientCount(true);
  127.  
  128. if (GetConVarBool(CheckAdmins))
  129. {
  130. if (CheckForAdmin(client))
  131. {
  132. if (MaxClass[clientcount][team][class] == 0)
  133. {
  134. if (GetConVarBool(CVAR_AllowAdminsDeadClass))
  135. return;
  136.  
  137. }
  138. else
  139. return;
  140. }
  141. }
  142.  
  143. #if DEBUG == 1
  144. LogMessage("Teambalanced: %N", client);
  145. #endif
  146.  
  147. if (MaxClass[clientcount][team][class] <= -1)
  148. return;
  149.  
  150. RecountClasses();
  151.  
  152. if (CurrentCount[team][class] > MaxClass[clientcount][team][class])
  153. {
  154. PrintToChat(client, "\x04[MaxClass]\x01 %t", "Class Overflow", ClassNames[class]);
  155. PrintCenterText(client, "%t", "Class Overflow", ClassNames[class]);
  156.  
  157. SwitchClientClass(client, FindUnusedClass(team, clientcount));
  158. TF2_RegeneratePlayer(client);
  159. }
  160. }
  161.  
  162. public Action:PlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast)
  163. {
  164. if (!isrunning)
  165. return;
  166.  
  167. if (!GetConVarBool(IsMaxPlayersOn))
  168. return;
  169.  
  170. new client = GetClientOfUserId(GetEventInt(event, "userid"));
  171. if (!client)
  172. return;
  173.  
  174. new TFTeam:team = TFTeam:GetClientTeam(client);
  175. if (team < TFTeam_Red || team > TFTeam_Blue)
  176. return;
  177.  
  178. new TFClassType:class = TF2_GetPlayerClass(client);
  179. if (class == TFClass_Unknown)
  180. return;
  181.  
  182. new clientcount = GetClientCount(true);
  183.  
  184. if (GetConVarBool(CheckAdmins))
  185. {
  186. if (CheckForAdmin(client))
  187. {
  188. if (MaxClass[clientcount][team][class] == 0)
  189. {
  190. if (GetConVarBool(CVAR_AllowAdminsDeadClass))
  191. return;
  192.  
  193. }
  194. else
  195. return;
  196. }
  197. }
  198.  
  199. if (MaxClass[clientcount][team][class] <= -1)
  200. return;
  201.  
  202. #if DEBUG == 1
  203. LogMessage("PlayerSpawn: %N", client);
  204. #endif
  205.  
  206. RecountClasses();
  207.  
  208. if (CurrentCount[team][class] > MaxClass[clientcount][team][class])
  209. {
  210. PrintToChat(client, "\x04[MaxClass]\x01 %t", "Class Overflow", ClassNames[class]);
  211. PrintCenterText(client, "%t", "Class Overflow", ClassNames[class]);
  212. EmitSoundToClient(client, g_sSounds[class]);
  213.  
  214. SwitchClientClass(client, FindUnusedClass(team, clientcount));
  215. TF2_RegeneratePlayer(client);
  216. }
  217. }
  218.  
  219. public Action:PlayerChangeClass(Handle:event, const String:name[], bool:dontBroadcast)
  220. {
  221. if (!isrunning)
  222. return;
  223.  
  224. if (!GetConVarBool(IsMaxPlayersOn))
  225. return;
  226.  
  227. new client = GetClientOfUserId(GetEventInt(event, "userid"));
  228. if (!client)
  229. return;
  230.  
  231. new TFClassType:class = TFClassType:GetEventInt(event, "class");
  232. new TFClassType:oldclass = TF2_GetPlayerClass(client);
  233.  
  234. if (class == oldclass)
  235. return;
  236.  
  237. if (class == TFClass_Unknown)
  238. return;
  239.  
  240. new TFTeam:team = TFTeam:GetClientTeam(client);
  241. if (team < TFTeam_Red || team > TFTeam_Blue)
  242. return;
  243.  
  244. new clientcount = GetClientCount(true);
  245.  
  246. if (GetConVarBool(CheckAdmins))
  247. {
  248. if (CheckForAdmin(client))
  249. {
  250. if (MaxClass[clientcount][team][class] == 0)
  251. {
  252. if (GetConVarBool(CVAR_AllowAdminsDeadClass))
  253. return;
  254.  
  255. }
  256. else
  257. return;
  258. }
  259. }
  260.  
  261. #if DEBUG == 1
  262. LogMessage("ChangeClass: %N", client);
  263. #endif
  264.  
  265.  
  266. if (MaxClass[clientcount][team][class] <= -1)
  267. return;
  268.  
  269. RecountClasses();
  270.  
  271. if (CurrentCount[team][class] >= MaxClass[clientcount][team][class])
  272. {
  273. EmitSoundToClient(client, g_sSounds[class]);
  274.  
  275. if (MaxClass[clientcount][team][class] == 0)
  276. {
  277. PrintToChat(client, "\x04[MaxClass]\x01 %t", "Not allowed", ClassNames[class]);
  278. PrintCenterText(client, "%t", "Not allowed", ClassNames[class]);
  279. }
  280. else
  281. {
  282. PrintToChat(client, "\x04[MaxClass]\x01 %t", "Classfull", ClassNames[class]);
  283. PrintCenterText(client, "%t", "Classfull", ClassNames[class]);
  284. }
  285.  
  286. //If the user just connected to server, his class is 0, with is nothing, let's just pick a random class
  287. if (oldclass == TFClass_Unknown)
  288. oldclass = FindUnusedClass(team, clientcount);
  289.  
  290. SwitchClientClass(client, oldclass);
  291. }
  292. }
  293.  
  294. TFClassType:FindUnusedClass(TFTeam:team, clientcount)
  295. {
  296. // Start with a random class each time
  297. new TFClassType:pick = TFClassType:GetRandomInt(1,9);
  298. new TFClassType:c = pick;
  299. for (;;)
  300. {
  301. if ((MaxClass[clientcount][team][c] == -1) || (CurrentCount[team][c] < MaxClass[clientcount][team][c]) )
  302. return c;
  303. else
  304. {
  305. c++;
  306. if (c > TFClass_Engineer) // Wrap back to 1st class
  307. c = TFClass_Scout;
  308.  
  309. if (c == pick) // if we hit the initial class, all classes must be full
  310. break;
  311. }
  312. }
  313. return TFClass_Unknown;
  314. }
  315.  
  316. RecountClasses()
  317. {
  318. for (new TFClassType:c=TFClass_Unknown; c<=TFClass_Engineer; c++)
  319. {
  320. CurrentCount[TFTeam_Red][c] = 0;
  321. CurrentCount[TFTeam_Blue][c] = 0;
  322. }
  323.  
  324. new bool:LookForAdmin = GetConVarBool(CheckAdmins) && !GetConVarBool(CountAdmins);
  325.  
  326. for (new i=1; i<=maxplayers; i++)
  327. {
  328. if (IsClientInGame(i))
  329. {
  330. if (LookForAdmin)
  331. {
  332. if (CheckForAdmin( i ))
  333. continue;
  334. }
  335. CurrentCount[ TFTeam:GetClientTeam(i) ][ TF2_GetPlayerClass(i) ]++;
  336. }
  337. }
  338. }
  339.  
  340. bool:StartReadingFromTable()
  341. {
  342. decl String:file[PLATFORM_MAX_PATH];
  343. decl String:config[PLATFORM_MAX_PATH];
  344. decl String:mapname[32];
  345. GetConVarString(ConfigFileName, config, sizeof(config));
  346. BuildPath(Path_SM, file, sizeof(file),"configs/%s", config);
  347.  
  348. if (!FileExists(file))
  349. BuildPath(Path_SM, file, sizeof(file),"configs/%s", "MaxClass.txt");
  350.  
  351. if (!FileExists(file))
  352. {
  353. LogError("[MaxClass] Class manager is not running! Could not find file %s", file);
  354. isrunning = false;
  355. return false;
  356. }
  357.  
  358. new Handle:kv = CreateKeyValues("MaxClassPlayers");
  359. FileToKeyValues(kv, file);
  360.  
  361. //Get in the first sub-key, first look for the map, then look for default
  362. GetCurrentMap(mapname, sizeof(mapname));
  363. if (!KvJumpToKey(kv, mapname))
  364. {
  365. // Check for map type!
  366. SplitString(mapname, "_", mapname, sizeof(mapname));
  367.  
  368. if (!KvJumpToKey(kv, mapname))
  369. {
  370. if (!KvJumpToKey(kv, "default"))
  371. {
  372. LogError("[MaxClass] Class manager is not running! Could not find where to read from file");
  373. isrunning = false;
  374. return false;
  375. }
  376. else
  377. LogMessage("Loading class config for default");
  378. }
  379. else
  380. LogMessage("Loading class config from map type: %s", mapname);
  381. }
  382. else
  383. LogMessage("Loading class config from map: %s", mapname);
  384.  
  385. //There is nothing else that can give errors, the pluggin in running!
  386. isrunning = true;
  387.  
  388. new MaxPlayers[TFClassType + TFClassType:1], breakpoint, iStart, iEnd, i, TFTeam:a;
  389. decl String:buffer[64],String:start[32], String:end[32];
  390. new redblue[TFTeam];
  391.  
  392. //Reset all numbers to -1
  393. for (i=0; i<10; i++)
  394. {
  395. MaxPlayers[i] = -1;
  396. }
  397.  
  398. for (i=0; i<=maxplayers; i++)
  399. {
  400. for (a=TFTeam_Unassigned; a <= TFTeam_Blue; a++)
  401. {
  402. MaxClass[i][a] = MaxPlayers;
  403. }
  404. }
  405.  
  406.  
  407. if (!KvGotoFirstSubKey(kv))
  408. {
  409. //If there is nothing in there, what there is to read?
  410. return true;
  411. }
  412.  
  413. do
  414. {
  415. KvGetSectionName(kv, buffer, sizeof(buffer));
  416.  
  417. //Collect all data
  418. MaxPlayers[TFClass_Scout] = KvGetNum(kv, TF_ClassNames[TFClass_Scout], -1);
  419. MaxPlayers[TFClass_Sniper] = KvGetNum(kv, TF_ClassNames[TFClass_Sniper], -1);
  420. MaxPlayers[TFClass_Soldier] = KvGetNum(kv, TF_ClassNames[TFClass_Soldier], -1);
  421. MaxPlayers[TFClass_DemoMan] = KvGetNum(kv, TF_ClassNames[TFClass_DemoMan], -1);
  422. MaxPlayers[TFClass_Medic] = KvGetNum(kv, TF_ClassNames[TFClass_Medic], -1);
  423. MaxPlayers[TFClass_Heavy] = KvGetNum(kv, TF_ClassNames[TFClass_Heavy], -1);
  424. MaxPlayers[TFClass_Pyro] = KvGetNum(kv, TF_ClassNames[TFClass_Pyro], -1);
  425. MaxPlayers[TFClass_Spy] = KvGetNum(kv, TF_ClassNames[TFClass_Spy], -1);
  426. MaxPlayers[TFClass_Engineer] = KvGetNum(kv, TF_ClassNames[TFClass_Engineer], -1);
  427.  
  428. //God... I hate having bad english, fix it if it does not find
  429. if (MaxPlayers[TFClass_Engineer] == -1)
  430. MaxPlayers[TFClass_Engineer] = KvGetNum(kv, "engenner", -1);
  431.  
  432. //Why am I doing the 4 teams if there are only 2?
  433. redblue[TFTeam_Red] = KvGetNum(kv, "team2", 1);
  434. redblue[TFTeam_Blue] = KvGetNum(kv, "team3", 1);
  435.  
  436. if (redblue[TFTeam_Red] == 1)
  437. redblue[TFTeam_Red] = KvGetNum(kv, "red", 1);
  438.  
  439. if (redblue[TFTeam_Blue] == 1)
  440. redblue[TFTeam_Blue] = KvGetNum(kv, "blue", 1);
  441.  
  442. if ((redblue[TFTeam_Red] + redblue[TFTeam_Blue]) == 0)
  443. continue;
  444.  
  445. //Just 1 number
  446. if (StrContains(buffer,"-") == -1)
  447. {
  448. iStart = CheckBoundries(StringToInt(buffer));
  449.  
  450. for (a=TFTeam_Unassigned; a<= TFTeam_Blue; a++)
  451. {
  452. if (redblue[a] == 1)
  453. MaxClass[iStart][a] = MaxPlayers;
  454. }
  455. //A range, like 1-5
  456. }
  457. else
  458. {
  459. //Break the "1-5" into "1" and "5"
  460. breakpoint = SplitString(buffer,"-",start,sizeof(buffer));
  461. strcopy(end,sizeof(end),buffer[breakpoint]);
  462. TrimString(start);
  463. TrimString(end);
  464.  
  465. //make "1" and "5" into integers
  466. //Check boundries, see if does not go out of the array limits
  467. iStart = CheckBoundries(StringToInt(start));
  468. iEnd = CheckBoundries(StringToInt(end));
  469.  
  470. //Copy data to the global array for each one in the range
  471. for (i= iStart; i<= iEnd;i++)
  472. {
  473. for (a=TFTeam_Unassigned; a<= TFTeam_Blue; a++)
  474. {
  475. if (redblue[a] == 1)
  476. MaxClass[i][a] = MaxPlayers;
  477. }
  478. }
  479. }
  480. } while (KvGotoNextKey(kv));
  481.  
  482. CloseHandle(kv);
  483.  
  484. return false;
  485. }
  486.  
  487. CheckBoundries(i)
  488. {
  489. if (i < 0)
  490. return 0;
  491. else if (i > MAXPLAYERS)
  492. return MAXPLAYERS;
  493. else
  494. return i;
  495. }
  496.  
  497. public Action:Command_PrintTable(client, args)
  498. {
  499. if (args < 1)
  500. {
  501. ReplyToCommand(client, "[SM] Usage: sm_classlimit <#team>");
  502. return Plugin_Handled;
  503. }
  504.  
  505. decl String:teamstr[64];
  506. GetCmdArg(1, teamstr, sizeof(teamstr));
  507. new i, TFTeam:team = TFTeam:StringToInt(teamstr);
  508.  
  509. StartReadingFromTable();
  510.  
  511. if (team < TFTeam_Red || team > TFTeam_Blue)
  512. {
  513. ReplyToCommand(client, "[SM] %d is not a valid team. 2=red and 3=blue.", team);
  514. return Plugin_Handled;
  515. }
  516.  
  517. if (client == 0)
  518. PrintToServer("Players Sco Sni Sol Dem Med Hea Pyr Spy Eng");
  519. else
  520. PrintToConsole(client,"Players Sco Sni Sol Dem Med Hea Pyr Spy Eng");
  521.  
  522. for (i=1; i<= maxplayers; i++)
  523. {
  524. if (client == 0)
  525. {
  526. PrintToServer("%d %d %d %d %d %d %d %d %d %d",
  527. i, MaxClass[i][team][TFClass_Scout], MaxClass[i][team][TFClass_Sniper],
  528. MaxClass[i][team][TFClass_Soldier],MaxClass[i][team][TFClass_DemoMan],
  529. MaxClass[i][team][TFClass_Medic],MaxClass[i][team][TFClass_Heavy],
  530. MaxClass[i][team][TFClass_Pyro],MaxClass[i][team][TFClass_Spy],
  531. MaxClass[i][team][TFClass_Engineer]);
  532. }
  533. else
  534. {
  535. PrintToConsole(client, "%d %d %d %d %d %d %d %d %d %d",
  536. i, MaxClass[i][team][TFClass_Scout], MaxClass[i][team][TFClass_Sniper],
  537. MaxClass[i][team][TFClass_Soldier],MaxClass[i][team][TFClass_DemoMan],
  538. MaxClass[i][team][TFClass_Medic],MaxClass[i][team][TFClass_Heavy],
  539. MaxClass[i][team][TFClass_Pyro],MaxClass[i][team][TFClass_Spy],
  540. MaxClass[i][team][TFClass_Engineer]);
  541. }
  542. }
  543.  
  544. return Plugin_Handled;
  545. }
  546.  
  547. stock SwitchClientClass(client, TFClassType:class)
  548. {
  549. #if DEBUG == 1
  550. LogMessage("Changing class: %N ---- %d", client, class);
  551. #endif
  552. //FakeClientCommand(client, "joinclass %s", TF_ClassNames[class]);
  553. //FakeClientCommandEx(client, "joinclass %s", TF_ClassNames[class]);
  554. TF2_SetPlayerClass(client, class, false, true);
  555. ShowVGUIPanel(client, GetClientTeam(client) == 3 ? "class_blue" : "class_red");
  556. }
  557.  
  558.  
  559. stock bool:CheckForAdmin( client )
  560. {
  561. new String:Flag[16];
  562. GetConVarString(CVAR_Adminflag, Flag, sizeof(Flag));
  563. if (client == 0 || GetUserFlagBits(client)&ReadFlagString(Flag) > 0 || GetUserFlagBits(client)&ADMFLAG_ROOT > 0)
  564. {
  565. return true;
  566. }
  567. else return false;
  568. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement