· 4 years ago · Dec 16, 2020, 10:08 AM
1/**
2 * ============================================================================
3 *
4 * Voter Core
5 *
6 * Developed by: sNok3
7 * Copyright (C) 2020 Denis Neagoe
8 *
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 *
22 * ============================================================================
23 **/
24#pragma semicolon 1
25
26#include <sourcemod>
27#include <cstrike>
28#include <SteamWorks>
29#include <multicolors>
30
31// Shop integration (and zombie xp)
32#undef REQUIRE_PLUGIN
33#undef REQUIRE_EXTENSIONS
34#tryinclude <store>
35#tryinclude <shop>
36#tryinclude <lvl_ranks>
37#define REQUIRE_EXTENSIONS
38#define REQUIRE_PLUGIN
39#tryinclude <zombieplague>
40
41#define PLUGINS_AUTHOR "sNok3"
42#define PLUGINS_VERSION "2.0.1"
43
44#pragma newdecls required
45
46public Plugin myinfo =
47{
48 name = "[Voter] Server voting plugin",
49 author = PLUGINS_AUTHOR,
50 description = "Give players rewards if they vote the server",
51 version = PLUGINS_VERSION,
52 url = "https://snok3.com"
53}
54
55// Convars
56ConVar g_ConVar_Voter_SoundEnabled,
57 g_ConVar_Voter_SoundPath,
58 g_ConVar_Voter_SoundVolume,
59 g_ConVar_Voter_ClaimURL,
60 g_ConVar_Voter_Credits,
61 g_ConVar_Voter_Enabled,
62 g_ConVar_Voter_ServerID,
63 g_ConVar_Voter_ServerKey,
64 g_ConVar_Voter_StreakEnabled,
65 g_ConVar_Voter_ShopType,
66 g_ConVar_Voter_VoteURL,
67 g_ConVar_Voter_DB,
68 g_ConVar_Voter_DBEnabled,
69 g_ConVar_Voter_Logs,
70 g_ConVar_Voter_LRXP,
71 g_ConVar_Voter_ZPXP,
72 g_ConVar_Voter_Prefix;
73
74Database hDatabase = null;
75
76char Prefix[255],
77 sDatabaseName[128];
78
79int iLogType,
80 iCredits,
81 iLR_Xp,
82 iStreak,
83 iVotes,
84 iZP_Xp;
85
86bool bCreditsEnabled,
87 bDBEnabled,
88 bLREnabled,
89 bStreakEnabled,
90 bSoundEnabled,
91 bZPEnabled;
92
93public void OnPluginStart()
94{
95 LoadTranslations("voter.phrases");
96
97 if(GetEngineVersion() != Engine_CSS && GetEngineVersion() != Engine_CSGO)
98 {
99 SetFailState("Unsupported Game! Only CS:GO and CS:S are supported.");
100 }
101
102 RegConsoleCmd("sm_voteserver", VoteLink, "Prints the server's vote link");
103 RegConsoleCmd("sm_vs", VoteLink, "Prints the server's vote link");
104 RegConsoleCmd("sm_claim", ClaimRewards, "Claims in-game rewards");
105 RegConsoleCmd("sm_votes", VotesAndStreaks, "Prints the number of votes and daily streak");
106
107 //Sound CVars
108 g_ConVar_Voter_SoundEnabled = CreateConVar("sm_voter_claim_sound_enabled", "1", "Enable or disable sounds for the claim (0 - Disabled, 1 - Enabled)", 0, true, 0.0, true, 1.0);
109 g_ConVar_Voter_SoundPath = CreateConVar("sm_voter_claim_sound_path", "sound/voter/fireworks.mp3", "The path to the sound used after the claim");
110 g_ConVar_Voter_SoundVolume = CreateConVar("sm_voter_claim_sound_volume", "1", "The volume of the played sound (0 - Minimum, 1 - Maximum)", 0, true, 0.0, true, 1.0);
111
112 //Claim CVars
113 g_ConVar_Voter_ClaimURL = CreateConVar("sm_voter_claim_url", "http://www.api.trackyserver.com/vote/?action=claim&key={serverkey}&steamid={steamid}", "The url for the claiming website (DON'T REMOVE {serverkey} OR {STEAMID})");
114 g_ConVar_Voter_Enabled = CreateConVar("sm_voter_enabled_voter", "1", "Enable/disable Voter (0 - Disabled, 1 - Enabled)", 0, true, 0.0, true, 1.0);
115 g_ConVar_Voter_ServerID = CreateConVar("sm_voter_server_id", "", "Your server's unique ID (trackyserver.com)");
116 g_ConVar_Voter_ServerKey = CreateConVar("sm_voter_server_key", "", "Your server's unique key from (trackyserver.com)");
117 g_ConVar_Voter_StreakEnabled= CreateConVar("sm_voter_streak_enabled", "1", "Enable/disable vote streaks", 0, true, 0.0, true, 1.0);
118 g_ConVar_Voter_ShopType = CreateConVar("sm_voter_shop_type", "", "Which Shop plugin will Voter use to award the players? (1 - Shop core, 2 - Zephyrus store, 3 - Zombie Plague store)", 0, true, 1.0, true, 3.0);
119 g_ConVar_Voter_VoteURL = CreateConVar("sm_voter_vote_url", "https://trackyserver.com/server/", "The url for the voting website (DON'T FORGET THE FINAL '/')");
120
121 //Awards
122 g_ConVar_Voter_Credits = CreateConVar("sm_voter_credits_awarded", "1", "Enable/disable credits rewards (0 - Disabled, 1 - Enabled)", 0, true, 0.0, true, 1.0);
123 g_ConVar_Voter_LRXP = CreateConVar("sm_voter_lr_xp_awarded", "1", "Enable/disable LevelRanks XP rewards (0 - Disabled, 1 - Enabled)", 0, true, 0.0, true, 1.0);
124 g_ConVar_Voter_ZPXP = CreateConVar("sm_voter_zp_xp_awarded", "1", "Enable/disable Zombie Plague XP rewards (0 - Disabled, 1 - Enabled)", 0, true, 0.0, true, 1.0);
125
126 //Other CVars
127 g_ConVar_Voter_DB = CreateConVar("sm_voter_db_name", "voter", "The name of the SQL table (both MySQL and database.cfg)");
128 g_ConVar_Voter_DBEnabled = CreateConVar("sm_voter_db_enabled", "1", "Enable/disable saving votes and streaks in the database (0 - Disabled, 1 - Enabled)", 0, true, 0.0, true, 1.0);
129 g_ConVar_Voter_Logs = CreateConVar("sm_voter_logs", "3", "Enable/disable Voter Logs (0 - Disabled, 1 - Error logs, 2 - Vote Logs, 3 - All logs)", 0, true, 0.0, true, 3.0);
130 g_ConVar_Voter_Prefix = CreateConVar("sm_voter_prefix", "{darkred}[Voter]{default}", "The prefix you want to have in your messages");
131
132 g_ConVar_Voter_Prefix.GetString(Prefix, sizeof(Prefix));
133 g_ConVar_Voter_DB.GetString(sDatabaseName, sizeof(sDatabaseName));
134
135 iLogType = g_ConVar_Voter_Logs.IntValue;
136
137 //Bools
138 bCreditsEnabled = g_ConVar_Voter_Credits.BoolValue;
139 bDBEnabled = g_ConVar_Voter_DBEnabled.BoolValue;
140 bLREnabled = g_ConVar_Voter_LRXP.BoolValue;
141 bSoundEnabled = g_ConVar_Voter_SoundEnabled.BoolValue;
142 bStreakEnabled = g_ConVar_Voter_StreakEnabled.BoolValue;
143 bZPEnabled = g_ConVar_Voter_ZPXP.BoolValue;
144
145 if(hDatabase == null)
146 if(bDBEnabled)
147 SQL_DBConnect();
148
149 AutoExecConfig(true, "voter");
150}
151
152public void OnConfigsExecuted()
153{
154 if(hDatabase == null)
155 if(bDBEnabled)
156 SQL_DBConnect();
157}
158
159public void OnMapStart()
160{
161 DownloadSound();
162}
163
164public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) // Remove errors related to some faulty store.inc
165{
166 MarkNativeAsOptional("Store_SetClientCredits");
167 MarkNativeAsOptional("Store_GetClientCredits");
168 return APLRes_Success;
169}
170
171// **********************************************/ Commands /**********************************************/
172public Action VoteLink(int client, int args)
173{
174 if(g_ConVar_Voter_Enabled.BoolValue)
175 {
176 if(IsClientValid(client)){
177 char buffer[256], cCredits[2], cLR_XP[2], c_ZP_XP[2];
178
179 g_ConVar_Voter_Credits.GetString(cCredits, sizeof(cCredits));
180 g_ConVar_Voter_LRXP.GetString(cLR_XP, sizeof(cLR_XP));
181 g_ConVar_Voter_ZPXP.GetString(c_ZP_XP, sizeof(c_ZP_XP));
182 if(strlen(cCredits) <= 0 && strlen(cLR_XP) <= 0 && strlen(c_ZP_XP) <= 0)
183 {
184 CPrintToChat(client, "%t", "Rewards disabled", Prefix);
185 }
186 else
187 {
188 g_ConVar_Voter_VoteURL.GetString(buffer, sizeof(buffer));
189 Format(buffer, sizeof(buffer), "%s%d", buffer, g_ConVar_Voter_ServerID.IntValue);
190
191 if(bStreakEnabled)
192 {
193 GetVotesStreak(client);
194 }
195 else
196 GetRewards("default");
197
198 CPrintToChat(client, "{darkred}================================================================");
199 CPrintToChat(client, "%t", "Vote us", Prefix);
200 CPrintToChat(client, "%t", "Vote message", Prefix);
201 CPrintToChat(client, "%t", "Vote rewards", Prefix, FormatRewards());
202 CPrintToChat(client, "%t", "Vote link", Prefix, buffer);
203 CPrintToChat(client, "{darkred}================================================================");
204 }
205 }
206 }
207 else
208 {
209 CPrintToChat(client, "%t", "Rewards disabled", Prefix);
210 }
211}
212
213public Action ClaimRewards(int client, int args)
214{
215 if(g_ConVar_Voter_Enabled.BoolValue)
216 {
217 if(IsClientValid(client))
218 {
219 char cCredits[2], cLR_XP[2], c_ZP_XP[2];
220
221 g_ConVar_Voter_Credits.GetString(cCredits, sizeof(cCredits));
222 g_ConVar_Voter_LRXP.GetString(cLR_XP, sizeof(cLR_XP));
223 g_ConVar_Voter_ZPXP.GetString(c_ZP_XP, sizeof(c_ZP_XP));
224 if(strlen(cCredits) >= 0 && strlen(cLR_XP) >= 0 && strlen(c_ZP_XP) >= 0)
225 {
226 char buffer[255], buffer_url[255], buffer_serverkey[255], SteamID[32];
227
228 // Parse Steam ID 64
229 GetClientAuthId(client, AuthId_SteamID64, SteamID, sizeof(SteamID));
230
231 g_ConVar_Voter_ClaimURL.GetString(buffer_url, sizeof(buffer_url));
232 g_ConVar_Voter_ServerKey.GetString(buffer_serverkey, sizeof(buffer_serverkey));
233
234 Format(buffer, sizeof(buffer), "%s&steamid=%s", buffer_serverkey, SteamID);
235 ReplaceString(buffer_url, sizeof(buffer_url), "{serverkey}&steamid={steamid}", buffer);
236
237 // Make API request
238 Handle hRequest = SteamWorks_CreateHTTPRequest(k_EHTTPMethodGET, buffer_url);
239 SteamWorks_SetHTTPCallbacks(hRequest, OnSteamWorksHTTPComplete);
240 SteamWorks_SetHTTPRequestContextValue(hRequest, GetClientUserId(client));
241 SteamWorks_SendHTTPRequest(hRequest);
242 if(!hRequest)
243 {
244 if(iLogType == 1 || iLogType == 3)
245 LogVoter("Error", "ERROR (Code 5): API request error(%i): %s", hRequest, buffer_url);
246 delete hRequest;
247 }
248 }
249 }
250 }
251 else
252 {
253 CPrintToChat(client, "%t", "Rewards disabled", Prefix);
254 }
255
256
257 return Plugin_Handled;
258}
259
260public Action VotesAndStreaks(int client, int args)
261{
262 if(g_ConVar_Voter_Enabled.BoolValue)
263 {
264 if(IsClientValid(client))
265 {
266 GetVotesStreak(client);
267 CPrintToChat(client, "{darkred}================================================================");
268 CPrintToChat(client, "%t", "Votes message", Prefix, iVotes);
269 if(bStreakEnabled)
270 CPrintToChat(client, "%t", "Streak message", Prefix, iStreak);
271 CPrintToChat(client, "{darkred}================================================================");
272 }
273 }
274 else
275 {
276 CPrintToChat(client, "%t", "Rewards disabled", Prefix);
277 }
278}
279
280// **********************************************/ Handles /**********************************************/
281public void OnSteamWorksHTTPComplete(Handle hRequest, bool bFailure, bool bRequestSuccessful, EHTTPStatusCode eStatusCode, any userid)
282{
283 int client = GetClientOfUserId(userid);
284 if(!bFailure && bRequestSuccessful && eStatusCode == k_EHTTPStatusCode200OK)
285 {
286 int bufferSize;
287 SteamWorks_GetHTTPResponseBodySize(hRequest, bufferSize);
288
289 char[] sResponseBody = new char[bufferSize];
290 SteamWorks_GetHTTPResponseBodyData(hRequest, sResponseBody, bufferSize);
291 delete hRequest;
292
293 int iResponse = StringToInt(sResponseBody);
294 iResponse = 1;
295 if(0 <= iResponse <= 2)
296 {
297 if(IsClientValid(client))
298 {
299 switch(iResponse)
300 {
301 case 0: CPrintToChat(client, "%t", "API response 0", Prefix); // Account not found
302
303 case 1:
304 {
305 if(bCreditsEnabled || bLREnabled || bZPEnabled)
306 {
307 if(bStreakEnabled)
308 {
309 GetVotesStreak(client);
310 }
311 else
312 GetRewards("default");
313
314 if(bCreditsEnabled)
315 {
316 char buffer[255];
317
318 int ShopType = g_ConVar_Voter_ShopType.IntValue;
319 g_ConVar_Voter_ShopType.GetString(buffer, sizeof(buffer));
320
321 if(strlen(buffer) > 0)
322 {
323 switch(ShopType)
324 {
325 case 1: // Shop core
326 {
327 if(GetFeatureStatus(FeatureType_Native, "Shop_GiveClientCredits") == FeatureStatus_Available)
328 Shop_GiveClientCredits(client, iCredits);
329 else
330 if(iLogType == 1 || iLogType == 3)
331 LogVoter("Error", "ERROR (Code 2): Shop core was not found");
332 }
333
334 case 2: // Zephyrus store
335 {
336 if(GetFeatureStatus(FeatureType_Native, "Store_GetClientCredits") == FeatureStatus_Available)
337 Store_SetClientCredits(client, Store_GetClientCredits(client) + iCredits);
338 else
339 if(iLogType == 1 || iLogType == 3)
340 LogVoter("Error", "ERROR (Code 2): Zephyrus store was not found");
341 }
342
343 case 3: // Zombie Plague store
344 {
345 if(GetFeatureStatus(FeatureType_Native, "ZP_GetClientMoney") == FeatureStatus_Available)
346 ZP_SetClientMoney(client, ZP_GetClientMoney(client) + iCredits);
347 else
348 if(iLogType == 1 || iLogType == 3)
349 LogVoter("Error", "ERROR (Code 2): Zombie Plague store was not found");
350 }
351
352 default: // Invalid store type
353 {
354 if(iLogType == 1 || iLogType == 3)
355 LogVoter("Error", "ERROR (Code 2): Store type value is invalid (1-3 only)");
356 }
357 }
358 }
359 }
360 if(bLREnabled)
361 {
362 if(GetFeatureStatus(FeatureType_Native, "LR_GetClientInfo") == FeatureStatus_Available)
363 LR_ChangeClientValue(client, LR_GetClientInfo(client, ST_EXP) + iLR_Xp); //!FIXME NU MERGE FUTE-IAS MUM
364 }
365 if(bZPEnabled)
366 {
367 if(GetFeatureStatus(FeatureType_Native, "ZP_GetClientExp") == FeatureStatus_Available)
368 ZP_SetClientExp(client, ZP_GetClientExp(client) + iZP_Xp);
369 }
370
371 PlayerVote(client);
372 }
373 else
374 {
375 CPrintToChat(client, "%t", "Rewards disabled", Prefix);
376 if(iLogType == 1 || iLogType == 3)
377 LogVoter("Error", "ERROR (Code 3): No rewards selected");
378 }
379 }
380 case 2: CPrintToChat(client, "%t", "API response 2", Prefix); // Already voted
381
382 default: CPrintToChat(client, "%t", "Error occured", Prefix); // API response code
383 }
384 }
385 }
386 }
387 else
388 {
389 if(iLogType == 1 || iLogType == 3)
390 LogVoter("Error", "ERROR (Code 1): Link might be invalid");
391 CPrintToChat(client, "%t", "Rewards disabled", Prefix);
392 }
393
394 delete hRequest;
395}
396
397public void PlayerVote(int client)
398{
399 CPrintToChatAll("%t", "Player voted", Prefix, client);
400 CPrintToChat(client, "%t", "API response 1", Prefix, FormatRewards()); // Claim rewards
401
402 if(bSoundEnabled)
403 PlaySound(client);
404
405 char sSteamID[32], sTimeStamp[64];
406
407 if(bDBEnabled)
408 SaveVotes(client);
409
410 GetClientAuthId(client, AuthId_Steam2, sSteamID, sizeof(sSteamID));
411 FormatTime(sTimeStamp, sizeof(sTimeStamp), "%Y/%m/%d", GetTime());
412
413 char sError[256];
414 if(iLogType == 2 || iLogType == 3){
415 Format(sError, sizeof(sError), "%L has voted the server! (%s)", client, sTimeStamp);
416 LogVoter("Votes", "VOTE: %s", sError);
417 }
418}
419// **********************************************/ Database /**********************************************/
420void SaveVotes(int client)
421{
422 if(hDatabase != null)
423 {
424 char sName[MAX_NAME_LENGTH], sSteamID[32], sQuery[512];
425
426 GetClientName(client, sName, sizeof(sName));
427 GetClientAuthId(client, AuthId_Steam2, sSteamID, sizeof(sSteamID));
428
429 int lenght = strlen(sName) * 2 + 1;
430 char[] sEscapedName = new char[lenght];
431 hDatabase.Escape(sName, sEscapedName, lenght);
432
433 hDatabase.Format(sQuery, sizeof(sQuery), "INSERT INTO `%s` (name, auth, votes, streak, votetime) VALUES ('%s', '%s', 1, 0, '%d') ON DUPLICATE KEY UPDATE votes = votes + 1, votetime = %d;", sDatabaseName, sEscapedName, sSteamID, GetTime(), GetTime());
434 hDatabase.Query(SQL_NoResult, sQuery, client);
435 }
436}
437
438public void GetVotesStreak(int client)
439{
440 if(hDatabase != null)
441 {
442 char sSteamID[32], sQuery[256];
443
444 GetClientAuthId(client, AuthId_Steam2, sSteamID, sizeof(sSteamID));
445 hDatabase.Format(sQuery, sizeof(sQuery), "SELECT `votes`,`streak` FROM `%s` WHERE auth = '%s';", sDatabaseName, sSteamID);
446 hDatabase.Query(SQL_VotesStreakCallback, sQuery, client);
447 }
448}
449
450void SQL_DBConnect()
451{
452 if(hDatabase != null)
453 delete hDatabase;
454 if(SQL_CheckConfig(sDatabaseName))
455 Database.Connect(SQL_ConnectionCallback, sDatabaseName);
456 else
457 if(iLogType == 1 || iLogType == 3)
458 LogVoter("Error", "ERROR (Code 7): Database connection failure! Couldn't find Voter's database entry in database.cfg");
459}
460
461void SQL_ConnectionCallback(Database db, char[] error, any data)
462{
463 if(db == null)
464 {
465 if(iLogType == 1 || iLogType == 3)
466 LogVoter("Error", "ERROR (Code 8): Can't connect to server. Error: %s", error);
467 return;
468 }
469 hDatabase = db;
470
471 char sQuery[512];
472 hDatabase.Format(sQuery, sizeof(sQuery), "CREATE TABLE IF NOT EXISTS `%s` (`id` INT UNSIGNED PRIMARY KEY AUTO_INCREMENT, `name` varchar(64) NOT NULL, `auth` varchar(32) NOT NULL, `votes` int(11) NOT NULL, `streak` int(11) NOT NULL, `votetime` varchar(32) NOT NULL, UNIQUE KEY `auth` (`auth`)) ENGINE = MyISAM DEFAULT CHARSET = utf8;", sDatabaseName);
473 hDatabase.Query(SQL_NoResult, sQuery, DBPrio_High);
474}
475
476void SQL_VotesStreakCallback(Database db, DBResultSet result, char[] error, any data)
477{
478 if(result == null)
479 {
480 if(iLogType == 1 || iLogType == 3)
481 LogVoter("Error", "ERROR (Code 9): %s", error);
482 }
483 else
484 {
485 result.FetchRow();
486 iVotes = result.FetchInt(0);
487 iStreak = result.FetchInt(1);
488
489 char sStreakDay[2];
490 Format(sStreakDay, sizeof(sStreakDay), "%i", iStreak);
491
492 if(iStreak > 0)
493 GetRewards(sStreakDay);
494 else
495 GetRewards("default");
496 }
497}
498
499void SQL_NoResult(Database db, DBResultSet result, char[] error, any data)
500{
501 if(result == null)
502 if(iLogType == 1 || iLogType == 3)
503 LogVoter("Error", "ERROR (Code 9): %s", error);
504 return;
505}
506
507// **********************************************/ Sounds /**********************************************/
508public void DownloadSound()
509{
510 char buffer[255];
511
512 g_ConVar_Voter_SoundPath.GetString(buffer, sizeof(buffer));
513
514 AddFileToDownloadsTable(buffer);
515 ReplaceString(buffer, sizeof(buffer), "sound/", "*/");
516 FakePrecacheSound(buffer);
517}
518
519public void PlaySound(int client)
520{
521 char buffer[128];
522
523 g_ConVar_Voter_SoundPath.GetString(buffer, sizeof(buffer));
524
525 ReplaceString(buffer, sizeof(buffer), "sound/", "*/");
526 EmitSoundToClient(client, buffer, _, _, _, _, g_ConVar_Voter_SoundVolume.FloatValue);
527}
528
529void FakePrecacheSound(const char[] szPath)
530{
531 AddToStringTable(FindStringTable("soundprecache"), szPath);
532}
533
534// **********************************************/ Files /**********************************************/
535public void LogVoter(char[] type, const char[] message, any...)
536{
537 char[] fMessage = new char[strlen(message) + 255];
538 VFormat(fMessage, strlen(message) + 255, message, 3);
539
540 char sFile[PLATFORM_MAX_PATH];
541 BuildPath(Path_SM, sFile, sizeof(sFile), "logs/Voter_%s.log", type);
542
543 LogToFile(sFile, fMessage);
544}
545
546public void GetRewards(char[] sDay)
547{
548 char CFG[256];
549 BuildPath(Path_SM, CFG, sizeof(CFG), "configs/Voter/rewards.ini");
550
551 if(!FileExists(CFG))
552 SetFailState("Config file \"%s\" couldn't be found!", CFG);
553
554 KeyValues kv = CreateKeyValues("Voter_Rewards");
555 kv.ImportFromFile(CFG);
556 kv.Rewind();
557 kv.JumpToKey(sDay);
558 if(bCreditsEnabled)
559 iCredits = kv.GetNum("credits");
560 if(bLREnabled)
561 iLR_Xp = kv.GetNum("xp_lr");
562 if(bZPEnabled)
563 iZP_Xp = kv.GetNum("xp_zp");
564
565 kv.Rewind();
566 delete kv;
567}
568
569// **********************************************/ Checkings /**********************************************/
570char[] FormatRewards()
571{
572 char buffer[256];
573
574 if(bCreditsEnabled && bLREnabled && bZPEnabled)
575 Format(buffer, sizeof(buffer), "{darkred}%d{default} credits, {darkred}%d{default} Level Ranks XP, {darkred}%d{default} Zombie Xp", iCredits, iLR_Xp, iZP_Xp);
576 else if(bCreditsEnabled && bLREnabled && bZPEnabled == false)
577 Format(buffer, sizeof(buffer), "{darkred}%d{default} credits, {darkred}%d{default} Level Ranks XP", iCredits, iLR_Xp);
578 else if(bCreditsEnabled && bLREnabled == false && bZPEnabled)
579 Format(buffer, sizeof(buffer), "{darkred}%d{default} credits, {darkred}%d{default} Zombie XP", iCredits, iZP_Xp);
580 else if(bCreditsEnabled == false && bLREnabled && bZPEnabled)
581 Format(buffer, sizeof(buffer), "{darkred}%d{default} Level Ranks, {darkred}%d{default} Zombie XP", iLR_Xp, iZP_Xp);
582 else if(bCreditsEnabled && bLREnabled == false && bZPEnabled == false)
583 Format(buffer, sizeof(buffer), "{darkred}%d{default} credits", iCredits);
584 else if(bCreditsEnabled == false && bLREnabled && bZPEnabled == false)
585 Format(buffer, sizeof(buffer), "{darkred}%d{default} Level Ranks XP", iLR_Xp);
586 else if(bCreditsEnabled == false && bLREnabled == false && bZPEnabled)
587 Format(buffer, sizeof(buffer), "{darkred}%d{default} Zombie XP", iZP_Xp);
588
589 return buffer;
590}
591
592public bool IsClientValid(int client)
593{
594 return (0 < client <= MaxClients) && IsClientInGame(client) && !IsFakeClient(client);
595}