[Request] Получение WeaponID для CS:GO оружия.

Сообщения
2.243
Реакции
2.259
Так как не имеется никаких функций для получения статистики по оружию игрока в SourceMod (CS:GO) (допустим get_user_wstats(), get_user_wstats() и подобного) решил сделать API. Попытка пока-что не увенчалась успехом, событие player_hurt не умеет адекватно показывать с какого оружия нанесён урон (вместо USP пишет P2000 и так с многими оружиями, что доступны для замены в инвентаре игрока). Таковая проблема имеется на vanilla сервере (игрой в логи пишется не корректное оружие).

Призываю в тред как минимум:

ибо проблема достойна публичного обсуждения. :dntknw:

Костылекод прикладываю.
Код:
#include <sourcemod>
#include <sdktools>
#include <cstrike>

#define MAX_PLAYERS 32

static const char VERSION[] = "1.0.0";
public Plugin myinfo =
{
    name        = "CSSTATS API",
    author        = "wopox1337",
    version        = VERSION
};

public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
{
    CreateNative("get_client_wstats", __get_client_wstats);
    return APLRes_Success;
}

ConVar csstats_pause;
ConVar csstats_rankbots;

public void OnPluginStart()
{
    csstats_pause = CreateConVar("csstats_pause", "0",
        .hasMin = true, .min = 0.0, .hasMax = true, .max = 1.0
    );

    csstats_rankbots = CreateConVar("csstats_rankbots", "1",
        .hasMin = true, .min = 0.0, .hasMax = true, .max = 1.0
    );

    HookEvent("weapon_fire",                       Event_CSGOWeaponFire);
    HookEvent("player_hurt",                     Event_CSGOPlayerHurt);
    // HookEvent("weapon_fire_on_empty",            Event_CSGOPlayerFire);

    Init_Tests();
}

// *****************************************************
// class Stats
// *****************************************************

enum BodyHits_s {
    hit_Chest,
    hit_Head,
    hit_Stomach,
    hit_LeftArm,
    hit_RightArm,
    hit_LeftLeg,
    hit_RightLeg
}

enum struct Stats_s {
    int hits;
    int shots;
    int damage;
    int hs;
    int tks;
    int kills;
    int deaths;
    int bodyHits[BodyHits_s];
    int bPlants;
    int bExplosions;
    int bDefusions;
    int bDefused;

    void Clear() {
        this.hits = this.shots = this.damage = this.hs = this.tks = this.kills = this.deaths = this.bDefusions = this.bDefused = this.bPlants = this.bExplosions = 0;
    }
}

enum struct PlayerWeapon {
    char name[32];
    int    ammo;
    int    clip;
    Stats_s stats;
}

const MAX_CWEAPONS = 6;
const PlayerWeapon_SIZE = view_as<int>(CSWeapon_MAX_WEAPONS) + MAX_CWEAPONS;

PlayerWeapon g_aWeapons[MAX_PLAYERS + 1][PlayerWeapon_SIZE];
PlayerWeapon g_aAttackers[MAX_PLAYERS + 1][MAX_PLAYERS + 1];
PlayerWeapon g_aVictims[MAX_PLAYERS + 1][MAX_PLAYERS + 1];
Stats_s g_aWeaponsR[MAX_PLAYERS + 1][PlayerWeapon_SIZE];
Stats_s g_aLifeStats[MAX_PLAYERS + 1];

#define _ClientWeaponStats(%1,%2)        g_aWeapons[%1][%2].stats
#define _ClientAttackersStats(%1,%2)    g_aAttackers[%1][%2].stats
#define _ClientVictimsStats(%1,%2)        g_aVictims[%1][%2].stats
#define _ClientWeaponStatsR(%1,%2)        g_aWeaponsR[%1][%2]
#define _ClientLifeStats(%1)            g_aLifeStats[%1]

// Player_s g_aPlayers[MAX_PLAYERS + 1];

