amxmodx/plugins/statsx.sma
2004-07-29 10:58:54 +00:00

1872 lines
63 KiB
Plaintext
Executable File

/************************************************************
AMX mod X script. Copyright (C) 2002-2004, XAD.
$Id: statsx.sma, v 0.9.8 2004-Mar-29
************************************************************/
/*************************************************************
StatsX AMX-plugin v0.9.8.
Based on "stats.sma" original written by OLO.
This file is provided as is (no warranties).
=============================================================
Description:
------------
This AMX-plugin will display game stats as HUD and chat messages.
Example of stats are: killing stats, most damage, best score,
accuracy, efficiency, attackers list, victims list, team scores.
All effeciency stats are calculated per game, not per round.
The accuracy stats are calculated per round, except for "/score"
which is per game.
Plugin support players switching teams and awarded players
disconnecting from server before round end. It also supports
game restart, ie all stats are resetted.
Plugin also support players switching of the stats announcements
on the HUD. The setting is a client parameter and therefore
saved between playing sessions.
Server Config:
--------------
Duration of HUD-statistics. (Default 12 sec.)
amx_statsx_duration < time in seconds >
HUD-statistics display limit relative round freeze end.
Negative time will clear the HUD-statstics before the
round freeze time has ended. (Default -2 sec.)
amx_statsx_freeze < time in seconds >
When activating this plugin the Stats Settings Plugin,
"statscfg", should also be activated.
Activating the Stats Settings Plugin allows configuration
to be done either with client GUI menus or in server
configuration files.
amx_statscfg < "on" or "off" > < option >
StatsX options:
"KillerChat" - show killer stats in the chat section
"ShowAttackers" - show attackers on HUD (deactivated)
"ShowVictims" - show victims on HUD (deactivated)
"ShowKiller" - show killer on HUD
"ShowTeamScore" - shows team score at round end (deactivated)
"ShowTotalStats" - shows round total stats (deactivated)
"ShowBestScore" - shows rounds best scored player
"ShowMostDisruptive"
- shows rounds most disruptive player
"EndPlayer" - display player stats MOTD at map end
"EndTop15" - display top15 MOTD at the end of map
"SayHP" - allow for say /hp
"SayStatsMe" - allow for say /statsme
"SayRankStats" - allow for say /rankstats
"SayMe" - allow for say /me
"SayRank" - allow for say /rank
"SayReport" - allow for say /report
"SayScore" - allow for say /score
"SayTop15" - allow for say /top15
"SayStatsAll" - allow for say /stats
"SayMiscFunc" - allow for say /timeleft, /thetime,
/currentmap, /ff
"ShowStats" - client HUD-stats switched on by default
"ShowDistHS" - show distance and HS in attackers and
victims HUD lists
"SpecRankInfo" - displays rank info when spectating
Stats plugin options (flags) to define what to display to
the clients.
NOTE! To deactivate all options set flags to " "
(one quoted space).
amx_statsx_mode < flags >
"a" - delay (0.01s) HUD stats on HUD reset
Client Commands:
----------------
Display info about your killer in the chat section.
say /hp
Display info about your stats, on the current map,
in a MOTD window.
say /statsme
Display your rank stats, in a MOTD window.
say /rankstats
Display your current round hit-stats in the chat section.
say /me
Display your rank in the chat section.
say /rank
Display the game score and stats in the chat section.
say /score
Display players current weapon status, as a say team command.
say /report
Display the 15 highest ranked players in a MOTD window.
say /top15
Display all players stats and rank in a MOTD window. The
displayed player is selection from a menu.
say /stats
Display time left on the map, as a say command.
say /timeleft
Display the current time, as a say command.
say /thetime
Display the current map, as a say command.
say /currentmap
Display the current status on friendly fire flag,
as a say command.
say /ff
Switch on or off all HUD-stats announcements ("_amxstatsx").
say /switch
=============================================================
Requirements/Limitations:
-------------------------
- AMX Mod X v0.16: Tested.
- Replaces "stats.amx" by OLO.
- Requires the AMX module "csstats_amx".
- You are recommended to not run any other death or
end-round plugins.
- If HUD-statistics are activated it is recommended that
other HUD-messages are deactivated as these otherwise
will disrupt HUD-statistics, such as PTB's. This is a
HalfLife-engine limitation.
- It is not recommended to use "mp_freezetime" less than
1.0 sec.
Installation:
-------------
- Copy the StatsX file "statsx.amx" to the AMX plugins
folder.
- In the AMX Mod X plugin configuration file ("plugins.ini"),
replace the line "stats.amx" with "statsx.amx".
- Add StatsX cvar to the server or AMX Mod X configuration file.
If menu configuration will not be used then also add
the "amx_statscfg" commands to one of the configuration
files.
- Restart server. Normally changing map is sufficient but
restarting the server is the "safe" procedure.
- If menu configuration is used then reconfigure the
stats settings using the "amx_statscfgmenu" command.
Tips:
-----
NOTE! All statistics collection is done by the module
"csstats_amx" and questions or issues should be directed
to the module author.
- To reset the player statistics in AMX Mod X 0.16, enter
"csstats_reset 1" in the server consol. At the next
mapchange the stats will be reset and the cvar
"csstats_reset" will be set back to "0". If you put
this command in the server.cfg the stats will be reset
at every mapchange.
- In AMX Mod X 0.16 statistics are by default only saved if the
difference between kills and deaths are greater than 0.
Comments:
---------
This code is based on/parts taken from OLO's stats v0.8.
- Added "endround" and some other functionality from
StatsMe 2.7 source code and config files.
- This plugin has been updated with new functionality
added to "stats" (by OLO), v0.8 to v0.9.6.
- New functions and configuration flags have been added.
- Original "/rank" is changed to "/rankstats".
- Attackers/victims is displayed before next round end.
- Last shot and kill included in all stats.
- Changed HUD number on killer stats to "2" to make
round end stats work with PTB.
- All statistics collection is done by the module
"csstats_mm" and questions or issues should be directed
to the module author.
Money is not supported in "say /report" (requires fun-module).
=============================================================
Modlog:
-------
0.8 2002-xx-xx OLO Original "stats.sma" version 0.8.
(c) Copyright 2002, OLO
This file is provided as is (no waranties).
0.8a 2002-12-xx XAD First modified version. Added file header.
0.8c 2002-12-20 XAD First production tested version.
0.9a 2003-01-23 XAD Recompiled for AMX 0.9.0.
0.9d 2003-02-14 XAD Added player weapon stats, timeleft, thetime,
and currentmap.
0.9e 2003-02-15 XAD Fixed missed friendly fire command registration.
Fixed divide by zero in best score and most
disruptive.
0.9g 2003-02-19 XAD Changed "/rank" to "/rankstats".
Added a new "/rank" command to report rank as
chat message.
Changed "/hp" to be in the chat section instead
on HUD.
Fixed some misspellings and changed from "say"
to server chat.
Added "/me", your current round stats.
Added "/switch", client command to disable all
stats announcements.
0.9h 2003-02-26 XAD Added player-rank menu (from stats/0.9.2).
Added config menu (from stats/0.9.3).
Increased global buffer to 2048 (fix in stats/0.9.3)
Changed to set tab equal to 4 spaces (from 6).
0.9i 2003-03-11 XAD Added efficiency and accuracy to top15, stats, and
rankstats MOTD (thanks Coxton).
Removed deny message on the non-stats related
commands so these can be overriden by other scripts.
0.9i.1 2003-03-20 XAD Fixed bug in "/statsme" (thanks NiLuJe).
0.9i.2 2003-04-05 XAD Fixed bug in "/statsme" (thanks NiLuJe, doh).
0.9j 2003-06-05 XAD Support client HUD stats switched off by default,
mode flag "s" (thanks Troopa).
Added killing distance to "/me", accuracy and HS-kills
for each weapons in "/statsme", killing distance and
HS on victim and killer HUD stats (thanks versus666).
Added HS-kills to "/statsme" and "/rankstats"
(thanks Jim66).
Added team score prediction to remove 0.5 sec. task
delay on round end stats.
Changed death-stats to be triggered 0.25 sec. after
death instead of "late" hit-event, to fix problems
with 3:rd party modules (thanks server_sgz).
Added support for HUD statistics over HUD reset with
time limits relative to the round freeze end.
Added cvar for HUD time parameters.
Special thanks to server_sgz for test work.
0.9.6b 2003-09-12 XAD Adapted to AMX 0.9.6h.
Added config menu ("statscfg" for AMX 0.9.6) and
removed old ("statsset" from AMX 0.9.3).
Changed to html in MOTD (thanks ootoaoo).
Deactivated victim list, attacker list, team score,
and total HUD-stats. The killer, best score, most
disruptive HUD-stats have been reduced in size to
be within the 79 character HUD-message limit.
Thanks for debug reports: BMJ.
0.9.8a 2003-11-30 XAD Updated for CS release 2003-nov-26.
Restored all HUD-messages changed in v0.9.6b, as
HUD-message limit has increased to 480 characters.
Reactivated last-hit fix, as AMX core function
"CS_DeathMsg" doesn't always work.
0.9.8a1a 2004-01-03 XAD Removed show_roundend_hudstats from kill_stats.
Reduced HUD_MIN_DURATION from 1 sec to 0.2 sec.
0.9.8b 2004-03-29 XAD Updated for AMX Mod X 0.16.
Changed the code to support non-zero initializing
Small compilers.
Removed support for 79 char limit HUD-message.
*************************************************************/
new PLUGIN_AUTHOR[] = "XAD (OLO)";
new PLUGIN_NAME[] = "STATSX";
new STRING_VERSION[] = "0.9.8b";
//--------------------------------
#include <amxmodx>
#include <amxmisc>
#include <csstats>
//--------------------------------
// Uncomment to activate log debug messages.
//#define STATSX_DEBUG
//#define STATSX_DEBUG2
// Comment to activate new DeathMsg process (if fixed).
#define STATSX_OLD_DEATHMSG
// HUD statistics duration in seconds (minimum 1.0 seconds).
new HUD_DURATION_CVAR[] = "amx_statsx_duration";
new HUD_DURATION[] = "12.0";
// HUD statistics stop relative freeze end in seconds.
// To stop before freeze end use a negative value.
new HUD_FREEZE_LIMIT_CVAR[] = "amx_statsx_freeze";
new HUD_FREEZE_LIMIT[] = "-2.0";
// HUD statistics minimum duration, in seconds, to trigger the display logic.
#define HUD_MIN_DURATION 0.2
// Config plugin constants.
#define MODE_HUD_DELAY 0 // Make a 0.01 sec delay on HUD reset process.
// You can also manualy enable or disable these options by setting them to 1
// For example:
// public ShowAttackers = 1
// However amx_statscfg command is recommended
public KillerChat = 0; // displays killer hp&ap to victim console
// and screen
public ShowAttackers = 0; // shows attackers
public ShowVictims = 0; // shows victims
public ShowKiller = 0; // shows killer
public ShowTeamScore = 0; // shows team score at round end
public ShowTotalStats = 0; // shows round total stats
public ShowBestScore = 0; // shows rounds best scored player
public ShowMostDisruptive = 0; // shows rounds most disruptive player
public EndPlayer = 0; // displays player stats at the end of map
public EndTop15 = 0; // displays top15 at the end of map
public SayHP = 0; // displays information about user killer
public SayStatsMe = 0; // displays user's stats and rank
public SayRankStats = 0; // displays user's rank stats
public SayMe = 0; // displays user's stats
public SayRank = 0; // displays user's rank
public SayReport = 0; // report user's weapon status to team
public SayScore = 0; // displays team's map score
public SayTop15 = 0; // displays first 15 players
public SayStatsAll = 0; // displays all players stats and rank
public SayMiscFunc = 0; // displays timeleft, thetime, currentmap, ff
public ShowStats = 0; // set client HUD-stats switched off by default
public ShowDistHS = 0; // show distance and HS in attackers and
// victims HUD lists
public ShowFullStats = 0; // show full HUD stats (more than 78 chars)
public SpecRankInfo = 0; // displays rank info when spectating
// Standard Contstants.
#define MAX_TEAMS 2
#define MAX_PLAYERS 32 + 1
#define MAX_NAME_LENGTH 31
#define MAX_WEAPON_LENGTH 31
#define MAX_TEXT_LENGTH 255
#define MAX_BUFFER_LENGTH 2047
// User messages.
new g_sDisabledMsg[] = "Server has disabled that option";
// User stats parms id
#define STATS_KILLS 0
#define STATS_DEATHS 1
#define STATS_HS 2
#define STATS_TKS 3
#define STATS_SHOTS 4
#define STATS_HITS 5
#define STATS_DAMAGE 6
// Global player flags.
new BODY_PART[8][] = {
"wholebody",
"head",
"chest",
"stomach",
"leftarm",
"rightarm",
"leftleg",
"rightleg"
}
// Killer information, save killer info at the time when player is killed.
#define KILLED_KILLER_ID 0 // Killer userindex/user-ID
#define KILLED_KILLER_HEALTH 1 // Killer's health
#define KILLED_KILLER_ARMOUR 2 // Killer's armour
#define KILLED_TEAM 3 // Killer's team
#define KILLED_KILLER_STATSFIX 4 // Fix to register the last hit/kill
new g_izKilled[MAX_PLAYERS][5];
// Menu variables and configuration
#define MAX_PPL_MENU_ACTIONS 2 // Number of player menu actions
#define PPL_MENU_OPTIONS 7 // Number of player options per displayed menu
new g_iPluginMode = 0;
new g_izUserMenuPosition[MAX_PLAYERS] = { 0, ... };
new g_izUserMenuAction[MAX_PLAYERS] = { 0, ... };
new g_izUserMenuPlayers[MAX_PLAYERS][32];
new g_izSpecMode[MAX_PLAYERS] = { 0, ... };
new g_izShowStatsFlags[MAX_PLAYERS] = { 0, ... };
new g_izStatsSwitch[MAX_PLAYERS] = { 0, ... };
new Float:g_fzShowUserStatsTime[MAX_PLAYERS] = { 0.0, ... };
new Float:g_fShowStatsTime = 0.0;
new Float:g_fFreezeTime = 0.0;
new Float:g_fFreezeLimitTime = 0.0;
new Float:g_fHUDDuration = 0.0;
new g_iRoundEndTriggered = 0;
new g_iRoundEndProcessed = 0;
new Float:g_fStartGame = 0.0;
new g_izTeamScore[MAX_TEAMS] = { 0, ... };
new g_izTeamEventScore[MAX_TEAMS] = { 0, ... };
new g_izTeamRndStats[MAX_TEAMS][8];
new g_izTeamGameStats[MAX_TEAMS][8];
new g_izUserUserID[MAX_PLAYERS] = { 0, ... };
new g_izUserAttackerDistance[MAX_PLAYERS] = { 0, ... };
new g_izUserVictimDistance[MAX_PLAYERS][MAX_PLAYERS];
new g_izUserRndName[MAX_PLAYERS][MAX_NAME_LENGTH];
new g_izUserRndStats[MAX_PLAYERS][8];
new g_izUserGameStats[MAX_PLAYERS][8];
// Common buffer to improve performance, as Small always zero-initializes all vars
new g_sBuffer[MAX_BUFFER_LENGTH+1] = "";
new g_sScore[MAX_TEXT_LENGTH+1] = "";
new g_sAwardAndScore[MAX_BUFFER_LENGTH+1] = "";
new t_sText[MAX_TEXT_LENGTH+1] = "";
new t_sName[MAX_NAME_LENGTH+1] = "";
new t_sWpn[MAX_WEAPON_LENGTH+1] = "";
//--------------------------------
// Initialize
//--------------------------------
public plugin_init() {
// Register plugin.
log_message( "[%s] %s v%s", PLUGIN_NAME, "StatsX", STRING_VERSION );
register_plugin( "StatsX", STRING_VERSION, PLUGIN_AUTHOR );
// Register events.
register_event( "TextMsg", "eventStartGame", "a",
"2=#Game_Commencing", "2=#Game_will_restart_in" );
register_event( "ResetHUD", "eventResetHud", "be" );
register_event( "RoundTime", "eventStartRound", "bc" );
#if defined STATSX_OLD_DEATHMSG
register_event( "DeathMsg", "eventDeathMsg", "a" );
register_event( "Damage", "eventDamage", "b", "2!0", "3=0", "4!0" );
#else
register_event( "CS_DeathMsg", "eventCSDeathMsg", "a" );
#endif
register_event( "SendAudio", "eventEndRound", "a",
"2=%!MRAD_terwin", "2=%!MRAD_ctwin", "2=%!MRAD_rounddraw" );
register_event( "TeamScore", "eventTeamScore", "a" );
register_event( "30", "eventIntermission", "a" );
register_event( "TextMsg", "eventSpecMode", "bd", "2&ec_Mod" );
register_event( "StatusValue", "eventShowRank", "bd", "1=2" );
// Register commands.
register_clcmd( "say /hp", "cmdHp", 0, "- display info. about your killer (chat)" );
register_clcmd( "say /statsme", "cmdStatsMe", 0, "- display your stats (MOTD)" );
register_clcmd( "say /rankstats", "cmdRankStats", 0, "- display your server stats (MOTD)" );
register_clcmd( "say /me", "cmdMe", 0, "- display current round stats (chat)" );
register_clcmd( "say /score", "cmdScore", 0, "- display last score (chat)" );
register_clcmd( "say /rank", "cmdRank", 0, "- display your rank (chat)" );
register_clcmd( "say /report", "cmdReport", 0, "- display waepon status (say_team)" );
register_clcmd( "say /top15", "cmdTop15", 0, "- display top 15 players (MOTD)" );
register_clcmd( "say /stats", "cmdStats", 0, "- display players stats (menu/MOTD)" );
register_clcmd( "say /timeleft", "cmdTimeLeft", 0, "- display time left on map (say)" );
register_clcmd( "say /thetime", "cmdTheTime", 0, "- display the time (say)" );
register_clcmd( "say /currentmap", "cmdCurrentMap", 0, "- display current map (say)" );
register_clcmd( "say /ff", "cmdFf", 0, "- display friendly fire status (say)" );
register_clcmd( "say /switch", "cmdSwitch", 0, "- switch client's stats on or off" );
// Register menus.
register_menucmd( register_menuid("Server Stats"), 1023, "actionStatsMenu" );
// Register special configuration setting and default value.
register_srvcmd( "amx_statsx_mode", "cmdPluginMode", ADMIN_CFG, "<flags> - sets plugin options" );
#if defined STATSX_DEBUG2
register_clcmd( "say /hudtest", "cmdHudTest" );
#endif
register_cvar( HUD_DURATION_CVAR, HUD_DURATION );
register_cvar( HUD_FREEZE_LIMIT_CVAR, HUD_FREEZE_LIMIT );
// Init buffers and some global vars.
g_sBuffer[0] = 0;
save_team_chatscore();
// Update local configuration vars with value in cvars.
get_config_cvars();
return PLUGIN_CONTINUE;
}
new g_addStast[] = "amx_statscfg add ^"%s^" %s"
public plugin_cfg(){
server_cmd( g_addStast, "Show killer hp&ap","KillerChat" );
server_cmd( g_addStast, "Show Attackers", "ShowAttackers" );
server_cmd( g_addStast, "Show Victims", "ShowVictims" );
server_cmd( g_addStast, "Show killer", "ShowKiller" );
server_cmd( g_addStast, "Show Team Score", "ShowTeamScore" );
server_cmd( g_addStast, "Show Total Stats", "ShowTotalStats" );
server_cmd( g_addStast, "Show Best Score", "ShowBestScore" );
server_cmd( g_addStast, "Show Most Disruptive", "ShowMostDisruptive" );
server_cmd( g_addStast, "HUD-stats default", "ShowStats" );
server_cmd( g_addStast, "Dist&HS in HUD lists", "ShowDistHS" );
server_cmd( g_addStast, "Stats at the end of map", "EndPlayer");
server_cmd( g_addStast, "Top15 at the end of map", "EndTop15" );
server_cmd( g_addStast, "Say /hp", "SayHP" );
server_cmd( g_addStast, "Say /statsme", "SayStatsMe" );
server_cmd( g_addStast, "Say /rankstats", "SayRankStats" );
server_cmd( g_addStast, "Say /me", "SayMe" );
server_cmd( g_addStast, "Say /rank", "SayRank" );
server_cmd( g_addStast, "Say /report", "SayReport" );
server_cmd( g_addStast, "Say /score", "SayScore" );
server_cmd( g_addStast, "Say /top15", "SayTop15" );
server_cmd( g_addStast, "Say /stats", "SayStatsAll" );
server_cmd( g_addStast, "Misc say commands", "SayMiscFunc" );
server_cmd( g_addStast, "Spec. Rank Info", "SpecRankInfo" );
}
// Set hudmessage format.
set_hudtype_killer( Float:fDuration )
set_hudmessage( 220, 80, 0, 0.05, 0.15, 0, 6.0, fDuration, (fDuration>=g_fHUDDuration)?1.0:0.0, 1.0, 2 );
set_hudtype_endround( Float:fDuration )
set_hudmessage( 100, 200, 0, 0.05, 0.55, 0, 0.02, fDuration, (fDuration>=g_fHUDDuration)?1.0:0.0, 1.0, 1 );
set_hudtype_attacker( Float:fDuration )
set_hudmessage( 220, 80, 0, 0.55, 0.35, 0, 6.0, fDuration, (fDuration>=g_fHUDDuration)?1.0:0.0, 1.0, 3 );
set_hudtype_victim( Float:fDuration )
set_hudmessage( 0, 80, 220, 0.55, 0.60, 0, 6.0, fDuration, (fDuration>=g_fHUDDuration)?1.0:0.0, 1.0, 4 );
set_hudtype_specmode() {
set_hudmessage( 255, 255, 255, 0.02, 0.87, 2, 0.05, 0.1, 0.01, 3.0, 1);
}
#if defined STATSX_DEBUG2
public cmdHudTest( id ) {
new i, iLen;
iLen = 0;
for( i = 1; i < 20; i++ )
iLen += format( g_sBuffer[iLen], MAX_BUFFER_LENGTH - iLen, "....x....1....x....2....x....3....x....4....x....^n" );
set_hudtype_killer( 50.0 );
show_hudmessage( id, g_sBuffer );
}
#endif
// Stats formulas
Float:accuracy( izStats[8] ) {
if( !izStats[STATS_SHOTS] ) return ( 0.0 );
return ( 100.0 * float( izStats[STATS_HITS] ) / float( izStats[STATS_SHOTS] ) );
}
Float:effec( izStats[8] ) {
if( !izStats[STATS_KILLS] ) return ( 0.0 );
return ( 100.0 * float( izStats[STATS_KILLS] ) / float( izStats[STATS_KILLS] + izStats[STATS_DEATHS] ) );
}
// Distance formula (metric)
Float:distance( iDistance ) {
return float( iDistance ) * 0.0254;
}
// Get plugin config flags.
set_plugin_mode( id, sFlags[] ){
if( sFlags[0] )
g_iPluginMode = read_flags( sFlags );
get_flags( g_iPluginMode, t_sText, MAX_TEXT_LENGTH );
console_print( id, "^"amx_statsx_mode^" set to ^"%s^"", t_sText );
return g_iPluginMode;
}
// Get config parameters.
get_config_cvars() {
g_fFreezeTime = get_cvar_float("mp_freezetime");
if( g_fFreezeTime < 0.0 ) g_fFreezeTime = 0.0;
g_fHUDDuration = get_cvar_float( HUD_DURATION_CVAR );
if( g_fHUDDuration < 1.0 ) g_fHUDDuration = 1.0;
g_fFreezeLimitTime = get_cvar_float( HUD_FREEZE_LIMIT_CVAR );
return;
}
// Get and format attackers header and list.
get_attackers( id, sBuffer[MAX_BUFFER_LENGTH+1] ) {
new izStats[8], izBody[8];
new iAttacker;
new iFound, iLen;
new iMaxPlayer = get_maxplayers();
iFound = 0;
sBuffer[0] = 0;
// Get and format header. Add killing attacker statistics if user is dead.
// Make sure shots is greater than zero or division by zero will occur.
// To print a '%', 4 of them must done in a row.
izStats[STATS_SHOTS] = 0;
iAttacker = g_izKilled[id][KILLED_KILLER_ID];
if( iAttacker )
get_user_astats( id, iAttacker, izStats, izBody );
if( izStats[STATS_SHOTS] && ShowFullStats ) {
get_user_name( iAttacker, t_sName, MAX_NAME_LENGTH );
iLen = format( sBuffer, MAX_BUFFER_LENGTH, "Attackers -- %s -- %0.2f%%%% acc.:^n",
t_sName, accuracy( izStats ) );
}
else
iLen = format( sBuffer, MAX_BUFFER_LENGTH, "Attackers:^n" );
// Get and format attacker list.
for( iAttacker = 1; iAttacker <= iMaxPlayer; iAttacker++ ) {
if( get_user_astats( id, iAttacker, izStats, izBody, t_sWpn, MAX_WEAPON_LENGTH ) ) {
iFound = 1;
get_user_name( iAttacker, t_sName, 32 );
if( izStats[STATS_KILLS] ) {
if( !ShowDistHS )
iLen += format( sBuffer[iLen], MAX_BUFFER_LENGTH - iLen,
"%s -- %d hit(s) / %d dmg / %s^n",
t_sName, izStats[STATS_HITS], izStats[STATS_DAMAGE],
t_sWpn );
else if( izStats[STATS_HS] )
iLen += format( sBuffer[iLen], MAX_BUFFER_LENGTH - iLen,
"%s -- %d hit(s) / %d dmg / %s / %0.0f m / HS^n",
t_sName, izStats[STATS_HITS], izStats[STATS_DAMAGE],
t_sWpn, distance(g_izUserAttackerDistance[id]) );
else
iLen += format( sBuffer[iLen], MAX_BUFFER_LENGTH - iLen,
"%s -- %d hit(s) / %d dmg / %s / %0.0f m^n",
t_sName, izStats[STATS_HITS], izStats[STATS_DAMAGE],
t_sWpn, distance(g_izUserAttackerDistance[id]) );
}
else
iLen += format( sBuffer[iLen], MAX_BUFFER_LENGTH - iLen,
"%s -- %d hit(s) / %d dmg^n",
t_sName, izStats[STATS_HITS], izStats[STATS_DAMAGE] );
}
}
if( !iFound )
sBuffer[0] = 0;
return iFound;
}
// Get and format victims header and list
get_victims( id, sBuffer[MAX_BUFFER_LENGTH+1] ) {
new izStats[8], izBody[8];
new iVictim;
new iFound, iLen;
new iMaxPlayer = get_maxplayers();
iFound = 0;
sBuffer[0] = 0;
// Get and format header.
// Make sure shots is greater than zero or division by zero will occur.
// To print a '%', 4 of them must done in a row.
izStats[STATS_SHOTS] = 0;
get_user_vstats( id, 0, izStats, izBody );
if( izStats[STATS_SHOTS] )
iLen = format( sBuffer, MAX_BUFFER_LENGTH, "Victims -- %0.2f%%%% acc.:^n",
accuracy( izStats ) );
else
iLen = format( sBuffer, MAX_BUFFER_LENGTH, "Victims:^n" );
for( iVictim = 1; iVictim <= iMaxPlayer; iVictim++ ) {
if( get_user_vstats( id, iVictim, izStats, izBody, t_sWpn, MAX_WEAPON_LENGTH ) ) {
iFound = 1;
get_user_name( iVictim, t_sName, MAX_NAME_LENGTH );
if( izStats[STATS_DEATHS] ) {
if( !ShowDistHS )
iLen += format( sBuffer[iLen], MAX_BUFFER_LENGTH - iLen,
"%s -- %d hit(s) / %d dmg / %s^n",
t_sName, izStats[STATS_HITS], izStats[STATS_DAMAGE],
t_sWpn );
else if( izStats[STATS_HS] )
iLen += format( sBuffer[iLen], MAX_BUFFER_LENGTH - iLen,
"%s -- %d hit(s) / %d dmg / %s / %0.0f m / HS^n",
t_sName, izStats[STATS_HITS], izStats[STATS_DAMAGE],
t_sWpn, distance(g_izUserVictimDistance[id][iVictim]) );
else
iLen += format( sBuffer[iLen], MAX_BUFFER_LENGTH - iLen,
"%s -- %d hit(s) / %d dmg / %s / %0.0f m^n",
t_sName, izStats[STATS_HITS], izStats[STATS_DAMAGE],
t_sWpn, distance(g_izUserVictimDistance[id][iVictim]) );
}
else
iLen += format( sBuffer[iLen], MAX_BUFFER_LENGTH - iLen,
"%s -- %d hit(s) / %d dmg^n",
t_sName, izStats[STATS_HITS], izStats[STATS_DAMAGE] );
}
}
if( !iFound )
sBuffer[0] = 0;
return iFound;
}
// Get and format kill info.
get_kill_info( id, iKiller, sBuffer[MAX_BUFFER_LENGTH+1] ) {
new iFound, iLen;
iFound = 0;
sBuffer[0] = 0;
if( iKiller && iKiller != id ) {
new izAStats[8], izABody[8], izVStats[8], iaVBody[8];
iFound = 1;
get_user_name( iKiller, t_sName, MAX_NAME_LENGTH );
izAStats[STATS_HITS] = 0;
izAStats[STATS_DAMAGE] = 0;
t_sWpn[0] = 0;
get_user_astats( id, iKiller, izAStats, izABody, t_sWpn, MAX_WEAPON_LENGTH );
izVStats[STATS_HITS] = 0;
izVStats[STATS_DAMAGE] = 0;
get_user_vstats( id, iKiller, izVStats, iaVBody );
iLen = format( sBuffer, MAX_BUFFER_LENGTH,
"%s killed you with %s^nfrom distance of %0.2f meters.^n",
t_sName, t_sWpn, distance(g_izUserAttackerDistance[id]) );
iLen += format( sBuffer[iLen], MAX_BUFFER_LENGTH - iLen,
"He did %d damage to you with %d hit(s)^nand still has %dhp and %dap.^n",
izAStats[STATS_DAMAGE], izAStats[STATS_HITS],
g_izKilled[id][KILLED_KILLER_HEALTH], g_izKilled[id][KILLED_KILLER_ARMOUR] );
iLen += format( sBuffer[iLen], MAX_BUFFER_LENGTH - iLen,
"You did %d damage to him with %d hit(s).^n",
izVStats[STATS_DAMAGE], izVStats[STATS_HITS] );
}
return iFound;
}
// Get and format most disruptive.
add_most_disruptive( sBuffer[MAX_BUFFER_LENGTH+1] ) {
new id, iMaxDamageId, iMaxDamage, iMaxHeadShots;
iMaxDamageId = 0;
iMaxDamage = 0;
iMaxHeadShots = 0;
// Find player.
for( id = 1; id < MAX_PLAYERS; id++ ) {
if( g_izUserRndStats[id][STATS_DAMAGE] >= iMaxDamage &&
( g_izUserRndStats[id][STATS_DAMAGE] > iMaxDamage || g_izUserRndStats[id][STATS_HS] > iMaxHeadShots ) ) {
iMaxDamageId = id;
iMaxDamage = g_izUserRndStats[id][STATS_DAMAGE];
iMaxHeadShots = g_izUserRndStats[id][STATS_HS];
}
}
// Format statistics.
if( iMaxDamageId ) {
id = iMaxDamageId;
new Float:fGameEff = effec( g_izUserGameStats[id] );
new Float:fRndAcc = accuracy( g_izUserRndStats[id] );
format( t_sText, MAX_TEXT_LENGTH,
"Most damage done by: %s^n%d hit(s) / %d dmg -- %0.2f%%%% eff. / %0.2f%%%% acc.^n",
g_izUserRndName[id], g_izUserRndStats[id][STATS_HITS],
iMaxDamage, fGameEff, fRndAcc );
add( sBuffer, MAX_BUFFER_LENGTH, t_sText );
}
return iMaxDamageId;
}
// Get and format best score.
add_best_score( sBuffer[MAX_BUFFER_LENGTH+1] ) {
new id, iMaxKillsId, iMaxKills, iMaxHeadShots;
iMaxKillsId = 0;
iMaxKills = 0;
iMaxHeadShots = 0;
// Find player
for( id = 1; id < MAX_PLAYERS; id++ ) {
if( g_izUserRndStats[id][STATS_KILLS] >= iMaxKills &&
( g_izUserRndStats[id][STATS_KILLS] > iMaxKills || g_izUserRndStats[id][STATS_HS] > iMaxHeadShots ) ) {
iMaxKillsId = id;
iMaxKills = g_izUserRndStats[id][STATS_KILLS];
iMaxHeadShots = g_izUserRndStats[id][STATS_HS];
}
}
// Format statistics.
if( iMaxKillsId ) {
id = iMaxKillsId;
new Float:fGameEff = effec( g_izUserGameStats[id] );
new Float:fRndAcc = accuracy( g_izUserRndStats[id] );
format( t_sText, MAX_TEXT_LENGTH,
"Best score: %s^n%d kill(s) / %d hs -- %0.2f%%%% eff. / %0.2f%%%% acc.^n",
g_izUserRndName[id], iMaxKills, iMaxHeadShots, fGameEff, fRndAcc );
add( sBuffer, MAX_BUFFER_LENGTH, t_sText );
}
return iMaxKillsId;
}
// Get and format team score.
add_team_score( sBuffer[MAX_BUFFER_LENGTH+1] ) {
new Float:fzMapEff[MAX_TEAMS], Float:fzMapAcc[MAX_TEAMS], Float:fzRndAcc[MAX_TEAMS];
// Calculate team stats
for( new iTeam = 0; iTeam < MAX_TEAMS; iTeam++ ) {
fzMapEff[iTeam] = effec( g_izTeamGameStats[iTeam] );
fzMapAcc[iTeam] = accuracy( g_izTeamGameStats[iTeam] );
fzRndAcc[iTeam] = accuracy( g_izTeamRndStats[iTeam] );
}
// Format round team stats, MOTD
format( t_sText, MAX_TEXT_LENGTH,
"TERRORIST %i / %0.2f%%%% eff. / %0.2f%%%% acc.^nCT %i / %0.2f%%%% eff. / %0.2f%%%% acc.^n",
g_izTeamScore[0], fzMapEff[0], fzRndAcc[0], g_izTeamScore[1], fzMapEff[1], fzRndAcc[1] );
add( sBuffer, MAX_BUFFER_LENGTH, t_sText );
}
// Get and format team stats, chat version
save_team_chatscore() {
new Float:fzMapEff[MAX_TEAMS], Float:fzMapAcc[MAX_TEAMS], Float:fzRndAcc[MAX_TEAMS];
// Calculate team stats
for( new iTeam = 0; iTeam < MAX_TEAMS; iTeam++ ) {
fzMapEff[iTeam] = effec( g_izTeamGameStats[iTeam] );
fzMapAcc[iTeam] = accuracy( g_izTeamGameStats[iTeam] );
fzRndAcc[iTeam] = accuracy( g_izTeamRndStats[iTeam] );
}
// Format game team stats, chat
format( g_sScore, MAX_BUFFER_LENGTH,
"TERRORIST %i / %0.2f%%%% eff. / %0.2f%%%% acc. -- CT %i / %0.2f%%%% eff. / %0.2f%%%% acc.",
g_izTeamScore[0], fzMapEff[0], fzMapAcc[0], g_izTeamScore[1], fzMapEff[1], fzMapAcc[1] );
}
// Get and format total stats.
add_total_stats( sBuffer[MAX_BUFFER_LENGTH+1] ) {
format( t_sText, MAX_TEXT_LENGTH,
"Total: %d kill(s) / %d hs -- %d hit(s) / %d shot(s)^n",
g_izUserRndStats[0][STATS_KILLS], g_izUserRndStats[0][STATS_HS],
g_izUserRndStats[0][STATS_HITS], g_izUserRndStats[0][STATS_SHOTS] );
add( sBuffer, MAX_BUFFER_LENGTH, t_sText );
}
// Get and format a user's list of body hits from an attacker.
add_attacker_hits( id, iAttacker, sBuffer[MAX_BUFFER_LENGTH+1] ) {
new iFound = 0;
if( iAttacker && iAttacker != id ) {
new izStats[8], izBody[8], iLen;
izStats[STATS_HITS] = 0;
get_user_astats( id, iAttacker, izStats, izBody );
if( izStats[STATS_HITS] ) {
iFound = 1;
iLen = strlen( sBuffer );
get_user_name( iAttacker, t_sName, MAX_NAME_LENGTH );
iLen += format( sBuffer[iLen], MAX_BUFFER_LENGTH - iLen,
"%s hits you in:^n", t_sName );
for( new i = 1; i < 8; i++ ) {
if( !izBody[i] ) continue;
iLen += format( sBuffer[iLen], MAX_BUFFER_LENGTH - iLen,
"%s: %d^n", BODY_PART[i], izBody[i] );
}
}
}
return iFound;
}
// Get and format killed stats: killer hp, ap, hits.
format_kill_ainfo( id, iKiller, sBuffer[MAX_BUFFER_LENGTH+1] ) {
new iFound = 0;
if( iKiller && iKiller != id ) {
new izStats[8], izBody[8];
new iLen;
iFound = 1;
get_user_name( iKiller, t_sName, MAX_NAME_LENGTH );
izStats[STATS_HITS] = 0;
get_user_astats( id, iKiller, izStats, izBody, t_sWpn, MAX_WEAPON_LENGTH );
iLen = format( sBuffer, MAX_BUFFER_LENGTH,
"Killed by %s with %s @ %0.0fm (%dhp, %dap) >>", t_sName, t_sWpn,
distance(g_izUserAttackerDistance[id]),
g_izKilled[id][KILLED_KILLER_HEALTH], g_izKilled[id][KILLED_KILLER_ARMOUR] );
if( izStats[STATS_HITS] ) {
for( new i = 1; i < 8; i++ ) {
if( !izBody[i] ) continue;
iLen += format( sBuffer[iLen], MAX_BUFFER_LENGTH - iLen,
" %s: %d", BODY_PART[i], izBody[i] );
}
}
else
iLen += format( sBuffer[iLen], MAX_BUFFER_LENGTH - iLen,
" no hits" );
}
else
copy( sBuffer, MAX_BUFFER_LENGTH, "You have no killer..." );
return iFound;
}
// Get and format killed stats: hits, damage on killer.
format_kill_vinfo( id, iKiller, sBuffer[MAX_BUFFER_LENGTH+1] ) {
new iFound = 0;
new izStats[8];
new izBody[8];
new iLen;
izStats[STATS_HITS] = 0;
izStats[STATS_DAMAGE] = 0;
get_user_vstats( id, iKiller, izStats, izBody );
if( iKiller && iKiller != id ) {
iFound = 1;
get_user_name( iKiller, t_sName, MAX_NAME_LENGTH );
iLen = format( sBuffer, MAX_BUFFER_LENGTH,
"You hit %s %d time(s), %d damage >>",
t_sName, izStats[STATS_HITS], izStats[STATS_DAMAGE] );
}
else
iLen = format( sBuffer, MAX_BUFFER_LENGTH,
"Last result: %d hit(s), %d damage >>",
izStats[STATS_HITS], izStats[STATS_DAMAGE] );
if( izStats[STATS_HITS] ) {
for( new i = 1; i < 8; i++ ) {
if( !izBody[i] ) continue;
iLen += format( sBuffer[iLen], MAX_BUFFER_LENGTH - iLen,
" %s: %d", BODY_PART[i], izBody[i] );
}
}
else
iLen += format( sBuffer[iLen], MAX_BUFFER_LENGTH - iLen,
" no hits" );
return iFound;
}
// Get and format top 15.
format_top15( sBuffer[MAX_BUFFER_LENGTH+1] ){
new iMax = get_statsnum();
new izStats[8], izBody[8];
new iLen = 0;
if( iMax > 15 )
iMax = 15;
iLen = format( sBuffer, MAX_BUFFER_LENGTH,
"<body bgcolor=#000000><font color=#FFB000><pre>" );
iLen += format( sBuffer[iLen], MAX_BUFFER_LENGTH - iLen,
"%2s %-22.22s %6s %6s %6s %6s %4s %4s %4s^n",
"#", "Nick", "Kills", "Deaths", "Hits", "Shots", "HS", "Eff.", "Acc." );
for( new i = 0; i < iMax && MAX_BUFFER_LENGTH - iLen > 0; i++ ) {
get_stats( i, izStats, izBody, t_sName, MAX_NAME_LENGTH );
iLen += format( sBuffer[iLen], MAX_BUFFER_LENGTH - iLen,
"%2d %-22.22s %6d %6d %6d %6d %4d %3.0f%% %3.0f%%^n", i+1, t_sName,
izStats[STATS_KILLS], izStats[STATS_DEATHS], izStats[STATS_HITS],
izStats[STATS_SHOTS], izStats[STATS_HS],
effec( izStats ), accuracy( izStats ) );
}
}
// Get and format rank stats.
format_rankstats( id, sBuffer[MAX_BUFFER_LENGTH+1], iMyId=0 ) {
new izStats[8] = { 0, ... };
new izBody[8];
new iRankPos, iLen;
iRankPos = get_user_stats( id, izStats, izBody );
iLen = format( sBuffer, MAX_BUFFER_LENGTH,
"<body bgcolor=#000000><font color=#FFB000><pre>" );
iLen += format( sBuffer[iLen], MAX_BUFFER_LENGTH - iLen,
"%s rank is %d of %d^n^n",
(!iMyId||iMyId==id)?"Your":"Players", iRankPos, get_statsnum() );
iLen += format( sBuffer[iLen], MAX_BUFFER_LENGTH - iLen,
"%s rank is %d of %d^n^n",
(!iMyId||iMyId==id)?"Your":"Players", iRankPos, get_statsnum() );
iLen += format( sBuffer[iLen], MAX_BUFFER_LENGTH - iLen,
"%6s: %d (%d with hs)^n%6s: %d^n%6s: %d^n%6s: %d^n%6s: %d^n%6s: %0.2f%%^n%6s: %0.2f%%^n^n",
"Kills", izStats[STATS_KILLS], izStats[STATS_HS],
"Deaths", izStats[STATS_DEATHS], "Hits", izStats[STATS_HITS],
"Shots", izStats[STATS_SHOTS], "Damage", izStats[STATS_DAMAGE],
"Eff.", effec( izStats ), "Acc.", accuracy( izStats ) );
iLen += format( sBuffer[iLen], MAX_BUFFER_LENGTH - iLen,
"%10s:^n%10s: %d^n%10s: %d^n%10s: %d^n%10s: %d^n%10s: %d^n%10s: %d^n%10s: %d",
"HITS", BODY_PART[1], izBody[1], BODY_PART[2], izBody[2],
BODY_PART[3], izBody[3], BODY_PART[4], izBody[4], BODY_PART[5], izBody[5],
BODY_PART[6], izBody[6], BODY_PART[7], izBody[7]);
return;
}
// Get and format stats.
format_stats( id, sBuffer[MAX_BUFFER_LENGTH+1] ) {
new izStats[8] = { 0, ... };
new izBody[8];
new iWeapon, iLen;
get_user_wstats( id, 0, izStats, izBody );
iLen = format( sBuffer, MAX_BUFFER_LENGTH,
"<body bgcolor=#000000><font color=#FFB000><pre>" );
iLen += format( sBuffer[iLen], MAX_BUFFER_LENGTH - iLen,
"%6s: %d (%d with hs)^n%6s: %d^n%6s: %d^n%6s: %d^n%6s: %d^n%6s: %0.2f%%^n%6s: %0.2f%%^n^n",
"Kills", izStats[STATS_KILLS], izStats[STATS_HS],
"Deaths", izStats[STATS_DEATHS], "Hits", izStats[STATS_HITS],
"Shots", izStats[STATS_SHOTS], "Damage", izStats[STATS_DAMAGE],
"Eff.", effec( izStats ), "Acc.", accuracy( izStats ) );
iLen += format( sBuffer[iLen], MAX_BUFFER_LENGTH - iLen,
"%-12.12s %6s %6s %6s %6s %6s %4s^n",
"Weapon", "Kills", "Deaths", "Hits", "Shots", "Damage", "Acc." );
for( iWeapon = 1; iWeapon < 31 && MAX_BUFFER_LENGTH - iLen > 0 ; iWeapon++ ) {
if( get_user_wstats( id, iWeapon, izStats, izBody ) ) {
get_weaponname( iWeapon, t_sWpn, MAX_WEAPON_LENGTH );
iLen += format( sBuffer[iLen], MAX_BUFFER_LENGTH - iLen,
"%-12.12s %6d %6d %6d %6d %6d %3.0f%%^n",
t_sWpn[7], izStats[STATS_KILLS], izStats[STATS_DEATHS],
izStats[STATS_HITS], izStats[STATS_SHOTS],
izStats[STATS_DAMAGE], accuracy( izStats ) );
}
}
return;
}
// Show round end stats. If gametime is zero then use default duration time.
show_roundend_hudstats( id, Float:fGameTime ) {
// Bail out if there no HUD stats should be shown
// for this player or end round stats not created.
if( !g_izStatsSwitch[id] ) return;
if( !g_sAwardAndScore[0] ) return;
// If round end timer is zero clear round end stats.
if( g_fShowStatsTime == 0.0 ) {
set_hudtype_endround( 0.05 );
show_hudmessage( id, "" );
#if defined STATSX_DEBUG
log_message( "[%s] Clear round end HUD stats for #%i", PLUGIN_NAME, id );
#endif
return;
}
// Set HUD-duration to default or remaining time.
new Float:fDuration;
if( fGameTime == 0.0 )
fDuration = g_fHUDDuration;
else {
fDuration = g_fShowStatsTime + g_fHUDDuration - fGameTime;
if( fDuration > g_fFreezeTime + g_fFreezeLimitTime )
fDuration = g_fFreezeTime + g_fFreezeLimitTime;
}
// Show stats only if more time left than coded minimum.
if( fDuration >= HUD_MIN_DURATION ) {
set_hudtype_endround( fDuration );
show_hudmessage( id, g_sAwardAndScore );
#if defined STATSX_DEBUG
log_message( "[%s] Show %1.2fs round end HUD stats for #%i", PLUGIN_NAME, fDuration, id );
#endif
}
return;
}
// Show round end stats.
show_user_hudstats( id, Float:fGameTime ) {
// Bail out if there no HUD stats should be shown
// for this player or user stats timer is zero.
if( !g_izStatsSwitch[id] ) return;
if( g_fzShowUserStatsTime[id] == 0.0 ) return;
// Set HUD-duration to default or remaining time.
new Float:fDuration;
if( fGameTime == 0.0 )
fDuration = g_fHUDDuration;
else {
fDuration = g_fzShowUserStatsTime[id] + g_fHUDDuration - fGameTime;
if( fDuration > g_fFreezeTime + g_fFreezeLimitTime )
fDuration = g_fFreezeTime + g_fFreezeLimitTime;
}
// Show stats only if more time left than coded minimum.
if( fDuration >= HUD_MIN_DURATION ) {
if( ShowKiller ) {
new iKiller;
iKiller = g_izKilled[id][KILLED_KILLER_ID];
get_kill_info( id, iKiller, g_sBuffer );
add_attacker_hits( id, iKiller, g_sBuffer );
set_hudtype_killer( fDuration );
show_hudmessage( id, g_sBuffer );
#if defined STATSX_DEBUG
log_message( "[%s] Show %1.2fs %suser HUD k-stats for #%i", PLUGIN_NAME, fDuration, g_sBuffer[0]?"":"no ", id );
#endif
}
if( ShowVictims ) {
get_victims( id, g_sBuffer );
set_hudtype_victim( fDuration );
show_hudmessage( id, g_sBuffer );
#if defined STATSX_DEBUG
log_message( "[%s] Show %1.2fs %suser HUD v-stats for #%i", PLUGIN_NAME, fDuration, g_sBuffer[0]?"":"no ", id );
#endif
}
if( ShowAttackers ) {
get_attackers( id, g_sBuffer );
set_hudtype_attacker( fDuration );
show_hudmessage( id, g_sBuffer );
#if defined STATSX_DEBUG
log_message( "[%s] Show %1.2fs %suser HUD a-stats for #%i", PLUGIN_NAME, fDuration, g_sBuffer[0]?"":"no ", id );
#endif
}
}
return;
}
//------------------------------------------------------------
// Plugin commands
//------------------------------------------------------------
// Set or get plugin config flags.
public cmdPluginMode( id, level, cid ) {
if( !cmd_access( id, level, cid, 1 ) )
return PLUGIN_HANDLED;
if( read_argc() > 1 )
read_argv( 1, g_sBuffer, MAX_BUFFER_LENGTH );
else
g_sBuffer[0] = 0;
set_plugin_mode( id, g_sBuffer );
return PLUGIN_HANDLED;
}
// Display MOTD stats.
public cmdStatsMe( id ) {
if( !SayStatsMe ) {
client_print( id, print_chat, g_sDisabledMsg );
return PLUGIN_HANDLED;
}
format_stats( id, g_sBuffer );
get_user_name( id, t_sName, MAX_NAME_LENGTH );
show_motd( id, g_sBuffer, t_sName );
return PLUGIN_CONTINUE;
}
// Display MOTD rank.
public cmdRankStats( id ) {
if( !SayRankStats ) {
client_print( id, print_chat, g_sDisabledMsg );
return PLUGIN_HANDLED;
}
format_rankstats( id, g_sBuffer );
get_user_name( id, t_sName, MAX_NAME_LENGTH );
show_motd( id, g_sBuffer, t_sName );
return PLUGIN_CONTINUE;
}
// Display MOTD top15 ranked.
public cmdTop15( id ) {
if( !SayTop15 ) {
client_print( id, print_chat, g_sDisabledMsg );
return PLUGIN_HANDLED;
}
format_top15( g_sBuffer );
show_motd( id, g_sBuffer, "Top 15" );
return PLUGIN_CONTINUE;
}
// Display killer information.
public cmdHp( id ) {
if( !SayHP ) {
client_print( id, print_chat, g_sDisabledMsg );
return PLUGIN_HANDLED;
}
new iKiller = g_izKilled[id][KILLED_KILLER_ID];
format_kill_ainfo( id, iKiller, g_sBuffer );
client_print( id, print_chat, "* %s", g_sBuffer );
return PLUGIN_CONTINUE;
}
// Display user stats.
public cmdMe( id ) {
if( !SayMe ) {
client_print( id, print_chat, g_sDisabledMsg );
return PLUGIN_HANDLED;
}
format_kill_vinfo( id, 0, g_sBuffer );
client_print( id, print_chat, "* %s", g_sBuffer );
return PLUGIN_CONTINUE;
}
// Display user rank
public cmdRank( id ) {
if( !SayRank ) {
client_print( id, print_chat, g_sDisabledMsg );
return PLUGIN_HANDLED;
}
new izStats[8], izBody[8];
new iRankPos, iRankMax;
new Float:fEff, Float:fAcc;
iRankPos = get_user_stats( id, izStats, izBody );
iRankMax = get_statsnum();
fEff = effec( izStats );
fAcc = accuracy( izStats );
client_print( id, print_chat,
"* Your rank is %d of %d with %d kill(s), %d hit(s), %0.2f%%%% eff. and %0.2f%%%% acc.",
iRankPos, iRankMax, izStats[STATS_KILLS], izStats[STATS_HITS],
fEff, fAcc );
return PLUGIN_CONTINUE;
}
// Report user weapon status to team.
public cmdReport( id ) {
if( !SayReport ) {
client_print( id, print_chat, g_sDisabledMsg );
return PLUGIN_HANDLED;
}
new iWeapon, iClip, iAmmo, iHealth, iArmor;
iWeapon = get_user_weapon( id, iClip, iAmmo );
get_weaponname( iWeapon, t_sWpn, MAX_WEAPON_LENGTH );
iHealth = get_user_health( id );
iArmor = get_user_armor( id );
if( iClip >= 0 )
format( g_sBuffer, MAX_BUFFER_LENGTH,
"weapon: %s, ammo: %d/%d, health: %d, armor: %d",
t_sWpn[7], iClip, iAmmo, iHealth, iArmor );
else
format( g_sBuffer, MAX_BUFFER_LENGTH,
"weapon: %s, health: %d, armor: %d",
t_sWpn[7], iHealth, iArmor );
engclient_cmd( id, "say_team", g_sBuffer );
return PLUGIN_CONTINUE;
}
// Display team map score
public cmdScore( id ) {
if( !SayScore ) {
client_print( id, print_chat, g_sDisabledMsg );
return PLUGIN_HANDLED;
}
client_print( id, print_chat, "Game score: %s", g_sScore );
return PLUGIN_CONTINUE;
}
// Display time left on map
public cmdTimeLeft( id ) {
if( !SayMiscFunc )
return PLUGIN_CONTINUE;
new iTimeLeft = get_timeleft();
if( iTimeLeft )
client_print( 0, print_chat,
"Time remaining: %02d:%02d", iTimeLeft / 60, iTimeLeft % 60 );
else
client_print( 0, print_chat, "* No Time Limit *" );
return PLUGIN_CONTINUE;
}
// Display the time.
public cmdTheTime( id ) {
if( !SayMiscFunc )
return PLUGIN_CONTINUE;
get_time( "%H:%M:%S", g_sBuffer, MAX_BUFFER_LENGTH );
client_print( 0, print_chat, "The time: %s", g_sBuffer );
return PLUGIN_CONTINUE;
}
// Display current map name.
public cmdCurrentMap( id ) {
if( !SayMiscFunc )
return PLUGIN_CONTINUE;
get_mapname( g_sBuffer, MAX_BUFFER_LENGTH );
client_print( 0, print_chat, "Played map: %s", g_sBuffer );
return PLUGIN_CONTINUE;
}
// Display friendly fire status.
public cmdFf( id ) {
if( !SayMiscFunc )
return PLUGIN_CONTINUE;
client_print( 0, print_chat, "Friendly fire: %s",
get_cvar_num( "mp_friendlyfire" ) ? "ON" : "OFF" );
return PLUGIN_CONTINUE;
}
// Client switch to enable or disable stats announcements.
public cmdSwitch( id ) {
g_izStatsSwitch[id] = ( g_izStatsSwitch[id] ) ? 0 : -1 ;
num_to_str( g_izStatsSwitch[id], t_sText, MAX_TEXT_LENGTH );
client_cmd( id, "setinfo _amxstatsx %s", t_sText );
client_print( id, print_chat, "* You have %s stats announcements",
g_izStatsSwitch[id] ? "enabled" : "disabled" );
return PLUGIN_CONTINUE;
}
// Player stats menu.
public cmdStats( id ) {
if ( !SayStatsAll ){
client_print( id, print_chat, g_sDisabledMsg );
return PLUGIN_HANDLED;
}
showStatsMenu( id, g_izUserMenuPosition[id]=0 );
return PLUGIN_CONTINUE;
}
//--------------------------------
// Menu
//--------------------------------
public actionStatsMenu( id, key ) {
switch( key ) {
// Key '1' to '7', execute action on this option
case 0..6: {
new iOption, iIndex;
iOption = ( g_izUserMenuPosition[id] * PPL_MENU_OPTIONS ) + key;
if( iOption >= 0 && iOption < 32 ) {
iIndex = g_izUserMenuPlayers[id][iOption];
if( is_user_connected( iIndex ) ){
switch( g_izUserMenuAction[id] ) {
case 0: format_stats( iIndex, g_sBuffer );
case 1: format_rankstats( iIndex, g_sBuffer, id );
default: g_sBuffer[0] = 0;
}
if( g_sBuffer[0] ) {
get_user_name( iIndex, t_sName, MAX_NAME_LENGTH );
show_motd( id, g_sBuffer, t_sName );
}
}
}
showStatsMenu( id, g_izUserMenuPosition[id] );
}
// Key '8', change action
case 7: {
g_izUserMenuAction[id]++;
if( g_izUserMenuAction[id] >= MAX_PPL_MENU_ACTIONS )
g_izUserMenuAction[id] = 0;
showStatsMenu( id, g_izUserMenuPosition[id] );
}
// Key '9', select next page of options
case 8:
showStatsMenu( id, ++g_izUserMenuPosition[id] );
// Key '10', cancel or go back to previous menu
case 9: {
if( g_izUserMenuPosition[id] > 0 )
showStatsMenu( id, --g_izUserMenuPosition[id] );
}
}
return PLUGIN_HANDLED;
}
new g_izUserMenuActionText[MAX_PPL_MENU_ACTIONS][] = { "Show stats", "Show rank stats" };
showStatsMenu( id, iMenuPos ) {
new iLen, iKeyMask, iPlayers;
new iUserIndex, iMenuPosMax, iMenuOption, iMenuOptionMax;
get_players( g_izUserMenuPlayers[id], iPlayers );
iMenuPosMax = (( iPlayers - 1 ) / PPL_MENU_OPTIONS ) + 1;
// If menu pos does not excist use last menu (if players has left)
if ( iMenuPos >= iMenuPosMax )
iMenuPos = iMenuPosMax - 1;
iUserIndex = iMenuPos * PPL_MENU_OPTIONS;
iLen = format( g_sBuffer, MAX_BUFFER_LENGTH, "\yServer Stats\R%d/%d^n\w^n",
iMenuPos + 1, iMenuPosMax );
iMenuOptionMax = iPlayers - iUserIndex;
if( iMenuOptionMax > PPL_MENU_OPTIONS )
iMenuOptionMax = PPL_MENU_OPTIONS;
for( iMenuOption = 0; iMenuOption < iMenuOptionMax; iMenuOption++ ) {
get_user_name( g_izUserMenuPlayers[id][iUserIndex++], t_sName, MAX_NAME_LENGTH );
iKeyMask |= (1<<iMenuOption);
iLen += format( g_sBuffer[iLen], MAX_BUFFER_LENGTH - iLen,
"%d. %s^n\w", iMenuOption + 1, t_sName );
}
iKeyMask |= MENU_KEY_8|MENU_KEY_0;
iLen += format( g_sBuffer[iLen], MAX_BUFFER_LENGTH - iLen, "^n8. %s^n\w",
g_izUserMenuActionText[ g_izUserMenuAction[id] ] );
if( iPlayers > iUserIndex ) {
iLen += copy( g_sBuffer[iLen], MAX_BUFFER_LENGTH - iLen, "^n9. More..." );
iKeyMask |= MENU_KEY_9;
}
if( iMenuPos > 0 )
iLen += copy( g_sBuffer[iLen], MAX_BUFFER_LENGTH - iLen, "^n0. Back" );
else
iLen += copy( g_sBuffer[iLen], MAX_BUFFER_LENGTH - iLen, "^n0. Exit" );
show_menu( id, iKeyMask, g_sBuffer );
return PLUGIN_HANDLED;
}
//------------------------------------------------------------
// Plugin events
//------------------------------------------------------------
// Reset game stats on game start and restart.
public eventStartGame() {
read_data( 2, t_sText, MAX_TEXT_LENGTH );
if( t_sText[6] == 'w' ) {
read_data( 3, t_sText, MAX_TEXT_LENGTH );
g_fStartGame = get_gametime() + float( str_to_num( t_sText ) );
}
else
g_fStartGame = get_gametime();
return PLUGIN_CONTINUE;
}
// Round start
public eventStartRound() {
new iTeam, id, i;
new iRoundTime;
iRoundTime = read_data( 1 );
if ( iRoundTime >= get_cvar_float("mp_roundtime") * 60 ) {
#if defined STATSX_DEBUG
log_message( "[%s] Reset round stats", PLUGIN_NAME );
#endif
// Reset game stats on game start and restart.
if( g_fStartGame > 0.0 && g_fStartGame <= get_gametime() ) {
#if defined STATSX_DEBUG
log_message( "[%s] Reset game stats", PLUGIN_NAME );
#endif
g_fStartGame = 0.0;
// Clear team and game stats.
for( iTeam = 0; iTeam < MAX_TEAMS; iTeam++ ) {
g_izTeamEventScore[iTeam] = 0;
for( i = 0; i < 8; i++ )
g_izTeamGameStats[iTeam][i] = 0;
}
// Clear game stats, incl '0' that is sum of all users.
for( id = 0; id < MAX_PLAYERS; id++ ) {
for( i = 0; i < 8; i++ )
g_izUserGameStats[id][i] = 0;
}
}
// Update team score with "TeamScore" event values and
// clear team round stats.
for( iTeam = 0; iTeam < MAX_TEAMS; iTeam++ ) {
g_izTeamScore[iTeam] = g_izTeamEventScore[iTeam];
for( i = 0; i < 8; i++ )
g_izTeamRndStats[iTeam][i] = 0;
}
// Clear user round stats, incl '0' that is sum of all users.
for( id = 0; id < MAX_PLAYERS; id++ ) {
g_izUserRndName[id][0] = 0;
for( i = 0; i < 8; i++ )
g_izUserRndStats[id][i] = 0;
g_fzShowUserStatsTime[id] = 0.0;
}
// Allow end round stats and reset end round triggered indicator.
g_iRoundEndTriggered = 0;
g_iRoundEndProcessed = 0;
g_fShowStatsTime = 0.0;
// Update local configuration vars with value in cvars.
get_config_cvars();
}
return PLUGIN_CONTINUE;
}
// Reset killer info on round restart.
public eventResetHud( id ) {
new args[1];
args[0] = id;
if( g_iPluginMode & MODE_HUD_DELAY )
set_task( 0.01, "delay_resethud", 200 + id, args, 1 );
else
delay_resethud( args );
return PLUGIN_CONTINUE;
}
public delay_resethud( args[] ) {
new id = args[0];
new Float:fGameTime
// Show user and score round stats after HUD-reset
#if defined STATSX_DEBUG
log_message( "[%s] Reset HUD for #%i", PLUGIN_NAME, id );
#endif
fGameTime = get_gametime();
show_user_hudstats( id, fGameTime );
show_roundend_hudstats( id, fGameTime );
// Reset round stats
g_izKilled[id][KILLED_KILLER_ID] = 0;
g_izKilled[id][KILLED_KILLER_STATSFIX] = 0;
g_izShowStatsFlags[id] = -1; // Initialize flags
g_fzShowUserStatsTime[id] = 0.0;
g_izUserAttackerDistance[id] = 0;
for( new i = 0; i < MAX_PLAYERS; i++ )
g_izUserVictimDistance[id][i] = 0;
return PLUGIN_CONTINUE;
}
#if defined STATSX_OLD_DEATHMSG
// Save killer info on death.
public eventDeathMsg() {
new iKiller = read_data( 1 );
new iVictim = read_data( 2 );
// Bail out if no killer.
if( !iKiller ) return PLUGIN_CONTINUE;
if( iKiller != iVictim ) {
new iaVOrigin[3], iaKOrigin[3];
new iDistance;
get_user_origin( iVictim, iaVOrigin );
get_user_origin( iKiller, iaKOrigin );
g_izKilled[iVictim][KILLED_KILLER_ID] = iKiller;
g_izKilled[iVictim][KILLED_KILLER_HEALTH] = get_user_health( iKiller );
g_izKilled[iVictim][KILLED_KILLER_ARMOUR] = get_user_armor( iKiller );
g_izKilled[iVictim][KILLED_KILLER_STATSFIX] = 0;
iDistance = get_distance( iaVOrigin, iaKOrigin );
g_izUserAttackerDistance[iVictim] = iDistance;
g_izUserVictimDistance[iKiller][iVictim] = iDistance;
}
g_izKilled[iVictim][KILLED_TEAM] = get_user_team( iVictim );
new args[1];
args[0] = iVictim;
set_task( 0.25, "delay_damage", 100 + iVictim, args, 1 );
return PLUGIN_CONTINUE;
}
public delay_damage( args[] ) {
new id = args[0];
// Display stats to killed player if player
// has not already been processed.
if( !g_izKilled[id][KILLED_KILLER_STATSFIX] ) {
g_izKilled[id][KILLED_KILLER_STATSFIX] = 1;
// Display round end stats to all players if
// round end has already been triggered.
if( g_iRoundEndTriggered )
endround_stats();
// Display kill stats for the player if round
// end stats was not processed.
if( !g_iRoundEndProcessed )
kill_stats( id );
}
}
// Trigger death stats processing.
public eventDamage( id ) {
// Bail out if player not killed or if player
// player has already been processed.
if( !g_izKilled[id][KILLED_KILLER_ID] || g_izKilled[id][KILLED_KILLER_STATSFIX] )
return PLUGIN_CONTINUE;
// Remove task if not alreay done and process.
remove_task( 100 + id );
// Process player deaths.
new izData[1];
izData[0] = id;
delay_damage( izData );
return PLUGIN_CONTINUE;
}
#endif // if defined STATSX_OLD_DEATHMSG
#if !defined STATSX_OLD_DEATHMSG
// Save killer info on death.
public eventCSDeathMsg() {
new iKiller = read_data( 1 );
new iVictim = read_data( 2 );
// Bail out if no killer.
if( !iKiller ) return PLUGIN_CONTINUE;
if( iKiller != iVictim ) {
new iaVOrigin[3], iaKOrigin[3];
new iDistance;
get_user_origin( iVictim, iaVOrigin );
get_user_origin( iKiller, iaKOrigin );
g_izKilled[iVictim][KILLED_KILLER_ID] = iKiller;
g_izKilled[iVictim][KILLED_KILLER_HEALTH] = get_user_health( iKiller );
g_izKilled[iVictim][KILLED_KILLER_ARMOUR] = get_user_armor( iKiller );
g_izKilled[iVictim][KILLED_KILLER_STATSFIX] = 0;
iDistance = get_distance( iaVOrigin, iaKOrigin );
g_izUserAttackerDistance[iVictim] = iDistance;
g_izUserVictimDistance[iKiller][iVictim] = iDistance;
}
g_izKilled[iVictim][KILLED_TEAM] = get_user_team( iVictim );
g_izKilled[iVictim][KILLED_KILLER_STATSFIX] = 1;
// Display kill stats for the player if round
// end stats was not processed.
if( !g_iRoundEndProcessed )
kill_stats( iVictim );
return PLUGIN_CONTINUE;
}
#endif
// Display hudmessage stats on death.
// This will also update all round and game stats.
// Must be called at least once per round.
kill_stats( id ) {
// Bail out if user stats timer is non-zero,
// ie function already called.
if( g_fzShowUserStatsTime[id] > 0.0 ) return;
// Flag kill stats displayed for this player.
g_fzShowUserStatsTime[id] = get_gametime();
// Add user death stats to user round stats
new izStats[8], izBody[8];
new iTeam, i;
new iKiller;
iKiller = g_izKilled[id][KILLED_KILLER_ID];
// Get user's team (if dead use the saved team)
if( iKiller )
iTeam = g_izKilled[id][KILLED_TEAM] - 1;
else
iTeam = get_user_team( id ) - 1;
get_user_name( id, g_izUserRndName[id], MAX_NAME_LENGTH );
if( get_user_rstats( id, izStats, izBody ) ) {
// Update user's team round stats
if( iTeam >= 0 && iTeam < MAX_TEAMS ) {
for( i = 0; i < 8; i++ ) {
g_izTeamRndStats[iTeam][i] += izStats[i];
g_izTeamGameStats[iTeam][i] += izStats[i];
g_izUserRndStats[0][i] += izStats[i];
g_izUserGameStats[0][i] += izStats[i];
}
}
// Update user's round stats
if( g_izUserUserID[id] == get_user_userid( id ) ) {
for( i = 0; i < 8; i++ ) {
g_izUserRndStats[id][i] += izStats[i];
g_izUserGameStats[id][i] += izStats[i];
}
}
else {
g_izUserUserID[id] = get_user_userid( id );
for( i = 0; i < 8; i++ ) {
g_izUserRndStats[id][i] = izStats[i];
g_izUserGameStats[id][i] = izStats[i];
}
}
} // endif( get_user_rstats() )
// Report stats in the chat section, if player is killed.
if( KillerChat && iKiller && iKiller != id ) {
if( format_kill_ainfo( id, iKiller, g_sBuffer ) ) {
client_print( id, print_chat, "* %s", g_sBuffer );
format_kill_vinfo( id, iKiller, g_sBuffer );
}
client_print( id, print_chat, "* %s", g_sBuffer );
}
// Display player stats info.
#if defined STATSX_DEBUG
log_message( "[%s] Kill stats for #%i", PLUGIN_NAME, id );
#endif
show_user_hudstats( id, 0.0 )
return;
}
public eventEndRound() {
// Update local configuration vars with value in cvars.
get_config_cvars();
// If first end round event in the round, calculate team score.
if( !g_iRoundEndTriggered ) {
read_data( 2, t_sText, MAX_TEXT_LENGTH );
if( t_sText[7] == 't' ) // Terrorist wins
g_izTeamScore[0]++;
else if( t_sText[7] == 'c' ) // CT wins
g_izTeamScore[1]++;
}
// Flag round end triggered.
g_iRoundEndTriggered = 1;
// Display round end stats to all players.
endround_stats();
return PLUGIN_CONTINUE;
}
endround_stats() {
// Bail out if end round stats has already been processed
// or round end not triggered.
if( g_iRoundEndProcessed || !g_iRoundEndTriggered ) return;
new iaPlayers[32], iPlayer, iPlayers, id;
get_players( iaPlayers, iPlayers );
#if defined STATSX_OLD_DEATHMSG
// Bail out if not all killers got their damage event (ie last stats)
for( iPlayer = 0; iPlayer < iPlayers; iPlayer++ ) {
id = iaPlayers[iPlayer];
if( g_izKilled[id][KILLED_KILLER_ID] && !g_izKilled[id][KILLED_KILLER_STATSFIX] )
return;
}
#endif
// Display attacker & victim list for all living players.
// This will also update all round and game stats for all players
// not killed.
#if defined STATSX_DEBUG
log_message( "[%s] End round stats", PLUGIN_NAME );
#endif
for( iPlayer = 0; iPlayer < iPlayers; iPlayer++ ) {
id = iaPlayers[iPlayer];
if( g_fzShowUserStatsTime[id] == 0.0 )
kill_stats( id );
}
g_sAwardAndScore[0] = 0;
// Create round awards.
if( ShowMostDisruptive )
add_most_disruptive( g_sAwardAndScore );
if( ShowBestScore )
add_best_score( g_sAwardAndScore );
// Create round score.
// Compensate HUD message if awards are disabled.
if( ShowTeamScore || ShowTotalStats ) {
if( ShowMostDisruptive && ShowBestScore )
add( g_sAwardAndScore, MAX_BUFFER_LENGTH, "^n^n" );
else if( ShowMostDisruptive || ShowBestScore )
add( g_sAwardAndScore, MAX_BUFFER_LENGTH, "^n^n^n^n" );
else
add( g_sAwardAndScore, MAX_BUFFER_LENGTH, "^n^n^n^n^n^n" );
if( ShowTeamScore )
add_team_score( g_sAwardAndScore );
if( ShowTotalStats )
add_total_stats( g_sAwardAndScore );
}
save_team_chatscore();
// Get and save round end stats time.
g_fShowStatsTime = get_gametime();
// Display round end stats to all players.
for( iPlayer = 0; iPlayer < iPlayers; iPlayer++ ) {
id = iaPlayers[iPlayer];
show_roundend_hudstats( id, 0.0 );
}
// Flag round end processed.
g_iRoundEndProcessed = 1;
return;
}
public eventTeamScore() {
new sTeamID[1+1], iTeamScore;
read_data( 1, sTeamID, 1 );
iTeamScore = read_data(2);
g_izTeamEventScore[ (sTeamID[0]=='C') ? 1 : 0 ] = iTeamScore;
return PLUGIN_CONTINUE;
}
public eventIntermission() {
if( EndPlayer || EndTop15 )
set_task( 1.0, "end_game_stats", 900 );
}
public end_game_stats() {
new iaPlayers[32], iPlayer, iPlayers, id;
if( EndPlayer ) {
get_players( iaPlayers, iPlayers );
for( iPlayer = 0; iPlayer < iPlayers; iPlayer++ ) {
id = iaPlayers[iPlayer];
if( !g_izStatsSwitch[id] ) continue; // Do not show any stats
cmdStatsMe( iaPlayers[iPlayer] );
}
}
else if( EndTop15 ) {
get_players( iaPlayers, iPlayers );
format_top15( g_sBuffer );
for( iPlayer = 0; iPlayer < iPlayers; iPlayer++ ) {
id = iaPlayers[iPlayer];
if( !g_izStatsSwitch[id] ) continue; // Do not show any stats
show_motd( iaPlayers[iPlayer], g_sBuffer, "Top 15" );
}
}
return PLUGIN_CONTINUE;
}
public eventSpecMode( id ) {
new sData[12];
read_data( 2, sData, 11 );
g_izSpecMode[id] = ( sData[10] == '2' );
return PLUGIN_CONTINUE;
}
public eventShowRank( id ) {
if( SpecRankInfo && g_izSpecMode[id] ) {
new iPlayer = read_data(2);
if( is_user_connected( iPlayer ) ) {
new izStats[8], izBody[8];
new iRankPos, iRankMax;
get_user_name( iPlayer, t_sName, MAX_NAME_LENGTH );
iRankPos = get_user_stats( iPlayer, izStats, izBody );
iRankMax = get_statsnum();
set_hudtype_specmode();
show_hudmessage( id, "%s's rank is %d of %d", t_sName, iRankPos, iRankMax );
}
}
return PLUGIN_CONTINUE;
}
public client_connect( id ) {
if( ShowStats ) {
get_user_info( id, "_amxstatsx", t_sText, MAX_TEXT_LENGTH );
g_izStatsSwitch[id] = ( t_sText[0] ) ? str_to_num( t_sText ) : -1 ;
}
else
g_izStatsSwitch[id] = 0;
g_izKilled[id][KILLED_KILLER_ID] = 0;
g_izKilled[id][KILLED_KILLER_STATSFIX] = 0;
g_izShowStatsFlags[id] = 0; // Clear all flags
g_fzShowUserStatsTime[id] = 0.0;
return PLUGIN_CONTINUE;
}