Creating entities

From Pragma
Jump to: navigation, search

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:

shared.lua

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)

Explanation:

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.

ents.register("prop_tumbleweed",PropTumbleWeed)

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.

init.lua

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(PHYSICSTYPE_DYNAMIC)
	if(phys ~= nil) then
		phys:WakeUp()
	end
end

Explanation:

include("shared.lua")

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(PHYSICSTYPE_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.

cl_init.lua

include("shared.lua")

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(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(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