Before we begin, please be aware that this tutorial is aimed toward scripters of intermediate level that understand Pawn conceptually but are still sketchy when it comes to implementation. Beginners may get confused, and experts may find they already know all this.
An entity is basically anything in the game that can change. The map is not an entity - it is impossible to change. You can do something like put a spraypaint on it, but that's not changing the map per se; rather it simply paints another texture on the location you sprayed.
Lights, players, the bomb, weapons (when on the ground, or even while being held in some mods) and breakable glass are all examples of entities. But what is important about ent(ities -- from now on they will be abbreviated to 'ents')? As said above, they can change. This means you can modify gameplay or make the game look different with them.
One of the most basic things that must be learned about entities is how to spawn one. Throughout this tutorial, I will post both an engine and fakemeta method of doing things, so here is one example:
Code:
// Engine
new Ent = create_entity("classname")
Code:
// Fakemeta
new Ent = engfunc(EngFunc_CreateNamedEntity,engfunc(EngFunc_AllocString,"classname"))
Now what do we see here that I haven't explained? What is this "classname"? A classname is given to all ents to help the engine determine what it is. If we stopped reading this tutorial and went outside, looking at various things, a person walking by might be classname "human" (however players in game are classname "player"), a tree might be classname "tree", a car might be "func_car" (func_ since it's a usable entity or it serves a purpose) and a cat might be "monster_cat" (monster_ because it's an NPC that may or may not be hostile). What can we do with the classname? We can use it to implement game functions (if you name an ent "hostage_entity" then Counter-Strike will automatically assume it's a hostage) or we can search for it (using find_ent_by_class or similar functions) among other things.
An entity also has other values. These values, such as health, target, targetname, gravity, movetype, etc. are all defined in part of HLSDK called the "edict_t" structure. An edict is effectively the basis for an entity because of this.
If you have any experience with AMXX scripting, you've probably ran into the function set_user_health. You may not understand what it does, but you know it works. What it basically does is go into the player's edict_t structure and change the "health" value (which is actually a float, not an int) to whatever you specify. Let's look at how it actually implements this though. Let's say for example we want to set a player's health to 200.
In fun, it looks like this:
Code:
set_user_health(id,200)
But in engine/fakemeta, it looks like this:
Code:
// engine
entity_set_float(id,EV_FL_health,200.0)
// fakemeta
set_pev(id,pev_health,200.0)
We obviously have more power this way. It looks much more confusing, but rather than simply making a "set_user_x" (where x is gravity, targetname, or what not) we have access to the entire edict_t structure (which links to the entvars structure, which is where all the information really is).
But what about this "entity_set_float"? There are 12 functions when it comes to engine and manipulating the entvars of an ent:
entity_get_int
entity_get_float
entity_get_string
entity_get_vector
entity_get_edict
entity_get_byte
entity_set_int
entity_set_float
entity_set_string
entity_set_vector
entity_set_edict
entity_set_byte
Additionally, we have some other functions that do similar things:
entity_set_origin
entity_set_model
entity_set_size
Note that these are not the same as simply using entity_set_vector/string, as they actually notify the engine (not the module) of the changes.
We have similar functions in fakemeta, however there are only 2 that do the same thing as these:
pev
set_pev
Because there is only 2, we have to be much more careful with fakemeta, since we could accidentally pass a float when the pev expects an int, or something to that effect.
There are also other functions which can be executed through engfunc and dllfunc (as shown near the top with the new Ent = engfunc... bit).
But now, how can these be implemented? We've already seen above how we can avoid fun and instead use engine/fakemeta, but what are some other things we can do with ents?
Well, let's say we want to teleport a player to a spot on the map. The first thing we must understand about where we want to teleport them, is that the location will be a vector. Why? Because a vector is a 3 cell array that contain data that generally acts as an x,y,z axis. We must also understand that all vectors implemented with engine/fakemeta are floats. Here is an example of a vector:
Code:
// static vector
Float:{0.0,1.0,2.0}
// dynamic vector
new Float:Vector[3]
Now, how is this relevant to setting a player's location? We must first look up how we set the entity's origin. After looking through the
funcwiki we come across this:
entity_set_origin
So what do we do? First, we find out where we want to teleport the player. Let's say we want to teleport them to 50,-30,1000. We must first convert this to a vector:
Code:
new Float:Origin[3] = {50.0,-30.0,1000.0}
Then set their origin.
Code:
// engine
entity_set_origin(id,Origin)
// fakemeta
engfunc(EngFunc_SetOrigin,id,Origin)
When creating an entity, one of the most important things is to set its owner. Say, for example, a player types "car" in their console which creates a car in front of them, that can only be used by them. This can be done by setting their owner, and then checking when the ent is used if the owner is the person using it. Here is an example:
Code:
// engine
#include <amxmodx>
#include <amxmisc>
#include <engine>
new g_Classname[] = "func_car"
new g_Model[] = "models/car.mdl"
public plugin_init()
{
register_plugin("OMFG","HAX","LOL")
register_clcmd("say car","CmdSayCar")
register_touch(g_Classname,"player","TouchCar")
}
public plugin_precache()
precache_model(g_Model)
public CmdSayCar(id)
{
new Ent = create_entity("info_target")
entity_set_string(Ent,EV_SZ_classname,g_Classname)
entity_set_model(Ent,g_Model)
entity_set_int(Ent,EV_INT_solid,SOLID_TRIGGER)
entity_set_size(Ent,Float:{-50.0,-50.0,-50.0},Float:{50.0,50.0,50.0})
entity_set_edict(Ent,EV_ENT_owner,id)
}
public TouchCar(Ptd,Ptr)
{
new Owner = entity_get_edict(Ptd,EV_ENT_owner)
if(Owner != Ptr)
return
client_print(Ptr,print_chat,"You have used this car. Due to time purposes, I'm not going to add code beyond this.")
}
Code:
// fakemeta
#include <amxmodx>
#include <amxmisc>
#include <fakemeta>
new g_Classname[] = "func_car"
new g_Model[] = "models/car.mdl"
public plugin_init()
{
register_plugin("OMFG","HAX","LOL")
register_clcmd("say car","CmdSayCar")
register_forward(FM_Touch,"ForwardTouch")
}
public plugin_precache()
precache_model(g_Model)
public CmdSayCar(id)
{
new Ent = engfunc(EngFunc_CreateNamedEntity,engfunc(EngFunc_AllocString,"info_target"))
set_pev(Ent,pev_classname,g_Classname)
engfunc(EngFunc_SetModel,Ent,g_Model)
set_pev(Ent,pev_solid,SOLID_TRIGGER)
engfunc(EngFunc_SetSize,Ent,Float:{-50.0,-50.0,-50.0},Float:{50.0,50.0,50.0})
set_pev(Ent,pev_owner,id)
}
public ForwardTouch(Ptd,Ptr)
{
new Classname[33],Ent = Ptd,id = Ptr
pev(Ptd,pev_classname,Classname,32)
if(!equal(Classname,g_Classname))
{
Ent = Ptr
id = Ptd
pev(Ptr,pev_classname,Classname,32)
if(!equal(Classname,g_Classname))
return
}
new Owner = pev(Ent,pev_owner)
if(Owner != id)
return
client_print(id,print_chat,"You have used this car. Due to time purposes, I'm not going to add code beyond this.")
}
With these examples, we went into a few things that we haven't before. First of all - solidity. When we boil water and steam floats up from it, if we put our hands through they do not get stopped after they touch the first water droplet. If go poke the nearest person, our finger will get stopped once it makes contact with them. This can be represented through solidity in the HL engine. There are 5 different solid types, which can be found here:
http://www.amxmodx.org/funcwiki.php?...=3#const_solid
The one we used in this case was SOLID_TRIGGER, because we want the player to trigger a touch when they touch the car (even though it will still trigger on BBOX or BSP).
Another concept we have yet to go over is entity_set_size / engfunc(EngFunc_SetSize..., which allows you to set the size of an entity. This is important because without it, the ent's solidity will not matter. If the entity is 0 units on the x,y,z axis, then it is by definition SOLID_NOT, even if we set it to SOLID_BSP/_BBOX. No matter how big the model is, it's the size that really matters.
This is already way too long, so I'm going to end this here. If you have any questions about this or any way to improve upon it, feel free to post.
Additional information:
The list of current Engine (module) entvar types can be found here (and below it):
http://www.amxmodx.org/funcwiki.php?...#const_ent_int
and the list of current Fakemeta entvars can be found here:
http://www.amxmodx.org/funcwiki.php?go=module&id=16
__________________