There are 4 types of entities:
After you have decided what type of entity you want to create, you need to create the script-files. The location for the files depends on the entity type:
- All Lua-scripted vehicles are located in "lua/vehicles/"
- Weapons are located in "lua/weapons/"
- NPCs are located in "lua/npcs/"
- Everything else is located in "lua/entities/"
In this Tutorial we'll create a regular entity, however the concept is the same for the other types as well.
First you need to decide a class-name for your entity. This is the name that can be used to create the entity, it has to be lower-case, and mustn't contain any special characters (Use underscores instead of spaces).
Create a new directory with that name, e.g. "lua/entities/prop_tumbleweed".
In that directory, create three files:
The "shared.lua"-file isn't required, but recommended. It isn't executed automatically, and will have to be included in "init.lua" and "cl_init.lua" respectively. It contains all code which has to be used clientside and serverside. Here we will initialize the entity class and register it so it can actually be spawned:
class 'PropTumbleWeed' (BaseEntity) function PropTumbleWeed:__init() BaseEntity.__init(self) end ents.register("prop_tumbleweed",PropTumbleWeed)
class 'PropTumbleWeed' (BaseEntity)
This will register the global class "PropTumbleWeed", derived from the class "BaseEntity". "BaseEntity" is hardcoded, and is required to be the base-class for all regular entities.
function PropTumbleWeed:__init() BaseEntity.__init(self) end
This is the constructor, which is called when a new entity of this type is created. In general you shouldn't do anything here, except call the base constructor.
This call is required to register the entity with the engine. The first argument should be the same name as the directory, the second argument is the name of the Lua-class.
This file will automatically be executed serverside. If you want to create a clientside-only entity, you can omit this file.
include("shared.lua") PropTumbleWeed.Type = Entity.TYPE_SHARED function PropTumbleWeed:OnSpawn() self:SetModel("props_vegetation/tumbleweed01.wmd") local phys = self:InitializePhysics(phys.TYPE_DYNAMIC) if(phys ~= nil) then phys:WakeUp() end end
Since our "shared.lua"-file includes the class-declaration, and registers our entity, we have to include it first.
PropTumbleWeed.Type = Entity.TYPE_SHARED
We need to tell the engine that we want to create a shared entity. The default type is ENTITY_TYPE_DEFAULT, which is used for serverside-only entities.
function PropTumbleWeed:OnSpawn() self:SetModel("props_vegetation/tumbleweed01.wmd") local phys = self:InitializePhysics(phys.TYPE_DYNAMIC) if(phys ~= nil) then phys:WakeUp() end end
OnSpawn is called after the entity has been spawned, but before it has been transmitted to the clients.
Here we can set our model and initialize our physics.
The clientside-part of our entity doesn't do anything special, so just include the "shared.lua" to register it.
With these in place, you have already created your first working entity. It won't do much yet, but you can spawn it ingame using "ent_create prop_tumbleweed".
Next, we want to make the tumbleweed controllable by the player. The player should be able to go up to the entity, press the use-key, and control the tumbleweed with the movement keys.
To do so, we'll have to make our entity usable first. Create the following functions inside the "init.lua":
function PropTumbleWeed:OnUse(pl) self:StartControl(pl) end function PropTumbleWeed:IsInUse() return util.is_valid(self.m_owner) end function PropTumbleWeed:CanUse(pl) return not self:IsInUse() end function PropTumbleWeed:OnRemove() self:EndControl() end
OnUse is called when a player has pressed the use-key on the entity, but only if CanUse has returned true!
In our case we don't want to allow more than one player controlling the tumble weed at a time, so we return false in CanUse if it is already in use.
StartControl and EndControl still need to be programmed:
function PropTumbleWeed:StartControl(pl) self:EndControl() -- End previous control if still active self.m_owner = pl -- 'm_owner' is whoever controls this entity pl:SetObserverTarget(self) -- The controller's camera will follow this entity pl:SetObserverMode(Player.OBSERVERMODE_THIRDPERSON) -- We want a third-person camera pl:SetObserverOffset(Vector(0.0,20.0,-100.0)) -- This will place the camera 200 units away from the origin of the entity, and slightly up self.m_cbAction = pl:AddCallback("OnActionInput",function(action,bPressed) if(self:IsValid() == false) then return end if(action == ACTION_USE and bPressed == true) then self:EndControl() return false -- Overwrite default action end end) end
function PropTumbleWeed:EndControl() local pl = self.m_owner if(util.is_valid(pl) == true) then pl:ResetObserverOffset() pl:SetObserverMode(Player.OBSERVERMODE_FIRSTPERSON) pl:SetObserverTarget(pl) end if(util.is_valid(self.m_cbAction) == true) then self.m_cbAction:Remove() end self.m_owner = nil end