public Action Event_CSGOWeaponFire(Event event, char[] name, bool dontBroadcast)
{
    int client = GetClientOfUserId(event.GetInt("userid"));

    static char szWeapon[64];
    event.GetString("weapon", szWeapon, sizeof(szWeapon));

    static CSWeaponID iWeaponID = _NormalizeWeaponID(szWeapon);

    ___LogWeapon("Event_CSGOWeaponFire", client, szWeapon, iWeaponID);

    saveShot(client, iWeaponID);
}

stock void ___LogWeapon(char[] func, int client, char[] szWeapon, CSWeaponID iWeaponID)
{
    PrintToServer(" ::: szWeapon:'%s'", szWeapon);
    CSWeaponID iCurWeapon;
    char szCurWeapon[64];
    switch(iWeaponID)
    {
        case CSWeapon_DEAGLE,
            // Grenades
            CSWeapon_FLASHBANG, CSWeapon_SMOKEGRENADE, CSWeapon_HEGRENADE,
            CSWeapon_INCGRENADE, CSWeapon_TAGGRENADE, CSWeapon_MOLOTOV,
            CSWeapon_DECOY,
            //
            CSWeapon_KNIFE:
            {
                iCurWeapon = iWeaponID;
            }
        default:
        {
            iCurWeapon = __GetCurrentWeaponID(client);
        }
    }

    if(!CS_IsValidWeaponID(iCurWeapon))
    {
        PrintToServer(" Invalid weaponID:%i", iCurWeapon);
        return;
    }

    CS_WeaponIDToAlias(iCurWeapon, szCurWeapon, sizeof(szCurWeapon));
    PrintToServer(" \t > %s() -> client:%i, szWeapon:'%s'(id:%i), __GetCurrentWeaponID:%i ('s')",
        func, client, szWeapon, iWeaponID, iCurWeapon
    );
}
// sv_cheats 1; bot_stop 1

CSWeaponID _NormalizeWeaponID(char[] szWeapon)
{
    CS_GetTranslatedWeaponAlias(szWeapon, szWeapon, 64);
    // PrintToServer("szWeapon='%s' (%i)", szWeapon, CS_AliasToWeaponID(szWeapon));
    return CS_AliasToWeaponID(szWeapon);
}

void saveShot(int client, CSWeaponID iWeaponID)
{
    if ( !isModuleActive() )
        return;

    if ( ignoreBots(client) )
        return;

    _ClientWeaponStats(client, iWeaponID).shots++;
    // _ClientWeaponStatsR(client, iWeaponID).shots++;

    // PrintToServer("client:%i, iWeaponID:%i, (WShots:%i)", client, iWeaponID, g_aPlayers[client].weapons[iWeaponID].shots);
}

public Action Event_CSGOPlayerHurt(Event event, char[] 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

    int client = GetClientOfUserId(event.GetInt("userid"));
    int attacker = GetClientOfUserId(event.GetInt("attacker"));

    static char szWeapon[64];
    event.GetString("weapon", szWeapon, sizeof(szWeapon));

    static CSWeaponID iWeaponID;
    if(strcmp(szWeapon, "inferno") == 0)
    {
        iWeaponID = CSWeapon_MOLOTOV;
        PrintToServer("AAAAAAAAAAAAAA!");
    }
       
    else iWeaponID = _NormalizeWeaponID(szWeapon);
   
    ___LogWeapon("Event_CSGOPlayerHurt", attacker, szWeapon, iWeaponID);

    int iDamage = GetClientOfUserId(event.GetInt("dmg_health"));
    int iHitGroup = GetClientOfUserId(event.GetInt("hitgroup"));

    saveHit(attacker, client, iWeaponID, iDamage, iHitGroup);
}
void saveHit(int client, int victim, CSWeaponID iWeaponID, int iDamage, int body)
{
    // if ( !isModuleActive() )
    //     return;

    // if ( ignoreBots(client, victim) )
    //     return;

    // if ( client == victim )
    //     return;

    _ClientWeaponStats(client, iWeaponID).hits++;

    PrintToServer("SaveHIT(client:%i, iWeap:%i), hits:%i", client, iWeaponID, _ClientWeaponStats(client, iWeaponID).hits);
    _ClientWeaponStats(client, iWeaponID).damage += iDamage;
    _ClientWeaponStats(client, iWeaponID).bodyHits[body]++;

    _ClientAttackersStats(client, victim).hits++;
    _ClientAttackersStats(client, victim).damage += iDamage;
    _ClientAttackersStats(client, victim).bodyHits[body]++;

    _ClientVictimsStats(victim, client).hits++;
    _ClientVictimsStats(victim, client).damage += iDamage;
    _ClientVictimsStats(victim, client).bodyHits[body]++;

    _ClientWeaponStatsR(client, iWeaponID).hits++;
    _ClientWeaponStatsR(client, iWeaponID).damage += iDamage;
    _ClientWeaponStatsR(client, iWeaponID).bodyHits[body]++;

    _ClientLifeStats(client).hits++;
    _ClientLifeStats(client).damage += iDamage;
    _ClientLifeStats(client).bodyHits[body]++;
}



// API
native bool get_client_wstats(int client, int iWeaponID, any[] aStats);

public int __get_client_wstats(Handle plugin, int numParams)
{
    enum { arg_client = 1, arg_weaponID, arg_stats };
    // Check player range
    // Check weapon range

    static int client; client = GetNativeCell(arg_client);
    static int iWeaponID; iWeaponID = GetNativeCell(arg_weaponID);

    static iShots; iShots = _ClientWeaponStats(client, iWeaponID).shots;
    if (iShots)
    {
        static Stats_s aStats;
        aStats.kills = _ClientWeaponStats(client, iWeaponID).kills;
        aStats.deaths = _ClientWeaponStats(client, iWeaponID).deaths;
        aStats.hs = _ClientWeaponStats(client, iWeaponID).hs;
        aStats.tks = _ClientWeaponStats(client, iWeaponID).tks;
        aStats.shots = _ClientWeaponStats(client, iWeaponID).shots;
        aStats.hits = _ClientWeaponStats(client, iWeaponID).hits;
        aStats.damage = _ClientWeaponStats(client, iWeaponID).damage;

        PrintToServer("[API CB]->{hits:%i, shots:%i, damage:%i, hs:%i, tks:%i, kills:%i, deaths:%i}",
            aStats.hits, aStats.shots, aStats.damage, aStats.hs, aStats.tks, aStats.kills, aStats.deaths
        );

        SetNativeArray(arg_stats, aStats, sizeof(aStats));
       
        return true;
    }

    return false;
}


// MISC
bool isModuleActive()
{
    return !csstats_pause.BoolValue;
}

bool ignoreBots(int client, int other = 0)
{
    return ( !csstats_rankbots.BoolValue && ( IsFakeClient(client) || (other && IsFakeClient(other)) ) );
}


// TEST
void Init_Tests()
{
    RegConsoleCmd("TEST", Command_Test);
}

public Action Command_Test(int client, int args)
{
    Stats_s aStats;
    get_client_wstats(client, 61, aStats);

    PrintToServer("[API Response]->{hits:%i, shots:%i, damage:%i, hs:%i, tks:%i, kills:%i, deaths:%i}",
        aStats.hits, aStats.shots, aStats.damage, aStats.hs, aStats.tks, aStats.kills, aStats.deaths
    );

    return Plugin_Handled;
}


stock CSWeaponID __GetCurrentWeaponID(int client)
{
    int iWeaponEnt = GetEntPropEnt(client, Prop_Data, "m_hActiveWeapon");
    // PrintToServer("m_hActiveWeapon:%i, m_iItemDefinitionIndex:%i", iWeaponEnt, GetEntProp(iWeaponEnt, Prop_Send, "m_iItemDefinitionIndex"));
    return view_as<CSWeaponID>( GetEntProp(iWeaponEnt, Prop_Send, "m_iItemDefinitionIndex") );
}

