Advertisement
Guest User

Untitled

a guest
Jul 21st, 2015
57
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 68.96 KB | None | 0 0
  1. /*
  2. -----------------------------------------------------------------------------
  3. LEFT 4 DEAD STATS - SOURCEMOD PLUGIN
  4. -----------------------------------------------------------------------------
  5. Code Written By msleeper (c) 2009
  6. Visit http://www.msleeper.com/ for more info!
  7. -----------------------------------------------------------------------------
  8. This is a ranking/stat tracking system for Left 4 Dead Co-op. It will track
  9. certain actions, such as giving a teammate Pills or rescuing them from a
  10. Hunter, as well as tracking kills of the types of Infected. The goal of the
  11. stats is both to rank players against one another, but also to promote
  12. teamwork by awarding more points for completing team-specific goals rather
  13. than simply basing on kills.
  14.  
  15. You can access your basic rank information by typing "rank" or "/rank" in
  16. the chat area. You can access the Top 10 Players by typing "top10" or
  17. "/top10" in the chat area.
  18.  
  19. The plugin ONLY works in Co-op mode, in every difficulty but Easy. Stats
  20. will automatically stop tracking if any of these conditions are met:
  21. . Game is in Easy difficulty
  22. . Game is in Versus mode
  23. . sv_cheats is set to "1"
  24. . There are not enough Human players, as determined by a Cvar
  25. . The Database connection has failed
  26.  
  27. The webstats portion provides more in-depth stat information, both for
  28. individual players as well as the server as a whole, with full campaign and
  29. map stat info. More information about webstats can be found in the webstats
  30. ZIP file.
  31.  
  32. Special thanks to DopeFish, Icettiflow, jasonfrog, and liv3d for helping me
  33. beta test prior to full public release.
  34.  
  35. Thank you and enjoy!
  36. - msleeper
  37. -----------------------------------------------------------------------------
  38. To Do List (Minor)
  39. . Fix minor bug with Campaign tracking
  40. . Add multilingual support
  41.  
  42. To Do List (Major)
  43. . Add "Squad" system
  44. . Add grace period and cooldown to Friendly Fire
  45. . Add achievement system
  46. . Add Survival support
  47. . Add Versus support
  48. -----------------------------------------------------------------------------
  49. Version History
  50.  
  51. -- 0.1.0 (1/8/09)
  52. . Initial closed beta release!
  53.  
  54. -- 0.1.1 (1/9/09)
  55. . Silenced plugin disable alerts, except "not enough Human players" alert.
  56. . Removed misc debug message.
  57. . Fixed misc error log messages.
  58.  
  59. -- 0.1.2 (1/12/09)
  60. . Testing new interstitial SQL method for Common Infected kill tracking.
  61. Instead of sending a SQL transaction after each kill, only send SQL during
  62. the update period when Common Infected points are displayed. The high
  63. amount of SQL traffic causes noticible lag during high combat periods,
  64. such as when a mob attacks.
  65.  
  66. -- 0.1.6 (1/13/09)
  67. . Fully implimented interstitial SQL update for Common Infected. Added
  68. check to send update when a player disconnects, so no points are lost if
  69. they disconnect between interstitial updates.
  70. . Cleaned up code a bit.
  71. . Improved player name sanitation.
  72. . Changed new players playtime to init at 1 instead of 0.
  73. . Changed point amounts from static values to Cvar values.
  74. . Added Cvar to control how stats messages are displayed to players:
  75. 0 = Stats messages are off
  76. 1 = Messages sent to the player who earned them only
  77. 2 = Same as 1, but Headshots on Special Infected are globally anounced
  78. 3 = All messages are global. Warning: This is VERY annoying!
  79. . Added Cvar to control whether Medkit points are given based on the amount
  80. healed, or a static amount set by Cvar. Amount healed is 0.5x in Normal,
  81. 1x in Advanced, and 2x in Expert.
  82. . Added check to disable stats if the Database connection has failed.
  83.  
  84. -- 0.1.8 (1/15/09)
  85. . Further cleaned up code.
  86. . Optimized UTF8 character support.
  87. . Removed log message on successful database connection.
  88. . Added threaded query to player inserting, to check if the player already
  89. exists and if so, don't attempt to INSERT IGNORE them.
  90. . Reformatted rank panels.
  91. . Added Cvar to list community site for more information in "rank" panel.
  92. . Removed table generation from the plugin. This will be handled by a
  93. setup script provided with webstats.
  94.  
  95. -- 0.1.9 (1/16/09)
  96. . Changed all updates to threaded queries, to fix lag caused by updates and
  97. server timeouts in rare cases.
  98.  
  99. -- 1.0.0 (1/18/09)
  100. . Initial public release!
  101.  
  102. -- 1.1.0 (1/25/09)
  103. . Fixed change in update/Common Infected announcement timer not obeying
  104. changes to the cvar, except when in the config file and the plugin/server
  105. is restarted.
  106. . Fixed team chat not picking up chat triggers.
  107. . Added invalid database connection checking to rank/top10 panel display.
  108. . Fixed bug where players would be inserted into the database, but their
  109. user data would not get updated and they would appear blank.
  110. . Removed plugin version from showing up in the config file.
  111. . Removed "Not enough Humans" message when in Versus.
  112. . Made rank panel display after client connect at the start of each map,
  113. and added cvar to enable/disable this.
  114. . Made "Playtime" display hours if the playtime is longer than 60 minutes.
  115. . Added cvar to hide the display of public chat triggers.
  116. -- 1.1.1 (4/22/09)
  117. . Changed "IsVersus()" function to "InvalidGameMode()" to fix deadstop bug
  118. with the Survival update. This is part of paving the way to Survival
  119. and Versus stats in a future release.
  120. . Fixed various error messages in error logs.
  121. . Fixed stats panel to now work properly for people with certain characters
  122. in their name not making it display.
  123. . Fixed (again) a certain case where blank users would be inserted.
  124. . Added cvar to enable/disable showing of the rank panel when not in a valid
  125. gamemode, showing of disabled messages, and letting players use the chat
  126. commands.
  127. . Added some stat whoring checks to the plugin:
  128. . A maximum amount of points can be earned in a single map
  129. . Only 3 Tanks may be awarded during a single map
  130. . Fixed minor bug with Healthpack point award not giving full amount.
  131. . Added a few currently unused cvars for future features:
  132. . sm_l4dstats_dbprefix -- Prefix to be used for database tables
  133. . sm_l4dstats_enablecoop -- Enable stats for Coop mode
  134. . sm_l4dstats_enablesv -- Enable stats for Survival mode
  135. . sm_l4dstats_enableversus -- Enable stats for Versus mode
  136. . sm_l4dstats_leaderboardtime -- Duration in days to show players top
  137. times on the Survival leaderboards
  138. -----------------------------------------------------------------------------
  139. */
  140.  
  141. #pragma semicolon 1
  142.  
  143. #include <sourcemod>
  144. #include <clientprefs>
  145.  
  146. #define PLUGIN_VERSION "1.1.1"
  147. #define MAX_LINE_WIDTH 64
  148.  
  149. // Database handle
  150. new Handle:db = INVALID_HANDLE;
  151.  
  152. // Update Timer handle
  153. new Handle:UpdateTimer = INVALID_HANDLE;
  154.  
  155. // Disable check Cvar handles
  156. new Handle:cvar_Difficulty = INVALID_HANDLE;
  157. new Handle:cvar_Gamemode = INVALID_HANDLE;
  158. new Handle:cvar_Cheats = INVALID_HANDLE;
  159.  
  160. // Game event booleans
  161. new bool:PlayerVomited = false;
  162. new bool:PlayerVomitedIncap = false;
  163. new bool:PanicEvent = false;
  164. new bool:PanicEventIncap = false;
  165. new bool:CampaignOver = false;
  166. new bool:WitchExists = false;
  167. new bool:WitchDisturb = false;
  168.  
  169. // Anti-Stat Whoring vars
  170. new CurrentPoints[MAXPLAYERS + 1];
  171. new TankCount = 0;
  172.  
  173. // Cvar handles
  174. new Handle:cvar_HumansNeeded = INVALID_HANDLE;
  175. new Handle:cvar_UpdateRate = INVALID_HANDLE;
  176. new Handle:cvar_AnnounceMode = INVALID_HANDLE;
  177. new Handle:cvar_MedkitMode = INVALID_HANDLE;
  178. new Handle:cvar_SiteURL = INVALID_HANDLE;
  179. new Handle:cvar_RankOnJoin = INVALID_HANDLE;
  180. new Handle:cvar_SilenceChat = INVALID_HANDLE;
  181. new Handle:cvar_DisabledMessages = INVALID_HANDLE;
  182. new Handle:cvar_MaxPoints = INVALID_HANDLE;
  183. new Handle:cvar_DbPrefix = INVALID_HANDLE;
  184. new Handle:cvar_LeaderboardTime = INVALID_HANDLE;
  185.  
  186. new Handle:cvar_EnableCoop = INVALID_HANDLE;
  187. new Handle:cvar_EnableSv = INVALID_HANDLE;
  188. new Handle:cvar_EnableVersus = INVALID_HANDLE;
  189.  
  190. new Handle:cvar_Infected = INVALID_HANDLE;
  191. new Handle:cvar_Hunter = INVALID_HANDLE;
  192. new Handle:cvar_Smoker = INVALID_HANDLE;
  193. new Handle:cvar_Boomer = INVALID_HANDLE;
  194.  
  195. new Handle:cvar_Pills = INVALID_HANDLE;
  196. new Handle:cvar_Medkit = INVALID_HANDLE;
  197. new Handle:cvar_SmokerDrag = INVALID_HANDLE;
  198. new Handle:cvar_ChokePounce = INVALID_HANDLE;
  199. new Handle:cvar_Revive = INVALID_HANDLE;
  200. new Handle:cvar_Rescue = INVALID_HANDLE;
  201. new Handle:cvar_Protect = INVALID_HANDLE;
  202.  
  203. new Handle:cvar_Tank = INVALID_HANDLE;
  204. new Handle:cvar_Panic = INVALID_HANDLE;
  205. new Handle:cvar_BoomerMob = INVALID_HANDLE;
  206. new Handle:cvar_SafeHouse = INVALID_HANDLE;
  207. new Handle:cvar_Witch = INVALID_HANDLE;
  208. new Handle:cvar_Campaign = INVALID_HANDLE;
  209.  
  210. new Handle:cvar_FFire = INVALID_HANDLE;
  211. new Handle:cvar_FIncap = INVALID_HANDLE;
  212. new Handle:cvar_FKill = INVALID_HANDLE;
  213. new Handle:cvar_InSafeRoom = INVALID_HANDLE;
  214. new Handle:cvar_Restart = INVALID_HANDLE;
  215.  
  216. // Clientprefs handles
  217. new Handle:ClientMaps = INVALID_HANDLE;
  218.  
  219. // Rank panel vars
  220. new RankTotal = 0;
  221. new ClientRank[MAXPLAYERS + 1];
  222. new ClientPoints[MAXPLAYERS + 1];
  223.  
  224. // Misc arrays
  225. new TimerPoints[MAXPLAYERS + 1];
  226. new TimerKills[MAXPLAYERS + 1];
  227. new TimerHeadshots[MAXPLAYERS + 1];
  228. new Pills[4096];
  229.  
  230. // Plugin Info
  231. public Plugin:myinfo =
  232. {
  233. name = "L4D Stats",
  234. author = "msleeper",
  235. description = "Player Stats and Ranking in Left 4 Dead Co-op",
  236. version = PLUGIN_VERSION,
  237. url = "http://www.msleeper.com/"
  238. };
  239.  
  240. // Here we go!
  241. public OnPluginStart()
  242. {
  243. // Plugin version public Cvar
  244. CreateConVar("sm_l4dstats_version", PLUGIN_VERSION, "L4D Stats Version", FCVAR_PLUGIN|FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY|FCVAR_DONTRECORD);
  245.  
  246. // Init MySQL connections
  247. ConnectDB();
  248.  
  249. // Disable setting Cvars
  250. cvar_Difficulty = FindConVar("z_difficulty");
  251. cvar_Gamemode = FindConVar("mp_gamemode");
  252. cvar_Cheats = FindConVar("sv_cheats");
  253.  
  254. // Config/control Cvars
  255. cvar_HumansNeeded = CreateConVar("sm_l4dstats_minhumans", "2", "Minimum Human players before stats will be enabled", FCVAR_PLUGIN, true, 1.0, true, 4.0);
  256. cvar_UpdateRate = CreateConVar("sm_l4dstats_updaterate", "90", "Number of seconds between Common Infected point earn announcement/update", FCVAR_PLUGIN, true, 30.0);
  257. cvar_AnnounceMode = CreateConVar("sm_l4dstats_announcemode", "2", "Chat announcment mode. 0 = Off, 1 = Player Only, 2 = Player Only w/ Public Headshots, 3 = All Public", FCVAR_PLUGIN, true, 0.0, true, 3.0);
  258. cvar_MedkitMode = CreateConVar("sm_l4dstats_medkitmode", "0", "Medkit point award mode. 0 = Based on amount healed, 1 = Static amount", FCVAR_PLUGIN, true, 0.0, true, 1.0);
  259. cvar_SiteURL = CreateConVar("sm_l4dstats_siteurl", "", "Community site URL, for rank panel display", FCVAR_PLUGIN);
  260. cvar_RankOnJoin = CreateConVar("sm_l4dstats_rankonjoin", "1", "Display player's rank when they connect. 0 = Disable, 1 = Enable", FCVAR_PLUGIN, true, 0.0, true, 1.0);
  261. cvar_SilenceChat = CreateConVar("sm_l4dstats_silencechat", "0", "Silence chat triggers. 0 = Show chat triggers, 1 = Silence chat triggers", FCVAR_PLUGIN, true, 0.0, true, 1.0);
  262. cvar_DisabledMessages = CreateConVar("sm_l4dstats_disabledmessages", "1", "Show 'Stats Disabled' messages, allow chat commands to work when stats disabled. 0 = Hide messages/disable chat, 1 = Show messages/allow chat", FCVAR_PLUGIN, true, 0.0, true, 1.0);
  263. cvar_MaxPoints = CreateConVar("sm_l4dstats_maxpoints", "500", "Maximum number of points that can be earned in a single map. Normal = x1, Adv = x2, Expert = x3", FCVAR_PLUGIN, true, 500.0);
  264. cvar_DbPrefix = CreateConVar("sm_l4dstats_dbprefix", "", "Prefix for your stats tables", FCVAR_PLUGIN);
  265. cvar_LeaderboardTime = CreateConVar("sm_l4dstats_leaderboardtime", "14", "Time in days to show Survival Leaderboard times", true, 1.0);
  266.  
  267. // Game mode Cvars
  268. cvar_EnableCoop = CreateConVar("sm_l4dstats_enablecoop", "1", "Enable/Disable coop stat tracking", FCVAR_PLUGIN, true, 0.0, true, 1.0);
  269. cvar_EnableSv = CreateConVar("sm_l4dstats_enablesv", "1", "Enable/Disable survival stat tracking", FCVAR_PLUGIN, true, 0.0, true, 1.0);
  270. cvar_EnableVersus = CreateConVar("sm_l4dstats_enableversus", "1", "Enable/Disable versus stat tracking", FCVAR_PLUGIN, true, 0.0, true, 1.0);
  271.  
  272. // Infected point Cvars
  273. cvar_Infected = CreateConVar("sm_l4dstats_infected", "1", "Base score for killing a Common Infected", FCVAR_PLUGIN, true, 1.0);
  274. cvar_Hunter = CreateConVar("sm_l4dstats_hunter", "2", "Base score for killing a Hunter", FCVAR_PLUGIN, true, 1.0);
  275. cvar_Smoker = CreateConVar("sm_l4dstats_smoker", "3", "Base score for killing a Smoker", FCVAR_PLUGIN, true, 1.0);
  276. cvar_Boomer = CreateConVar("sm_l4dstats_boomer", "5", "Base score for killing a Boomer", FCVAR_PLUGIN, true, 1.0);
  277.  
  278. // Misc personal gain Cvars
  279. cvar_Pills = CreateConVar("sm_l4dstats_pills", "15", "Base score for giving Pills to a friendly", FCVAR_PLUGIN, true, 1.0);
  280. cvar_Medkit = CreateConVar("sm_l4dstats_medkit", "20", "Base score for using a Medkit on a friendly", FCVAR_PLUGIN, true, 1.0);
  281. cvar_SmokerDrag = CreateConVar("sm_l4dstats_smokerdrag", "5", "Base score for saving a friendly from a Smoker Tongue Drag", FCVAR_PLUGIN, true, 1.0);
  282. cvar_ChokePounce = CreateConVar("sm_l4dstats_chokepounce", "10", "Base score for saving a friendly from a Hunter Pounce / Smoker Choke", FCVAR_PLUGIN, true, 1.0);
  283. cvar_Revive = CreateConVar("sm_l4dstats_revive", "15", "Base score for Revive a friendly from Incapacitated state", FCVAR_PLUGIN, true, 1.0);
  284. cvar_Rescue = CreateConVar("sm_l4dstats_rescue", "10", "Base score for Rescue a friendly from a closet", FCVAR_PLUGIN, true, 1.0);
  285. cvar_Protect = CreateConVar("sm_l4dstats_protect", "3", "Base score for Protect a friendly in combat", FCVAR_PLUGIN, true, 1.0);
  286.  
  287. // Team gain Cvars
  288. cvar_Tank = CreateConVar("sm_l4dstats_tank", "25", "Base team score for killing a Tank", FCVAR_PLUGIN, true, 1.0);
  289. cvar_Panic = CreateConVar("sm_l4dstats_panic", "25", "Base team score for surviving a Panic Event with no Incapacitations", FCVAR_PLUGIN, true, 1.0);
  290. cvar_BoomerMob = CreateConVar("sm_l4dstats_boomermob", "10", "Base team score for surviving a Boomer Mob with no Incapacitations", FCVAR_PLUGIN, true, 1.0);
  291. cvar_SafeHouse = CreateConVar("sm_l4dstats_safehouse", "10", "Base score for reaching a Safe House", FCVAR_PLUGIN, true, 1.0);
  292. cvar_Witch = CreateConVar("sm_l4dstats_witch", "10", "Base score for Not Disturbing a Witch", FCVAR_PLUGIN, true, 1.0);
  293. cvar_Campaign = CreateConVar("sm_l4dstats_campaign", "5", "Base score for Completing a Campaign", FCVAR_PLUGIN, true, 1.0);
  294.  
  295. // Point loss Cvars
  296. cvar_FFire = CreateConVar("sm_l4dstats_ffire", "25", "Base score for Friendly Fire", FCVAR_PLUGIN, true, 1.0);
  297. cvar_FIncap = CreateConVar("sm_l4dstats_fincap", "75", "Base score for a Friendly Incap", FCVAR_PLUGIN, true, 1.0);
  298. cvar_FKill = CreateConVar("sm_l4dstats_fkill", "250", "Base score for a Friendly Kill", FCVAR_PLUGIN, true, 1.0);
  299. cvar_InSafeRoom = CreateConVar("sm_l4dstats_insaferoom", "5", "Base score for letting Infected in the Safe Room", FCVAR_PLUGIN, true, 1.0);
  300. cvar_Restart = CreateConVar("sm_l4dstats_restart", "100", "Base score for a Round Restart", FCVAR_PLUGIN, true, 1.0);
  301.  
  302. // Make that config!
  303. AutoExecConfig(true, "l4d_stats");
  304.  
  305. // Personal Gain Events
  306. HookEvent("player_death", event_PlayerDeath);
  307. HookEvent("infected_death", event_InfectedDeath);
  308. HookEvent("tank_killed", event_TankKilled);
  309. HookEvent("weapon_given", event_GivePills);
  310. HookEvent("heal_success", event_HealPlayer);
  311. HookEvent("revive_success", event_RevivePlayer);
  312. HookEvent("tongue_pull_stopped", event_TongueSave);
  313. HookEvent("choke_stopped", event_ChokeSave);
  314. HookEvent("pounce_stopped", event_PounceSave);
  315.  
  316. // Personal Loss Events
  317. HookEvent("friendly_fire", event_FriendlyFire);
  318. HookEvent("player_incapacitated", event_PlayerIncap);
  319.  
  320. // Team Gain Events
  321. HookEvent("finale_vehicle_leaving", event_CampaignWin);
  322. HookEvent("map_transition", event_MapTransition);
  323. HookEvent("create_panic_event", event_PanicEvent);
  324. HookEvent("player_now_it", event_PlayerBlind);
  325. HookEvent("player_no_longer_it", event_PlayerBlindEnd);
  326.  
  327. // Team Loss Events / Misc. Events
  328. HookEvent("award_earned", event_Award);
  329. HookEvent("witch_spawn", event_WitchSpawn);
  330. HookEvent("witch_harasser_set", event_WitchDisturb);
  331.  
  332. // Startup the plugin's timers
  333. CreateTimer(1.0, InitPlayers);
  334. CreateTimer(60.0, timer_UpdatePlayers, INVALID_HANDLE, TIMER_REPEAT);
  335. UpdateTimer = CreateTimer(GetConVarFloat(cvar_UpdateRate), timer_ShowTimerScore, INVALID_HANDLE, TIMER_REPEAT);
  336. HookConVarChange(cvar_UpdateRate, action_TimerChanged);
  337.  
  338. // Clientprefs settings
  339. ClientMaps = RegClientCookie("l4dstats_maps", "Number of maps completed in a campaign", CookieAccess_Private);
  340.  
  341. // Register chat commands for rank panels
  342. RegConsoleCmd("say", cmd_Say);
  343. RegConsoleCmd("say_team", cmd_Say);
  344.  
  345. // Register console commands for rank panels
  346. RegConsoleCmd("sm_rank", cmd_ShowRank);
  347. RegConsoleCmd("sm_top10", cmd_ShowTop10);
  348. }
  349.  
  350. // Reset all boolean variables when a map changes.
  351.  
  352. public OnMapStart()
  353. {
  354. ResetVars();
  355. }
  356.  
  357. // Init player on connect, and update total rank and client rank.
  358.  
  359. public OnClientPostAdminCheck(client)
  360. {
  361. if (db == INVALID_HANDLE)
  362. return;
  363.  
  364. if (IsClientBot(client))
  365. return;
  366.  
  367. decl String:SteamID[MAX_LINE_WIDTH];
  368. GetClientAuthString(client, SteamID, sizeof(SteamID));
  369.  
  370. CheckPlayerDB(client);
  371.  
  372. TimerPoints[client] = 0;
  373. TimerKills[client] = 0;
  374. TimerHeadshots[client] = 0;
  375.  
  376. SQL_TQuery(db, GetRankTotal, "SELECT COUNT(*) FROM players", client);
  377.  
  378. decl String:query[256];
  379. Format(query, sizeof(query), "SELECT points FROM players WHERE steamid = '%s'", SteamID);
  380. SQL_TQuery(db, GetClientPoints, query, client);
  381.  
  382. CreateTimer(10.0, RankConnect, client);
  383. }
  384.  
  385. // Show rank on connect.
  386.  
  387. public Action:RankConnect(Handle:timer, any:value)
  388. {
  389. if (GetConVarBool(cvar_RankOnJoin))
  390. cmd_ShowRank(value, 0);
  391. }
  392.  
  393. // Update the player's interstitial stats, since they may have
  394. // gotten points between the last update and when they disconnect.
  395.  
  396. public OnClientDisconnect(client)
  397. {
  398. if (IsClientBot(client))
  399. return;
  400.  
  401. InterstitialPlayerUpdate(client);
  402. }
  403.  
  404. // Update the Update Timer when the Cvar is changed.
  405.  
  406. public action_TimerChanged(Handle:convar, const String:oldValue[], const String:newValue[])
  407. {
  408. if (convar == cvar_UpdateRate)
  409. {
  410. CloseHandle(UpdateTimer);
  411.  
  412. new NewTime = StringToInt(newValue);
  413. UpdateTimer = CreateTimer(float(NewTime), timer_ShowTimerScore, INVALID_HANDLE, TIMER_REPEAT);
  414. }
  415. }
  416.  
  417. // Make connection to database.
  418.  
  419. public ConnectDB()
  420. {
  421. if (SQL_CheckConfig("l4dstats"))
  422. {
  423. new String:Error[256];
  424. db = SQL_Connect("l4dstats", true, Error, sizeof(Error));
  425.  
  426. if (db == INVALID_HANDLE)
  427. LogError("Failed to connect to database: %s", Error);
  428. else
  429. SendSQLUpdate("SET NAMES 'utf8'");
  430. }
  431. else
  432. LogError("Database.cfg missing 'l4dstats' entry!");
  433. }
  434.  
  435. // Perform player init.
  436.  
  437. public Action:InitPlayers(Handle:timer)
  438. {
  439. if (db == INVALID_HANDLE)
  440. return;
  441.  
  442. SQL_TQuery(db, GetRankTotal, "SELECT COUNT(*) FROM players", 0);
  443.  
  444. decl String:SteamID[MAX_LINE_WIDTH];
  445. decl String:query[256];
  446. new maxplayers = GetMaxClients();
  447.  
  448. for (new i = 1; i <= maxplayers; i++)
  449. {
  450. if (IsClientConnected(i) && IsClientInGame(i) && !IsClientBot(i))
  451. {
  452. CheckPlayerDB(i);
  453.  
  454. GetClientAuthString(i, SteamID, sizeof(SteamID));
  455. Format(query, sizeof(query), "SELECT points FROM players WHERE steamid = '%s'", SteamID);
  456. SQL_TQuery(db, GetClientPoints, query, i);
  457.  
  458. TimerPoints[i] = 0;
  459. TimerKills[i] = 0;
  460. }
  461. }
  462. }
  463.  
  464. // Check if a player is already in the DB, and update their timestamp and playtime.
  465.  
  466. CheckPlayerDB(client)
  467. {
  468. if (StatsDisabled())
  469. return;
  470.  
  471. if (IsClientBot(client))
  472. return;
  473.  
  474. decl String:SteamID[MAX_LINE_WIDTH];
  475. GetClientAuthString(client, SteamID, sizeof(SteamID));
  476.  
  477. decl String:query[512];
  478. Format(query, sizeof(query), "SELECT steamid FROM players WHERE steamid = '%s'", SteamID);
  479. SQL_TQuery(db, InsertPlayerDB, query, client);
  480. }
  481.  
  482. // Insert a player into the database if they do not already exist.
  483.  
  484. public InsertPlayerDB(Handle:owner, Handle:hndl, const String:error[], any:data)
  485. {
  486. if (db == INVALID_HANDLE)
  487. return;
  488.  
  489. new client = data;
  490.  
  491. if (!client || hndl == INVALID_HANDLE)
  492. return;
  493.  
  494. if (StatsDisabled())
  495. return;
  496.  
  497. if (!SQL_GetRowCount(hndl))
  498. {
  499. new String:SteamID[MAX_LINE_WIDTH];
  500. GetClientAuthString(client, SteamID, sizeof(SteamID));
  501.  
  502. new String:query[512];
  503. Format(query, sizeof(query), "INSERT IGNORE INTO players SET steamid = '%s'", SteamID);
  504. SQL_TQuery(db, SQLErrorCheckCallback, query);
  505. }
  506.  
  507. UpdatePlayer(client);
  508. }
  509.  
  510. // Run a SQL query, used for UPDATE's only.
  511.  
  512. public SendSQLUpdate(String:query[])
  513. {
  514. if (db == INVALID_HANDLE)
  515. return;
  516.  
  517. SQL_TQuery(db, SQLErrorCheckCallback, query);
  518. }
  519.  
  520. // Report error on sql query;
  521.  
  522. public SQLErrorCheckCallback(Handle:owner, Handle:hndl, const String:error[], any:data)
  523. {
  524. if (db == INVALID_HANDLE)
  525. return;
  526.  
  527. if(!StrEqual("", error))
  528. LogError("SQL Error: %s", error);
  529. }
  530.  
  531. // Perform player update of name, playtime, and timestamp.
  532.  
  533. public UpdatePlayer(client)
  534. {
  535. decl String:SteamID[MAX_LINE_WIDTH];
  536. GetClientAuthString(client, SteamID, sizeof(SteamID));
  537.  
  538. decl String:Name[MAX_LINE_WIDTH];
  539. GetClientName(client, Name, sizeof(Name));
  540. ReplaceString(Name, sizeof(Name), "<?php", "");
  541. ReplaceString(Name, sizeof(Name), "<?PHP", "");
  542. ReplaceString(Name, sizeof(Name), "?>", "");
  543. ReplaceString(Name, sizeof(Name), "\\", "");
  544. ReplaceString(Name, sizeof(Name), "\"", "");
  545. ReplaceString(Name, sizeof(Name), "'", "");
  546. ReplaceString(Name, sizeof(Name), ";", "");
  547. ReplaceString(Name, sizeof(Name), "ґ", "");
  548. ReplaceString(Name, sizeof(Name), "`", "");
  549.  
  550. decl String:query[512];
  551. Format(query, sizeof(query), "UPDATE players SET lastontime = UNIX_TIMESTAMP(), playtime = playtime + 1, name = '%s' WHERE steamid = '%s'", Name, SteamID);
  552. SendSQLUpdate(query);
  553. }
  554.  
  555. // Perform a map stat update.
  556. public UpdateMapStat(String:Field[MAX_LINE_WIDTH], Score)
  557. {
  558. if (!Score)
  559. Score = 1;
  560.  
  561. decl String:MapName[64];
  562. GetCurrentMap(MapName, sizeof(MapName));
  563.  
  564. decl String:DiffSQL[MAX_LINE_WIDTH];
  565. decl String:Difficulty[MAX_LINE_WIDTH];
  566. GetConVarString(cvar_Difficulty, Difficulty, sizeof(Difficulty));
  567.  
  568. if (StrEqual(Difficulty, "Normal")) Format(DiffSQL, sizeof(DiffSQL), "nor");
  569. else if (StrEqual(Difficulty, "Hard")) Format(DiffSQL, sizeof(DiffSQL), "adv");
  570. else if (StrEqual(Difficulty, "Impossible")) Format(DiffSQL, sizeof(DiffSQL), "exp");
  571. else return;
  572.  
  573. decl String:FieldSQL[MAX_LINE_WIDTH];
  574. Format(FieldSQL, sizeof(FieldSQL), "%s_%s", Field, DiffSQL);
  575.  
  576. decl String:query[512];
  577. Format(query, sizeof(query), "UPDATE maps SET %s = %s + %i WHERE name = '%s'", FieldSQL, FieldSQL, Score, MapName);
  578. SendSQLUpdate(query);
  579. }
  580.  
  581. // Perform minutely updates of player database.
  582. // Reports Disabled message if in Versus, Easy mode, not enough Human players, and if cheats are active.
  583.  
  584. public Action:timer_UpdatePlayers(Handle:timer, Handle:hndl)
  585. {
  586. if (CheckHumans() && GetConVarBool(cvar_DisabledMessages))
  587. PrintToChatAll("\x04[\x03RANK\x04] \x01Left 4 Dead Stats are \x04DISABLED\x01, not enough Human players!");
  588.  
  589. if (StatsDisabled())
  590. return;
  591.  
  592. UpdateMapStat("playtime", 1);
  593.  
  594. new maxplayers = GetMaxClients();
  595. for (new i = 1; i <= maxplayers; i++)
  596. {
  597. if (IsClientConnected(i) && IsClientInGame(i) && !IsClientBot(i))
  598. CheckPlayerDB(i);
  599. }
  600. }
  601.  
  602. // Display common Infected scores to each player.
  603.  
  604. public Action:timer_ShowTimerScore(Handle:timer, Handle:hndl)
  605. {
  606. if (StatsDisabled())
  607. return;
  608.  
  609. new Mode = GetConVarInt(cvar_AnnounceMode);
  610. decl String:Name[MAX_LINE_WIDTH];
  611.  
  612. new maxplayers = GetMaxClients();
  613. for (new i = 1; i <= maxplayers; i++)
  614. {
  615. if (IsClientConnected(i) && IsClientInGame(i) && !IsClientBot(i))
  616. {
  617. // if (CurrentPoints[i] > GetConVarInt(cvar_MaxPoints))
  618. // continue;
  619.  
  620. if (TimerKills[i] > 0)
  621. {
  622. if (Mode == 1 || Mode == 2)
  623. {
  624. PrintToChat(i, "\x04[\x03RANK\x04] \x01You have earned \x04%i \x01points for killing \x05%i \x01Infected (Столько поинтов вы заработали за указанное кол-во бомжей)!", TimerPoints[i], TimerKills[i]);
  625. }
  626. else if (Mode == 3)
  627. {
  628. GetClientName(i, Name, sizeof(Name));
  629. PrintToChatAll("\x04[\x03RANK\x04] \x05%s \x01has earned \x04%i \x01points for killing \x05%i \x01Infected (...поинтов заработал за указанное кол-во бомжей)!", Name, TimerPoints[i], TimerKills[i]);
  630. }
  631. }
  632.  
  633. InterstitialPlayerUpdate(i);
  634. }
  635.  
  636. TimerPoints[i] = 0;
  637. TimerKills[i] = 0;
  638. TimerHeadshots[i] = 0;
  639. }
  640.  
  641. }
  642.  
  643. // Update a player's stats, used for interstitial updating.
  644.  
  645. public InterstitialPlayerUpdate(client)
  646. {
  647. decl String:ClientID[MAX_LINE_WIDTH];
  648. GetClientAuthString(client, ClientID, sizeof(ClientID));
  649.  
  650. new len = 0;
  651. decl String:query[1024];
  652. len += Format(query[len], sizeof(query)-len, "UPDATE players SET points = points + %i, ", TimerPoints[client]);
  653. len += Format(query[len], sizeof(query)-len, "kills = kills + %i, kill_infected = kill_infected + %i, ", TimerKills[client], TimerKills[client]);
  654. len += Format(query[len], sizeof(query)-len, "headshots = headshots + %i ", TimerHeadshots[client]);
  655. len += Format(query[len], sizeof(query)-len, "WHERE steamid = '%s'", ClientID);
  656. SendSQLUpdate(query);
  657.  
  658. UpdateMapStat("kills", TimerKills[client]);
  659. UpdateMapStat("points", TimerPoints[client]);
  660.  
  661. CurrentPoints[client] = CurrentPoints[client] + TimerPoints[client];
  662. }
  663.  
  664. // Player Death event. Used for killing AI Infected. +2 on headshot, and global announcement.
  665. // Team Kill code is in the awards section. Tank Kill code is in Tank section.
  666.  
  667. public Action:event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast)
  668. {
  669. if (StatsDisabled())
  670. return;
  671.  
  672. new Mode = GetConVarInt(cvar_AnnounceMode);
  673. new Attacker = GetClientOfUserId(GetEventInt(event, "attacker"));
  674.  
  675. if (GetEventBool(event, "attackerisbot") || !GetEventBool(event, "victimisbot"))
  676. return;
  677.  
  678. decl String:AttackerID[MAX_LINE_WIDTH];
  679. GetClientAuthString(Attacker, AttackerID, sizeof(AttackerID));
  680. decl String:AttackerName[MAX_LINE_WIDTH];
  681. GetClientName(Attacker, AttackerName, sizeof(AttackerName));
  682.  
  683. decl String:VictimName[MAX_LINE_WIDTH];
  684. GetEventString(event, "victimname", VictimName, sizeof(VictimName));
  685.  
  686. new Score = 0;
  687. decl String:InfectedType[8];
  688.  
  689. if (StrEqual(VictimName, "Hunter"))
  690. {
  691. Format(InfectedType, sizeof(InfectedType), "hunter");
  692. Score = ModifyScoreDifficulty(GetConVarInt(cvar_Hunter), 2, 3);
  693. }
  694. else if (StrEqual(VictimName, "Smoker"))
  695. {
  696. Format(InfectedType, sizeof(InfectedType), "smoker");
  697. Score = ModifyScoreDifficulty(GetConVarInt(cvar_Smoker), 2, 3);
  698. }
  699. else if (StrEqual(VictimName, "Boomer"))
  700. {
  701. Format(InfectedType, sizeof(InfectedType), "boomer");
  702. Score = ModifyScoreDifficulty(GetConVarInt(cvar_Boomer), 2, 3);
  703. }
  704. else
  705. return;
  706.  
  707. new String:Headshot[32];
  708. if (GetEventBool(event, "headshot"))
  709. {
  710. Format(Headshot, sizeof(Headshot), ", headshots = headshots + 1");
  711. Score = Score + 2;
  712. }
  713.  
  714. new len = 0;
  715. decl String:query[1024];
  716. len += Format(query[len], sizeof(query)-len, "UPDATE players SET points = points + %i, ", Score);
  717. len += Format(query[len], sizeof(query)-len, "kills = kills + 1, kill_%s = kill_%s + 1", InfectedType, InfectedType);
  718. len += Format(query[len], sizeof(query)-len, "%s WHERE steamid = '%s'", Headshot, AttackerID);
  719. SendSQLUpdate(query);
  720.  
  721. if (Mode)
  722. {
  723. if (GetEventBool(event, "headshot"))
  724. {
  725. if (Mode > 1)
  726. PrintToChatAll("\x04[\x03RANK\x04] \x05%s \x01has earned \x04%i \x01points for killing a \x05%s \x01with a \x04HEAD SHOT (...заработал поинты за убийства вместе с указаным кол-ом хедщотов)!", AttackerName, Score, VictimName);
  727. else
  728. PrintToChat(Attacker, "\x04[\x03RANK\x04] \x01You have earned \x04%i \x01points for killing a \x05%s \x01with a \x04HEAD SHOT (вы заработали поинты за убийства вместе с указаным кол-ом хедщотов)!", Score, VictimName);
  729. }
  730. else
  731. {
  732. if (Mode > 2)
  733. PrintToChatAll("\x04[\x03RANK\x04] \x05%s \x01has earned \x04%i \x01points for killing a (заработал поинты за убийство) \x05%s!", AttackerName, Score, VictimName);
  734. else
  735. PrintToChat(Attacker, "\x04[\x03RANK\x04] \x01You have earned \x04%i \x01points for killing a (вы заработали поинты за убийство) \x05%s!", Score, VictimName);
  736. }
  737. }
  738.  
  739. UpdateMapStat("kills", 1);
  740. UpdateMapStat("points", Score);
  741. CurrentPoints[Attacker] = CurrentPoints[Attacker] + Score;
  742. }
  743.  
  744. // Common Infected death code. +1 on headshot.
  745.  
  746. public Action:event_InfectedDeath(Handle:event, const String:name[], bool:dontBroadcast)
  747. {
  748. if (StatsDisabled())
  749. return;
  750.  
  751. new Attacker = GetClientOfUserId(GetEventInt(event, "attacker"));
  752.  
  753. if (!Attacker || IsClientBot(Attacker))
  754. return;
  755.  
  756. decl String:AttackerID[MAX_LINE_WIDTH];
  757. GetClientAuthString(Attacker, AttackerID, sizeof(AttackerID));
  758. decl String:AttackerName[MAX_LINE_WIDTH];
  759. GetClientName(Attacker, AttackerName, sizeof(AttackerName));
  760.  
  761. new Score = ModifyScoreDifficulty(GetConVarInt(cvar_Infected), 2, 3);
  762.  
  763. if (GetEventBool(event, "headshot"))
  764. {
  765. Score = Score + 1;
  766. TimerHeadshots[Attacker] = TimerHeadshots[Attacker] + 1;
  767. }
  768.  
  769. TimerPoints[Attacker] = TimerPoints[Attacker] + Score;
  770. TimerKills[Attacker] = TimerKills[Attacker] + 1;
  771. }
  772.  
  773. // Tank death code. Points are given to all players.
  774.  
  775. public Action:event_TankKilled(Handle:event, const String:name[], bool:dontBroadcast)
  776. {
  777. if (StatsDisabled())
  778. return;
  779.  
  780. if (CampaignOver)
  781. return;
  782.  
  783. if (TankCount >= 3)
  784. return;
  785.  
  786. new Mode = GetConVarInt(cvar_AnnounceMode);
  787. new Score = ModifyScoreDifficulty(GetConVarInt(cvar_Tank), 2, 4);
  788.  
  789. new Deaths = 0;
  790. new Modifier = 0;
  791.  
  792. new maxplayers = GetMaxClients();
  793. for (new i = 1; i <= maxplayers; i++)
  794. {
  795. if (IsClientConnected(i) && IsClientInGame(i) && !IsClientBot(i))
  796. {
  797. if (IsPlayerAlive(i))
  798. Modifier++;
  799. else
  800. Deaths++;
  801. }
  802. }
  803.  
  804. Score = Score * Modifier;
  805.  
  806. decl String:iID[MAX_LINE_WIDTH];
  807. decl String:query[512];
  808.  
  809. for (new i = 1; i <= maxplayers; i++)
  810. {
  811. if (IsClientConnected(i) && IsClientInGame(i) && !IsClientBot(i))
  812. {
  813. GetClientAuthString(i, iID, sizeof(iID));
  814. Format(query, sizeof(query), "UPDATE players SET points = points + %i, award_tankkill = award_tankkill + 1 WHERE steamid = '%s'", Score, iID);
  815. SendSQLUpdate(query);
  816.  
  817. CurrentPoints[i] = CurrentPoints[i] + Score;
  818. }
  819. }
  820.  
  821. if (Mode)
  822. PrintToChatAll("\x04[\x03RANK\x04] \x03ALL SURVIVORS \x01have earned \x04%i \x01points for killing a Tank with \x05%i Deaths (все выжившие заработали столько поинтов за убийство Танка, велючая умерших)!", Score, Deaths);
  823.  
  824. UpdateMapStat("kills", 1);
  825. UpdateMapStat("points", Score);
  826. TankCount = TankCount + 1;
  827. }
  828.  
  829. // Pill give code. Special note, Pills can only be given once.
  830.  
  831. public Action:event_GivePills(Handle:event, const String:name[], bool:dontBroadcast)
  832. {
  833. if (StatsDisabled())
  834. return;
  835.  
  836. new Recepient = GetClientOfUserId(GetEventInt(event, "userid"));
  837. new Giver = GetClientOfUserId(GetEventInt(event, "giver"));
  838. new Mode = GetConVarInt(cvar_AnnounceMode);
  839.  
  840. if (IsClientBot(Recepient) || IsClientBot(Giver))
  841. return;
  842.  
  843. new PillsID = GetEventInt(event, "weaponentid");
  844.  
  845. if (Pills[PillsID] == 1)
  846. return;
  847. else
  848. Pills[PillsID] = 1;
  849.  
  850. decl String:RecepientName[MAX_LINE_WIDTH];
  851. GetClientName(Recepient, RecepientName, sizeof(RecepientName));
  852. decl String:RecepientID[MAX_LINE_WIDTH];
  853. GetClientAuthString(Recepient, RecepientID, sizeof(RecepientID));
  854.  
  855. decl String:GiverName[MAX_LINE_WIDTH];
  856. GetClientName(Giver, GiverName, sizeof(GiverName));
  857. decl String:GiverID[MAX_LINE_WIDTH];
  858. GetClientAuthString(Giver, GiverID, sizeof(GiverID));
  859.  
  860. decl String:Item[16];
  861.  
  862. if (GetEventInt(event, "weapon") == 12)
  863. Format(Item, sizeof(Item), "Pain Pills");
  864. else
  865. return;
  866.  
  867. new Score = ModifyScoreDifficulty(GetConVarInt(cvar_Pills), 2, 4);
  868.  
  869. decl String:query[1024];
  870. Format(query, sizeof(query), "UPDATE players SET points = points + %i, award_pills = award_pills + 1 WHERE steamid = '%s'", Score, GiverID);
  871. SendSQLUpdate(query);
  872.  
  873. UpdateMapStat("points", Score);
  874. CurrentPoints[Giver] = CurrentPoints[Giver] + Score;
  875.  
  876. if (Mode == 1 || Mode == 2)
  877. PrintToChat(Giver, "\x04[\x03RANK\x04] \x01You have earned \x04%i \x01points for giving pills to \x05%s (вы заслужили поинты за передачу таблов)!", Score, RecepientName);
  878. else if (Mode == 3)
  879. PrintToChatAll("\x04[\x03RANK\x04] \x05%s \x01has earned \x04%i \x01points for giving pills to \x05%s (он заслужил поинты за передачу таблов)!", GiverName, Score, RecepientName);
  880. }
  881.  
  882. // Medkit give code.
  883.  
  884. public Action:event_HealPlayer(Handle:event, const String:name[], bool:dontBroadcast)
  885. {
  886. if (StatsDisabled())
  887. return;
  888.  
  889. new Recepient = GetClientOfUserId(GetEventInt(event, "subject"));
  890. new Giver = GetClientOfUserId(GetEventInt(event, "userid"));
  891. new Amount = GetEventInt(event, "health_restored");
  892. new Mode = GetConVarInt(cvar_AnnounceMode);
  893.  
  894. if (IsClientBot(Recepient) || IsClientBot(Giver))
  895. return;
  896.  
  897. if (Recepient == Giver)
  898. return;
  899.  
  900. decl String:RecepientName[MAX_LINE_WIDTH];
  901. GetClientName(Recepient, RecepientName, sizeof(RecepientName));
  902. decl String:RecepientID[MAX_LINE_WIDTH];
  903. GetClientAuthString(Recepient, RecepientID, sizeof(RecepientID));
  904.  
  905. decl String:GiverName[MAX_LINE_WIDTH];
  906. GetClientName(Giver, GiverName, sizeof(GiverName));
  907. decl String:GiverID[MAX_LINE_WIDTH];
  908. GetClientAuthString(Giver, GiverID, sizeof(GiverID));
  909.  
  910. new Score = (Amount + 1) / 2;
  911. if (GetConVarInt(cvar_MedkitMode))
  912. Score = ModifyScoreDifficulty(GetConVarInt(cvar_Medkit), 2, 4);
  913. else
  914. Score = ModifyScoreDifficulty(Score, 2, 3);
  915.  
  916. decl String:query[1024];
  917. Format(query, sizeof(query), "UPDATE players SET points = points + %i, award_medkit = award_medkit + 1 WHERE steamid = '%s'", Score, GiverID);
  918. SendSQLUpdate(query);
  919.  
  920. UpdateMapStat("points", Score);
  921. CurrentPoints[Giver] = CurrentPoints[Giver] + Score;
  922.  
  923. if (Mode == 1 || Mode == 2)
  924. PrintToChat(Giver, "\x04[\x03RANK\x04] \x01You have earned \x04%i \x01points for healing \x05%s (вы заработали поинты за лечение)!", Score, RecepientName);
  925. else if (Mode == 3)
  926. PrintToChatAll("\x04[\x03RANK\x04] \x05%s \x01has earned \x04%i \x01points for healing \x05%s (он заработал поинты за лечение)!", GiverName, Score, RecepientName);
  927. }
  928.  
  929. // Friendly fire code.
  930.  
  931. public Action:event_FriendlyFire(Handle:event, const String:name[], bool:dontBroadcast)
  932. {
  933. if (StatsDisabled())
  934. return;
  935.  
  936. new Attacker = GetClientOfUserId(GetEventInt(event, "attacker"));
  937. new Victim = GetClientOfUserId(GetEventInt(event, "victim"));
  938. new Mode = GetConVarInt(cvar_AnnounceMode);
  939.  
  940. if (!Attacker || !Victim)
  941. return;
  942.  
  943. if (IsClientBot(Victim))
  944. return;
  945.  
  946. decl String:AttackerName[MAX_LINE_WIDTH];
  947. GetClientName(Attacker, AttackerName, sizeof(AttackerName));
  948. decl String:AttackerID[MAX_LINE_WIDTH];
  949. GetClientAuthString(Attacker, AttackerID, sizeof(AttackerID));
  950.  
  951. decl String:VictimName[MAX_LINE_WIDTH];
  952. GetClientName(Victim, VictimName, sizeof(VictimName));
  953.  
  954. new Score = ModifyScoreDifficulty(GetConVarInt(cvar_FFire), 2, 4);
  955. Score = Score * -1;
  956.  
  957. decl String:query[1024];
  958. Format(query, sizeof(query), "UPDATE players SET points = points + %i, award_friendlyfire = award_friendlyfire + 1 WHERE steamid = '%s'", Score, AttackerID);
  959. SendSQLUpdate(query);
  960.  
  961. if (Mode == 1 || Mode == 2)
  962. PrintToChat(Attacker, "\x04[\x03RANK\x04] \x01You have \x03LOST \x04%i \x01points for \x03Friendly Firing \x05%s (вы потеряли поинты за указанное кол-во ТК за стрельбу по своим)!", Score, VictimName);
  963. else if (Mode == 3)
  964. PrintToChatAll("\x04[\x03RANK\x04] \x05%s \x01has \x03LOST \x04%i \x01points for \x03Friendly Firing \x05%s (он потерял поинты за указанное кол-во ТК за стрельбу по своим)!", AttackerName, Score, VictimName);
  965. }
  966.  
  967. // Campaign win code. Points are based on maps completed + survivors.
  968.  
  969. public Action:event_CampaignWin(Handle:event, const String:name[], bool:dontBroadcast)
  970. {
  971. if (StatsDisabled())
  972. return;
  973.  
  974. CampaignOver = true;
  975. new Mode = GetConVarInt(cvar_AnnounceMode);
  976.  
  977. new Score = ModifyScoreDifficulty(GetConVarInt(cvar_Campaign), 4, 12);
  978. new SurvivorCount = GetEventInt(event, "survivorcount");
  979. new BaseScore = Score * SurvivorCount;
  980.  
  981. decl String:query[1024];
  982. decl String:iID[MAX_LINE_WIDTH];
  983. decl String:Name[MAX_LINE_WIDTH];
  984. decl String:cookie[MAX_LINE_WIDTH];
  985. new Maps = 0;
  986. new WinScore = 0;
  987.  
  988. new maxplayers = GetMaxClients();
  989. for (new i = 1; i <= maxplayers; i++)
  990. {
  991. if (IsClientConnected(i) && IsClientInGame(i) && !IsClientBot(i))
  992. {
  993. GetClientAuthString(i, iID, sizeof(iID));
  994.  
  995. GetClientCookie(i, Handle:ClientMaps, cookie, 32);
  996. Maps = StringToInt(cookie) + 1;
  997.  
  998. if (Maps > 5)
  999. Maps = 5;
  1000.  
  1001. WinScore = BaseScore * Maps;
  1002.  
  1003. if (Mode == 1 || Mode == 2)
  1004. {
  1005. PrintToChat(i, "\x04[\x03RANK\x04] \x01You have earned \x04%i \x01points for completing Campaign (%i / 5) with \x05%i survivors (вы заработали поинты за завершение кампании вместе с остальными)!", WinScore, Maps, SurvivorCount);
  1006. }
  1007. else if (Mode == 3)
  1008. {
  1009. GetClientName(i, Name, sizeof(Name));
  1010. PrintToChatAll("\x04[\x03RANK\x04] \x05%s \x01has earned \x04%i \x01points for completing Campaign (%i / 5) with \x05%i survivors (он заработал поинты за завершение кампании вместе с остальными)!", Name, WinScore, Maps, SurvivorCount);
  1011. }
  1012.  
  1013. Format(query, sizeof(query), "UPDATE players SET points = points + %i, award_campaigns = award_campaigns + 1 WHERE steamid = '%s'", WinScore, iID);
  1014. SendSQLUpdate(query);
  1015.  
  1016. SetClientCookie(i, ClientMaps, "0");
  1017. UpdateMapStat("points", WinScore);
  1018. CurrentPoints[i] = CurrentPoints[i] + WinScore;
  1019. }
  1020. }
  1021. }
  1022.  
  1023. // Safe House reached code. Points are given to all players.
  1024. // Also, Witch Not Disturbed code, points also given to all players.
  1025.  
  1026. public Action:event_MapTransition(Handle:event, const String:name[], bool:dontBroadcast)
  1027. {
  1028. if (StatsDisabled())
  1029. return;
  1030.  
  1031. new Mode = GetConVarInt(cvar_AnnounceMode);
  1032.  
  1033. decl String:iID[MAX_LINE_WIDTH];
  1034. decl String:query[1024];
  1035. new maxplayers = GetMaxClients();
  1036. new Score = ModifyScoreDifficulty(GetConVarInt(cvar_Witch), 5, 10);
  1037.  
  1038. if (WitchExists && !WitchDisturb)
  1039. {
  1040. for (new i = 1; i <= maxplayers; i++)
  1041. {
  1042. if (IsClientConnected(i) && IsClientInGame(i) && !IsClientBot(i))
  1043. {
  1044. GetClientAuthString(i, iID, sizeof(iID));
  1045. Format(query, sizeof(query), "UPDATE players SET points = points + %i WHERE steamid = '%s'", Score, iID);
  1046. SendSQLUpdate(query);
  1047. UpdateMapStat("points", Score);
  1048. CurrentPoints[i] = CurrentPoints[i] + Score;
  1049. }
  1050. }
  1051.  
  1052. if (Mode)
  1053. PrintToChatAll("\x04[\x03RANK\x04] \x03ALL SURVIVORS \x01have earned \x04%i \x01points for \x05Not Disturbing A Witch (все выжившие заработали поинты, не потревожив Ведьму)!", Score);
  1054. }
  1055.  
  1056. Score = 0;
  1057. new Deaths = 0;
  1058. new BaseScore = ModifyScoreDifficulty(GetConVarInt(cvar_SafeHouse), 2, 5);
  1059.  
  1060. for (new i = 1; i <= maxplayers; i++)
  1061. {
  1062. if (IsClientConnected(i) && IsClientInGame(i) && !IsClientBot(i))
  1063. {
  1064.  
  1065. if (IsPlayerAlive(i))
  1066. Score = Score + BaseScore;
  1067. else
  1068. Deaths++;
  1069. }
  1070. }
  1071.  
  1072. new String:All4Safe[64] = "";
  1073. if (Deaths == 0)
  1074. Format(All4Safe, sizeof(All4Safe), ", award_allinsafehouse = award_allinsafehouse + 1");
  1075.  
  1076. decl String:cookie[MAX_LINE_WIDTH];
  1077. new Maps = 0;
  1078.  
  1079. for (new i = 1; i <= maxplayers; i++)
  1080. {
  1081. if (IsClientConnected(i) && IsClientInGame(i) && !IsClientBot(i))
  1082. {
  1083. InterstitialPlayerUpdate(i);
  1084.  
  1085. GetClientCookie(i, Handle:ClientMaps, cookie, 32);
  1086. Maps = StringToInt(cookie) + 1;
  1087. IntToString(Maps, cookie, sizeof(cookie));
  1088. SetClientCookie(i, ClientMaps, cookie);
  1089.  
  1090. GetClientAuthString(i, iID, sizeof(iID));
  1091. Format(query, sizeof(query), "UPDATE players SET points = points + %i%s WHERE steamid = '%s'", Score, All4Safe, iID);
  1092. SendSQLUpdate(query);
  1093. UpdateMapStat("points", Score);
  1094. CurrentPoints[i] = CurrentPoints[i] + Score;
  1095. }
  1096. }
  1097.  
  1098. if (Mode)
  1099. PrintToChatAll("\x04[\x03RANK\x04] \x03ALL SURVIVORS \x01have earned \x04%i \x01points for reaching a Safe House with \x05%i Deaths (все выжившие заработали поинты за достижения убеги, включая умерших)!", Score, Deaths);
  1100.  
  1101. PlayerVomited = false;
  1102. PanicEvent = false;
  1103. }
  1104.  
  1105. // Begin panic event.
  1106.  
  1107. public Action:event_PanicEvent(Handle:event, const String:name[], bool:dontBroadcast)
  1108. {
  1109. if (StatsDisabled())
  1110. return;
  1111.  
  1112. if (CampaignOver || PanicEvent)
  1113. return;
  1114.  
  1115. PanicEvent = true;
  1116. CreateTimer(75.0, timer_PanicEventEnd);
  1117. }
  1118.  
  1119. // Panic Event with no Incaps code. Points given to all players.
  1120.  
  1121. public Action:timer_PanicEventEnd(Handle:timer, Handle:hndl)
  1122. {
  1123. if (StatsDisabled())
  1124. return;
  1125.  
  1126. if (CampaignOver)
  1127. return;
  1128.  
  1129. new Mode = GetConVarInt(cvar_AnnounceMode);
  1130.  
  1131. if (PanicEvent && !PanicEventIncap)
  1132. {
  1133. new Score = ModifyScoreDifficulty(GetConVarInt(cvar_Panic), 2, 4);
  1134.  
  1135. decl String:query[1024];
  1136. decl String:iID[MAX_LINE_WIDTH];
  1137.  
  1138. new maxplayers = GetMaxClients();
  1139. for (new i = 1; i <= maxplayers; i++)
  1140. {
  1141. if (IsClientConnected(i) && IsClientInGame(i) && !IsClientBot(i))
  1142. {
  1143. GetClientAuthString(i, iID, sizeof(iID));
  1144. Format(query, sizeof(query), "UPDATE players SET points = points + %i WHERE steamid = '%s' ", Score, iID);
  1145. SendSQLUpdate(query);
  1146. UpdateMapStat("points", Score);
  1147. CurrentPoints[i] = CurrentPoints[i] + Score;
  1148. }
  1149. }
  1150.  
  1151. if (Mode)
  1152. PrintToChatAll("\x04[\x03RANK\x04] \x03ALL SURVIVORS \x01have earned \x04%i \x01points for \x05No Incapicitates After Panic Event (все выжившие заработали поинты за то что не вырубились после Волны бомжей)!", Score);
  1153. }
  1154.  
  1155. PanicEvent = false;
  1156. PanicEventIncap = false;
  1157. }
  1158.  
  1159. // Begin Boomer blind.
  1160.  
  1161. public Action:event_PlayerBlind(Handle:event, const String:name[], bool:dontBroadcast)
  1162. {
  1163. if (StatsDisabled())
  1164. return;
  1165.  
  1166. if (CampaignOver || PlayerVomited)
  1167. return;
  1168.  
  1169. PlayerVomited = true;
  1170. }
  1171.  
  1172. // Boomer Mob Survival with no Incaps code. Points are given to all players.
  1173.  
  1174. public Action:event_PlayerBlindEnd(Handle:event, const String:name[], bool:dontBroadcast)
  1175. {
  1176. if (StatsDisabled())
  1177. return;
  1178.  
  1179. new Mode = GetConVarInt(cvar_AnnounceMode);
  1180.  
  1181. if (PlayerVomited && !PlayerVomitedIncap)
  1182. {
  1183. new Score = ModifyScoreDifficulty(GetConVarInt(cvar_BoomerMob), 2, 5);
  1184.  
  1185. decl String:query[1024];
  1186. decl String:iID[MAX_LINE_WIDTH];
  1187.  
  1188. new maxplayers = GetMaxClients();
  1189. for (new i = 1; i <= maxplayers; i++)
  1190. {
  1191. if (IsClientConnected(i) && IsClientInGame(i) && !IsClientBot(i))
  1192. {
  1193. GetClientAuthString(i, iID, sizeof(iID));
  1194. Format(query, sizeof(query), "UPDATE players SET points = points + %i WHERE steamid = '%s' ", Score, iID);
  1195. SendSQLUpdate(query);
  1196. UpdateMapStat("points", Score);
  1197. CurrentPoints[i] = CurrentPoints[i] + Score;
  1198. }
  1199. }
  1200.  
  1201. if (Mode)
  1202. PrintToChatAll("\x04[\x03RANK\x04] \x03ALL SURVIVORS \x01have earned \x04%i \x01points for \x05No Incapicitates After Boomer Mob (все выжившие заработали поинты, за то что не вырубились после Блевотяшки)!", Score);
  1203. }
  1204.  
  1205. PlayerVomited = false;
  1206. PlayerVomitedIncap = false;
  1207. }
  1208.  
  1209. // Friendly Incapicitate code. Also handles if players should be awarded
  1210. // points for surviving a Panic Event or Boomer Mob without incaps.
  1211.  
  1212. public Action:event_PlayerIncap(Handle:event, const String:name[], bool:dontBroadcast)
  1213. {
  1214. if (StatsDisabled())
  1215. return;
  1216.  
  1217. new Attacker = GetClientOfUserId(GetEventInt(event, "attacker"));
  1218. new Victim = GetClientOfUserId(GetEventInt(event, "userid"));
  1219. new Mode = GetConVarInt(cvar_AnnounceMode);
  1220.  
  1221. if (PanicEvent)
  1222. PanicEventIncap = true;
  1223.  
  1224. if (PlayerVomited)
  1225. PlayerVomitedIncap = true;
  1226.  
  1227. if (!Attacker)
  1228. return;
  1229.  
  1230. if (IsClientBot(Attacker) || IsClientBot(Victim))
  1231. return;
  1232.  
  1233. decl String:AttackerID[MAX_LINE_WIDTH];
  1234. GetClientAuthString(Attacker, AttackerID, sizeof(AttackerID));
  1235. decl String:AttackerName[MAX_LINE_WIDTH];
  1236. GetClientName(Attacker, AttackerName, sizeof(AttackerName));
  1237.  
  1238. decl String:VictimName[MAX_LINE_WIDTH];
  1239. GetClientName(Victim, VictimName, sizeof(VictimName));
  1240.  
  1241. new Score = ModifyScoreDifficulty(GetConVarInt(cvar_FIncap), 2, 4);
  1242. Score = Score * -1;
  1243.  
  1244. decl String:query[512];
  1245. Format(query, sizeof(query), "UPDATE players SET points = points + %i WHERE steamid = '%s'", Score, AttackerID);
  1246. SendSQLUpdate(query);
  1247.  
  1248. if (Mode == 1 || Mode == 2)
  1249. PrintToChat(Attacker, "\x04[\x03RANK\x04] \x01You have \x03LOST \x04%i \x01points for \x03Incapicitating \x05%s (вы потеряли очки за потерю дееспособности)!", Score, VictimName);
  1250. else if (Mode == 3)
  1251. PrintToChatAll("\x04[\x03RANK\x04] \x05%s \x01has \x03LOST \x04%i \x01points for \x03Incapicitating \x05%s (он потерял очки за потерю дееспособности)!", AttackerName, Score, VictimName);
  1252. }
  1253.  
  1254. // Save friendly from being dragged by Smoker.
  1255.  
  1256. public Action:event_TongueSave(Handle:event, const String:name[], bool:dontBroadcast)
  1257. {
  1258. if (StatsDisabled())
  1259. return;
  1260.  
  1261. HunterSmokerSave(GetEventInt(event, "userid"), GetEventInt(event, "victim"), GetConVarInt(cvar_SmokerDrag), 2, 3, "Smoker", "award_smoker");
  1262. }
  1263.  
  1264. // Save friendly from being choked by Smoker.
  1265.  
  1266. public Action:event_ChokeSave(Handle:event, const String:name[], bool:dontBroadcast)
  1267. {
  1268. if (StatsDisabled())
  1269. return;
  1270.  
  1271. HunterSmokerSave(GetEventInt(event, "userid"), GetEventInt(event, "victim"), GetConVarInt(cvar_ChokePounce), 2, 3, "Smoker", "award_smoker");
  1272. }
  1273.  
  1274. // Save friendly from being pounced by Hunter.
  1275.  
  1276. public Action:event_PounceSave(Handle:event, const String:name[], bool:dontBroadcast)
  1277. {
  1278. if (StatsDisabled())
  1279. return;
  1280.  
  1281. HunterSmokerSave(GetEventInt(event, "userid"), GetEventInt(event, "victim"), GetConVarInt(cvar_ChokePounce), 2, 3, "Hunter", "award_hunter");
  1282. }
  1283.  
  1284. // Revive friendly code.
  1285.  
  1286. public Action:event_RevivePlayer(Handle:event, const String:name[], bool:dontBroadcast)
  1287. {
  1288. if (StatsDisabled())
  1289. return;
  1290.  
  1291. if (GetEventBool(event, "ledge_hang"))
  1292. return;
  1293.  
  1294. new Savior = GetClientOfUserId(GetEventInt(event, "userid"));
  1295. new Victim = GetClientOfUserId(GetEventInt(event, "subject"));
  1296. new Mode = GetConVarInt(cvar_AnnounceMode);
  1297.  
  1298. if (IsClientBot(Savior) || IsClientBot(Victim))
  1299. return;
  1300.  
  1301. decl String:SaviorName[MAX_LINE_WIDTH];
  1302. GetClientName(Savior, SaviorName, sizeof(SaviorName));
  1303. decl String:SaviorID[MAX_LINE_WIDTH];
  1304. GetClientAuthString(Savior, SaviorID, sizeof(SaviorID));
  1305.  
  1306. decl String:VictimName[MAX_LINE_WIDTH];
  1307. GetClientName(Victim, VictimName, sizeof(VictimName));
  1308. decl String:VictimID[MAX_LINE_WIDTH];
  1309. GetClientAuthString(Victim, VictimID, sizeof(VictimID));
  1310.  
  1311. new Score = ModifyScoreDifficulty(GetConVarInt(cvar_Revive), 2, 3);
  1312.  
  1313. decl String:query[1024];
  1314. Format(query, sizeof(query), "UPDATE players SET points = points + %i, award_revive = award_revive + 1 WHERE steamid = '%s'", Score, SaviorID);
  1315. SendSQLUpdate(query);
  1316.  
  1317. UpdateMapStat("points", Score);
  1318. CurrentPoints[Savior] = CurrentPoints[Savior] + Score;
  1319.  
  1320. if (Mode == 1 || Mode == 2)
  1321. PrintToChat(Savior, "\x04[\x03RANK\x04] \x01You have earned \x04%i \x01points for Reviving \x05%s (вы заслужили поинты за Возрождение)!", Score, VictimName);
  1322. else if (Mode == 3)
  1323. PrintToChatAll("\x04[\x03RANK\x04] \x05%s \x01has earned \x04%i \x01points for Reviving \x05%s (он заслужил поинты за Возрождение)!", SaviorName, Score, VictimName);
  1324. }
  1325.  
  1326. // Miscellaneous events and awards. See specific award for info.
  1327.  
  1328. public Action:event_Award(Handle:event, const String:name[], bool:dontBroadcast)
  1329. {
  1330. if (StatsDisabled())
  1331. return;
  1332.  
  1333. new PlayerID = GetEventInt(event, "userid");
  1334. new SubjectID = GetEventInt(event, "subjectentid");
  1335. new Mode = GetConVarInt(cvar_AnnounceMode);
  1336.  
  1337. if (!PlayerID)
  1338. return;
  1339.  
  1340. new User = GetClientOfUserId(PlayerID);
  1341.  
  1342. decl String:UserID[MAX_LINE_WIDTH];
  1343. GetClientAuthString(User, UserID, sizeof(UserID));
  1344. decl String:UserName[MAX_LINE_WIDTH];
  1345. GetClientName(User, UserName, sizeof(UserName));
  1346.  
  1347. if (IsClientBot(User))
  1348. return;
  1349.  
  1350. new Recepient;
  1351. decl String:RecepientName[MAX_LINE_WIDTH];
  1352.  
  1353. new Score = 0;
  1354. new String:AwardSQL[128];
  1355. new AwardID = GetEventInt(event, "award");
  1356.  
  1357. if (AwardID == 67) // Protect friendly
  1358. {
  1359. if (!SubjectID)
  1360. return;
  1361.  
  1362. Recepient = GetClientOfUserId(GetClientUserId(SubjectID));
  1363. GetClientName(Recepient, RecepientName, sizeof(RecepientName));
  1364.  
  1365. if (IsClientBot(Recepient))
  1366. return;
  1367.  
  1368. Format(AwardSQL, sizeof(AwardSQL), ", award_protect = award_protect + 1");
  1369. Score = ModifyScoreDifficulty(GetConVarInt(cvar_Protect), 2, 3);
  1370. UpdateMapStat("points", Score);
  1371. CurrentPoints[User] = CurrentPoints[User] + Score;
  1372.  
  1373. if (Mode == 1 || Mode == 2)
  1374. PrintToChat(User, "\x04[\x03RANK\x04] \x01You have earned \x04%i \x01points for Protecting \x05%s (вы заслужили поинты за Защиту)!", Score, RecepientName);
  1375. else if (Mode == 3)
  1376. PrintToChatAll("\x04[\x03RANK\x04] \x05%s \x01has earned \x04%i \x01points for Protecting \x05%s (он заслужил поинты за Защиту)!", UserName, Score, RecepientName);
  1377. }
  1378. else if (AwardID == 79) // Respawn friendly
  1379. {
  1380. if (!SubjectID)
  1381. return;
  1382.  
  1383. Recepient = GetClientOfUserId(GetClientUserId(SubjectID));
  1384. GetClientName(Recepient, RecepientName, sizeof(RecepientName));
  1385.  
  1386. if (IsClientBot(Recepient))
  1387. return;
  1388.  
  1389. Format(AwardSQL, sizeof(AwardSQL), ", award_rescue = award_rescue + 1");
  1390. Score = ModifyScoreDifficulty(GetConVarInt(cvar_Rescue), 2, 3);
  1391. UpdateMapStat("points", Score);
  1392. CurrentPoints[User] = CurrentPoints[User] + Score;
  1393.  
  1394. if (Mode == 1 || Mode == 2)
  1395. PrintToChat(User, "\x04[\x03RANK\x04] \x01You have earned \x04%i \x01points for Rescuing \x05%s (вы заслужили поинты за спасение)!", Score, RecepientName);
  1396. else if (Mode == 3)
  1397. PrintToChatAll("\x04[\x03RANK\x04] \x05%s \x01has earned \x04%i \x01points for Rescuing \x05%s (он заслужил поинты за спасение)!", UserName, Score, RecepientName);
  1398. }
  1399. else if (AwardID == 80) // Kill Tank with no deaths
  1400. {
  1401. Format(AwardSQL, sizeof(AwardSQL), ", award_tankkillnodeaths = award_tankkillnodeaths + 1");
  1402. Score = ModifyScoreDifficulty(0, 1, 1);
  1403. }
  1404. else if (AwardID == 83) // Team kill
  1405. {
  1406. if (!SubjectID)
  1407. return;
  1408.  
  1409. Recepient = GetClientOfUserId(GetClientUserId(SubjectID));
  1410.  
  1411. if (IsClientBot(Recepient))
  1412. return;
  1413.  
  1414. Format(AwardSQL, sizeof(AwardSQL), ", award_teamkill = award_teamkill + 1");
  1415. Score = ModifyScoreDifficulty(GetConVarInt(cvar_FKill), 2, 4);
  1416. Score = Score * -1;
  1417.  
  1418. if (Mode == 1 || Mode == 2)
  1419. PrintToChat(User, "\x04[\x03RANK\x04] \x01You have \x03LOST \x04%i \x01points for \x03Team Killing (вы проебали поинты за Убийство Команды)!", Score);
  1420. else if (Mode == 3)
  1421. PrintToChatAll("\x04[\x03RANK\x04] \x05%s \x01has \x03LOST \x04%i \x01points for \x03Team Killing (он проебал поинты за Убийство Команды)!", UserName, Score);
  1422. }
  1423. else if (AwardID == 85) // Left friendly for dead
  1424. {
  1425. Format(AwardSQL, sizeof(AwardSQL), ", award_left4dead = award_left4dead + 1");
  1426. Score = ModifyScoreDifficulty(0, 1, 1);
  1427. }
  1428. else if (AwardID == 94) // Let infected in safe room
  1429. {
  1430. Format(AwardSQL, sizeof(AwardSQL), ", award_letinsafehouse = award_letinsafehouse + 1");
  1431. Score = ModifyScoreDifficulty(GetConVarInt(cvar_InSafeRoom), 2, 4);
  1432. Score = Score * -1;
  1433.  
  1434. if (Mode == 1 || Mode == 2)
  1435. PrintToChat(User, "\x04[\x03RANK\x04] \x01You have \x03LOST \x04%i \x01points for letting \x03Infected In The Safe Room (вы проебали поинты за Пропуск Инфицированных в Убегу)!", Score);
  1436. else if (Mode == 3)
  1437. PrintToChatAll("\x04[\x03RANK\x04] \x05%s \x01has \x03LOST \x04%i \x01points for letting \x03Infected In The Safe Room (он проебал поинты за Пропуск Инфицированных в Убегу)!", UserName, Score);
  1438. }
  1439. else if (AwardID == 98) // Round restart
  1440. {
  1441. Score = ModifyScoreDifficulty(GetConVarInt(cvar_Restart), 2, 3);
  1442. Score = (400 - Score) * -1;
  1443. UpdateMapStat("restarts", 1);
  1444.  
  1445. if (Mode)
  1446. PrintToChat(User, "\x04[\x03RANK\x04] \x03ALL SURVIVORS \x01have \x03LOST \x04%i \x01points for \x03All Survivors Dying!", Score);
  1447. }
  1448. else
  1449. return;
  1450.  
  1451. decl String:query[1024];
  1452. Format(query, sizeof(query), "UPDATE players SET points = points + %i%s WHERE steamid = '%s'", Score, AwardSQL, UserID);
  1453. SendSQLUpdate(query);
  1454. }
  1455.  
  1456. // Reset Witch existence in the world when a new one is created.
  1457.  
  1458. public Action:event_WitchSpawn(Handle:event, const String:name[], bool:dontBroadcast)
  1459. {
  1460. if (StatsDisabled())
  1461. return;
  1462.  
  1463. WitchExists = true;
  1464. }
  1465.  
  1466. // Witch was disturbed!
  1467.  
  1468. public Action:event_WitchDisturb(Handle:event, const String:name[], bool:dontBroadcast)
  1469. {
  1470. if (StatsDisabled())
  1471. return;
  1472.  
  1473. if (WitchExists)
  1474. {
  1475. WitchDisturb = true;
  1476.  
  1477. if (!GetEventInt(event, "userid"))
  1478. return;
  1479.  
  1480. new User = GetClientOfUserId(GetEventInt(event, "userid"));
  1481.  
  1482. if (IsClientBot(User))
  1483. return;
  1484.  
  1485. decl String:UserID[MAX_LINE_WIDTH];
  1486. GetClientAuthString(User, UserID, sizeof(UserID));
  1487.  
  1488. decl String:query[1024];
  1489. Format(query, sizeof(query), "UPDATE players SET award_witchdisturb = award_witchdisturb + 1 WHERE steamid = '%s'", UserID);
  1490. SendSQLUpdate(query);
  1491. }
  1492. }
  1493.  
  1494. /*
  1495. -----------------------------------------------------------------------------
  1496. Chat/command handling and panels for Rank and Top10
  1497. -----------------------------------------------------------------------------
  1498. */
  1499.  
  1500. // Parse chat for RANK and TOP10 triggers.
  1501. public Action:cmd_Say(client, args)
  1502. {
  1503. decl String:Text[192];
  1504. new String:Command[64];
  1505. new Start = 0;
  1506.  
  1507. GetCmdArgString(Text, sizeof(Text));
  1508.  
  1509. if (Text[strlen(Text)-1] == '"')
  1510. {
  1511. Text[strlen(Text)-1] = '\0';
  1512. Start = 1;
  1513. }
  1514.  
  1515. if (strcmp(Command, "say2", false) == 0)
  1516. Start += 4;
  1517.  
  1518. if (strcmp(Text[Start], "rank", false) == 0)
  1519. {
  1520. cmd_ShowRank(client, 0);
  1521. if (GetConVarBool(cvar_SilenceChat))
  1522. return Plugin_Handled;
  1523. }
  1524.  
  1525. if (strcmp(Text[Start], "top10", false) == 0)
  1526. {
  1527. cmd_ShowTop10(client, 0);
  1528. if (GetConVarBool(cvar_SilenceChat))
  1529. return Plugin_Handled;
  1530. }
  1531.  
  1532. return Plugin_Continue;
  1533. }
  1534.  
  1535. // Begin generating the RANK display panel.
  1536. public Action:cmd_ShowRank(client, args)
  1537. {
  1538. if (!IsClientConnected(client) && !IsClientInGame(client))
  1539. return Plugin_Handled;
  1540.  
  1541. if (IsClientBot(client))
  1542. return Plugin_Handled;
  1543.  
  1544. decl String:SteamID[MAX_LINE_WIDTH];
  1545. GetClientAuthString(client, SteamID, sizeof(SteamID));
  1546.  
  1547. decl String:query[256];
  1548. Format(query, sizeof(query), "SELECT COUNT(*) FROM players");
  1549. SQL_TQuery(db, GetRankTotal, query, client);
  1550.  
  1551. Format(query, sizeof(query), "SELECT COUNT(*) FROM players WHERE points >=%i", ClientPoints[client]);
  1552. SQL_TQuery(db, GetClientRank, query, client);
  1553.  
  1554. Format(query, sizeof(query), "SELECT name, playtime, points, kills, headshots FROM players WHERE steamid = '%s'", SteamID);
  1555. SQL_TQuery(db, DisplayRank, query, client);
  1556.  
  1557. return Plugin_Handled;
  1558. }
  1559.  
  1560. // Generate client's point total.
  1561. public GetClientPoints(Handle:owner, Handle:hndl, const String:error[], any:data)
  1562. {
  1563. new client = data;
  1564.  
  1565. if (!client || hndl == INVALID_HANDLE)
  1566. return;
  1567.  
  1568. while (SQL_FetchRow(hndl))
  1569. ClientPoints[client] = SQL_FetchInt(hndl, 0);
  1570. }
  1571.  
  1572. // Generate client's rank.
  1573. public GetClientRank(Handle:owner, Handle:hndl, const String:error[], any:data)
  1574. {
  1575. new client = data;
  1576.  
  1577. if (!client || hndl == INVALID_HANDLE)
  1578. return;
  1579.  
  1580. while (SQL_FetchRow(hndl))
  1581. ClientRank[client] = SQL_FetchInt(hndl, 0);
  1582. }
  1583.  
  1584. // Generate total rank amount.
  1585. public GetRankTotal(Handle:owner, Handle:hndl, const String:error[], any:data)
  1586. {
  1587. if (hndl == INVALID_HANDLE)
  1588. return;
  1589.  
  1590. while (SQL_FetchRow(hndl))
  1591. RankTotal = SQL_FetchInt(hndl, 0);
  1592. }
  1593.  
  1594. // Send the RANK panel to the client's display.
  1595. public DisplayRank(Handle:owner, Handle:hndl, const String:error[], any:data)
  1596. {
  1597. new client = data;
  1598.  
  1599. if (!client || hndl == INVALID_HANDLE)
  1600. return;
  1601.  
  1602. new Playtime, Points, Kills, Headshots;
  1603. new String:Name[32];
  1604.  
  1605. while (SQL_FetchRow(hndl))
  1606. {
  1607. SQL_FetchString(hndl, 0, Name, sizeof(Name));
  1608. Playtime = SQL_FetchInt(hndl, 1);
  1609. Points = SQL_FetchInt(hndl, 2);
  1610. Kills = SQL_FetchInt(hndl, 3);
  1611. Headshots = SQL_FetchInt(hndl, 4);
  1612. }
  1613.  
  1614. new Handle:RankPanel = CreatePanel();
  1615. new String:Value[MAX_LINE_WIDTH];
  1616. new String:URL[MAX_LINE_WIDTH];
  1617.  
  1618. GetConVarString(cvar_SiteURL, URL, sizeof(URL));
  1619. new Float:HeadshotRatio = Headshots == 0 ? 0.00 : FloatDiv(float(Headshots), float(Kills))*100;
  1620.  
  1621. Format(Value, sizeof(Value), "Рейтинг of %s" , Name);
  1622. SetPanelTitle(RankPanel, Value);
  1623.  
  1624. Format(Value, sizeof(Value), "Ранг: %i of %i" , ClientRank[client], RankTotal);
  1625. DrawPanelText(RankPanel, Value);
  1626.  
  1627. if (Playtime > 60)
  1628. {
  1629. Format(Value, sizeof(Value), "Время игры: %.2f hours" , FloatDiv(float(Playtime), 60.0));
  1630. DrawPanelText(RankPanel, Value);
  1631. }
  1632. else
  1633. {
  1634. Format(Value, sizeof(Value), "Время игры: %i min" , Playtime);
  1635. DrawPanelText(RankPanel, Value);
  1636. }
  1637.  
  1638. Format(Value, sizeof(Value), "Очков: %i" , Points);
  1639. DrawPanelText(RankPanel, Value);
  1640.  
  1641. Format(Value, sizeof(Value), "Убито: %i" , Kills);
  1642. DrawPanelText(RankPanel, Value);
  1643.  
  1644. Format(Value, sizeof(Value), "Убито в голову: %i" , Headshots);
  1645. DrawPanelText(RankPanel, Value);
  1646.  
  1647. Format(Value, sizeof(Value), "Точность в голову: %.2f \%" , HeadshotRatio);
  1648. DrawPanelText(RankPanel, Value);
  1649.  
  1650. if (!StrEqual(URL, "", false))
  1651. {
  1652. Format(Value, sizeof(Value), "Полная статистика доступа по адресу: %s", URL);
  1653. DrawPanelText(RankPanel, Value);
  1654. }
  1655.  
  1656. DrawPanelItem(RankPanel, "Close");
  1657. SendPanelToClient(RankPanel, client, RankPanelHandler, 30);
  1658. CloseHandle(RankPanel);
  1659. }
  1660.  
  1661. // Generate the TOP10 display panel.
  1662. public Action:cmd_ShowTop10(client, args)
  1663. {
  1664. if (!IsClientConnected(client) && !IsClientInGame(client) && IsClientBot(client))
  1665. return Plugin_Handled;
  1666.  
  1667. decl String:query[256];
  1668. Format(query, sizeof(query), "SELECT COUNT(*) FROM players");
  1669. SQL_TQuery(db, GetRankTotal, query, client);
  1670.  
  1671. Format(query, sizeof(query), "SELECT name FROM players ORDER BY points DESC LIMIT 10");
  1672. SQL_TQuery(db, DisplayTop10, query, client);
  1673.  
  1674. return Plugin_Handled;
  1675. }
  1676.  
  1677. // Find a player from Top 10 ranking.
  1678. public GetClientFromTop10(client, rank)
  1679. {
  1680. decl String:query[256];
  1681. Format(query, sizeof(query), "SELECT points, steamid FROM players ORDER BY points DESC LIMIT %i,1", rank);
  1682. SQL_TQuery(db, GetClientTop10, query, client);
  1683. }
  1684.  
  1685. // Send the Top 10 player's info to the client.
  1686. public GetClientTop10(Handle:owner, Handle:hndl, const String:error[], any:data)
  1687. {
  1688. new client = data;
  1689.  
  1690. if (!client || hndl == INVALID_HANDLE)
  1691. return;
  1692.  
  1693. decl String:query[256];
  1694. decl String:SteamID[MAX_LINE_WIDTH];
  1695.  
  1696. while (SQL_FetchRow(hndl))
  1697. {
  1698. Format(query, sizeof(query), "SELECT COUNT(*) FROM players WHERE points >=%i", SQL_FetchInt(hndl, 0));
  1699. SQL_TQuery(db, GetClientRank, query, client);
  1700.  
  1701. SQL_FetchString(hndl, 1, SteamID, sizeof(SteamID));
  1702. Format(query, sizeof(query), "SELECT name, playtime, points, kills, headshots FROM players WHERE steamid = '%s'", SteamID);
  1703. SQL_TQuery(db, DisplayRank, query, client);
  1704. }
  1705. }
  1706.  
  1707. // Send the TOP10 panel to the client's display.
  1708. public DisplayTop10(Handle:owner, Handle:hndl, const String:error[], any:data)
  1709. {
  1710. new client = data;
  1711.  
  1712. if (!client || hndl == INVALID_HANDLE)
  1713. return;
  1714.  
  1715. new String:Name[32];
  1716.  
  1717. new Handle:Top10Panel = CreatePanel();
  1718. SetPanelTitle(Top10Panel, "Top 10 Players");
  1719.  
  1720. while (SQL_FetchRow(hndl))
  1721. {
  1722. SQL_FetchString(hndl, 0, Name, sizeof(Name));
  1723.  
  1724. ReplaceString(Name, sizeof(Name), "&lt;", "<");
  1725. ReplaceString(Name, sizeof(Name), "&gt;", ">");
  1726. ReplaceString(Name, sizeof(Name), "&#37;", "%");
  1727. ReplaceString(Name, sizeof(Name), "&#61;", "=");
  1728. ReplaceString(Name, sizeof(Name), "&#42;", "*");
  1729.  
  1730. DrawPanelItem(Top10Panel, Name);
  1731. }
  1732.  
  1733. SendPanelToClient(Top10Panel, client, Top10PanelHandler, 30);
  1734. CloseHandle(Top10Panel);
  1735. }
  1736.  
  1737. // Handler for RANK panel.
  1738. public RankPanelHandler(Handle:menu, MenuAction:action, param1, param2)
  1739. {
  1740. }
  1741.  
  1742. // Handler for TOP10 panel.
  1743. public Top10PanelHandler(Handle:menu, MenuAction:action, param1, param2)
  1744. {
  1745. if (action == MenuAction_Select)
  1746. {
  1747. if (param2 == 0)
  1748. param2 = 10;
  1749.  
  1750. GetClientFromTop10(param1, param2 - 1);
  1751. }
  1752. }
  1753.  
  1754. /*
  1755. -----------------------------------------------------------------------------
  1756. Private functions
  1757. -----------------------------------------------------------------------------
  1758. */
  1759.  
  1760. HunterSmokerSave(Savior, Victim, BasePoints, AdvMult, ExpertMult, String:SaveFrom[], String:SQLField[])
  1761. {
  1762. if (StatsDisabled())
  1763. return;
  1764.  
  1765. Savior = GetClientOfUserId(Savior);
  1766. Victim = GetClientOfUserId(Victim);
  1767.  
  1768. if (IsClientBot(Savior) || IsClientBot(Victim))
  1769. return;
  1770.  
  1771. decl String:SaviorName[MAX_LINE_WIDTH];
  1772. GetClientName(Savior, SaviorName, sizeof(SaviorName));
  1773. decl String:SaviorID[MAX_LINE_WIDTH];
  1774. GetClientAuthString(Savior, SaviorID, sizeof(SaviorID));
  1775.  
  1776. decl String:VictimName[MAX_LINE_WIDTH];
  1777. GetClientName(Victim, VictimName, sizeof(VictimName));
  1778. decl String:VictimID[MAX_LINE_WIDTH];
  1779. GetClientAuthString(Victim, VictimID, sizeof(VictimID));
  1780.  
  1781. if (StrEqual(SaviorID, VictimID))
  1782. return;
  1783.  
  1784. new Score = ModifyScoreDifficulty(BasePoints, AdvMult, ExpertMult);
  1785.  
  1786. decl String:query[1024];
  1787. Format(query, sizeof(query), "UPDATE players SET points = points + %i, %s = %s + 1 WHERE steamid = '%s'", Score, SQLField, SQLField, SaviorID);
  1788. SendSQLUpdate(query);
  1789.  
  1790. PrintToChat(Savior, "\x04[\x03RANK\x04] \x01You have earned \x04%i \x01points for saving \x05%s\x01 from a \x04%s!", Score, VictimName, SaveFrom);
  1791. UpdateMapStat("points", Score);
  1792. CurrentPoints[Savior] = CurrentPoints[Savior] + Score;
  1793. }
  1794.  
  1795. IsClientBot(client)
  1796. {
  1797. decl String:SteamID[MAX_LINE_WIDTH];
  1798. GetClientAuthString(client, SteamID, sizeof(SteamID));
  1799.  
  1800. if (StrEqual(SteamID, "BOT"))
  1801. return true;
  1802.  
  1803. return false;
  1804. }
  1805.  
  1806. ModifyScoreDifficulty(BaseScore, AdvMult, ExpMult)
  1807. {
  1808. decl String:Difficulty[MAX_LINE_WIDTH];
  1809. GetConVarString(cvar_Difficulty, Difficulty, sizeof(Difficulty));
  1810.  
  1811. if (StrEqual(Difficulty, "Hard")) BaseScore = BaseScore * AdvMult;
  1812. if (StrEqual(Difficulty, "Impossible")) BaseScore = BaseScore * ExpMult;
  1813.  
  1814. return BaseScore;
  1815. }
  1816.  
  1817. IsDifficultyEasy()
  1818. {
  1819. decl String:Difficulty[MAX_LINE_WIDTH];
  1820. GetConVarString(cvar_Difficulty, Difficulty, sizeof(Difficulty));
  1821.  
  1822. if (StrEqual(Difficulty, "Easy"))
  1823. return true;
  1824.  
  1825. return false;
  1826. }
  1827.  
  1828. InvalidGameMode()
  1829. {
  1830. new String:CurrentMode[16];
  1831. GetConVarString(cvar_Gamemode, CurrentMode, sizeof(CurrentMode));
  1832.  
  1833. // Currently will always return False in Survival and Versus gamemodes.
  1834. // This will be removed in a future version when stats for those versions work.
  1835.  
  1836. if (StrContains(CurrentMode, "coop", false) != -1 && GetConVarBool(cvar_EnableCoop))
  1837. return false;
  1838. else if (StrContains(CurrentMode, "survival", false) != -1)
  1839. return true;
  1840. else if (StrContains(CurrentMode, "versus", false) != -1)
  1841. return true;
  1842.  
  1843. return true;
  1844. }
  1845.  
  1846. CheckHumans()
  1847. {
  1848. new MinHumans = GetConVarInt(cvar_HumansNeeded);
  1849. new Humans = 0;
  1850. new maxplayers = GetMaxClients();
  1851.  
  1852. for (new i = 1; i <= maxplayers; i++)
  1853. {
  1854. if (IsClientConnected(i) && IsClientInGame(i) && !IsClientBot(i))
  1855. Humans++;
  1856. }
  1857.  
  1858. if (Humans < MinHumans)
  1859. return true;
  1860. else
  1861. return false;
  1862. }
  1863.  
  1864. ResetVars()
  1865. {
  1866. PlayerVomited = false;
  1867. PlayerVomitedIncap = false;
  1868. PanicEvent = false;
  1869. PanicEventIncap = false;
  1870. CampaignOver = false;
  1871. WitchExists = false;
  1872. WitchDisturb = false;
  1873.  
  1874. // Reset kill/point score timer amount
  1875. CreateTimer(1.0, InitPlayers);
  1876.  
  1877. TankCount = 0;
  1878.  
  1879. new maxplayers = GetMaxClients();
  1880. for (new i = 1; i <= maxplayers; i++)
  1881. {
  1882. CurrentPoints[i] = 0;
  1883. }
  1884. }
  1885.  
  1886. StatsDisabled()
  1887. {
  1888. if (InvalidGameMode())
  1889. return true;
  1890.  
  1891. if (IsDifficultyEasy())
  1892. return true;
  1893.  
  1894. if (CheckHumans())
  1895. return true;
  1896.  
  1897. if (GetConVarBool(cvar_Cheats))
  1898. return true;
  1899.  
  1900. if (db == INVALID_HANDLE)
  1901. return true;
  1902.  
  1903. return false;
  1904. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement