mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 05:43:46 +03:00
Upload
This commit is contained in:
698
lua/includes/extensions/entity.lua
Normal file
698
lua/includes/extensions/entity.lua
Normal file
@@ -0,0 +1,698 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
local meta = FindMetaTable( "Entity" )
|
||||
|
||||
-- Return if there's nothing to add on to
|
||||
if ( !meta ) then return end
|
||||
|
||||
function meta:GetShouldPlayPickupSound()
|
||||
return self.m_bPlayPickupSound or false
|
||||
end
|
||||
|
||||
function meta:SetShouldPlayPickupSound( bPlaySound )
|
||||
self.m_bPlayPickupSound = tobool( bPlaySound ) or false
|
||||
end
|
||||
|
||||
--
|
||||
-- Entity index accessor. This used to be done in engine, but it's done in Lua now because it's faster
|
||||
--
|
||||
function meta:__index( key )
|
||||
|
||||
--
|
||||
-- Search the metatable. We can do this without dipping into C, so we do it first.
|
||||
--
|
||||
local val = meta[ key ]
|
||||
if ( val != nil ) then return val end
|
||||
|
||||
--
|
||||
-- Search the entity table
|
||||
--
|
||||
local tab = self:GetTable()
|
||||
if ( tab ) then
|
||||
local tabval = tab[ key ]
|
||||
if ( tabval != nil ) then return tabval end
|
||||
end
|
||||
|
||||
--
|
||||
-- Legacy: sometimes use self.Owner to get the owner.. so lets carry on supporting that stupidness
|
||||
-- This needs to be retired, just like self.Entity was.
|
||||
--
|
||||
if ( key == "Owner" ) then return meta.GetOwner( self ) end
|
||||
|
||||
return nil
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: Short cut to add entities to the table
|
||||
-----------------------------------------------------------]]
|
||||
function meta:GetVar( name, default )
|
||||
|
||||
local Val = self:GetTable()[ name ]
|
||||
if ( Val == nil ) then return default end
|
||||
|
||||
return Val
|
||||
|
||||
end
|
||||
|
||||
if ( SERVER ) then
|
||||
|
||||
function meta:SetCreator( ply --[[= NULL]] )
|
||||
if ( ply == nil ) then
|
||||
ply = NULL
|
||||
elseif ( !isentity( ply ) ) then
|
||||
error( "bad argument #1 to 'SetCreator' (Entity expected, got " .. type( ply ) .. ")", 2 )
|
||||
end
|
||||
|
||||
self.m_PlayerCreator = ply
|
||||
end
|
||||
|
||||
function meta:GetCreator()
|
||||
return self.m_PlayerCreator or NULL
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: Returns true if the entity has constraints attached to it
|
||||
-----------------------------------------------------------]]
|
||||
function meta:IsConstrained()
|
||||
|
||||
if ( CLIENT ) then return self:GetNWBool( "IsConstrained" ) end
|
||||
|
||||
local c = self:GetTable().Constraints
|
||||
local bIsConstrained = false
|
||||
|
||||
if ( c ) then
|
||||
|
||||
for k, v in pairs( c ) do
|
||||
if ( IsValid( v ) ) then bIsConstrained = true break end
|
||||
c[ k ] = nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
self:SetNWBool( "IsConstrained", bIsConstrained )
|
||||
return bIsConstrained
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: Short cut to set tables on the entity table
|
||||
-----------------------------------------------------------]]
|
||||
function meta:SetVar( name, value )
|
||||
|
||||
self:GetTable()[ name ] = value
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: CallOnRemove
|
||||
Desc: Call this function when this entity dies.
|
||||
Calls the function like Function( <entity>, <optional args> )
|
||||
-----------------------------------------------------------]]
|
||||
function meta:CallOnRemove( name, func, ... )
|
||||
|
||||
local mytable = self:GetTable()
|
||||
mytable.OnDieFunctions = mytable.OnDieFunctions or {}
|
||||
|
||||
mytable.OnDieFunctions[ name ] = { Name = name, Function = func, Args = { ... } }
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: RemoveCallOnRemove
|
||||
Desc: Removes the named hook
|
||||
-----------------------------------------------------------]]
|
||||
function meta:RemoveCallOnRemove( name )
|
||||
|
||||
local mytable = self:GetTable()
|
||||
mytable.OnDieFunctions = mytable.OnDieFunctions or {}
|
||||
mytable.OnDieFunctions[ name ] = nil
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Simple mechanism for calling the die functions.
|
||||
-----------------------------------------------------------]]
|
||||
local function DoDieFunction( ent )
|
||||
|
||||
if ( !ent or !ent.OnDieFunctions ) then return end
|
||||
|
||||
for k, v in pairs( ent.OnDieFunctions ) do
|
||||
|
||||
-- Functions aren't saved - so this could be nil if we loaded a game.
|
||||
if ( v && v.Function ) then
|
||||
|
||||
v.Function( ent, unpack( v.Args ) )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
hook.Add( "EntityRemoved", "DoDieFunction", DoDieFunction )
|
||||
|
||||
function meta:PhysWake()
|
||||
|
||||
local phys = self:GetPhysicsObject()
|
||||
if ( !IsValid( phys ) ) then return end
|
||||
|
||||
phys:Wake()
|
||||
|
||||
end
|
||||
|
||||
local GetColorOriginal4 = meta.GetColor4Part -- Do not use me! I will be removed
|
||||
local GetColorOriginal = meta.GetColor
|
||||
function meta:GetColor()
|
||||
|
||||
-- Backwards comp slower method
|
||||
if ( !GetColorOriginal4 ) then
|
||||
return GetColorOriginal( self )
|
||||
end
|
||||
|
||||
return Color( GetColorOriginal4( self ) )
|
||||
|
||||
end
|
||||
|
||||
local SetColorOriginal4 = meta.SetColor4Part -- Do not use me! I will be removed
|
||||
local SetColorOriginal = meta.SetColor
|
||||
function meta:SetColor( col )
|
||||
|
||||
-- Backwards comp slower method
|
||||
if ( !SetColorOriginal4 ) then
|
||||
return SetColorOriginal( self, col )
|
||||
end
|
||||
|
||||
-- Even more backwards compat
|
||||
if ( !col ) then
|
||||
return SetColorOriginal4( self, 255, 255, 255, 255 )
|
||||
end
|
||||
|
||||
SetColorOriginal4( self, col.r, col.g, col.b, col.a )
|
||||
|
||||
end
|
||||
|
||||
function meta:GetChildBones( bone )
|
||||
|
||||
local bonecount = self:GetBoneCount()
|
||||
if ( bonecount == 0 or bonecount < bone ) then return end
|
||||
|
||||
local bones = {}
|
||||
|
||||
for k = 0, bonecount - 1 do
|
||||
if ( self:GetBoneParent( k ) != bone ) then continue end
|
||||
table.insert( bones, k )
|
||||
end
|
||||
|
||||
return bones
|
||||
|
||||
end
|
||||
|
||||
function DTVar_ReceiveProxyGL( ent, name, id, val )
|
||||
if ( ent.CallDTVarProxies ) then
|
||||
ent:CallDTVarProxies( name, id, val )
|
||||
end
|
||||
end
|
||||
|
||||
function meta:InstallDataTable()
|
||||
|
||||
self.dt = {}
|
||||
local typetable = {}
|
||||
local datatable = {}
|
||||
local keytable = {}
|
||||
local dtmeta = {}
|
||||
local editing = {}
|
||||
|
||||
dtmeta.__index = function ( ent, key )
|
||||
|
||||
local dt = datatable[ key ]
|
||||
if ( dt == nil ) then return end
|
||||
|
||||
return dt.GetFunc( self, dt.index, key )
|
||||
|
||||
end
|
||||
|
||||
dtmeta.__newindex = function( ent, key, value )
|
||||
|
||||
local dt = datatable[ key ]
|
||||
if ( dt == nil ) then return end
|
||||
|
||||
dt.SetFunc( self, dt.index, value )
|
||||
|
||||
end
|
||||
|
||||
local function FindUnusedIndex( typename )
|
||||
|
||||
local tbl = typetable[ typename ]
|
||||
if ( !tbl ) then return 0 end
|
||||
|
||||
for i = 0, 31 do
|
||||
if ( !tbl[i] ) then return i end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
self.IsDTVarSlotUsed = function( ent, typename, index )
|
||||
|
||||
local tbl = typetable[ typename ]
|
||||
if ( !tbl or !tbl[index] ) then return false end
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
self.DTVar = function( ent, typename, index, name )
|
||||
|
||||
if ( isstring( index ) && !name ) then
|
||||
name = index
|
||||
index = FindUnusedIndex( typename )
|
||||
elseif ( !index && isstring( name ) ) then
|
||||
index = FindUnusedIndex( typename )
|
||||
end
|
||||
|
||||
local SetFunc = ent[ "SetDT" .. typename ]
|
||||
local GetFunc = ent[ "GetDT" .. typename ]
|
||||
|
||||
if ( !SetFunc or !GetFunc ) then
|
||||
MsgN( "Couldn't addvar ", name, " - type ", typename, " is invalid!" )
|
||||
return
|
||||
end
|
||||
|
||||
local data = {
|
||||
index = index,
|
||||
name = name,
|
||||
SetFunc = SetFunc,
|
||||
GetFunc = GetFunc,
|
||||
typename = typename,
|
||||
Notify = {}
|
||||
}
|
||||
|
||||
typetable[ typename ] = typetable[ typename ] or {}
|
||||
typetable[ typename ][ index ] = data
|
||||
datatable[ name ] = data
|
||||
|
||||
return data
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- Access to the editing table
|
||||
--
|
||||
self.GetEditingData = function()
|
||||
return editing
|
||||
end
|
||||
|
||||
--
|
||||
-- Adds an editable variable.
|
||||
--
|
||||
self.SetupEditing = function( ent, name, keyname, data )
|
||||
|
||||
if ( !data ) then return end
|
||||
|
||||
if ( !data.title ) then data.title = name end
|
||||
|
||||
editing[ keyname ] = data
|
||||
|
||||
end
|
||||
|
||||
self.SetupKeyValue = function( ent, keyname, kvtype, setfunc, getfunc, other_data )
|
||||
|
||||
keyname = keyname:lower()
|
||||
|
||||
keytable[ keyname ] = {
|
||||
KeyName = keyname,
|
||||
Set = setfunc,
|
||||
Get = getfunc,
|
||||
Type = kvtype
|
||||
}
|
||||
|
||||
if ( other_data ) then
|
||||
|
||||
table.Merge( keytable[ keyname ], other_data )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
local CallProxies = function( ent, tbl, name, oldval, newval )
|
||||
|
||||
for i = 1, #tbl do
|
||||
tbl[ i ]( ent, name, oldval, newval )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
self.CallDTVarProxies = function( ent, typename, index, newVal )
|
||||
|
||||
local t = typetable[ typename ] && typetable[ typename ][ index ] or nil
|
||||
if ( t ) then
|
||||
CallProxies( ent, t.Notify, t.name, t.GetFunc( ent, index ), newVal )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
self.NetworkVar = function( ent, typename, index, name, other_data )
|
||||
|
||||
if ( isstring( index ) && ( istable( name ) or !name ) ) then
|
||||
other_data = name
|
||||
name = index
|
||||
index = FindUnusedIndex( typename )
|
||||
elseif ( !index && isstring( name ) ) then
|
||||
index = FindUnusedIndex( typename )
|
||||
end
|
||||
|
||||
local t = ent.DTVar( ent, typename, index, name )
|
||||
|
||||
-- Some addons call these on the entity table, and that used to work, so we keep that
|
||||
ent[ "Set" .. name ] = function( selfent, value )
|
||||
CallProxies( ent, t.Notify, name, t.GetFunc( ent, index ), value )
|
||||
t.SetFunc( ent, index, value )
|
||||
end
|
||||
|
||||
ent[ "Get" .. name ] = function( selfent )
|
||||
return t.GetFunc( ent, index )
|
||||
end
|
||||
|
||||
if ( !other_data ) then return end
|
||||
|
||||
-- This KeyName stuff is absolutely unnecessary, there's absolutely no reason for it to exist
|
||||
-- But we cannot remove it now because dupes will break. It should've used the "name" variable
|
||||
if ( other_data.KeyName ) then
|
||||
ent:SetupKeyValue( other_data.KeyName, typename, ent[ "Set" .. name ], ent[ "Get" .. name ], other_data )
|
||||
ent:SetupEditing( name, other_data.KeyName, other_data.Edit )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- Add a function that gets called when the variable changes
|
||||
-- Note: this doesn't work on the client yet - which drastically reduces its usefulness.
|
||||
--
|
||||
self.NetworkVarNotify = function( ent, name, func )
|
||||
|
||||
if ( !datatable[ name ] ) then error( "calling NetworkVarNotify on missing network var " .. name ) end
|
||||
|
||||
table.insert( datatable[ name ].Notify, func )
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- Create an accessor of an element. This is mainly so you can use spare
|
||||
-- network vars (vectors, angles) to network single floats.
|
||||
--
|
||||
self.NetworkVarElement = function( ent, typename, index, element, name, other_data )
|
||||
|
||||
if ( isstring( index ) && isstring( element ) ) then
|
||||
other_data = name
|
||||
name = element
|
||||
element = index
|
||||
index = FindUnusedIndex( typename )
|
||||
elseif ( !index && isstring( name ) ) then
|
||||
index = FindUnusedIndex( typename )
|
||||
end
|
||||
|
||||
local t = ent.DTVar( ent, typename, index, name )
|
||||
t.element = element
|
||||
|
||||
ent[ "Set" .. name ] = function( selfent, value )
|
||||
local old = t.GetFunc( selfent, index )
|
||||
old[ element ] = value
|
||||
t.SetFunc( selfent, index, old )
|
||||
end
|
||||
|
||||
ent[ "Get" .. name ] = function( selfent )
|
||||
return t.GetFunc( selfent, index )[ element ]
|
||||
end
|
||||
|
||||
if ( !other_data ) then return end
|
||||
|
||||
-- This KeyName stuff is absolutely unnecessary, there's absolutely no reason for it to exist
|
||||
-- But we cannot remove it now because dupes will break. It should've used the "name" variable
|
||||
if ( other_data.KeyName ) then
|
||||
ent:SetupKeyValue( other_data.KeyName, "float", ent[ "Set" .. name ], ent[ "Get" .. name ], other_data )
|
||||
ent:SetupEditing( name, other_data.KeyName, other_data.Edit )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
self.SetNetworkKeyValue = function( ent, key, value )
|
||||
|
||||
key = key:lower()
|
||||
|
||||
local k = keytable[ key ]
|
||||
if ( !k ) then return end
|
||||
|
||||
local v = util.StringToType( value, k.Type )
|
||||
if ( v == nil ) then return end
|
||||
|
||||
k.Set( ent, v )
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
self.GetNetworkKeyValue = function( ent, key )
|
||||
|
||||
key = key:lower()
|
||||
|
||||
local k = keytable[ key ]
|
||||
if ( !k ) then return end
|
||||
|
||||
return k.Get( ent )
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- Called by the duplicator system to get the network vars
|
||||
--
|
||||
self.GetNetworkVars = function( ent )
|
||||
|
||||
local dt = {}
|
||||
|
||||
for k, v in pairs( datatable ) do
|
||||
|
||||
-- Don't try to save entities (yet?)
|
||||
if ( v.typename == "Entity" ) then continue end
|
||||
|
||||
if ( v.element ) then
|
||||
dt[ k ] = v.GetFunc( ent, v.index )[ v.element ]
|
||||
else
|
||||
dt[ k ] = v.GetFunc( ent, v.index )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- If there's nothing in our table - then return nil.
|
||||
--
|
||||
if ( table.IsEmpty( dt ) ) then return nil end
|
||||
|
||||
return dt
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- Called by the duplicator system to restore from network vars
|
||||
--
|
||||
self.RestoreNetworkVars = function( ent, tab )
|
||||
|
||||
if ( !tab ) then return end
|
||||
|
||||
-- Loop this entities data table
|
||||
for k, v in pairs( datatable ) do
|
||||
|
||||
-- If it contains this entry
|
||||
if ( tab[ k ] == nil ) then continue end
|
||||
|
||||
-- Support old saves/dupes with incorrectly saved data
|
||||
if ( v.element && ( isangle( tab[ k ] ) or isvector( tab[ k ] ) ) ) then
|
||||
tab[ k ] = tab[ k ][ v.element ]
|
||||
end
|
||||
|
||||
-- Set it.
|
||||
if ( ent[ "Set" .. k ] ) then
|
||||
ent[ "Set" .. k ]( ent, tab[ k ] )
|
||||
else
|
||||
v.SetFunc( ent, v.index, tab[ k ] )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
setmetatable( self.dt, dtmeta )
|
||||
|
||||
--
|
||||
-- In sandbox the client can edit certain values on certain entities
|
||||
-- we implement this here incase any other gamemodes want to use it
|
||||
-- although it is of course deactivated by default.
|
||||
--
|
||||
|
||||
--
|
||||
-- This function takes a keyname and a value - both strings.
|
||||
--
|
||||
--
|
||||
-- Called serverside it will set the value.
|
||||
--
|
||||
self.EditValue = function( ent, variable, value )
|
||||
|
||||
if ( !isstring( variable ) ) then return end
|
||||
if ( !isstring( value ) ) then return end
|
||||
|
||||
--
|
||||
-- It can be called clientside to send a message to the server
|
||||
-- to request a change of value.
|
||||
--
|
||||
if ( CLIENT ) then
|
||||
|
||||
net.Start( "editvariable" )
|
||||
net.WriteEntity( ent )
|
||||
net.WriteString( variable )
|
||||
net.WriteString( value )
|
||||
net.SendToServer()
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- Called serverside it simply changes the value
|
||||
--
|
||||
if ( SERVER ) then
|
||||
|
||||
ent:SetNetworkKeyValue( variable, value )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if ( SERVER ) then
|
||||
|
||||
util.AddNetworkString( "editvariable" )
|
||||
|
||||
net.Receive( "editvariable", function( len, client )
|
||||
|
||||
local ent = net.ReadEntity()
|
||||
|
||||
if ( !IsValid( ent ) ) then return end
|
||||
if ( !isfunction( ent.GetEditingData ) ) then return end
|
||||
if ( ent.AdminOnly && !( client:IsAdmin() or game.SinglePlayer() ) ) then return end
|
||||
|
||||
local key = net.ReadString()
|
||||
|
||||
-- Is this key in our edit table?
|
||||
local editor = ent:GetEditingData()[ key ]
|
||||
if ( !istable( editor ) ) then return end
|
||||
|
||||
local val = net.ReadString()
|
||||
hook.Run( "VariableEdited", ent, client, key, val, editor )
|
||||
|
||||
end )
|
||||
|
||||
function meta:GetUnFreezable()
|
||||
return self.m_bUnFreezable or false
|
||||
end
|
||||
|
||||
function meta:SetUnFreezable( bFreeze )
|
||||
self.m_bUnFreezable = tobool( bFreeze ) or false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- Networked var proxies
|
||||
--
|
||||
function meta:SetNetworked2VarProxy( name, func )
|
||||
|
||||
if ( !self.NWVarProxies ) then
|
||||
self.NWVarProxies = {}
|
||||
end
|
||||
|
||||
self.NWVarProxies[ name ] = func
|
||||
|
||||
end
|
||||
|
||||
function meta:GetNetworked2VarProxy( name )
|
||||
|
||||
if ( self.NWVarProxies ) then
|
||||
local func = self.NWVarProxies[ name ]
|
||||
if ( isfunction( func ) ) then
|
||||
return func
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
|
||||
end
|
||||
|
||||
meta.SetNW2VarProxy = meta.SetNetworked2VarProxy
|
||||
meta.GetNW2VarProxy = meta.GetNetworked2VarProxy
|
||||
|
||||
hook.Add( "EntityNetworkedVarChanged", "NetworkedVars", function( ent, name, oldValue, newValue )
|
||||
|
||||
if ( ent.NWVarProxies ) then
|
||||
local func = ent.NWVarProxies[ name ]
|
||||
|
||||
if ( isfunction( func ) ) then
|
||||
func( ent, name, oldValue, newValue )
|
||||
end
|
||||
end
|
||||
|
||||
end )
|
||||
|
||||
--
|
||||
-- Vehicle Extensions
|
||||
--
|
||||
local vehicle = FindMetaTable( "Vehicle" )
|
||||
|
||||
--
|
||||
-- We steal some DT slots by default for vehicles
|
||||
-- to control the third person view. You should use
|
||||
-- these functions if you want to play with them because
|
||||
-- they might eventually be moved into the engine - so manually
|
||||
-- editing the DT values will stop working.
|
||||
--
|
||||
function vehicle:SetVehicleClass( s )
|
||||
|
||||
self:SetDTString( 3, s )
|
||||
|
||||
end
|
||||
|
||||
function vehicle:GetVehicleClass()
|
||||
|
||||
return self:GetDTString( 3 )
|
||||
|
||||
end
|
||||
|
||||
function vehicle:SetThirdPersonMode( b )
|
||||
|
||||
self:SetDTBool( 3, b )
|
||||
|
||||
end
|
||||
|
||||
function vehicle:GetThirdPersonMode()
|
||||
|
||||
return self:GetDTBool( 3 )
|
||||
|
||||
end
|
||||
|
||||
function vehicle:SetCameraDistance( dist )
|
||||
|
||||
self:SetDTFloat( 3, dist )
|
||||
|
||||
end
|
||||
|
||||
function vehicle:GetCameraDistance()
|
||||
|
||||
return self:GetDTFloat( 3 )
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user