// sv_cheats 1; bot_stop 1
30 Авг 2019
Нечто подобное API реализовано в плагине gameME Plugin
https://github.com/gamemedev/plugin-sourcemod/blob/master/scripting/gameme.sp#L161
 
Сообщения
2.243
Реакции
2.259
Приведу простейший код для воспроизведения проблемы. Запускаем плагин, производим выстрел по противнику с USP - получаем неведомое.
Код:
#include <sourcemod>
#include <cstrike>

static const char VERSION[] = "1.0.0";
public Plugin myinfo =
{
    name        = "Test",
    author        = "wopox1337",
    version        = VERSION
};

public void OnPluginStart() {
    HookEvent("weapon_fire", Event_CSGOWeaponFire); // Регистрируем хук эвента выстрела игрока
    HookEvent("player_hurt", Event_CSGOPlayerHurt); // ... хук эвента попадания по игроку
}

public Action Event_CSGOWeaponFire(Event event, char[] name, bool dontBroadcast)
{
    int client = GetClientOfUserId(event.GetInt("userid")); // получаем id клиента

    static char szWeapon[64];
    event.GetString("weapon", szWeapon, sizeof(szWeapon)); // получаем строку с названием оружия, из которого был произведён выстрел

    PrintToServer(" \t (#1) > Event_CSGOWeaponFire() -> (client:%i, weapon:'%s')", client, szWeapon);
}

public Action Event_CSGOPlayerHurt(Event event, char[] name, bool dontBroadcast)
{
    int attacker = GetClientOfUserId(event.GetInt("attacker")); // id атакующего

    static char szWeapon[64];
    event.GetString("weapon", szWeapon, sizeof(szWeapon)); // оружие, с которого был нанесён урон

    PrintToServer(" \t (#2) > Event_CSGOPlayerHurt() -> (attacker:%i, weapon:'%s')", attacker, szWeapon);
}

// OUTPUT: (??? WTF ???)
// (#1) > Event_CSGOWeaponFire() -> (client:2, weapon:'weapon_usp_silencer')
// (#2) > Event_CSGOPlayerHurt() -> (attacker:2, weapon:'hkp2000')
Краткая информация о сервере, на котором тестирование производилось:

Код:
> version
Protocol version 13711 [964/964]
Exe version 1.37.1.1 (csgo)
Exe build: 20:26:59 Jul 25 2019 (7508) (730)
meta version
Metamod:Source Version Information
    Metamod:Source version 1.11.0-dev+1127
    Plugin interface version: 16:14
    SourceHook version: 5:5
    Loaded As: Valve Server Plugin
    Compiled on: Mar 28 2019 17:00:48
    Built from: https://github.com/alliedmodders/metamod-source/commit/de33074
    Build ID: 1127:de33074
    http://www.metamodsource.net/
> sm version
SourceMod Version Information:
    SourceMod Version: 1.10.0.6434
    SourcePawn Engine: 1.10.0.6434, jit-x86 (build 1.10.0.6434)
    SourcePawn API: v1 = 5, v2 = 12
    Compiled on: Aug  1 2019 05:49:09
    Built from: https://github.com/alliedmodders/sourcemod/commit/b8fd7db
    Build ID: 6434:b8fd7db
    http://www.sourcemod.net/
> sm plugins list
[SM] Listing 1 plugin:
  1 "Test" (1.0.0) by wopox1337
30 Авг 2019
хочется добавить:
"К сожалению, вынужден констатировать тот факт, что самоделка полная херня и нифига не работает" (с)
30 Авг 2019
Стоит подметить, что сам CS:GO не правильно указывает даже в логах наименование оружия.
Код:
> Event_CSGOWeaponFire()-> client:2, szWeapon:'weapon_usp_silencer'

L 08/28/2019 - 21:23:17: "Shorokhov Sergey<3><STEAM_1:0:56777063><CT>" [1207 -403 -164] attacked "Yahn<11><BOT><TERRORIST>" [1216 -307 -166] with "hkp2000" (damage "17") (damage_armor "8") (health "83") (armor "91") (hitgroup "chest")

