Raised This Month: $32 Target: $400
 8% 

[ TUT ] WeaponBox entities


Post New Thread Reply   
 
Thread Tools Display Modes
Author Message
HamletEagle
AMX Mod X Plugin Approver
Join Date: Sep 2013
Location: Romania
Old 04-13-2015 , 09:19   [ TUT ] WeaponBox entities
Reply With Quote #1

Before you start reading, please note that this tutorial is recommended for intermediate coders. Although I will explain everything related to weaponboxs, I won't cover the basic about entities and/or pawn coding, beginners may be confused. If you are un such situation, add a comment with which confused you and I will do my best to clarify it.

Now, let's start by seeing what is a weaponbox entity. According to the wiki, it is "a canister full of specific weapon/ammo". In other words, this entity hold one or more weapons(this will be covered later) and associated ammo for them. It is just a box in which exists a weapon_* entity. Such entities are created when you drop a weapon.

1.What you need to know before we get into more advanced stuffs is how to create an entity.

Code:
new Entity = create_entity("classname") //Or by fakemeta IntClassNameString = engfunc(EngFunc_AllocString, "classname") //cache this value new Entity = engfunc(EngFunc_CreateNamedEntity, IntClassNameString)

With fakemeta way another step is requiered. You must obtain an offset from the classname string. EngFunc_CreateNamedEntity calls pfnCreateNamedEntity which expect and integer value of the string. I would recommend to cache this offset, because for the same string it won't be different, and doing this operantion for a huge number of entities is an expensive operation.
Engine does this step internally(int iszClass = AmxStringToEngine(amx, params[1], len);)

Weaponbox entity is removed on next round, but what you see when you use the snippet above ? This doesn not happen. Why ? I won't go in deep, can read here more: https://forums.alliedmods.net/showthread.php?p=2235917
It's because for each new entity game create a hash value, and it loop through it. This means that entities are cached, and with amxmodx < 1.8.3 you don't have a way to alter the hash table without orpheu/okapi. In CS, a custom function called CREATE_NAMED_ENTITY exists and it calls both AddEntityHashValue and pfnCreateNamedEntity

All this stuffs, about hash table is cs specific. I don't know how it is in other mods, but I guess that simply using pfnCreateNamedEntity is enough.

