#include <amxmodx>
#include <fakemeta>
#include <hamsandwich>
#include <reapi>
#include <xs>
stock const PLUGIN_NAME[] = "Holy Grenade";
stock const PLUGIN_VERSION[] = "1.1";
stock const PLUGIN_AUTHOR[] = "BiZaJe";
new const g_FileSetting[] = "holygrenade.ini"
new const g_szWeaponName[] = "weapon_hegrenade";
new const g_szClassName[] = "weapon_holygrenade";
new const g_szAmmoName[] = "HolyGrenade";
new g_szWeaponBox[] = "weaponbox_holygrenade";
new const szModels[][] = {
"models/holygrenade/p_holygrenade.mdl",
"models/holygrenade/v_holygrenade.mdl",
"models/holygrenade/w_holygrenade.mdl"
};
new const szSound[][] = {
"weapons/holygrenade_hit.wav",
"weapons/run_run_bitch.wav"
}
enum _:Cvars{
AMMO_MAX,
SLOT,
POSITION,
WPN_ID,
Float:RADIUS,
Float:DMG,
NADEDROP,
STAYTIME
}
new Trie:g_szHolyGrenade;
new g_Cvars[Cvars]
new g_iPartSprite, g_iExpSprite, g_Msg_AmmoPickUp, g_Msg_WeaponList, g_hRegUserMsg, g_hWeaponList, WeaponIdType:g_iWeaponID;
new HookChain:g_hCanHaveItem;
public plugin_precache(){
g_szHolyGrenade = TrieCreate();
new szFile[256];
new iLen = get_localinfo("amxx_configsdir", szFile, charsmax(szFile));
copy(szFile[iLen], charsmax(szFile) - iLen, fmt("/%s", g_FileSetting));
if(!file_exists(szFile)){
set_fail_state("[HOLYGRENADE] Конфигурационный файл %s не найден", szFile);
return;
}
new pFile = fopen(szFile, "rt");
new szStr[32], szKey[16], szValue[4];
while(fgets(pFile, szStr, charsmax(szStr))){
switch(szStr[0]){
default:{
if(strtok2(szStr, szKey, charsmax(szKey), szValue, charsmax(szValue), '=', TRIM_FULL) != -1){
TrieSetString(g_szHolyGrenade, szKey, szValue, true);
}
}
}
}
fclose(pFile);
if(TrieGetString(g_szHolyGrenade, "ammo_max", szValue, charsmax(szValue))){
g_Cvars[AMMO_MAX] = str_to_num(szValue);
}else{
g_Cvars[AMMO_MAX] = 1;
}
if(TrieGetString(g_szHolyGrenade, "slot_id", szValue, charsmax(szValue))){
g_Cvars[SLOT] = str_to_num(szValue);
}else{
g_Cvars[SLOT] = 3;
}
if(TrieGetString(g_szHolyGrenade, "position", szValue, charsmax(szValue))){
g_Cvars[POSITION] = str_to_num(szValue);
}else{
g_Cvars[POSITION] = 4;
}
if(TrieGetString(g_szHolyGrenade, "weapon_id", szValue, charsmax(szValue))){
g_Cvars[WPN_ID] = str_to_num(szValue);
}else{
g_Cvars[WPN_ID] = 2;
}
TrieDestroy(g_szHolyGrenade);
g_iWeaponID = rg_get_weapon_info(g_szWeaponName, WI_ID);
new i;
for(i = 0; i <= charsmax(szModels); i++){
precache_model(szModels[i]);
}
for(i = 0; i <= charsmax(szSound); i++){
precache_sound(szSound[i]);
}
register_clcmd(g_szClassName, "CmdSelect");
g_iPartSprite = precache_model("sprites/muz7.spr");
g_iExpSprite = precache_model("sprites/eexplo.spr");
precache_generic("sprites/weapon_holygrenade.txt");
precache_generic("sprites/holygrenade.spr");
if(g_Cvars[WPN_ID] != 2){
g_Msg_WeaponList = get_user_msgid("WeaponList");
if(g_Msg_WeaponList) {
g_hWeaponList = register_message(g_Msg_WeaponList, "HookWeaponList");
} else {
g_hRegUserMsg = register_forward(FM_RegUserMsg, "RegUserMsg_Post", true);
}
}
}
public plugin_natives()
{
register_native("give_holygrenade", "GiveHolyGrenade_Native");
}
public plugin_init(){
register_plugin(PLUGIN_NAME, PLUGIN_VERSION, PLUGIN_AUTHOR);
RegisterCvars();
g_Msg_AmmoPickUp = get_user_msgid("AmmoPickup");
if(g_Cvars[WPN_ID] == 2){
g_Msg_WeaponList = get_user_msgid("WeaponList");
UTIL_WeapoList(
MSG_INIT, 0,
g_szClassName,
19, g_Cvars[AMMO_MAX],
-1, -1, InventorySlotType:g_Cvars[SLOT], g_Cvars[POSITION], WeaponIdType:g_Cvars[WPN_ID],
ITEM_FLAG_LIMITINWORLD | ITEM_FLAG_EXHAUSTIBLE
);
}else{
if(g_hRegUserMsg) {
unregister_forward(FM_RegUserMsg, g_hRegUserMsg, true);
}
unregister_message(g_Msg_WeaponList, g_hWeaponList);
RegisterHookChain(RG_CBasePlayer_HasRestrictItem, "CBasePlayer_HasRestrictItem_Pre", .post = false);
g_hCanHaveItem = RegisterHookChain(RG_CSGameRules_CanHavePlayerItem, "CSGameRules_CanHavePlayerItem_Pre", .post = false);
}
RegisterHookChain(RG_CBasePlayer_GiveAmmo, "CBasePlayer_GiveAmmo_Pre", .post = false);
RegisterHookChain(RG_CBasePlayerWeapon_DefaultDeploy, "CBasePlayerWeapon_DefaultDeploy_Pre", .post = false);
RegisterHam(Ham_Item_Deploy, g_szWeaponName, "Item_Deploy_Post", true);
RegisterHam(Ham_Item_Holster, g_szWeaponName, "Item_Holster_Post", true);
RegisterHookChain(RG_CBasePlayer_ThrowGrenade, "CBasePlayer_ThrowGrenade_Pre", .post = false);
RegisterHookChain(RG_CBasePlayer_ThrowGrenade, "CBasePlayer_ThrowGrenade_Post", .post = true);
RegisterHookChain(RG_CGrenade_ExplodeHeGrenade, "CGrenade_ExplodeHeGrenade_Pre", .post = false);
RegisterHam(Ham_Touch, "grenade", "Touch_Pre", false);
RegisterHookChain(RH_SV_StartSound, "SV_StartSound_Pre", .post = false);
RegisterHookChain(RG_CBasePlayer_Killed, "CBasePlayer_Killed_Pre", .post = false);
RegisterHookChain(RG_CSGameRules_CleanUpMap, "CSGameRules_CleanUpMap_Post", .post = true);
}
public OnConfigsExecuted()
{
bind_pcvar_num(get_cvar_pointer("mp_nadedrops"), g_Cvars[NADEDROP]);
g_Cvars[STAYTIME] = get_cvar_num("mp_item_staytime");
}
public CmdSelect(iPlayer){
if(!is_user_alive(iPlayer)){
return PLUGIN_HANDLED;
}
new Ent = rg_get_player_item(iPlayer, "weapon_holygrenade", InventorySlotType:4);
if(Ent != 0 && get_member(iPlayer, m_pActiveItem) != Ent){
rg_switch_weapon(iPlayer, Ent);
}
return PLUGIN_HANDLED;
}
RegisterCvars(){
bind_pcvar_float(create_cvar(
"holygrenade_radius",
"700.0",
FCVAR_NONE,
"Радиус поражения от взрыва святой гранаты в юнитах"),
g_Cvars[RADIUS]
);
bind_pcvar_float(create_cvar(
"holygrenade_dmg",
"300.0",
FCVAR_NONE,
"Урон, наносимый игрокам при взрыве святой гранаты (в эпицентре)"),
g_Cvars[DMG]
);
bind_pcvar_num(create_cvar(
"holygrenade_ammo_max",
"1",
FCVAR_NONE,
"Тип дропа^n0 - выкл | 1 - учитывать квар `mp_nadedrops`"),
g_Cvars[NADEDROP]
);
AutoExecConfig(true)
}
public GiveHolyGrenade_Native(){
new iPlayer = get_param(1);
new iAmmoAmount = get_param(2);
new Ent = rg_get_player_item(iPlayer, "weapon_holygrenade", InventorySlotType:4);
if (Ent)
{
GiveAmmo(iPlayer, iAmmoAmount, 19, g_Cvars[AMMO_MAX]);
return Ent;
}
Ent = GiveGrenade(iPlayer);
if (iAmmoAmount > 1 && (Ent && is_entity(Ent)))
{
GiveAmmo(iPlayer, iAmmoAmount, 19, g_Cvars[AMMO_MAX]);
}
return Ent;
}
public RegUserMsg_Post(szName[]){
if((strcmp(szName, "WeaponList") == 0)){
g_Msg_WeaponList = get_orig_retval();
g_hWeaponList = register_message(g_Msg_WeaponList, "HookWeaponList");
}
}
public HookWeaponList(const msg_id, const msg_dest, const msg_entity)
{
enum {
arg_name = 1,
arg_ammo1,
arg_ammo1_max,
arg_ammo2,
arg_ammo2_max,
arg_slot,
arg_position,
arg_id,
arg_flags,
};
if (msg_dest != MSG_INIT || WeaponIdType:get_msg_arg_int(arg_id) != WeaponIdType:g_Cvars[WPN_ID]) {
return PLUGIN_CONTINUE;
}
set_msg_arg_string(arg_name, g_szClassName);
set_msg_arg_int(arg_ammo1, ARG_BYTE, 19);
set_msg_arg_int(arg_ammo1_max, ARG_BYTE, g_Cvars[AMMO_MAX]);
set_msg_arg_int(arg_ammo2, ARG_BYTE, -1);
set_msg_arg_int(arg_ammo2_max, ARG_BYTE, -1);
set_msg_arg_int(arg_slot, ARG_BYTE, _:InventorySlotType:g_Cvars[SLOT]-1);
set_msg_arg_int(arg_position, ARG_BYTE, g_Cvars[POSITION]);
set_msg_arg_int(arg_flags, ARG_BYTE, ITEM_FLAG_LIMITINWORLD | ITEM_FLAG_EXHAUSTIBLE);
return PLUGIN_CONTINUE;
}
public CBasePlayer_HasRestrictItem_Pre(const this, ItemID:item, ItemRestType:type){
if(item == ItemID:g_iWeaponID && rg_get_player_item(this, "weapon_holygrenade", InventorySlotType:4)){
SetHookChainReturn(ATYPE_BOOL, true);
return HC_SUPERCEDE;
}
return HC_CONTINUE;
}
public CSGameRules_CanHavePlayerItem_Pre(const index, const item){
if(is_entity(item) && WeaponIdType:g_Cvars[WPN_ID] == get_member(item, m_iId)){
SetHookChainReturn(ATYPE_INTEGER, false);
return HC_SUPERCEDE;
}
return HC_CONTINUE;
}
public CBasePlayer_GiveAmmo_Pre(const this, iAmount, szName[], iMax){
if(strcmp(szName, g_szAmmoName)){
return HC_CONTINUE;
}
GiveAmmo(this, iAmount, 19, g_Cvars[AMMO_MAX]);
SetHookChainReturn(ATYPE_INTEGER, 19);
return HC_SUPERCEDE;
}
public CBasePlayerWeapon_DefaultDeploy_Pre(const this, szViewModel[], szWeaponModel[], iAnim, szAnimExt[], skiplocal){
if(FClassnameIs(this, g_szClassName)){
SetHookChainArg(2, ATYPE_STRING, szModels[1]);
SetHookChainArg(3, ATYPE_STRING, szModels[0]);
}
new WeaponIdType:weaponid = WeaponIdType:rg_get_iteminfo(this, ItemInfo_iId);
if(weaponid != g_iWeaponID && weaponid != WeaponIdType:78){
return HC_CONTINUE;
}
new lastItem = get_member(get_member(this, m_pPlayer), m_pLastItem);
if(is_nullent(lastItem) || this == lastItem){
return HC_CONTINUE;
}
if(WeaponIdType:rg_get_iteminfo(lastItem, ItemInfo_iId) == g_iWeaponID){
SetHookChainArg(6, ATYPE_INTEGER, 0);
}
return HC_CONTINUE;
}
public Item_Deploy_Post(this){
if(WeaponIdType:rg_get_iteminfo(this, ItemInfo_iId) == WeaponIdType:78){
rg_set_iteminfo(this, ItemInfo_iId, g_iWeaponID);
}
new next = get_member(get_member(this, m_pPlayer), m_rgpPlayerItems, InventorySlotType:4);
while(!is_nullent(next)){
if(next != this && WeaponIdType:rg_get_iteminfo(next, ItemInfo_iId) == g_iWeaponID){
rg_set_iteminfo(next, ItemInfo_iId, WeaponIdType:78);
}
next = get_member(next, m_pNext);
}
}
public Item_Holster_Post(this){
new next = get_member(get_member(this, m_pPlayer), m_rgpPlayerItems, InventorySlotType:4);
while(!is_nullent(next)){
if(next != this && WeaponIdType:rg_get_iteminfo(next, ItemInfo_iId) == WeaponIdType:78){
rg_set_iteminfo(next, ItemInfo_iId, g_iWeaponID);
}
next = get_member(next, m_pNext);
}
}
public CBasePlayer_ThrowGrenade_Pre(const this, const grenade, Float:vecSrc[3], Float:vecThrow[3], Float:time, const usEvent){
if(FClassnameIs(grenade, g_szClassName)){
set_member(grenade, m_iId, 4);
}
return HC_CONTINUE;
}
public CBasePlayer_ThrowGrenade_Post(const this, const grenade, Float:vecSrc[3], Float:vecThrow[3], Float:time, const usEvent){
if(!FClassnameIs(grenade, g_szClassName)){
return;
}
set_member(grenade, m_iId, WeaponIdType:g_Cvars[WPN_ID]);
new Ent = GetHookChainReturn(ATYPE_INTEGER);
if(is_nullent(Ent)){
return;
}
set_entvar(Ent, var_impulse, 1338);
engfunc(EngFunc_SetModel, Ent, szModels[2]);
}
public CGrenade_ExplodeHeGrenade_Pre(const this, tracehandle, const bitsDamageType){
if(get_entvar(this, var_impulse) == 1338){
return HC_SUPERCEDE;
}
return HC_CONTINUE;
}
public Touch_Pre(this, idother){
if(IsHolyGrenade(this)){
remove_task(this);
set_task(0.1, "HolyGrenadeVelocity_Task", this);
new Float:flOrigin[3];
get_entvar(this, var_origin, flOrigin);
message_begin(MSG_ALL, SVC_TEMPENTITY);
write_byte(TE_SPRITETRAIL);
write_coord_f(flOrigin[0]);
write_coord_f(flOrigin[1]);
write_coord_f(floatadd(20.0, flOrigin[2]));
write_coord_f(flOrigin[0]);
write_coord_f(flOrigin[1]);
write_coord_f(floatadd(20.0, flOrigin[2]));
write_short(g_iPartSprite);
write_byte(5);
write_byte(1);
write_byte(2);
write_byte(random_num(10, 20));
write_byte(30);
message_end();
}
}
public HolyGrenadeVelocity_Task(Ent){
if (is_nullent(Ent) || (!FClassnameIs(Ent, "grenade") || !IsHolyGrenade(Ent)))
{
return;
}
new Float:flVelocity[3];
get_entvar(Ent, var_velocity, flVelocity);
if(!flVelocity[0] && !flVelocity[1] && !flVelocity[2]){
rh_emit_sound2(Ent, 0, CHAN_VOICE, szSound[1], VOL_NORM, ATTN_NORM, 0, PITCH_NORM);
SetThink(Ent, "ExplodeHolyGrenade_Think");
set_entvar(Ent, var_nextthink, get_gametime()+3.0);
}
}
public ExplodeHolyGrenade_Think(Ent){
if (!is_entity(Ent)){
return;
}
new Float:flOrigin[3];
get_entvar(Ent, var_origin, flOrigin);
message_begin_f(MSG_PVS, SVC_TEMPENTITY, flOrigin, 0);
write_byte(TE_EXPLOSION);
write_coord_f(flOrigin[0]);
write_coord_f(flOrigin[1]);
write_coord_f(flOrigin[2]);
write_short(g_iExpSprite);
write_byte(floatround(g_Cvars[RADIUS] / 2));
write_byte(18);
write_byte(DRC_FLAG_DRAMATIC);
message_end();
rg_dmg_radius(flOrigin, Ent, get_entvar(Ent, var_owner), g_Cvars[DMG], g_Cvars[RADIUS], 0, DMG_GRENADE);
set_entvar(Ent, var_flags, FL_KILLME);
}
public SV_StartSound_Pre(const recipients, const entity, const channel, const sample[], const volume, Float:attenuation, const fFlags, const pitch){
if(FClassnameIs(entity, "grenade") && IsHolyGrenade(entity)){
if (sample[8] == 'h' && sample[11] == 'b'){
SetHookChainArg(4, ATYPE_STRING, szSound[0]);
}
}
return HC_CONTINUE;
}
public CBasePlayer_Killed_Pre(const this, pevAttacker, iGib){
if(!is_user_connected(this)){
return;
}
new activeItem = get_member(this, m_pActiveItem);
if(is_nullent(activeItem) && (FClassnameIs(activeItem, g_szClassName) && get_entvar(this, var_button) & IN_ATTACK)){
set_member(activeItem, m_iId, 4);
return;
}
if(g_Cvars[NADEDROP] >= 1 && rg_get_player_item(this, "weapon_holygrenade", InventorySlotType:4)){
new Float:flOrigin[3];
get_entvar(this, var_origin, flOrigin);
new Float:flVelocity[3];
get_entvar(this, var_velocity, flVelocity);
xs_vec_mul_scalar(flVelocity, 0.75, flVelocity);
new Float:flAngles[3];
get_entvar(this, var_angles, flAngles);
new weaponBox = rg_create_entity("info_target");
if(weaponBox < 1){
return;
}
set_entvar(weaponBox, var_classname, g_szWeaponBox);
engfunc(EngFunc_SetOrigin, weaponBox, flOrigin);
set_entvar(weaponBox, var_angles, flAngles);
set_entvar(weaponBox, var_movetype, MOVETYPE_TOSS);
set_entvar(weaponBox, var_solid, SOLID_TRIGGER);
engfunc(EngFunc_SetSize, weaponBox, Float:{-6.0, -6.0, -1.0}, Float:{6.0, 6.0, 1.0});
engfunc(EngFunc_SetModel, weaponBox, szModels[2]);
set_entvar(weaponBox, var_velocity, flVelocity);
new GrenadesNum[1];
GrenadesNum[0] = get_member(this, m_rgAmmo, 19);
SetTouch(weaponBox, "Box_Touch", GrenadesNum);
SetThink(weaponBox, "Box_Think");
set_entvar(weaponBox, var_nextthink, get_gametime() + g_Cvars[STAYTIME]);
}
}
public Box_Touch(Ent, iPlayer, GrenadeNum[])
{
if(!is_user_alive(iPlayer) || !is_entity(Ent)){
return;
}
if(g_Cvars[AMMO_MAX] <= get_member(iPlayer, m_rgAmmo, 19)){
return;
}
set_entvar(Ent, var_flags, FL_KILLME);
GiveGrenade(iPlayer);
GiveAmmo(iPlayer, GrenadeNum[0], 19, g_Cvars[AMMO_MAX]);
}
public Box_Think(Ent){
if(is_entity(Ent)){
set_entvar(Ent, var_flags, FL_KILLME);
}
}
public CSGameRules_CleanUpMap_Post(){
new Ent = NULLENT;
while((Ent = rg_find_ent_by_class(Ent, g_szWeaponBox, false))){
set_entvar(Ent, var_flags, FL_KILLME);
}
}
GiveGrenade(iPlayer){
if(!is_user_alive(iPlayer)){
return NULLENT;
}
new Ent = rg_get_player_item(iPlayer, "weapon_holygrenade", InventorySlotType:4);
if(Ent != 0) {
GiveAmmo(iPlayer, 1, 19, 1);
return Ent;
}
Ent = rg_create_entity("weapon_hegrenade", false);
if(is_nullent(Ent)){
return NULLENT;
}
new Float:flOrigin[3];
get_entvar(iPlayer, var_origin, flOrigin);
set_entvar(Ent, var_origin, flOrigin);
set_entvar(Ent, var_spawnflags, get_entvar(Ent, var_spawnflags) | SF_NORESPAWN);
set_member(Ent, m_Weapon_iPrimaryAmmoType, 19);
set_member(Ent, m_Weapon_iSecondaryAmmoType, -1);
set_entvar(Ent, var_classname, g_szClassName);
dllfunc(DLLFunc_Spawn, Ent);
set_member(Ent, m_iId, WeaponIdType:g_Cvars[WPN_ID]);
rg_set_iteminfo(Ent, ItemInfo_iMaxClip, -1);
rg_set_iteminfo(Ent, ItemInfo_pszName, g_szClassName);
rg_set_iteminfo(Ent, ItemInfo_pszAmmo1, g_szAmmoName);
rg_set_iteminfo(Ent, ItemInfo_iMaxAmmo1, g_Cvars[AMMO_MAX]);
rg_set_iteminfo(Ent, ItemInfo_iId, WeaponIdType:78);
rg_set_iteminfo(Ent, ItemInfo_iPosition, g_Cvars[POSITION]);
rg_set_iteminfo(Ent, ItemInfo_iWeight, 1);
rg_set_iteminfo(Ent, ItemInfo_iFlags, ITEM_FLAG_LIMITINWORLD | ITEM_FLAG_EXHAUSTIBLE)
if(g_Cvars[WPN_ID] != 2){
DisableHookChain(g_hCanHaveItem);
}
dllfunc(DLLFunc_Touch, Ent, iPlayer);
if(g_Cvars[WPN_ID] != 2){
EnableHookChain(g_hCanHaveItem);
}
if (get_entvar(Ent, var_owner) != iPlayer){
set_entvar(Ent, var_flags, FL_KILLME);
return NULLENT;
}
return Ent;
}
GiveAmmo(iPlayer, iAmount, iAmmo, iMax){
if(get_entvar(iPlayer, var_flags) & FL_SPECTATOR){
return;
}
new Count = get_member(iPlayer, m_rgAmmo, iAmmo);
new Add = min(iAmount, iMax - Count);
if(Add < 1){
return;
}
set_member(iPlayer, m_rgAmmo, Add + Count, iAmmo);
emessage_begin(MSG_ONE, g_Msg_AmmoPickUp, .player = iPlayer);
ewrite_byte(iAmmo);
ewrite_byte(Add);
emessage_end();
rh_emit_sound2(iPlayer, 0, CHAN_ITEM, "items/gunpickup2.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);
}
bool:IsHolyGrenade(Ent){
return GetGrenadeType(Ent) == WeaponIdType:4 && get_entvar(Ent, var_impulse) == 1338;
}
stock UTIL_WeapoList(
const type,
const player,
const name[],
const ammo1,
const maxAmmo1,
const ammo2,
const maxammo2,
const InventorySlotType:slot,
const position,
const WeaponIdType:id,
const flags
) {
message_begin(type, g_Msg_WeaponList, .player = player);
write_string(name);
write_byte(ammo1);
write_byte(maxAmmo1);
write_byte(ammo2);
write_byte(maxammo2);
write_byte(_:slot - 1);
write_byte(position);
write_byte(_:id);
write_byte(flags);
message_end();
}
stock rg_get_player_item(const iPlayer, const szClassName[], const InventorySlotType:iSlot = NONE_SLOT) {
new Item = get_member(iPlayer, m_rgpPlayerItems, iSlot);
while (!is_nullent(Item)) {
if (FClassnameIs(Item, szClassName)) {
return Item;
}
Item = get_member(Item, m_pNext);
}
return 0;
}