> Event_CSGOPlayerHurt()-> attacker:2, szWeapon:'hkp2000'
 
Последнее редактирование:
Сообщения
454
Реакции
422
Предупреждения
9
// OUTPUT: (??? WTF ???) // (#1) > Event_CSGOWeaponFire() -> (client:2, weapon:'weapon_usp_silencer') // (#2) > Event_CSGOPlayerHurt() -> (attacker:2, weapon:'hkp2000')
Так и что не верно? В выстреле энтити, в смерти название иконки. Оружие одно и тоже.
30 Авг 2019
там макрос вообще-то есть уже такой. И он не 32. MAXPLAYERS называется.
30 Авг 2019
CSWeaponID iCurWeapon;
Что это такое вообще?
Юзай m_iItemDefinitionIndex
30 Авг 2019
Ну и более больной работы со структурами я не видел.
 
Сообщения
454
Реакции
422
Предупреждения
9
wopox1337, а что нужно? Оптимизация?
 
Сообщения
2.243
Реакции
2.259
один Some Scripter подсказал тыц
Действительно, этот косытль помог с правильным определением оружия. Благодарю.
Код:
#include <sourcemod>
#include <sdktools>
#include <sdkhooks>
#include <cstrike>

#define MAX_PLAYERS 32

static const char VERSION[] = "1.0.0";
public Plugin myinfo =
{
    name        = "CSSTATS API",
    author        = "wopox1337",
    version        = VERSION
};

public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
{
    CreateNative("get_client_wstats", __get_client_wstats);
    return APLRes_Success;
}

ConVar csstats_pause;
ConVar csstats_rankbots;

public void OnPluginStart()
{
    csstats_pause = CreateConVar("csstats_pause", "0",
        .hasMin = true, .min = 0.0, .hasMax = true, .max = 1.0
    );

    csstats_rankbots = CreateConVar("csstats_rankbots", "1",
        .hasMin = true, .min = 0.0, .hasMax = true, .max = 1.0
    );

    Init_WeaponsTable();

    // Init hooks
    // HookEvent("weapon_fire",                       Event_CSGOWeaponFire);
    // HookEvent("player_hurt",                     Event_CSGOPlayerHurt);
    // HookEvent("weapon_fire_on_empty",            Event_CSGOPlayerFire);

    Init_Tests();
}

// *****************************************************
// class Stats
// *****************************************************

enum BodyHits_s {
    hit_Chest,
    hit_Head,
    hit_Stomach,
    hit_LeftArm,
    hit_RightArm,
    hit_LeftLeg,
    hit_RightLeg
}

enum struct Stats_s {
    int hits;
    int shots;
    int damage;
    int hs;
    int tks;
    int kills;
    int deaths;
    int bodyHits[BodyHits_s];
    int bPlants;
    int bExplosions;
    int bDefusions;
    int bDefused;

    void Clear() {
        this.hits = this.shots = this.damage = this.hs = this.tks = this.kills = this.deaths = this.bDefusions = this.bDefused = this.bPlants = this.bExplosions = 0;
    }
}

enum struct PlayerWeapon {
    char name[32];
    int    ammo;
    int    clip;
    Stats_s stats;
}

const MAX_CWEAPONS = 6;
const PlayerWeapon_SIZE = view_as<int>(CSWeapon_MAX_WEAPONS) + MAX_CWEAPONS;

PlayerWeapon g_aWeapons[MAX_PLAYERS + 1][PlayerWeapon_SIZE];
PlayerWeapon g_aAttackers[MAX_PLAYERS + 1][MAX_PLAYERS + 1];
PlayerWeapon g_aVictims[MAX_PLAYERS + 1][MAX_PLAYERS + 1];
Stats_s g_aWeaponsR[MAX_PLAYERS + 1][PlayerWeapon_SIZE];
Stats_s g_aLifeStats[MAX_PLAYERS + 1];