Solution for AmxModX < 1.8.3:
PHP Code:
IntClassNameString engfunc(EngFunc_AllocString"classname"//cache this value

new OrpheuFunction:HandleCreateNamedEntityFunc OrpheuGetFunction("CREATE_NAMED_ENTITY")
new 
Entity OrpheuCall(HandleCreateNamedEntityFuncIntClassNameString
Signature can be obtained from the topic linked above.

Solution for AmxModX >= 1.8.3:
PHP Code:
new Entity cs_create_entity("classname"
This was a bit generic, for all entities. If you want to create a weaponbox entity, you must use weaponbox as classname.

2.Spawning a weaponbox: is enough to call Ham_Spawn on entity index obtained when creating the entity.

3.Adding a weapon to weaponbox:

Ideally, adding a weapon to weaponbox can be done by calling CWeaponBox:ackWeapon.

PHP Code:
new OrpheuFunction:HandlePackWeaponFunc
HandlePackWeaponFunc 
OrpheuGetFunction("PackWeapon" "CWeaponBox")

OrpheuCall(HandlePackWeaponFuncWeaponBoxEntityWeaponEntity
For parameters:
HandlePackWeaponFunc: the function that will be called
WeaponBoxEntity: the weaponbox entity in which you want to add that weapon
WeaponEntity: the weapon entity that must be added inside weaponbox

In theory a weaponbox can contain as many weapons as you want(but only one weapon of the same type - e.g. you can't have two ak47 inside same weaponbox), but practically it contains only one weapon. This weapons are linked with m_pNext offset(const m_pNext = 42 ).
WeaponEntity can be obtained from a player entity's weapon slot with m_rgpPlayerItems_CBasePlayer offsets(new const m_rgpPlayerItems_CBasePlayer[6] = {367, 368, ...}. It starts from 1(as weapon slots). You should use get_pdata_cbase on them. Keep in mind that XO_Player is 5.

PHP Code:
new WeaponBoxEntity OrpheuCall(HandleCreateNamedEntityFuncIntClassNameString)
new 
WeaponEntity get_pdata_cbase(idm_rgpPlayerItems_CBasePlayer[slot], XO_Player)

if(
pev_valid(WeaponEntity))
{
    
OrpheuCall(HandlePackWeaponFuncWeaponBoxEntityWeaponEntity)

Game already remove the weapon from player, but you will still see it in your hud. To fix that:
PHP Code:
new WeaponIndex cs_get_weapon_id(WeaponEntity)
if(
CSW_P228 <= WeaponIndex <= CSW_P90)
{
    
user_has_weapon(indexWeaponIndex0)

WeaponEntity is weapon entity index, by using cs_get_weapon_id we obtain the CSW_* index, which we can use with user_has_weapon.

4. Adding ammo for that weapon:

This is a bit difficult, more than packing a weapon. I have decided to hook CWeaponBox:ackAmmo, which calls CWeaponBox::GiveAmmo after doing some checks regarding ammo name and max ammo for that type.

To understand why I'm saying that is a bit difficult I will provide firstly the code:
PHP Code:
new IntAmmoNames[16]

//Maybe in plugin_init or where you need them
new const StringAmmoNames[][] = 
{
    
""
    
"338Magnum"
    
"762Nato",
    
"556NatoBox"
    
"556Nato",
    
"buckshot"
    
"45ACP"
    
"57mm"
    
"50AE"
    
"357SIG",
    
"9mm"
    
"Flashbang",
    
"HEGrenade"
    
"SmokeGrenade"
    
"C4"
}

new 
i
for(1sizeof StringAmmoNamesi++)
{
    
IntAmmoNames[i] = engfunc(EngFunc_AllocStringStringAmmoNames[i]) //caching this
}

new 
OrpheuFunction:HandlePackAmmoFunc OrpheuGetFunction("PackAmmo""CWeaponBox")

new 
AmmoIndex ExecuteHam(Ham_Item_PrimaryAmmoIndexWeaponEntity)
if(
AmmoIndex)
{
    
OrpheuCall(HandlePackAmmoFuncWeaponBoxEntityIntAmmoNames[AmmoIndex], ammo)

Firstly, convert ammo string to an offset, as we did several times before, but for other strings. Then, find the needed ammo type based on AmmoIndex.

Notice that in CS, weapons like knife/grenades are not mean to be droped. This is a bitsum of weapons with that restriction:
PHP Code:
new const NoDrop = (<< CSW_HEGRENADE) | (<< CSW_SMOKEGRENADE) | (<< CSW_FLASHBANG) | (<< CSW_KNIFE) |(<< CSW_VEST) | (<< CSW_VESTHELM
Then you can check:
PHP Code:
if(NoDrop & ( << CSW_*)) 
5.Killing an weaponbox:

At some point you may need to remove a weaponbox, before the game does this automatically. This is pretty simple. Think() can be called in order to remove it, or CWeaponBox:Kill

PHP Code:
set_pev(weaponboxpev_nextthinkget_gametime( ) + 0.1)
call_think(weaponbox)
ExecuteHam(Ham_Thinkweaponbox)

//Or with orpheu
new OrpheuFunction:HandleKillFunc
HandleKillFunc  
OrpheuGetFunction("Kill""CWeaponBox")
OrpheuCall(HandleKillFuncweaponbox
See that here are several ways of doing it. I would use one of the first 3, it's a bit faster(1 natives vs 2 natives) and does not need a signature file.

WeaponBox offsets can be found here: https://wiki.alliedmods.net/CWeaponBox_(CS)
WeaponBox functions code can be found here: https://github.com/Arkshine/CSSDK/bl...ls/weapons.cpp

Now, for the sake of working a bit with offsets, let's look at how we can redo CWeaponBox::HasWeapon.

PHP Code:
new ItemSlot ExecuteHam(Ham_Item_ItemSlotWeaponEntity)  //get the slot of the weapon we want to check(the one from player)
//m_rgpPlayerItems_CWeaponBox are the items that match player slot from weaponbox
new WpnBoxWeapon get_pdata_cbase(WeaponBoxEntitym_rgpPlayerItems_CWeaponBox[ItemSlot], XO_CWeaponBox//get the weapon from weaponbox that match the slot

//Failed, no valid weapon
if(!pev_valid(WpnBoxWeapon))
{
    
log_error(AMX_ERR_NATIVE"Invalid weapon in weaponbox")
    return 
0
}

//Get first weapon classname
const XO_CWeaponBox 4
new ItemClassName[20], WeaponClassName[20]
pev(Itempev_classnameItemClassNamecharsmax(ItemClassName))
while(
pev_valid(WpnBoxWeapon))
{
    
//Get second weapon classname
    
pev(WpnBoxWeaponpev_classnameWeaponClassNamecharsmax(WeaponClassName))
    if(
equal(ItemClassNameWeaponClassName))//check is done by classname
    
{
        
//If classnames match, then we found something
        
return 1
    
}
    
WpnBoxWeapon get_pdata_cbase(WpnBoxWeaponm_pNextXO_CWeaponBox)//get the next weapon 
    //As said, weapons are linked with m_pNext offset
    //Here we obtain next weapon index from the previous one

Some peoples swap the two offset groups:
PHP Code:
//Slots are the same for same weapon
new const m_rgpPlayerItems_CBasePlayer[6] = {367368, ...} //items from player. Use XO_Player
new const m_rgpPlayerItems_CWeaponBox[6] = {3435, ...} //items from weaponbox. Use XO_CWeaponBox 
XO_CWeaponBox, XO_Player are the difference from windows to linux, when it comes to offset. Linux is: windows offset + XO_*.
For most player offset this is 5, for weapons is 4. This is true for converted offsets, that can be used with get/set_pdata_int/cbase/char/byte and so on.

One more thing that needs to be stated. Game will create a weaponbox with models/w_weaponbox.mdl. You need to set the right model for the weapon manually.

PHP Code:
new const WeaponBoxModels[][] =
{
    
"""models/w_p228.mdl",  "",
    
"models/w_scout.mdl",  """models/w_xm1014.mdl",
    
"models/w_c4.mdl""models/w_mac10.mdl""models/w_aug.mdl""",
    
"models/w_elite.mdl""models/w_fiveseven.mdl",  "models/w_ump45.mdl"
    
"models/w_sg550.mdl""models/w_galil.mdl",  "models/w_famas.mdl"
    
"models/w_usp.mdl""models/w_glock18.mdl""models/w_awp.mdl"
    
"models/w_mp5.mdl""models/w_m249.mdl",  "models/w_m3.mdl",
    
"models/w_m4a1.mdl""models/w_tmp.mdl""models/w_g3sg1.mdl"
    
"""models/w_deagle.mdl""models/w_sg552.mdl""models/w_ak47.mdl",
    
"""models/w_p90.mdl",  """"
}

new 
WeaponId cs_get_weapon_id(WeaponEntity)
if(
WeaponBoxModels[WeaponId][0] != EOS)
{
    
engfunc(EngFunc_SetModelWeaponBoxEntityWeaponBoxModels[WeaponId])

Empty fields are from weapons that can't be dropped.
WeaponEntity is the entity obtained from m_rgpPlayerItems_CBasePlayer. Check is done to prevent crash when trying to apply an empty model.
On weaponbox created by game directly, offsets are not filled until the model is changed(model is no longer models/w_weaponbox.mdl). This means that pfnSetModel is send 2 time for a weaponbox.

The signatures used here can be found at: https://forums.alliedmods.net/showthread.php?t=260124

As always, suggetions/improvements/corrections/questions are welcomed.
__________________

Last edited by HamletEagle; 04-14-2015 at 09:19.
HamletEagle is offline
Kia
AlliedModders Donor
Join Date: Apr 2010
Location: In a world of madness
Old 04-13-2015 , 13:25   Re: [ TUT ] WeaponBox entities
Reply With Quote #2

Nice guide, good job.
__________________
Kia is offline
joropito
AlliedModders Donor
Join Date: Mar 2009
Location: pfnAddToFullPack
Old 04-14-2015 , 08:24   Re: [ TUT ] WeaponBox entities
Reply With Quote #3

I really don't understand why you keep adding orpheu examples. It's not part of base amxmodx (that should be in a separate thread).

Anyway, the only correct way to remove a weaponbox it's to call Think immediately.
You mix things when you try to set pev_nextthink. That's a way to tell engine to call Think on next loop.

If you use nextthink, you should warn that "something" could happen before it gets deleted.
For example entities with lower id will Think before and that could break your game logic.
__________________

Divide et vinces
approved plugins | steam account

I don't accept PM for support. Just ask on forums.
If you're looking for private work, PM me.
joropito is offline
Send a message via MSN to joropito
HamletEagle
AMX Mod X Plugin Approver
Join Date: Sep 2013
Location: Romania
Old 04-14-2015 , 08:38   Re: [ TUT ] WeaponBox entities
Reply With Quote #4

Quote:
I really don't understand why you keep adding orpheu examples. It's not part of base amxmodx (that should be in a separate thread).
With orpheu you can hook the functions that allow you to create, pack weapon/ammo inside an weaponbox. It doesn't matter if it's part of base amxmodx or not IMO. It matter that it can do the job.
I'm not sure to understand your point of view.

Quote:
Anyway, the only correct way to remove a weaponbox it's to call Think immediately.
If CWeaponBox::Kill works fine for game why not in an amxx plugin ? It basically call think, nothing more. Feel free to explain.
__________________

Last edited by HamletEagle; 04-14-2015 at 08:38.
HamletEagle is offline
man_s_our
Senior Member
Join Date: Jul 2017
Location: aim_taliban
Old 03-21-2018 , 18:21   Re: [ TUT ] WeaponBox entities
Reply With Quote #5

how to use get_pdata_cbase to get the car or train driver?
__________________
man_s_our is offline
Reply


Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 16:54.


Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.
Theme made by Freecode