

local meta = FindMetaTable( "Entity" )



-- Return if there's nothing to add on to

if ( !meta ) then return end



AccessorFunc( meta, "m_bPlayPickupSound", "ShouldPlayPickupSound" )



--

-- 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 val = tab[ key ]

		if ( val != nil ) then return val 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 )

		self.m_PlayerCreator = ply

	end



	function meta:GetCreator()

		return self.m_PlayerCreator;

	end



end



--[[---------------------------------------------------------

   Name: Returns true if the entity has constraints attached to it

-----------------------------------------------------------]]

function meta:IsConstrained()



	if (CLIENT) then return self:GetNetworkedBool( "IsConstrained" ) end

	

	local c = self:GetTable().Constraints

	local bIsConstrained = false

	

	if ( c ) then

	

		for k,v in pairs( c ) do

			if v:IsValid() then bIsConstrained = true break end

			c[k] = nil

		end

		

	end



	self:SetNetworkedBool( "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 || !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 )



--[[---------------------------------------------------------

   Name: PhysWake

-----------------------------------------------------------]]

function meta:PhysWake()



	local phys = self:GetPhysicsObject()

	if ( !phys || !phys:IsValid() ) then return end

	

	phys:Wake()

	

end



function meta:GetChildBones( bone )



	local bonecount = self:GetBoneCount()

	if ( bonecount == 0 || 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 meta:InstallDataTable()



	self.dt = {}

	local datatable = {}

	local keytable = {}

	local meta = {}

	local editing = {}



	meta.__index = function ( ent, key )



		local dt = datatable[ key ]

		if ( dt == nil ) then return end	

		

		return dt.GetFunc( self, dt.index, key )

		

	end

	

	meta.__newindex = function( ent, key, value )

				

		local dt = datatable[ key ]

		if ( dt == nil ) then return end

		

		dt.SetFunc( self, dt.index, value )

	

	end	

	

	self.DTVar = function( ent, typename, index, name )

	

		local SetFunc = ent[ "SetDT"..typename ]

		local GetFunc = ent[ "GetDT"..typename ]

		

		if ( !SetFunc || !GetFunc ) then

			MsgN( "Couldn't addvar " , name, " - type ", typename," is invalid!" )

			return

		end



		datatable[ name ] = { 

							index = index,

							SetFunc = SetFunc,

							GetFunc = GetFunc,

							typename = typename,

							keyname = keyname,

							Notify = {}

							}



		return datatable[ name ];

							

	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, type, setfunc, getfunc, other_data )

	

		keyname = keyname:lower()



		keytable[ keyname ] = 

		{

			KeyName		= keyname,

			Set			= setfunc,

			Get			= getfunc,

			Type		= type,

			EditType	= EditType,

			EditData	= EditData

		}



		if ( other_data ) then



			table.Merge( keytable[ keyname ], other_data )



		end



	end



	local CallProxies = function( ent, tbl, name, oldval, newval )



		for k, v in pairs( tbl ) do

			v( ent, name, oldval, newval )

		end



	end



	self.NetworkVar = function( ent, typename, index, name, other_data )

	

		local t = ent.DTVar( ent, typename, index, name )



		ent[ 'Set' .. name ] =	function( self, value )		

									CallProxies( ent, t.Notify, name, self.dt[name], value )

									self.dt[name] = value			

								end



		ent[ 'Get' .. name ] =	function( self )				

									return self.dt[name]			

								end

		

		if ( !other_data ) then return end



		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 )

	

		ent.DTVar( ent, typename, index, name, keyname )



		ent[ 'Set' .. name ] =	function( self, value )	

									local old = self.dt[name];

									old[element] = value;

									self.dt[name] = old			

								end



		ent[ 'Get' .. name ] =	function( self )		

									return self.dt[name][element]		

								end



		if ( !other_data ) then return end



		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( self, 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( self, v );

		return true



	end





	self.GetNetworkKeyValue = function( self, key )

		

		key = key:lower()



		local k = keytable[ key ]

		if ( !k ) then return end



		return k.Get( self );



	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



			dt[ k ] = v.GetFunc( ent, v.index );



		end



		--

		-- If there's nothing in our table - then return nil.

		--

		if ( table.Count( dt ) == 0 ) 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



			-- Set it.

			v.SetFunc( ent, v.index, tab[k] );



		end



		-- PrintTable( tab )

							

	end



	setmetatable( self.dt, meta )





	--

	-- 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( self, 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.WriteUInt( self:EntIndex(), 32 )

				net.WriteString( variable )

				net.WriteString( value )

			net.SendToServer()



		end



		--

		-- Called serverside it simply changes the value

		--

		if ( SERVER ) then



			self:SetNetworkKeyValue( variable, value )



		end



	end



	if ( SERVER ) then



		util.AddNetworkString( "editvariable" )



		net.Receive( "editvariable", function( len, client )

		

			local iIndex = net.ReadUInt( 32 )

			local ent = Entity( iIndex )



			if ( !IsValid( ent ) ) then return end

			if ( !isfunction( ent.GetEditingData ) ) 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 )



	end

	

end



if ( SERVER ) then



	AccessorFunc( meta, "m_bUnFreezable", "UnFreezable" )

	



end





saverestore.AddSaveHook( "NetworkedVars", function ( save )



	-- Note: BuildNetworkedVarsTable should only ever be called when saving

	-- It is quite slow, and should definitiely not be used to select and find

	-- networked vars.

	

	local NetworkVars = BuildNetworkedVarsTable()

	saverestore.WriteTable( NetworkVars, save )



end )





saverestore.AddRestoreHook( "NetworkedVars", function ( restore )



	local NetworkVars = saverestore.ReadTable( restore )

	

	-- First load the global vars. They have an index of 0.

	local Globals = NetworkVars[ 0 ]

	-- Remove it from the table so we don't process it again.

	NetworkVars[ 0 ] = nil

	

	if ( Globals ) then

		for k, v in pairs( Globals ) do

			SetGlobalVar( k, v )

		end

	end	

	

	-- Now load the entity vars.

	for ent, enttab in pairs( NetworkVars ) do

		for k, v in pairs( enttab ) do

			ent:SetNetworkedVar( k, v )

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