#define _ClientWeaponStats(%1,%2)        g_aWeapons[%1][%2].stats
#define _ClientAttackersStats(%1,%2)    g_aAttackers[%1][%2].stats
#define _ClientVictimsStats(%1,%2)        g_aVictims[%1][%2].stats
#define _ClientWeaponStatsR(%1,%2)        g_aWeaponsR[%1][%2]
#define _ClientLifeStats(%1)            g_aLifeStats[%1]

// Player_s g_aPlayers[MAX_PLAYERS + 1];
/*
public Action Event_CSGOWeaponFire(Event event, char[] name, bool dontBroadcast)
{
    int client = GetClientOfUserId(event.GetInt("userid"));

    static char szWeapon[64];
    event.GetString("weapon", szWeapon, sizeof(szWeapon));

    static CSWeaponID iWeaponID = _NormalizeWeaponID(szWeapon);

    ___LogWeapon("Event_CSGOWeaponFire", client, szWeapon, iWeaponID);

    saveShot(client, iWeaponID);
}
 */
stock void ___LogWeapon(char[] func, int client, char[] szWeapon, CSWeaponID iWeaponID)
{
    PrintToServer(" ::: szWeapon:'%s'", szWeapon);
    CSWeaponID iCurWeapon;
    char szCurWeapon[64];
    switch(iWeaponID)
    {
        case CSWeapon_DEAGLE,
            // Grenades
            CSWeapon_FLASHBANG, CSWeapon_SMOKEGRENADE, CSWeapon_HEGRENADE,
            CSWeapon_INCGRENADE, CSWeapon_TAGGRENADE, CSWeapon_MOLOTOV,
            CSWeapon_DECOY,
            //
            CSWeapon_KNIFE:
            {
                iCurWeapon = iWeaponID;
            }
        default:
        {
            iCurWeapon = __GetCurrentWeaponID(client);
        }
    }

    if(!CS_IsValidWeaponID(iCurWeapon))
    {
        PrintToServer(" Invalid weaponID:%i", iCurWeapon);
        return;
    }

    CS_WeaponIDToAlias(iCurWeapon, szCurWeapon, sizeof(szCurWeapon));
    PrintToServer(" \t > %s() -> client:%i, szWeapon:'%s'(id:%i), __GetCurrentWeaponID:%i ('s')",
        func, client, szWeapon, iWeaponID, iCurWeapon
    );
}
// sv_cheats 1; bot_stop 1

CSWeaponID _NormalizeWeaponID(char[] szWeapon)
{
    CS_GetTranslatedWeaponAlias(szWeapon, szWeapon, 64);
    // PrintToServer("szWeapon='%s' (%i)", szWeapon, CS_AliasToWeaponID(szWeapon));
    return CS_AliasToWeaponID(szWeapon);
}

void saveShot(int client, CSWeaponID iWeaponID)
{
    if ( !isModuleActive() )
        return;

    if ( ignoreBots(client) )
        return;

    _ClientWeaponStats(client, iWeaponID).shots++;
    // _ClientWeaponStatsR(client, iWeaponID).shots++;

    // PrintToServer("client:%i, iWeaponID:%i, (WShots:%i)", client, iWeaponID, g_aPlayers[client].weapons[iWeaponID].shots);
}

