Баг molotov

Сообщения
509
Реакции
111
Помог
16 раз(а)
Ошибка
Баг с выдачей молотова в новом раунде
ОС
Windows
Amx Mod X
1.9.0
Билд
Protocol version 48
Exe version 1.1.2.7/Stdio (cstrike)
ReHLDS version: 3.4.0.669-dev
Build date: 15:27:41 Apr 8 2019 (1822)
Build from: https://github.com/dreamstalker/rehlds/commit/f6822e3
ReGamedll
game version
ReGameDLL version: 5.7.0.329-dev
Build date: 10:47:52 Apr 17 2019
Build from: https://github.com/s1lentq/ReGameDLL_CS/commit/8c85aee
Версия Metamod
Metamod-r v1.3.0.128, API (5:13)
Metamod-r build: 17:47:54 Aug 24 2018
Metamod-r from: https://github.com/theAsmodai/metamod-r/commit/0cf2f70
Список метамодулей
Currently loaded plugins:
description stat pend file vers src load unload
[ 1] VoiceTranscoder RUN - VoiceTranscoder.so v2017RC3 ini ANY ANY
[ 2] SafeNameAndChat RUN - SafeNameAndChat.so v1.1 ini ANY ANY
[ 3] Reunion RUN - reunion_mm_i386.so v0.1.0.92 ini Start Never
[ 4] AMX Mod X RUN - amxmodx_mm_i386.so v1.9.0.5232 ini Start ANY
[ 5] Rechecker RUN - rechecker_mm_i386.so v2.5 ini Chlvl ANY
[ 6] WHBlocker RUN - whblocker_mm_i386.so v1.5.696 ini Chlvl ANY
[ 7] ReSemiclip RUN - resemiclip_mm_i386.so v2.3.9 ini Chlvl ANY
[ 8] MySQL RUN - mysql_amxx_i386.so v1.9.0.5232 pl4 ANY ANY
[ 9] ReAPI RUN - reapi_amxx_i386.so v5.8.0.166-dev pl4 ANY Never
[10] FakeMeta RUN - fakemeta_amxx_i386.so v1.9.0.5232 pl4 ANY ANY
[11] Ham Sandwich RUN - hamsandwich_amxx_i386.so v1.9.0.5232 pl4 ANY ANY
[12] Engine RUN - engine_amxx_i386.so v1.9.0.5232 pl4 ANY ANY
[13] CStrike RUN - cstrike_amxx_i386.so v1.9.0.5232 pl4 ANY ANY
[14] Fun RUN - fun_amxx_i386.so v1.9.0.5232 pl4 ANY ANY
14 plugins, 14 running
Список плагинов
Чистый + AES

