Pushing the new version of the in game plug-in that has the CS:GO Protobuf changes.

Thanks to Popoklopsi for making the change and sharing it with the community.

https://forums.alliedmods.net/showpost.php?p=1879655&postcount=10

Also adding the source for all the super logs plugins so it is all in one place.

The complied version in the scripting folder are built with sourcemod 1.5.3 on windows.Pushing the new version of the in game plug-in that has the CS:GO Protobuf changes.

Thanks to Popoklopsi for making the change and sharing it with the community.

https://forums.alliedmods.net/showpost.php?p=1879655&postcount=10

Also adding the source for all the super logs plugins so it is all in one place.

The complied version in the scripting folder are built with sourcemod 1.5.3 on windows.init push
This commit is contained in:
Chris Lynch 2014-06-29 07:05:54 -04:00
parent 84e635f2a4
commit 1c6d91b818
32 changed files with 6833 additions and 26 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -626,6 +626,7 @@ public Action:ProtectForwardingChange(args)
}
public Action:ProtectForwardingDelallChange(args)
{
if (hlx_protect_address != INVALID_HANDLE)
@ -777,6 +778,7 @@ color_player(color_type, player_index, String: client_message[192])
}
color_all_players(String: message[192])
{
new color_index = -1;
@ -1160,11 +1162,11 @@ public Action:hlx_sm_psay(args)
}
if (strcmp(message_prefix, "") == 0)
{
Format(display_message, sizeof(display_message), "\x01%s", client_message);
Format(display_message, sizeof(display_message), "\x01\x0B\x01%s", client_message);
}
else
{
Format(display_message, sizeof(display_message), "%c%s\x01 %s", ((gamemod == Game_ZPS || gamemod == Game_GES)?5:4), message_prefix, client_message);
Format(display_message, sizeof(display_message), "\x01\x0B%c%s\x01 %s", ((gamemod == Game_ZPS || gamemod == Game_GES)?5:4), message_prefix, client_message);
}
new bool: setupColorForRecipients = false;
@ -1188,23 +1190,28 @@ public Action:hlx_sm_psay(args)
color_index = player_index;
}
new Handle:hBf;
hBf = StartMessageOne("SayText2", player_index);
hBf = StartMessageOne("SayText2", player_index, USERMSG_RELIABLE|USERMSG_BLOCKHOOKS);
if (hBf != INVALID_HANDLE)
{
BfWriteByte(hBf, color_index);
BfWriteByte(hBf, 0);
if (gamemod == Game_CSGO)
if(GetUserMessageType() == UM_Protobuf)
{
// hackhackhack...
// CS:GO won't print any colors unless you not only start with standard color (1)
// like in other games, but also have a 'printable' character following it. We will just
// use an unused control code
BfWriteByte(hBf, 1);
BfWriteByte(hBf, 11);
PbSetInt(hBf, "ent_idx", 0);
PbSetBool(hBf, "chat", false);
PbSetString(hBf, "msg_name", display_message);
PbAddString(hBf, "params", "");
PbAddString(hBf, "params", "");
PbAddString(hBf, "params", "");
PbAddString(hBf, "params", "");
}
else
{
BfWriteByte(hBf, color_index);
BfWriteByte(hBf, 0);
BfWriteString(hBf, display_message);
}
BfWriteString(hBf, display_message);
EndMessage();
}
}
@ -1324,21 +1331,39 @@ public Action:hlx_sm_psay2(args)
{
// thanks to Fyren and IceMatrix for help with this
new Handle:hBf;
hBf = StartMessageOne("SayText", player_index);
hBf = StartMessageOne("SayText", player_index, USERMSG_RELIABLE|USERMSG_BLOCKHOOKS);
if (hBf != INVALID_HANDLE)
{
BfWriteByte(hBf, 1);
BfWriteBool(hBf, true);
BfWriteByte(hBf, player_index);
if (prefix == 0)
if(GetUserMessageType() == UM_Protobuf)
{
BfWriteString(hBf, buffer_message);
PbSetInt(hBf, "ent_idx", player_index);
PbSetBool(hBf, "chat", true);
if (prefix == 0)
{
PbSetString(hBf, "text", buffer_message);
}
else
{
PbSetString(hBf, "text", client_message);
}
}
else
{
BfWriteString(hBf, client_message);
BfWriteByte(hBf, 1);
BfWriteBool(hBf, true);
BfWriteByte(hBf, player_index);
if (prefix == 0)
{
BfWriteString(hBf, buffer_message);
}
else
{
BfWriteString(hBf, client_message);
}
}
EndMessage();
}
}
@ -1564,6 +1589,36 @@ public Action:hlx_sm_browse(args)
{
if (g_bPlyrCanDoMotd[player_index])
{
if (GetUserMessageType() == UM_Protobuf)
{
decl String:typeStr[5];
IntToString(MOTDPANEL_TYPE_URL, typeStr, 4);
new Handle:pb = StartMessageOne("VGUIMenu", player_index);
PbSetString(pb, "name", "info");
PbSetBool(pb, "show", true);
new Handle:modkey = PbAddMessage(pb, "subkeys");
PbSetString(modkey, "name", "type");
PbSetString(modkey, "str", typeStr);
modkey = PbAddMessage(pb, "subkeys");
PbSetString(modkey, "name", "title");
PbSetString(modkey, "str", "HLstatsX:CE");
modkey = PbAddMessage(pb, "subkeys");
PbSetString(modkey, "name", "msg");
PbSetString(modkey, "str", client_url);
EndMessage();
}
else
{
ShowMOTDPanel(player_index, "HLstatsX:CE", client_url, MOTDPANEL_TYPE_URL);
}
ShowMOTDPanel(player_index, "HLstatsX:CE", client_url, MOTDPANEL_TYPE_URL);
}
else
@ -1662,6 +1717,7 @@ public Action:hlx_sm_redirect(args)
}
public Action:hlx_sm_player_action(args)
{
if (args < 2)
@ -2134,12 +2190,21 @@ stock PrintToChatRecipientsFF(const String:message[])
if (client > 0 && !IsFakeClient(client) && IsClientInGame(client))
{
new Handle:hBf;
hBf = StartMessageOne("SayText", client);
hBf = StartMessageOne("SayText", client, USERMSG_RELIABLE|USERMSG_BLOCKHOOKS);
if (hBf != INVALID_HANDLE)
{
BfWriteByte(hBf, 0); // send as console
BfWriteString(hBf, message);
BfWriteByte(hBf, 1); // 1 to enable color parsing, 0 to not
if(GetUserMessageType() == UM_Protobuf)
{
PbSetInt(hBf, "ent_idx", 0);
PbSetBool(hBf, "chat", true);
PbSetString(hBf, "text", message);
}
else
{
BfWriteByte(hBf, 0); // send as console
BfWriteString(hBf, message);
BfWriteByte(hBf, 1); // 1 to enable color parsing, 0 to not
}
EndMessage();
}
}

View File

@ -0,0 +1,138 @@
#define HITGROUP_GENERIC 0
#define HITGROUP_HEAD 1
#define HITGROUP_CHEST 2
#define HITGROUP_STOMACH 3
#define HITGROUP_LEFTARM 4
#define HITGROUP_RIGHTARM 5
#define HITGROUP_LEFTLEG 6
#define HITGROUP_RIGHTLEG 7
#define LOG_HIT_OFFSET 7
#define LOG_HIT_SHOTS 0
#define LOG_HIT_HITS 1
#define LOG_HIT_KILLS 2
#define LOG_HIT_HEADSHOTS 3
#define LOG_HIT_TEAMKILLS 4
#define LOG_HIT_DAMAGE 5
#define LOG_HIT_DEATHS 6
#define LOG_HIT_GENERIC 7
#define LOG_HIT_HEAD 8
#define LOG_HIT_CHEST 9
#define LOG_HIT_STOMACH 10
#define LOG_HIT_LEFTARM 11
#define LOG_HIT_RIGHTARM 12
#define LOG_HIT_LEFTLEG 13
#define LOG_HIT_RIGHTLEG 14
new Handle:g_weapon_trie = INVALID_HANDLE;
CreatePopulateWeaponTrie()
{
// Create a Trie
g_weapon_trie = CreateTrie();
// Initial populate
for (new i = 0; i < MAX_LOG_WEAPONS; i++)
{
if (g_weapon_list[i][0] == 0)
{
// some games have a couple blanks as place holders (so array indexes match with weapon ids)
decl String:randomKey[6];
Format(randomKey, sizeof(randomKey), "%c%c%c%c%c%c", GetURandomInt(), GetURandomInt(), GetURandomInt(), GetURandomInt(), GetURandomInt(), GetURandomInt());
SetTrieValue(g_weapon_trie, randomKey, i);
continue;
}
SetTrieValue(g_weapon_trie, g_weapon_list[i], i);
}
}
dump_player_stats(client)
{
if (IsClientInGame(client) && IsClientConnected(client))
{
decl String: player_authid[64];
if (!GetClientAuthString(client, player_authid, sizeof(player_authid)))
{
strcopy(player_authid, sizeof(player_authid), "UNKNOWN");
}
new player_team_index = GetClientTeam(client);
new player_userid = GetClientUserId(client);
new is_logged;
for (new i = 0; (i < MAX_LOG_WEAPONS); i++)
{
#if defined INS
if (g_weapon_stats[client][i][LOG_HIT_HITS] > 0)
{
LogToGame("\"%N<%d><%s><%s>\" triggered \"weaponstats\" (weapon \"weapon_%s\") (shots \"%d\") (hits \"%d\") (kills \"%d\") (headshots \"%d\") (tks \"%d\") (damage \"%d\") (deaths \"%d\")", client, player_userid, player_authid, g_team_list[player_team_index], g_weapon_list[i], g_weapon_stats[client][i][LOG_HIT_SHOTS], g_weapon_stats[client][i][LOG_HIT_HITS], g_weapon_stats[client][i][LOG_HIT_KILLS], g_weapon_stats[client][i][LOG_HIT_HEADSHOTS], g_weapon_stats[client][i][LOG_HIT_TEAMKILLS], g_weapon_stats[client][i][LOG_HIT_DAMAGE], g_weapon_stats[client][i][LOG_HIT_DEATHS]);
LogToGame("\"%N<%d><%s><%s>\" triggered \"weaponstats2\" (weapon \"weapon_%s\") (head \"%d\") (chest \"%d\") (stomach \"%d\") (leftarm \"%d\") (rightarm \"%d\") (leftleg \"%d\") (rightleg \"%d\")", client, player_userid, player_authid, g_team_list[player_team_index], g_weapon_list[i], g_weapon_stats[client][i][LOG_HIT_HEAD], g_weapon_stats[client][i][LOG_HIT_CHEST], g_weapon_stats[client][i][LOG_HIT_STOMACH], g_weapon_stats[client][i][LOG_HIT_LEFTARM], g_weapon_stats[client][i][LOG_HIT_RIGHTARM], g_weapon_stats[client][i][LOG_HIT_LEFTLEG], g_weapon_stats[client][i][LOG_HIT_RIGHTLEG]);
#else
if (g_weapon_stats[client][i][LOG_HIT_SHOTS] > 0)
{
#if defined GES
LogToGame("\"%N<%d><%s><%s>\" triggered \"weaponstats\" (weapon \"weapon_%s\") (shots \"%d\") (hits \"%d\") (kills \"%d\") (headshots \"%d\") (tks \"%d\") (damage \"%d\") (deaths \"%d\")", client, player_userid, player_authid, g_team_list[player_team_index], g_weapon_loglist[i], g_weapon_stats[client][i][LOG_HIT_SHOTS], g_weapon_stats[client][i][LOG_HIT_HITS], g_weapon_stats[client][i][LOG_HIT_KILLS], g_weapon_stats[client][i][LOG_HIT_HEADSHOTS], g_weapon_stats[client][i][LOG_HIT_TEAMKILLS], g_weapon_stats[client][i][LOG_HIT_DAMAGE], g_weapon_stats[client][i][LOG_HIT_DEATHS]);
LogToGame("\"%N<%d><%s><%s>\" triggered \"weaponstats2\" (weapon \"weapon_%s\") (head \"%d\") (chest \"%d\") (stomach \"%d\") (leftarm \"%d\") (rightarm \"%d\") (leftleg \"%d\") (rightleg \"%d\")", client, player_userid, player_authid, g_team_list[player_team_index], g_weapon_loglist[i], g_weapon_stats[client][i][LOG_HIT_HEAD], g_weapon_stats[client][i][LOG_HIT_CHEST], g_weapon_stats[client][i][LOG_HIT_STOMACH], g_weapon_stats[client][i][LOG_HIT_LEFTARM], g_weapon_stats[client][i][LOG_HIT_RIGHTARM], g_weapon_stats[client][i][LOG_HIT_LEFTLEG], g_weapon_stats[client][i][LOG_HIT_RIGHTLEG]);
#else
LogToGame("\"%N<%d><%s><%s>\" triggered \"weaponstats\" (weapon \"%s\") (shots \"%d\") (hits \"%d\") (kills \"%d\") (headshots \"%d\") (tks \"%d\") (damage \"%d\") (deaths \"%d\")", client, player_userid, player_authid, g_team_list[player_team_index], g_weapon_list[i], g_weapon_stats[client][i][LOG_HIT_SHOTS], g_weapon_stats[client][i][LOG_HIT_HITS], g_weapon_stats[client][i][LOG_HIT_KILLS], g_weapon_stats[client][i][LOG_HIT_HEADSHOTS], g_weapon_stats[client][i][LOG_HIT_TEAMKILLS], g_weapon_stats[client][i][LOG_HIT_DAMAGE], g_weapon_stats[client][i][LOG_HIT_DEATHS]);
LogToGame("\"%N<%d><%s><%s>\" triggered \"weaponstats2\" (weapon \"%s\") (head \"%d\") (chest \"%d\") (stomach \"%d\") (leftarm \"%d\") (rightarm \"%d\") (leftleg \"%d\") (rightleg \"%d\")", client, player_userid, player_authid, g_team_list[player_team_index], g_weapon_list[i], g_weapon_stats[client][i][LOG_HIT_HEAD], g_weapon_stats[client][i][LOG_HIT_CHEST], g_weapon_stats[client][i][LOG_HIT_STOMACH], g_weapon_stats[client][i][LOG_HIT_LEFTARM], g_weapon_stats[client][i][LOG_HIT_RIGHTARM], g_weapon_stats[client][i][LOG_HIT_LEFTLEG], g_weapon_stats[client][i][LOG_HIT_RIGHTLEG]);
#endif
#endif
is_logged++;
}
}
if (is_logged > 0)
{
reset_player_stats(client);
}
}
}
reset_player_stats(client)
{
for (new i = 0; (i < MAX_LOG_WEAPONS); i++)
{
g_weapon_stats[client][i][LOG_HIT_SHOTS] = 0;
g_weapon_stats[client][i][LOG_HIT_HITS] = 0;
g_weapon_stats[client][i][LOG_HIT_KILLS] = 0;
g_weapon_stats[client][i][LOG_HIT_HEADSHOTS] = 0;
g_weapon_stats[client][i][LOG_HIT_TEAMKILLS] = 0;
g_weapon_stats[client][i][LOG_HIT_DAMAGE] = 0;
g_weapon_stats[client][i][LOG_HIT_DEATHS] = 0;
g_weapon_stats[client][i][LOG_HIT_GENERIC] = 0;
g_weapon_stats[client][i][LOG_HIT_HEAD] = 0;
g_weapon_stats[client][i][LOG_HIT_CHEST] = 0;
g_weapon_stats[client][i][LOG_HIT_STOMACH] = 0;
g_weapon_stats[client][i][LOG_HIT_LEFTARM] = 0;
g_weapon_stats[client][i][LOG_HIT_RIGHTARM] = 0;
g_weapon_stats[client][i][LOG_HIT_LEFTLEG] = 0;
g_weapon_stats[client][i][LOG_HIT_RIGHTLEG] = 0;
}
}
stock get_weapon_index(const String:weapon_name[])
{
new index = -1;
GetTrieValue(g_weapon_trie, weapon_name, index);
return index;
}
WstatsDumpAll()
{
for (new i = 1; i <= MaxClients; i++)
{
dump_player_stats(i);
}
}
OnPlayerDisconnect(client)
{
if(client > 0 && IsClientInGame(client))
{
dump_player_stats(client);
reset_player_stats(client);
}
}

View File

@ -0,0 +1,270 @@
/**
* HLstatsX Community Edition - SourceMod plugin to generate advanced weapon logging
* http://www.hlxcommunity.com
* Copyright (C) 2009 Nicholas Hastings (psychonic)
* Copyright (C) 2007-2008 TTS Oetzel & Goerz GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma semicolon 1
#include <sourcemod>
#include <sdktools>
#define NAME "SuperLogs: Age of Chivalry"
#define VERSION "1.0.2"
new Handle:g_cvar_headshots = INVALID_HANDLE;
new Handle:g_cvar_locations = INVALID_HANDLE;
new Handle:g_cvar_classchanges = INVALID_HANDLE;
new Handle:g_cvar_actions = INVALID_HANDLE;
new bool:g_logheadshots = true;
new bool:g_loglocations = true;
new bool:g_logclasschanges = true;
new bool:g_logactions = true;
new bool:g_bLogClassNextSpawn[MAXPLAYERS+1];
#include <loghelper>
public Plugin:myinfo = {
name = NAME,
author = "psychonic",
description = "Advanced logging for Age of Chivalry. Generates auxilary logging for use with log parsers such as HLstatsX and Psychostats",
version = VERSION,
url = "http://www.hlxcommunity.com"
};
public OnPluginStart()
{
g_cvar_locations = CreateConVar("superlogs_locations", "1", "Enable logging of kill coordinates (default on)", 0, true, 0.0, true, 1.0);
g_cvar_headshots = CreateConVar("superlogs_headshots", "1", "Enable logging of headshot and decapitation actions (default on)", 0, true, 0.0, true, 1.0);
g_cvar_classchanges = CreateConVar("superlogs_classchanges", "1", "Enable logging of character changes (default on)", 0, true, 0.0, true, 1.0);
g_cvar_actions = CreateConVar("superlogs_actions", "1", "Enable logging of actions, such as \"Round_Win\" (default on)", 0, true, 0.0, true, 1.0);
HookConVarChange(g_cvar_locations, OnCvarLocationsChange);
HookConVarChange(g_cvar_headshots, OnCvarHeadshotsChange);
HookConVarChange(g_cvar_classchanges, OnCvarClasschangesChange);
CreateConVar("superlogs_aoc_version", VERSION, NAME, FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY);
HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
HookEvent("round_end", Event_RoundEnd);
hook_classchanges();
CreateTimer(1.0, LogMap);
GetTeams();
}
hook_classchanges()
{
HookUserMessage(GetUserMessageId("ClassChanged"), Event_ClassChanged);
HookEvent("player_spawn", Event_Spawn, EventHookMode_Pre);
for (new i = 1; i <= MAXPLAYERS; i++)
{
g_bLogClassNextSpawn[i] = false;
}
}
unhook_classchanges()
{
UnhookUserMessage(GetUserMessageId("ClassChanged"), Event_ClassChanged);
UnhookEvent("player_spawn", Event_Spawn, EventHookMode_Pre);
}
public OnMapStart()
{
GetTeams();
}
public Action:Event_ClassChanged(UserMsg:msg_id, Handle:bf, const players[], playersNum, bool:reliable, bool:init)
{
g_bLogClassNextSpawn[players[0]] = true;
return Plugin_Continue;
}
public Action:Event_Spawn(Handle:event, const String:name[], bool:dontBroadcast)
{
new client = GetClientOfUserId(GetEventInt(event, "userid"));
if (client > 0 && g_bLogClassNextSpawn[client])
{
switch (GetEntProp(client, Prop_Send, "m_iClass"))
{
case 0:
LogRoleChange(client, "Longbowman");
case 1:
LogRoleChange(client, "Crossbowman");
case 2:
LogRoleChange(client, "Javelineer");
case 3:
LogRoleChange(client, "Man at Arms");
case 4:
LogRoleChange(client, "Sergeant");
case 5:
LogRoleChange(client, "Guardsman");
case 6:
LogRoleChange(client, "Crusader");
case 7:
LogRoleChange(client, "Knight");
case 8:
LogRoleChange(client, "Heavy Knight");
}
g_bLogClassNextSpawn[client] = false;
}
return Plugin_Continue;
}
public Action:Event_PlayerDeathPre(Handle:event, const String:name[], bool:dontBroadcast)
{
// "userid" "short" // user ID who died
// "attacker" "short" // user ID who killed
// "weapon" "string" // weapon name killed used
// "printweapon" "string" // full print weapon name killed used
// "hitgroup" "long" // Part of body that was hit
// "damagetype" "long" // CDamageInfo.GetAOCDamageType()
// "weaponid" "short" // Weapon ID
// "team" "short" // victim's team
new attacker = GetEventInt(event, "attacker");
new victim = GetEventInt(event, "userid");
if (attacker > 0 && victim > 0)
{
if (g_loglocations)
{
LogKillLoc(GetClientOfUserId(attacker), GetClientOfUserId(victim));
}
if (g_logheadshots)
{
new bool:headshot = GetEventBool(event, "headshot");
new bool:decapped = GetEventBool(event, "decapped");
new bool:headexplodie = GetEventBool(event, "headexplodie");
if (decapped)
{
//decapitation
LogPlayerEvent(GetClientOfUserId(attacker), "triggered", "headshot", true, " (hstype \"decap\")");
}
else if (headshot)
{
//headshot
LogPlayerEvent(GetClientOfUserId(attacker), "triggered", "headshot", true, " (hstype \"headshot\")");
}
else if (headexplodie)
{
// head "explodie"
LogPlayerEvent(GetClientOfUserId(attacker), "triggered", "headshot", true, " (hstype \"headexplodie\")");
}
}
}
return Plugin_Continue;
}
public OnClientPutInGame(client)
{
g_bLogClassNextSpawn[client] = false;
}
public Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast)
{
LogTeamEvent(GetEventInt(event, "winner"), "triggered", "Round_Win");
}
public OnCvarLocationsChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_loglocations;
g_loglocations = GetConVarBool(g_cvar_locations);
if (old_value != g_loglocations)
{
if (g_loglocations & !g_logheadshots)
{
HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
}
else if (!g_logheadshots)
{
UnhookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
}
}
}
public OnCvarActionsChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_logactions;
g_logactions = GetConVarBool(g_cvar_actions);
if (old_value != g_logactions)
{
if (g_logactions)
{
HookEvent("round_end", Event_RoundEnd);
}
else
{
UnhookEvent("round_end", Event_RoundEnd);
}
}
}
public OnCvarHeadshotsChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_logheadshots;
g_logheadshots = GetConVarBool(g_cvar_headshots);
if (old_value != g_logheadshots)
{
if (g_logheadshots & !g_loglocations)
{
HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
}
else if (!g_loglocations)
{
UnhookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
}
}
}
public OnCvarClasschangesChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_logclasschanges;
g_logclasschanges = GetConVarBool(g_cvar_classchanges);
if (old_value != g_logclasschanges)
{
if (g_logclasschanges)
{
hook_classchanges();
}
else
{
unhook_classchanges();
}
}
}
public Action:LogMap(Handle:timer)
{
// Called 1 second after OnPluginStart since srcds does not log the first map loaded. Idea from Stormtrooper's "mapfix.sp" for psychostats
LogMapLoad();
}

View File

@ -0,0 +1,116 @@
#include <sourcemod>
#include <loghelper>
#define VERSION "2.2"
public Plugin:myinfo =
{
name = "SuperLogs: CSpromod",
author = "NeoCortex, psychonic",
description = "Rewrites the logs from CSProMod so HLStatsX:CE will understand them",
version = VERSION,
url = "http://www.sourcemod.net/"
};
public OnPluginStart()
{
CreateConVar("superlogs_cspromod_version", VERSION, "SuperLogs: CSpromod", FCVAR_NOTIFY);
HookEvent("player_death", Event_PlayerDeath)
HookEvent("player_connect", Event_PlayerConnect)
HookEvent("player_disconnect", Event_PlayerDisconnect)
HookEvent("player_team", Event_PlayerTeam)
HookEvent("round_start", Event_RoundStart)
HookEvent("round_end", Event_RoundEnd)
CreateTimer(1.0, LogMap);
}
public Action:LogMap(Handle:timer)
{
// Taken from superlogs-generic.sp by psychonic
// Called 1 second after OnPluginStart since srcds does not log the first map loaded. Idea from Stormtrooper's "mapfix.sp" for psychostats
LogMapLoad();
}
public OnMapStart()
{
// For loghelper
GetTeams();
}
public Event_PlayerConnect(Handle:event, const String:name[], bool:dontBroadcast)
{
decl String:cname[MAX_NAME_LENGTH];
GetEventString(event, "name", cname, sizeof(cname));
decl String:steamid[24];
GetEventString(event, "networkid", steamid, sizeof(steamid));
decl String:ip[32];
GetEventString(event, "address", ip, sizeof(ip));
LogToGame("\"%s<%d><%s><>\" connected, address \"%s\"", cname, GetEventInt(event, "userid"), steamid, ip);
}
public Event_PlayerDisconnect(Handle:event, const String:name[], bool:dontBroadcast)
{
new client = GetClientOfUserId(GetEventInt(event, "userid"));
decl String:cname[MAX_NAME_LENGTH];
GetClientName(client, cname, sizeof(cname));
decl String:cauth[32];
GetClientAuthString(client, cauth, 32);
decl String:creason[128];
GetEventString(event, "reason", creason, sizeof(creason));
LogToGame("\"%s<%d><%s><>\" disconnected (reason \"%s\")", cname, client, cauth, creason);
}
public Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast)
{
// Version 2 of this subroutine (using loghelper) is written by psychonic
new victim = GetClientOfUserId(GetEventInt(event, "userid"));
new attacker = GetClientOfUserId(GetEventInt(event, "attacker"));
decl String:AWeapon[64];
GetEventString(event, "weapon", AWeapon, sizeof(AWeapon));
decl String:properties[12] = "";
if (GetEventBool(event, "headshot"))
{
strcopy(properties, sizeof(properties), " (headshot)");
}
// Player_Suicide
if (attacker == victim)
{
LogSuicide(victim, AWeapon, true, properties);
return;
}
LogKill(attacker, victim, AWeapon, true, properties);
}
public Event_RoundStart(Handle:event, const String:name[], bool:dontBroadcast)
{
LogToGame("World triggered \"Round_Start\"");
}
public Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast)
{
new winner = GetEventInt(event, "winner");
if (winner == 2 || winner == 3)
{
LogTeamEvent(winner, "triggered", "Round_Win");
}
LogToGame("World triggered \"Round_End\"");
}
public Event_PlayerTeam(Handle:event, const String:name[], bool:dontBroadcast)
{
new client = GetClientOfUserId(GetEventInt(event, "userid"));
new NTeam = GetEventInt(event, "team");
decl String:STeam[32];
GetTeamName(NTeam, STeam, sizeof(STeam));
LogPlayerEvent(client, "joined team", STeam, true)
}

View File

@ -0,0 +1,471 @@
/**
* HLstatsX Community Edition - SourceMod plugin to generate advanced weapon logging
* http://www.hlxcommunity.com
* Copyright (C) 2009 Nicholas Hastings (psychonic)
* Copyright (C) 2007-2008 TTS Oetzel & Goerz GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma semicolon 1
#include <sourcemod>
#include <sdktools>
#define NAME "SuperLogs: CSS"
#define VERSION "1.2.4"
#define MAX_LOG_WEAPONS 28
#define IGNORE_SHOTS_START 25
#define MAX_WEAPON_LEN 13
new g_weapon_stats[MAXPLAYERS+1][MAX_LOG_WEAPONS][15];
new const String:g_weapon_list[MAX_LOG_WEAPONS][MAX_WEAPON_LEN] = {
"ak47",
"m4a1",
"awp",
"deagle",
"mp5navy",
"aug",
"p90",
"famas",
"galil",
"scout",
"g3sg1",
"usp",
"glock",
"m249",
"m3",
"elite",
"fiveseven",
"mac10",
"p228",
"sg550",
"sg552",
"tmp",
"ump45",
"xm1014",
"knife",
"hegrenade",
"smokegrenade",
"flashbang"
};
new Handle:g_cvar_wstats = INVALID_HANDLE;
new Handle:g_cvar_headshots = INVALID_HANDLE;
new Handle:g_cvar_actions = INVALID_HANDLE;
new Handle:g_cvar_locations = INVALID_HANDLE;
new Handle:g_cvar_ktraj = INVALID_HANDLE;
new Handle:g_cvar_version = INVALID_HANDLE;
new bool:g_logwstats = true;
new bool:g_logheadshots = true;
new bool:g_logactions = true;
new bool:g_loglocations = true;
new bool:g_logktraj = false;
#include <loghelper>
#include <wstatshelper>
public Plugin:myinfo = {
name = NAME,
author = "psychonic",
description = "Advanced logging for CSS. Generates auxilary logging for use with log parsers such as HLstatsX and Psychostats",
version = VERSION,
url = "http://www.hlxcommunity.com"
};
#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3
public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max)
#else
public bool:AskPluginLoad(Handle:myself, bool:late, String:error[], err_max)
#endif
{
decl String:game_description[64];
GetGameDescription(game_description, sizeof(game_description), true);
if (StrContains(game_description, "Counter-Strike", false) == -1)
{
decl String:game_folder[64];
GetGameFolderName(game_folder, sizeof(game_folder));
if (StrContains(game_folder, "cstrike", false) == -1)
{
strcopy(error, err_max, "This plugin is only supported on CS:S");
#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3
return APLRes_Failure;
#else
return false;
#endif
}
}
#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3
return APLRes_Success;
#else
return true;
#endif
}
public OnPluginStart()
{
CreatePopulateWeaponTrie();
g_cvar_wstats = CreateConVar("superlogs_wstats", "1", "Enable logging of weapon stats (default on)", 0, true, 0.0, true, 1.0);
g_cvar_headshots = CreateConVar("superlogs_headshots", "1", "Enable logging of headshot player action (default on)", 0, true, 0.0, true, 1.0);
g_cvar_actions = CreateConVar("superlogs_actions", "1", "Enable logging of player actions (default on)", 0, true, 0.0, true, 1.0);
g_cvar_locations = CreateConVar("superlogs_locations", "1", "Enable logging of location on player death (default on)", 0, true, 0.0, true, 1.0);
g_cvar_ktraj = CreateConVar("superlogs_ktraj", "0", "Enable Psychostats \"KTRAJ\" logging (default off)", 0, true, 0.0, true, 1.0);
// cvars will have already existed if plugin was reloaded and might be set to non-default values
g_logwstats = GetConVarBool(g_cvar_wstats);
g_logheadshots = GetConVarBool(g_cvar_headshots);
g_logactions = GetConVarBool(g_cvar_actions);
g_loglocations = GetConVarBool(g_cvar_locations);
g_logktraj = GetConVarBool(g_cvar_ktraj);
HookConVarChange(g_cvar_wstats, OnCvarWstatsChange);
HookConVarChange(g_cvar_headshots, OnCvarHeadshotsChange);
HookConVarChange(g_cvar_actions, OnCvarActionsChange);
HookConVarChange(g_cvar_locations, OnCvarLocationsChange);
HookConVarChange(g_cvar_ktraj, OnCvarKtrajChange);
g_cvar_version = CreateConVar("superlogs_css_version", VERSION, NAME, FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY);
hook_wstats();
hook_actions();
HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
HookEvent("player_death", Event_PlayerDeath);
CreateTimer(1.0, LogMap);
GetTeams();
}
public OnMapStart()
{
GetTeams();
}
public OnConfigsExecuted()
{
decl String:version[255];
GetConVarString(g_cvar_version, version, sizeof(version));
SetConVarString(g_cvar_version, version);
}
hook_wstats()
{
HookEvent("weapon_fire", Event_PlayerShoot);
HookEvent("player_hurt", Event_PlayerHurt);
HookEvent("player_spawn", Event_PlayerSpawn);
HookEvent("round_end", Event_RoundEnd, EventHookMode_PostNoCopy);
HookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre);
}
unhook_wstats()
{
UnhookEvent("weapon_fire", Event_PlayerShoot);
UnhookEvent("player_hurt", Event_PlayerHurt);
UnhookEvent("player_spawn", Event_PlayerSpawn);
UnhookEvent("round_end", Event_RoundEnd, EventHookMode_PostNoCopy);
UnhookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre);
}
hook_actions()
{
HookEvent("round_mvp", Event_RoundMVP);
}
unhook_actions()
{
UnhookEvent("round_mvp", Event_RoundMVP);
}
public OnClientPutInServer(client)
{
reset_player_stats(client);
}
public Event_PlayerShoot(Handle:event, const String:name[], bool:dontBroadcast)
{
// "userid" "short"
// "weapon" "string" // weapon name used
new attacker = GetClientOfUserId(GetEventInt(event, "userid"));
if (attacker > 0)
{
decl String: weapon[MAX_WEAPON_LEN];
GetEventString(event, "weapon", weapon, sizeof(weapon));
new weapon_index = get_weapon_index(weapon);
if (weapon_index > -1 && weapon_index < IGNORE_SHOTS_START)
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_SHOTS]++;
}
}
}
public Event_PlayerHurt(Handle:event, const String:name[], bool:dontBroadcast)
{
// "userid" "short" // player index who was hurt
// "attacker" "short" // player index who attacked
// "health" "byte" // remaining health points
// "armor" "byte" // remaining armor points
// "weapon" "string" // weapon name attacker used, if not the world
// "dmg_health" "byte" // damage done to health
// "dmg_armor" "byte" // damage done to armor
// "hitgroup" "byte" // hitgroup that was damaged
new attacker = GetClientOfUserId(GetEventInt(event, "attacker"));
if (attacker > 0) {
decl String: weapon[MAX_WEAPON_LEN];
GetEventString(event, "weapon", weapon, sizeof(weapon));
new weapon_index = get_weapon_index(weapon);
if (weapon_index > -1)
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_HITS]++;
g_weapon_stats[attacker][weapon_index][LOG_HIT_DAMAGE] += GetEventInt(event, "dmg_health");
new hitgroup = GetEventInt(event, "hitgroup");
if (hitgroup < 8)
{
g_weapon_stats[attacker][weapon_index][hitgroup + LOG_HIT_OFFSET]++;
}
}
}
}
public Action:Event_PlayerDeathPre(Handle:event, const String:name[], bool:dontBroadcast)
{
// "userid" "short" // user ID who died
// "attacker" "short" // user ID who killed
// "weapon" "string" // weapon name killer used
// "headshot" "bool" // signals a headshot
new attacker = GetEventInt(event, "attacker");
if (g_loglocations)
{
LogKillLoc(GetClientOfUserId(attacker), GetClientOfUserId(GetEventInt(event, "userid")));
}
if (g_logheadshots && GetEventBool(event, "headshot"))
{
LogPlayerEvent(GetClientOfUserId(attacker), "triggered", "headshot");
}
return Plugin_Continue;
}
public Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast)
{
// this extents the original player_death by a new fields
// "userid" "short" // user ID who died
// "attacker" "short" // user ID who killed
// "weapon" "string" // weapon name killer used
// "headshot" "bool" // signals a headshot
// "dominated" "short" // did killer dominate victim with this kill
// "revenge" "short" // did killer get revenge on victim with this kill
new victim = GetClientOfUserId(GetEventInt(event, "userid"));
new attacker = GetClientOfUserId(GetEventInt(event, "attacker"));
decl String: weapon[MAX_WEAPON_LEN];
GetEventString(event, "weapon", weapon, sizeof(weapon));
if (attacker <= 0 || victim <= 0)
{
return;
}
if (g_logwstats)
{
new weapon_index = get_weapon_index(weapon);
if (weapon_index > -1)
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_KILLS]++;
if (GetEventBool(event, "headshot"))
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_HEADSHOTS]++;
}
g_weapon_stats[victim][weapon_index][LOG_HIT_DEATHS]++;
if (GetClientTeam(attacker) == GetClientTeam(victim))
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_TEAMKILLS]++;
}
dump_player_stats(victim);
}
}
if (g_logktraj)
{
LogPSKillTraj(attacker, victim, weapon);
}
if (g_logactions)
{
// these are only in Orangebox CS:S. These properties won't exist on ep1 css and should eval to 0/false.
if (GetEventInt(event, "dominated"))
{
LogPlyrPlyrEvent(attacker, victim, "triggered", "domination");
}
else if (GetEventInt(event, "revenge"))
{
LogPlyrPlyrEvent(attacker, victim, "triggered", "revenge");
}
}
}
public Event_PlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast)
{
// "userid" "short" // user ID on server
new client = GetClientOfUserId(GetEventInt(event, "userid"));
if (client > 0)
{
reset_player_stats(client);
}
}
public Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast)
{
WstatsDumpAll();
}
public Event_RoundMVP(Handle:event, const String:name[], bool:dontBroadcast)
{
LogPlayerEvent(GetClientOfUserId(GetEventInt(event, "userid")), "triggered", "round_mvp");
}
public Action:Event_PlayerDisconnect(Handle:event, const String:name[], bool:dontBroadcast)
{
new client = GetClientOfUserId(GetEventInt(event, "userid"));
OnPlayerDisconnect(client);
return Plugin_Continue;
}
public Action:LogMap(Handle:timer)
{
// Called 1 second after OnPluginStart since srcds does not log the first map loaded. Idea from Stormtrooper's "mapfix.sp" for psychostats
LogMapLoad();
}
public OnCvarWstatsChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_logwstats;
g_logwstats = GetConVarBool(g_cvar_wstats);
if (old_value != g_logwstats)
{
if (g_logwstats)
{
hook_wstats();
if (!g_logktraj && !g_logactions)
{
HookEvent("player_death", Event_PlayerDeath);
}
}
else
{
unhook_wstats();
if (!g_logktraj && !g_logactions)
{
UnhookEvent("player_death", Event_PlayerDeath);
}
}
}
}
public OnCvarActionsChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_logactions;
g_logactions = GetConVarBool(g_cvar_actions);
if (old_value != g_logactions)
{
if (g_logactions)
{
hook_actions();
if (!g_logktraj && !g_logwstats)
{
HookEvent("player_death", Event_PlayerDeath);
}
}
else
{
unhook_actions();
if (!g_logktraj && !g_logwstats)
{
UnhookEvent("player_death", Event_PlayerDeath);
}
}
}
}
public OnCvarHeadshotsChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_logheadshots;
g_logheadshots = GetConVarBool(g_cvar_headshots);
if (old_value != g_logheadshots)
{
if (g_logheadshots && !g_loglocations)
{
HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
}
else if (!g_loglocations)
{
UnhookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
}
}
}
public OnCvarLocationsChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_loglocations;
g_loglocations = GetConVarBool(g_cvar_locations);
if (old_value != g_loglocations)
{
if (g_loglocations && !g_logheadshots)
{
HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
}
else if (!g_logheadshots)
{
UnhookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
}
}
}
public OnCvarKtrajChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_logktraj;
g_logktraj = GetConVarBool(g_cvar_ktraj);
if (old_value != g_logktraj)
{
if (g_logktraj && !g_logwstats && !g_logactions)
{
HookEvent("player_death", Event_PlayerDeath);
}
else if (!g_logwstats && !g_logactions)
{
UnhookEvent("player_death", Event_PlayerDeath);
}
}
}

View File

@ -0,0 +1,444 @@
/**
* HLstatsX Community Edition - SourceMod plugin to generate advanced weapon logging
* http://www.hlxcommunity.com
* Copyright (C) 2009-2012 Nicholas Hastings (psychonic)
* Copyright (C) 2007-2008 TTS Oetzel & Goerz GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma semicolon 1
#include <sourcemod>
#include <sdktools>
#include <sdkhooks>
#define NAME "SuperLogs: Dino D-Day"
#define VERSION "1.1.1"
#define MAX_LOG_WEAPONS 47
#define MAX_WEAPON_LEN 12
#define PREFIX_LEN 7
// Some DDD Things
#define WEAPON_MG42 18
#define WEAPON_TREX 37
#define PLAYERCLASS_TREX 8
#define PLAYERCLASS_DILOPHOSAURUS 5
#define CUSTOMKILL_GOAT 5
#define GOAT_BITE 19
#define DDD_TEAM_ALLIES 2
#define DDD_TEAM_AXIS 3
new g_weapon_stats[MAXPLAYERS+1][MAX_LOG_WEAPONS][15];
new const String:g_weapon_list[MAX_LOG_WEAPONS][MAX_WEAPON_LEN] = {
"",
"mp40",
"thompson",
"shotgun",
"",
"pistol",
"",
"",
"garand",
"bar",
"luger",
"",
"piat",
"",
"mosin",
"k98",
"",
"",
"mg42",
"",
"k98sniper",
"",
"",
"",
"flechette",
"",
"",
"",
"",
"mp44",
"",
"",
"sten",
"p38",
"nagant",
"",
"",
"trex",
"",
"",
"trigger",
"stygimoloch",
"",
"",
"",
"carbine",
"greasegun"
};
new g_iNextHitgroup[MAXPLAYERS+1];
new bool:g_DiloGoatBite[MAXPLAYERS+1];
new g_LastClass[MAXPLAYERS+1] = { -1, ... };
new g_LastTeam[MAXPLAYERS+1] = { -1, ... };
new bool:g_bLate;
new bool:g_bIgnoreLog;
PauseLogging() { g_bIgnoreLog = true; }
ResumeLogging() { g_bIgnoreLog = false; }
#include <loghelper>
#include <wstatshelper>
public Plugin:myinfo = {
name = NAME,
author = "psychonic and FeuerSturm",
description = "Advanced logging for Dino D-Day. Generates auxilary logging for use with log parsers such as HLstatsX and Psychostats",
version = VERSION,
url = "http://www.hlxce.com"
};
public APLRes:AskPluginLoad2( Handle:myself, bool:late, String:error[], err_max )
{
decl String:szGameDir[64];
GetGameFolderName( szGameDir, sizeof(szGameDir) );
if ( !!strcmp( szGameDir, "dinodday" ) )
{
strcopy( error, err_max, "This plugin is only supported on Dino D-Day" );
return APLRes_Failure;
}
g_bLate = late;
return APLRes_Success;
}
public OnPluginStart()
{
CreatePopulateWeaponTrie();
new Handle:hVersion = CreateConVar( "superlogs_dinodday_version", VERSION, NAME, FCVAR_NOTIFY|FCVAR_DONTRECORD );
SetConVarString(hVersion, VERSION, false, true);
HookEvent( "player_death", Event_PlayerDeathPre, EventHookMode_Pre );
HookEvent( "player_death", Event_PlayerDeath, EventHookMode_Post );
HookEvent( "player_spawn", Event_PlayerSpawn, EventHookMode_Post );
HookEvent( "update_roundscore", Event_RoundEnd, EventHookMode_Post );
HookEvent( "player_changeclass", Event_PlayerChangeClassPre, EventHookMode_Pre );
HookEvent( "player_changeclass", Event_PlayerChangeClass, EventHookMode_Post );
HookEvent( "teamplay_point_captured", Event_PointCaptured, EventHookMode_Post );
AddGameLogHook( OnGameLog );
AddTempEntHook( "Shotgun Shot", OnFireBullets );
}
public OnAllPluginsLoaded()
{
for ( new i = 1; i <= MaxClients; i++ )
{
if ( IsClientInGame( i ) )
{
OnClientPutInServer( i );
}
}
}
public OnMapStart()
{
static bool:bLoggedMap = false;
if( g_bLate && !bLoggedMap )
{
LogMapLoad();
}
bLoggedMap = true;
GetTeams();
}
public OnClientPutInServer( client )
{
SDKHook( client, SDKHook_TraceAttackPost, OnTraceAttack );
SDKHook( client, SDKHook_OnTakeDamagePost, OnTakeDamage );
reset_player_stats( client );
g_LastTeam[client] = -1;
g_LastClass[client] = -1;
g_DiloGoatBite[client] = false;
}
public Action:OnFireBullets( const String:szName[], const clients[], clientCount, Float:flDelay )
{
new client = TE_ReadNum( "m_iPlayer" ) + 1;
new weapon_index = TE_ReadNum( "m_iWeaponID" );
if( weapon_index >= 0 )
{
if( GetEntProp(client, Prop_Send, "m_iPlayerClass") == PLAYERCLASS_TREX && weapon_index == WEAPON_MG42 )
{
weapon_index = WEAPON_TREX;
}
g_weapon_stats[client][weapon_index][LOG_HIT_SHOTS]++;
}
return Plugin_Continue;
}
public OnTraceAttack( victim, attacker, inflictor, Float:damage, damagetype, ammotype, hitbox, hitgroup )
{
if ( hitgroup > 0 && attacker > 0 && attacker <= MaxClients && victim > 0 && victim <= MaxClients )
{
g_iNextHitgroup[victim] = hitgroup;
}
}
public OnTakeDamage( victim, attacker, inflictor, Float:damage, damagetype )
{
if ( attacker > 0 && attacker <= MaxClients && victim > 0 && victim <= MaxClients )
{
decl String: weapon[MAX_WEAPON_LEN + PREFIX_LEN];
GetClientWeapon( attacker, weapon, sizeof(weapon) );
new weapon_index = get_weapon_index(weapon[PREFIX_LEN]);
new hitgroup = g_iNextHitgroup[victim];
if ( hitgroup < 8 )
{
hitgroup += LOG_HIT_OFFSET;
}
new bool:headshot = ( !IsPlayerAlive( victim ) && g_iNextHitgroup[victim] == HITGROUP_HEAD );
if ( weapon_index > -1 )
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_HITS]++;
g_weapon_stats[attacker][weapon_index][LOG_HIT_DAMAGE] += RoundToNearest( damage );
g_weapon_stats[attacker][weapon_index][hitgroup]++;
if ( headshot )
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_HEADSHOTS]++;
LogPlayerEvent( attacker, "triggered", "headshot" );
}
}
g_iNextHitgroup[victim] = 0;
}
}
public OnEntityCreated(entity, const String:classname[])
{
if(entity > MaxClients && IsValidEdict(entity) && StrEqual(classname, "npc_goat", true))
{
SDKHook(entity, SDKHook_ThinkPost, OnGoatThinkPost);
}
}
public OnGoatThinkPost(npc_goat)
{
if(npc_goat > MaxClients && IsValidEdict(npc_goat))
{
new client = GetEntPropEnt(npc_goat, Prop_Send, "moveparent");
if(client >= 1 && client <= MaxClients && IsClientInGame(client) && IsPlayerAlive(client) && GetClientTeam(client) == DDD_TEAM_AXIS && GetEntProp(client, Prop_Send, "m_iPlayerClass") == PLAYERCLASS_DILOPHOSAURUS)
{
if(GetEntProp(client, Prop_Send, "m_nSequence") == GOAT_BITE)
{
g_DiloGoatBite[client] = true;
}
}
}
}
public Action:OnGameLog( const String:szMessage[] )
{
if( g_bIgnoreLog )
return Plugin_Handled;
return Plugin_Continue;
}
public Action:Event_PointCaptured( Handle:event, const String:name[], bool:dontBroadcast )
{
new client;
decl String:cappers[256];
GetEventString(event, "cappers", cappers, sizeof(cappers));
for (new i = 0 ; i < strlen(cappers); i++)
{
client = cappers[i];
if(client > 0 && client <= MaxClients && IsClientInGame(client))
{
LogPlayerEvent(client, "triggered", "point_captured");
}
}
return Plugin_Continue;
}
public Action:Event_PlayerChangeClassPre( Handle:event, const String:name[], bool:dontBroadcast )
{
PauseLogging();
return Plugin_Continue;
}
public Event_PlayerChangeClass( Handle:event, const String:name[], bool:dontBroadcast )
{
ResumeLogging();
}
public Action:Event_PlayerDeathPre( Handle:event, const String:name[], bool:dontBroadcast )
{
PauseLogging();
new customkill = GetEventInt(event, "customkill");
if( customkill == CUSTOMKILL_GOAT )
{
new attacker = GetClientOfUserId( GetEventInt( event, "attacker" ) );
new victim = GetClientOfUserId( GetEventInt( event, "userid" ) );
if(attacker == victim && g_DiloGoatBite[attacker])
{
SetEventBroadcast(event, true);
return Plugin_Continue;
}
}
return Plugin_Continue;
}
public Event_PlayerDeath( Handle:event, const String:name[], bool:dontBroadcast )
{
ResumeLogging();
new victim = GetClientOfUserId( GetEventInt( event, "userid" ) );
if ( victim > 0 )
{
new attacker = GetClientOfUserId( GetEventInt( event, "attacker" ) );
decl String:weapon[32];
GetEventString( event, "weapon", weapon, sizeof(weapon) );
if( attacker > 0 && attacker != victim )
{
new weapon_index = get_weapon_index( weapon );
if ( weapon_index > -1 )
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_KILLS]++;
g_weapon_stats[victim][weapon_index][LOG_HIT_DEATHS]++;
if ( GetClientTeam( attacker ) == GetClientTeam( victim ) )
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_TEAMKILLS]++;
}
}
LogKill( attacker, victim, weapon, true );
new dominated = GetEventInt(event, "dominated");
new revenge = GetEventInt(event, "revenge");
if(dominated == 1)
{
LogPlyrPlyrEvent(attacker, victim, "triggered", "dominated");
}
else if(revenge == 1)
{
LogPlyrPlyrEvent(attacker, victim, "triggered", "revenge");
}
new assisteruid = GetEventInt(event, "assister");
if(assisteruid != -1)
{
new assister = GetClientOfUserId(assisteruid);
if(assister > 0 && attacker != assister)
{
LogPlayerEvent(assister, "triggered", "assister");
if(GetEventInt(event, "assister_revenge") == 1)
{
LogPlyrPlyrEvent(assister, victim, "triggered", "assister_revenge");
}
else if(GetEventInt(event, "assister_dominated") == 1)
{
LogPlyrPlyrEvent(assister, victim, "triggered", "assister_dominated");
}
}
}
}
else
{
new customkill = GetEventInt(event, "customkill");
if( customkill == CUSTOMKILL_GOAT )
{
if(g_DiloGoatBite[attacker])
{
g_DiloGoatBite[attacker] = false;
return;
}
LogPlayerEvent(attacker, "triggered", "kill_goat");
return;
}
LogPlayerEvent(victim, "committed suicide with", weapon);
}
dump_player_stats( victim );
}
}
public Event_PlayerSpawn( Handle:event, const String:name[], bool:dontBroadcast )
{
new client = GetClientOfUserId( GetEventInt( event, "userid" ) );
if( client == 0 || !IsClientInGame(client) )
return;
reset_player_stats( client );
new currentTeam = GetClientTeam( client );
if( currentTeam != DDD_TEAM_ALLIES && currentTeam != DDD_TEAM_AXIS )
return;
new currentClass = GetEntProp( client, Prop_Send, "m_iPlayerClass" );
if( g_LastClass[client] != currentClass || g_LastTeam[client] != currentTeam )
{
decl String:szRoleString[32];
if( currentTeam == DDD_TEAM_ALLIES )
Format( szRoleString, sizeof(szRoleString), "#class_blue_class%d", currentClass+1 );
else // == DDD_TEAM_AXIS
Format( szRoleString, sizeof(szRoleString), "#class_red_class%d", currentClass+1 );
LogRoleChange( client, szRoleString );
g_LastTeam[client] = currentTeam;
g_LastClass[client] = currentClass;
}
g_DiloGoatBite[client] = false;
}
public Event_RoundEnd( Handle:event, const String:name[], bool:dontBroadcast )
{
new winner = GetEventInt( event, "team_that_won" );
if( winner == DDD_TEAM_ALLIES || winner == DDD_TEAM_AXIS )
{
decl String:round_win[32];
Format(round_win, sizeof(round_win), "%s", winner == DDD_TEAM_ALLIES ? "roundwin_allies" : "roundwin_axis");
LogTeamEvent(winner, "triggered", round_win);
WstatsDumpAll();
}
}
public OnClientDisconnect( client )
{
OnPlayerDisconnect( client );
}

View File

@ -0,0 +1,410 @@
/**
* HLstatsX Community Edition - SourceMod plugin to generate advanced weapon logging
* http://www.hlxcommunity.com
* Copyright (C) 2009 Nicholas Hastings (psychonic)
* Copyright (C) 2007-2008 TTS Oetzel & Goerz GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma semicolon 1
#include <sourcemod>
#include <sdktools>
#define NAME "SuperLogs: DOD:S"
#define VERSION "1.1.3"
#define MAX_LOG_WEAPONS 27
#define IGNORE_SHOTS_START 20
#define MAX_WEAPON_LEN 16
new g_weapon_stats[MAXPLAYERS+1][MAX_LOG_WEAPONS][15];
new const String: g_weapon_list[MAX_LOG_WEAPONS][MAX_WEAPON_LEN] = {
"amerknife",
"spade",
"colt",
"p38",
"c96",
"garand",
"m1carbine",
"k98",
"spring",
"k98_scoped",
"thompson",
"mp40",
"mp44",
"bar",
"30cal",
"mg42",
"bazooka",
"pschreck",
"frag_us",
"frag_ger",
"",
"",
"smoke_us",
"smoke_ger",
"riflegren_us",
"riflegren_ger",
"dod_bomb_target"
};
new Handle:g_cvar_wstats = INVALID_HANDLE;
new Handle:g_cvar_headshots = INVALID_HANDLE;
new Handle:g_cvar_locations = INVALID_HANDLE;
new Handle:g_cvar_ktraj = INVALID_HANDLE;
new Handle:g_cvar_version = INVALID_HANDLE;
new bool:g_logwstats = true;
new bool:g_logheadshots = true;
new bool:g_loglocations = true;
new bool:g_logktraj = true;
#include <loghelper>
#include <wstatshelper>
public Plugin:myinfo = {
name = NAME,
author = "psychonic",
description = "Advanced logging for DOD:S. Generates auxilary logging for use with log parsers such as HLstatsX and Psychostats",
version = VERSION,
url = "http://www.hlxcommunity.com"
};
#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3
public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max)
#else
public bool:AskPluginLoad(Handle:myself, bool:late, String:error[], err_max)
#endif
{
decl String:game_description[64];
GetGameDescription(game_description, sizeof(game_description), true);
if (StrContains(game_description, "Day of Defeat", false) == -1)
{
decl String:game_folder[64];
GetGameFolderName(game_folder, sizeof(game_folder));
if (strncmp(game_folder, "dod", 3, false) != 0)
{
strcopy(error, err_max, "This plugin is only supported on DOD:S");
#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3
return APLRes_Failure;
#else
return false;
#endif
}
}
#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3
return APLRes_Success;
#else
return true;
#endif
}
public OnPluginStart()
{
CreatePopulateWeaponTrie();
g_cvar_wstats = CreateConVar("superlogs_wstats", "1", "Enable logging of weapon stats (default on)", 0, true, 0.0, true, 1.0);
g_cvar_headshots = CreateConVar("superlogs_headshots", "1", "Enable logging of headshot player action (default on)", 0, true, 0.0, true, 1.0);
g_cvar_locations = CreateConVar("superlogs_locations", "1", "Enable logging of location on player death (default on)", 0, true, 0.0, true, 1.0);
g_cvar_ktraj = CreateConVar("superlogs_ktraj", "0", "Enable Psychostats \"KTRAJ\" logging (default off)", 0, true, 0.0, true, 1.0);
HookConVarChange(g_cvar_wstats, OnCvarWstatsChange);
HookConVarChange(g_cvar_headshots, OnCvarHeadshotsChange);
HookConVarChange(g_cvar_locations, OnCvarLocationsChange);
HookConVarChange(g_cvar_ktraj, OnCvarKtrajChange);
g_cvar_version = CreateConVar("superlogs_dods_version", VERSION, NAME, FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY);
hook_wstats();
HookEvent("player_hurt", Event_PlayerHurt);
HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
HookEvent("player_death", Event_PlayerDeath);
CreateTimer(1.0, LogMap);
GetTeams();
}
public OnMapStart()
{
GetTeams();
}
public OnConfigsExecuted()
{
decl String:version[255];
GetConVarString(g_cvar_version, version, sizeof(version));
SetConVarString(g_cvar_version, version);
}
hook_wstats()
{
HookEvent("dod_stats_weapon_attack", Event_PlayerShoot);
HookEvent("player_spawn", Event_PlayerSpawn);
HookEvent("dod_round_win", Event_RoundEnd, EventHookMode_PostNoCopy);
HookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre);
}
unhook_wstats()
{
UnhookEvent("dod_stats_weapon_attack", Event_PlayerShoot);
UnhookEvent("player_spawn", Event_PlayerSpawn);
UnhookEvent("dod_round_win", Event_RoundEnd, EventHookMode_PostNoCopy);
UnhookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre);
}
public OnClientPutInServer(client)
{
reset_player_stats(client);
}
public Event_PlayerShoot(Handle:event, const String:name[], bool:dontBroadcast)
{
// "attacker" "short"
// "weapon" "byte"
new attacker = GetClientOfUserId(GetEventInt(event, "attacker"));
if (attacker > 0)
{
new weapon_index = GetEventInt(event, "weapon") - 1;
switch (weapon_index)
{
case 28, 29:
weapon_index = -1;
case 30:
weapon_index = 5;
case 32:
weapon_index = 8;
case 33:
weapon_index = 9;
case 34:
weapon_index = 14;
case 35:
weapon_index = 15;
case 37:
weapon_index = 12;
}
if (weapon_index > -1 && weapon_index < IGNORE_SHOTS_START)
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_SHOTS]++;
}
}
}
public Event_PlayerHurt(Handle:event, const String:name[], bool:dontBroadcast)
{
// "userid" "short" // user ID who was hurt
// "attacker" "short" // user ID who attacked
// "weapon" "string" // weapon name attacker used
// "health" "byte" // health remaining
// "damage" "byte" // how much damage in this attack
// "hitgroup" "byte" // what hitgroup was hit
new attacker = GetClientOfUserId(GetEventInt(event, "attacker"));
new hitgroup = GetEventInt(event, "hitgroup");
new bool:headshot = (GetEventInt(event, "health") <= 0 && hitgroup == HITGROUP_HEAD);
if (g_logwstats && attacker > 0)
{
decl String: weapon[MAX_WEAPON_LEN];
GetEventString(event, "weapon", weapon, sizeof(weapon));
new weapon_index = get_weapon_index(weapon);
if (weapon_index > -1)
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_HITS]++;
g_weapon_stats[attacker][weapon_index][LOG_HIT_DAMAGE] += GetEventInt(event, "damage");
if (hitgroup < 8)
{
g_weapon_stats[attacker][weapon_index][hitgroup + LOG_HIT_OFFSET]++;
}
if (headshot)
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_HEADSHOTS]++;
}
}
}
if (g_logheadshots && headshot)
{
LogPlayerEvent(attacker, "triggered", "headshot");
}
}
public Action:Event_PlayerDeathPre(Handle:event, const String:name[], bool:dontBroadcast)
{
LogKillLoc(GetClientOfUserId(GetEventInt(event, "attacker")), GetClientOfUserId(GetEventInt(event, "userid")));
return Plugin_Continue;
}
public Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast)
{
// this extents the original player_death
// "userid" "short" // user ID who died
// "attacker" "short" // user ID who killed
// "weapon" "string" // weapon name killed used
new victim = GetClientOfUserId(GetEventInt(event, "userid"));
new attacker = GetClientOfUserId(GetEventInt(event, "attacker"));
decl String: weapon[MAX_WEAPON_LEN];
GetEventString(event, "weapon", weapon, sizeof(weapon));
if (g_logwstats && victim > 0 && attacker > 0)
{
new weapon_index = get_weapon_index(weapon);
if (weapon_index > -1)
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_KILLS]++;
g_weapon_stats[victim][weapon_index][LOG_HIT_DEATHS]++;
if (GetClientTeam(attacker) == GetClientTeam(victim))
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_TEAMKILLS]++;
}
dump_player_stats(victim);
}
}
if (g_logktraj)
{
LogPSKillTraj(attacker, victim, weapon);
}
}
public Event_PlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast)
{
// "userid" "short" // user ID on server
new client = GetClientOfUserId(GetEventInt(event, "userid"));
if (client > 0)
{
reset_player_stats(client);
}
}
public Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast)
{
WstatsDumpAll();
}
public Action:Event_PlayerDisconnect(Handle:event, const String:name[], bool:dontBroadcast)
{
new client = GetClientOfUserId(GetEventInt(event, "userid"));
OnPlayerDisconnect(client);
return Plugin_Continue;
}
public Action:LogMap(Handle:timer)
{
// Called 1 second after OnPluginStart since srcds does not log the first map loaded. Idea from Stormtrooper's "mapfix.sp" for psychostats
LogMapLoad();
}
public OnCvarWstatsChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_logwstats;
g_logwstats = GetConVarBool(g_cvar_wstats);
if (old_value != g_logwstats)
{
if (g_logwstats)
{
hook_wstats();
if (!g_logheadshots)
{
HookEvent("player_hurt", Event_PlayerHurt);
}
if (!g_logktraj)
{
HookEvent("player_death", Event_PlayerDeath);
}
}
else
{
unhook_wstats();
if (!g_logheadshots)
{
UnhookEvent("player_hurt", Event_PlayerHurt);
}
if (!g_logktraj)
{
UnhookEvent("player_death", Event_PlayerDeath);
}
}
}
}
public OnCvarHeadshotsChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_logheadshots;
g_logheadshots = GetConVarBool(g_cvar_headshots);
if (old_value != g_logheadshots)
{
if (g_logheadshots && !g_logwstats)
{
HookEvent("player_hurt", Event_PlayerHurt);
}
else if (!g_logwstats)
{
UnhookEvent("player_hurt", Event_PlayerHurt);
}
}
}
public OnCvarLocationsChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_loglocations;
g_loglocations = GetConVarBool(g_cvar_locations);
if (old_value != g_loglocations)
{
if (g_loglocations)
{
HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
}
else
{
UnhookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
}
}
}
public OnCvarKtrajChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_logktraj;
g_logktraj = GetConVarBool(g_cvar_ktraj);
if (old_value != g_logktraj)
{
if (g_logktraj && !g_logwstats)
{
HookEvent("player_death", Event_PlayerDeath);
}
else if (!g_logwstats)
{
UnhookEvent("player_death", Event_PlayerDeath);
}
}
}

View File

@ -0,0 +1,89 @@
/**
* HLstatsX Community Edition - SourceMod plugin to generate advanced weapon logging
* http://www.hlxcommunity.com
* Copyright (C) 2009 Nicholas Hastings (psychonic)
* Copyright (C) 2007-2008 TTS Oetzel & Goerz GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma semicolon 1
#include <sourcemod>
#include <sdktools>
#define NAME "SuperLogs: Generic"
#define VERSION "1.0"
new Handle:g_cvar_enable = INVALID_HANDLE;
new bool:g_log = true;
#include <loghelper>
public Plugin:myinfo = {
name = NAME,
author = "psychonic",
description = "Advanced logging. Generates auxilary logging for use with log parsers such as HLstatsX and Psychostats",
version = VERSION,
url = "http://www.hlxcommunity.com"
};
public OnPluginStart()
{
g_cvar_enable = CreateConVar("superlogs_locations", "1", "Enable logging of kill coordinates (default on)", 0, true, 0.0, true, 1.0);
HookConVarChange(g_cvar_enable, OnCvarEnableChange);
CreateConVar("superlogs_generic_version", VERSION, NAME, FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY);
HookEvent("player_death", Event_PlayerDeath, EventHookMode_Pre);
CreateTimer(1.0, LogMap);
}
public Action:Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast)
{
LogKillLoc(GetClientOfUserId(GetEventInt(event, "attacker")), GetClientOfUserId(GetEventInt(event, "userid")));
return Plugin_Continue;
}
public OnCvarEnableChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_log;
g_log = GetConVarBool(g_cvar_enable);
if (old_value != g_log)
{
if (g_log)
{
HookEvent("player_death", Event_PlayerDeath, EventHookMode_Pre);
}
else
{
UnhookEvent("player_death", Event_PlayerDeath, EventHookMode_Pre);
}
}
}
public Action:LogMap(Handle:timer)
{
// Called 1 second after OnPluginStart since srcds does not log the first map loaded. Idea from Stormtrooper's "mapfix.sp" for psychostats
LogMapLoad();
}

View File

@ -0,0 +1,478 @@
/**
* HLstatsX Community Edition - SourceMod plugin to generate advanced weapon logging
* http://www.hlxcommunity.com
* Copyright (C) 2009 Nicholas Hastings (psychonic)
* Copyright (C) 2007-2008 TTS Oetzel & Goerz GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma semicolon 1
#include <sourcemod>
#include <sdktools>
#define NAME "SuperLogs: GES"
#define VERSION "1.1.2"
#define MAX_LOG_WEAPONS 18
#define MAX_WEAPON_LEN 13
#define PREFIX_LEN 7
#define GES
new g_weapon_stats[MAXPLAYERS+1][MAX_LOG_WEAPONS][20];
new const String:g_weapon_list[MAX_LOG_WEAPONS][MAX_WEAPON_LEN] = {
"pp7",
"pp7_silenced",
"dd44",
"klobb",
"cmag",
"kf7",
"zmg",
"d5k",
"d5k_silenced",
"phantom",
"AR33",
"rcp90",
"shotgun",
"auto_shotgun",
"sniper_rifle",
"golden_gun",
"silver_pp7",
"golden_pp7"
};
new const String:g_weapon_loglist[MAX_LOG_WEAPONS][] = {
"#GE_PP7",
"#GE_PP7_SILENCED",
"#GE_DD44",
"#GE_Klobb",
"#GE_CougarMagnum",
"#GE_KF7Soviet",
"#GE_ZMG",
"#GE_D5K",
"#GE_D5K_SILENCED",
"#GE_Phantom",
"#GE_AR33",
"#GE_RCP90",
"#GE_Shotgun",
"#GE_AutoShotgun",
"#GE_SniperRifle",
"#GE_GoldenGun",
"#GE_SilverPP7",
"#GE_GoldPP7"
};
new Handle:g_cvar_wstats = INVALID_HANDLE;
new Handle:g_cvar_headshots = INVALID_HANDLE;
new Handle:g_cvar_locations = INVALID_HANDLE;
new Handle:g_cvar_actions = INVALID_HANDLE;
new Handle:g_cvar_classchanges = INVALID_HANDLE;
new bool:g_logwstats = true;
new bool:g_logheadshots = true;
new bool:g_loglocations = true;
new bool:g_logactions = true;
new bool:g_logclasschanges = true;
#include <loghelper>
#include <wstatshelper>
public Plugin:myinfo = {
name = NAME,
author = "psychonic",
description = "Advanced logging for GoldenEye: Source. Generates auxilary logging for use with log parsers such as HLstatsX and Psychostats",
version = VERSION,
url = "http://www.hlxcommunity.com"
};
public OnPluginStart()
{
CreatePopulateWeaponTrie();
g_cvar_wstats = CreateConVar("superlogs_wstats", "1", "Enable logging of weapon stats (default on)", 0, true, 0.0, true, 1.0);
g_cvar_headshots = CreateConVar("superlogs_headshots", "1", "Enable logging of headshot player action (default off)", 0, true, 0.0, true, 1.0);
g_cvar_locations = CreateConVar("superlogs_locations", "1", "Enable logging of location on player death (default on)", 0, true, 0.0, true, 1.0);
g_cvar_actions = CreateConVar("superlogs_actions", "1", "Enable logging of actions, such as round winner and awards won (default on)", 0, true, 0.0, true, 1.0);
g_cvar_classchanges = CreateConVar("superlogs_classchanges", "1", "Enable logging of character changes (default on)", 0, true, 0.0, true, 1.0);
HookConVarChange(g_cvar_wstats, OnCvarWstatsChange);
HookConVarChange(g_cvar_headshots, OnCvarHeadshotsChange);
HookConVarChange(g_cvar_locations, OnCvarLocationsChange);
HookConVarChange(g_cvar_actions, OnCvarActionsChange);
HookConVarChange(g_cvar_classchanges, OnCvarClasschangesChange);
CreateConVar("superlogs_ges_version", VERSION, NAME, FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY);
hook_wstats();
HookEvent("player_hurt", Event_PlayerHurt);
HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
HookEvent("player_changeident", Event_RoleChange);
HookEvent("round_end", Event_RoundEnd);
CreateTimer(1.0, LogMap);
GetTeams();
}
public OnMapStart()
{
GetTeams();
}
hook_wstats()
{
HookEvent("player_death", Event_PlayerDeath);
HookEvent("player_shoot", Event_PlayerShoot);
HookEvent("player_spawn", Event_PlayerSpawn);
HookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre);
}
unhook_wstats()
{
UnhookEvent("player_death", Event_PlayerDeath);
UnhookEvent("player_shoot", Event_PlayerShoot);
UnhookEvent("player_spawn", Event_PlayerSpawn);
UnhookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre);
}
public OnClientPutInServer(client)
{
reset_player_stats(client);
}
public Event_PlayerShoot(Handle:event, const String:name[], bool:dontBroadcast)
{
// "userid" "local" // user ID on server
// "weapon" "local" // weapon name
// "mode" "local" // weapon mode [0 normal, 1 aimed]
new attacker = GetClientOfUserId(GetEventInt(event, "userid"));
if (g_logwstats && attacker > 0)
{
decl String:weapon[MAX_WEAPON_LEN+PREFIX_LEN];
GetEventString(event, "weapon", weapon, sizeof(weapon));
new weapon_index = get_weapon_index(weapon[PREFIX_LEN]);
if (weapon_index > -1)
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_SHOTS]++;
}
}
}
public Event_PlayerHurt(Handle:event, const String:name[], bool:dontBroadcast)
{
// "userid" "local" // user ID who was hurt
// "attacker" "local" // user ID who attacked
// "weapon" "local" // weapon name attacker used
// "health" "local" // health remaining
// "armor" "local" // armor remaining
// "damage" "local" // how much damage in this attack
// "hitgroup" "local" // what hitgroup was hit
new attacker = GetClientOfUserId(GetEventInt(event, "attacker"));
new hitgroup = GetEventInt(event, "hitgroup");
new bool:headshot = (GetEventInt(event, "health") <= 0 && hitgroup == HITGROUP_HEAD);
if (g_logwstats && attacker > 0)
{
decl String: weapon[MAX_WEAPON_LEN+PREFIX_LEN];
GetEventString(event, "weapon", weapon, sizeof(weapon));
new weapon_index = get_weapon_index(weapon[PREFIX_LEN]);
if (weapon_index > -1)
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_HITS]++;
g_weapon_stats[attacker][weapon_index][LOG_HIT_DAMAGE] += GetEventInt(event, "damage");
if (hitgroup < 8)
{
g_weapon_stats[attacker][weapon_index][hitgroup + LOG_HIT_OFFSET]++;
}
if (headshot)
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_HEADSHOTS]++;
}
}
}
if (g_logheadshots && headshot)
{
LogPlayerEvent(attacker, "triggered", "headshot");
}
}
public Action:Event_PlayerDeathPre(Handle:event, const String:name[], bool:dontBroadcast)
{
LogKillLoc(GetClientOfUserId(GetEventInt(event, "attacker")), GetClientOfUserId(GetEventInt(event, "userid")));
return Plugin_Continue;
}
public Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast)
{
// "userid" "short" // user ID who died
// "attacker" "short" // user ID who killed
// "weapon" "string" // weapon name killed used
// "weaponid" "short" // GE Weapon ID (for easy comparison)
// "custom" "byte" // Used for achievements
new victim = GetClientOfUserId(GetEventInt(event, "userid"));
new attacker = GetClientOfUserId(GetEventInt(event, "attacker"));
if (g_logwstats && (victim > 0) && (attacker > 0))
{
decl String: weapon[MAX_WEAPON_LEN+PREFIX_LEN];
GetEventString(event, "weapon", weapon, sizeof(weapon));
new weapon_index = get_weapon_index(weapon[PREFIX_LEN]);
if (weapon_index > -1)
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_KILLS]++;
g_weapon_stats[victim][weapon_index][LOG_HIT_DEATHS]++;
if (GetClientTeam(attacker) == GetClientTeam(victim))
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_TEAMKILLS]++;
}
dump_player_stats(victim);
}
}
}
public Event_PlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast)
{
// "userid" "short" // user ID on server
new client = GetClientOfUserId(GetEventInt(event, "userid"));
if (client > 0)
{
reset_player_stats(client);
}
}
public Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast)
{
if (g_logwstats)
{
WstatsDumpAll();
}
if (g_logactions)
{
new winner = GetEventInt(event, "winnerid");
if (winner > 0)
{
LogPlayerEvent(winner, "triggered", "Round_Win", true);
}
else
{
winner = GetEventInt(event, "teamid");
if (winner > 0)
{
LogTeamEvent(winner, "triggered", "Round_Win_Team");
}
}
new awards[6];
awards[0] = GetEventInt(event, "award1_id");
awards[1] = GetEventInt(event, "award2_id");
awards[2] = GetEventInt(event, "award3_id");
awards[3] = GetEventInt(event, "award4_id");
awards[4] = GetEventInt(event, "award5_id");
awards[5] = GetEventInt(event, "award6_id");
new winners[6];
winners[0] = GetEventInt(event, "award1_winner");
winners[1] = GetEventInt(event, "award2_winner");
winners[2] = GetEventInt(event, "award3_winner");
winners[3] = GetEventInt(event, "award4_winner");
winners[4] = GetEventInt(event, "award5_winner");
winners[5] = GetEventInt(event, "award6_winner");
for (new i = 0; i < 6; i++)
{
if (winners[i] > 0)
{
switch(awards[i])
{
case 0:
LogPlayerEvent(winners[i], "triggered", "GE_AWARD_DEADLY", true);
case 1:
LogPlayerEvent(winners[i], "triggered", "GE_AWARD_HONORABLE", true);
case 2:
LogPlayerEvent(winners[i], "triggered", "GE_AWARD_PROFESSIONAL", true);
case 3:
LogPlayerEvent(winners[i], "triggered", "GE_AWARD_MARKSMANSHIP", true);
case 4:
LogPlayerEvent(winners[i], "triggered", "GE_AWARD_AC10", true);
case 5:
LogPlayerEvent(winners[i], "triggered", "GE_AWARD_FRANTIC", true);
case 6:
LogPlayerEvent(winners[i], "triggered", "GE_AWARD_WTA", true);
case 7:
LogPlayerEvent(winners[i], "triggered", "GE_AWARD_LEMMING", true);
case 8:
LogPlayerEvent(winners[i], "triggered", "GE_AWARD_LONGIN", true);
case 9:
LogPlayerEvent(winners[i], "triggered", "GE_AWARD_SHORTIN", true);
case 10:
LogPlayerEvent(winners[i], "triggered", "GE_AWARD_DISHONORABLE", true);
case 11:
LogPlayerEvent(winners[i], "triggered", "GE_AWARD_NOTAC10", true);
case 12:
LogPlayerEvent(winners[i], "triggered", "GE_AWARD_MOSTLYHARMLESS", true);
}
}
}
}
}
public Event_RoleChange(Handle:event, const String:name[], bool:dontBroadcast)
{
// "playerid" "short"
// "ident" "string"
new client = GetEventInt(event, "playerid");
if (client > 0)
{
decl String:ident[32];
GetEventString(event, "ident", ident, sizeof(ident));
LogRoleChange(client, ident);
}
}
public Action:Event_PlayerDisconnect(Handle:event, const String:name[], bool:dontBroadcast)
{
new client = GetClientOfUserId(GetEventInt(event, "userid"));
OnPlayerDisconnect(client);
return Plugin_Continue;
}
public Action:LogMap(Handle:timer)
{
// Called 1 second after OnPluginStart since srcds does not log the first map loaded. Idea from Stormtrooper's "mapfix.sp" for psychostats
LogMapLoad();
}
public OnCvarWstatsChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_logwstats;
g_logwstats = GetConVarBool(g_cvar_wstats);
if (old_value != g_logwstats)
{
if (g_logwstats)
{
hook_wstats();
if (!g_logheadshots)
{
HookEvent("player_hurt", Event_PlayerHurt);
}
if (!g_logactions)
{
HookEvent("round_end", Event_RoundEnd);
}
}
else
{
unhook_wstats();
if (!g_logheadshots)
{
UnhookEvent("player_hurt", Event_PlayerHurt);
}
if (!g_logactions)
{
UnhookEvent("round_end", Event_RoundEnd);
}
}
}
}
public OnCvarHeadshotsChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_logheadshots;
g_logheadshots = GetConVarBool(g_cvar_headshots);
if (old_value != g_logheadshots)
{
if (g_logheadshots && !g_logwstats)
{
HookEvent("player_hurt", Event_PlayerHurt);
}
else if (!g_logwstats)
{
UnhookEvent("player_hurt", Event_PlayerHurt);
}
}
}
public OnCvarLocationsChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_loglocations;
g_loglocations = GetConVarBool(g_cvar_locations);
if (old_value != g_loglocations)
{
if (g_loglocations)
{
HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
}
else
{
UnhookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
}
}
}
public OnCvarActionsChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_logactions;
g_logactions = GetConVarBool(g_cvar_actions);
if (old_value != g_logactions)
{
if (g_logactions && !g_logwstats)
{
HookEvent("round_end", Event_RoundEnd);
}
else if (!g_logactions)
{
UnhookEvent("round_end", Event_RoundEnd);
}
}
}
public OnCvarClasschangesChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_logclasschanges;
g_logclasschanges = GetConVarBool(g_cvar_classchanges);
if (old_value != g_logclasschanges)
{
if (g_logclasschanges)
{
HookEvent("player_changeident", Event_RoleChange);
}
else
{
UnhookEvent("player_changeident", Event_RoleChange);
}
}
}

View File

@ -0,0 +1,402 @@
/**
* HLstatsX Community Edition - SourceMod plugin to generate advanced weapon logging
* http://www.hlxcommunity.com
* Copyright (C) 2009-2010 Nicholas Hastings (psychonic)
* Copyright (C) 2007-2008 TTS Oetzel & Goerz GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma semicolon 1
#include <sourcemod>
#include <sdktools>
#include <sdkhooks>
#define NAME "SuperLogs: HL2MP"
#define VERSION "1.1.3"
#define MAX_LOG_WEAPONS 6
#define MAX_WEAPON_LEN 14
#define PREFIX_LEN 7
#define CROSSBOW 0
new g_weapon_stats[MAXPLAYERS+1][MAX_LOG_WEAPONS][15];
new const String:g_weapon_list[MAX_LOG_WEAPONS][MAX_WEAPON_LEN] = {
"crossbow_bolt",
"pistol",
"357",
"smg1",
"ar2",
"shotgun"
};
new Handle:g_cvar_headshots = INVALID_HANDLE;
new Handle:g_cvar_locations = INVALID_HANDLE;
new Handle:g_cvar_teamplay = INVALID_HANDLE;
new bool:g_logheadshots = true;
new bool:g_loglocations = true;
new g_iNextHitgroup[MAXPLAYERS+1];
new g_iNextBowHitgroup[MAXPLAYERS+1];
new g_bTeamPlay;
new g_iCrossBowOwnerOffs = -1;
new Handle:g_hBoltChecks = INVALID_HANDLE;
#include <loghelper>
#include <wstatshelper>
public Plugin:myinfo = {
name = NAME,
author = "psychonic",
description = "Advanced logging for HL2DM & Sourceforts. Generates auxilary logging for use with log parsers such as HLstatsX and Psychostats",
version = VERSION,
url = "http://www.hlxcommunity.com"
};
#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3
public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max)
#else
public bool:AskPluginLoad(Handle:myself, bool:late, String:error[], err_max)
#endif
{
decl String:szGameDesc[64];
GetGameDescription(szGameDesc, sizeof(szGameDesc), true);
if (StrContains(szGameDesc, "Half-Life 2 Deathmatch", false) == -1 && StrContains(szGameDesc, "SourceForts", false) == -1)
{
decl String:szGameDir[64];
GetGameFolderName(szGameDir, sizeof(szGameDir));
if (StrContains(szGameDir, "hl2mp", false) == -1 && StrContains(szGameDir, "sourceforts", false) == -1)
{
strcopy(error, err_max, "This plugin is only supported on HL2MP & SourceForts");
#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3
return APLRes_Failure;
#else
return false;
#endif
}
}
#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3
return APLRes_Success;
#else
return true;
#endif
}
public OnPluginStart()
{
CreatePopulateWeaponTrie();
g_cvar_headshots = CreateConVar("superlogs_headshots", "1", "Enable logging of headshot player action (default on)", 0, true, 0.0, true, 1.0);
g_cvar_locations = CreateConVar("superlogs_locations", "1", "Enable logging of location on player death (default on)", 0, true, 0.0, true, 1.0);
HookConVarChange(g_cvar_headshots, OnCvarHeadshotsChange);
HookConVarChange(g_cvar_locations, OnCvarLocationsChange);
CreateConVar("superlogs_hl2mp_version", VERSION, NAME, FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY);
g_iCrossBowOwnerOffs = FindSendPropInfo("CCrossbowBolt", "m_hOwnerEntity");
hook_wstats();
HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
CreateTimer(1.0, LogMap);
g_cvar_teamplay = FindConVar("mp_teamplay");
if (g_cvar_teamplay != INVALID_HANDLE)
{
g_bTeamPlay = GetConVarBool(g_cvar_teamplay);
HookConVarChange(g_cvar_teamplay, OnTeamPlayChange);
}
g_hBoltChecks = CreateStack();
}
public OnAllPluginsLoaded()
{
if (GetExtensionFileStatus("sdkhooks.ext") != 1)
{
SetFailState("SDK Hooks v1.3 or higher is required for SuperLogs: HL2MP");
}
for (new i = 1; i <= MaxClients; i++)
{
if (IsClientInGame(i))
{
SDKHook(i, SDKHook_FireBulletsPost, OnFireBullets);
SDKHook(i, SDKHook_TraceAttackPost, OnTraceAttack);
SDKHook(i, SDKHook_OnTakeDamagePost, OnTakeDamage);
}
}
}
public OnMapStart()
{
GetTeams();
}
hook_wstats()
{
HookEvent("player_death", Event_PlayerDeath);
HookEvent("player_spawn", Event_PlayerSpawn);
HookEvent("round_end", Event_RoundEnd, EventHookMode_PostNoCopy);
HookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre);
}
public OnClientPutInServer(client)
{
SDKHook(client, SDKHook_FireBulletsPost, OnFireBullets);
SDKHook(client, SDKHook_TraceAttackPost, OnTraceAttack);
SDKHook(client, SDKHook_OnTakeDamagePost, OnTakeDamage);
reset_player_stats(client);
}
public OnEntityCreated(entity, const String:classname[])
{
if (strcmp(classname, "crossbow_bolt") == 0)
{
PushStackCell(g_hBoltChecks, entity);
}
}
public OnGameFrame()
{
new bowent;
while (PopStackCell(g_hBoltChecks, bowent))
{
if (!IsValidEntity(bowent))
continue;
new owner = GetEntDataEnt2(bowent, g_iCrossBowOwnerOffs);
if (owner < 0 || owner > MaxClients)
continue;
g_weapon_stats[owner][CROSSBOW][LOG_HIT_SHOTS]++;
}
}
public OnFireBullets(attacker, shots, String:weaponname[])
{
if (attacker > 0 && attacker <= MaxClients)
{
new weapon_index = get_weapon_index(weaponname[PREFIX_LEN]);
if (weapon_index > -1)
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_SHOTS]++;
}
}
}
public OnTraceAttack(victim, attacker, inflictor, Float:damage, damagetype, ammotype, hitbox, hitgroup)
{
if (hitgroup > 0 && attacker > 0 && attacker <= MaxClients && victim > 0 && victim <= MaxClients)
{
if (IsValidEntity(inflictor))
{
decl String:inflictorclsname[64];
if (GetEntityNetClass(inflictor, inflictorclsname, sizeof(inflictorclsname)) && strcmp(inflictorclsname, "CCrossbowBolt") == 0)
{
g_iNextBowHitgroup[victim] = hitgroup;
return;
}
}
g_iNextHitgroup[victim] = hitgroup;
}
}
public OnTakeDamage(victim, attacker, inflictor, Float:damage, damagetype)
{
if (attacker > 0 && attacker <= MaxClients && victim > 0 && victim <= MaxClients)
{
decl String: weapon[MAX_WEAPON_LEN + PREFIX_LEN];
GetClientWeapon(attacker, weapon, sizeof(weapon));
new weapon_index = -1;
if (IsValidEntity(inflictor))
{
decl String:inflictorclsname[64];
if (GetEntityNetClass(inflictor, inflictorclsname, sizeof(inflictorclsname)) && strcmp(inflictorclsname, "CCrossbowBolt") == 0)
{
weapon_index = CROSSBOW;
}
}
if (weapon_index == -1)
{
weapon_index = get_weapon_index(weapon[PREFIX_LEN]);
}
new hitgroup = ((weapon_index == CROSSBOW)?g_iNextBowHitgroup[victim]:g_iNextHitgroup[victim]);
if (hitgroup < 8)
{
hitgroup += LOG_HIT_OFFSET;
}
new bool:headshot = (GetClientHealth(victim) <= 0 && hitgroup == HITGROUP_HEAD);
if (weapon_index > -1)
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_HITS]++;
g_weapon_stats[attacker][weapon_index][LOG_HIT_DAMAGE] += RoundToNearest(damage);
g_weapon_stats[attacker][weapon_index][hitgroup]++;
if (headshot)
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_HEADSHOTS]++;
}
}
if (weapon_index == CROSSBOW)
{
g_iNextBowHitgroup[victim] = 0;
}
else
{
g_iNextHitgroup[victim] = 0;
}
}
}
public Action:Event_PlayerDeathPre(Handle:event, const String:name[], bool:dontBroadcast)
{
// "userid" "short" // user ID who died
// "attacker" "short" // user ID who killed
// "weapon" "string" // weapon name killer used
new attacker = GetClientOfUserId(GetEventInt(event, "attacker"));
new victim = GetClientOfUserId(GetEventInt(event, "userid"));
if (g_loglocations)
{
LogKillLoc(attacker, victim);
}
if (g_logheadshots)
{
decl String:weapon[64];
GetEventString(event, "weapon", weapon, sizeof(weapon));
if (strcmp(weapon, "crossbow_bolt") == 0)
{
if (g_iNextBowHitgroup[victim] == HITGROUP_HEAD)
{
LogPlayerEvent(attacker, "triggered", "headshot");
}
}
else if (g_iNextHitgroup[victim] == HITGROUP_HEAD)
{
LogPlayerEvent(attacker, "triggered", "headshot");
}
}
return Plugin_Continue;
}
public Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast)
{
// this extents the original player_death by a new fields
// "userid" "short" // user ID who died
// "attacker" "short" // user ID who killed
// "weapon" "string" // weapon name killer used
new victim = GetClientOfUserId(GetEventInt(event, "userid"));
if (victim > 0)
{
new attacker = GetClientOfUserId(GetEventInt(event, "attacker"));
if (attacker != victim && attacker > 0)
{
decl String: weapon[MAX_WEAPON_LEN];
GetEventString(event, "weapon", weapon, sizeof(weapon));
new weapon_index = get_weapon_index(weapon);
if (weapon_index > -1)
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_KILLS]++;
g_weapon_stats[victim][weapon_index][LOG_HIT_DEATHS]++;
if (g_bTeamPlay && GetClientTeam(attacker) == GetClientTeam(victim))
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_TEAMKILLS]++;
}
}
}
dump_player_stats(victim);
}
}
public Event_PlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast)
{
// "userid" "short" // user ID on server
new client = GetClientOfUserId(GetEventInt(event, "userid"));
if (client > 0)
{
reset_player_stats(client);
}
}
public Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast)
{
WstatsDumpAll();
}
public Action:Event_PlayerDisconnect(Handle:event, const String:name[], bool:dontBroadcast)
{
new client = GetClientOfUserId(GetEventInt(event, "userid"));
OnPlayerDisconnect(client);
return Plugin_Continue;
}
public Action:LogMap(Handle:timer)
{
// Called 1 second after OnPluginStart since srcds does not log the first map loaded. Idea from Stormtrooper's "mapfix.sp" for psychostats
LogMapLoad();
}
public OnCvarHeadshotsChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_logheadshots;
g_logheadshots = GetConVarBool(g_cvar_headshots);
if (old_value != g_logheadshots)
{
if (g_logheadshots && !g_loglocations)
{
HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
}
else if (!g_loglocations)
{
UnhookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
}
}
}
public OnCvarLocationsChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_loglocations;
g_loglocations = GetConVarBool(g_cvar_locations);
if (old_value != g_loglocations)
{
if (g_loglocations && !g_logheadshots)
{
HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
}
else if (!g_logheadshots)
{
UnhookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
}
}
}
public OnTeamPlayChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
g_bTeamPlay = GetConVarBool(g_cvar_teamplay);
}

View File

@ -0,0 +1,508 @@
/**
* HLstatsX Community Edition - SourceMod plugin to generate advanced weapon logging
* http://www.hlxcommunity.com
* Copyright (C) 2009 Nicholas Hastings (psychonic)
* Copyright (C) 2007-2008 TTS Oetzel & Goerz GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma semicolon 1
#include <sourcemod>
#include <sdktools>
#define NAME "SuperLogs: Insurgency"
#define VERSION "1.1.4"
#define MAX_LOG_WEAPONS 19
#define MAX_WEAPON_LEN 8
#define PREFIX_LEN 7
#define INS
new g_weapon_stats[MAXPLAYERS+1][MAX_LOG_WEAPONS][15];
new const String: g_weapon_list[MAX_LOG_WEAPONS][MAX_WEAPON_LEN] = {
"makarov",
"m9",
"sks",
"m1014",
"toz",
"svd",
"rpk",
"m249",
"m16m203",
"l42a1",
"m4med",
"m4",
"m16a4",
"m14",
"fnfal",
"aks74u",
"ak47",
"kabar",
"bayonet"
};
new Handle:g_cvar_wstats = INVALID_HANDLE;
new Handle:g_cvar_actions = INVALID_HANDLE;
new Handle:g_cvar_headshots = INVALID_HANDLE;
new Handle:g_cvar_chat = INVALID_HANDLE;
new Handle:g_cvar_captures = INVALID_HANDLE;
new Handle:g_cvar_locations = INVALID_HANDLE;
new Handle:g_cvar_ktraj = INVALID_HANDLE;
new bool:g_logwstats = true;
new bool:g_logheadshots = true;
new bool:g_logactions = true;
new bool:g_logchat = true;
new bool:g_logcaptures = true;
new bool:g_loglocations = true;
new bool:g_logktraj = false;
new g_client_last_weapon[MAXPLAYERS+1] = {-1, ...};
new String:g_client_last_weaponstring[MAXPLAYERS+1][64];
#include <loghelper>
#include <wstatshelper>
public Plugin:myinfo = {
name = NAME,
author = "psychonic",
description = "Advanced logging for Insurgency. Generates auxilary logging for use with log parsers such as HLstatsX and Psychostats",
version = VERSION,
url = "http://www.hlxcommunity.com"
};
#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3
public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max)
#else
public bool:AskPluginLoad(Handle:myself, bool:late, String:error[], err_max)
#endif
{
decl String:game_description[64];
GetGameDescription(game_description, sizeof(game_description), true);
if (StrContains(game_description, "Insurgency", false) == -1)
{
decl String:game_folder[64];
GetGameFolderName(game_folder, sizeof(game_folder));
if (StrContains(game_folder, "insurgency", false) == -1)
{
#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3
return APLRes_Failure;
#else
return false;
#endif
}
}
#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3
return APLRes_Success;
#else
return true;
#endif
}
public OnPluginStart()
{
CreatePopulateWeaponTrie();
g_cvar_wstats = CreateConVar("superlogs_wstats", "1", "Enable logging of weapon stats (default on)", 0, true, 0.0, true, 1.0);
g_cvar_actions = CreateConVar("superlogs_actions", "1", "Enable logging of actions, such as \"Round_Win\" (default on)", 0, true, 0.0, true, 1.0);
g_cvar_headshots = CreateConVar("superlogs_headshots", "1", "Enable logging of headshot player action (default on)", 0, true, 0.0, true, 1.0);
g_cvar_chat = CreateConVar("superlogs_chat", "1", "Enable logging of chat (default on)", 0, true, 0.0, true, 1.0);
g_cvar_captures = CreateConVar("superlogs_captures", "1", "Enable logging of capturing objectives (default on)", 0, true, 0.0, true, 1.0);
g_cvar_locations = CreateConVar("superlogs_locations", "1", "Enable logging of location on player death (default on)", 0, true, 0.0, true, 1.0);
g_cvar_ktraj = CreateConVar("superlogs_ktraj", "0", "Enable Psychostats \"KTRAJ\" logging (default off)", 0, true, 0.0, true, 1.0);
HookConVarChange(g_cvar_wstats, OnCvarWstatsChange);
HookConVarChange(g_cvar_actions, OnCvarActionsChange);
HookConVarChange(g_cvar_headshots, OnCvarHeadshotsChange);
HookConVarChange(g_cvar_chat, OnCvarChatChange);
HookConVarChange(g_cvar_captures, OnCvarCapturesChange);
HookConVarChange(g_cvar_locations, OnCvarLocationsChange);
HookConVarChange(g_cvar_ktraj, OnCvarKtrajChange);
CreateConVar("superlogs_ins_version", VERSION, NAME, FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY);
hook_wstats();
HookUserMessage(GetUserMessageId("ObjMsg"), objmsg);
HookEvent("player_hurt", Event_PlayerHurt);
HookEvent("round_end", Event_RoundEnd);
HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
HookEvent("player_death", Event_PlayerDeath);
RegConsoleCmd("say2", Command_Chat);
CreateTimer(1.0, LogMap);
GetTeams(true);
}
public OnMapStart()
{
GetTeams(true);
}
hook_wstats()
{
HookEvent("player_spawn", Event_PlayerSpawn);
HookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre);
}
unhook_wstats()
{
UnhookEvent("player_spawn", Event_PlayerSpawn);
UnhookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre);
}
public OnClientPutInServer(client)
{
reset_player_stats(client);
}
public Event_PlayerHurt(Handle:event, const String:name[], bool:dontBroadcast)
{
// "userid" "short" // user ID on server
// "attacker" "short" // CLIENT INDEX! on server of the attacker
// "dmg_health" "short" // lost health points
// "hitgroup" "short" // Hit groups
// "weapon" "string" // Weapon name, like WEAPON_AK47
new attacker = GetEventInt(event, "attacker");
new victim = GetEventInt(event, "userid");
if (attacker > 0 && attacker != victim)
{
// ... wtf insurgency... userid is userid and attacker is client index?
//attacker = GetClientOfUserId(attacker);
new hitgroup = GetEventInt(event, "hitgroup");
if (hitgroup < 8)
{
hitgroup += LOG_HIT_OFFSET;
}
if (g_logwstats)
{
decl String:weapon[MAX_WEAPON_LEN+PREFIX_LEN];
GetEventString(event, "weapon", weapon, sizeof(weapon));
new weapon_index = get_weapon_index(weapon[PREFIX_LEN]);
if (weapon_index > -1) {
g_weapon_stats[attacker][weapon_index][LOG_HIT_HITS]++;
g_weapon_stats[attacker][weapon_index][LOG_HIT_DAMAGE] += GetEventInt(event, "dmg_health");
g_weapon_stats[attacker][weapon_index][hitgroup]++;
if (hitgroup == (HITGROUP_HEAD+LOG_HIT_OFFSET))
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_HEADSHOTS]++;
}
g_client_last_weapon[attacker] = weapon_index;
g_client_last_weaponstring[attacker] = weapon;
}
}
if (g_logheadshots && hitgroup == (HITGROUP_HEAD+LOG_HIT_OFFSET))
{
LogPlayerEvent(attacker, "triggered", "headshot");
}
}
}
public Action:Event_PlayerDeathPre(Handle:event, const String:name[], bool:dontBroadcast)
{
LogKillLoc(GetClientOfUserId(GetEventInt(event, "attacker")), GetClientOfUserId(GetEventInt(event, "userid")));
return Plugin_Continue;
}
public Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast)
{
// "userid" "short" // user ID who died
// "attacker" "short" // user ID who killed
// "type" "byte" // type of death
// "nodeath" "bool" // true if death messages were off when player died
new victim = GetClientOfUserId(GetEventInt(event, "userid"));
new attacker = GetClientOfUserId(GetEventInt(event, "attacker"));
if (attacker == 0 || victim == 0 || attacker == victim)
{
return;
}
if (g_logwstats)
{
new weapon_index = g_client_last_weapon[attacker];
if (weapon_index > -1)
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_KILLS]++;
g_weapon_stats[victim][weapon_index][LOG_HIT_DEATHS]++;
if (GetClientTeam(attacker) == GetClientTeam(victim))
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_TEAMKILLS]++;
}
dump_player_stats(victim);
}
}
if (g_logktraj)
{
LogPSKillTraj(attacker, victim, g_client_last_weaponstring[attacker]);
}
}
public Event_PlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast)
{
// "userid" "short" // user ID on server
new client = GetClientOfUserId(GetEventInt(event, "userid"));
if (client > 0)
{
reset_player_stats(client);
}
}
public Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast)
{
if (g_logwstats)
{
WstatsDumpAll();
}
if (g_logactions)
{
LogTeamEvent(GetEventInt(event, "winner"), "triggered", "Round_Win");
}
}
public Action:Event_PlayerDisconnect(Handle:event, const String:name[], bool:dontBroadcast)
{
new client = GetClientOfUserId(GetEventInt(event, "userid"));
OnPlayerDisconnect(client);
return Plugin_Continue;
}
public Action:objmsg(UserMsg:msg_id, Handle:bf, const players[], playersNum, bool:reliable, bool:init)
{
new point = BfReadByte(bf); // Objective Point: 1 = point A, 2 = point B, 3 = point C, etc.
new capstatus = BfReadByte(bf); // Capture Status: 1 on starting capture, 2 on finished capture
new team = BfReadByte(bf); // Team Index: 1 = Marines, 2 = Insurgents
if (capstatus == 2)
{
switch (point)
{
case 1:
LogTeamEvent(team, "triggered", "captured_a");
case 2:
LogTeamEvent(team, "triggered", "captured_b");
case 3:
LogTeamEvent(team, "triggered", "captured_c");
case 4:
LogTeamEvent(team, "triggered", "captured_d");
case 5:
LogTeamEvent(team, "triggered", "captured_e");
}
}
return Plugin_Continue;
}
public Action:Command_Chat(client, args)
{
// method partially taken from "Insurgency Chat" by "Stevo.TVR"
if (g_logchat)
{
new String:message[192];
new startidx = 4;
if (GetCmdArgString(message, sizeof(message)) < 1 || client == 0)
{
return Plugin_Continue;
}
new lastchar = strlen(message) - 1;
if (message[lastchar] == '"')
{
message[lastchar] = '\0';
startidx += 1;
}
if (message[0] == '1')
{
LogPlayerEvent(client, "say", message[startidx], false);
}
else
{
LogPlayerEvent(client, "say_team", message[startidx], false);
}
}
return Plugin_Continue;
}
public Action:LogMap(Handle:timer)
{
// Called 1 second after OnPluginStart since srcds does not log the first map loaded. Idea from Stormtrooper's "mapfix.sp" for psychostats
LogMapLoad();
}
public OnCvarWstatsChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_logwstats;
g_logwstats = GetConVarBool(g_cvar_wstats);
if (old_value != g_logwstats)
{
if (g_logwstats)
{
hook_wstats();
if (!g_logheadshots)
{
HookEvent("player_hurt", Event_PlayerHurt);
}
if (!g_logactions)
{
HookEvent("round_end", Event_RoundEnd);
}
if (!g_logktraj)
{
HookEvent("player_death", Event_PlayerDeath);
}
}
else
{
unhook_wstats();
if (!g_logheadshots)
{
UnhookEvent("player_hurt", Event_PlayerHurt);
}
if (!g_logactions)
{
UnhookEvent("round_end", Event_RoundEnd);
}
if (!g_logktraj)
{
UnhookEvent("player_death", Event_PlayerDeath);
}
}
}
}
public OnCvarActionsChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_logactions;
g_logactions = GetConVarBool(g_cvar_actions);
if (old_value != g_logactions)
{
if (g_logactions && !g_logwstats)
{
HookEvent("round_end", Event_RoundEnd);
}
else if (!g_logwstats)
{
UnhookEvent("round_end", Event_RoundEnd);
}
}
}
public OnCvarHeadshotsChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_logheadshots;
g_logheadshots = GetConVarBool(g_cvar_headshots);
if (old_value != g_logheadshots)
{
if (g_logheadshots && !g_logwstats)
{
HookEvent("player_hurt", Event_PlayerHurt);
}
else if (!g_logwstats)
{
UnhookEvent("player_hurt", Event_PlayerHurt);
}
}
}
public OnCvarCapturesChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_logcaptures;
g_logcaptures = GetConVarBool(g_cvar_captures);
if (old_value != g_logcaptures)
{
if (g_logcaptures)
{
HookUserMessage(GetUserMessageId("ObjMsg"), objmsg);
}
else
{
UnhookUserMessage(GetUserMessageId("ObjMsg"), objmsg);
}
}
}
public OnCvarChatChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
g_logchat = GetConVarBool(g_cvar_chat);
}
public OnCvarLocationsChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_loglocations;
g_loglocations = GetConVarBool(g_cvar_locations);
if (old_value != g_loglocations)
{
if (g_loglocations)
{
HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
}
else
{
UnhookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
}
}
}
public OnCvarKtrajChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_logktraj;
g_logktraj = GetConVarBool(g_cvar_ktraj);
if (old_value != g_logktraj)
{
if (g_logktraj && !g_logwstats)
{
HookEvent("player_death", Event_PlayerDeath);
}
else if (!g_logwstats)
{
UnhookEvent("player_death", Event_PlayerDeath);
}
}
}

View File

@ -0,0 +1,686 @@
/**
* HLstatsX Community Edition - SourceMod plugin to generate advanced weapon logging
* http://www.hlxcommunity.com
* Copyright (C) 2009 Nicholas Hastings (psychonic)
* Copyright (C) 2007-2008 TTS Oetzel & Goerz GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma semicolon 1
#include <sourcemod>
#include <sdktools>
#define NAME "SuperLogs: L4D"
#define VERSION "1.3.3"
#define MAX_LOG_WEAPONS 27
#define MAX_WEAPON_LEN 16
new g_weapon_stats[MAXPLAYERS+1][MAX_LOG_WEAPONS][15];
new const String:g_weapon_list[MAX_LOG_WEAPONS][MAX_WEAPON_LEN] = {
"autoshotgun",
"rifle",
"pumpshotgun",
"smg",
"dual_pistols",
"pipe_bomb",
"hunting_rifle",
"pistol",
"prop_minigun",
"tank_claw",
"hunter_claw",
"smoker_claw",
"boomer_claw",
"smg_silenced", //l4d2 start 14 [13]
"pistol_magnum",
"rifle_ak47",
"rifle_desert",
"shotgun_chrome",
"shotgun_spas",
"sniper_military",
"rifle_sg552",
"smg_mp5",
"sniper_awp",
"sniper_scout",
"jockey_claw",
"splitter_claw",
"charger_claw"
};
new Handle:g_cvar_wstats = INVALID_HANDLE;
new Handle:g_cvar_actions = INVALID_HANDLE;
new Handle:g_cvar_headshots = INVALID_HANDLE;
new Handle:g_cvar_meleeoverride = INVALID_HANDLE;
new bool:g_logwstats = true;
new bool:g_logactions = true;
new bool:g_logheadshots = false;
new bool:g_logmeleeoverride = true;
new g_iActiveWeaponOffset;
new bool:g_bIsL4D2;
#include <loghelper>
#include <wstatshelper>
public Plugin:myinfo = {
name = NAME,
author = "psychonic",
description = "Advanced logging for Left 4 Dead. Generates auxilary logging for use with log parsers such as HLstatsX and Psychostats",
version = VERSION,
url = "http://www.hlxcommunity.com"
};
#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3
public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max)
#else
public bool:AskPluginLoad(Handle:myself, bool:late, String:error[], err_max)
#endif
{
new String:szGameDesc[64];
GetGameDescription(szGameDesc, sizeof(szGameDesc), true);
if (strncmp(szGameDesc, "L4D", 3, false) != 0 && StrContains(szGameDesc, "Left 4 D", false) == -1)
{
new String:szGameDir[64];
GetGameFolderName(szGameDir, sizeof(szGameDir));
if (StrContains(szGameDir, "left4dead", false) == -1)
{
strcopy(error, err_max, "This plugin is only supported on L4D & L4D2");
#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3
return APLRes_Failure;
#else
return false;
#endif
}
}
#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3
return APLRes_Success;
#else
return true;
#endif
}
public OnPluginStart()
{
CreatePopulateWeaponTrie();
g_cvar_wstats = CreateConVar("superlogs_wstats", "1", "Enable logging of weapon stats (default on)", 0, true, 0.0, true, 1.0);
g_cvar_actions = CreateConVar("superlogs_actions", "1", "Enable logging of player actions, such as \"Got_The_Bomb\" (default on)", 0, true, 0.0, true, 1.0);
g_cvar_headshots = CreateConVar("superlogs_headshots", "0", "Enable logging of headshot player action (default off)", 0, true, 0.0, true, 1.0);
g_cvar_meleeoverride = CreateConVar("superlogs_meleeoverride", "1", "Enable changing \"melee\" weapon in server logs to specific weapon (L4D2-only) (default on)", 0, true, 0.0, true, 1.0);
HookConVarChange(g_cvar_wstats, OnCvarWstatsChange);
HookConVarChange(g_cvar_actions, OnCvarActionsChange);
HookConVarChange(g_cvar_headshots, OnCvarHeadshotsChange);
HookConVarChange(g_cvar_meleeoverride, OnCvarMeleeOverrideChange);
CreateConVar("superlogs_l4d_version", VERSION, NAME, FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY);
if (GuessSDKVersion() != SOURCE_SDK_LEFT4DEAD)
{
g_bIsL4D2 = true;
}
hook_actions();
hook_wstats();
HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
CreateTimer(120.0, FlushWeaponLogs, 0, TIMER_REPEAT);
CreateTimer(1.0, LogMap);
GetTeams();
g_iActiveWeaponOffset = FindSendPropInfo("CTerrorPlayer", "m_hActiveWeapon");
}
public OnMapStart()
{
GetTeams();
}
hook_actions()
{
HookEvent("survivor_rescued", Event_RescueSurvivor);
HookEvent("heal_success", Event_Heal);
HookEvent("revive_success", Event_Revive);
HookEvent("witch_harasser_set", Event_StartleWitch);
HookEvent("lunge_pounce", Event_Pounce);
HookEvent("player_now_it", Event_Boomered);
HookEvent("friendly_fire", Event_FF);
HookEvent("witch_killed", Event_WitchKilled);
HookEvent("award_earned", Event_Award);
if (g_bIsL4D2)
{
HookEvent("defibrillator_used", Event_Defib);
HookEvent("adrenaline_used", Event_Adrenaline);
HookEvent("jockey_ride", Event_JockeyRide);
HookEvent("charger_pummel_start", Event_ChargerPummelStart);
HookEvent("vomit_bomb_tank", Event_VomitBombTank);
HookEvent("scavenge_match_finished", Event_ScavengeEnd);
HookEvent("versus_match_finished", Event_VersusEnd);
}
}
unhook_actions()
{
UnhookEvent("survivor_rescued", Event_RescueSurvivor);
UnhookEvent("heal_success", Event_Heal);
UnhookEvent("revive_success", Event_Revive);
UnhookEvent("witch_harasser_set", Event_StartleWitch);
UnhookEvent("lunge_pounce", Event_Pounce);
UnhookEvent("player_now_it", Event_Boomered);
UnhookEvent("friendly_fire", Event_FF);
UnhookEvent("witch_killed", Event_WitchKilled);
UnhookEvent("award_earned", Event_Award);
if (g_bIsL4D2)
{
UnhookEvent("defibrillator_used", Event_Defib);
UnhookEvent("adrenaline_used", Event_Adrenaline);
UnhookEvent("jockey_ride", Event_JockeyRide);
UnhookEvent("charger_pummel_start", Event_ChargerPummelStart);
UnhookEvent("vomit_bomb_tank", Event_VomitBombTank);
UnhookEvent("scavenge_match_finished", Event_ScavengeEnd);
UnhookEvent("versus_match_finished", Event_VersusEnd);
}
}
hook_wstats()
{
HookEvent("weapon_fire", Event_PlayerShoot);
HookEvent("weapon_fire_on_empty", Event_PlayerShoot);
HookEvent("player_hurt", Event_PlayerHurt);
HookEvent("infected_hurt", Event_InfectedHurt);
HookEvent("player_death", Event_PlayerDeath);
HookEvent("player_spawn", Event_PlayerSpawn);
HookEvent("round_end_message", Event_RoundEnd, EventHookMode_PostNoCopy);
HookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre);
}
unhook_wstats()
{
UnhookEvent("weapon_fire", Event_PlayerShoot);
UnhookEvent("weapon_fire_on_empty", Event_PlayerShoot);
UnhookEvent("player_hurt", Event_PlayerHurt);
UnhookEvent("infected_hurt", Event_InfectedHurt);
UnhookEvent("player_death", Event_PlayerDeath);
UnhookEvent("player_spawn", Event_PlayerSpawn);
UnhookEvent("round_end_message", Event_RoundEnd, EventHookMode_PostNoCopy);
UnhookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre);
}
public OnClientPutInServer(client)
{
reset_player_stats(client);
}
public Action:FlushWeaponLogs(Handle:timer, any:index)
{
WstatsDumpAll();
}
public Event_PlayerShoot(Handle:event, const String:name[], bool:dontBroadcast)
{
// "local" "1" // don't network this, its way too spammy
// "userid" "short"
// "weapon" "string" // used weapon name
// "weaponid" "short" // used weapon ID
// "count" "short" // number of bullets
new attacker = GetClientOfUserId(GetEventInt(event, "userid"));
if (attacker > 0)
{
decl String: weapon[MAX_WEAPON_LEN];
GetEventString(event, "weapon", weapon, sizeof(weapon));
new weapon_index = get_weapon_index(weapon);
if (weapon_index > -1)
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_SHOTS]++;
}
}
}
public Event_PlayerHurt(Handle:event, const String:name[], bool:dontBroadcast)
{
// "local" "1" // Not networked
// "userid" "short" // user ID who was hurt
// "attacker" "short" // user id who attacked
// "attackerentid" "long" // entity id who attacked, if attacker not a player, and userid therefore invalid
// "health" "short" // remaining health points
// "armor" "byte" // remaining armor points
// "weapon" "string" // weapon name attacker used, if not the world
// "dmg_health" "short" // damage done to health
// "dmg_armor" "byte" // damage done to armor
// "hitgroup" "byte" // hitgroup that was damaged
// "type" "long" // damage type
new attacker = GetClientOfUserId(GetEventInt(event, "attacker"));
if (attacker > 0)
{
decl String: weapon[MAX_WEAPON_LEN];
GetEventString(event, "weapon", weapon, sizeof(weapon));
new weapon_index = get_weapon_index(weapon);
if (weapon_index > -1)
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_HITS]++;
g_weapon_stats[attacker][weapon_index][LOG_HIT_DAMAGE] += GetEventInt(event, "dmg_health");
new hitgroup = GetEventInt(event, "hitgroup");
if (hitgroup < 8)
{
g_weapon_stats[attacker][weapon_index][hitgroup + LOG_HIT_OFFSET]++;
}
}
else if (g_logactions && !strcmp(weapon, "insect_swarm"))
{
new victim = GetClientOfUserId(GetEventInt(event, "userid"));
if (victim > 0 && IsClientInGame(victim) && GetClientTeam(victim) == 2 && !GetEntProp(victim, Prop_Send, "m_isIncapacitated"))
{
LogPlyrPlyrEvent(attacker, victim, "triggered", "spit_hurt", true);
}
}
}
}
public Event_InfectedHurt(Handle:event, const String:name[], bool:dontBroadcast)
{
// "local" "1" // don't network this, its way too spammy
// "attacker" "short" // player userid who attacked
// "entityid" "long" // entity id of infected
// "hitgroup" "byte" // hitgroup that was damaged
// "amount" "short" // how much damage was done
// "type" "long" // damage type
new attacker = GetClientOfUserId(GetEventInt(event, "attacker"));
if (attacker > 0)
{
decl String: weapon[MAX_WEAPON_LEN];
GetClientWeapon(attacker, weapon, sizeof(weapon));
new weapon_index = get_weapon_index(weapon[7]);
if (weapon_index > -1)
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_HITS]++;
g_weapon_stats[attacker][weapon_index][LOG_HIT_DAMAGE] += GetEventInt(event, "amount");
new hitgroup = GetEventInt(event, "hitgroup");
if (hitgroup < 8)
{
g_weapon_stats[attacker][weapon_index][hitgroup + LOG_HIT_OFFSET]++;
}
}
}
}
public Event_PlayerDeathPre(Handle:event, const String:name[], bool:dontBroadcast)
{
new attacker = GetClientOfUserId(GetEventInt(event, "attacker"));
if (g_logheadshots && GetEventBool(event, "headshot"))
{
LogPlayerEvent(attacker, "triggered", "headshot");
}
if (g_logmeleeoverride && g_bIsL4D2 && attacker > 0 && IsClientInGame(attacker))
{
decl String:szWeapon[64];
GetEventString(event, "weapon", szWeapon, sizeof(szWeapon));
if (strncmp(szWeapon, "melee", 5) == 0)
{
new iWeapon = GetEntDataEnt2(attacker, g_iActiveWeaponOffset);
if (IsValidEdict(iWeapon))
{
// They have time to switch weapons after the kill before the death event
GetEdictClassname(iWeapon, szWeapon, sizeof(szWeapon));
if (strncmp(szWeapon[7], "melee", 5) == 0)
{
GetEntPropString(iWeapon, Prop_Data, "m_strMapSetScriptName", szWeapon, sizeof(szWeapon));
SetEventString(event, "weapon", szWeapon);
}
}
}
}
}
public Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast)
{
// "userid" "short" // user ID who died
// "entityid" "long" // entity ID who died, userid should be used first, to get the dead Player. Otherwise, it is not a player, so use this. $
// "attacker" "short" // user ID who killed
// "attackername" "string" // What type of zombie, so we don't have zombie names
// "attackerentid" "long" // if killer not a player, the entindex of who killed. Again, use attacker first
// "weapon" "string" // weapon name killer used
// "headshot" "bool" // signals a headshot
// "attackerisbot" "bool" // is the attacker a bot
// "victimname" "string" // What type of zombie, so we don't have zombie names
// "victimisbot" "bool" // is the victim a bot
// "abort" "bool" // did the victim abort
// "type" "long" // damage type
// "victim_x" "float"
// "victim_y" "float"
// "victim_z" "float"
new victim = GetClientOfUserId(GetEventInt(event, "userid"));
new attacker = GetClientOfUserId(GetEventInt(event, "attacker"));
if (g_logwstats && victim > 0 && attacker > 0)
{
decl String: weapon[MAX_WEAPON_LEN];
GetEventString(event, "weapon", weapon, sizeof(weapon));
new weapon_index = get_weapon_index(weapon);
if (weapon_index > -1)
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_KILLS]++;
if (GetEventBool(event, "headshot"))
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_HEADSHOTS]++;
}
g_weapon_stats[victim][weapon_index][LOG_HIT_DEATHS]++;
if (GetClientTeam(attacker) == GetClientTeam(victim))
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_TEAMKILLS]++;
}
dump_player_stats(victim);
}
}
}
public Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast)
{
WstatsDumpAll();
}
public Event_PlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast)
{
// "userid" "short" // user ID on server
new client = GetClientOfUserId(GetEventInt(event, "userid"));
if (client > 0)
{
reset_player_stats(client);
}
}
public Action:Event_PlayerDisconnect(Handle:event, const String:name[], bool:dontBroadcast)
{
new client = GetClientOfUserId(GetEventInt(event, "userid"));
OnPlayerDisconnect(client);
return Plugin_Continue;
}
public Action:LogMap(Handle:timer)
{
// Called 1 second after OnPluginStart since srcds does not log the first map loaded. Idea from Stormtrooper's "mapfix.sp" for psychostats
LogMapLoad();
return Plugin_Continue;
}
public Event_RescueSurvivor(Handle:event, const String:name[], bool:dontBroadcast)
{
new player = GetClientOfUserId(GetEventInt(event, "rescuer"));
if (player > 0)
{
LogPlayerEvent(player, "triggered", "rescued_survivor", true);
}
}
public Event_Heal(Handle:event, const String:name[], bool:dontBroadcast)
{
new player = GetClientOfUserId(GetEventInt(event, "userid"));
if (player > 0 && player != GetClientOfUserId(GetEventInt(event, "subject")))
{
LogPlayerEvent(player, "triggered", "healed_teammate", true);
}
}
public Event_Revive(Handle:event, const String:name[], bool:dontBroadcast)
{
new player = GetClientOfUserId(GetEventInt(event, "userid"));
if (player > 0)
{
LogPlayerEvent(player, "triggered", "revived_teammate", true);
}
}
public Event_StartleWitch(Handle:event, const String:name[], bool:dontBroadcast)
{
new player = GetClientOfUserId(GetEventInt(event, "userid"));
if (player > 0 && (!g_bIsL4D2 || GetEventBool(event, "first")))
{
LogPlayerEvent(player, "triggered", "startled_witch", true);
}
}
public Event_Pounce(Handle:event, const String:name[], bool:dontBroadcast)
{
new player = GetClientOfUserId(GetEventInt(event, "userid"));
new victim = GetClientOfUserId(GetEventInt(event, "victim"));
if (victim > 0)
{
LogPlyrPlyrEvent(player, victim, "triggered", "pounce", true);
}
else
{
LogPlayerEvent(player, "triggered", "pounce", true);
}
}
public Event_Boomered(Handle:event, const String:name[], bool:dontBroadcast)
{
new player = GetClientOfUserId(GetEventInt(event, "attacker"));
new victim = GetClientOfUserId(GetEventInt(event, "userid"));
if (player > 0 && (!g_bIsL4D2 || GetEventBool(event, "by_boomer")))
{
if (victim > 0)
{
LogPlyrPlyrEvent(player, victim, "triggered", "vomit", true);
}
else
{
LogPlayerEvent(player, "triggered", "vomit", true);
}
}
}
public Event_FF(Handle:event, const String:name[], bool:dontBroadcast)
{
new player = GetClientOfUserId(GetEventInt(event, "attacker"));
new victim = GetClientOfUserId(GetEventInt(event, "victim"));
if (player > 0 && player == GetClientOfUserId(GetEventInt(event, "guilty")))
{
if (victim > 0)
{
LogPlyrPlyrEvent(player, victim, "triggered", "friendly_fire", true);
}
else
{
LogPlayerEvent(player, "triggered", "friendly_fire", true);
}
}
}
public Event_WitchKilled(Handle:event, const String:name[], bool:dontBroadcast)
{
if (GetEventBool(event, "oneshot"))
{
LogPlayerEvent(GetClientOfUserId(GetEventInt(event, "userid")), "triggered", "cr0wned", true);
}
}
public Event_Defib(Handle:event, const String:name[], bool:dontBroadcast)
{
LogPlayerEvent(GetClientOfUserId(GetEventInt(event, "userid")), "triggered", "defibrillated_teammate", true);
}
public Event_Adrenaline(Handle:event, const String:name[], bool:dontBroadcast)
{
LogPlayerEvent(GetClientOfUserId(GetEventInt(event, "userid")), "triggered", "used_adrenaline", true);
}
public Event_JockeyRide(Handle:event, const String:name[], bool:dontBroadcast)
{
new player = GetClientOfUserId(GetEventInt(event, "userid"));
new victim = GetClientOfUserId(GetEventInt(event, "victim"));
if (player > 0)
{
if (victim > 0)
{
LogPlyrPlyrEvent(player, victim, "triggered", "jockey_ride", true);
}
else
{
LogPlayerEvent(player, "triggered", "jockey_ride", true);
}
}
}
public Event_ChargerPummelStart(Handle:event, const String:name[], bool:dontBroadcast)
{
new player = GetClientOfUserId(GetEventInt(event, "userid"));
new victim = GetClientOfUserId(GetEventInt(event, "victim"));
if (victim > 0)
{
LogPlyrPlyrEvent(player, victim, "triggered", "charger_pummel", true);
}
else
{
LogPlayerEvent(player, "triggered", "charger_pummel", true);
}
}
public Event_VomitBombTank(Handle:event, const String:name[], bool:dontBroadcast)
{
LogPlayerEvent(GetClientOfUserId(GetEventInt(event, "userid")), "triggered", "bilebomb_tank", true);
}
public Event_ScavengeEnd(Handle:event, const String:name[], bool:dontBroadcast)
{
LogTeamEvent(GetEventInt(event, "winners"), "triggered", "Scavenge_Win");
}
public Event_VersusEnd(Handle:event, const String:name[], bool:dontBroadcast)
{
LogTeamEvent(GetEventInt(event, "winners"), "triggered", "Versus_Win");
}
public Action:Event_Award(Handle:event, const String:name[], bool:dontBroadcast)
{
// "userid" "short" // player who earned the award
// "entityid" "long" // client likes ent id
// "subjectentid" "long" // entity id of other party in the award, if any
// "award" "short" // id of award earned
switch(GetEventInt(event, "award"))
{
case 21:
LogPlayerEvent(GetClientOfUserId(GetEventInt(event, "userid")), "triggered", "hunter_punter", true);
case 27:
LogPlayerEvent(GetClientOfUserId(GetEventInt(event, "userid")), "triggered", "tounge_twister", true);
case 67:
LogPlayerEvent(GetClientOfUserId(GetEventInt(event, "userid")), "triggered", "protect_teammate", true);
case 80:
LogPlayerEvent(GetClientOfUserId(GetEventInt(event, "userid")), "triggered", "no_death_on_tank", true);
case 136:
LogPlayerEvent(GetClientOfUserId(GetEventInt(event, "userid")), "triggered", "killed_all_survivors", true);
}
}
public OnCvarWstatsChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_logwstats;
g_logwstats = GetConVarBool(g_cvar_wstats);
if (old_value != g_logwstats)
{
if (g_logwstats)
{
hook_wstats();
}
else
{
unhook_wstats();
}
}
}
public OnCvarActionsChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_logactions;
g_logactions = GetConVarBool(g_cvar_actions);
if (old_value != g_logactions)
{
if (g_logactions)
{
hook_actions();
}
else
{
unhook_actions();
}
}
}
public OnCvarHeadshotsChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_logheadshots;
g_logheadshots = GetConVarBool(g_cvar_headshots);
if (old_value != g_logheadshots)
{
if (g_logheadshots && !g_logmeleeoverride)
{
HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
}
else if (!g_logmeleeoverride)
{
UnhookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
}
}
}
public OnCvarMeleeOverrideChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_logmeleeoverride;
g_logmeleeoverride = GetConVarBool(g_cvar_meleeoverride);
if (old_value != g_logmeleeoverride)
{
if (g_logmeleeoverride && !g_logheadshots)
{
HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
}
else if (!g_logheadshots)
{
UnhookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
}
}
}

View File

@ -0,0 +1,122 @@
/**
* HLstatsX Community Edition - SourceMod plugin to generate advanced weapon logging
* http://www.hlxcommunity.com
* Copyright (C) 2009 Nicholas Hastings (psychonic)
* Copyright (C) 2007-2008 TTS Oetzel & Goerz GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma semicolon 1
#include <sourcemod>
#include <sdktools>
#define NAME "SuperLogs: NeoTokyo"
#define VERSION "1.0.2"
new Handle:g_cvar_headshots = INVALID_HANDLE;
new Handle:g_cvar_locations = INVALID_HANDLE;
new bool:g_logheadshots = true;
new bool:g_loglocations = true;
#include <loghelper>
public Plugin:myinfo = {
name = NAME,
author = "psychonic",
description = "Advanced logging for NeoTokyo. Generates auxilary logging for use with log parsers such as HLstatsX and Psychostats",
version = VERSION,
url = "http://www.hlxcommunity.com"
};
public OnPluginStart()
{
g_cvar_headshots = CreateConVar("superlogs_headshots", "1", "Enable logging of headshot player action (default off)", 0, true, 0.0, true, 1.0);
g_cvar_locations = CreateConVar("superlogs_locations", "1", "Enable logging of location on player death (default on)", 0, true, 0.0, true, 1.0);
HookConVarChange(g_cvar_headshots, OnCvarHeadshotsChange);
HookConVarChange(g_cvar_locations, OnCvarLocationsChange);
CreateConVar("superlogs_nts_version", VERSION, NAME, FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY);
HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
CreateTimer(1.0, LogMap);
}
public Action:Event_PlayerDeathPre(Handle:event, const String:name[], bool:dontBroadcast)
{
new attacker = GetEventInt(event, "attacker");
if (attacker > 0)
{
if (g_loglocations)
{
LogKillLoc(GetClientOfUserId(attacker), GetClientOfUserId(GetEventInt(event, "userid")));
}
if (g_logheadshots && GetEventInt(event, "icon") == 2)
{
LogPlayerEvent(GetClientOfUserId(attacker), "triggered", "headshot");
}
}
return Plugin_Continue;
}
public OnCvarHeadshotsChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_logheadshots;
g_logheadshots = GetConVarBool(g_cvar_headshots);
if (old_value != g_logheadshots)
{
if (g_logheadshots && !g_loglocations)
{
HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
}
else if (!g_loglocations)
{
UnhookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
}
}
}
public OnCvarLocationsChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_loglocations;
g_loglocations = GetConVarBool(g_cvar_locations);
if (old_value != g_loglocations)
{
if (g_loglocations && !g_logheadshots)
{
HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
}
else if (!g_logheadshots)
{
UnhookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
}
}
}
public Action:LogMap(Handle:timer)
{
// Called 1 second after OnPluginStart since srcds does not log the first map loaded. Idea from Stormtrooper's "mapfix.sp" for psychostats
LogMapLoad();
}

View File

@ -0,0 +1,445 @@
/**
* HLstatsX Community Edition - SourceMod plugin to generate advanced weapon logging
* http://www.hlxcommunity.com
* Copyright (C) 2009 Nicholas Hastings (psychonic)
* Copyright (C) 2007-2008 TTS Oetzel & Goerz GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma semicolon 1
#include <sourcemod>
#include <sdktools>
#include <sdkhooks>
#define NAME "SuperLogs: Nuclear Dawn"
#define VERSION "1.0"
#define MAX_LOG_WEAPONS 28
#define IGNORE_SHOTS_START 16
#define MAX_WEAPON_LEN 20
#define BUNKER_DAMAGE_TIMES 10
#define ND_TEAM_EMP 3
#define ND_TEAM_CT 2
new g_weapon_stats[MAXPLAYERS+1][MAX_LOG_WEAPONS][15];
new const String:g_weapon_list[MAX_LOG_WEAPONS][MAX_WEAPON_LEN] = {
"avenger",
"bag90",
"chaingun",
"daisy cutter",
"f2000",
"grenade launcher",
"m95",
"mp500",
"mp7",
"nx300",
"p900",
"paladin",
"pp22",
"psg",
"shotgun",
"sp5",
"x01",
"medpack",
"armblade",
"mine",
"emp grenade",
"p12 grenade",
"remote grenade",
"repair tool",
"svr grenade",
"u23 grenade",
"armknives",
"frag grenade"
};
#include <loghelper>
#include <wstatshelper>
new g_bReadyToShoot[MAXPLAYERS+1] = {false,...};
new g_iBunkerAttacked[2] = {0,...};
public Plugin:myinfo = {
name = NAME,
author = "Peace-Maker",
description = "Advanced logging. Generates auxilary logging for use with log parsers such as HLstatsX and Psychostats",
version = VERSION,
url = "http://www.hlxcommunity.com"
};
public OnPluginStart()
{
CreatePopulateWeaponTrie();
CreateConVar("superlogs_nucleardawn_version", VERSION, NAME, FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY);
HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
HookEvent("promoted_to_commander", Event_PromotedToCommander);
HookEvent("resource_captured", Event_ResourceCaptured);
HookEvent("structure_damage_sparse", Event_StructureDamageSparse);
HookEvent("structure_death", Event_StructureDeath);
// wstats
HookEvent("player_spawn", Event_PlayerSpawn);
HookEvent("round_win", Event_RoundWin);
HookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre);
CreateTimer(1.0, LogMap);
GetTeams();
// GetTeamName gets #ND_Consortium and #ND_Empire in release version -.-. Game logs with CONSORTIUM and EMPIRE translated
strcopy(g_team_list[ND_TEAM_CT], sizeof(g_team_list[]), "CONSORTIUM");
strcopy(g_team_list[ND_TEAM_EMP], sizeof(g_team_list[]), "EMPIRE");
for(new i=1;i<=MaxClients;i++)
{
if(IsClientInGame(i))
OnClientPutInServer(i);
}
}
public OnMapStart()
{
GetTeams();
// GetTeamName gets #ND_Consortium and #ND_Empire in release version -.-. Game logs with CONSORTIUM and EMPIRE translated
strcopy(g_team_list[ND_TEAM_CT], sizeof(g_team_list[]), "CONSORTIUM");
strcopy(g_team_list[ND_TEAM_EMP], sizeof(g_team_list[]), "EMPIRE");
g_iBunkerAttacked[0] = 0;
g_iBunkerAttacked[1] = 0;
}
public OnClientPutInServer(client)
{
g_bReadyToShoot[client] = false;
SDKHook(client, SDKHook_TraceAttackPost, Hook_TraceAttackPost);
SDKHook(client, SDKHook_PostThink, Hook_PostThink);
SDKHook(client, SDKHook_PostThinkPost, Hook_PostThinkPost);
reset_player_stats(client);
}
public Action:Event_PlayerDeathPre(Handle:event, const String:name[], bool:dontBroadcast)
{
new victim = GetClientOfUserId(GetEventInt(event, "userid"));
new attacker = GetClientOfUserId(GetEventInt(event, "attacker"));
decl String:weapon[MAX_WEAPON_LEN];
GetEventString(event, "weapon", weapon, sizeof(weapon));
if (attacker <= 0 || victim <= 0)
{
return Plugin_Continue;
}
// Which commander ablilty?!
if(StrEqual(weapon, "commander ability"))
{
new damagebits = GetEventInt(event, "damagebits");
if(damagebits & DMG_ENERGYBEAM)
{
Format(weapon, sizeof(weapon), "commander poison");
SetEventString(event, "weapon", weapon);
}
else if(damagebits & DMG_BLAST)
{
Format(weapon, sizeof(weapon), "commander damage");
SetEventString(event, "weapon", weapon);
}
}
if(attacker != victim)
{
// Check if victim was commander?
if(GameRules_GetPropEnt("m_hCommanders", ND_TEAM_CT-2) == victim || GameRules_GetPropEnt("m_hCommanders", ND_TEAM_EMP-2) == victim)
LogPlayerEvent(attacker, "triggered", "killed_commander");
}
new weapon_index = get_weapon_index(weapon);
if (weapon_index > -1)
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_KILLS]++;
g_weapon_stats[victim][weapon_index][LOG_HIT_DEATHS]++;
if (GetClientTeam(attacker) == GetClientTeam(victim))
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_TEAMKILLS]++;
}
dump_player_stats(victim);
}
return Plugin_Continue;
}
public Hook_PostThink(client)
{
if(!IsPlayerAlive(client))
return;
new iWeapon = GetEntPropEnt(client, Prop_Send, "m_hActiveWeapon");
if(iWeapon == -1 || !IsValidEdict(iWeapon))
{
g_bReadyToShoot[client] = false;
return;
}
decl String:sWeapon[32];
GetEdictClassname(iWeapon, sWeapon, sizeof(sWeapon));
if(StrContains(sWeapon, "weapon_", false) != 0)
return;
new Float:flNextAttackTime = GetEntPropFloat(iWeapon, Prop_Send, "m_flNextPrimaryAttack");
if(flNextAttackTime <= GetGameTime() && GetEntProp(iWeapon, Prop_Send, "m_iClip1") > 0)
g_bReadyToShoot[client] = true;
else
g_bReadyToShoot[client] = false;
}
public Hook_PostThinkPost(client)
{
if(!IsPlayerAlive(client))
return;
new iWeapon = GetEntPropEnt(client, Prop_Send, "m_hActiveWeapon");
if(iWeapon == -1 || !IsValidEdict(iWeapon))
return;
decl String:sWeapon[30];
GetEdictClassname(iWeapon, sWeapon, sizeof(sWeapon));
if(StrContains(sWeapon, "weapon_", false) != 0)
return;
ReplaceString(sWeapon, sizeof(sWeapon), "weapon_", "", false);
FixWeaponLoggingName(sWeapon, sizeof(sWeapon));
new Float:flNextAttackTime = GetEntPropFloat(iWeapon, Prop_Send, "m_flNextPrimaryAttack");
if(g_bReadyToShoot[client] && flNextAttackTime > GetGameTime())
{
new weapon_index = get_weapon_index(sWeapon);
if (weapon_index > -1 && weapon_index < IGNORE_SHOTS_START)
{
g_weapon_stats[client][weapon_index][LOG_HIT_SHOTS]++;
}
g_bReadyToShoot[client] = false;
}
}
public Hook_TraceAttackPost(victim, attacker, inflictor, Float:damage, damagetype, ammotype, hitbox, hitgroup)
{
if(IsClientInGame(victim))
{
if(1 <= attacker <= MaxClients && IsClientInGame(attacker))
{
new iWeapon = GetEntPropEnt(attacker, Prop_Send, "m_hActiveWeapon");
new String:sWeapon[64];
if(iWeapon > 0)
GetEdictClassname(iWeapon, sWeapon, sizeof(sWeapon));
ReplaceString(sWeapon, sizeof(sWeapon), "weapon_", "", false);
FixWeaponLoggingName(sWeapon, sizeof(sWeapon));
new weapon_index = get_weapon_index(sWeapon);
// player_death
if((GetClientHealth(victim) - RoundToCeil(damage)) < 0)
{
if (hitgroup == HITGROUP_HEAD)
{
LogPlayerEvent(attacker, "triggered", "headshot");
if (weapon_index > -1)
g_weapon_stats[attacker][weapon_index][LOG_HIT_HEADSHOTS]++;
}
}
// player_hurt
else
{
if (weapon_index > -1)
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_HITS]++;
g_weapon_stats[attacker][weapon_index][LOG_HIT_DAMAGE] += RoundToCeil(damage);
if (hitgroup < 8)
{
g_weapon_stats[attacker][weapon_index][hitgroup + LOG_HIT_OFFSET]++;
}
}
}
}
}
}
public Event_PlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast)
{
// "userid" "short" // user ID on server
new client = GetClientOfUserId(GetEventInt(event, "userid"));
if (client > 0)
{
reset_player_stats(client);
}
}
public Action:Event_PlayerDisconnect(Handle:event, const String:name[], bool:dontBroadcast)
{
new client = GetClientOfUserId(GetEventInt(event, "userid"));
OnPlayerDisconnect(client);
return Plugin_Continue;
}
public Event_RoundWin(Handle:event, const String:name[], bool:dontBroadcast)
{
new team = GetEventInt(event, "team");
if(team >= 2)
{
LogTeamEvent(team, "triggered", "round_win");
LogTeamEvent(GetOtherTeam(team), "triggered", "round_lose");
}
g_iBunkerAttacked[0] = 0;
g_iBunkerAttacked[1] = 0;
WstatsDumpAll();
}
public Event_PromotedToCommander(Handle:event, const String:name[], bool:dontBroadcast)
{
LogPlayerEvent(GetClientOfUserId(GetEventInt(event, "userid")), "triggered", "promoted_to_commander");
}
public Event_ResourceCaptured(Handle:event, const String:name[], bool:dontBroadcast)
{
new team = GetEventInt(event, "team");
if(team >= 2)
LogTeamEvent(team, "triggered", "resource_captured");
}
public Event_StructureDamageSparse(Handle:event, const String:name[], bool:dontBroadcast)
{
if(!GetEventBool(event, "bunker"))
return;
new team = GetEventInt(event, "ownerteam");
if(team >= 2)
{
g_iBunkerAttacked[team-2]++;
if(g_iBunkerAttacked[team-2] == BUNKER_DAMAGE_TIMES)
{
LogTeamEvent(GetOtherTeam(team), "triggered", "damaged_opposite_bunker");
g_iBunkerAttacked[team-2] = 0;
}
}
}
public Event_StructureDeath(Handle:event, const String:name[], bool:dontBroadcast)
{
new iEnt = GetEventInt(event, "entindex");
new iAttacker = GetClientOfUserId(GetEventInt(event, "attacker"));
if(iAttacker > 0 && iAttacker <= MaxClients && iEnt != -1 && IsValidEntity(iEnt))
{
decl String:sBuffer[32];
GetEdictClassname(iEnt, sBuffer, sizeof(sBuffer));
PrintToChatAll("%N destroyed %s", iAttacker, sBuffer);
if(StrEqual(sBuffer, "struct_armoury"))
{
LogPlayerEvent(iAttacker, "triggered", "armoury_destroyed");
}
else if(StrEqual(sBuffer, "struct_artillery_explosion"))
{
LogPlayerEvent(iAttacker, "triggered", "artillery_destroyed");
}
else if(StrEqual(sBuffer, "struct_assembler"))
{
LogPlayerEvent(iAttacker, "triggered", "assembler_destroyed");
}
else if(StrEqual(sBuffer, "struct_flamethrower_turret"))
{
LogPlayerEvent(iAttacker, "triggered", "flamethrowerturret_destroyed");
}
else if(StrEqual(sBuffer, "struct_fusion_reactor"))
{
LogPlayerEvent(iAttacker, "triggered", "wirelessrepeater_destroyed");
}
else if(StrEqual(sBuffer, "struct_power_station"))
{
LogPlayerEvent(iAttacker, "triggered", "powerstation_destroyed");
}
else if(StrEqual(sBuffer, "struct_radar"))
{
LogPlayerEvent(iAttacker, "triggered", "radar_destroyed");
}
else if(StrEqual(sBuffer, "struct_power_relay"))
{
LogPlayerEvent(iAttacker, "triggered", "powerrelay_destroyed");
}
else if(StrEqual(sBuffer, "struct_rocket_turret"))
{
LogPlayerEvent(iAttacker, "triggered", "rocketturret_destroyed");
}
else if(StrEqual(sBuffer, "struct_sonic_turret"))
{
LogPlayerEvent(iAttacker, "triggered", "sonicturret_destroyed");
}
else if(StrEqual(sBuffer, "struct_support_station"))
{
LogPlayerEvent(iAttacker, "triggered", "supply_destroyed");
}
else if(StrEqual(sBuffer, "struct_transport_gate"))
{
LogPlayerEvent(iAttacker, "triggered", "transportgate_destroyed");
}
else if(StrEqual(sBuffer, "struct_machinegun_turret"))
{
LogPlayerEvent(iAttacker, "triggered", "machineguneturret_destroyed");
}
}
}
public Action:LogMap(Handle:timer)
{
// Called 1 second after OnPluginStart since srcds does not log the first map loaded. Idea from Stormtrooper's "mapfix.sp" for psychostats
LogMapLoad();
}
stock GetOtherTeam(team)
{
if(team == 2)
return 3;
else if(team == 3)
return 2;
return team;
}
stock FixWeaponLoggingName(String:sWeapon[], maxlength)
{
if(StrEqual(sWeapon, "daisycutter"))
strcopy(sWeapon, maxlength, "daisy cutter");
else if(StrEqual(sWeapon, "emp_grenade"))
strcopy(sWeapon, maxlength, "emp grenade");
else if(StrEqual(sWeapon, "frag_grenade"))
strcopy(sWeapon, maxlength, "frag grenade");
else if(StrEqual(sWeapon, "grenade_launcher"))
strcopy(sWeapon, maxlength, "grenade launcher");
else if(StrEqual(sWeapon, "p12_grenade"))
strcopy(sWeapon, maxlength, "p12 grenade");
else if(StrEqual(sWeapon, "remote_grenade"))
strcopy(sWeapon, maxlength, "remote grenade");
//else if(StrEqual(sWeapon, "repair_tool"))
// strcopy(sWeapon, maxlength, "repair tool");
else if(StrEqual(sWeapon, "u23_grenade"))
strcopy(sWeapon, maxlength, "u23 grenade");
}

View File

@ -0,0 +1,326 @@
/**
* HLstatsX Community Edition - SourceMod plugin to generate advanced weapon logging
* http://www.hlxcommunity.com
* Copyright (C) 2009-2010 Nicholas Hastings (psychonic)
* Copyright (C) 2007-2008 TTS Oetzel & Goerz GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma semicolon 1
#include <sourcemod>
#include <sdktools>
#define NAME "SuperLogs: PVKII"
#define VERSION "1.0.1"
#define MAX_LOG_WEAPONS 6
#define MAX_WEAPON_LEN 14
new g_weapon_stats[MAXPLAYERS+1][MAX_LOG_WEAPONS][15];
new const String:g_weapon_list[MAX_LOG_WEAPONS][MAX_WEAPON_LEN] = {
"blunderbuss",
"flintlock",
"arrow",
"crossbow_bolt",
"throwaxe",
"javalin"
};
new Handle:g_cvar_ktraj = INVALID_HANDLE;
new bool:g_logktraj = true;
new bool:g_bHasChest[MAXPLAYERS+1] = {false,...};
new bool:g_bHasGrail[MAXPLAYERS+1] = {false,...};
new g_iLastClass = -1;
new const String:g_szClassNames[][] = {
"Skirmisher",
"Captain",
"",
"Berserker",
"Huscarl",
"Gestir",
"Heavy Knight",
"Archer"
};
#define PVKII
#include <loghelper>
#include <wstatshelper>
public Plugin:myinfo = {
name = NAME,
author = "psychonic",
description = "Advanced logging for PVKII. Generates auxilary logging for use with log parsers such as HLstatsX and Psychostats",
version = VERSION,
url = "http://www.hlxce.com"
};
#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3
public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max)
#else
public bool:AskPluginLoad(Handle:myself, bool:late, String:error[], err_max)
#endif
{
decl String:szGameDesc[64];
GetGameDescription(szGameDesc, sizeof(szGameDesc), true);
if (StrContains(szGameDesc, "PVKII", false) == -1)
{
decl String:szGameDir[64];
GetGameFolderName(szGameDir, sizeof(szGameDir));
if (StrContains(szGameDir, "pvkii", false) == -1)
{
strcopy(error, err_max, "This plugin is only supported on Pirate, Vikings, and Knights");
#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3
return APLRes_Failure;
#else
return false;
#endif
}
}
#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3
return APLRes_Success;
#else
return true;
#endif
}
public OnPluginStart()
{
CreatePopulateWeaponTrie();
g_cvar_ktraj = CreateConVar("superlogs_ktraj", "0", "Enable Psychostats \"KTRAJ\" logging (default off)", 0, true, 0.0, true, 1.0);
HookConVarChange(g_cvar_ktraj, OnCvarKtrajChange);
CreateConVar("superlogs_pvkii_version", VERSION, NAME, FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY);
HookEvent("player_ranged_impact", Event_PlayerRangedImpact);
HookEvent("player_death", Event_PlayerDeath);
HookEvent("player_spawn", Event_PlayerSpawn);
HookEvent("update_mvp_panel", Event_RoundEnd);
HookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre);
HookEvent("player_nemesis", Event_PlayerNemesis);
HookEvent("player_revenge", Event_PlayerRevenge);
HookEvent("player_objective", Event_PlayerObjective);
HookEvent("grail_pickup", Event_GrailPickup);
HookEvent("grail_drop", Event_GrailDrop);
HookEvent("chest_pickup", Event_ChestPickup);
HookEvent("chest_drop", Event_ChestDrop);
HookEvent("chest_capture", Event_ChestCapture);
CreateTimer(1.0, LogMap);
}
public OnMapStart()
{
GetTeams();
}
public OnClientPutInServer(client)
{
g_iLastClass = -1;
reset_player_stats(client);
g_bHasChest[client] = false;
g_bHasGrail[client] = false;
}
public Event_PlayerNemesis(Handle:event, const String:name[], bool:dontBroadcast)
{
LogPlyrPlyrEvent(GetClientOfUserId(GetEventInt(event, "userid")), GetClientOfUserId(GetEventInt(event, "victim")), "triggered", "domination");
}
public Event_PlayerRevenge(Handle:event, const String:name[], bool:dontBroadcast)
{
LogPlyrPlyrEvent(GetClientOfUserId(GetEventInt(event, "userid")), GetClientOfUserId(GetEventInt(event, "victim")), "triggered", "revenge");
}
public Event_PlayerObjective(Handle:event, const String:name[], bool:dontBroadcast)
{
LogPlayerEvent(GetClientOfUserId(GetEventInt(event, "userid")), "triggered", "obj_complete");
}
public Event_ChestPickup(Handle:event, const String:name[], bool:dontBroadcast)
{
g_bHasChest[GetClientOfUserId(GetEventInt(event, "userid"))] = true;
}
public Event_ChestDrop(Handle:event, const String:name[], bool:dontBroadcast)
{
g_bHasChest[GetClientOfUserId(GetEventInt(event, "userid"))] = false;
}
public Event_GrailPickup(Handle:event, const String:name[], bool:dontBroadcast)
{
g_bHasGrail[GetClientOfUserId(GetEventInt(event, "userid"))] = true;
}
public Event_GrailDrop(Handle:event, const String:name[], bool:dontBroadcast)
{
g_bHasGrail[GetClientOfUserId(GetEventInt(event, "userid"))] = false;
}
public Event_ChestCapture(Handle:event, const String:name[], bool:dontBroadcast)
{
LogPlayerEvent(GetClientOfUserId(GetEventInt(event, "userid")), "triggered", "chest_capture");
}
public Event_PlayerRangedImpact(Handle:event, const String:name[], bool:dontBroadcast)
{
// "userid" "short" // user ID of player who fired
// "victim" "short" // entindex of entity that was hit (if any)
// "weapon" "string" // weapon that was fired
// "damage" "float" // how much damage was dealt, if 0 obviously missed or blocked by shield if victim is set
new attacker = GetClientOfUserId(GetEventInt(event, "userid"));
if (attacker == 0 || !IsClientInGame(attacker))
return;
decl String:weapon[MAX_WEAPON_LEN];
GetEventString(event, "weapon", weapon, sizeof(weapon));
new weapon_index = get_weapon_index(weapon);
if (weapon_index == -1)
return;
if (!strcmp(weapon, "blunderbuss"))
{
// buckshot of 8
g_weapon_stats[attacker][weapon_index][LOG_HIT_SHOTS] += 8;
}
else
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_SHOTS]++;
}
new victim = GetEventInt(event, "victim");
if (victim < 1 || victim > MaxClients || !IsClientInGame(victim))
return;
g_weapon_stats[attacker][weapon_index][LOG_HIT_HITS]++;
g_weapon_stats[attacker][weapon_index][LOG_HIT_DAMAGE] += RoundToNearest(GetEventFloat(event, "damage"));
}
public Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast)
{
// this extents the original player_death by a new fields
// "userid" "short" // user ID who died
// "attacker" "short" // user ID who killed
// "weapon" "string" // weapon name killer used
new victim = GetClientOfUserId(GetEventInt(event, "userid"));
new attacker = GetClientOfUserId(GetEventInt(event, "attacker"));
decl String: weapon[MAX_WEAPON_LEN];
GetEventString(event, "weapon", weapon, sizeof(weapon));
if (victim > 0 && attacker > 0)
{
new weapon_index = get_weapon_index(weapon);
if (weapon_index > -1)
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_KILLS]++;
g_weapon_stats[victim][weapon_index][LOG_HIT_DEATHS]++;
if (GetClientTeam(attacker) == GetClientTeam(victim))
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_TEAMKILLS]++;
}
}
dump_player_stats(victim);
LogPlyrPlyrEvent(GetEventInt(event, "assistid"), victim, "triggered", "kill assist", true);
if (g_bHasGrail[victim])
{
LogPlayerEvent(attacker, "triggered", "grail_defend");
}
if (g_bHasChest[victim])
{
LogPlayerEvent(attacker, "triggered", "chest_defend");
}
g_bHasGrail[victim] = false;
g_bHasChest[victim] = false;
}
if (g_logktraj)
{
LogPSKillTraj(attacker, victim, weapon);
}
}
public Event_PlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast)
{
// "userid" "short" // user ID on server
new client = GetClientOfUserId(GetEventInt(event, "userid"));
if (client > 0)
{
reset_player_stats(client);
if (IsClientInGame(client))
{
new iCurrentClass = GetEntProp(client, Prop_Send, "m_iPlayerClass");
if (iCurrentClass > -1 && iCurrentClass != g_iLastClass)
{
LogRoleChange(client, g_szClassNames[iCurrentClass]);
}
g_iLastClass = iCurrentClass;
}
}
}
public Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast)
{
new winner = GetEventInt(event, "winner");
if (winner > 1)
{
LogTeamEvent(winner, "triggered", "Round_Win");
}
LogPlayerEvent(GetEventInt(event, "pid_1"), "triggered", "mvp1");
LogPlayerEvent(GetEventInt(event, "pid_2"), "triggered", "mvp2");
LogPlayerEvent(GetEventInt(event, "pid_3"), "triggered", "mvp3");
for (new i = 1; i <= MaxClients; i++)
{
g_bHasChest[i] = false;
g_bHasGrail[i] = false;
}
WstatsDumpAll();
}
public Action:Event_PlayerDisconnect(Handle:event, const String:name[], bool:dontBroadcast)
{
new client = GetClientOfUserId(GetEventInt(event, "userid"));
OnPlayerDisconnect(client);
return Plugin_Continue;
}
public Action:LogMap(Handle:timer)
{
// Called 1 second after OnPluginStart since srcds does not log the first map loaded. Idea from Stormtrooper's "mapfix.sp" for psychostats
LogMapLoad();
}
public OnCvarKtrajChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
g_logktraj = GetConVarBool(g_cvar_ktraj);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,325 @@
/**
* HLstatsX Community Edition - SourceMod plugin to generate advanced weapon logging
* http://www.hlxcommunity.com
* Copyright (C) 2009-2010 Nicholas Hastings (psychonic)
* Copyright (C) 2007-2008 TTS Oetzel & Goerz GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma semicolon 1
#include <sourcemod>
#include <sdktools>
#include <sdkhooks>
#define NAME "SuperLogs: ZPS"
#define VERSION "1.1.2"
#define MAX_LOG_WEAPONS 11
#define MAX_WEAPON_LEN 12
#define PREFIX_LEN 7
new g_weapon_stats[MAXPLAYERS+1][MAX_LOG_WEAPONS][15];
new const String:g_weapon_list[MAX_LOG_WEAPONS][MAX_WEAPON_LEN] = {
"870",
"revolver",
"ak47",
"usp",
"glock18c",
"glock",
"mp5",
"m4",
"supershorty",
"winchester",
"ppk"
};
new Handle:g_cvar_headshots = INVALID_HANDLE;
new Handle:g_cvar_locations = INVALID_HANDLE;
new Handle:g_cvar_ktraj = INVALID_HANDLE;
new bool:g_logheadshots = true;
new bool:g_loglocations = true;
new bool:g_logktraj = true;
new g_iNextHitgroup[MAXPLAYERS+1];
#include <loghelper>
#include <wstatshelper>
public Plugin:myinfo = {
name = NAME,
author = "psychonic",
description = "Advanced logging for ZPS. Generates auxilary logging for use with log parsers such as HLstatsX and Psychostats",
version = VERSION,
url = "http://www.hlxcommunity.com"
};
#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3
public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max)
#else
public bool:AskPluginLoad(Handle:myself, bool:late, String:error[], err_max)
#endif
{
new String:szGameDesc[64];
GetGameDescription(szGameDesc, sizeof(szGameDesc), true);
if (StrContains(szGameDesc, "ZPS", false) == -1)
{
new String:szGameDir[64];
GetGameFolderName(szGameDir, sizeof(szGameDir));
if (StrContains(szGameDir, "zps", false) == -1)
{
strcopy(error, err_max, "This plugin is only supported on Zombie Panic: Source");
#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3
return APLRes_Failure;
#else
return false;
#endif
}
}
#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3
return APLRes_Success;
#else
return true;
#endif
}
public OnPluginStart()
{
CreatePopulateWeaponTrie();
g_cvar_headshots = CreateConVar("superlogs_headshots", "1", "Enable logging of headshot player action (default on)", 0, true, 0.0, true, 1.0);
g_cvar_locations = CreateConVar("superlogs_locations", "1", "Enable logging of location on player death (default on)", 0, true, 0.0, true, 1.0);
g_cvar_ktraj = CreateConVar("superlogs_ktraj", "0", "Enable Psychostats \"KTRAJ\" logging (default off)", 0, true, 0.0, true, 1.0);
HookConVarChange(g_cvar_headshots, OnCvarHeadshotsChange);
HookConVarChange(g_cvar_locations, OnCvarLocationsChange);
HookConVarChange(g_cvar_ktraj, OnCvarKtrajChange);
CreateConVar("superlogs_zps_version", VERSION, NAME, FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY);
HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
HookEvent("player_death", Event_PlayerDeath);
HookEvent("player_spawn", Event_PlayerSpawn);
HookEvent("round_end", Event_RoundEnd, EventHookMode_PostNoCopy);
HookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre);
CreateTimer(1.0, LogMap);
}
public OnAllPluginsLoaded()
{
if (GetExtensionFileStatus("sdkhooks.ext") != 1)
{
SetFailState("SDK Hooks v1.3 or higher is required for SuperLogs: ZPS");
}
for (new i = 1; i <= MaxClients; i++)
{
if (IsClientInGame(i))
{
SDKHook(i, SDKHook_FireBulletsPost, OnFireBullets);
SDKHook(i, SDKHook_TraceAttackPost, OnTraceAttack);
SDKHook(i, SDKHook_OnTakeDamagePost, OnTakeDamage);
}
}
}
public OnMapStart()
{
GetTeams();
}
public OnClientPutInServer(client)
{
SDKHook(client, SDKHook_FireBulletsPost, OnFireBullets);
SDKHook(client, SDKHook_TraceAttackPost, OnTraceAttack);
SDKHook(client, SDKHook_OnTakeDamagePost, OnTakeDamage);
reset_player_stats(client);
}
public OnFireBullets(attacker, shots, String:weaponname[])
{
if (attacker > 0 && attacker <= MaxClients)
{
new weapon_index = get_weapon_index(weaponname[PREFIX_LEN]);
if (weapon_index > -1)
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_SHOTS]++;
}
}
}
public OnTraceAttack(victim, attacker, inflictor, Float:damage, damagetype, ammotype, hitbox, hitgroup)
{
if (hitgroup > 0 && attacker > 0 && attacker <= MaxClients && victim > 0 && victim <= MaxClients)
{
g_iNextHitgroup[victim] = hitgroup;
}
}
public OnTakeDamage(victim, attacker, inflictor, Float:damage, damagetype)
{
if (attacker > 0 && attacker <= MaxClients && victim > 0 && victim <= MaxClients)
{
new hitgroup = g_iNextHitgroup[victim];
if (hitgroup < 8)
{
hitgroup += LOG_HIT_OFFSET;
}
new bool:headshot = (GetClientHealth(victim) <= 0 && hitgroup == HITGROUP_HEAD);
decl String: weapon[MAX_WEAPON_LEN + PREFIX_LEN];
GetClientWeapon(attacker, weapon, sizeof(weapon));
new weapon_index = get_weapon_index(weapon[PREFIX_LEN]);
if (weapon_index > -1)
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_HITS]++;
g_weapon_stats[attacker][weapon_index][LOG_HIT_DAMAGE] += RoundToNearest(damage);
if (headshot)
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_HEADSHOTS]++;
}
}
g_iNextHitgroup[victim] = 0;
}
}
public Action:Event_PlayerDeathPre(Handle:event, const String:name[], bool:dontBroadcast)
{
// "userid" "short" // user ID who died
// "attacker" "short" // user ID who killed
// "weapon" "string" // weapon name killer used
new attacker = GetClientOfUserId(GetEventInt(event, "attacker"));
new victim = GetClientOfUserId(GetEventInt(event, "userid"));
if (g_loglocations)
{
LogKillLoc(attacker, victim);
}
if (g_logheadshots && g_iNextHitgroup[victim] == HITGROUP_HEAD)
{
LogPlayerEvent(attacker, "triggered", "headshot");
}
return Plugin_Continue;
}
public Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast)
{
// this extents the original player_death by a new fields
// "userid" "short" // user ID who died
// "attacker" "short" // user ID who killed
// "weapon" "string" // weapon name killer used
new victim = GetClientOfUserId(GetEventInt(event, "userid"));
new attacker = GetClientOfUserId(GetEventInt(event, "attacker"));
decl String: weapon[MAX_WEAPON_LEN];
GetEventString(event, "weapon", weapon, sizeof(weapon));
if (victim > 0 && attacker > 0)
{
new weapon_index = get_weapon_index(weapon);
if (weapon_index > -1)
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_KILLS]++;
g_weapon_stats[victim][weapon_index][LOG_HIT_DEATHS]++;
if (GetClientTeam(attacker) == GetClientTeam(victim))
{
g_weapon_stats[attacker][weapon_index][LOG_HIT_TEAMKILLS]++;
}
}
dump_player_stats(victim);
}
if (g_logktraj)
{
LogPSKillTraj(attacker, victim, weapon);
}
}
public Event_PlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast)
{
// "userid" "short" // user ID on server
new client = GetClientOfUserId(GetEventInt(event, "userid"));
if (client > 0)
{
reset_player_stats(client);
}
}
public Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast)
{
WstatsDumpAll();
}
public Action:Event_PlayerDisconnect(Handle:event, const String:name[], bool:dontBroadcast)
{
new client = GetClientOfUserId(GetEventInt(event, "userid"));
OnPlayerDisconnect(client);
return Plugin_Continue;
}
public Action:LogMap(Handle:timer)
{
// Called 1 second after OnPluginStart since srcds does not log the first map loaded. Idea from Stormtrooper's "mapfix.sp" for psychostats
LogMapLoad();
}
public OnCvarHeadshotsChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_logheadshots;
g_logheadshots = GetConVarBool(g_cvar_headshots);
if (old_value != g_logheadshots)
{
if (g_logheadshots && !g_loglocations)
{
HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
}
else if (!g_loglocations)
{
UnhookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
}
}
}
public OnCvarLocationsChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
new bool:old_value = g_loglocations;
g_loglocations = GetConVarBool(g_cvar_locations);
if (old_value != g_loglocations)
{
if (g_loglocations && !g_logheadshots)
{
HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
}
else if (!g_logheadshots)
{
UnhookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre);
}
}
}
public OnCvarKtrajChange(Handle:cvar, const String:oldVal[], const String:newVal[])
{
g_logktraj = GetConVarBool(g_cvar_ktraj);
}