Code:
#include <sourcemod>
#include <sdktools>
#include <sdkhooks>
#include <cstrike>
#include <zombieplague>
#pragma newdecls required
/**
* Record plugin info.
**/
public Plugin myinfo =
{
name = "[ZP] ExtraItem: PlasmaGun",
author = "qubka (Nikita Ushakov)",
description = "Add new extraitem to human",
version = "2.0",
url = "https://forums.alliedmods.net/showthread.php?t=272546"
}
/**
* @section Information about extra items.
**/
#define EXTRA_ITEM_NAME "PlasmaGun"
#define EXTRA_ITEM_COST 5
#define EXTRA_ITEM_LEVEL 0
#define EXTRA_ITEM_ONLINE 0
#define EXTRA_ITEM_LIMIT 0
// Weapon
#define WEAPON_NAME "weapon_plasma"
#define WEAPON_REFERANCE "weapon_scar20"
#define WEAPON_SLOT WEAPON_SLOT_PRIMARY
// Models
#define MODEL_WORLD "models/weapons/plasma/w_snip_plasma.mdl"
#define MODEL_VIEW "models/weapons/plasma/v_snip_plasma.mdl"
#define MODEL_BEAM "materials/sprites/laserbeam.vmt"
// Sounds
#define SOUND_FIRE "weapons/RequestsStudio/UT3/AvrilFire.mp3"
#define SOUND_DEPLOY "weapons/RequestsStudio/UT3/AvrilDeploy.mp3"
// Beam
#define BEAM_LIFE 0.105
#define BEAM_COLOR {100, 50, 253, 255}
// Damage
#define WEAPON_MULTIPLIER_DAMAGE 1.23
/**
* @endsection
**/
//*********************************************************************
//* Don't modify the code below this line unless *
//* you know _exactly_ what you are doing!!! *
//*********************************************************************
enum
{
WEAPON_SLOT_INVALID = -1, /** Used as return value when an weapon doens't exist. */
WEAPON_SLOT_PRIMARY, /** Primary slot */
WEAPON_SLOT_SECONDARY, /** Secondary slot */
WEAPON_SLOT_MELEE, /** Melee slot */
WEAPON_SLOT_EQUEPMENT /** Equepment slot */
};
// Item index
int iItem;
bool bHasCustomWeapon[MAXPLAYERS+1];
// Weapon model indexes
int iViewModel;
int iWorldModel;
int iBeamModel;
/**
* Plugin is loading.
**/
public void OnPluginStart()
{
// Initilizate extra item
iItem = ZP_RegisterExtraItem(EXTRA_ITEM_NAME, EXTRA_ITEM_COST, TEAM_HUMAN, EXTRA_ITEM_LEVEL, EXTRA_ITEM_ONLINE, EXTRA_ITEM_LIMIT);
// Hook temp entity
AddTempEntHook("Shotgun Shot", WeaponFireBullets);
HookEvent("bullet_impact", EventBulletImpact, EventHookMode_Post);
}
/**
* The map is starting.
**/
public void OnMapStart(/*void*/)
{
// Precache models
iBeamModel = PrecacheModel(MODEL_BEAM);
iViewModel = PrecacheModel(MODEL_VIEW);
iWorldModel = PrecacheModel(MODEL_WORLD);
// Precache sound
char sSound[128];
Format(sSound, sizeof(sSound), "*/%s", SOUND_FIRE);
AddToStringTable(FindStringTable("soundprecache"), sSound);
Format(sSound, sizeof(sSound), "sound/%s", SOUND_FIRE);
AddFileToDownloadsTable(sSound);
Format(sSound, sizeof(sSound), "*/%s", SOUND_DEPLOY);
AddToStringTable(FindStringTable("soundprecache"), sSound);
Format(sSound, sizeof(sSound), "sound/%s", SOUND_DEPLOY);
AddFileToDownloadsTable(sSound);
// Add models to download list
AddFileToDownloadsTable("models/weapons/plasma/v_snip_plasma.mdl");
AddFileToDownloadsTable("models/weapons/plasma/v_snip_plasma.dx90");
AddFileToDownloadsTable("models/weapons/plasma/v_snip_plasma.vvd");
AddFileToDownloadsTable("models/weapons/plasma/w_snip_plasma.mdl");
AddFileToDownloadsTable("models/weapons/plasma/w_snip_plasma.dx90");
AddFileToDownloadsTable("models/weapons/plasma/w_snip_plasma.phy");
AddFileToDownloadsTable("models/weapons/plasma/w_snip_plasma.vvd");
// Add textures to download list
AddFileToDownloadsTable("materials/models/weapons/v_models/Requests Studio/UT3/Shock Rifle D.vtf");
AddFileToDownloadsTable("materials/models/weapons/v_models/Requests Studio/UT3/Shock Rifle N.vtf");
AddFileToDownloadsTable("materials/models/weapons/v_models/Requests Studio/UT3/Shock Rifle.vmt");
AddFileToDownloadsTable("materials/models/weapons/w_models/Requests Studio/UT3/Shock Rifle.vmt");
}
/**
* Called after select an extraitem in equipment menu.
*
* @param clientIndex The client index.
* @param extraitemIndex Index of extraitem from ZP_RegisterExtraItem() native.
*
* @return Plugin_Handled to block purhase. Anything else
* (like Plugin_Continue) to allow purhase and taking ammopacks.
**/
public Action ZP_OnExtraBuyCommand(int clientIndex, int extraitemIndex)
{
// Verify that the client is connected and alive
if(!IsPlayerExist(clientIndex))
{
return Plugin_Handled;
}
// Check our item index
if(extraitemIndex == iItem)
{
// Return ammopacks
if(ZP_IsPlayerZombie(clientIndex) || ZP_IsPlayerSurvivor(clientIndex))
{
return Plugin_Handled;
}
//**********************************************
//* GIVE WEAPON *
//**********************************************
// Get weapon index from slot
int iSlot = GetPlayerWeaponSlot(clientIndex, WEAPON_SLOT_PRIMARY);
// If weapon is valid, then drop
if (iSlot != WEAPON_SLOT_INVALID)
{
CS_DropWeapon(clientIndex, iSlot, true, false);
}
// Give item
bHasCustomWeapon[clientIndex] = true;
GivePlayerItem(clientIndex, WEAPON_REFERANCE);
FakeClientCommandEx(clientIndex, "use %s", WEAPON_REFERANCE);
}
// Allow buying
return Plugin_Continue;
}
/**
* Set dropped model.
*
* @param weaponIndex The weapon index.
**/
public void SetDroppedModel(int weaponIndex)
{
// If weapon isn't custom
if(!IsCustomItemEntity(weaponIndex))
{
return;
}
// Set dropped model
SetEntityModel(weaponIndex, MODEL_WORLD);
}
/**
* Event callback (Shotgun Shot)
* The weapon is about to shoot.
*
* @param sTEName Temp name.
* @param iPlayers Array containing target player indexes.
* @param numClients Number of players in the array.
* @param flDelay Delay in seconds to send the TE.
**/
public Action WeaponFireBullets(const char[] sTEName, const int[] iPlayers, int numClients, float flDelay)
{
// Initialize weapon index
int weaponIndex;
// Get all required event info
int clientIndex = TE_ReadNum("m_iPlayer") + 1;
// If weapon isn't custom
if(!IsCustomItem(clientIndex, weaponIndex))
{
return;
}
// Initialize sound
char sSound[128];
Format(sSound, sizeof(sSound), "*/%s", SOUND_FIRE);
// Emit fire sound
EmitSoundToAll(sSound, clientIndex, SNDCHAN_WEAPON, SNDLEVEL_ROCKET);
EmitSoundToAll(sSound, clientIndex, SNDCHAN_STATIC, SNDLEVEL_NORMAL);
}
/**
* Event callback (bullet_impact)
* The bullet hits something.
*
* @param gEventHook The event handle.
* @param gEventName Name of the event.
* @param iDontBroadcast If true, event is broadcasted to all clients, false if not.
**/
public Action EventBulletImpact(Event gEventHook, const char[] gEventName, bool iDontBroadcast)
{
// Initialize weapon index
int weaponIndex;
// Get all required event info
int clientIndex = GetClientOfUserId(GetEventInt(gEventHook, "userid"));
// If weapon isn't custom
if(!IsCustomItem(clientIndex, weaponIndex))
{
return;
}
// Initialize vector variables
float flStart[3];
float flEnd[3];
// Get start position
GetClientEyePosition(clientIndex, flStart);
// Get end position
flEnd[0] = GetEventFloat(gEventHook, "x");
flEnd[1] = GetEventFloat(gEventHook, "y");
flEnd[2] = GetEventFloat(gEventHook, "z");
// Calculate weapon pos
float flDistance = GetVectorDistance(flStart, flEnd);
float flPercent = (0.4 / (flDistance / 100.0));
flStart[0] = flStart[0] + ((flEnd[0] - flStart[0]) * flPercent);
flStart[1] = flStart[1] + ((flEnd[1] - flStart[1]) * flPercent) - 0.08;
flStart[2] = flStart[2] + ((flEnd[2] - flStart[2]) * flPercent);
// Sent a beam
TE_SetupBeamPoints(flStart, flEnd, iBeamModel, 0 , 0, 0, BEAM_LIFE, 2.0, 2.0, 10, 1.0, BEAM_COLOR, 30);
TE_SendToAll();
}
//**********************************************
//* DAMAGE FUNCTIONS *
//**********************************************
/**
* Hook: OnTakeDamage
* Called right before damage is done.
*
* @param iVictim The client index.
* @param iAttacker The client index of the attacker.
* @param iInflicter The entity index of the inflicter.
* @param flDamage The amount of damage inflicted.
* @param pDamageBits The type of damage inflicted.
**/
public Action WeaponTakeDamage(int iVictim, int &iAttacker, int &iInflicter, float &flDamage, int &pDamageBits)
{
// Initialize weapon index
int weaponIndex;
// If weapon isn't custom
if(!IsCustomItem(iAttacker, weaponIndex))
{
return Plugin_Continue;
}
// Change damage
flDamage *= WEAPON_MULTIPLIER_DAMAGE;
return Plugin_Changed;
}
/**
* Hook: WeaponSwitchPost
* Called, when player deploy any weapon.
*
* @param clientIndex The client index.
* @param weaponIndex The weapon index.
**/
public void WeaponDeployPost(int clientIndex, int weaponIndex)
{
// If client just buy this custom weapon
if(bHasCustomWeapon[clientIndex])
{
// Reset bool
bHasCustomWeapon[clientIndex] = false;
// Verify that the weapon is valid
if(!IsValidEdict(weaponIndex))
{
return;
}
// Set custom name
DispatchKeyValue(weaponIndex, "globalname", WEAPON_NAME);
}
// If weapon isn't valid, then stop
if(!IsCustomItemEntity(weaponIndex))
{
return;
}
// Verify that the client is connected and alive
if(!IsPlayerExist(clientIndex))
{
return;
}
// Set weapon models
SetViewModel(clientIndex, weaponIndex, iViewModel);
SetWorldModel(weaponIndex, iWorldModel);
}
//**********************************************
//* OTHER FUNCTIONS *
//**********************************************
/**
* Called once a client is authorized and fully in-game, and
* after all post-connection authorizations have been performed.
*
* This callback is gauranteed to occur on all clients, and always
* after each OnClientPutInServer() call.
*
* @param clientIndex The client index.
**/
public void OnClientPutInServer(int clientIndex)
{
SDKHook(clientIndex, SDKHook_WeaponDropPost, WeaponDropPost)
SDKHook(clientIndex, SDKHook_WeaponSwitchPost, WeaponDeployPost);
SDKHook(clientIndex, SDKHook_OnTakeDamage, WeaponTakeDamage);
}
/**
* Called after dropping weapon.
*
* @param clientIndex The client index.
* @param weaponIndex The weapon index.
**/
public Action WeaponDropPost(int clientIndex, int weaponIndex)
{
// Set dropped model on next frame
RequestFrame(view_as<RequestFrameCallback>(SetDroppedModel), weaponIndex);
}
//**********************************************
//* STOCKS *
//**********************************************
/**
* Validate custom weapon and player.
*
* @param clientIndex The client index.
* @param weaponIndex The weapon index.
* @return True if valid, false if not.
**/
stock bool IsCustomItem(int clientIndex, int &weaponIndex)
{
// Validate client
if (!IsPlayerExist(clientIndex))
{
return false;
}
// Get weapon index
weaponIndex = GetEntPropEnt(clientIndex, Prop_Data, "m_hActiveWeapon");
// Verify that the weapon is valid
if(!IsValidEdict(weaponIndex))
{
return false;
}
// Get weapon classname
char sClassname[32];
GetEntityClassname(weaponIndex, sClassname, sizeof(sClassname));
// If weapon classname isn't equal, then stop
if(!StrEqual(sClassname, WEAPON_REFERANCE))
{
return false;
}
// Get weapon global name
GetEntPropString(weaponIndex, Prop_Data, "m_iGlobalname", sClassname, sizeof(sClassname));
// If weapon key isn't equal, then stop
if(!StrEqual(sClassname, WEAPON_NAME))
{
return false;
}
// If it is custom weapon
return true;
}
/**
* Validate custom weapon.
*
* @param weaponIndex The weapon index.
* @return True if valid, false if not.
**/
stock bool IsCustomItemEntity(int weaponIndex)
{
// Verify that the weapon is valid
if(!IsValidEdict(weaponIndex))
{
return false;
}
// Get weapon classname
char sClassname[32];
GetEntityClassname(weaponIndex, sClassname, sizeof(sClassname));
// If weapon classname isn't equal, then stop
if(!StrEqual(sClassname, WEAPON_REFERANCE))
{
return false;
}
// Get weapon global name
GetEntPropString(weaponIndex, Prop_Data, "m_iGlobalname", sClassname, sizeof(sClassname));
// If weapon key isn't equal, then stop
if(!StrEqual(sClassname, WEAPON_NAME))
{
return false;
}
// If it is custom weapon
return true;
}