public Action Event_CSGOPlayerHurt(Event event, char[] 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

    int client = GetClientOfUserId(event.GetInt("userid"));
    int attacker = GetClientOfUserId(event.GetInt("attacker"));

    static char szWeapon[64];
    event.GetString("weapon", szWeapon, sizeof(szWeapon));

    static CSWeaponID iWeaponID;
    if(strcmp(szWeapon, "inferno") == 0)
    {
        iWeaponID = CSWeapon_MOLOTOV;
        PrintToServer("AAAAAAAAAAAAAA!");
    }
        
    else iWeaponID = _NormalizeWeaponID(szWeapon);
    
//    ___LogWeapon("Event_CSGOPlayerHurt", attacker, szWeapon, iWeaponID);

    int iDamage = GetClientOfUserId(event.GetInt("dmg_health"));
    int iHitGroup = GetClientOfUserId(event.GetInt("hitgroup"));

    saveHit(attacker, client, iWeaponID, iDamage, iHitGroup);
}
void saveHit(int client, int victim, CSWeaponID iWeaponID, int iDamage, int body)
{
    // if ( !isModuleActive() )
    //     return;

    // if ( ignoreBots(client, victim) )
    //     return;

    // if ( client == victim )
    //     return;

    _ClientWeaponStats(client, iWeaponID).hits++;

    PrintToServer("SaveHIT(client:%i, iWeap:%i), hits:%i", client, iWeaponID, _ClientWeaponStats(client, iWeaponID).hits);
    _ClientWeaponStats(client, iWeaponID).damage += iDamage;
    _ClientWeaponStats(client, iWeaponID).bodyHits[body]++;

    _ClientAttackersStats(client, victim).hits++;
    _ClientAttackersStats(client, victim).damage += iDamage;
    _ClientAttackersStats(client, victim).bodyHits[body]++;

    _ClientVictimsStats(victim, client).hits++;
    _ClientVictimsStats(victim, client).damage += iDamage;
    _ClientVictimsStats(victim, client).bodyHits[body]++;

    _ClientWeaponStatsR(client, iWeaponID).hits++;
    _ClientWeaponStatsR(client, iWeaponID).damage += iDamage;
    _ClientWeaponStatsR(client, iWeaponID).bodyHits[body]++;

    _ClientLifeStats(client).hits++;
    _ClientLifeStats(client).damage += iDamage;
    _ClientLifeStats(client).bodyHits[body]++;
}



// API
native bool get_client_wstats(int client, int iWeaponID, any[] aStats);

public int __get_client_wstats(Handle plugin, int numParams)
{
    enum { arg_client = 1, arg_weaponID, arg_stats };
    // Check player range
    // Check weapon range

    static int client; client = GetNativeCell(arg_client);
    static int iWeaponID; iWeaponID = GetNativeCell(arg_weaponID);

    static iShots; iShots = _ClientWeaponStats(client, iWeaponID).shots;
    if (iShots)
    {
        static Stats_s aStats;
        aStats.kills = _ClientWeaponStats(client, iWeaponID).kills;
        aStats.deaths = _ClientWeaponStats(client, iWeaponID).deaths;
        aStats.hs = _ClientWeaponStats(client, iWeaponID).hs;
        aStats.tks = _ClientWeaponStats(client, iWeaponID).tks;
        aStats.shots = _ClientWeaponStats(client, iWeaponID).shots;
        aStats.hits = _ClientWeaponStats(client, iWeaponID).hits;
        aStats.damage = _ClientWeaponStats(client, iWeaponID).damage;

        PrintToServer("[API CB]->{hits:%i, shots:%i, damage:%i, hs:%i, tks:%i, kills:%i, deaths:%i}",
            aStats.hits, aStats.shots, aStats.damage, aStats.hs, aStats.tks, aStats.kills, aStats.deaths
        );

        SetNativeArray(arg_stats, aStats, sizeof(aStats));
        
        return true;
    }

    return false;
}


// MISC
bool isModuleActive()
{
    return !csstats_pause.BoolValue;
}

bool ignoreBots(int client, int other = 0)
{
    return ( !csstats_rankbots.BoolValue && ( IsFakeClient(client) || (other && IsFakeClient(other)) ) );
}


// TEST
void Init_Tests()
{
    ServerCommand("sv_cheats 1; bot_stop 1");
    RegConsoleCmd("TEST", Command_Test);
}
 
public Action Command_Test(int client, int args)
{
    Stats_s aStats;
    get_client_wstats(client, 61, aStats);

    PrintToServer("[API Response]->{hits:%i, shots:%i, damage:%i, hs:%i, tks:%i, kills:%i, deaths:%i}",
        aStats.hits, aStats.shots, aStats.damage, aStats.hs, aStats.tks, aStats.kills, aStats.deaths
    );

    return Plugin_Handled;
}

