From ac37af20ff3e5c51b8262def81b73d75c29e647f Mon Sep 17 00:00:00 2001 From: Jackz Date: Mon, 28 Sep 2020 17:08:21 -0500 Subject: [PATCH] improvements i guess i dont remember --- plugins/l4d2_stats_recorder.smx | Bin 13238 -> 15862 bytes scripting/l4d2_stats_recorder.sp | 383 +++++++++++++++++++++---------- 2 files changed, 265 insertions(+), 118 deletions(-) diff --git a/plugins/l4d2_stats_recorder.smx b/plugins/l4d2_stats_recorder.smx index 1d06ed9e6cd2df59c6cfab950ac85997bebd8b35..aaeeed1ece7186d7b8e46dc064e7e072c552089e 100644 GIT binary patch literal 15862 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 OKc#{`qteUjiTyuUsdJbB literal 13238 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 Z!hG)(j3`fa;yk~FieW$dp4=X5{~rYBQn3I4 diff --git a/scripting/l4d2_stats_recorder.sp b/scripting/l4d2_stats_recorder.sp index 048139d..1e2c78c 100644 --- a/scripting/l4d2_stats_recorder.sp +++ b/scripting/l4d2_stats_recorder.sp @@ -2,50 +2,50 @@ #pragma newdecls required //#define DEBUG - -#define PLUGIN_NAME "L4D(2) Stats Recorder" -#define PLUGIN_DESCRIPTION "" -#define PLUGIN_AUTHOR "jackzmc" #define PLUGIN_VERSION "1.0" -#define PLUGIN_URL "" #include #include +#include +#include //#include public Plugin myinfo = { - name = PLUGIN_NAME, - author = PLUGIN_AUTHOR, - description = PLUGIN_DESCRIPTION, + name = "L4D(2) Stats Recorder", + author = "jackzmc", + description = "", version = PLUGIN_VERSION, - url = PLUGIN_URL + url = "" }; static Database g_db; -char steamidcache[MAXPLAYERS+1][18]; +static char steamidcache[MAXPLAYERS+1][32]; bool lateLoaded = false, bVersus, bRealism; //Stats that need to be only sent periodically. (note: possibly deaths?) -static int meleeKills[MAXPLAYERS+1]; static int damageSurvivorGiven[MAXPLAYERS+1]; -static int damageSurvivorRec[MAXPLAYERS+1]; static int damageInfectedGiven[MAXPLAYERS+1]; static int damageInfectedRec[MAXPLAYERS+1]; static int damageSurvivorFF[MAXPLAYERS+1]; -static int infectedKills[MAXPLAYERS+1]; -static int infectedHeadshots[MAXPLAYERS+1]; static int doorOpens[MAXPLAYERS+1]; -static int damageToTank[MAXPLAYERS+1]; -static int damageWitch[MAXPLAYERS+1]; +static int witchKills[MAXPLAYERS+1]; static int startedPlaying[MAXPLAYERS+1]; +static int points[MAXPLAYERS+1]; +static int upgradePacksDeployed[MAXPLAYERS+1]; +static int finaleTimeStart; +static int molotovDamage[MAXPLAYERS+1]; +static int pipeKills[MAXPLAYERS+1]; public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { if(late) { lateLoaded = true; } } -//TODO: melee_kill (Cache clients and on map end push?) //TODO: player_use (Check laser sights usage) +//TODO: Witch startles +//TODO: Versus as infected stats +//TODO: Move kills to queue stats not on demand +//TODO: map_stats record fastest timestamp public void OnPluginStart() { @@ -62,9 +62,9 @@ public void OnPluginStart() if(lateLoaded) { for(int i=1; i 0) { for(int i=1; i<=MaxClients;i++) { if(IsClientConnected(i) && IsClientInGame(i) && !IsFakeClient(i) && steamidcache[i][0]) { - //Don't update player's stats if they have not done anything. - if(damageSurvivorGiven[i] > 0 || damageSurvivorRec[i] > 0 || damageInfectedGiven[i] > 0 || damageInfectedRec[i] > 0) { - FlushQueuedStats(i); - } + FlushQueuedStats(i); } } } @@ -138,10 +140,9 @@ public void CVC_GamemodeChange(ConVar convar, const char[] oldValue, const char[ ///////////////////////////////// public void OnClientAuthorized(int client, const char[] auth) { - if(!IsFakeClient(client)) { - strcopy(steamidcache[client], 18, auth); + if(client > 0 && !IsFakeClient(client)) { + strcopy(steamidcache[client], 32, auth); CreateDBUser(client, steamidcache[client]); - IncrementStat(client, "connections", 1); startedPlaying[client] = GetTime(); } } @@ -149,6 +150,8 @@ public void OnClientDisconnect(int client) { //Check if any pending stats to send. if(!IsFakeClient(client)) { FlushQueuedStats(client); + steamidcache[client][0] = '\0'; + points[client] = 0; } } @@ -168,13 +171,14 @@ bool ConnectDB() { return true; } } -void CreateDBUser(int client, const char steamid[18]) { - char query[255], escaped_id[37]; - g_db.Escape(steamid, escaped_id, 37); - Format(query, sizeof(query), "SELECT steamid,last_alias FROM stats WHERE steamid='%s'", escaped_id); - g_db.Query(DBC_CheckUserExistance, query, GetClientUserId(client)); +void CreateDBUser(int client, const char steamid[32]) { + if(client > 0 && !IsFakeClient(client)) { + char query[128]; + Format(query, sizeof(query), "SELECT steamid,last_alias,points FROM stats WHERE steamid='%s'", steamid); + g_db.Query(DBC_CheckUserExistance, query, GetClientUserId(client)); + } } -void IncrementStat(int client, const char[] name, int amount = 1, bool lowPriority = false) { +void IncrementStat(int client, const char[] name, int amount = 1, bool lowPriority = false, bool retry = true) { if (steamidcache[client][0] && !IsFakeClient(client)) { if(g_db == INVALID_HANDLE) { LogError("Database handle is invalid."); @@ -185,23 +189,26 @@ void IncrementStat(int client, const char[] name, int amount = 1, bool lowPriori char query[255]; g_db.Escape(name, escaped_name, escaped_name_size); Format(query, sizeof(query), "UPDATE stats SET `%s`=`%s`+%d WHERE steamid='%s'", escaped_name, escaped_name, amount, steamidcache[client]); - PrintToServer("[Debug] Updated Stat %s (+%d) for %s", name, amount, steamidcache[client]); + PrintToServer("[Debug] Updating Stat %s (+%d) for %N (%d) [%s]", name, amount, client, client, steamidcache[client]); g_db.Query(DBC_Generic, query, _, lowPriority ? DBPrio_Low : DBPrio_Normal); }else{ - #if defined debug - LogError("Incrementing stat (%s) for client %d failure: No steamid", name, client); - #endif if(!IsFakeClient(client)) { + #if defined debug + LogError("Incrementing stat (%s) for client %N (%d) [%s] failure: No steamid or is bot", name, client, client, steamidcache[client]); + #endif //attempt to fetch it - char steamid[18]; + char steamid[32]; GetClientAuthId(client, AuthId_Steam2, steamid, sizeof(steamid)); steamidcache[client] = steamid; + if(retry) { + IncrementStat(client, name, amount, lowPriority, false); + } } } } void IncrementMapStat(int client, const char[] mapname, int difficulty) { if (steamidcache[client][0] && !IsFakeClient(client)) { - char query[255], difficultyName[16]; + char query[256], difficultyName[16]; int realism_amount = bRealism ? 1 : 0; switch(difficulty) { case 0: strcopy(difficultyName, sizeof(difficultyName), "easy"); @@ -209,41 +216,45 @@ void IncrementMapStat(int client, const char[] mapname, int difficulty) { case 2: strcopy(difficultyName, sizeof(difficultyName), "advanced"); case 3: strcopy(difficultyName, sizeof(difficultyName), "expert"); } + int time = GetTime() - finaleTimeStart; - Format(query, sizeof(query), "INSERT INTO stats_maps (steamid, map_name, wins, `difficulty_%s`, realism)\nVALUES ('%s', '%s', 1, 1, %d)\n ON DUPLICATE KEY UPDATE wins=wins+1,`difficulty_%s`=`difficulty_%s`+1,realism=realism+%d", - difficultyName, steamidcache[client], mapname, realism_amount, difficultyName, difficultyName, realism_amount); + Format(query, sizeof(query), "INSERT INTO stats_maps (steamid, map_name, wins, `difficulty_%s`, realism, best_time)\nVALUES ('%s', '%s', 1, 1, %d, %d)\n ON DUPLICATE KEY UPDATE wins=wins+1,`difficulty_%s`=`difficulty_%s`+1,realism=realism+%d,best_time=GREATEST(%d,VALUES(best_time))", + difficultyName, steamidcache[client], mapname, realism_amount, time, difficultyName, difficultyName, realism_amount, time); PrintToServer("[Debug] Updated Map Stat %s for %s", mapname, steamidcache[client]); g_db.Query(DBC_Generic, query, _); }else{ #if defined debug - LogError("Incrementing stat (%s) for client %d failure: No steamid", mapname, client); + LogError("Incrementing stat (%s) for client %d error: No steamid", mapname, client); #endif } } public void FlushQueuedStats(int client) { //Update stats (don't bother checking if 0.) - char query[512]; + char query[1023]; int minutes_played = (GetTime() - startedPlaying[client]) / 60; - Format(query, sizeof(query), "UPDATE stats SET survivor_damage_give=survivor_damage_give+%d,survivor_damage_rec=survivor_damage_rec+%d, infected_damage_give=infected_damage_give+%d,infected_damage_rec=infected_damage_rec+%d,survivor_ff=survivor_ff+%d,common_kills=common_kills+%d,common_headshots=common_headshots+%d,melee_kills=melee_kills+%d,door_opens=door_opens+%d,damage_to_tank=damage_to_tank+%d, damage_witch=damage_witch+%d,minutes_played=minutes_played+%d WHERE steamid='%s'", - damageSurvivorGiven[client], - damageSurvivorRec[client], - damageInfectedGiven[client], - damageInfectedRec[client], - damageSurvivorFF[client], - infectedKills[client], - infectedHeadshots[client], - meleeKills[client], - doorOpens[client], - damageToTank[client], - damageWitch[client], - minutes_played, + Format(query, sizeof(query), "UPDATE stats SET survivor_damage_give=survivor_damage_give+%d,survivor_damage_rec=survivor_damage_rec+%d, infected_damage_give=infected_damage_give+%d,infected_damage_rec=infected_damage_rec+%d,survivor_ff=survivor_ff+%d,common_kills=common_kills+%d,common_headshots=common_headshots+%d,melee_kills=melee_kills+%d,door_opens=door_opens+%d,damage_to_tank=damage_to_tank+%d, damage_witch=damage_witch+%d,minutes_played=minutes_played+%d, kills_witch=kills_witch+%d, points=%d, packs_used=packs_used+%d, damage_molotov=damage_molotov+%d, kills_pipe=kills_pipe+%d WHERE steamid='%s'", + damageSurvivorGiven[client], //survivor_damage_give + GetEntProp(client, Prop_Send, "m_checkpointDamageTaken"), //survivor_damage_rec + damageInfectedGiven[client], //infected_damage_give + damageInfectedRec[client], //infected_damage_rec + damageSurvivorFF[client], //survivor_ff + GetEntProp(client, Prop_Send, "m_checkpointZombieKills"), //common_kills + GetEntProp(client, Prop_Send, "m_checkpointHeadshots"), //common_headshots + GetEntProp(client, Prop_Send, "m_checkpointMeleeKills"), //melee_kills + doorOpens[client], //door_opens + GetEntProp(client, Prop_Send, "m_checkpointDamageToTank"), //damage_to_tank + GetEntProp(client, Prop_Send, "m_checkpointDamageToWitch"), //damage_witch + minutes_played, //minutes_played + witchKills[client], //kills_witch + points[client], //points + upgradePacksDeployed[client], //packs_used + molotovDamage[client], //damage_molotov + pipeKills[client], //kills_pipe steamidcache[client][0] ); g_db.Query(DBC_FlushQueuedStats, query, client); //And clear them. - - steamidcache[client][0] = '\0'; } ///////////////////////////////// //DATABASE CALLBACKS @@ -254,21 +265,37 @@ public void DBC_CheckUserExistance(Database db, DBResultSet results, const char[ return; } int client = GetClientOfUserId(data); - char alias[33]; + int alias_length = 2*MAX_NAME_LENGTH+1; + char alias[MAX_NAME_LENGTH], ip[40], country_name[45]; + char[] safe_alias = new char[alias_length]; + GetClientName(client, alias, sizeof(alias)); + db.Escape(alias, safe_alias, alias_length); + GetClientIP(client, ip, sizeof(ip)); + GeoipCountry(ip, country_name, sizeof(country_name)); + if(results.RowCount == 0) { //user does not exist in db, create now char query[255]; - Format(query, sizeof(query), "INSERT INTO `stats` (`steamid`, `last_alias`, `last_join_date`) VALUES ('%s', '%s', NOW())", steamidcache[client], alias); + Format(query, sizeof(query), "INSERT INTO `stats` (`steamid`, `last_alias`, `last_join_date`,`created_date`,`country`) VALUES ('%s', '%s', UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), '%s')", steamidcache[client], safe_alias, country_name); g_db.Query(DBC_Generic, query); PrintToServer("Created new database entry for %N (%s)", client, steamidcache[client]); }else{ //User does exist, check if alias is outdated and update if needed - char safe_alias[67], query[255]; - db.Escape(alias, safe_alias, 67); + while(results.FetchRow()) { + int field_num; + if(results.FieldNameToNum("points", field_num)) { + points[client] = results.FetchInt(field_num); + } + } + if(points[client] == 0) { + PrintToServer("[Warning] Existing player %N (%d) has no points", client, client); + } + char query[255]; + int connections_amount = lateLoaded ? 0 : 1; - Format(query, sizeof(query), "UPDATE `stats` SET `last_alias`='%s', `last_join_date`=NOW() WHERE `steamid`='%s'",safe_alias, steamidcache[client]); + Format(query, sizeof(query), "UPDATE `stats` SET `last_alias`='%s', `last_join_date`=UNIX_TIMESTAMP(), `country`='%s', connections=connections+%d WHERE `steamid`='%s'", safe_alias, country_name, connections_amount, steamidcache[client]); g_db.Query(DBC_Generic, query); } } @@ -281,20 +308,16 @@ public void DBC_Generic(Database db, DBResultSet results, const char[] error, an } public void DBC_FlushQueuedStats(Database db, DBResultSet results, const char[] error, any data) { if(db == null || results == null) { - LogError("DBC_FlushQueued returne derror: %S", error); + LogError("DBC_FlushQueuedStats returned error: %s", error); }else{ int client = data; - meleeKills[client] = 0; - damageSurvivorGiven[client] = 0; - damageSurvivorRec[client] = 0; - damageInfectedGiven[client] = 0; - damageInfectedRec[client] = 0; damageSurvivorFF[client] = 0; - infectedKills[client] = 0; - infectedHeadshots[client] = 0; + damageSurvivorGiven[client] = 0; doorOpens[client] = 0; - damageToTank[client] = 0; - damageWitch[client] = 0; + witchKills[client] = 0; + upgradePacksDeployed[client] = 0; + molotovDamage[client] = 0; + pipeKills[client] = 0; startedPlaying[client] = GetTime(); } } @@ -302,14 +325,13 @@ public void DBC_FlushQueuedStats(Database db, DBResultSet results, const char[] // COMMANDS /////////////////////////// public Action Command_DebugStats(int client, int args) { - ReplyToCommand(client, "Your queued stats: "); - ReplyToCommand(client, "melee_kills = %d", meleeKills[client]); - ReplyToCommand(client, "damageSurvivorGiven = %d", damageSurvivorGiven[client]); - ReplyToCommand(client, "damageSurvivorRec = %d", damageSurvivorRec[client]); - ReplyToCommand(client, "damageInfectedGiven = %d", damageInfectedGiven[client]); - ReplyToCommand(client, "damageInfectedRec= %d", damageInfectedRec[client]); - ReplyToCommand(client, "infectedKills = %d", infectedKills[client]); - ReplyToCommand(client, "infectedHeadshots = %d", infectedHeadshots[client]); + int meleeKills = GetEntProp(client, Prop_Send, "m_checkpointMeleeKills"); + int zombiekills = GetEntProp(client, Prop_Send, "m_checkpointZombieKills"); + ReplyToCommand(client, "m_checkpointMeleeKills %d", meleeKills); + ReplyToCommand(client, "damageSurvivorGiven %d", damageSurvivorGiven[client]); + ReplyToCommand(client, "m_checkpointDamageTaken %d", GetEntProp(client, Prop_Send, "m_checkpointDamageTaken")); + ReplyToCommand(client, "m_checkpointZombieKills: %d", zombiekills); + ReplyToCommand(client, "points = %d", points[client]); return Plugin_Handled; } public Action Command_DebugCache(int client, int args) { @@ -326,39 +348,28 @@ public Action Command_DebugCache(int client, int args) { // EVENTS //////////////////////////// -public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast) { - int client = GetClientOfUserId(event.GetInt("userid")); - if(client > 0 && !IsFakeClient(client)) { - int team = GetClientTeam(client); - if(team == 2) { - IncrementStat(client, "survivor_deaths", 1); - }else if(team == 3) { - IncrementStat(client, "infected_deaths", 1); - } - } -} public void Event_InfectedHurt(Event event, const char[] name, bool dontBroadcast) { - //TODO: Record damage done to a tank, and a witch. int attacker = GetClientOfUserId(event.GetInt("attacker")); - int dmg = event.GetInt("amount"); if(attacker > 0 && !IsFakeClient(attacker)) { + int dmg = event.GetInt("amount"); damageSurvivorGiven[attacker] += dmg; - int target_id = event.GetInt("entityid"); - char entity_name[32]; - GetEntityClassname(target_id, entity_name, sizeof(entity_name)); - if(StrEqual(entity_name, "witch", false)) { - damageWitch[attacker]++; - } } } public void Event_InfectedDeath(Event event, const char[] name, bool dontBroadcast) { int attacker = GetClientOfUserId(event.GetInt("attacker")); if(attacker > 0 && !IsFakeClient(attacker)) { + bool blast = event.GetBool("blast"); bool headshot = event.GetBool("headshot"); + char wpn_name[32]; + GetClientWeapon(attacker, wpn_name, sizeof(wpn_name)); + if(headshot) { - infectedHeadshots[attacker]++; + points[attacker]+=2; + }else{ + points[attacker]++; } - infectedKills[attacker]++; + + if(blast) pipeKills[attacker]++; } } public void Event_PlayerHurt(Event event, const char[] name, bool dontBroadcast) { @@ -367,31 +378,64 @@ public void Event_PlayerHurt(Event event, const char[] name, bool dontBroadcast) int victim_team = GetClientTeam(victim); int dmg = event.GetInt("dmg_health"); if(dmg <= 0) return; - if(victim > 0 && !IsFakeClient(victim)) { - if(victim_team == 2) { - damageSurvivorRec[victim] += dmg; - }else if(victim_team == 3) { - damageInfectedRec[victim] += dmg; - } - } if(attacker > 0 && !IsFakeClient(attacker)) { int attacker_team = GetClientTeam(attacker); + char wpn_name[32]; + event.GetString("weapon", wpn_name, sizeof(wpn_name)); + if(attacker_team == 2) { damageSurvivorGiven[attacker] += dmg; - char victim_name[64]; - GetClientName(victim, victim_name, sizeof(victim_name)); - if(IsFakeClient(victim) && StrContains(victim_name, "Tank", true) > -1) { - damageToTank[attacker] += dmg; + if(victim_team == 3 && StrEqual(wpn_name, "inferno", true)) { + molotovDamage[attacker] += dmg; + points[attacker]++; //give points (not per dmg tho) } }else if(attacker_team == 3) { damageInfectedGiven[attacker] += dmg; } if(attacker_team == 2 && victim_team == 2) { + points[attacker]--; damageSurvivorFF[attacker] += dmg; } } } +public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast) { + int victim = GetClientOfUserId(event.GetInt("userid")); + if(victim > 0) { + int attacker = GetClientOfUserId(event.GetInt("attacker")); + int victim_team = GetClientTeam(victim); + + if(!IsFakeClient(victim)) { + if(victim_team == 2) { + IncrementStat(victim, "survivor_deaths", 1); + }else if(victim_team == 3) { + IncrementStat(victim, "infected_deaths", 1); + } + } + + if(attacker > 0 && !IsFakeClient(attacker) && GetClientTeam(attacker) == 2) { + if(victim_team == 3) { + int victim_class = GetEntProp(victim, Prop_Send, "m_zombieClass"); + char class[8], statname[16], wpn_name[32]; + event.GetString("weapon", wpn_name, sizeof(wpn_name)); + + if(GetInfectedClassName(victim_class, class, sizeof(class))) { + Format(statname, sizeof(statname), "kills_%s", class); + IncrementStat(attacker, statname, 1); + points[attacker] += 5; //special kill + } + if(StrEqual(wpn_name, "inferno", true) || StrEqual(wpn_name, "entityflame", true)) { + IncrementStat(attacker, "kills_molotov", 1); + } + }else if(victim_team == 2) { + IncrementStat(attacker, "ff_kills", 1); + points[attacker] -= 30; //30 point lost for killing teammate + } + } + } + +} + public void Event_ItemPickup(Event event, const char[] name, bool dontBroadcast) { char statname[72], item[64]; @@ -411,21 +455,24 @@ public void Event_PlayerIncap(Event event, const char[] name, bool dontBroadcast } public void Event_ItemUsed(Event event, const char[] name, bool dontBroadcast) { int client = GetClientOfUserId(event.GetInt("userid")); - if(!IsFakeClient(client)) { + if(client > 0 && !IsFakeClient(client)) { if(StrEqual(name, "heal_success", true)) { int subject = GetClientOfUserId(event.GetInt("subject")); if(subject == client) { IncrementStat(client, "heal_self", 1); }else{ + points[client] += 10; IncrementStat(client, "heal_others", 1); } }else if(StrEqual(name, "revive_success", true)) { int subject = GetClientOfUserId(event.GetInt("subject")); if(subject != client) { IncrementStat(client, "revived_others", 1); + points[client] += 3; IncrementStat(subject, "revived", 1); } }else if(StrEqual(name, "defibrillator_used", true)) { + points[client]+=9; IncrementStat(client, "defibs_used", 1); }else{ IncrementStat(client, name, 1); @@ -435,20 +482,25 @@ public void Event_ItemUsed(Event event, const char[] name, bool dontBroadcast) { public void Event_MeleeKill(Event event, const char[] name, bool dontBroadcast) { int client = GetClientOfUserId(event.GetInt("userid")); if(client > 0 && !IsFakeClient(client)) { - meleeKills[client]++; + points[client]++; } } public void Event_TankKilled(Event event, const char[] name, bool dontBroadcast) { int attacker = GetClientOfUserId(event.GetInt("attacker")); - bool solo = event.GetBool("solo"); - bool melee_only = event.GetBool("melee_only"); + int solo = event.GetBool("solo") ? 1 : 0; + int melee_only = event.GetBool("melee_only") ? 1 : 0; if(attacker > 0 && !IsFakeClient(attacker)) { if(solo) { + points[attacker]+=100; IncrementStat(attacker, "tanks_killed_solo", 1); } if(melee_only) { + points[attacker]+=150; IncrementStat(attacker, "tanks_killed_melee", 1); } + points[attacker]+=200; + char query[256]; + Format(query, sizeof(query), "UPDATE `stats` SET tanks_killed=tanks_killed+1, tanks_killed_solo=tanks_killed_solo+%d, tanks_killed_melee=tanks_killed_melee+%d WHERE `steamid` = '%s'", solo, melee_only); IncrementStat(attacker, "tanks_killed", 1); } } @@ -461,8 +513,11 @@ public void Event_DoorOpened(Event event, const char[] name, bool dontBroadcast) } public void Event_UpgradePackUsed(Event event, const char[] name, bool dontBroadcast) { - int upgradeid = event.GetInt("upgradeid"); - PrintToServer("upgradepackused: %d", upgradeid); + int client = GetClientOfUserId(event.GetInt("userid")); + if(client > 0 && !IsFakeClient(client)) { + upgradePacksDeployed[client]++; + points[client]+=3; + } } public void Event_FinaleWin(Event event, const char[] name, bool dontBroadcast) { char map_name[128]; @@ -474,7 +529,99 @@ public void Event_FinaleWin(Event event, const char[] name, bool dontBroadcast) if(team == 2) { IncrementMapStat(i, map_name, difficulty); IncrementStat(i, "finales_won",1); + points[i]+=400; } } } +} +public void Event_WitchKilled(Event event, const char[] name, bool dontBroadcast) { + int client = GetClientOfUserId(event.GetInt("userid")); + if(client > 0 && !IsFakeClient(client)) { + witchKills[client]++; + points[client]+=100; + } +} + +public void Event_FinaleStart(Event event, const char[] name, bool dontBroadcast) { + finaleTimeStart = GetTime(); +} +public void Event_GrenadeDenonate(Event event, const char[] name, bool dontBroadcast) { + int client = GetClientOfUserId(event.GetInt("userid")); + if(client > 0 && !IsFakeClient(client)) { + char wpn_name[32]; + GetClientWeapon(client, wpn_name, sizeof(wpn_name)); + //PrintToServer("wpn_Name %s", wpn_name); + //Somehow have to check if molotov or gr + } +} +public void OnEntityCreated(int entity) { + char class[32]; + GetEntityClassname(entity, class, sizeof(class)); + if(StrContains(class,"_projectile",true)) { + for(int i=1; i 0.0) { + IncrementStat(i, "throws_puke", 1); + break; + } + //PrintToServer("[#%d (%N)] test1=%f test2=%d owner=%d", i, i, test, test2, owner); + }else if(StrEqual(name, "weapon_molotov", true)) { + float throwtime = GetEntPropFloat(wpn, Prop_Send, "m_fThrowTime"); + if(throwtime > 0.0) { + IncrementStat(i, "throws_molotov", 1); + break; + } + }else if(StrEqual(name, "weapon_pipe_bomb", true)) { + float throwtime = GetEntPropFloat(wpn, Prop_Send, "m_fThrowTime"); + if(throwtime > 0.0) { + IncrementStat(i, "throws_pipe", 1); + break; + } + } + //PrintToServer("CREATED #%d (%N): wpnid=%d wpnname=%s", i, i, wpn, name); + } + } + } + // + } +} + +//////////////////////////// +// STOCKS +/////////////////////////// +stock bool GetInfectedClassName(int type, char[] buffer, int bufferSize) { + switch(type) { + case 1: strcopy(buffer, bufferSize, "smoker"); + case 2: strcopy(buffer, bufferSize, "boomer"); + case 3: strcopy(buffer, bufferSize, "hunter"); + case 4: strcopy(buffer, bufferSize, "spitter"); + case 5: strcopy(buffer, bufferSize, "jockey"); + case 6: strcopy(buffer, bufferSize, "charger"); + default: return false; + } + return true; +} +//entity abs origin code from here +//http://forums.alliedmods.net/showpost.php?s=e5dce96f11b8e938274902a8ad8e75e9&p=885168&postcount=3 +stock void GetEntityAbsOrigin(int entity, float origin[3]) { + if (entity && IsValidEntity(entity) + && (GetEntSendPropOffs(entity, "m_vecOrigin") != -1) + && (GetEntSendPropOffs(entity, "m_vecMins") != -1) + && (GetEntSendPropOffs(entity, "m_vecMaxs") != -1)) + { + float mins[3], maxs[3]; + GetEntPropVector(entity, Prop_Send, "m_vecOrigin", origin); + GetEntPropVector(entity, Prop_Send, "m_vecMins", mins); + GetEntPropVector(entity, Prop_Send, "m_vecMaxs", maxs); + + origin[0] += (mins[0] + maxs[0]) * 0.5; + origin[1] += (mins[1] + maxs[1]) * 0.5; + origin[2] += (mins[2] + maxs[2]) * 0.5; + } } \ No newline at end of file