[ 1] Commands Menu 1.9.0.5232 AMXX Dev Team cmdmenu.amxx running
[ 2] Admin Votes 1.9.0.5232 AMXX Dev Team adminvote.amxx running
[ 3] Admin Commands 1.9.0.5232 AMXX Dev Team admincmd.amxx running
[ 4] Restrict Weapons 1.9.0.5232 AMXX Dev Team restmenu.amxx running
[ 5] StatsX 1.9.0.5232 AMXX Dev Team statsx.amxx running
[ 6] Stats Configuration 1.9.0.5232 AMXX Dev Team statscfg.amxx running
[ 7] Advanced Experience Sy 0.5.9 [REA serfreeman1337/s aes_main.amxx running
[ 8] AES: CStrike Addon 0.5.9 [REA serfreeman1337/s aes_exp_cstrike running
[ 9] AES: Informer 0.5.9 [REA serfreeman1337/s aes_informer.am running
[ 10] AES: Admin Tools 0.5.9 [REA serfreeman1337/s aes_exp_editor. running
[ 11] AES: Bonus System 0.5.9 Vega serfreeman1337/s aes_bonus_syste running
[ 12] AES: Bonus CSTRIKE 0.5.9 [REA serfreeman1337/s aes_bonus_cstri running
[ 13] AES Bonus: Flags 0.2 Sonyx aes_bonus_flags running

13plugins, 13 running
Автор плагина
xUnicorn (t3rkecorejz)
Версия плагина
1.1
Исходный код
#include < amxmodx >
#include < fakemeta_util >
#include < hamsandwich >

//#define ZP_SUPPORT // Поддержка Zombie Plague 4.3

#if defined ZP_SUPPORT
#include < zombieplague >
#endif

#define linux_diff_weapon 4
#define linux_diff_player 5

// CBasePlayerItem
#define m_pPlayer 41

#define MAX_CLIENTS 32
#define NADE_TYPE 1337

#define FLAME_CLASSNAME "ent_flame" // Класснейм огня
#define FLAME_DURATION 5.0 // Сколько будет идти горение
#define FLAME_TIMERESET 0.2 // Через сколько будет наносится урон

// Урон от огня
#if defined ZP_SUPPORT
#define FLAME_DAMAGE random_float(30.0, 50.0)
#else
#define FLAME_DAMAGE random_float(3.0, 5.0)
#endif

#define GRENADE_VIEW_MODEL "models/x/v_molotov.mdl"
#define GRENADE_PLAYER_MODEL "models/x/p_molotov.mdl"
#define GRENADE_WORLD_MODEL "models/x/w_molotov.mdl"
#define GRENADE_MODEL_BODY 0

#define GRENADE_EXPLODE_SOUND "x/molotov_exp.wav"

#define WEAPON_OLD "weapon_smokegrenade"
#define WEAPON_NEW "x/weapon_molotov"
#define WEAPON_HUD "sprites/x/640hudx1.spr"

new const iWeaponList[] = {
13, 1, -1, -1, 3, 3, 9, 24 // weapon_smokegrenade
}


#if defined ZP_SUPPORT
new g_iItemID;
#endif

new g_iThinkTimes[512];
new g_iszModelIndexSprite;
new g_iUserHasMolotov[MAX_CLIENTS + 1];

public plugin_init()
{
register_plugin("[AMXX] Grenade: Molotov", "1.1", "xUnicorn (t3rkecorejz)");

RegisterHam(Ham_Killed, "player", "CPlayer__Killed_Post", .Post = 1);
RegisterHam(Ham_Touch, "grenade", "CGrenade__Touch_Pre", .Post = 0);
RegisterHam(Ham_Think, "grenade", "CGrenade__Think_Pre", .Post = 0);
RegisterHam(Ham_Think, "info_target", "CEntity__Think_Pre", .Post = 0);
RegisterHam(Ham_Item_Deploy, WEAPON_OLD, "CGrenade__Item_Deploy_Post", .Post = 1);
RegisterHam(Ham_Item_AddToPlayer, WEAPON_OLD, "CGrenade__Item_AddToPlayer_Post", .Post = 1);

register_forward(FM_SetModel, "FM_Hook_SetModel_Pre", ._post = 0);

register_clcmd(WEAPON_NEW, "HookSelect");

#if defined ZP_SUPPORT
g_iItemID = zp_register_extra_item("Molotov", 10, ZP_TEAM_HUMAN);
#else
register_clcmd("say molotov", "Command_GiveMolotov");
#endif
}

public plugin_precache()
{
new szBuffer[64]; formatex(szBuffer, charsmax(szBuffer), "sprites/%s.txt", WEAPON_NEW);

engfunc(EngFunc_PrecacheGeneric, szBuffer);
engfunc(EngFunc_PrecacheGeneric, WEAPON_HUD);

g_iszModelIndexSprite = engfunc(EngFunc_PrecacheModel, "sprites/x/flame.spr");

engfunc(EngFunc_PrecacheModel, GRENADE_VIEW_MODEL);
engfunc(EngFunc_PrecacheModel, GRENADE_PLAYER_MODEL);
engfunc(EngFunc_PrecacheModel, GRENADE_WORLD_MODEL);

engfunc(EngFunc_PrecacheSound, GRENADE_EXPLODE_SOUND);
}

public client_disconnected(iPlayer) g_iUserHasMolotov[iPlayer] = 0;

#if defined ZP_SUPPORT
public zp_extra_item_selected(iPlayer, iItemID)
{
if(iItemID == g_iItemID)
Command_GiveMolotov(iPlayer);
}
#endif

public HookSelect(iPlayer)
{
engclient_cmd(iPlayer, WEAPON_OLD);
return PLUGIN_HANDLED;
}

public Command_GiveMolotov(iPlayer)
{
if(!is_user_alive(iPlayer))
return PLUGIN_HANDLED;

if(user_has_weapon(iPlayer, CSW_SMOKEGRENADE))
{
g_iUserHasMolotov[iPlayer]++;
ExecuteHamB(Ham_GiveAmmo, iPlayer, 1, "SmokeGrenade", 32);
emit_sound(iPlayer, CHAN_ITEM, "items/9mmclip1.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);
}

if(CPlayer__MakeWeapon(iPlayer))
{
g_iUserHasMolotov[iPlayer]++;
emit_sound(iPlayer, CHAN_ITEM, "items/gunpickup2.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);
}
#if defined ZP_SUPPORT
else return ZP_PLUGIN_HANDLED;
#else
else return PLUGIN_HANDLED;
#endif

return PLUGIN_HANDLED;
}

public CPlayer__Killed_Post(iVictim) g_iUserHasMolotov[iVictim] = 0;
public CGrenade__Touch_Pre(iEntity)
{
if(!pev_valid(iEntity))
return HAM_IGNORED;

if(pev(iEntity, pev_flTimeStepSound) == NADE_TYPE)
{
static Float: vecOrigin[3];
pev(iEntity, pev_origin, vecOrigin);

if(engfunc(EngFunc_PointContents, vecOrigin) == CONTENTS_WATER) {
set_pev(iEntity, pev_flags, FL_KILLME);
return HAM_IGNORED;
}

set_pev(iEntity, pev_dmgtime, get_gametime());
}

return HAM_IGNORED;
}

public CGrenade__Think_Pre(iEntity)
{
if(!pev_valid(iEntity))
return HAM_IGNORED;

static Float: flDamageTime; pev(iEntity, pev_dmgtime, flDamageTime);

if(flDamageTime > get_gametime())
return HAM_IGNORED;

if(pev(iEntity, pev_flTimeStepSound) == NADE_TYPE)
{
static Float: vecOrigin[3];
pev(iEntity, pev_origin, vecOrigin);

if(engfunc(EngFunc_PointContents, vecOrigin) == CONTENTS_WATER) {
set_pev(iEntity, pev_flags, FL_KILLME);
return HAM_IGNORED;
}

MolotovExplode(iEntity);
return HAM_SUPERCEDE;
}

return HAM_IGNORED;
}

MolotovExplode(iEntity)
{
if(iEntity < 0)
return;

static Float: vecOrigin[3];
pev(iEntity, pev_origin, vecOrigin);

emit_sound(iEntity, CHAN_WEAPON, GRENADE_EXPLODE_SOUND, VOL_NORM, ATTN_NORM, 0, PITCH_NORM);

static iReference;

new iOwner; iOwner = pev(iEntity, pev_owner);

if(iReference || (iReference = engfunc(EngFunc_AllocString, "info_target")))
{
new iFlameEntity = engfunc(EngFunc_CreateNamedEntity, iReference);

g_iThinkTimes[iFlameEntity] = 0;

set_pev(iFlameEntity, pev_classname, FLAME_CLASSNAME);
set_pev(iFlameEntity, pev_solid, SOLID_TRIGGER);
set_pev(iFlameEntity, pev_movetype, MOVETYPE_TOSS);
set_pev(iFlameEntity, pev_effects, EF_NODRAW);
set_pev(iFlameEntity, pev_owner, iOwner);
set_pev(iFlameEntity, pev_nextthink, get_gametime() + FLAME_TIMERESET);

engfunc(EngFunc_SetModel, iFlameEntity, "models/w_ak47.mdl");
engfunc(EngFunc_SetOrigin, iFlameEntity, vecOrigin);
}

message_begin(MSG_BROADCAST,SVC_TEMPENTITY);
write_byte(TE_SPRITETRAIL);
engfunc(EngFunc_WriteCoord, vecOrigin[0]);
engfunc(EngFunc_WriteCoord, vecOrigin[1]);
engfunc(EngFunc_WriteCoord, vecOrigin[2] + 100.0);
engfunc(EngFunc_WriteCoord, vecOrigin[0]);
engfunc(EngFunc_WriteCoord, vecOrigin[1]);
engfunc(EngFunc_WriteCoord, vecOrigin[2] + 30.0);
write_short(g_iszModelIndexSprite);
write_byte(50);
write_byte(random_num(17, 20));
write_byte(random_num(4, 6));
write_byte(15);
write_byte(10);
message_end();

set_pev(iEntity, pev_flags, FL_KILLME);
}

public CEntity__Think_Pre(iEntity)
{
if(!pev_valid(iEntity))
return HAM_IGNORED;

static iVictim = -1;

new szClassName[32], vecOrigin[3], iOwner;
pev(iEntity, pev_classname, szClassName, charsmax(szClassName));

if(equal(szClassName, FLAME_CLASSNAME))
{
pev(iEntity, pev_origin, vecOrigin);
iOwner = pev(iEntity, pev_owner);

if(g_iThinkTimes[iEntity] == floatround(FLAME_DURATION / FLAME_TIMERESET))
{
set_pev(iEntity, pev_flags, FL_KILLME);

return HAM_IGNORED;
}

while((iVictim = engfunc(EngFunc_FindEntityInSphere, iVictim, vecOrigin, 100.0)))
{
if(iOwner == iVictim || !is_user_alive(iVictim))
continue;

#if defined ZP_SUPPORT
if(zp_get_user_zombie(iOwner) || !zp_get_user_zombie(iVictim))
continue;
#endif

ExecuteHamB(Ham_TakeDamage, iVictim, iEntity, iOwner, FLAME_DAMAGE, DMG_BURN | DMG_NEVERGIB);
}

g_iThinkTimes[iEntity] += 1;
set_pev(iEntity, pev_nextthink, get_gametime() + FLAME_TIMERESET);
}

return HAM_IGNORED;
}

public CGrenade__Item_Deploy_Post(iEntity)
{
static iPlayer; iPlayer = get_pdata_cbase(iEntity, m_pPlayer, linux_diff_weapon);

#if defined ZP_SUPPORT
if(zp_get_user_zombie(iPlayer))
return;
#endif

if(!g_iUserHasMolotov[iPlayer])
return;

set_pev(iPlayer, pev_viewmodel2, GRENADE_VIEW_MODEL);
set_pev(iPlayer, pev_weaponmodel2, GRENADE_PLAYER_MODEL);
}

public CGrenade__Item_AddToPlayer_Post(iItem, iPlayer) p_set_user_weaponlist(iPlayer, g_iUserHasMolotov[iPlayer]);
public FM_Hook_SetModel_Pre(iEntity, const szModel[])
{
if(!pev_valid(iEntity))
return FMRES_IGNORED;

static Float: flDamageTime; pev(iEntity, pev_dmgtime, flDamageTime);

if(flDamageTime == 0.0)
return FMRES_IGNORED;

static iOwner; iOwner = pev(iEntity, pev_owner);

if(equal(szModel, "models/w_smokegrenade.mdl"))
{
if(g_iUserHasMolotov[iOwner])
{
engfunc(EngFunc_SetModel, iEntity, GRENADE_WORLD_MODEL);

set_pev(iEntity, pev_body, GRENADE_MODEL_BODY);
set_pev(iEntity, pev_flTimeStepSound, NADE_TYPE);

g_iUserHasMolotov[iOwner] -= 1;

return FMRES_SUPERCEDE;
}
}

return FMRES_IGNORED;
}

public CPlayer__MakeWeapon(iPlayer)
{
static iEntity, g_AllocString_E;

if(g_AllocString_E || (g_AllocString_E = engfunc(EngFunc_AllocString, WEAPON_OLD)))
iEntity = engfunc(EngFunc_CreateNamedEntity, g_AllocString_E);

if(iEntity <= 0)
return 0;

g_iUserHasMolotov[iPlayer]++;
set_pev(iEntity, pev_spawnflags, SF_NORESPAWN);
ExecuteHam(Ham_Spawn, iEntity);

if(!ExecuteHamB(Ham_AddPlayerItem, iPlayer, iEntity))
{
set_pev(iEntity, pev_flags, FL_KILLME);
return 0;
}

ExecuteHamB(Ham_Item_AttachToPlayer, iEntity, iPlayer);
return iEntity;
}

stock p_set_user_weaponlist(iPlayer, iList) {
message_begin(MSG_ONE, get_user_msgid("WeaponList"), _, iPlayer);
write_string(iList ? WEAPON_NEW : WEAPON_OLD);
write_byte(iWeaponList[0]);
write_byte(iWeaponList[1]);
write_byte(iWeaponList[2]);
write_byte(iWeaponList[3]);
write_byte(iWeaponList[4]);
write_byte(iWeaponList[5]);
write_byte(iWeaponList[6]);
write_byte(iWeaponList[7]);
message_end();
}
Баг заключается в том что если купить молотов,и потом использовать его в течении раунда,то в новом раунде он выдается снова без команды и покупки. Так случается не всегда,может сразу нормально сработать,тоесть AES выдаст дым,а бывает 10 раундов подряд выдает молотов хотя игрок его не покупал.
Но такое случается только если в AES прописана выдача гранаты в новом раунде. Возможно это пофиксить?
 

Вложения

Сообщения
845
Реакции
521
Помог
13 раз(а)
Код:
#include < amxmodx >
#include < fakemeta_util >
#include < hamsandwich >

//#define ZP_SUPPORT // Поддержка Zombie Plague 4.3

#if defined ZP_SUPPORT
    #include < zombieplague >
#endif

#define linux_diff_weapon       4
#define linux_diff_player       5

// CBasePlayerItem
#define m_pPlayer               41

#define MAX_CLIENTS             32
#define NADE_TYPE               1337

#define FLAME_CLASSNAME         "ent_flame" // Класснейм огня
#define FLAME_DURATION          5.0 // Сколько будет идти горение
#define FLAME_TIMERESET         0.2 // Через сколько будет наносится урон

// Урон от огня
#if defined ZP_SUPPORT
    #define FLAME_DAMAGE        random_float(30.0, 50.0)
#else
    #define FLAME_DAMAGE        random_float(3.0, 5.0)
#endif

#define GRENADE_VIEW_MODEL      "models/x/v_molotov.mdl"
#define GRENADE_PLAYER_MODEL    "models/x/p_molotov.mdl"
#define GRENADE_WORLD_MODEL     "models/x/w_molotov.mdl"
#define GRENADE_MODEL_BODY      0

#define GRENADE_EXPLODE_SOUND   "x/molotov_exp.wav"

#define WEAPON_OLD              "weapon_smokegrenade"
#define WEAPON_NEW              "x/weapon_molotov"
#define WEAPON_HUD              "sprites/x/640hudx1.spr"

new const iWeaponList[] = {
    13, 1,  -1, -1, 3, 3, 9,  24 // weapon_smokegrenade
}


#if defined ZP_SUPPORT
    new g_iItemID;
#endif

new g_iThinkTimes[512];
new g_iszModelIndexSprite;
new g_iUserHasMolotov[MAX_CLIENTS + 1];

public plugin_init()
{
    register_plugin("[AMXX] Grenade: Molotov", "1.1", "xUnicorn (t3rkecorejz)");

    RegisterHam(Ham_Killed, "player", "CPlayer__Killed_Post", .Post = 1);
    RegisterHam(Ham_Touch, "grenade", "CGrenade__Touch_Pre", .Post = 0);
    RegisterHam(Ham_Think, "grenade", "CGrenade__Think_Pre", .Post = 0);
    RegisterHam(Ham_Think, "info_target", "CEntity__Think_Pre", .Post = 0);
    RegisterHam(Ham_Item_Deploy, WEAPON_OLD, "CGrenade__Item_Deploy_Post", .Post = 1);
    RegisterHam(Ham_Item_AddToPlayer, WEAPON_OLD, "CGrenade__Item_AddToPlayer_Post", .Post = 1);

    register_forward(FM_SetModel, "FM_Hook_SetModel_Pre", ._post = 0);

    register_clcmd(WEAPON_NEW, "HookSelect");

    #if defined ZP_SUPPORT
        g_iItemID = zp_register_extra_item("Molotov", 10, ZP_TEAM_HUMAN);
    #else
        register_clcmd("say molotov", "Command_GiveMolotov");
    #endif
}

public plugin_precache()
{
    new szBuffer[64]; formatex(szBuffer, charsmax(szBuffer), "sprites/%s.txt", WEAPON_NEW);

    engfunc(EngFunc_PrecacheGeneric, szBuffer);
    engfunc(EngFunc_PrecacheGeneric, WEAPON_HUD);
    
    g_iszModelIndexSprite = engfunc(EngFunc_PrecacheModel, "sprites/x/flame.spr");

    engfunc(EngFunc_PrecacheModel, GRENADE_VIEW_MODEL);
    engfunc(EngFunc_PrecacheModel, GRENADE_PLAYER_MODEL);
    engfunc(EngFunc_PrecacheModel, GRENADE_WORLD_MODEL);

    engfunc(EngFunc_PrecacheSound, GRENADE_EXPLODE_SOUND);
}

public client_disconnected(iPlayer) g_iUserHasMolotov[iPlayer] = 0;

#if defined ZP_SUPPORT
    public zp_extra_item_selected(iPlayer, iItemID)
    {
        if(iItemID == g_iItemID)
            Command_GiveMolotov(iPlayer);
    }
#endif

public HookSelect(iPlayer)
{
    engclient_cmd(iPlayer, WEAPON_OLD);
    return PLUGIN_HANDLED;
}

public Command_GiveMolotov(iPlayer)
{
    if(!is_user_alive(iPlayer))
        return PLUGIN_HANDLED;

    if(user_has_weapon(iPlayer, CSW_SMOKEGRENADE))
    {
        g_iUserHasMolotov[iPlayer]++;
        ExecuteHamB(Ham_GiveAmmo, iPlayer, 1, "SmokeGrenade", 32);
        emit_sound(iPlayer, CHAN_ITEM, "items/9mmclip1.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);
    }

    if(CPlayer__MakeWeapon(iPlayer))
    {
        g_iUserHasMolotov[iPlayer]++;
        emit_sound(iPlayer, CHAN_ITEM, "items/gunpickup2.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);
    }
    #if defined ZP_SUPPORT
        else return ZP_PLUGIN_HANDLED;
    #else
        else return PLUGIN_HANDLED;
    #endif

    return PLUGIN_HANDLED;
}

public CPlayer__Killed_Post(iVictim) g_iUserHasMolotov[iVictim] = 0;
public CGrenade__Touch_Pre(iEntity)
{
    if(!pev_valid(iEntity))
        return HAM_IGNORED;

    if(pev(iEntity, pev_flTimeStepSound) == NADE_TYPE)
    {
        static Float: vecOrigin[3];
        pev(iEntity, pev_origin, vecOrigin);

        if(engfunc(EngFunc_PointContents, vecOrigin) == CONTENTS_WATER) {
            set_pev(iEntity, pev_flags, FL_KILLME);
            return HAM_IGNORED;
        }

        set_pev(iEntity, pev_dmgtime, get_gametime());
    }

    return HAM_IGNORED;
}

public CGrenade__Think_Pre(iEntity)
{
    if(!pev_valid(iEntity))
        return HAM_IGNORED;

    static Float: flDamageTime; pev(iEntity, pev_dmgtime, flDamageTime);
    
    if(flDamageTime > get_gametime())
        return HAM_IGNORED;

    if(pev(iEntity, pev_flTimeStepSound) == NADE_TYPE)
    {
        static Float: vecOrigin[3];
        pev(iEntity, pev_origin, vecOrigin);

        if(engfunc(EngFunc_PointContents, vecOrigin) == CONTENTS_WATER) {
            set_pev(iEntity, pev_flags, FL_KILLME);
            return HAM_IGNORED;
        }

        MolotovExplode(iEntity);
        return HAM_SUPERCEDE;
    }

    return HAM_IGNORED;
}

MolotovExplode(iEntity)
{
    if(iEntity < 0)
        return;
    
    static Float: vecOrigin[3];
    pev(iEntity, pev_origin, vecOrigin);

    emit_sound(iEntity, CHAN_WEAPON, GRENADE_EXPLODE_SOUND, VOL_NORM, ATTN_NORM, 0, PITCH_NORM);

    static iReference;

    new iOwner; iOwner = pev(iEntity, pev_owner);

    if(iReference || (iReference = engfunc(EngFunc_AllocString, "info_target")))
    {
        new iFlameEntity = engfunc(EngFunc_CreateNamedEntity, iReference);

        g_iThinkTimes[iFlameEntity] = 0;

        set_pev(iFlameEntity, pev_classname, FLAME_CLASSNAME);
        set_pev(iFlameEntity, pev_solid, SOLID_TRIGGER);
        set_pev(iFlameEntity, pev_movetype, MOVETYPE_TOSS);
        set_pev(iFlameEntity, pev_effects, EF_NODRAW);
        set_pev(iFlameEntity, pev_owner, iOwner);
        set_pev(iFlameEntity, pev_nextthink, get_gametime() + FLAME_TIMERESET);

        engfunc(EngFunc_SetModel, iFlameEntity, "models/w_ak47.mdl");
        engfunc(EngFunc_SetOrigin, iFlameEntity, vecOrigin);
    }

    message_begin(MSG_BROADCAST,SVC_TEMPENTITY);
    write_byte(TE_SPRITETRAIL);
    engfunc(EngFunc_WriteCoord, vecOrigin[0]);
    engfunc(EngFunc_WriteCoord, vecOrigin[1]);
    engfunc(EngFunc_WriteCoord, vecOrigin[2] + 100.0);
    engfunc(EngFunc_WriteCoord, vecOrigin[0]);
    engfunc(EngFunc_WriteCoord, vecOrigin[1]);
    engfunc(EngFunc_WriteCoord, vecOrigin[2] + 30.0);
    write_short(g_iszModelIndexSprite);
    write_byte(50);
    write_byte(random_num(17, 20));
    write_byte(random_num(4, 6));
    write_byte(15);
    write_byte(10);
    message_end();

    set_pev(iEntity, pev_flags, FL_KILLME);
}

public CEntity__Think_Pre(iEntity)
{
    if(!pev_valid(iEntity))
        return HAM_IGNORED;

    static iVictim = -1;

    new szClassName[32], vecOrigin[3], iOwner;
    pev(iEntity, pev_classname, szClassName, charsmax(szClassName));

    if(equal(szClassName, FLAME_CLASSNAME))
    {
        pev(iEntity, pev_origin, vecOrigin);
        iOwner = pev(iEntity, pev_owner);

        if(g_iThinkTimes[iEntity] == floatround(FLAME_DURATION / FLAME_TIMERESET))
        {
            set_pev(iEntity, pev_flags, FL_KILLME);

            return HAM_IGNORED;
        }

        while((iVictim = engfunc(EngFunc_FindEntityInSphere, iVictim, vecOrigin, 100.0)))
        {
            if(iOwner == iVictim || !is_user_alive(iVictim))
                continue;

            #if defined ZP_SUPPORT
                if(zp_get_user_zombie(iOwner) || !zp_get_user_zombie(iVictim))
                    continue;
            #endif

            ExecuteHamB(Ham_TakeDamage, iVictim, iEntity, iOwner, FLAME_DAMAGE, DMG_BURN | DMG_NEVERGIB);
        }

        g_iThinkTimes[iEntity] += 1;
        set_pev(iEntity, pev_nextthink, get_gametime() + FLAME_TIMERESET);
    }

    return HAM_IGNORED;
}

public CGrenade__Item_Deploy_Post(iEntity)
{
    static iPlayer; iPlayer = get_pdata_cbase(iEntity, m_pPlayer, linux_diff_weapon);

    #if defined ZP_SUPPORT
        if(zp_get_user_zombie(iPlayer))
            return;
    #endif

    if(!g_iUserHasMolotov[iPlayer])
        return;

    set_pev(iPlayer, pev_viewmodel2, GRENADE_VIEW_MODEL);
    set_pev(iPlayer, pev_weaponmodel2, GRENADE_PLAYER_MODEL);
}

public CGrenade__Item_AddToPlayer_Post(iItem, iPlayer) p_set_user_weaponlist(iPlayer, g_iUserHasMolotov[iPlayer]);
public FM_Hook_SetModel_Pre(iEntity, const szModel[])
{
    if(!pev_valid(iEntity))
        return FMRES_IGNORED;

    static Float: flDamageTime; pev(iEntity, pev_dmgtime, flDamageTime);
    
    if(flDamageTime == 0.0)
        return FMRES_IGNORED;

    static iOwner; iOwner = pev(iEntity, pev_owner);

    if(equal(szModel, "models/w_smokegrenade.mdl"))
    {
        if(g_iUserHasMolotov[iOwner])
        {
            engfunc(EngFunc_SetModel, iEntity, GRENADE_WORLD_MODEL);
            
            set_pev(iEntity, pev_body, GRENADE_MODEL_BODY);
            set_pev(iEntity, pev_flTimeStepSound, NADE_TYPE);

            g_iUserHasMolotov[iOwner] -= 1;

            return FMRES_SUPERCEDE;
        }
    }

    return FMRES_IGNORED;
}

public CPlayer__MakeWeapon(iPlayer)
{
    static iEntity, g_AllocString_E;

    if(g_AllocString_E || (g_AllocString_E = engfunc(EngFunc_AllocString, WEAPON_OLD)))
        iEntity = engfunc(EngFunc_CreateNamedEntity, g_AllocString_E);

    if(iEntity <= 0)
        return 0;

    //g_iUserHasMolotov[iPlayer]++;
    set_pev(iEntity, pev_spawnflags, SF_NORESPAWN);
    ExecuteHam(Ham_Spawn, iEntity);

    if(!ExecuteHamB(Ham_AddPlayerItem, iPlayer, iEntity))
    {
        set_pev(iEntity, pev_flags, FL_KILLME);
        return 0;
    }

    ExecuteHamB(Ham_Item_AttachToPlayer, iEntity, iPlayer);
    return iEntity;
}

stock p_set_user_weaponlist(iPlayer, iList) {
    message_begin(MSG_ONE, get_user_msgid("WeaponList"), _, iPlayer);
    write_string(iList ? WEAPON_NEW : WEAPON_OLD);
    write_byte(iWeaponList[0]);
    write_byte(iWeaponList[1]);
    write_byte(iWeaponList[2]);
    write_byte(iWeaponList[3]);
    write_byte(iWeaponList[4]);
    write_byte(iWeaponList[5]);
    write_byte(iWeaponList[6]);
    write_byte(iWeaponList[7]);
    message_end();
}
в логике небольшая ошибка, чек
 
Сообщения
509
Реакции
111
Помог
16 раз(а)
BalbuR, Спасибо работает,но баг получается воспроизвести если купить больше 1 молотова,то в след раунде он появляется вместо дыма,но ровно на столько раундов ,сколько ты купил молотовых до этого. Возможно добавить ограничение на покупку скажем 1 как в старом плагине молотова?


Код:
/*******************************************************************************
*
*   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 version 3.
*
*   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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*
*   In addition, as a special exception, the author gives permission to
*   link the code of this program with the Half-Life Game Engine ("HL
*   Engine") and Modified Game Libraries ("MODs") developed by Valve,
*   L.L.C ("Valve"). You must obey the GNU General Public License in all
*   respects for all of the code used other than the HL Engine and MODs
*   from Valve. If you modify this file, you must extend this exception
*   to your version of the file.
*
********************************************************************************


            Molotov Cocktail
              Version 3.30
        Maintained by: DynamicBits (Andy)

* Commands:
- say molotov - Buy a Molotov
- say /molotov - Buy a Molotov
- molotov_give <player|@all|@t|@ct|@al|@ax|@br|@b|@r|@y|@g> - Give Molotov(s) to a player, a team, or everyone
- molotov_cocktail [0|1] - Enable/disable the plugin (If no arguments, show the status)
- molotov_override [0|1] - Enable/disable the standard grenade override (If no arguments, show the status)

* Cvars
- molotov_enabled <0|1> - (Default: 1) Enable(1)/disable(0) the plugin
- molotov_price <N> - (Default: 300) Set the Molotov price (Counter-Strike only)
- molotov_damage <N> - (Default: 50.0) Set the damage done by initial Molotov explosion
- molotov_radius <N> - (Default: 150.0) Set the radius of Molotov damage
- molotov_firetime <N> - (Default: 6) Duration (in seconds) of fire effects, sounds, etc.
- molotov_firedamage <N> - (Default: 3) Amount of damage done by fire effects (every 0.2 secs)
- molotov_ff <0|1|-1|-2> - (Default: 1) Set Molotov friendly fire status (Was molotov_tk)
  *  0 - Disable friendly fire for Molotovs (regardless of mp_friendlyfire)
  *  1 - Enable friendly fire for Molotovs (regardless of mp_friendlyfire)
  * -1 - Use mp_friendlyfire value
  * -2 - Check bit 5 (decimal: 16) of mp_teamplay (DOD and TFC only)
- molotov_override_he <0|1> - (Default: 0) Override the mod's standard grenade automatically with Molotov (Was molotov_tempoverride)
- molotov_max <N> - (Default: 1) Limit carried Molotovs to this amount
  * (Recommended: CSTRIKE: ≤ 10; DOD: ≤ 9; TFC: ≤ 4;)
- molotov_buyzone <0|1> - (Default: 1) Limit Molotov buying to buyzone (Counter-Strike only)
- molotov_menu <0|1> - (Default: 0) Enable menu at beginning of each round (Was amx_molotovmenu)

* Required Modules:
- Fakemeta
- Cstrike (Counter-Strike only)
- Csx (Counter-Strike only)
- Dodfun (Day of Defeat only)
- Dodx (Day of Defeat only)
- Tfcx (Team Fortress Classic only)
- Engine (TFC with debugging only)

* Changelog/Credit:
- DynamicBits
  * Version 3.30 (2014-04-13)
    - (Beta) Day of Defeat support was added
    - (Beta) Team Fortress Classic support was added
    - (Untested) Stats logging support was added
    - New values for molotov_ff were added
    - Friction/velocity after explosion was adjusted for realism
    - Bottle breaking sound was added
    - Molotov suicides no longer reward extra score points
    - Frag count calculations were fixed
    - Money calculations were fixed
    - Override no longer sets a negative number of Molotovs
    - Default price was changed to match standard grenades
    - Console text now goes to correct console
    - Text (non-VGUI) buy menu support was removed
    - Converted fun functions to fakemeta_util functions (removed fun include)
    - Various optimizations
    - A few typos were fixed
  * Version 3.20 (2008-11-20)
    - My first public release
    - Finally tracked down and fixed the intermittent crashing problem (I hope!)
    - Modified default damage values
    - molotov_cocktail/molotov_override commands now change settings *or* display status
    - Broken Molotov model stays closer to the explosion (looks more realistic)
    - Task IDs are now guaranteed to be unique
    - Modified anti-lag calculations to be more accurate (less likely to lag)
    - Changed amx_molotovmenu CVAR to molotov_menu
    - Changed molotov_tk CVAR to molotov_ff
    - Changed molotov_tempoverride CVAR to molotov_override_he
    - Preparation for support of mods other than Counter-Strike
    - Fixed lots of coding mistakes
    - Optimized several sections of code
    - Corrected grammar/typos
    - Clean up code (Removed unused code/unhelpful comments, fixed formatting, and semicolons!)
- Raffe (CantShoot)
  * (Unversioned release)
    - Originally fixed plugin to run on Linux servers
    - Added optional menu to purchase Molotov cocktails each round
    - Moved models and sounds into proper molotov/ subdirectories
    - Fixed Molotovs not being reset upon player disconnect
    - (Almost) fixed Molotovs not being removed for new round
    - Added @all/@ct/@t arguments to molotov_give command
    - Changed some models/sound
- [ --<-@ ] Black Rose
  * Version 3.0-3.1c ?
    - Unknown changes
- SAMURAI
  * Original plugin author


*/

#pragma semicolon 1

// Uncomment only the define that applies to your mod
#define CSTRIKE
//#define DOD
//#define TFC

// Uncomment the following line to enable debug logging.
//#define MOLOTOV_DEBUG


#include <amxmodx>
#include <amxmisc>
//#include <fakemeta>        // (runtime only)
#include <fakemeta_util>
#if defined CSTRIKE
    #include <cstrike>
    #include <csx>        // Used only for custom_weapon_* functions
#endif
#if defined DOD
    #include <dodfun>
    #include <dodx>        // Used only for custom_weapon_* functions
#endif
#if defined TFC
    #include <tfcx>
    #include <engine>    // Used only with debugging enabled
#endif

// If you really want to same some memory and know you won't have 32 players, you can change this.
#define MAX_PLAYERS        32
#define ADMIN_ACCESS        ADMIN_KICK

#define ANTI_LAGG        7    // Defines max calculations before a flame is spawned without check if on ground
// This is to prevent lag at really narrow ents where you could end up with 400 calculations per flame. Suggested: <= 10

#define MOLOTOV_HARD_LIMIT    10    // Maximum Molotov cocktails this code is capable of handling without bugs (per player)

#define ID_TO_INDEX(%0)        %0 - 1    // Use this macro rather than dim the arrays with 33

#define MOLOTOV_MENU_KEYS MENU_KEY_0|MENU_KEY_1|MENU_KEY_2            // Choices to look for with optional menu

// Task IDs
#define MOLOTOV_TASKID_RESET     1000                        // Set g_bReset to false after a short delay (task is TFC only)
#define MOLOTOV_TASKID_OFFSET    MOLOTOV_HARD_LIMIT
// These task IDs are dynamically set per-Molotov
#define MOLOTOV_TASKID_BASE1    2000                        // By default, with 32 players, task ids
#define MOLOTOV_TASKID_BASE2    MOLOTOV_TASKID_BASE1 + (MOLOTOV_TASKID_OFFSET * MAX_PLAYERS) // from 2000 to 2959 can
#define MOLOTOV_TASKID_BASE3    MOLOTOV_TASKID_BASE2 + (MOLOTOV_TASKID_OFFSET * MAX_PLAYERS) // potentially be used used.

#define TEAM_UNASSIGNED     0
#define TEAM_ONE        1
#define TEAM_TWO        2
#define TEAM_THREE        3
#define TEAM_FOUR        4
#define MC_TFC_PC_CIVILIAN    11    // Temporary workaround for AMXX bug 6042


new const g_PLUGIN[]  = "Molotov Cocktail";
new const g_AUTHORS[] = "DynamicBits";
new const g_VERSION[] = "3.30";

new pEnabled;                // Pointer to molotov_enabled
new pMlDamage;                // Pointer to molotov_damage
new pMlRadius;                // Pointer to molotov_radius
new pFireTime;                // Pointer to molotov_firetime
new pOverride;                // Pointer to molotov_override_he
new pMFF;                // Pointer to molotov_ff
new pFriendlyFire;            // Pointer to mp_friendlyfire
new pFireDmg;                // Pointer to molotov_firedamage
new pMaxMolotovs;            // Pointer to molotov_max
#if defined DOD || defined TFC
new pTeamPlay;                // Pointer to mp_teamplay
#endif
#if defined CSTRIKE
new pBuyZone;                // Pointer to molotov_buyzone
new pMolotovMenu;            // Pointer to molotov_menu
new pPrice;                // Pointer to molotov_price

new g_msgScoreInfo;            // ScoreInfo message ID
#endif
new g_msgDeathMsg;            // DeathMsg message ID

new g_NumMolotov[MAX_PLAYERS];        // How many Molotovs each player has
#if defined CSTRIKE
new bool:g_bRestarted;            // Reset Molotovs after first round restart
#endif
new g_MaxPlayers;            // Max players (calculated at runtime to make loops more efficient)
new g_wpnMolotov;            // Custom weapon ID
new bool:g_bReset;            // Reset and stop explosions after round ends; Stop reset_tasks() from getting called once per player

new g_iFireSprite, g_iSmokeSprite[2];    // Handles to the precached sprites
new g_iMolotovOffset[MAX_PLAYERS];    // Offset used for a player's task ID

// The Pawn compiler does not optimize the DATA section. Any string that appears multiple times should be optimized with a global constant.
//   *Unused* constants do not affect the compiled size, however they create compiler warnings. (#pragma unused is a quick fix for the warnings.)
new const EVENT_ROUND_END[] = "event_round_end";
#if defined CSTRIKE
new const BUY_MOLOTOV[] = "buy_molotov";
new const WEAPON_HEGRENADE[] = "weapon_hegrenade";
#endif
#if defined DOD
new const WEAPON_HANDGRENADE[] = "weapon_handgrenade";
new const WEAPON_STICKGRENADE[] = "weapon_stickgrenade";
#endif
#if defined DOD || defined TFC
new const HUDTEXT[] = "HudText";
#endif
#if defined CSTRIKE || defined TFC
new const TEXTMSG[] = "TextMsg";
#endif

// Check for outdated tfcconst.inc file (and likely outdated AMX Mod X core/modules).
//   My patch for AMXX bug 6042 was accepted, but I think I'll wait for a new final release of AMXX to enable this check.
//   In the meantime, I created the MC_TFC_PC_CIVILIAN define.
//#if defined TFC && TFC_PC_CIVILIAN != 11    // TFC_PC_CIVILIAN was (incorrectly) 10 in older versions
//    #error TFC_PC_CIVILIAN != 11. Update your tfcconst.inc include file. Get the latest AMX Mod X snapshots at www.amxmodx.org/snapshots.php
//#endif


// Initialize the plugin
public plugin_init() {

    register_plugin(g_PLUGIN, g_VERSION, g_AUTHORS);
    server_print("[MC] ---- Molotov Cocktail %s loaded ----", g_VERSION);
    register_cvar("MolotovCocktail", g_VERSION, FCVAR_SERVER|FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);

#if defined CSTRIKE
    register_menucmd(register_menuid("Buy Molotov Cocktail"), MOLOTOV_MENU_KEYS, "giveMolotov");

    #if defined MOLOTOV_DEBUG
    register_clcmd("molotov_menutest", "show_molotov_menu");
    #endif

    register_clcmd("say /molotov", BUY_MOLOTOV);
    register_clcmd("say molotov", BUY_MOLOTOV);
#endif
    register_concmd("molotov_give", "molotov_give", ADMIN_ACCESS, "<player|@all|@t|@ct|@al|@ax|@br|@b|@r|@y|@g> - Give free Molotov cocktails");
    register_concmd("molotov_override", "molotov_override", ADMIN_ACCESS, "[0|1] - Enable/disable the standard grenade override (If no arguments, show the status)");
    register_concmd("molotov_cocktail", "molotov_cocktail", ADMIN_ACCESS, "[0|1] - Enable/disable the plugin (If no arguments, show the status)");

    pEnabled = register_cvar("molotov_enabled", "1", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
    pOverride = register_cvar("molotov_override_he", "0", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
    pMlDamage = register_cvar("molotov_damage", "50.0", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
    pMlRadius = register_cvar("molotov_radius", "150.0", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
    pFireTime = register_cvar("molotov_firetime", "6", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
    pFireDmg = register_cvar("molotov_firedamage", "3", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
    pMFF = register_cvar("molotov_ff", "1", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
    pMaxMolotovs = register_cvar("molotov_max", "1", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
    pFriendlyFire = register_cvar("mp_friendlyfire", "0", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
#if defined CSTRIKE
    pBuyZone = register_cvar("molotov_buyzone", "1", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
    pMolotovMenu = register_cvar("molotov_menu", "0", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
    pPrice = register_cvar("molotov_price", "300", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
#endif
#if defined DOD
    pTeamPlay = register_cvar("mp_teamplay", "0", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
#endif
#if defined TFC
    pTeamPlay = register_cvar("mp_teamplay", "21", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
#endif

    register_event("DeathMsg", "event_deathmsg", "a", "2>0");    // For some reason, arg2 (Victim) is sometimes -1 (at least in TFC on Windows HLDS with FoxBot).
#if defined CSTRIKE || defined DOD
    register_event("CurWeapon", "event_curweapon", "be", "1=1");
    register_event("HLTV", "event_new_round", "a", "1=0", "2=0");    // cstrike/dod new round; So far, I haven't found a TFC equivalent
#endif
#if defined CSTRIKE
    register_event(TEXTMSG, "event_gamerestart", "a", "2=#Game_Commencing", "2=#Game_will_restart_in");
#endif
#if defined DOD
    register_event(HUDTEXT, EVENT_ROUND_END, "b", "1&VICTORY");    // Sent once per player on round end
#endif
#if defined TFC
    // Since TFC doesn't have any generic end of round event/message, specific messages need to be caught for certain maps.
    //   Maps that don't have traditional rounds (2fort, badlands, casbah, crossover2, cz2, ravelin, skate2, well, etc.) don't apply here.
    //   All of the default maps are accounted for. If there is demand for specific custom maps, I will add the appropriate message(s).
    new sCurrentMap[32];
    get_mapname(sCurrentMap, charsmax(sCurrentMap));
    if (!strcmp(sCurrentMap, "avanti")) {
        register_event(HUDTEXT, EVENT_ROUND_END, "b", "1=#italy_endround_win");        // Was Avanti originally called Italy?
    } else if ((!strcmp(sCurrentMap, "dustbowl")) || (!strcmp(sCurrentMap, "castleargh")) || (!strcmp(sCurrentMap, "castleargh2"))) {
        register_event(TEXTMSG, EVENT_ROUND_END, "b", "2=#dustbowl_blue_secures_one");    // Technically these are "stages," not "rounds"
        register_event(TEXTMSG, EVENT_ROUND_END, "b", "2=#dustbowl_blue_secures_two");
        //register_event(TEXTMSG, "event_round_end", "b", "2=#dustbowl_blue_caps");    // The map ends after this cap
    } else if (!strcmp(sCurrentMap, "epicenter")) {
        register_event(HUDTEXT, EVENT_ROUND_END, "b", "1=#dblmint_you_capped_flag");    // dblmint?!
    } else if (!strcmp(sCurrentMap, "flagrun")) {
        register_event(HUDTEXT, EVENT_ROUND_END, "b", "1&you won this round!");
    } else if (!strcmp(sCurrentMap, "hunted")) {
        register_event(TEXTMSG, EVENT_ROUND_END, "b", "2=#hunted_target_killed");
    } else if (!strcmp(sCurrentMap, "push")) {
        register_event(TEXTMSG, EVENT_ROUND_END, "b", "2&_netname_scores");
    } else if (!strcmp(sCurrentMap, "rock2")) {
        register_event(HUDTEXT, EVENT_ROUND_END, "b", "1=1 . . .^n");
    } else if (!strcmp(sCurrentMap, "warpath")) {
        register_event(HUDTEXT, EVENT_ROUND_END, "b", "1=#warpath_red_wins");
        register_event(HUDTEXT, EVENT_ROUND_END, "b", "1=#warpath_blue_wins");
    // ---------- Custom Maps ----------
    } else if (!strcmp(sCurrentMap, "castleargh3")) {
        register_event(HUDTEXT, EVENT_ROUND_END, "b", "1&You have secured");        // This works for all four stages
    } else if (!strcmp(sCurrentMap, "hwguyz2")) {
        register_event(HUDTEXT, EVENT_ROUND_END, "b", "1=#2fort_you_capped_flag");
        register_event(HUDTEXT, EVENT_ROUND_END, "b", "1&Time Ran Out");
    }

    //#if defined MOLOTOV_DEBUG
    //register_event(TEXTMSG, "event_textmsg_a", "a");
    //register_event(TEXTMSG, "event_textmsg_b", "b");
    //register_event(HUDTEXT, "event_hudtext_a", "a");
    //register_event(HUDTEXT, "event_hudtext_b", "b");
    //#endif
#endif

#if defined CSTRIKE
    register_logevent(EVENT_ROUND_END, 2, "1=Round_End");
#endif

    register_forward(FM_EmitSound, "fw_emitsound");
#if defined TFC
    register_forward(FM_SetModel, "fw_setmodel_post", 1);
#endif

    g_MaxPlayers = get_maxplayers();

#if defined CSTRIKE
    g_msgScoreInfo = get_user_msgid("ScoreInfo");
#endif
    g_msgDeathMsg = get_user_msgid("DeathMsg");

    g_wpnMolotov = custom_weapon_add("molotov", 0, "molotov");    // I can hardly find any documentation or sample code for this. I have no
                                    //   idea if I'm using it correctly or not. I'm not even sure what it affects.
}

// These are primarily for catching messages in TFC to add custom round end triggers.
/*
#if defined MOLOTOV_DEBUG
public event_textmsg_a() {
    new sArg2[64];
    read_data(2, sArg2, charsmax(sArg2));
    
    client_print(0, print_chat, "event_textmsg_a 1(%d) 2(%s)", read_data(1), sArg2);
    console_print(0, "event_textmsg_a 1(%d) 2(%s)", read_data(1), sArg2);
}

public event_textmsg_b() {
    new sArg2[64];
    read_data(2, sArg2, charsmax(sArg2));

    client_print(0, print_chat, "event_textmsg_b 1(%d) 2(%s)", read_data(1), sArg2);
    console_print(0, "event_textmsg_b 1(%d) 2(%s)", read_data(1), sArg2);
}

public event_hudtext_a() {
    new sArg1[64];
    read_data(1, sArg1, charsmax(sArg1));

    client_print(0, print_chat, "event_hudtext_a 1(%s) 2(%d)", sArg1, read_data(1));
    console_print(0, "event_hudtext_a 1(%s) 2(%d)", sArg1, read_data(1));
}

public event_hudtext_b() {
    new sArg1[64];
    read_data(1, sArg1, charsmax(sArg1));

    client_print(0, print_chat, "event_hudtext_b 1(%s) 2(%d)", sArg1, read_data(1));
    console_print(0, "event_hudtext_b 1(%s) 2(%d)", sArg1, read_data(1));
}
#endif
*/

// Precache models and sound(s)
public plugin_precache() {

    g_iFireSprite = precache_model("sprites/flame.spr");

    g_iSmokeSprite[0] = precache_model("sprites/black_smoke3.spr");
#if defined DOD
    g_iSmokeSprite[1] = g_iSmokeSprite[0];            // steam1.spr shows a black background in dod
#else
    g_iSmokeSprite[1] = precache_model("sprites/steam1.spr");
#endif

#if defined CSTRIKE || defined DOD
    precache_model("models/molotov/p_molotov.mdl");
    precache_model("models/molotov/v_molotov.mdl");
#endif
    precache_model("models/molotov/w_molotov.mdl");
    precache_model("models/molotov/w_broke_molotov.mdl");

    precache_sound("molotov/molotov_fire.wav");

}

// Reset Molotovs so that a new player doesn't have any
public client_disconnect(id) {
    g_NumMolotov[ID_TO_INDEX(id)] = 0;
}

// Catch the first impact of the Molotov and start the sound/explosion
//   A Molotov cocktail should "explode" on impact, not after a set time.
public fw_emitsound(ent, channel, sample[]) {
#if defined CSTRIKE
    if (equal(sample[8], "he_bounce", 9)) {
#else
    #if defined DOD || defined TFC
    // DOD: debris/bustglass2.wav and debris/bustglass1.wav are played for breaking glass, but ent is not the grenade, so Molotovs "disappear" (This is a bug in this plugin)
    //   A fix would be to use FM_Touch or Ham_Touch or register_touch instead of FM_EmitSound
    if (equal(sample[8], "grenade_hit", 11)) {
    #endif
#endif

        new sModel[32];
        pev(ent, pev_model, sModel, charsmax(sModel));

        // Depending on where the Molotov lands, the EmitSound forward may get called 50+ times.
        // After the first hit, the model is changed to w_broke_molotov, so this code is skipped on successive calls
        if (equal(sModel[15], "w_molotov.mdl")) {
#if defined TFC
            set_pev(ent, pev_nextthink, 99999.0);        // For TFC, this is about the only way I can stop the explosion.
#endif
            // The glass breaking sound has a low range (ATTN_STATIC) so as not to be overpowering
            emit_sound(ent, CHAN_AUTO, "debris/glass2.wav", VOL_NORM, ATTN_STATIC, 0, PITCH_LOW);

            new Float:fFriction, Float:fVelocity[3];
            pev(ent, pev_friction, fFriction);
            fFriction *= 1.15;                // Increase friction to make it look more realistic
            set_pev(ent, pev_friction, fFriction);

            pev(ent, pev_velocity, fVelocity);
            fVelocity[0] *= 0.3;                // Decrease velocity because friction doesn't do it all
            fVelocity[1] *= 0.3;
            fVelocity[2] *= 0.3;
            set_pev(ent, pev_velocity, fVelocity);

            molotov_explode(ent);                // Replacement for normal grenade explosion

            return FMRES_SUPERCEDE;
        } else if (equal(sModel[15], "w_broke_molotov.")) {    // "mdl" is truncated because of the array size, which is OK
            return FMRES_SUPERCEDE;            // Don't play any sounds for bounces.
        }
    }
    
    return FMRES_IGNORED;
}

// Since TFC handles grenades differently, this is roughly equivalant to event_curweapon() used by cstrike and dod.
#if defined TFC
public fw_setmodel_post(ent, const model[]) {
    if (!pev_valid(ent)) {            // Check if it's a valid entity to prevent errors
        return FMRES_IGNORED;
    }

    new sClassname[32];
    pev(ent, pev_classname, sClassname, charsmax(sClassname));

    if (!get_pcvar_num(pEnabled) || !equal(sClassname, "normalgrenade")) {
        return FMRES_IGNORED;
    }

    new iOwner = pev(ent, pev_owner);
    if (!g_NumMolotov[ID_TO_INDEX(iOwner)] && !get_pcvar_num(pOverride)) {    // If no Molotovs and override is disabled, return
        return FMRES_IGNORED;
    }
    
    if (g_NumMolotov[ID_TO_INDEX(iOwner)] > 0) {    // Prevent negative values
        g_NumMolotov[ID_TO_INDEX(iOwner)]--;
    }

    set_pev(ent, pev_team, get_user_team(iOwner));
    custom_weapon_shot(g_wpnMolotov, iOwner);

    engfunc(EngFunc_SetModel, ent, "models/molotov/w_molotov.mdl");

    return FMRES_HANDLED;
}
#endif

// When the player changes weapons to the Molotov, update the model
#if defined CSTRIKE || defined DOD
public event_curweapon(id) {

    if (!get_pcvar_num(pEnabled) || !is_user_alive(id)) {
        return PLUGIN_CONTINUE;
    }

    if (!g_NumMolotov[ID_TO_INDEX(id)] && !get_pcvar_num(pOverride)) {    // If no Molotovs and override is disabled, return
        return PLUGIN_CONTINUE;
    }

    new iWeaponID = get_user_weapon(id, _, _);
#if defined CSTRIKE
    if (iWeaponID != CSW_HEGRENADE) {
#else                            // elseif *should* work, but there is a bug in the compiler
    #if defined DOD
    // current weapon is never set to DODW_MILLS_BOMB in this event; only DODW_HANDGRENADE/DODW_STICKGRENADE
    if ((iWeaponID != DODW_HANDGRENADE) && (iWeaponID != DODW_STICKGRENADE)) {
    #endif
#endif
        return PLUGIN_CONTINUE;
    }

    set_pev(id, pev_viewmodel2, "models/molotov/v_molotov.mdl");    // View model (First person)    *model2 doesn't require allocating the string
    set_pev(id, pev_weaponmodel2, "models/molotov/p_molotov.mdl");    // Player model (Third person)

#if defined DOD
    // I think 3 is correct, but it looks strange..
    set_pev(id, pev_weaponanim, 3);    // 0: "idle"; 1: "pullpin"; 2: "throw"; 3: "deploy"
#endif

    return PLUGIN_CONTINUE;
}
#endif

// Reset Molotovs on death
public event_deathmsg() {
#if defined MOLOTOV_DEBUG
    log_amx("[MC] ========== DeathMsg ========== K(%d) V(%d)", read_data(1), read_data(2));
#endif
    g_NumMolotov[ID_TO_INDEX(read_data(2))] = 0;
}

// cstrike only
#if defined CSTRIKE
public event_gamerestart() {
#if defined MOLOTOV_DEBUG
    log_amx("[MC] ========== Game Restart ==========");
#endif
    g_bRestarted = true;
}
#endif

// cstrike, dod, and tfc will all call this once per player on round end
public event_round_end() {
#if defined MOLOTOV_DEBUG
    log_amx("[MC] ========== Round End ==========");
#endif

    if (g_bReset == false) {
        reset_tasks();
        g_bReset = true;
#if defined TFC
        set_task(2.0, "cancel_reset", MOLOTOV_TASKID_RESET);    // TFC won't call event_new_round, so do that stuff here instead
#endif
    }
}

// cstrike and dod will call this once per round, but TFC won't.
#if defined CSTRIKE || defined DOD
public event_new_round(id) {
#if defined MOLOTOV_DEBUG
    log_amx("[MC] ========== New Round ==========");
#endif
    g_bReset = false;    // Stop blocking

    if (!get_pcvar_num(pEnabled)) {
        return PLUGIN_CONTINUE;
    }

    reset_tasks();                        // This probably isn't needed anymore, but it shouldn't hurt anything

#if defined CSTRIKE
    if (get_pcvar_num(pMolotovMenu)) {
        if (get_pcvar_num(pOverride)) {
            client_print(id, print_center, "Molotov cocktails will replace purchased HE grenades");
        } else {
            show_molotov_menu(id);
        }
    }

    // For cstrike only, make sure the player didn't quickly purchase a Molotov before the first actual round
    if (g_bRestarted) {
        arrayset(g_NumMolotov, 0, sizeof(g_NumMolotov));    // Reset everyone to zero Molotovs
        g_bRestarted = false;
    }
#endif

    if (get_pcvar_num(pOverride)) {
        set_molotovs();
    } else {
        reset_molotovs();
    }

    return PLUGIN_CONTINUE;
}
#endif

// Enable/Disable/Get status of override
public molotov_override(id, level, cid) {

    if (!cmd_access(id, level, cid, 1)) {        // First argument (passed to molotov_override) is optional
        return PLUGIN_HANDLED;
    }

    if (!get_pcvar_num(pEnabled)) {
        return PLUGIN_HANDLED;
    }

    if (read_argc() == 1) {                // No arguments; Display status
        console_print(id, "Override is currently %s.", get_pcvar_num(pOverride) ? "enabled" : "disabled");
        return PLUGIN_HANDLED;
    }

    new sArg[2];
    read_argv(1, sArg, charsmax(sArg));

    new iArg = str_to_num(sArg);

    if ((iArg < 0) || (iArg > 1) || (!isdigit(sArg[0]))) {    // If less than 0 or greater than 1 or not a digit
        console_print(id, "Invalid argument(%s). Valid arguments are ^"0^" and ^"1^".", sArg);
        return PLUGIN_HANDLED;
    }

    if (iArg == get_pcvar_num(pOverride)) {
        console_print(id, "Override is already %s.", iArg ? "enabled" : "disabled");
        return PLUGIN_HANDLED;
    }

    set_pcvar_num(pOverride, iArg);
    console_print(id, "Override was %s.", iArg ? "enabled" : "disabled");

#if defined CSTRIKE || defined DOD
    if (iArg) {                    // If plugin is enabled (checked above) and override is enabled, set models to Molotov
        set_molotovs();
    } else {
        reset_molotovs();
    }
#endif

    return PLUGIN_HANDLED;
}

// Enable/Disable/Get status of plugin
public molotov_cocktail(id, level, cid) {
    if (!cmd_access(id, level, cid, 1)) {        // First argument (passed to molotov_cocktail) is optional
        return PLUGIN_HANDLED;
    }

    if (read_argc() == 1) {                // No arguments; Display status
        console_print(id, "Plugin is currently %s. (Override:%d; MFF:%d)", get_pcvar_num(pEnabled) ? "enabled" : "disabled", get_pcvar_num(pOverride), get_pcvar_num(pMFF));
        return PLUGIN_HANDLED;
    }

    new sArg[2];
    read_argv(1, sArg, charsmax(sArg));

    new iArg = str_to_num(sArg);

    if ((iArg < 0) || (iArg > 1) || (!isdigit(sArg[0]))) {    // If less than 0 or greater than 1 or not a digit
        console_print(id, "Invalid argument(%s). Valid arguments are ^"0^" and ^"1^".", sArg);
        return PLUGIN_HANDLED;
    }

    if (iArg == get_pcvar_num(pEnabled)) {
        console_print(id, "Plugin is already %s.", iArg ? "enabled" : "disabled");
        return PLUGIN_HANDLED;
    }

    set_pcvar_num(pEnabled, iArg);
    console_print(id, "Plugin was %s.", iArg ? "enabled" : "disabled");

#if defined CSTRIKE || defined DOD
    if (iArg && get_pcvar_num(pOverride)) {        // If the plugin was enabled and override is enabled, set models to Molotov
        set_molotovs();
    } else {
        reset_molotovs();
    }
#endif

    return PLUGIN_HANDLED;
}

// Handle molotov_give console command
public molotov_give(id, level, cid) {
    if (!cmd_access(id, level, cid, 2)) {
        return PLUGIN_HANDLED;
    }

    new sArg1[16], iTarget;
    read_argv(1, sArg1, charsmax(sArg1));
#if defined MOLOTOV_DEBUG
    log_amx("[MC] molotov_give sArg1[0](%s)", sArg1[0]);
#endif
    new sAdmin[32];
    get_user_name(id, sAdmin, charsmax(sAdmin));
    new iGiveAmount = (get_pcvar_num(pMaxMolotovs) < MOLOTOV_HARD_LIMIT ? get_pcvar_num(pMaxMolotovs) : MOLOTOV_HARD_LIMIT);

    if (sArg1[0] == '@') {

        new iTargetTeam, sTeamName[32];
        new Players[MAX_PLAYERS], iNum;

        if (equali(sArg1[1], "all")) {
            iTargetTeam = 0;
        } else if (equali(sArg1[1], "t") || equali(sArg1[1], "al") || equali(sArg1[1], "br") || equali(sArg1[1], "b")) {    // CS_TEAM_T or ALLIES/British or Blue
            iTargetTeam = TEAM_ONE;
        } else if (equali(sArg1[1], "ct") || equali(sArg1[1], "ax") || equali(sArg1[1], "r")) {    // CS_TEAM_CT or AXIS or Red
            iTargetTeam = TEAM_TWO;
#if defined TFC
        } else if (equali(sArg1[1], "y")) {    // Yellow
            iTargetTeam = TEAM_THREE;
        } else if (equali(sArg1[1], "g")) {    // Green
            iTargetTeam = TEAM_FOUR;
#endif
        }

        get_players(Players, iNum, "ach");    // alive, no bots, no HLTV

        for (new i = 0; i < iNum; ++i) {
            iTarget = Players[i];

            if ((iTargetTeam == 0) || (get_user_team(iTarget) == iTargetTeam)) {
                g_NumMolotov[ID_TO_INDEX(iTarget)] = iGiveAmount;

#if defined CSTRIKE
                fm_give_item(iTarget, WEAPON_HEGRENADE);
                cs_set_user_bpammo(iTarget, CSW_HEGRENADE, iGiveAmount);
#endif
#if defined DOD
                // TODO - This sets the count, but it is not immediately updated on the HUD
                switch(get_user_team(iTarget)) {
                    case ALLIES: {    // (or British)
                        fm_give_item(iTarget, WEAPON_HANDGRENADE);
                        dod_set_user_ammo(iTarget, DODW_HANDGRENADE, iGiveAmount);
                    }
                    case AXIS: {
                        fm_give_item(iTarget, WEAPON_STICKGRENADE);
                        dod_set_user_ammo(iTarget, DODW_STICKGRENADE, iGiveAmount);
                    }
                }
#endif
#if defined TFC
                new iClass = pev(iTarget, pev_playerclass);
                if ((iClass > 0) && (iClass != TFC_PC_SCOUT) && (iClass != MC_TFC_PC_CIVILIAN)) {    // No unselected/spectator, scout, or civilian
                    tfc_setbammo(iTarget, TFC_AMMO_NADE1, iGiveAmount);    // Requires 1.8.3-dev-hg185 or newer
                }
#endif
#if defined CSTRIKE
                emit_sound(iTarget, CHAN_WEAPON, "items/gunpickup2.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);
#endif
#if defined DOD
                emit_sound(iTarget, CHAN_WEAPON, "items/ammopickup.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);    // "items/weaponpickup.wav" could work too, I suppose
#endif
#if defined TFC
                // Shotgun pumping sound for picking up grenades... That's how TFC does it!
                emit_sound(iTarget, CHAN_WEAPON, "weapons/scock1.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);   
#endif
            }
        }

        switch(iTargetTeam) {
            case 0: {
                sTeamName = "everyone";
            }
            case TEAM_ONE: {
#if defined CSTRIKE
                sTeamName = "all terrorists";
#endif
#if defined DOD
                sTeamName = "all allies";    // TODO - Allies or British
#endif
#if defined TFC
                sTeamName = "all blue";        // I *could* pull the team1_name value from the info_tfdetect entity (but only for some maps?)
#endif
            }
            case TEAM_TWO: {
#if defined CSTRIKE
                sTeamName = "all ct's";
#endif
#if defined DOD
                sTeamName = "all axis";
#endif
#if defined TFC
                sTeamName = "all red";
            }
            case TEAM_THREE: {
                sTeamName = "all yellow";
            }
            case TEAM_FOUR: {
                sTeamName = "all green";
#endif
            }
        }
        client_print(0, print_chat, "ADMIN %s has given %s %d Molotov cocktails!", sAdmin, sTeamName, iGiveAmount);

    } else {

        iTarget = cmd_target(id, sArg1, 6);

        if (!is_user_connected(iTarget) || !is_user_alive(iTarget)) {
            return PLUGIN_HANDLED;
        }

        g_NumMolotov[ID_TO_INDEX(iTarget)] = iGiveAmount;

#if defined CSTRIKE
        fm_give_item(iTarget, WEAPON_HEGRENADE);
        cs_set_user_bpammo(iTarget, CSW_HEGRENADE, iGiveAmount);
#endif
#if defined DOD
        switch(get_user_team(iTarget)) {
            case ALLIES: {    // (or British)
                fm_give_item(iTarget, WEAPON_HANDGRENADE);
                dod_set_user_ammo(iTarget, DODW_HANDGRENADE, iGiveAmount);
            }
            case AXIS: {
                fm_give_item(iTarget, WEAPON_STICKGRENADE);
                dod_set_user_ammo(iTarget, DODW_STICKGRENADE, iGiveAmount);
            }
        }
#endif
#if defined TFC
        new iClass = pev(iTarget, pev_playerclass);
        if ((iClass > 0) && (iClass != TFC_PC_SCOUT) && (iClass != MC_TFC_PC_CIVILIAN)) {    // No unselected/spectator, scout, or civilian
            tfc_setbammo(iTarget, TFC_AMMO_NADE1, iGiveAmount);                // Requires 1.8.3-dev-hg185 or newer
        }
#endif
#if defined CSTRIKE
                emit_sound(iTarget, CHAN_WEAPON, "items/gunpickup2.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);
#endif
#if defined DOD
                emit_sound(iTarget, CHAN_WEAPON, "items/ammopickup.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);    // "items/weaponpickup.wav" could work too, I suppose
#endif
#if defined TFC
                // Shotgun pumping sound for picking up grenades... That's how TFC does it!
                emit_sound(iTarget, CHAN_WEAPON, "weapons/scock1.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);   
#endif

        client_print(iTarget, print_chat, "ADMIN %s has given you %d Molotov cocktails!", sAdmin, iGiveAmount);

    }
    return PLUGIN_HANDLED;
}

// Handle the /molotov command and molotov menu
#if defined CSTRIKE
public buy_molotov(id) {

    if (!get_pcvar_num(pEnabled)) {
        return PLUGIN_HANDLED;
    }

    //if (get_pcvar_num(pOverride)) {
    //    client_print(id, print_center, "Just buy a HE grenade and get Molotov automatically!");
    //    return PLUGIN_HANDLED;
    //}

    if (!is_user_alive(id)) {
        client_print(id, print_center, "You can't buy Molotov cocktails because you are dead.");
        return PLUGIN_HANDLED;
    }

    if (!cs_get_user_buyzone(id) && get_pcvar_num(pBuyZone)) {
        client_print(id, print_center, "You are not in a buyzone.");
        return PLUGIN_HANDLED;
    }

    new iMoney = cs_get_user_money(id);

    if (iMoney < get_pcvar_num(pPrice)) {
        client_print(id, print_center, "You don't have enough $ to buy a Molotov cocktail.");
        return PLUGIN_HANDLED;
    }

    if (!g_NumMolotov[ID_TO_INDEX(id)] && user_has_weapon(id, CSW_HEGRENADE)) {
        if (get_pcvar_num(pOverride)) {
            g_NumMolotov[ID_TO_INDEX(id)] = cs_get_user_bpammo(id, CSW_HEGRENADE);    // If the user buys one from the VGUI menu with the override enabled, this updates g_NumMolotov
        } else {
            client_print(id, print_center, "You already have an HE Grenade.");
            return PLUGIN_HANDLED;
        }
    }

    if (g_NumMolotov[ID_TO_INDEX(id)] == get_pcvar_num(pMaxMolotovs)) {
        if (g_NumMolotov[ID_TO_INDEX(id)] == 1) {
            client_print(id, print_center, "You already have a Molotov cocktail.");
        } else {
            client_print(id, print_center, "You already have %d Molotov cocktails.", g_NumMolotov[ID_TO_INDEX(id)]);
        }
        return PLUGIN_HANDLED;
    }

    cs_set_user_money(id, iMoney - get_pcvar_num(pPrice));
    fm_give_item(id, WEAPON_HEGRENADE);
    cs_set_user_bpammo(id, CSW_HEGRENADE, ++g_NumMolotov[ID_TO_INDEX(id)]);


    client_print(id, print_chat, "You got a Molotov cocktail!");

    return PLUGIN_HANDLED;
}
#endif

// Just before the grenade is thrown, change the model
#if defined CSTRIKE || defined DOD
public grenade_throw(id, ent, wid) {
#if defined CSTRIKE
    if (!get_pcvar_num(pEnabled) || !is_user_connected(id) || wid != CSW_HEGRENADE) {
#else
    #if defined DOD
    // current weapon can be DODW_MILLS_BOMB in this forward, but not in CurWeapon
    if (!get_pcvar_num(pEnabled) || !is_user_connected(id) || ((wid != DODW_HANDGRENADE) && (wid != DODW_STICKGRENADE) && (wid != DODW_MILLS_BOMB))) {
    #endif
#endif
        return PLUGIN_CONTINUE;
    }

    if (!g_NumMolotov[ID_TO_INDEX(id)] && !get_pcvar_num(pOverride)) {    // If no Molotovs and override is disabled, return
        return PLUGIN_CONTINUE;
    }

    if (g_NumMolotov[ID_TO_INDEX(id)] > 0) {    // Prevent negative values
        g_NumMolotov[ID_TO_INDEX(id)]--;
    }

    engfunc(EngFunc_SetModel, ent, "models/molotov/w_molotov.mdl");
    set_pev(ent, pev_nextthink, 99999.0);

    custom_weapon_shot(g_wpnMolotov, id);

#if defined CSTRIKE    // dod sets the team, cstrike doesn't, TFC sets this in fw_setmodel_post()
    set_pev(ent, pev_team, get_user_team(id));
#endif

#if defined DOD
    //set_pev(id, pev_weaponanim, 0);        // 0:"idle"; 1:"pullpin"; 2:"throw"; 3:"deploy"
#endif

    return PLUGIN_HANDLED;
}
#endif

// Set up the explosion, sound, damage, etc.
molotov_explode(ent) {

    new param[7], iOrigin[3];
    new Float:fOrigin[3];
    new iOwner = pev(ent, pev_owner);
    // The broken bottle may continue to travel, but the fire will be centered around the explosion site, marked by this temporary info_target entity.
    new ent2 = engfunc(EngFunc_CreateNamedEntity, engfunc(EngFunc_AllocString, "info_target"));

    pev(ent, pev_origin, fOrigin);

#if defined MOLOTOV_DEBUG
    log_amx("[MC] molotov_explode ent(%d) owner(%d) ent2(%d) -----", ent, iOwner, ent2);
#endif

    param[0] = ent;
    param[1] = ent2;
    param[2] = iOwner;
    param[3] = pev(ent, pev_team);
    param[4] = iOrigin[0] = floatround(fOrigin[0]);
    param[5] = iOrigin[1] = floatround(fOrigin[1]);
    param[6] = iOrigin[2] = floatround(fOrigin[2]);

    engfunc(EngFunc_SetModel, ent, "models/molotov/w_broke_molotov.mdl");

    random_fire(iOrigin, ent2);
    radius_damage2(iOwner, param[3], fOrigin, get_pcvar_float(pMlDamage), get_pcvar_float(pMlRadius), DMG_BLAST, true);

    // If the round ends because of damage inflicted by the initial blast (in the previous line of code), skip any further Molotov effects.
    // g_bReset may already be set, so it is safe to check it at this point.
    if (g_bReset == true) {
        set_pev(ent, pev_flags, pev(ent, pev_flags) | FL_KILLME);    // Remove the Molotov and later cancel the explosion
        return PLUGIN_HANDLED;
    }

    new Float:FireTime = get_pcvar_float(pFireTime);

    if (++g_iMolotovOffset[ID_TO_INDEX(iOwner)] == MOLOTOV_HARD_LIMIT) {
        g_iMolotovOffset[ID_TO_INDEX(iOwner)] = 0;
    }

    set_task(0.2, "fire_damage", MOLOTOV_TASKID_BASE1 + (MOLOTOV_TASKID_OFFSET * (iOwner - 1)) + g_iMolotovOffset[ID_TO_INDEX(iOwner)], param, 7, "a", floatround(FireTime / 0.2, floatround_floor));
    set_task(1.0, "fire_sound", MOLOTOV_TASKID_BASE2 + (MOLOTOV_TASKID_OFFSET * (iOwner - 1)) + g_iMolotovOffset[ID_TO_INDEX(iOwner)], param, 7, "a", floatround(FireTime) - 1);
    // This task removes the broken Molotov and "info_target" entity once molotov_firetime has expired
    set_task(FireTime, "fire_stop", MOLOTOV_TASKID_BASE3 + (MOLOTOV_TASKID_OFFSET * (iOwner - 1)) + g_iMolotovOffset[ID_TO_INDEX(iOwner)], param, 7);

    return PLUGIN_CONTINUE;
}

// Since there isn't a reliable new round trigger in TFC, a task is created at round end that calls this function after a delay
#if defined TFC
public cancel_reset() {
    g_bReset = false;
}
#endif

// Make fire sounds
public fire_sound(param[]) {
    emit_sound(param[1], CHAN_AUTO, "molotov/molotov_fire.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);
}

// Remove Molotov entities
public fire_stop(param[]) {
    if (pev_valid(param[0])) { set_pev(param[0], pev_flags, pev(param[0], pev_flags) | FL_KILLME); }    // Molotov entity
    if (pev_valid(param[1])) { set_pev(param[1], pev_flags, pev(param[1], pev_flags) | FL_KILLME); }    // info_target entity
}

// Call visual effect and damage functions
public fire_damage(param[]) {

    new iOrigin[3], Float:fOrigin[3];
    iOrigin[0] = param[4];
    iOrigin[1] = param[5];
    iOrigin[2] = param[6];

    random_fire(iOrigin, param[1]);    // Visual effect

    IVecFVec(iOrigin, fOrigin);
    radius_damage2(param[2], param[3], fOrigin, get_pcvar_float(pFireDmg), get_pcvar_float(pMlRadius), DMG_BURN, false);    // Actual damage
}

// There is a radius_damage() in engine, so this was renamed.
stock radius_damage2(iAttacker, iAttackerTeam, Float:fOrigin[3], Float:fDamage, Float:fRange, iDmgType, bool:bCalc = true) {

    new Float:pOrigin[3], Float:fDist, Float:fTmpDmg;
    new i, iFF = get_pcvar_num(pMFF);

    if (iFF == -1) {            // Obey mp_friendlyfire
        iFF = get_pcvar_num(pFriendlyFire);
#if defined DOD || defined TFC
    } else if (iFF == -2) {        // Obey mp_teamplay (bit 5)
        new iTeamPlay = get_pcvar_num(pTeamPlay);
        if (iTeamPlay & (1 << 4)) {    // bit 5 (16 = teammates take no damage from explosive weaponfire)
            iFF = 0;
        }
#endif
    }    // else, leave it at 0 or 1

    while (i++ < g_MaxPlayers) {
        if (!is_user_alive(i)) {
            continue;
        }

#if defined TFC
        if ((iFF == 0) && ((iAttackerTeam == get_user_team(i)) || (tfc_is_team_ally(iAttackerTeam, get_user_team(i))))) {    // TODO: tfc_is_team_ally is broken in AMX Mod X
#else
        if ((iFF == 0) && (iAttackerTeam == get_user_team(i))) {
#endif
            continue;
        }

        pev(i, pev_origin, pOrigin);
        fDist = get_distance_f(fOrigin, pOrigin);

        if (fDist > fRange) {
            continue;
        }

        if (bCalc) {
            fTmpDmg = fDamage - (fDamage / fRange) * fDist;
        } else {
            fTmpDmg = fDamage;
        }

        if (floatround(fTmpDmg) > 0) {    // This eliminated the "[CSX] Invalid damage 0" error
            custom_weapon_dmg(g_wpnMolotov, iAttacker, i, floatround(fTmpDmg), 0);
        }

        if (pev(i, pev_health) <= fTmpDmg) {
            kill(iAttacker, i, iAttackerTeam);
        } else {
            fm_fakedamage(i, "molotov", fTmpDmg, iDmgType);
        }
    }
    
    // At this point, i is one higher than the highest possible player ID, so this loop only affects non-player entities
    while ((i = engfunc(EngFunc_FindEntityInSphere, i, fOrigin, fRange))) {    // Extra parentheses fix warning 211: possibly unintended assignment
        if (pev(i, pev_takedamage)) {
            if (bCalc) {
                pev(i, pev_origin, pOrigin);
                fTmpDmg = fDamage - (fDamage / fRange) * get_distance_f(fOrigin, pOrigin);
            } else {
                fTmpDmg = fDamage;
            }
            fm_fakedamage(i, "molotov", fTmpDmg, iDmgType);
        }
    }
}

// This stock only creates the visual effect. It does not handle any damage.
// I tried using TE_FIREFIELD, but I can't make it look good in dod.
stock random_fire(Origin[3], ent) {

    static iRange, iOrigin[3], g_g, i;

    iRange = get_pcvar_num(pMlRadius);

    for (i = 1; i <= 5; i++) {

        g_g = 1;

        iOrigin[0] = Origin[0] + random_num(-iRange, iRange);
        iOrigin[1] = Origin[1] + random_num(-iRange, iRange);
        iOrigin[2] = Origin[2];
        iOrigin[2] = ground_z(iOrigin, ent);

        while (get_distance(iOrigin, Origin) > iRange) {        // If iOrigin is too far away, recalculate its position

            iOrigin[0] = Origin[0] + random_num(-iRange, iRange);
            iOrigin[1] = Origin[1] + random_num(-iRange, iRange);
            iOrigin[2] = Origin[2];

            if (++g_g >= ANTI_LAGG) {
                iOrigin[2] = ground_z(iOrigin, ent, 1);
            } else {
                iOrigin[2] = ground_z(iOrigin, ent);
            }
        }

        new rand = random_num(5, 15);

        message_begin(MSG_BROADCAST, SVC_TEMPENTITY);
        write_byte(TE_SPRITE);
        write_coord(iOrigin[0]);    // Position
        write_coord(iOrigin[1]);
        write_coord(iOrigin[2] + rand * 5);
        write_short(g_iFireSprite);    // Sprite index
        write_byte(rand);        // Scale
        write_byte(100);        // Brightness
        message_end();
    }

    // One smoke puff for each call to random_fire, regardless of number of flames
    message_begin(MSG_BROADCAST, SVC_TEMPENTITY);
    write_byte(TE_SMOKE);
    write_coord(iOrigin[0]);            // Position
    write_coord(iOrigin[1]);
    write_coord(iOrigin[2] + 120);
    write_short(g_iSmokeSprite[random_num(0, 1)]);    // Sprite index
    write_byte(random_num(10, 30));            // Scale
    write_byte(random_num(10, 20));            // Framerate
    message_end();

}

// Stop all visual effect/physical damage tasks
stock reset_tasks() {
#if defined MOLOTOV_DEBUG
    new tmpdbgid;
#endif
    for (new i; i < g_MaxPlayers; i++) {    // for 0..31
        for (new o; o < MOLOTOV_TASKID_OFFSET; o++) {
            if (task_exists(MOLOTOV_TASKID_BASE1 + (MOLOTOV_TASKID_OFFSET * i) + o)) {
                remove_task(MOLOTOV_TASKID_BASE1 + (MOLOTOV_TASKID_OFFSET * i) + o);
#if defined MOLOTOV_DEBUG
                tmpdbgid = MOLOTOV_TASKID_BASE1 + (MOLOTOV_TASKID_OFFSET * i) + o;
                log_amx("[MC] %d exists. ----------==========----------", tmpdbgid);
#endif
            }

            if (task_exists(MOLOTOV_TASKID_BASE2 + (MOLOTOV_TASKID_OFFSET * i) + o)) {
                remove_task(MOLOTOV_TASKID_BASE2 + (MOLOTOV_TASKID_OFFSET * i) + o);
#if defined MOLOTOV_DEBUG
                tmpdbgid = MOLOTOV_TASKID_BASE2 + (MOLOTOV_TASKID_OFFSET * i) + o;
                log_amx("[MC] %d exists. ----------==========----------", tmpdbgid);
#endif
            }
            // The third task for each Molotov is not stopped so it can remove the Molotov/info_target entities.
        }
    }
}

// This function handles the killing and scoring.
//   iKillerTeam is stored because the killer can disconnect before the Molotov kills someone, leading to inaccurate scoring
stock kill(iKiller, iVictim, iKillerTeam) {

//TFC:    DeathMsg, ScoreInfo, ScoreInfo        // One ScoreInfo for killer and one for victim (order varies)
//DOD:    DeathMsg, ScoreShort, Frags        // ScoreShort=victim, Frags=killer
// CS:    DeathMsg, Money, ScoreInfo, ScoreInfo

//    Scoreboard
// CSTRIKE:    Score Deaths
// DMC:        Frags Deaths
// DOD:        Score Kills Deaths
// HL:        Score Deaths
// HLOF:    Kills Deaths
// TFC:        Score Deaths
// Ricochet:    Points
/*                ----- Attacker -----        ------ Victim ------
                Score    Deaths    Kills        Score    Death    Kills
    CS  kill        +1    -    N/A        -    +1    N/A
    CS  team kill        -1    -    N/A        -    +1    N/A
    CS  suicide        -1    +1    N/A        ---------------------
    CS  detonate/defuse    +3    -    N/A        ---------------------
    DOD kill        -    -    +1        -    +1    -
    DOD team kill        -    -    -        -    +1    -    (mp_tkpenalty handles punishment)
    DOD suicide        -    +1    -        ---------------------
    DOD cap            +1    -    -        ---------------------
    TFC kill        +1    -    N/A        -    +1    N/A
    TFC team kill        -1    -    N/A        -    +1    N/A
    TFC suicide        -1    +1    N/A        ---------------------
    TFC cap/control        varies    -    N/A        ---------------------
*/


// ------------------------------------------------------------------------------------------------- DeathMsg (CS, DOD, TFC)
    // DeathMsg - Triggers HUD message and player console message
    // DOD and CSTRIKE have different formats for DeathMsg; most other mods should be default
    message_begin(MSG_ALL, g_msgDeathMsg, {0,0,0}, 0);
    write_byte(iKiller);        // Killer ID
    write_byte(iVictim);        // Victim ID
#if defined CSTRIKE
    write_byte(0);            // Is Headshot?
#endif   
#if defined DOD
    write_byte(0);            // Weapon ID - These don't match the DODW_* constants, and a custom weapon ID does not work, so we use "world"
#else
    write_string("molotov");    // Truncated Weapon Name
#endif
    message_end();
// ------------------------------------------------------------------------------------------------- /DeathMsg (CS, DOD, TFC)

    // This block of code actually kills the user (silently - DeathMsg was already created)
    new iVictimTeam = get_user_team(iVictim);
    new iMsgBlock = get_msg_block(g_msgDeathMsg);    // Store original block value
    set_msg_block(g_msgDeathMsg, BLOCK_ONCE);    // Start blocking DeathMsg

#if defined CSTRIKE

    new iKillerFrags = get_user_frags(iKiller);
    new iVictimFrags = get_user_frags(iVictim);

    // TFC and CS scoring are mostly the same. See TFC comment. (I did most of my testing with TFC first)
    if (iKiller != iVictim) {
        fm_set_user_frags(iVictim, iVictimFrags + 1);            // Add frag that user_kill() will remove
    }

    if (iKillerTeam != iVictimTeam) {
        iKillerFrags++;                            // Killer's Score = Score + 1
    } else {
        iKillerFrags--;                            // Killer's Score = Score - 1
    }
    fm_set_user_frags(iKiller, iKillerFrags);

    //    CSTRIKE Results    --------------------------------------------------------------------------------------------------
                        //    DeathMsg    ScoreInfoMsg    KScore    KDeath    VScore    VDeath    Internally
    //user_kill(iVictim, 0);        //    Yes        Yes (Victim)    0    0    -1    +1    Everything matches
    //user_kill(iVictim, 1);        //    Yes        Yes (Victim)    0    0    -1    +1    VScore is different internally (unchanged)
    //user_silentkill(iVictim);        //     No        Yes (Victim)    0    0    -1    +1    VScore is different internally (unchanged)

    //dllfunc(DLLFunc_ClientKill, iVictim);    //    Yes        Yes (Victim)    0    0    -1    +1    Everything matches
    //fm_user_kill(iVictim, 0);        //    Yes        Yes (Victim)    0    0    -1    +1    Everything matches
    //fm_user_kill(iVictim, 1);        //    Yes        Yes (Victim)    0    0     0    +1    Everything matches (fm_user_kill adds 1 to VScore)

    // user_silentkill() blocks the DeathMsg, but it doesn't update the scoreboard properly
    // fm_user_kill() updates the scoreboard properly, but generates a DeathMsg with Victim=Killer, so we have to block that.
#endif

#if defined TFC
    new iVictimFrags = tfc_get_user_frags(iVictim);

    // TFC treats every type of kill in code as a suicide and takes away 1 frag from the victim.
    //   After *extensive* testing, I found that the easiest solution is to increment the victim frags
    //   (stored in pdata) beforehand and let the game decrement it and send out ScoreInfo messages.
    //   This applies to *victims* of team kills as well as normal kills. Suicides still lose a frag.
    if (iKiller != iVictim) {
        tfc_set_user_frags(iVictim, iVictimFrags + 1);
    }

    if ((iKillerTeam == iVictimTeam) || tfc_is_team_ally(iKillerTeam, iVictimTeam)) {    // TODO: tfc_is_team_ally() is broken in AMX Mod X
        tfc_set_user_frags(iKiller, get_user_frags(iKiller) - 1);    // Killer's Score = Score - 1
    } else {
        tfc_set_user_frags(iKiller, get_user_frags(iKiller) + 1);    // Killer's Score = Score + 1
    }

    //    TFC Results    --------------------------------------------------------------------------
                        //    DeathMsg    ScoreInfoMsg    VFrag    Internally
    //user_kill(iVictim, 0);        //    Yes        Yes        -1    Everything matches
    //user_kill(iVictim, 1);        //    Yes        Yes         0    VFrags doesn't match
    //user_silentkill(iVictim);        //     No        Yes         0    VFrags doesn't match

    //dllfunc(DLLFunc_ClientKill, iVictim);    //    Yes        Yes        -1    Everything matches
    //fm_user_kill(iVictim, 0);        //    Yes        Yes        -1    Everything matches
    //fm_user_kill(iVictim, 1);        //    Yes        Yes        -1    Everything matches

#endif

    // DOD just works..
    //    DOD Results    --------------------------------------------------------------------------------------------------
                        //    DeathMsg    ScoreShortMsg    FragMsg    KFrag    VDeath    VFrag    Internally
    //user_kill(iVictim, 0);        //    Yes        Yes        N    0    +1     0    Everything matches
    //user_kill(iVictim, 1);        //    Yes        Yes        N    0    +1     0    Everything matches
    //user_silentkill(iVictim);        //     No        Yes        N    0    +1     0    Everything matches

    //dllfunc(DLLFunc_ClientKill, iVictim);    //    Yes        Yes        N    0    +1     0    Everything matches
    //fm_user_kill(iVictim, 0);        //    Yes        Yes        N    0    +1     0    Everything matches
    //fm_user_kill(iVictim, 1);        //    Yes        Yes        N    0    +1    +1    OK, but VFrags shouldn't be changed

    user_kill(iVictim, 0);
    set_msg_block(g_msgDeathMsg, iMsgBlock);        // Stop blocking DeathMsg

    //CSTRIKE client console messages:
    //Kill    "Player1 killed [P0D]M0rbid Desire (2) with molotov"
    //TK    "Player1 killed [POD]Kate_Winslet (2) with molotov"
    //Self    "Player1 killed self with molotov"

    //DOD client console messages: (DOD uses indexes instead of strings and a custom weapon name can't be sent to the player console)
    //Kill    "Player1 killed Sgt.Moving_Target with world"
    //TK    "Player1 killed his teammate Sgt.dontSHOOTiJUSTwannaTALK with world"
    //Self    "Player1 killed self"

    //TFC client console messages:
    //Kill    "Player1 killed [FoX]JesseJames with molotov"
    //TK    "Player1 killed [FoX]Barry with molotov"
    //Self    "Player1 killed self with molotov"


    // I'm not really sure if this does anything, but it seems to match the Valve wiki: https://developer.valvesoftware.com/wiki/HL_Log_Standard
    new sVictim[32], sVictimAuth[35], sVictimTeam[32];
    get_user_name(iVictim, sVictim, charsmax(sVictim));
    get_user_authid(iVictim, sVictimAuth, charsmax(sVictimAuth));
    get_user_team(iVictim, sVictimTeam, charsmax(sVictimTeam));    // TERRORIST, CT, Allies, Axis, #Dustbowl_team1 (Attackers/Blue), #Dustbowl_team2 (Defenders/Red)
    if (iKiller == iVictim) {
        log_message("^"%s<%d><%s><%s>^" committed suicide with ^"molotov^"", sVictim, get_user_userid(iVictim), sVictimAuth, sVictimTeam);
    } else if (is_user_connected(iKiller)) {
        new sKiller[32], sKillerAuth[35], sKillerTeam[32];
        get_user_name(iKiller, sKiller, charsmax(sKiller));
        get_user_authid(iKiller, sKillerAuth, charsmax(sKillerAuth));
        get_user_team(iKiller, sKillerTeam, charsmax(sKillerTeam));
        log_message("^"%s<%d><%s><%s>^" killed ^"%s<%d><%s><%s>^" with ^"molotov^"", sKiller, get_user_userid(iKiller), sKillerAuth, sKillerTeam, sVictim, get_user_userid(iVictim), sVictimAuth, sVictimTeam);
    }
    // TODO: There currently isn't a log message for a kill by a disconnected player. The wiki doesn't show the expected format.

// ------------------------------------------------------------------------------------------------- Money (CS)
#if defined CSTRIKE
    new iMoney;
    if (iKillerTeam == iVictimTeam) {
        iMoney = cs_get_user_money(iKiller) - 3300;            // TODO - $1500 hostage kill penalty
        cs_set_user_money(iKiller, iMoney < 0 ? 0 : iMoney);
    } else {
        iMoney = cs_get_user_money(iKiller) + 300;
        cs_set_user_money(iKiller, iMoney > 16000 ? 16000 : iMoney);
    }
#endif
// ------------------------------------------------------------------------------------------------- /Money (CS)
// ------------------------------------------------------------------------------------------------- ScoreInfo (CS and TFC)
    // ScoreInfo - Updates scoreboard on clients (actual values are changed elsewhere)
    // TFC - ScoreInfo messages are sent automatically after killing a player.
    // CS - ScoreInfo is sent automatically for the victim, but not killer.
#if defined CSTRIKE
    message_begin(MSG_ALL, g_msgScoreInfo);        // Killer ScoreInfo
    write_byte(iKiller);
    write_short(iKillerFrags);
    write_short(get_user_deaths(iKiller));
    write_short(0);
    write_short(iKillerTeam);
    message_end();
#endif
// ------------------------------------------------------------------------------------------------- /ScoreInfo (CS and TFC)

#if defined DOD       
// ------------------------------------------------------------------------------------------------- ScoreShort (DOD)
    // ScoreShort is sent by user_kill()
// ------------------------------------------------------------------------------------------------- /ScoreShort (DOD)
// ------------------------------------------------------------------------------------------------- Frags (DOD)
    if (iKillerTeam != iVictimTeam) {    // Only give a frag if the player killed was an enemy (not suicide or TK)
        dod_set_user_kills(iKiller, dod_get_user_kills(iKiller) + 1, 1);    // These natives seem to work properly.
    }
// ------------------------------------------------------------------------------------------------- /Frags (DOD)
#endif
}

// Attempt to drop the passed coordinates to ground level
stock ground_z(iOrigin[3], ent, skip = 0, iRecursion = 0) {

    iOrigin[2] += random_num(5, 80);

    if (!pev_valid(ent)) {
        return iOrigin[2];
    }

    new Float:fOrigin[3];
    IVecFVec(iOrigin, fOrigin);
    set_pev(ent, pev_origin, fOrigin);
    engfunc(EngFunc_DropToFloor, ent);

    if (!skip && !engfunc(EngFunc_EntIsOnFloor, ent)) {
        if (iRecursion >= ANTI_LAGG) {
            skip = 1;
        }

        return ground_z(iOrigin, ent, skip, ++iRecursion);
    }

    pev(ent, pev_origin, fOrigin);

    return floatround(fOrigin[2]);
}

// If plugin or override is disabled, reset Molotov models to original models
#if defined CSTRIKE || defined DOD
stock reset_molotovs() {
    new ent = g_MaxPlayers;
#if defined CSTRIKE
    // TODO - My limited testing showed this code is pointless.
    //   It has no negative effect, so I'm leaving it for the 3.30 release.
    //   (I don't think "model" is a valid parameter.)
    new iOwner;
    while ((ent = engfunc(EngFunc_FindEntityByString, ent, "model", "models/molotov/w_molotov.mdl"))) {
        iOwner = pev(ent, pev_owner);
#if defined MOLOTOV_DEBUG
        client_print(0, print_chat, "reset_molotovs - found one Molotov! Owner(%d)", iOwner);
#endif
        // If plugin is disabled or player owns no molotovs, reset their model
        if (!get_pcvar_num(pEnabled) || !g_NumMolotov[ID_TO_INDEX(iOwner)]) {
            engfunc(EngFunc_SetModel, ent, "models/w_hegrenade.mdl");
        }
    }
#endif
#if defined DOD
    new iOwner;
    while ((ent = engfunc(EngFunc_FindEntityByString, ent, "model", "models/molotov/w_molotov.mdl"))) {
#if defined MOLOTOV_DEBUG
        client_print(0, print_chat, "reset_molotovs - found one Molotov!");
#endif

        iOwner = pev(ent, pev_owner);
        if (iOwner) {
            switch(get_user_team(iOwner)) {
                case ALLIES: {    // (or British)
                    engfunc(EngFunc_SetModel, ent, "models/w_grenade.mdl");        // Mills is the same model, so this is probably fine
                }
                case AXIS: {
                    engfunc(EngFunc_SetModel, ent, "models/w_stick.mdl");
                }
            }
        }
    }
#endif

}
#endif

// Mods that show the model before it is thrown need the model set (I think)
#if defined CSTRIKE || defined DOD
stock set_molotovs() {
    new ent = g_MaxPlayers;
#if defined CSTRIKE
    while ((ent = engfunc(EngFunc_FindEntityByString, ent, "model", "models/w_hegrenade.mdl"))) {
#if defined MOLOTOV_DEBUG
        client_print(0, print_chat, "set_molotovs - found one hegrenade!");
#endif
        engfunc(EngFunc_SetModel, ent, "models/molotov/w_molotov.mdl");
    }
#endif
#if defined DOD
    while ((ent = engfunc(EngFunc_FindEntityByString, ent, "model", "models/w_grenade.mdl"))) {
#if defined MOLOTOV_DEBUG
        client_print(0, print_chat, "set_molotovs - found one grenade!");
#endif
        engfunc(EngFunc_SetModel, ent, "models/molotov/w_molotov.mdl");
    }
    ent = g_MaxPlayers;
    while ((ent = engfunc(EngFunc_FindEntityByString, ent, "model", "models/w_stick.mdl"))) {
#if defined MOLOTOV_DEBUG
        client_print(0, print_chat, "set_molotovs - found one stick!");
#endif
        engfunc(EngFunc_SetModel, ent, "models/molotov/w_molotov.mdl");
    }
    ent = g_MaxPlayers;
    while ((ent = engfunc(EngFunc_FindEntityByString, ent, "model", "models/w_mills.mdl"))) {
#if defined MOLOTOV_DEBUG
        client_print(0, print_chat, "set_molotovs - found one Mills!");
#endif
        engfunc(EngFunc_SetModel, ent, "models/molotov/w_molotov.mdl");
    }
#endif

}
#endif

// Show optional buy menu
#if defined CSTRIKE
public show_molotov_menu(id) {
    new menu[128];
    formatex(menu, charsmax(menu), "Buy Molotov Cocktail ($%d)?^n^n1. Yes^n2. No^n^n0. Exit", get_pcvar_num(pPrice));

    // This shows the menu for 30 seconds, I tried first with get_cvar_num("mp_buytime")*60 , but it didn't work well
    // when using 0.5 as mp_buytime. If you want to, just change the time below.
    show_menu(id, MOLOTOV_MENU_KEYS, menu, 30);

    return PLUGIN_HANDLED;
}
#endif

// Our menu function will get the player id and the key they pressed
#if defined CSTRIKE
public giveMolotov(id, key) {

    //key will start at zero
    switch(key) {
        case 0: buy_molotov(id);
        //I don't think these messages are necessary.
        //case 1: client_print(id, print_center, "You have chosen not to buy a Molotov cocktail");
        //default: client_print(id, print_center, "You have chosen to exit the Molotov menu");
    }
}
#endif

// Set user frags (score) in TFC
#if defined TFC
stock tfc_set_user_frags(iIndex, iNewFrags) {
    if (is_linux_server()) {
        set_pdata_int(iIndex, 76, iNewFrags);        // real_frags = 76 (on Linux)        Required!
    } else {
        set_pdata_int(iIndex, 77, iNewFrags);        // real_frags = 77 (on Windows)        Required?
    }    // Is there a mac version?

    // As far as I can tell, real_frags is what should be set, and something copies it to m_iClientFrags.
    //set_pdata_int(iIndex, 643, iNewFrags);        // m_iClientFrags = 643 (on Linux/Windows)

    // Sometimes this is required, sometimes it isn't. I think what is happening is that in
    //   the cases it doesn't seem to be required, pev_frags is getting updated internally.
    set_pev(iIndex, pev_frags, float(iNewFrags));

#if defined MOLOTOV_DEBUG
    mydump(iIndex, 65, 85);
    mydump(iIndex, 635, 655);
#endif
}
#endif

// Return a user's frags, and verify that the TFC offsets haven't changed
#if defined TFC
stock tfc_get_user_frags(iIndex) {
    new iOffset = (is_linux_server() ? 76 : 77);    // real_frags = 76 (Linux), 77 (Windows)
    new iFrags = get_user_frags(iIndex);

    // This code is the easiest way to detect a change in the offsets and help prevent annoying troubleshooting.
    // get_user_frags seems to always return the correct value.
    if (iFrags != get_pdata_int(iIndex, iOffset)) {
        client_print(0, print_chat, "OFFSET CHANGED! get_user_frags(%d):%d; get_pdata_int(%d, %d):%d; Contact plugin author!", iIndex, iFrags, iIndex, iOffset, get_pdata_int(iIndex, iOffset));
        console_print(0, "WARNING! get_user_frags != real_frags! Contact plugin author!!!!!!!!!!!!!!!!!!!!");
    }

    return get_pdata_int(iIndex, iOffset);
}
#endif

// This won't be compiled unless MOLOTOV_DEBUG is set (and only for TFC)
#if defined TFC
stock mydump(iIndex, iStart, iEnd) {

    new sLine[512];
    new FILE[] = "addons/amxmodx/logs/pdatadump.log";

    new sClassname[64];
    entity_get_string(iIndex, EV_SZ_classname, sClassname, charsmax(sClassname));
    format(sLine, charsmax(sLine), "Starting dump of entity %s %d", sClassname, iIndex);
    console_print(1, "%s to file %s", sLine, FILE);
    if (!write_file(FILE, sLine)) {
        console_print(1, "Error dumping to %s!", FILE);
        return PLUGIN_HANDLED;
    }

    for (new i = iStart; i <= iEnd; i++) {
        format(sLine, charsmax(sLine), "%s %d: Offset %d:^t%d^t%f", sClassname, iIndex, i, get_pdata_int(iIndex, i), get_pdata_float(iIndex, i));
        if (!write_file(FILE, sLine)) {
            console_print(1, "Error dumping to %s!", FILE);
            return PLUGIN_HANDLED;
        }
    }

    console_print(1, "Dump done. Check %s!", FILE);

    return 1;
}
#endif
Код:
new pMaxMolotovs;            // Pointer to molotov_max
 

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

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