// sv_cheats 1; bot_stop 1


// Init weapons table info (CS:GO fixes)

Handle g_tItems;

void Init_WeaponsTable()
{
    g_tItems = CreateTrie();

    SMCParser hSMC = new SMCParser();
    {
        hSMC.OnEnterSection = SMC_OnEnterSection;
        hSMC.OnKeyValue = SMC_OnKeyValue;
        hSMC.OnLeaveSection = SMC_OnLeaveSection;

        SMCError hError = hSMC.ParseFile("scripts/items/items_game.txt");

        if(hError != SMCError_Okay)
        {
            char szBuffer[128];
            if(hSMC.GetErrorString(hError, szBuffer, sizeof(szBuffer)))
            {
                LogError("%s", szBuffer);
            } else {
                LogError("Fatal parse error");
            }
        }

        delete hSMC;
    }
}

bool g_bItems;
char g_szNewSectionName[255];
int g_iPlaceRow;

public SMCResult SMC_OnEnterSection(SMCParser hSMC, const char[] name, bool opt_quotes)
{
    ++g_iPlaceRow;

    if(!g_bItems && g_iPlaceRow == 2 && strcmp(name, "items") == 0)
        g_bItems = true;
    
    if(g_bItems && g_iPlaceRow == 3 && StringToInt(name) != 0)
        strcopy(g_szNewSectionName, sizeof(g_szNewSectionName), name);
}

public SMCResult SMC_OnKeyValue(SMCParser hSMC, const char[] key, const char[] value, bool key_quotes, bool value_quotes)
{
    if(g_bItems && g_iPlaceRow == 3 && strcmp(key, "name") == 0 && StrContains(value, "weapon_") == 0)
        SetTrieString(g_tItems, g_szNewSectionName, value);
}

public SMCResult SMC_OnLeaveSection(SMCParser hSMC)
{
    --g_iPlaceRow;

    if(g_bItems && g_iPlaceRow < 2)
        g_bItems = false;
}

/* Hooks */

public void OnClientPostAdminCheck(int client)
{
    SDKHookEx(client, SDKHook_OnTakeDamage, CBasePlayer_OnTakeDamage);
}

public Action CBasePlayer_OnTakeDamage(int client, int &attacker, int &inflictor, float &damage, int &damagetype)
{
    if(!IsClientInGame(attacker))
        return;

    char szWeaponName[64];
    __GetCurrentWeapon(attacker, szWeaponName);

    PrintToServer(" \t> OnTakeDamage(cl:%i, szWeap:'%s', dmg:%.2f", attacker, szWeaponName, damage);
}


stock bool __GetCurrentWeapon(int client, char[] szWeaponName, int iSize = 64)
{
    int iWeapon = GetEntPropEnt(client, Prop_Send, "m_hActiveWeapon");
    //new bool:mode = GetEntProp(client, Prop_Send, "m_weaponMode") == 1;
    bool bSilencerOn = GetEntProp(iWeapon, Prop_Send, "m_bSilencerOn") == 1;
    iWeapon = GetEntProp(iWeapon, Prop_Send, "m_iItemDefinitionIndex");
    
    IntToString(iWeapon, szWeaponName, iSize);
    GetTrieString(g_tItems, szWeaponName, szWeaponName, iSize);
    PrintToServer("%10N shoots %s %s", client, szWeaponName, bSilencerOn ? "Silencer":"");

    return true;
}
1 Сен 2019
я планировал простенько "списать" с AMXX структуру для API. Более лучшего варианта работы с подобной структурой чем enum structs пока-что не нашёл. Вот и спрашиваю. возможно как-то более лучшим способом реализовать задачу можно.
 
Сообщения
454
Реакции
422
Предупреждения
9
wopox1337, _______я спросил, что нужно сделать, а не описывать своё творение. ___________Кстати, максимум игроков снова не 32.
 

Пользователи, просматривающие эту тему

Сейчас на форуме нет ни одного пользователя.
Сверху Снизу