Initial commit
This commit is contained in:
98
lua/includes/modules/ai_schedule.lua
Normal file
98
lua/includes/modules/ai_schedule.lua
Normal file
@@ -0,0 +1,98 @@
|
||||
|
||||
-- Serverside only.
|
||||
if ( CLIENT ) then return end
|
||||
|
||||
local setmetatable = setmetatable
|
||||
local tostring = tostring
|
||||
local table = table
|
||||
local ai_task = ai_task
|
||||
|
||||
module( "ai_schedule" )
|
||||
|
||||
-- Define the Schedule object
|
||||
|
||||
local Schedule = {}
|
||||
Schedule.__index = Schedule
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: Init
|
||||
Sets the object up. Called automatically from ai_schedule.new.
|
||||
-----------------------------------------------------------]]
|
||||
function Schedule:Init( _debugname_ )
|
||||
|
||||
self.DebugName = tostring( _debugname_ )
|
||||
self.Tasks = {}
|
||||
self.TaskCount = 0
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
|
||||
Adds an engine task to the schedule
|
||||
|
||||
schd:EngTask( "TASK_TARGET_PLAYER", 0 )
|
||||
|
||||
-----------------------------------------------------------]]
|
||||
function Schedule:EngTask( _taskname_, _taskdata_ )
|
||||
|
||||
local NewTask = ai_task.New()
|
||||
NewTask:InitEngine( _taskname_, _taskdata_ )
|
||||
self.TaskCount = table.insert( self.Tasks, NewTask )
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
|
||||
Adds Lua NPC task to the schedule
|
||||
|
||||
schdChase:AddTask( "LookForPlayer", "AnyDataYouWant" )
|
||||
|
||||
will call the functions
|
||||
|
||||
NPC:TaskStart_LookForPlayer( "AnyDataYouWant" ) - once on start
|
||||
NPC:Task_LookForPlayer( "AnyDataYouWant" ) - every think until TaskComplete
|
||||
|
||||
-----------------------------------------------------------]]
|
||||
function Schedule:AddTask( _functionname_, _data_ )
|
||||
|
||||
local NewTask = ai_task.New()
|
||||
NewTask:InitFunctionName( "TaskStart_" .. _functionname_, "Task_" .. _functionname_, _data_ )
|
||||
self.TaskCount = table.insert( self.Tasks, NewTask )
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
|
||||
The same as above but you get to specify the exact
|
||||
function name to call
|
||||
|
||||
-----------------------------------------------------------]]
|
||||
function Schedule:AddTaskEx( _start, _run, _data_ )
|
||||
|
||||
local NewTask = ai_task.New()
|
||||
NewTask:InitFunctionName( _start, _run, _data_ )
|
||||
self.TaskCount = table.insert( self.Tasks, NewTask )
|
||||
|
||||
end
|
||||
|
||||
function Schedule:NumTasks()
|
||||
return self.TaskCount
|
||||
end
|
||||
|
||||
function Schedule:GetTask( num )
|
||||
return self.Tasks[ num ]
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Create a new empty task (this is ai_schedule.New )
|
||||
-----------------------------------------------------------]]
|
||||
function New( debugname )
|
||||
|
||||
local pNewSchedule = {}
|
||||
setmetatable( pNewSchedule, Schedule )
|
||||
|
||||
pNewSchedule:Init( debugname )
|
||||
|
||||
return pNewSchedule
|
||||
|
||||
end
|
||||
123
lua/includes/modules/ai_task.lua
Normal file
123
lua/includes/modules/ai_task.lua
Normal file
@@ -0,0 +1,123 @@
|
||||
|
||||
-- Serverside only.
|
||||
if ( CLIENT ) then return end
|
||||
|
||||
local setmetatable = setmetatable
|
||||
--local table = table
|
||||
local ai = ai
|
||||
|
||||
module( "ai_task" )
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
ENUMs for which kind of task it is.
|
||||
-----------------------------------------------------------]]
|
||||
local TYPE_ENGINE = 1
|
||||
local TYPE_FNAME = 2
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Keep track of created tasks
|
||||
UNDONE: There's no need for this right now.
|
||||
-----------------------------------------------------------]]
|
||||
--local Task_Index = {}
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Task metatable
|
||||
-----------------------------------------------------------]]
|
||||
local Task = {}
|
||||
Task.__index = Task
|
||||
|
||||
function Task:Init()
|
||||
self.Type = nil
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Creates an engine based task
|
||||
-----------------------------------------------------------]]
|
||||
function Task:InitEngine( _taskname_, _taskdata_ )
|
||||
|
||||
self.TaskName = _taskname_
|
||||
self.TaskID = nil
|
||||
self.TaskData = _taskdata_
|
||||
self.Type = TYPE_ENGINE
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Creates an engine based task
|
||||
-----------------------------------------------------------]]
|
||||
function Task:InitFunctionName( _start, _end, _taskdata_ )
|
||||
self.StartFunctionName = _start
|
||||
self.FunctionName = _end
|
||||
self.TaskData = _taskdata_
|
||||
self.Type = TYPE_FNAME
|
||||
end
|
||||
|
||||
function Task:IsEngineType()
|
||||
return ( self.Type == TYPE_ENGINE )
|
||||
end
|
||||
|
||||
function Task:IsFNameType()
|
||||
return ( self.Type == TYPE_FNAME )
|
||||
end
|
||||
|
||||
function Task:Start( npc )
|
||||
|
||||
if ( self:IsFNameType() ) then self:Start_FName( npc ) return end
|
||||
|
||||
if ( self:IsEngineType() ) then
|
||||
|
||||
if ( !self.TaskID ) then self.TaskID = ai.GetTaskID( self.TaskName ) end
|
||||
|
||||
npc:StartEngineTask( self.TaskID, self.TaskData )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Start_FName (called from Task:Start)
|
||||
-----------------------------------------------------------]]
|
||||
function Task:Start_FName( npc )
|
||||
|
||||
if ( !self.StartFunctionName ) then return end
|
||||
--if ( !npc[ self.StartFunctionName ] ) then return end
|
||||
|
||||
-- Run the start function. Safely.
|
||||
npc[ self.StartFunctionName ]( npc, self.TaskData )
|
||||
|
||||
end
|
||||
|
||||
function Task:Run( npc )
|
||||
|
||||
if ( self:IsFNameType() ) then self:Run_FName( npc ) return end
|
||||
|
||||
if ( self:IsEngineType() ) then
|
||||
npc:RunEngineTask( self.TaskID, self.TaskData )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function Task:Run_FName( npc )
|
||||
|
||||
if ( !self.FunctionName ) then return end
|
||||
--if (!npc[ self.StartFunctionName ]) then return end
|
||||
|
||||
-- Run the start function. Safely.
|
||||
npc[ self.FunctionName ]( npc, self.TaskData )
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Create a new empty task (this is ai_task.New )
|
||||
-----------------------------------------------------------]]
|
||||
function New()
|
||||
|
||||
local pNewTask = {}
|
||||
setmetatable( pNewTask, Task )
|
||||
|
||||
pNewTask:Init()
|
||||
|
||||
--table.insert( Task_Index, pNewTask )
|
||||
|
||||
return pNewTask
|
||||
|
||||
end
|
||||
58
lua/includes/modules/baseclass.lua
Normal file
58
lua/includes/modules/baseclass.lua
Normal file
@@ -0,0 +1,58 @@
|
||||
|
||||
|
||||
--
|
||||
-- The baseclass module uses upvalues to give the impression of inheritence.
|
||||
--
|
||||
-- At the top of your class file add
|
||||
--
|
||||
-- DEFINE_BASECLASS( "base_class_name" )
|
||||
--
|
||||
-- Now the local variable BaseClass will be available.
|
||||
-- ( in engine DEFINE_BASECLASS is replaced with "local BaseClass = baseclass.Get" )
|
||||
--
|
||||
-- Baseclasses are added using baseclass.Set - this is done automatically for:
|
||||
--
|
||||
-- > widgets
|
||||
-- > panels
|
||||
-- > drive modes
|
||||
-- > entities
|
||||
-- > weapons
|
||||
--
|
||||
-- Classes don't have to be created in any particular order. The system is
|
||||
-- designed to work with whatever order the classes are defined.
|
||||
--
|
||||
-- The only caveat is that classnames must be unique.
|
||||
-- eg Creating a panel and widget with the same name will cause problems.
|
||||
--
|
||||
|
||||
module( "baseclass", package.seeall )
|
||||
|
||||
local BaseClassTable = {}
|
||||
|
||||
function Get( name )
|
||||
|
||||
if ( ENT ) then ENT.Base = name end
|
||||
if ( SWEP ) then SWEP.Base = name end
|
||||
|
||||
BaseClassTable[name] = BaseClassTable[name] or {}
|
||||
|
||||
return BaseClassTable[name]
|
||||
|
||||
end
|
||||
|
||||
function Set( name, tab )
|
||||
|
||||
if ( !BaseClassTable[name] ) then
|
||||
|
||||
BaseClassTable[name] = tab
|
||||
|
||||
else
|
||||
|
||||
table.Merge( BaseClassTable[name], tab )
|
||||
setmetatable( BaseClassTable[name], getmetatable(tab) )
|
||||
|
||||
end
|
||||
|
||||
BaseClassTable[name].ThisClass = name
|
||||
|
||||
end
|
||||
241
lua/includes/modules/cleanup.lua
Normal file
241
lua/includes/modules/cleanup.lua
Normal file
@@ -0,0 +1,241 @@
|
||||
|
||||
module( "cleanup", package.seeall )
|
||||
|
||||
local cleanup_types = {}
|
||||
|
||||
local function IsType( type )
|
||||
|
||||
for key, val in pairs( cleanup_types ) do
|
||||
|
||||
if ( val == type ) then return true end
|
||||
|
||||
end
|
||||
|
||||
return false
|
||||
|
||||
end
|
||||
|
||||
function Register( type )
|
||||
|
||||
if ( type == "all" ) then return end
|
||||
|
||||
for key, val in pairs( cleanup_types ) do
|
||||
|
||||
if val == type then return end
|
||||
|
||||
end
|
||||
|
||||
table.insert( cleanup_types, type )
|
||||
|
||||
end
|
||||
|
||||
function GetTable()
|
||||
return cleanup_types
|
||||
end
|
||||
|
||||
|
||||
if ( SERVER ) then
|
||||
|
||||
local cleanup_list = {}
|
||||
|
||||
function GetList()
|
||||
return cleanup_list
|
||||
end
|
||||
|
||||
local function Save( save )
|
||||
|
||||
saverestore.WriteTable( cleanup_list, save )
|
||||
|
||||
end
|
||||
|
||||
local function Restore( restore )
|
||||
|
||||
cleanup_list = saverestore.ReadTable( restore )
|
||||
|
||||
end
|
||||
|
||||
saverestore.AddSaveHook( "CleanupTable", Save )
|
||||
saverestore.AddRestoreHook( "CleanupTable", Restore )
|
||||
|
||||
function Add( pl, type, ent )
|
||||
|
||||
if ( !ent ) then return end
|
||||
|
||||
if ( !IsType( type ) ) then return end
|
||||
|
||||
local id = pl:UniqueID()
|
||||
|
||||
cleanup_list[ id ] = cleanup_list[ id ] or {}
|
||||
cleanup_list[ id ][ type ] = cleanup_list[ id ][ type ] or {}
|
||||
|
||||
if ( !IsValid( ent ) ) then return end
|
||||
|
||||
table.insert( cleanup_list[ id ][ type ], ent )
|
||||
|
||||
end
|
||||
|
||||
function ReplaceEntity( from, to )
|
||||
|
||||
local ActionTaken = false
|
||||
|
||||
for _, PlayerTable in pairs( cleanup_list ) do
|
||||
for _, TypeTable in pairs( PlayerTable ) do
|
||||
for key, ent in pairs( TypeTable ) do
|
||||
|
||||
if ( ent == from ) then
|
||||
TypeTable[ key ] = to
|
||||
ActionTaken = true
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return ActionTaken
|
||||
|
||||
end
|
||||
|
||||
|
||||
function CC_Cleanup( pl, command, args )
|
||||
|
||||
if ( !IsValid( pl ) ) then return end
|
||||
|
||||
local id = pl:UniqueID()
|
||||
|
||||
if ( !cleanup_list[ id ] ) then return end
|
||||
|
||||
if ( !args[ 1 ] ) then
|
||||
|
||||
local count = 0
|
||||
|
||||
for key, val in pairs( cleanup_list[ id ] ) do
|
||||
|
||||
for _, ent in pairs( val ) do
|
||||
|
||||
if ( IsValid( ent ) ) then ent:Remove() end
|
||||
count = count + 1
|
||||
|
||||
end
|
||||
|
||||
table.Empty( val )
|
||||
|
||||
end
|
||||
|
||||
-- Send tooltip command to client
|
||||
if ( count > 0 ) then
|
||||
pl:SendLua( "hook.Run('OnCleanup','all')" )
|
||||
end
|
||||
|
||||
return
|
||||
|
||||
end
|
||||
|
||||
if ( !IsType( args[1] ) ) then return end
|
||||
if ( !cleanup_list[id][ args[1] ] ) then return end
|
||||
|
||||
for key, ent in pairs( cleanup_list[id][ args[1] ] ) do
|
||||
|
||||
if ( IsValid( ent ) ) then ent:Remove() end
|
||||
|
||||
end
|
||||
|
||||
table.Empty( cleanup_list[id][ args[1] ] )
|
||||
|
||||
-- Send tooltip command to client
|
||||
pl:SendLua( string.format( "hook.Run('OnCleanup',%q)", args[1] ) )
|
||||
|
||||
end
|
||||
|
||||
function CC_AdminCleanup( pl, command, args )
|
||||
|
||||
if ( IsValid( pl ) && !pl:IsAdmin() ) then return end
|
||||
|
||||
if ( !args[ 1 ] ) then
|
||||
|
||||
for key, ply in pairs( cleanup_list ) do
|
||||
|
||||
for _, type in pairs( ply ) do
|
||||
|
||||
for __, ent in pairs( type ) do
|
||||
|
||||
if ( IsValid( ent ) ) then ent:Remove() end
|
||||
|
||||
end
|
||||
|
||||
table.Empty( type )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
game.CleanUpMap( false, nil, function()
|
||||
-- Send tooltip command to client
|
||||
if ( IsValid( pl ) ) then pl:SendLua( "hook.Run('OnCleanup','all')" ) end
|
||||
end )
|
||||
|
||||
return
|
||||
|
||||
end
|
||||
|
||||
if ( !IsType( args[ 1 ] ) ) then return end
|
||||
|
||||
for key, ply in pairs( cleanup_list ) do
|
||||
|
||||
if ( ply[ args[ 1 ] ] != nil ) then
|
||||
|
||||
for id, ent in pairs( ply[ args[ 1 ] ] ) do
|
||||
|
||||
if ( IsValid( ent ) ) then ent:Remove() end
|
||||
|
||||
end
|
||||
|
||||
table.Empty( ply[ args[ 1 ] ] )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Send tooltip command to client
|
||||
if ( IsValid( pl ) ) then pl:SendLua( string.format( "hook.Run('OnCleanup',%q)", args[1] ) ) end
|
||||
|
||||
end
|
||||
|
||||
concommand.Add( "gmod_cleanup", CC_Cleanup, nil, "", { FCVAR_DONTRECORD } )
|
||||
concommand.Add( "gmod_admin_cleanup", CC_AdminCleanup, nil, "", { FCVAR_DONTRECORD } )
|
||||
|
||||
else
|
||||
|
||||
function UpdateUI()
|
||||
|
||||
local cleanup_types_s = {}
|
||||
for id, val in pairs( cleanup_types ) do
|
||||
cleanup_types_s[ language.GetPhrase( "Cleanup_" .. val ) ] = val
|
||||
end
|
||||
|
||||
local Panel = controlpanel.Get( "User_Cleanup" )
|
||||
if ( IsValid( Panel ) ) then
|
||||
Panel:Clear()
|
||||
Panel:Help( "#spawnmenu.utilities.cleanup.help" )
|
||||
Panel:Button( "#spawnmenu.utilities.cleanup.all", "gmod_cleanup" )
|
||||
|
||||
for key, val in SortedPairs( cleanup_types_s ) do
|
||||
Panel:Button( key, "gmod_cleanup", val )
|
||||
end
|
||||
end
|
||||
|
||||
local AdminPanel = controlpanel.Get( "Admin_Cleanup" )
|
||||
if ( IsValid( AdminPanel ) ) then
|
||||
AdminPanel:Clear()
|
||||
AdminPanel:Help( "#spawnmenu.utilities.cleanup.help" )
|
||||
AdminPanel:Button( "#spawnmenu.utilities.cleanup.all", "gmod_admin_cleanup" )
|
||||
|
||||
for key, val in SortedPairs( cleanup_types_s ) do
|
||||
AdminPanel:Button( key, "gmod_admin_cleanup", val )
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
hook.Add( "PostReloadToolsMenu", "BuildCleanupUI", UpdateUI )
|
||||
|
||||
end
|
||||
75
lua/includes/modules/concommand.lua
Normal file
75
lua/includes/modules/concommand.lua
Normal file
@@ -0,0 +1,75 @@
|
||||
|
||||
local AddConsoleCommand = AddConsoleCommand
|
||||
local string = string
|
||||
local Msg = Msg
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: concommand
|
||||
Desc: A module to take care of the registration and calling
|
||||
of Lua console commands.
|
||||
-----------------------------------------------------------]]
|
||||
module( "concommand" )
|
||||
|
||||
local CommandList = {}
|
||||
local CompleteList = {}
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: concommand.GetTable( )
|
||||
Desc: Returns the table of console commands and auto complete
|
||||
-----------------------------------------------------------]]
|
||||
function GetTable()
|
||||
return CommandList, CompleteList
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: concommand.Add( name, func, completefunc )
|
||||
Desc: Register a new console command
|
||||
-----------------------------------------------------------]]
|
||||
function Add( name, func, completefunc, help, flags )
|
||||
local LowerName = string.lower( name )
|
||||
CommandList[ LowerName ] = func
|
||||
CompleteList[ LowerName ] = completefunc
|
||||
AddConsoleCommand( name, help, flags )
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: concommand.Remove( name )
|
||||
Desc: Removes a console command
|
||||
-----------------------------------------------------------]]
|
||||
function Remove( name )
|
||||
local LowerName = string.lower( name )
|
||||
CommandList[ LowerName ] = nil
|
||||
CompleteList[ LowerName ] = nil
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: concommand.Run( )
|
||||
Desc: Called by the engine when an unknown console command is run
|
||||
-----------------------------------------------------------]]
|
||||
function Run( player, command, arguments, argumentsStr )
|
||||
|
||||
local LowerCommand = string.lower( command )
|
||||
|
||||
if ( CommandList[ LowerCommand ] != nil ) then
|
||||
CommandList[ LowerCommand ]( player, command, arguments, argumentsStr )
|
||||
return true
|
||||
end
|
||||
|
||||
Msg( "Unknown command: " .. command .. "\n" )
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: concommand.AutoComplete( )
|
||||
Desc: Returns a table for the autocompletion
|
||||
-----------------------------------------------------------]]
|
||||
function AutoComplete( command, argumentsStr, arguments )
|
||||
|
||||
local LowerCommand = string.lower( command )
|
||||
|
||||
if ( CompleteList[ LowerCommand ] != nil ) then
|
||||
return CompleteList[ LowerCommand ]( command, argumentsStr, arguments )
|
||||
end
|
||||
|
||||
end
|
||||
1684
lua/includes/modules/constraint.lua
Normal file
1684
lua/includes/modules/constraint.lua
Normal file
File diff suppressed because it is too large
Load Diff
153
lua/includes/modules/construct.lua
Normal file
153
lua/includes/modules/construct.lua
Normal file
@@ -0,0 +1,153 @@
|
||||
|
||||
local ents = ents
|
||||
local SERVER = SERVER
|
||||
local duplicator = duplicator
|
||||
local numpad = numpad
|
||||
local Msg = Msg
|
||||
local IsValid = IsValid
|
||||
|
||||
module( "construct" )
|
||||
|
||||
if SERVER then
|
||||
|
||||
function SetPhysProp( Player, Entity, BoneID, Bone, Data )
|
||||
|
||||
if ( !IsValid( Bone ) ) then
|
||||
|
||||
Bone = Entity:GetPhysicsObjectNum( BoneID )
|
||||
if ( !IsValid( Bone ) ) then
|
||||
Msg( "SetPhysProp: Error applying attributes to invalid physics object!\n" )
|
||||
return
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if ( Data.GravityToggle != nil ) then Bone:EnableGravity( Data.GravityToggle ) end
|
||||
if ( Data.Material != nil ) then Bone:SetMaterial( Data.Material ) end
|
||||
|
||||
-- todo: Rename/Implement
|
||||
--[[
|
||||
if ( Data.motionb != nil ) then Bone:EnableMotion( Data.motionb ) end
|
||||
if ( Data.mass != nil ) then Bone:SetMass( Data.mass ) end
|
||||
if ( Data.dragb != nil ) then Bone:EnableDrag( Data.dragb ) end
|
||||
if ( Data.drag != nil ) then Bone:SetDragCoefficient( Data.drag ) end
|
||||
if ( Data.buoyancy != nil ) then Bone:SetBuoyancyRatio( Data.buoyancy ) end
|
||||
if ( Data.rotdamping != nil ) then Bone:SetDamping( PhysBone:GetSpeedDamping(), Data.rotdamping ) end
|
||||
if ( Data.speeddamping != nil ) then Bone:SetDamping( Data.speeddamping, PhysBone:GetRotDamping() ) end
|
||||
--]]
|
||||
|
||||
-- HACK HACK
|
||||
-- If we don't do this the prop will be motion enabled and will
|
||||
-- slide through the world with no gravity.
|
||||
if ( !Bone:IsMoveable() ) then
|
||||
|
||||
Bone:EnableMotion( true )
|
||||
Bone:EnableMotion( false )
|
||||
|
||||
end
|
||||
|
||||
duplicator.StoreBoneModifier( Entity, BoneID, "physprops", Data )
|
||||
|
||||
end
|
||||
|
||||
duplicator.RegisterBoneModifier( "physprops", SetPhysProp )
|
||||
|
||||
|
||||
local function MagnetOff( pl, magnet )
|
||||
|
||||
if ( !IsValid( magnet ) ) then return false end
|
||||
if ( magnet:GetTable().toggle != 0 ) then return true end
|
||||
|
||||
magnet:Fire( "TurnOff", "" , 0 )
|
||||
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
|
||||
local function MagnetOn( pl, magnet )
|
||||
|
||||
if ( !IsValid( magnet ) ) then return false end
|
||||
|
||||
if ( magnet:GetTable().toggle != 0 ) then
|
||||
|
||||
magnet:GetTable().toggle_state = !magnet:GetTable().toggle_state
|
||||
|
||||
if ( magnet:GetTable().toggle_state ) then
|
||||
magnet:Fire( "TurnOn", "" , 0 )
|
||||
else
|
||||
magnet:Fire( "TurnOff", "" , 0 )
|
||||
end
|
||||
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
magnet:Fire( "TurnOn", "" , 0 )
|
||||
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
numpad.Register( "MagnetOff", MagnetOff )
|
||||
numpad.Register( "MagnetOn", MagnetOn )
|
||||
|
||||
function Magnet( pl, pos, angle, model, material, key, maxobjects, strength, nopull, allowrot, alwayson, toggle, Vel, aVel, frozen )
|
||||
|
||||
local magnet = ents.Create( "phys_magnet" )
|
||||
magnet:SetPos( pos )
|
||||
magnet:SetAngles( angle )
|
||||
magnet:SetModel( model )
|
||||
if ( material ) then magnet:SetMaterial( material ) end
|
||||
|
||||
local spawnflags = 4
|
||||
if ( nopull && nopull > 0 ) then spawnflags = spawnflags - 4 end -- no pull required: remove the suck flag
|
||||
if ( allowrot && allowrot > 0 ) then spawnflags = spawnflags + 8 end
|
||||
|
||||
if ( maxobjects ) then magnet:SetKeyValue( "maxobjects", maxobjects ) end
|
||||
if ( strength ) then magnet:SetKeyValue( "forcelimit", strength ) end
|
||||
magnet:SetKeyValue( "overridescript", "surfaceprop,metal")
|
||||
magnet:SetKeyValue( "massScale", 0 )
|
||||
|
||||
magnet:Activate()
|
||||
magnet:Spawn()
|
||||
|
||||
if ( IsValid( magnet:GetPhysicsObject() ) ) then
|
||||
local Phys = magnet:GetPhysicsObject()
|
||||
if ( Vel ) then Phys:SetVelocity( Vel ) end
|
||||
if ( aVel ) then Phys:AddAngleVelocity( aVel ) end
|
||||
Phys:EnableMotion( frozen != true )
|
||||
end
|
||||
|
||||
if ( alwayson && alwayson > 0 ) then
|
||||
magnet:Input( "TurnOn", nil, nil, nil )
|
||||
else
|
||||
magnet:Input( "TurnOff", nil, nil, nil )
|
||||
end
|
||||
|
||||
local mtable = {
|
||||
Model = model,
|
||||
material = material,
|
||||
key = key,
|
||||
maxobjects = maxobjects,
|
||||
strength = strength,
|
||||
nopull = nopull,
|
||||
allowrot = allowrot,
|
||||
alwayson = alwayson,
|
||||
toggle = toggle
|
||||
}
|
||||
|
||||
magnet:SetTable( mtable )
|
||||
|
||||
if ( key ) then
|
||||
numpad.OnDown( pl, key, "MagnetOn", magnet )
|
||||
numpad.OnUp( pl, key, "MagnetOff", magnet )
|
||||
end
|
||||
|
||||
return magnet
|
||||
|
||||
end
|
||||
|
||||
duplicator.RegisterEntityClass( "phys_magnet", Magnet, "Pos", "Ang", "Model", "material", "key", "maxobjects", "strength", "nopull", "allowrot", "alwayson", "toggle", "Vel", "aVel", "frozen" )
|
||||
|
||||
end
|
||||
47
lua/includes/modules/controlpanel.lua
Normal file
47
lua/includes/modules/controlpanel.lua
Normal file
@@ -0,0 +1,47 @@
|
||||
|
||||
local ControlPanels = {}
|
||||
|
||||
module( "controlpanel", package.seeall )
|
||||
|
||||
-- A hack for a very annoying race condition where spawnmenu_reload deletes the controlpanels on the next frame
|
||||
-- But some panels are updated "this" frame after spawnmenu reloaded
|
||||
local function ShouldReCreate( pnl )
|
||||
if ( !IsValid( pnl ) || pnl:IsMarkedForDeletion() ) then return true end
|
||||
|
||||
local p = pnl
|
||||
-- Can't use IsValid because it's false for marked for deletion panels
|
||||
while ( IsValid( p ) && p:GetParent() != nil ) do
|
||||
if ( p:GetParent():IsMarkedForDeletion() ) then return true end
|
||||
p = p:GetParent()
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function Get( name )
|
||||
|
||||
if ( ShouldReCreate( ControlPanels[ name ] ) ) then
|
||||
local cp = vgui.Create( "ControlPanel" )
|
||||
if ( !cp ) then
|
||||
|
||||
debug.Trace()
|
||||
Error( "controlpanel.Get() - Error creating a ControlPanel!\nYou're calling this function too early! Call it in a hook!\n" )
|
||||
return nil
|
||||
|
||||
end
|
||||
|
||||
cp:SetVisible( false )
|
||||
cp.Name = name
|
||||
ControlPanels[ name ] = cp
|
||||
|
||||
end
|
||||
|
||||
return ControlPanels[ name ]
|
||||
|
||||
end
|
||||
|
||||
function Clear()
|
||||
|
||||
ControlPanels = {}
|
||||
|
||||
end
|
||||
133
lua/includes/modules/cookie.lua
Normal file
133
lua/includes/modules/cookie.lua
Normal file
@@ -0,0 +1,133 @@
|
||||
|
||||
if ( !sql.TableExists( "cookies" ) ) then
|
||||
|
||||
sql.Query( "CREATE TABLE IF NOT EXISTS cookies ( key TEXT NOT NULL PRIMARY KEY, value TEXT );" )
|
||||
|
||||
end
|
||||
|
||||
module( "cookie", package.seeall )
|
||||
|
||||
local CachedEntries = {}
|
||||
local BufferedWrites = {}
|
||||
local BufferedDeletes = {}
|
||||
|
||||
local function GetCache( key )
|
||||
|
||||
if ( BufferedDeletes[ key ] ) then return nil end
|
||||
|
||||
local entry = CachedEntries[ key ]
|
||||
|
||||
if ( entry == nil || SysTime() > entry[ 1 ] ) then
|
||||
local name = SQLStr( key )
|
||||
local val = sql.QueryValue( "SELECT value FROM cookies WHERE key = " .. name )
|
||||
|
||||
if !val then
|
||||
return false
|
||||
end
|
||||
|
||||
CachedEntries[ key ] = { SysTime() + 30, val }
|
||||
end
|
||||
|
||||
return CachedEntries[ key ][ 2 ]
|
||||
|
||||
end
|
||||
|
||||
local function FlushCache()
|
||||
|
||||
CachedEntries = {}
|
||||
BufferedWrites = {}
|
||||
BufferedDeletes = {}
|
||||
|
||||
end
|
||||
|
||||
local function CommitToSQLite()
|
||||
|
||||
sql.Begin()
|
||||
|
||||
for k, v in pairs( BufferedWrites ) do
|
||||
sql.Query( "INSERT OR REPLACE INTO cookies ( key, value ) VALUES ( " .. SQLStr( k ) .. ", " .. SQLStr( v ) .. " )" )
|
||||
end
|
||||
|
||||
for k, v in pairs( BufferedDeletes ) do
|
||||
sql.Query( "DELETE FROM cookies WHERE key = " .. SQLStr( k ) )
|
||||
end
|
||||
|
||||
BufferedWrites = {}
|
||||
BufferedDeletes = {}
|
||||
|
||||
sql.Commit()
|
||||
|
||||
end
|
||||
|
||||
local function ScheduleCommit()
|
||||
|
||||
timer.Create( "Cookie_CommitToSQLite", 0.1, 1, CommitToSQLite )
|
||||
|
||||
end
|
||||
|
||||
local function SetCache( key, value )
|
||||
|
||||
if ( value == nil ) then return Delete( key ) end
|
||||
|
||||
if CachedEntries[ key ] then
|
||||
CachedEntries[ key ][ 1 ] = SysTime() + 30
|
||||
CachedEntries[ key ][ 2 ] = value
|
||||
else
|
||||
CachedEntries[ key ] = { SysTime() + 30, value }
|
||||
end
|
||||
|
||||
BufferedWrites[ key ] = value
|
||||
|
||||
ScheduleCommit()
|
||||
|
||||
end
|
||||
|
||||
-- Get a String Value
|
||||
function GetString( name, default )
|
||||
|
||||
local val = GetCache( name )
|
||||
if ( !val ) then return default end
|
||||
|
||||
return val
|
||||
|
||||
end
|
||||
|
||||
-- Get a Number Value
|
||||
function GetNumber( name, default )
|
||||
|
||||
local val = GetCache( name )
|
||||
if ( !val ) then return default end
|
||||
|
||||
return tonumber( val )
|
||||
|
||||
end
|
||||
|
||||
-- Delete a Value
|
||||
function Delete( name )
|
||||
|
||||
CachedEntries[ name ] = nil
|
||||
BufferedWrites[ name ] = nil
|
||||
BufferedDeletes[ name ] = true
|
||||
|
||||
ScheduleCommit()
|
||||
|
||||
end
|
||||
|
||||
-- Set a Value
|
||||
function Set( name, value )
|
||||
|
||||
SetCache( name, value )
|
||||
|
||||
end
|
||||
|
||||
hook.Add( "ShutDown", "SaveCookiesOnShutdown", CommitToSQLite )
|
||||
|
||||
if ( !CLIENT_DLL ) then return end
|
||||
|
||||
concommand.Add( "lua_cookieclear", function( ply, command, arguments )
|
||||
|
||||
sql.Query( "DELETE FROM cookies" )
|
||||
FlushCache()
|
||||
|
||||
end )
|
||||
|
||||
147
lua/includes/modules/cvars.lua
Normal file
147
lua/includes/modules/cvars.lua
Normal file
@@ -0,0 +1,147 @@
|
||||
|
||||
local table = table
|
||||
local type = type
|
||||
local istable = istable
|
||||
local isstring = isstring
|
||||
local assert = assert
|
||||
local format = string.format
|
||||
local GetConVar = GetConVar
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: cvar
|
||||
Desc: Callbacks when cvars change
|
||||
-----------------------------------------------------------]]
|
||||
module( "cvars" )
|
||||
|
||||
local ConVars = {}
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: GetConVarCallbacks
|
||||
Desc: Returns a table of the given ConVars callbacks
|
||||
-----------------------------------------------------------]]
|
||||
function GetConVarCallbacks( name, createIfNotFound )
|
||||
|
||||
local tab = ConVars[ name ]
|
||||
if ( createIfNotFound and !tab ) then
|
||||
tab = {}
|
||||
ConVars[ name ] = tab
|
||||
end
|
||||
|
||||
return tab
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: OnConVarChanged
|
||||
Desc: Called by the engine
|
||||
-----------------------------------------------------------]]
|
||||
function OnConVarChanged( name, old, new )
|
||||
|
||||
local tab = GetConVarCallbacks( name )
|
||||
if ( !tab ) then return end
|
||||
|
||||
for i = 1, #tab do
|
||||
local callback = tab[ i ]
|
||||
if ( istable( callback ) ) then
|
||||
callback[ 1 ]( name, old, new )
|
||||
else
|
||||
callback( name, old, new )
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: AddChangeCallback
|
||||
Desc: Adds a callback to be called when convar changes
|
||||
-----------------------------------------------------------]]
|
||||
function AddChangeCallback( name, func, identifier )
|
||||
|
||||
if ( identifier ) then
|
||||
assert( isstring( identifier ), format( "bad argument #%i (string expected, got %s)", 3, type( identifier ) ) )
|
||||
end
|
||||
|
||||
local tab = GetConVarCallbacks( name, true )
|
||||
|
||||
if ( !identifier ) then
|
||||
table.insert( tab, func )
|
||||
return
|
||||
end
|
||||
|
||||
for i = 1, #tab do
|
||||
local callback = tab[ i ]
|
||||
if ( istable( callback ) and callback[ 2 ] == identifier ) then
|
||||
callback[ 1 ] = func
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
table.insert( tab, { func, identifier } )
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: RemoveChangeCallback
|
||||
Desc: Removes callback with identifier
|
||||
-----------------------------------------------------------]]
|
||||
function RemoveChangeCallback( name, identifier )
|
||||
|
||||
if ( identifier ) then
|
||||
assert( isstring( identifier ), format( "bad argument #%i (string expected, got %s)", 2, type( identifier ) ) )
|
||||
end
|
||||
|
||||
local tab = GetConVarCallbacks( name, true )
|
||||
for i = 1, #tab do
|
||||
local callback = tab[ i ]
|
||||
if ( istable( callback ) and callback[ 2 ] == identifier ) then
|
||||
table.remove( tab, i )
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: String
|
||||
Desc: Retrieves console variable as a string
|
||||
-----------------------------------------------------------]]
|
||||
function String( name, default )
|
||||
|
||||
local convar = GetConVar( name )
|
||||
if ( convar ~= nil ) then
|
||||
return convar:GetString()
|
||||
end
|
||||
|
||||
return default
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: Number
|
||||
Desc: Retrieves console variable as a number
|
||||
-----------------------------------------------------------]]
|
||||
function Number( name, default )
|
||||
|
||||
local convar = GetConVar( name )
|
||||
if ( convar ~= nil ) then
|
||||
return convar:GetFloat()
|
||||
end
|
||||
|
||||
return default
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: Bool
|
||||
Desc: Retrieves console variable as a boolean
|
||||
-----------------------------------------------------------]]
|
||||
function Bool( name, default )
|
||||
|
||||
local convar = GetConVar( name )
|
||||
if ( convar ~= nil ) then
|
||||
return convar:GetBool()
|
||||
end
|
||||
|
||||
return default
|
||||
|
||||
end
|
||||
314
lua/includes/modules/draw.lua
Normal file
314
lua/includes/modules/draw.lua
Normal file
@@ -0,0 +1,314 @@
|
||||
|
||||
local string = string
|
||||
local surface = surface
|
||||
local math = math
|
||||
local Color = Color
|
||||
local tostring = tostring
|
||||
local color_white = color_white
|
||||
|
||||
module( "draw" )
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Constants used for text alignment.
|
||||
These must be the same values as in the markup module.
|
||||
-----------------------------------------------------------]]
|
||||
TEXT_ALIGN_LEFT = 0
|
||||
TEXT_ALIGN_CENTER = 1
|
||||
TEXT_ALIGN_RIGHT = 2
|
||||
TEXT_ALIGN_TOP = 3
|
||||
TEXT_ALIGN_BOTTOM = 4
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Textures we use to get shit done
|
||||
-----------------------------------------------------------]]
|
||||
local tex_corner8 = surface.GetTextureID( "gui/corner8" )
|
||||
local tex_corner16 = surface.GetTextureID( "gui/corner16" )
|
||||
local tex_corner32 = surface.GetTextureID( "gui/corner32" )
|
||||
local tex_corner64 = surface.GetTextureID( "gui/corner64" )
|
||||
local tex_corner512 = surface.GetTextureID( "gui/corner512" )
|
||||
local tex_white = surface.GetTextureID( "vgui/white" )
|
||||
|
||||
local CachedFontHeights = {}
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: GetFontHeight( font )
|
||||
Desc: Returns the height of a single line
|
||||
-----------------------------------------------------------]]
|
||||
function GetFontHeight( font )
|
||||
|
||||
if ( CachedFontHeights[ font ] != nil ) then
|
||||
return CachedFontHeights[ font ]
|
||||
end
|
||||
|
||||
surface.SetFont( font )
|
||||
local w, h = surface.GetTextSize( "W" )
|
||||
CachedFontHeights[ font ] = h
|
||||
|
||||
return h
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: SimpleText(text, font, x, y, colour)
|
||||
Desc: Simple "draw text at position function"
|
||||
color is a table with r/g/b/a elements
|
||||
-----------------------------------------------------------]]
|
||||
function SimpleText( text, font, x, y, colour, xalign, yalign )
|
||||
|
||||
text = tostring( text )
|
||||
font = font or "DermaDefault"
|
||||
x = x or 0
|
||||
y = y or 0
|
||||
xalign = xalign or TEXT_ALIGN_LEFT
|
||||
yalign = yalign or TEXT_ALIGN_TOP
|
||||
|
||||
surface.SetFont( font )
|
||||
local w, h = surface.GetTextSize( text )
|
||||
|
||||
if ( xalign == TEXT_ALIGN_CENTER ) then
|
||||
x = x - w / 2
|
||||
elseif ( xalign == TEXT_ALIGN_RIGHT ) then
|
||||
x = x - w
|
||||
end
|
||||
|
||||
if ( yalign == TEXT_ALIGN_CENTER ) then
|
||||
y = y - h / 2
|
||||
elseif ( yalign == TEXT_ALIGN_BOTTOM ) then
|
||||
y = y - h
|
||||
end
|
||||
|
||||
surface.SetTextPos( math.ceil( x ), math.ceil( y ) )
|
||||
|
||||
if ( colour != nil ) then
|
||||
surface.SetTextColor( colour.r, colour.g, colour.b, colour.a )
|
||||
else
|
||||
surface.SetTextColor( 255, 255, 255, 255 )
|
||||
end
|
||||
|
||||
surface.DrawText( text )
|
||||
|
||||
return w, h
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: SimpleTextOutlined( text, font, x, y, colour, xalign, yalign, outlinewidth, outlinecolour )
|
||||
Desc: Simple draw text at position, but this will expand newlines and tabs.
|
||||
color is a table with r/g/b/a elements
|
||||
-----------------------------------------------------------]]
|
||||
function SimpleTextOutlined(text, font, x, y, colour, xalign, yalign, outlinewidth, outlinecolour)
|
||||
|
||||
local steps = ( outlinewidth * 2 ) / 3
|
||||
if ( steps < 1 ) then steps = 1 end
|
||||
|
||||
for _x = -outlinewidth, outlinewidth, steps do
|
||||
for _y = -outlinewidth, outlinewidth, steps do
|
||||
SimpleText( text, font, x + _x, y + _y, outlinecolour, xalign, yalign )
|
||||
end
|
||||
end
|
||||
|
||||
return SimpleText( text, font, x, y, colour, xalign, yalign )
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: DrawText(text, font, x, y, colour, align )
|
||||
Desc: Simple draw text at position, but this will expand newlines and tabs.
|
||||
color is a table with r/g/b/a elements
|
||||
-----------------------------------------------------------]]
|
||||
local gmatch = string.gmatch
|
||||
local find = string.find
|
||||
local ceil = math.ceil
|
||||
local GetTextSize = surface.GetTextSize
|
||||
local max = math.max
|
||||
function DrawText( text, font, x, y, colour, xalign )
|
||||
|
||||
if ( font == nil ) then font = "DermaDefault" end
|
||||
if ( text != nil ) then text = tostring( text ) end
|
||||
if ( x == nil ) then x = 0 end
|
||||
if ( y == nil ) then y = 0 end
|
||||
|
||||
local curX = x
|
||||
local curY = y
|
||||
local curString = ""
|
||||
|
||||
surface.SetFont( font )
|
||||
local sizeX, lineHeight = GetTextSize( "\n" )
|
||||
local tabWidth = 50
|
||||
|
||||
for str in gmatch( text, "[^\n]*" ) do
|
||||
if #str > 0 then
|
||||
if find( str, "\t" ) then -- there's tabs, some more calculations required
|
||||
for tabs, str2 in gmatch( str, "(\t*)([^\t]*)" ) do
|
||||
curX = ceil( ( curX + tabWidth * max( #tabs - 1, 0 ) ) / tabWidth ) * tabWidth
|
||||
|
||||
if #str2 > 0 then
|
||||
SimpleText( str2, font, curX, curY, colour, xalign )
|
||||
|
||||
local w, _ = GetTextSize( str2 )
|
||||
curX = curX + w
|
||||
end
|
||||
end
|
||||
else -- there's no tabs, this is easy
|
||||
SimpleText( str, font, curX, curY, colour, xalign )
|
||||
end
|
||||
else
|
||||
curX = x
|
||||
curY = curY + ( lineHeight / 2 )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: RoundedBox( bordersize, x, y, w, h, color )
|
||||
Desc: Draws a rounded box - ideally bordersize will be 8 or 16
|
||||
Usage: color is a table with r/g/b/a elements
|
||||
-----------------------------------------------------------]]
|
||||
function RoundedBox( bordersize, x, y, w, h, color )
|
||||
|
||||
return RoundedBoxEx( bordersize, x, y, w, h, color, true, true, true, true )
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: RoundedBox( bordersize, x, y, w, h, color )
|
||||
Desc: Draws a rounded box - ideally bordersize will be 8 or 16
|
||||
Usage: color is a table with r/g/b/a elements
|
||||
-----------------------------------------------------------]]
|
||||
function RoundedBoxEx( bordersize, x, y, w, h, color, tl, tr, bl, br )
|
||||
|
||||
surface.SetDrawColor( color.r, color.g, color.b, color.a )
|
||||
|
||||
-- Do not waste performance if they don't want rounded corners
|
||||
if ( bordersize <= 0 ) then
|
||||
surface.DrawRect( x, y, w, h )
|
||||
return
|
||||
end
|
||||
|
||||
x = math.Round( x )
|
||||
y = math.Round( y )
|
||||
w = math.Round( w )
|
||||
h = math.Round( h )
|
||||
bordersize = math.min( math.Round( bordersize ), math.floor( w / 2 ), math.floor( h / 2 ) )
|
||||
|
||||
-- Draw as much of the rect as we can without textures
|
||||
surface.DrawRect( x + bordersize, y, w - bordersize * 2, h )
|
||||
surface.DrawRect( x, y + bordersize, bordersize, h - bordersize * 2 )
|
||||
surface.DrawRect( x + w - bordersize, y + bordersize, bordersize, h - bordersize * 2 )
|
||||
|
||||
local tex = tex_corner8
|
||||
if ( bordersize > 8 ) then tex = tex_corner16 end
|
||||
if ( bordersize > 16 ) then tex = tex_corner32 end
|
||||
if ( bordersize > 32 ) then tex = tex_corner64 end
|
||||
if ( bordersize > 64 ) then tex = tex_corner512 end
|
||||
|
||||
surface.SetTexture( tex )
|
||||
|
||||
if ( tl ) then
|
||||
surface.DrawTexturedRectUV( x, y, bordersize, bordersize, 0, 0, 1, 1 )
|
||||
else
|
||||
surface.DrawRect( x, y, bordersize, bordersize )
|
||||
end
|
||||
|
||||
if ( tr ) then
|
||||
surface.DrawTexturedRectUV( x + w - bordersize, y, bordersize, bordersize, 1, 0, 0, 1 )
|
||||
else
|
||||
surface.DrawRect( x + w - bordersize, y, bordersize, bordersize )
|
||||
end
|
||||
|
||||
if ( bl ) then
|
||||
surface.DrawTexturedRectUV( x, y + h -bordersize, bordersize, bordersize, 0, 1, 1, 0 )
|
||||
else
|
||||
surface.DrawRect( x, y + h - bordersize, bordersize, bordersize )
|
||||
end
|
||||
|
||||
if ( br ) then
|
||||
surface.DrawTexturedRectUV( x + w - bordersize, y + h - bordersize, bordersize, bordersize, 1, 1, 0, 0 )
|
||||
else
|
||||
surface.DrawRect( x + w - bordersize, y + h - bordersize, bordersize, bordersize )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: WordBox( bordersize, x, y, font, color, font, color, fontcolor, xalign, yalign )
|
||||
Desc: Draws a rounded box - ideally bordersize will be 8 or 16
|
||||
Usage: color is a table with r/g/b/a elements
|
||||
-----------------------------------------------------------]]
|
||||
function WordBox( bordersize, x, y, text, font, color, fontcolor, xalign, yalign )
|
||||
|
||||
surface.SetFont( font )
|
||||
local w, h = surface.GetTextSize( text )
|
||||
|
||||
if ( xalign == TEXT_ALIGN_CENTER ) then
|
||||
x = x - ( bordersize + w / 2 )
|
||||
elseif ( xalign == TEXT_ALIGN_RIGHT ) then
|
||||
x = x - ( bordersize * 2 + w )
|
||||
end
|
||||
|
||||
if ( yalign == TEXT_ALIGN_CENTER ) then
|
||||
y = y - ( bordersize + h / 2 )
|
||||
elseif ( yalign == TEXT_ALIGN_BOTTOM ) then
|
||||
y = y - ( bordersize * 2 + h )
|
||||
end
|
||||
|
||||
RoundedBox( bordersize, x, y, w+bordersize * 2, h+bordersize * 2, color )
|
||||
|
||||
surface.SetTextColor( fontcolor.r, fontcolor.g, fontcolor.b, fontcolor.a )
|
||||
surface.SetTextPos( x + bordersize, y + bordersize )
|
||||
surface.DrawText( text )
|
||||
|
||||
return w + bordersize * 2, h + bordersize * 2
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: Text( table )
|
||||
Desc: Draws text from a table
|
||||
-----------------------------------------------------------]]
|
||||
function Text( tab )
|
||||
|
||||
return SimpleText( tab.text, tab.font, tab.pos[ 1 ], tab.pos[ 2 ], tab.color, tab.xalign, tab.yalign )
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: TextShadow( table )
|
||||
Desc: Draws text from a table
|
||||
-----------------------------------------------------------]]
|
||||
function TextShadow( tab, distance, alpha )
|
||||
|
||||
alpha = alpha or 200
|
||||
|
||||
local color = tab.color
|
||||
local pos = tab.pos
|
||||
tab.color = Color( 0, 0, 0, alpha )
|
||||
tab.pos = { pos[ 1 ] + distance, pos[ 2 ] + distance }
|
||||
|
||||
Text( tab )
|
||||
|
||||
tab.color = color
|
||||
tab.pos = pos
|
||||
|
||||
return Text( tab )
|
||||
|
||||
end
|
||||
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: TexturedQuad( table )
|
||||
Desc: pawrapper
|
||||
-----------------------------------------------------------]]
|
||||
function TexturedQuad( tab )
|
||||
|
||||
local color = tab.color or color_white
|
||||
|
||||
surface.SetTexture( tab.texture )
|
||||
surface.SetDrawColor( color.r, color.g, color.b, color.a )
|
||||
surface.DrawTexturedRect( tab.x, tab.y, tab.w, tab.h )
|
||||
|
||||
end
|
||||
|
||||
function NoTexture()
|
||||
surface.SetTexture( tex_white )
|
||||
end
|
||||
241
lua/includes/modules/drive.lua
Normal file
241
lua/includes/modules/drive.lua
Normal file
@@ -0,0 +1,241 @@
|
||||
|
||||
local IsValid = IsValid
|
||||
local setmetatable = setmetatable
|
||||
local SERVER = SERVER
|
||||
local util = util
|
||||
local ErrorNoHalt = ErrorNoHalt
|
||||
local baseclass = baseclass
|
||||
local LocalPlayer = LocalPlayer
|
||||
|
||||
module( "drive" )
|
||||
|
||||
local Type = {}
|
||||
|
||||
function Register( name, table, base )
|
||||
|
||||
Type[ name ] = table
|
||||
|
||||
--
|
||||
-- If we have a base method then hook
|
||||
-- it up in the meta table
|
||||
--
|
||||
if ( base ) then
|
||||
Type[ base ] = Type[ base ] or baseclass.Get( base )
|
||||
setmetatable( Type[ name ], { __index = Type[ base ] } )
|
||||
end
|
||||
|
||||
if ( SERVER ) then
|
||||
util.AddNetworkString( name )
|
||||
end
|
||||
|
||||
--
|
||||
-- drive methods cooperate with the baseclass system
|
||||
-- /lua/includes/modules/baseclass.lua
|
||||
--
|
||||
baseclass.Set( name, Type[ name ] )
|
||||
|
||||
end
|
||||
|
||||
function PlayerStartDriving( ply, ent, mode )
|
||||
|
||||
local method = Type[mode]
|
||||
if ( !method ) then ErrorNoHalt( "Unknown drive type " .. ( mode ) .. "!\n" ) return end
|
||||
|
||||
local id = util.NetworkStringToID( mode )
|
||||
|
||||
ply:SetDrivingEntity( ent, id )
|
||||
|
||||
end
|
||||
|
||||
function PlayerStopDriving( ply )
|
||||
|
||||
ply:SetDrivingEntity( nil )
|
||||
|
||||
end
|
||||
|
||||
function GetMethod( ply )
|
||||
|
||||
--
|
||||
-- Not driving, return immediately
|
||||
--
|
||||
if ( !ply:IsDrivingEntity() ) then return end
|
||||
|
||||
local ent = ply:GetDrivingEntity()
|
||||
local modeid = ply:GetDrivingMode()
|
||||
|
||||
--
|
||||
-- Entity is invalid or mode isn't set - return out
|
||||
--
|
||||
if ( !IsValid( ent ) || modeid == 0 ) then return end
|
||||
|
||||
--
|
||||
-- Have we already got a drive method? If so then reuse.
|
||||
--
|
||||
local method = ply.m_CurrentDriverMethod
|
||||
if ( method && method.Entity == ent && method.ModeID == modeid ) then return method end
|
||||
|
||||
--
|
||||
-- No method - lets create one. Get the string from the modeid.
|
||||
--
|
||||
local modename = util.NetworkIDToString( modeid )
|
||||
if ( !modename ) then return end
|
||||
|
||||
--
|
||||
-- Get that type. Fail if we don't have the type.
|
||||
--
|
||||
local type = Type[ modename ]
|
||||
if ( !type ) then return end
|
||||
|
||||
local method = {}
|
||||
method.Entity = ent
|
||||
method.Player = ply
|
||||
method.ModeID = modeid
|
||||
|
||||
setmetatable( method, { __index = type } )
|
||||
|
||||
ply.m_CurrentDriverMethod = method
|
||||
|
||||
method:Init()
|
||||
return method
|
||||
|
||||
end
|
||||
|
||||
function DestroyMethod( pl )
|
||||
|
||||
if ( !IsValid( pl ) ) then return end
|
||||
|
||||
pl.m_CurrentDriverMethod = nil
|
||||
|
||||
end
|
||||
--
|
||||
-- Called when the player first
|
||||
-- starts driving this entity
|
||||
--
|
||||
function Start( ply, ent )
|
||||
|
||||
if ( SERVER ) then
|
||||
|
||||
-- Set this to the ent's view entity
|
||||
ply:SetViewEntity( ent )
|
||||
|
||||
-- Save the player's eye angles
|
||||
ply.m_PreDriveEyeAngles = ply:EyeAngles()
|
||||
ply.m_PreDriveObserveMode = ply:GetObserverMode()
|
||||
|
||||
-- Lock the player's eye angles to our angles
|
||||
local ang = ent:GetAngles()
|
||||
ply:SetEyeAngles( ang )
|
||||
|
||||
-- Hide the controlling player's world model
|
||||
ply:DrawWorldModel( false )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- Clientside, the client creates the cmd (usercommand)
|
||||
-- from their input device (mouse, keyboard) and then
|
||||
-- it's sent to the server. Restrict view angles here :)
|
||||
--
|
||||
function CreateMove( cmd )
|
||||
|
||||
local method = GetMethod( LocalPlayer() )
|
||||
if ( !method ) then return end
|
||||
|
||||
method:SetupControls( cmd )
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- Optionally alter the view
|
||||
--
|
||||
function CalcView( ply, view )
|
||||
|
||||
local method = GetMethod( ply )
|
||||
if ( !method ) then return end
|
||||
|
||||
method:CalcView( view )
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- The user command is received by the server and then
|
||||
-- converted into a move. This is also run clientside
|
||||
-- when in multiplayer, for prediction to work.
|
||||
--
|
||||
function StartMove( ply, mv, cmd )
|
||||
|
||||
local method = GetMethod( ply )
|
||||
if ( !method ) then return end
|
||||
|
||||
method:StartMove( mv, cmd )
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- The move is executed here.
|
||||
--
|
||||
function Move( ply, mv )
|
||||
|
||||
local method = GetMethod( ply )
|
||||
if ( !method ) then return end
|
||||
|
||||
method:Move( mv )
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- The move is finished. Copy mv back into the target.
|
||||
--
|
||||
function FinishMove( ply, mv )
|
||||
|
||||
local method = GetMethod( ply )
|
||||
if ( !method ) then return end
|
||||
|
||||
method:FinishMove( mv )
|
||||
|
||||
if ( method.StopDriving ) then
|
||||
PlayerStopDriving( ply )
|
||||
end
|
||||
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- Player has stopped driving the entity
|
||||
--
|
||||
function End( ply, ent )
|
||||
|
||||
--
|
||||
-- If the player is valid then set the view entity to nil
|
||||
--
|
||||
if ( SERVER && IsValid( ply ) ) then
|
||||
|
||||
if ( ply.m_PreDriveEyeAngles != nil ) then
|
||||
ply:SetEyeAngles( ply.m_PreDriveEyeAngles )
|
||||
ply.m_PreDriveEyeAngles = nil
|
||||
end
|
||||
|
||||
if ( ply.m_PreDriveObserveMode != nil ) then
|
||||
ply:SetObserverMode( ply.m_PreDriveObserveMode )
|
||||
ply.m_PreDriveObserveMode = nil
|
||||
end
|
||||
|
||||
ply:SetViewEntity( nil )
|
||||
|
||||
-- Show the controlling player's world model
|
||||
ply:DrawWorldModel( true )
|
||||
|
||||
end
|
||||
|
||||
DestroyMethod( ply )
|
||||
|
||||
end
|
||||
|
||||
1048
lua/includes/modules/duplicator.lua
Normal file
1048
lua/includes/modules/duplicator.lua
Normal file
File diff suppressed because it is too large
Load Diff
77
lua/includes/modules/effects.lua
Normal file
77
lua/includes/modules/effects.lua
Normal file
@@ -0,0 +1,77 @@
|
||||
local ents = ents
|
||||
local pairs = pairs
|
||||
local ipairs = ipairs
|
||||
local string = string
|
||||
local table = table
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: effects
|
||||
Desc: Engine effects hooking
|
||||
-----------------------------------------------------------]]
|
||||
module( "effects" )
|
||||
|
||||
local EffectList = {}
|
||||
|
||||
function Register( t, name )
|
||||
|
||||
name = string.lower( name )
|
||||
|
||||
local old = EffectList[ name ]
|
||||
|
||||
EffectList[ name ] = t
|
||||
|
||||
--
|
||||
-- If we're reloading this entity class
|
||||
-- then refresh all the existing entities.
|
||||
--
|
||||
if ( old != nil ) then
|
||||
|
||||
--
|
||||
-- For each entity using this class
|
||||
--
|
||||
for _, entity in ipairs( ents.FindByClass( name ) ) do
|
||||
|
||||
--
|
||||
-- Replace the contents with this entity table
|
||||
--
|
||||
table.Merge( entity, t )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function Create( name, retval )
|
||||
|
||||
name = string.lower( name )
|
||||
|
||||
--Msg( "Create.. ".. name .. "\n" )
|
||||
|
||||
if ( EffectList[ name ] == nil ) then return nil end
|
||||
|
||||
local NewEffect = retval or {}
|
||||
|
||||
for k, v in pairs( EffectList[ name ] ) do
|
||||
|
||||
NewEffect[ k ] = v
|
||||
|
||||
end
|
||||
|
||||
table.Merge( NewEffect, EffectList[ "base" ] )
|
||||
|
||||
return NewEffect
|
||||
|
||||
end
|
||||
|
||||
function GetList()
|
||||
|
||||
local result = {}
|
||||
|
||||
for k, v in pairs( EffectList ) do
|
||||
table.insert( result, v )
|
||||
end
|
||||
|
||||
return result
|
||||
|
||||
end
|
||||
86
lua/includes/modules/gamemode.lua
Normal file
86
lua/includes/modules/gamemode.lua
Normal file
@@ -0,0 +1,86 @@
|
||||
|
||||
local gmod = gmod
|
||||
local Msg = Msg
|
||||
local hook = hook
|
||||
local table = table
|
||||
local baseclass = baseclass
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: gamemode
|
||||
Desc: A module to manage gamemodes
|
||||
-----------------------------------------------------------]]
|
||||
module( "gamemode" )
|
||||
|
||||
local GameList = {}
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: RegisterGamemode( table, string )
|
||||
Desc: Used to register your gamemode with the engine
|
||||
-----------------------------------------------------------]]
|
||||
function Register( t, name, derived )
|
||||
|
||||
local CurrentGM = gmod.GetGamemode()
|
||||
|
||||
if ( CurrentGM ) then
|
||||
|
||||
if ( CurrentGM.FolderName == name ) then
|
||||
table.Merge( CurrentGM, t )
|
||||
Call( "OnReloaded" );
|
||||
end
|
||||
|
||||
if ( CurrentGM.BaseClass && CurrentGM.BaseClass.FolderName == name ) then
|
||||
table.Merge( CurrentGM.BaseClass, t )
|
||||
Call( "OnReloaded" );
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- This gives the illusion of inheritence
|
||||
if ( name != "base" ) then
|
||||
|
||||
local basetable = Get( derived )
|
||||
if ( basetable ) then
|
||||
t = table.Inherit( t, basetable )
|
||||
else
|
||||
Msg( "Warning: Couldn't find derived gamemode (", derived, ")\n" )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
GameList[ name ] = t
|
||||
|
||||
--
|
||||
-- Using baseclass for gamemodes kind of sucks, because
|
||||
-- the base gamemode is called "base" - and they have to all be unique.
|
||||
-- so here we prefix the gamemode name with "gamemode_" - and when using
|
||||
-- DEFINE_BASECLASS you're expected to do the same.
|
||||
--
|
||||
baseclass.Set( "gamemode_" .. name, t )
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: Get( string )
|
||||
Desc: Get a gamemode by name.
|
||||
-----------------------------------------------------------]]
|
||||
function Get( name )
|
||||
return GameList[ name ]
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: Call( name, args )
|
||||
Desc: Calls a gamemode function
|
||||
-----------------------------------------------------------]]
|
||||
local currentGM
|
||||
function Call( name, ... )
|
||||
|
||||
if ( !currentGM ) then
|
||||
currentGM = gmod.GetGamemode()
|
||||
end
|
||||
|
||||
-- If the gamemode function doesn't exist just return false
|
||||
if ( currentGM && currentGM[name] == nil ) then return false end
|
||||
|
||||
return hook.Call( name, currentGM, ... )
|
||||
|
||||
end
|
||||
150
lua/includes/modules/halo.lua
Normal file
150
lua/includes/modules/halo.lua
Normal file
@@ -0,0 +1,150 @@
|
||||
|
||||
module( "halo", package.seeall )
|
||||
|
||||
local mat_Copy = Material( "pp/copy" )
|
||||
local mat_Add = Material( "pp/add" )
|
||||
local mat_Sub = Material( "pp/sub" )
|
||||
local rt_Store = render.GetScreenEffectTexture( 0 )
|
||||
local rt_Blur = render.GetScreenEffectTexture( 1 )
|
||||
|
||||
local List = {}
|
||||
local RenderEnt = NULL
|
||||
|
||||
function Add( entities, color, blurx, blury, passes, add, ignorez )
|
||||
|
||||
if ( table.IsEmpty( entities ) ) then return end
|
||||
if ( add == nil ) then add = true end
|
||||
if ( ignorez == nil ) then ignorez = false end
|
||||
|
||||
local t =
|
||||
{
|
||||
Ents = entities,
|
||||
Color = color,
|
||||
BlurX = blurx or 2,
|
||||
BlurY = blury or 2,
|
||||
DrawPasses = passes or 1,
|
||||
Additive = add,
|
||||
IgnoreZ = ignorez
|
||||
}
|
||||
|
||||
table.insert( List, t )
|
||||
|
||||
end
|
||||
|
||||
function RenderedEntity()
|
||||
return RenderEnt
|
||||
end
|
||||
|
||||
function Render( entry )
|
||||
|
||||
local rt_Scene = render.GetRenderTarget()
|
||||
|
||||
-- Store a copy of the original scene
|
||||
render.CopyRenderTargetToTexture( rt_Store )
|
||||
|
||||
-- Clear our scene so that additive/subtractive rendering with it will work later
|
||||
if ( entry.Additive ) then
|
||||
render.Clear( 0, 0, 0, 255, false, true )
|
||||
else
|
||||
render.Clear( 255, 255, 255, 255, false, true )
|
||||
end
|
||||
|
||||
-- For certain materials this is necessary to not have the entire screen go pitch black
|
||||
-- For example the glass doors in Episode 2 GMan sequence
|
||||
render.UpdateRefractTexture()
|
||||
|
||||
-- Render colored props to the scene and set their pixels high
|
||||
cam.Start3D()
|
||||
render.SetStencilEnable( true )
|
||||
render.SuppressEngineLighting( true )
|
||||
cam.IgnoreZ( entry.IgnoreZ )
|
||||
|
||||
render.SetStencilWriteMask( 1 )
|
||||
render.SetStencilTestMask( 1 )
|
||||
render.SetStencilReferenceValue( 1 )
|
||||
|
||||
render.SetStencilCompareFunction( STENCIL_ALWAYS )
|
||||
render.SetStencilPassOperation( STENCIL_REPLACE )
|
||||
render.SetStencilFailOperation( STENCIL_KEEP )
|
||||
render.SetStencilZFailOperation( STENCIL_KEEP )
|
||||
|
||||
for k, v in pairs( entry.Ents ) do
|
||||
if ( !IsValid( v ) or v:GetNoDraw() ) then continue end
|
||||
|
||||
RenderEnt = v
|
||||
|
||||
v:DrawModel()
|
||||
end
|
||||
|
||||
RenderEnt = NULL
|
||||
|
||||
render.SetStencilCompareFunction( STENCIL_EQUAL )
|
||||
render.SetStencilPassOperation( STENCIL_KEEP )
|
||||
-- render.SetStencilFailOperation( STENCIL_KEEP )
|
||||
-- render.SetStencilZFailOperation( STENCIL_KEEP )
|
||||
|
||||
cam.Start2D()
|
||||
local entryColor = entry.Color
|
||||
surface.SetDrawColor( entryColor.r, entryColor.g, entryColor.b, entryColor.a )
|
||||
surface.DrawRect( 0, 0, ScrW(), ScrH() )
|
||||
cam.End2D()
|
||||
|
||||
cam.IgnoreZ( false )
|
||||
render.SuppressEngineLighting( false )
|
||||
render.SetStencilEnable( false )
|
||||
cam.End3D()
|
||||
|
||||
-- Store a blurred version of the colored props in an RT
|
||||
render.CopyRenderTargetToTexture( rt_Blur )
|
||||
render.BlurRenderTarget( rt_Blur, entry.BlurX, entry.BlurY, 1 )
|
||||
|
||||
-- Restore the original scene
|
||||
render.SetRenderTarget( rt_Scene )
|
||||
mat_Copy:SetTexture( "$basetexture", rt_Store )
|
||||
mat_Copy:SetString( "$color", "1 1 1" )
|
||||
mat_Copy:SetString( "$alpha", "1" )
|
||||
render.SetMaterial( mat_Copy )
|
||||
render.DrawScreenQuad()
|
||||
|
||||
-- Draw back our blured colored props additively/subtractively, ignoring the high bits
|
||||
render.SetStencilEnable( true )
|
||||
|
||||
render.SetStencilCompareFunction( STENCIL_NOTEQUAL )
|
||||
-- render.SetStencilPassOperation( STENCIL_KEEP )
|
||||
-- render.SetStencilFailOperation( STENCIL_KEEP )
|
||||
-- render.SetStencilZFailOperation( STENCIL_KEEP )
|
||||
|
||||
if ( entry.Additive ) then
|
||||
mat_Add:SetTexture( "$basetexture", rt_Blur )
|
||||
render.SetMaterial( mat_Add )
|
||||
else
|
||||
mat_Sub:SetTexture( "$basetexture", rt_Blur )
|
||||
render.SetMaterial( mat_Sub )
|
||||
end
|
||||
|
||||
for i = 0, entry.DrawPasses do
|
||||
render.DrawScreenQuad()
|
||||
end
|
||||
|
||||
render.SetStencilEnable( false )
|
||||
|
||||
-- Return original values
|
||||
render.SetStencilTestMask( 0 )
|
||||
render.SetStencilWriteMask( 0 )
|
||||
render.SetStencilReferenceValue( 0 )
|
||||
|
||||
end
|
||||
|
||||
hook.Add( "PostDrawEffects", "RenderHalos", function()
|
||||
|
||||
hook.Run( "PreDrawHalos" )
|
||||
|
||||
if ( #List == 0 ) then return end
|
||||
|
||||
for k, v in ipairs( List ) do
|
||||
Render( v )
|
||||
end
|
||||
|
||||
List = {}
|
||||
|
||||
end )
|
||||
143
lua/includes/modules/hook.lua
Normal file
143
lua/includes/modules/hook.lua
Normal file
@@ -0,0 +1,143 @@
|
||||
local gmod = gmod
|
||||
local pairs = pairs
|
||||
local isfunction = isfunction
|
||||
local isstring = isstring
|
||||
local isnumber = isnumber
|
||||
local isbool = isbool
|
||||
local IsValid = IsValid
|
||||
local type = type
|
||||
local ErrorNoHaltWithStack = ErrorNoHaltWithStack
|
||||
|
||||
module( "hook" )
|
||||
|
||||
local Hooks = {}
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: GetTable
|
||||
Desc: Returns a table of all hooks.
|
||||
-----------------------------------------------------------]]
|
||||
function GetTable()
|
||||
return Hooks
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: Add
|
||||
Args: string hookName, any identifier, function func
|
||||
Desc: Add a hook to listen to the specified event.
|
||||
-----------------------------------------------------------]]
|
||||
function Add( event_name, name, func )
|
||||
|
||||
if ( !isstring( event_name ) ) then ErrorNoHaltWithStack( "bad argument #1 to 'Add' (string expected, got " .. type( event_name ) .. ")" ) return end
|
||||
if ( !isfunction( func ) ) then ErrorNoHaltWithStack( "bad argument #3 to 'Add' (function expected, got " .. type( func ) .. ")" ) return end
|
||||
|
||||
local notValid = name == nil || isnumber( name ) or isbool( name ) or isfunction( name ) or !name.IsValid
|
||||
if ( !isstring( name ) and notValid ) then ErrorNoHaltWithStack( "bad argument #2 to 'Add' (string expected, got " .. type( name ) .. ")" ) return end
|
||||
|
||||
if ( Hooks[ event_name ] == nil ) then
|
||||
Hooks[ event_name ] = {}
|
||||
end
|
||||
|
||||
Hooks[ event_name ][ name ] = func
|
||||
|
||||
end
|
||||
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: Remove
|
||||
Args: string hookName, identifier
|
||||
Desc: Removes the hook with the given indentifier.
|
||||
-----------------------------------------------------------]]
|
||||
function Remove( event_name, name )
|
||||
|
||||
if ( !isstring( event_name ) ) then ErrorNoHaltWithStack( "bad argument #1 to 'Remove' (string expected, got " .. type( event_name ) .. ")" ) return end
|
||||
|
||||
local notValid = isnumber( name ) or isbool( name ) or isfunction( name ) or !name.IsValid
|
||||
if ( !isstring( name ) and notValid ) then ErrorNoHaltWithStack( "bad argument #2 to 'Remove' (string expected, got " .. type( name ) .. ")" ) return end
|
||||
|
||||
if ( !Hooks[ event_name ] ) then return end
|
||||
|
||||
Hooks[ event_name ][ name ] = nil
|
||||
|
||||
end
|
||||
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: Run
|
||||
Args: string hookName, vararg args
|
||||
Desc: Calls hooks associated with the hook name.
|
||||
-----------------------------------------------------------]]
|
||||
local currentGM
|
||||
|
||||
function Run( name, ... )
|
||||
if ( !currentGM ) then
|
||||
currentGM = gmod and gmod.GetGamemode() or nil
|
||||
end
|
||||
|
||||
return Call( name, currentGM, ... )
|
||||
end
|
||||
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: Run
|
||||
Args: string hookName, table gamemodeTable, vararg args
|
||||
Desc: Calls hooks associated with the hook name.
|
||||
-----------------------------------------------------------]]
|
||||
function Call( name, gm, ... )
|
||||
|
||||
--
|
||||
-- Run hooks
|
||||
--
|
||||
local HookTable = Hooks[ name ]
|
||||
if ( HookTable != nil ) then
|
||||
|
||||
local a, b, c, d, e, f;
|
||||
|
||||
for k, v in pairs( HookTable ) do
|
||||
|
||||
if ( isstring( k ) ) then
|
||||
|
||||
--
|
||||
-- If it's a string, it's cool
|
||||
--
|
||||
a, b, c, d, e, f = v( ... )
|
||||
|
||||
else
|
||||
|
||||
--
|
||||
-- If the key isn't a string - we assume it to be an entity
|
||||
-- Or panel, or something else that IsValid works on.
|
||||
--
|
||||
if ( IsValid( k ) ) then
|
||||
--
|
||||
-- If the object is valid - pass it as the first argument (self)
|
||||
--
|
||||
a, b, c, d, e, f = v( k, ... )
|
||||
else
|
||||
--
|
||||
-- If the object has become invalid - remove it
|
||||
--
|
||||
HookTable[ k ] = nil
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- Hook returned a value - it overrides the gamemode function
|
||||
--
|
||||
if ( a != nil ) then
|
||||
return a, b, c, d, e, f
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- Call the gamemode function
|
||||
--
|
||||
if ( !gm ) then return end
|
||||
|
||||
local GamemodeFunction = gm[ name ]
|
||||
if ( GamemodeFunction == nil ) then return end
|
||||
|
||||
return GamemodeFunction( gm, ... )
|
||||
|
||||
end
|
||||
107
lua/includes/modules/http.lua
Normal file
107
lua/includes/modules/http.lua
Normal file
@@ -0,0 +1,107 @@
|
||||
|
||||
local HTTP = HTTP
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
HTTP Module. Interaction with HTTP.
|
||||
-----------------------------------------------------------]]
|
||||
module( "http" )
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
|
||||
Get the contents of a webpage.
|
||||
|
||||
Callback should be
|
||||
|
||||
function callback( (args optional), contents, size )
|
||||
|
||||
-----------------------------------------------------------]]
|
||||
function Fetch( url, onsuccess, onfailure, header )
|
||||
|
||||
local request = {
|
||||
url = url,
|
||||
method = "get",
|
||||
headers = header or {},
|
||||
|
||||
success = function( code, body, headers )
|
||||
|
||||
if ( !onsuccess ) then return end
|
||||
|
||||
onsuccess( body, body:len(), headers, code )
|
||||
|
||||
end,
|
||||
|
||||
failed = function( err )
|
||||
|
||||
if ( !onfailure ) then return end
|
||||
|
||||
onfailure( err )
|
||||
|
||||
end
|
||||
}
|
||||
|
||||
local success = HTTP( request )
|
||||
if ( !success && onfailure ) then onfailure( "HTTP failed" ) end
|
||||
|
||||
end
|
||||
|
||||
function Post( url, params, onsuccess, onfailure, header )
|
||||
|
||||
local request = {
|
||||
url = url,
|
||||
method = "post",
|
||||
parameters = params,
|
||||
headers = header or {},
|
||||
|
||||
success = function( code, body, headers )
|
||||
|
||||
if ( !onsuccess ) then return end
|
||||
|
||||
onsuccess( body, body:len(), headers, code )
|
||||
|
||||
end,
|
||||
|
||||
failed = function( err )
|
||||
|
||||
if ( !onfailure ) then return end
|
||||
|
||||
onfailure( err )
|
||||
|
||||
end
|
||||
}
|
||||
|
||||
local success = HTTP( request )
|
||||
if ( !success && onfailure ) then onfailure( "HTTP failed" ) end
|
||||
|
||||
end
|
||||
|
||||
--[[
|
||||
|
||||
Or use HTTP( table )
|
||||
|
||||
local request = {
|
||||
url = "http://pastebin.com/raw.php?i=3jsf50nL",
|
||||
method = "post",
|
||||
|
||||
parameters = {
|
||||
id = "548",
|
||||
country = "England"
|
||||
}
|
||||
|
||||
success = function( code, body, headers )
|
||||
|
||||
Msg( "Request Successful\n" )
|
||||
Msg( "Code: ", code, "\n" )
|
||||
Msg( "Body Length:\n", body:len(), "\n" )
|
||||
Msg( "Body:\n", body, "\n" )
|
||||
PrintTable( headers )
|
||||
|
||||
end,
|
||||
|
||||
failed = function( reason )
|
||||
Msg( "Request failed: ", reason, "\n" )
|
||||
end
|
||||
}
|
||||
|
||||
HTTP( request )
|
||||
|
||||
--]]
|
||||
205
lua/includes/modules/killicon.lua
Normal file
205
lua/includes/modules/killicon.lua
Normal file
@@ -0,0 +1,205 @@
|
||||
|
||||
-- Globals that we need
|
||||
local surface = surface
|
||||
local Msg = Msg
|
||||
local Color = Color
|
||||
local Material = Material
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: killicon
|
||||
Desc: Stores and serves killicons for deathnotice
|
||||
-----------------------------------------------------------]]
|
||||
module( "killicon" )
|
||||
|
||||
local Icons = {}
|
||||
local TYPE_FONT = 0
|
||||
local TYPE_MATERIAL = 1
|
||||
local TYPE_MATERIAL_UV = 2
|
||||
|
||||
function AddFont( name, font, character, color, heightScale )
|
||||
|
||||
Icons[name] = {
|
||||
type = TYPE_FONT,
|
||||
font = font,
|
||||
character = character,
|
||||
color = color or Color( 255, 80, 0 ),
|
||||
|
||||
-- Correct certain icons
|
||||
heightScale = heightScale
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
function Add( name, material, color )
|
||||
|
||||
Icons[name] = {
|
||||
type = TYPE_MATERIAL,
|
||||
material = Material( material ),
|
||||
color = color or Color( 255, 255, 255 )
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
function AddTexCoord( name, material, color, x, y, w, h )
|
||||
|
||||
Icons[name] = {
|
||||
type = TYPE_MATERIAL_UV,
|
||||
material = Material( material ),
|
||||
color = color,
|
||||
tex_x = x,
|
||||
tex_y = y,
|
||||
tex_w = w,
|
||||
tex_h = h
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
function AddAlias( name, alias )
|
||||
|
||||
Icons[name] = Icons[alias]
|
||||
|
||||
end
|
||||
|
||||
function Exists( name )
|
||||
|
||||
return Icons[name] != nil
|
||||
|
||||
end
|
||||
|
||||
function GetSize( name, dontEqualizeHeight )
|
||||
|
||||
if ( !Icons[name] ) then
|
||||
Msg( "Warning: killicon not found '" .. name .. "'\n" )
|
||||
Icons[name] = Icons["default"]
|
||||
end
|
||||
|
||||
local t = Icons[name]
|
||||
|
||||
-- Check the cache
|
||||
if ( t.size ) then
|
||||
|
||||
-- Maintain the old behavior
|
||||
if ( !dontEqualizeHeight ) then return t.size.adj_w, t.size.adj_h end
|
||||
|
||||
return t.size.w, t.size.h
|
||||
end
|
||||
|
||||
local w, h = 0, 0
|
||||
|
||||
if ( t.type == TYPE_FONT ) then
|
||||
|
||||
surface.SetFont( t.font )
|
||||
w, h = surface.GetTextSize( t.character )
|
||||
|
||||
if ( t.heightScale ) then h = h * t.heightScale end
|
||||
|
||||
elseif ( t.type == TYPE_MATERIAL ) then
|
||||
|
||||
w, h = t.material:Width(), t.material:Height()
|
||||
|
||||
elseif ( t.type == TYPE_MATERIAL_UV ) then
|
||||
|
||||
w = t.tex_w
|
||||
h = t.tex_h
|
||||
|
||||
end
|
||||
|
||||
t.size = {}
|
||||
t.size.w = w or 32
|
||||
t.size.h = h or 32
|
||||
|
||||
-- Height adjusted behavior
|
||||
if ( t.type == TYPE_FONT ) then
|
||||
t.size.adj_w, t.size.adj_h = surface.GetTextSize( t.character )
|
||||
-- BUG: This is not same height as the texture icons, and we cannot change it beacuse backwards compability
|
||||
else
|
||||
surface.SetFont( "HL2MPTypeDeath" )
|
||||
local _, fh = surface.GetTextSize( "0" )
|
||||
fh = fh * 0.75 -- Fudge it slightly
|
||||
|
||||
-- Resize, maintaining aspect ratio
|
||||
t.size.adj_w = w * ( fh / h )
|
||||
t.size.adj_h = fh
|
||||
end
|
||||
|
||||
-- Maintain the old behavior
|
||||
if ( !dontEqualizeHeight ) then return t.size.adj_w, t.size.adj_h end
|
||||
|
||||
return w, h
|
||||
|
||||
end
|
||||
|
||||
local function DrawInternal( x, y, name, alpha, noCorrections, dontEqualizeHeight )
|
||||
|
||||
alpha = alpha or 255
|
||||
|
||||
if ( !Icons[name] ) then
|
||||
Msg( "Warning: killicon not found '" .. name .. "'\n" )
|
||||
Icons[name] = Icons["default"]
|
||||
end
|
||||
|
||||
local t = Icons[name]
|
||||
|
||||
local w, h = GetSize( name, dontEqualizeHeight )
|
||||
|
||||
if ( !noCorrections ) then x = x - w * 0.5 end
|
||||
|
||||
if ( t.type == TYPE_FONT ) then
|
||||
|
||||
-- HACK: Default font killicons are anchored to the top, so correct for it
|
||||
if ( noCorrections && !dontEqualizeHeight ) then
|
||||
local _, h2 = GetSize( name, !dontEqualizeHeight )
|
||||
y = y + ( h - h2 ) / 2
|
||||
end
|
||||
|
||||
if ( !noCorrections ) then y = y - h * 0.1 end
|
||||
|
||||
surface.SetTextPos( x, y )
|
||||
surface.SetFont( t.font )
|
||||
surface.SetTextColor( t.color.r, t.color.g, t.color.b, alpha )
|
||||
surface.DrawText( t.character )
|
||||
|
||||
end
|
||||
|
||||
if ( t.type == TYPE_MATERIAL ) then
|
||||
|
||||
if ( !noCorrections ) then y = y - h * 0.3 end
|
||||
|
||||
surface.SetMaterial( t.material )
|
||||
surface.SetDrawColor( t.color.r, t.color.g, t.color.b, alpha )
|
||||
surface.DrawTexturedRect( x, y, w, h )
|
||||
|
||||
end
|
||||
|
||||
if ( t.type == TYPE_MATERIAL_UV ) then
|
||||
|
||||
if ( !noCorrections ) then y = y - h * 0.3 end
|
||||
|
||||
local tw = t.material:Width()
|
||||
local th = t.material:Height()
|
||||
surface.SetMaterial( t.material )
|
||||
surface.SetDrawColor( t.color.r, t.color.g, t.color.b, alpha )
|
||||
surface.DrawTexturedRectUV( x, y, w, h, t.tex_x / tw, t.tex_y / th, ( t.tex_x + t.tex_w ) / tw, ( t.tex_y + t.tex_h ) / th )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Old function with weird vertical adjustments
|
||||
function Draw( x, y, name, alpha )
|
||||
|
||||
DrawInternal( x, y, name, alpha )
|
||||
|
||||
end
|
||||
|
||||
-- The new function that doesn't have the weird vertical adjustments
|
||||
function Render( x, y, name, alpha, dontEqualizeHeight )
|
||||
|
||||
DrawInternal( x, y, name, alpha, true, dontEqualizeHeight )
|
||||
|
||||
end
|
||||
|
||||
local Color_Icon = Color( 255, 80, 0, 255 )
|
||||
|
||||
Add( "default", "HUD/killicons/default", Color_Icon )
|
||||
AddAlias( "suicide", "default" )
|
||||
86
lua/includes/modules/list.lua
Normal file
86
lua/includes/modules/list.lua
Normal file
@@ -0,0 +1,86 @@
|
||||
|
||||
local table = table
|
||||
local pairs = pairs
|
||||
local istable = istable
|
||||
|
||||
module( "list" )
|
||||
|
||||
|
||||
local Lists = {}
|
||||
|
||||
function Get( listid )
|
||||
|
||||
return table.Copy( GetForEdit( listid ) )
|
||||
|
||||
end
|
||||
|
||||
function GetForEdit( listid, nocreate )
|
||||
|
||||
local list = Lists[ listid ]
|
||||
|
||||
if ( !nocreate && list == nil ) then
|
||||
list = {}
|
||||
Lists[ listid ] = list
|
||||
end
|
||||
|
||||
return list
|
||||
|
||||
end
|
||||
|
||||
function GetTable()
|
||||
|
||||
return table.GetKeys( Lists )
|
||||
|
||||
end
|
||||
|
||||
function Add( listid, value )
|
||||
|
||||
return table.insert( GetForEdit( listid ), value )
|
||||
|
||||
end
|
||||
|
||||
function Contains( listid, value )
|
||||
|
||||
local list = Lists[ listid ]
|
||||
if ( list == nil ) then return false end
|
||||
|
||||
for k, v in pairs( list ) do
|
||||
if ( v == value ) then return true end
|
||||
end
|
||||
|
||||
return false
|
||||
|
||||
end
|
||||
|
||||
function Set( listid, key, value )
|
||||
|
||||
GetForEdit( listid )[ key ] = value
|
||||
|
||||
end
|
||||
|
||||
function RemoveEntry( listid, key )
|
||||
|
||||
GetForEdit( listid )[ key ] = nil
|
||||
|
||||
end
|
||||
|
||||
function HasEntry( listid, key )
|
||||
|
||||
local list = Lists[ listid ]
|
||||
|
||||
return list != nil && list[ key ] != nil
|
||||
|
||||
end
|
||||
|
||||
function GetEntry( listid, key )
|
||||
|
||||
local list = GetForEdit( listid )
|
||||
local value = list[ key ]
|
||||
|
||||
if ( istable( value ) ) then
|
||||
value = table.Copy( value )
|
||||
end
|
||||
|
||||
return value
|
||||
|
||||
end
|
||||
557
lua/includes/modules/markup.lua
Normal file
557
lua/includes/modules/markup.lua
Normal file
@@ -0,0 +1,557 @@
|
||||
|
||||
local string = string
|
||||
local table = table
|
||||
local surface = surface
|
||||
local tostring = tostring
|
||||
local ipairs = ipairs
|
||||
local setmetatable = setmetatable
|
||||
local tonumber = tonumber
|
||||
local math = math
|
||||
local utf8 = utf8
|
||||
local _Color = Color
|
||||
|
||||
local MarkupObject = {}
|
||||
MarkupObject.__index = MarkupObject
|
||||
RegisterMetaTable( "MarkupObject", MarkupObject )
|
||||
|
||||
module( "markup" )
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: Constants used for text alignment.
|
||||
These must be the same values as in the draw module.
|
||||
-----------------------------------------------------------]]
|
||||
TEXT_ALIGN_LEFT = 0
|
||||
TEXT_ALIGN_CENTER = 1
|
||||
TEXT_ALIGN_RIGHT = 2
|
||||
TEXT_ALIGN_TOP = 3
|
||||
TEXT_ALIGN_BOTTOM = 4
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: Color( Color( r, g, b, a ) )
|
||||
Desc: Convenience function which converts a Color object into a string
|
||||
which can be used in the <color=r,g,b,a></color> tag
|
||||
|
||||
e.g. Color( 255, 0, 0, 150 ) -> 255,0,0,150
|
||||
Color( 255, 0, 0 ) -> 255,0,0
|
||||
Color( 255, 0, 0, 255 ) -> 255,0,0
|
||||
|
||||
Usage: markup.Color( Color( r, g, b, a ) )
|
||||
-----------------------------------------------------------]]
|
||||
function Color( col )
|
||||
return
|
||||
col.r .. "," ..
|
||||
col.g .. "," ..
|
||||
col.b ..
|
||||
-- If the alpha value is 255, we don't need to include it in the <color> tag, so just omit it:
|
||||
( col.a == 255 and "" or ( "," .. col.a ) )
|
||||
end
|
||||
|
||||
local Color = _Color
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: Temporary information used when building text frames.
|
||||
-----------------------------------------------------------]]
|
||||
local colour_stack = { Color( 255, 255, 255 ) }
|
||||
local font_stack = { "DermaDefault" }
|
||||
local blocks = {}
|
||||
|
||||
local colourmap = {
|
||||
|
||||
-- it's all black and white
|
||||
["black"] = Color( 0, 0, 0 ),
|
||||
["white"] = Color( 255, 255, 255 ),
|
||||
|
||||
-- it's greys
|
||||
["dkgrey"] = Color( 64, 64, 64 ),
|
||||
["grey"] = Color( 128, 128, 128 ),
|
||||
["ltgrey"] = Color( 192, 192, 192 ),
|
||||
|
||||
-- account for speeling mistakes
|
||||
["dkgray"] = Color( 64, 64, 64 ),
|
||||
["gray"] = Color( 128, 128, 128 ),
|
||||
["ltgray"] = Color( 192, 192, 192 ),
|
||||
|
||||
-- normal colours
|
||||
["red"] = Color( 255, 0, 0 ),
|
||||
["green"] = Color( 0, 255, 0 ),
|
||||
["blue"] = Color( 0, 0, 255 ),
|
||||
["yellow"] = Color( 255, 255, 0 ),
|
||||
["purple"] = Color( 255, 0, 255 ),
|
||||
["cyan"] = Color( 0, 255, 255 ),
|
||||
["turq"] = Color( 0, 255, 255 ),
|
||||
|
||||
-- dark variations
|
||||
["dkred"] = Color( 128, 0, 0 ),
|
||||
["dkgreen"] = Color( 0, 128, 0 ),
|
||||
["dkblue"] = Color( 0, 0, 128 ),
|
||||
["dkyellow"] = Color( 128, 128, 0 ),
|
||||
["dkpurple"] = Color( 128, 0, 128 ),
|
||||
["dkcyan"] = Color( 0, 128, 128 ),
|
||||
["dkturq"] = Color( 0, 128, 128 ),
|
||||
|
||||
-- light variations
|
||||
["ltred"] = Color( 255, 128, 128 ),
|
||||
["ltgreen"] = Color( 128, 255, 128 ),
|
||||
["ltblue"] = Color( 128, 128, 255 ),
|
||||
["ltyellow"] = Color( 255, 255, 128 ),
|
||||
["ltpurple"] = Color( 255, 128, 255 ),
|
||||
["ltcyan"] = Color( 128, 255, 255 ),
|
||||
["ltturq"] = Color( 128, 255, 255 ),
|
||||
}
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: colourMatch( c )
|
||||
Desc: Match a colour name to an rgb value.
|
||||
Usage: ** INTERNAL ** Do not use!
|
||||
-----------------------------------------------------------]]
|
||||
local function colourMatch( c )
|
||||
return colourmap[ string.lower( c ) ]
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: ExtractParams(p1,p2,p3)
|
||||
Desc: This function is used to extract the tag information.
|
||||
Usage: ** INTERNAL ** Do not use!
|
||||
-----------------------------------------------------------]]
|
||||
local function ExtractParams( p1, p2, p3 )
|
||||
|
||||
if ( string.sub( p1, 1, 1 ) == "/" ) then
|
||||
|
||||
local tag = string.sub( p1, 2 )
|
||||
|
||||
if ( tag == "color" or tag == "colour" ) then
|
||||
table.remove( colour_stack )
|
||||
elseif ( tag == "font" or tag == "face" ) then
|
||||
table.remove( font_stack )
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
if ( p1 == "color" or p1 == "colour" ) then
|
||||
|
||||
local rgba = colourMatch( p2 )
|
||||
|
||||
if ( rgba == nil ) then
|
||||
rgba = Color( 255, 255, 255, 255 )
|
||||
local x = { "r", "g", "b", "a" }
|
||||
local n = 1
|
||||
for k, v in string.gmatch( p2, "(%d+),?" ) do
|
||||
rgba[ x[ n ] ] = tonumber( k )
|
||||
n = n + 1
|
||||
end
|
||||
end
|
||||
|
||||
table.insert( colour_stack, rgba )
|
||||
|
||||
elseif ( p1 == "font" or p1 == "face" ) then
|
||||
|
||||
table.insert( font_stack, tostring( p2 ) )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: CheckTextOrTag( p )
|
||||
Desc: This function places data in the "blocks" table
|
||||
depending of if p is a tag, or some text
|
||||
Usage: ** INTERNAL ** Do not use!
|
||||
-----------------------------------------------------------]]
|
||||
local function CheckTextOrTag( p )
|
||||
if ( p == "" ) then return end
|
||||
if ( p == nil ) then return end
|
||||
|
||||
if ( string.sub( p, 1, 1 ) == "<" ) then
|
||||
string.gsub( p, "<([/%a]*)=?([^>]*)", ExtractParams )
|
||||
else
|
||||
|
||||
local text_block = {}
|
||||
text_block.text = p
|
||||
text_block.colour = colour_stack[ #colour_stack ]
|
||||
text_block.font = font_stack[ #font_stack ]
|
||||
table.insert( blocks, text_block )
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: ProcessMatches(p1,p2,p3)
|
||||
Desc: CheckTextOrTag for 3 parameters. Called by string.gsub
|
||||
Usage: ** INTERNAL ** Do not use!
|
||||
-----------------------------------------------------------]]
|
||||
local function ProcessMatches( p1, p2, p3 )
|
||||
if ( p1 ) then CheckTextOrTag( p1 ) end
|
||||
if ( p2 ) then CheckTextOrTag( p2 ) end
|
||||
if ( p3 ) then CheckTextOrTag( p3 ) end
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: MarkupObject:GetWidth()
|
||||
Desc: Returns the width of a markup block
|
||||
Usage: ml:GetWidth()
|
||||
-----------------------------------------------------------]]
|
||||
function MarkupObject:GetWidth()
|
||||
return self.totalWidth
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: MarkupObject:GetMaxWidth()
|
||||
Desc: Returns the maximum width of a markup block
|
||||
Usage: ml:GetMaxWidth()
|
||||
-----------------------------------------------------------]]
|
||||
function MarkupObject:GetMaxWidth()
|
||||
return self.maxWidth or self.totalWidth
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: MarkupObject:GetHeight()
|
||||
Desc: Returns the height of a markup block
|
||||
Usage: ml:GetHeight()
|
||||
-----------------------------------------------------------]]
|
||||
function MarkupObject:GetHeight()
|
||||
return self.totalHeight
|
||||
end
|
||||
|
||||
function MarkupObject:Size()
|
||||
return self.totalWidth, self.totalHeight
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: MarkupObject:Draw(xOffset, yOffset, halign, valign, alphaoverride)
|
||||
Desc: Draw the markup text to the screen as position xOffset, yOffset.
|
||||
Halign and Valign can be used to align the text relative to its offset.
|
||||
Alphaoverride can be used to override the alpha value of the text-colour.
|
||||
textAlign can be used to align the actual text inside of its bounds.
|
||||
Usage: MarkupObject:Draw(100, 100)
|
||||
-----------------------------------------------------------]]
|
||||
function MarkupObject:Draw( xOffset, yOffset, halign, valign, alphaoverride, textAlign )
|
||||
for i, blk in ipairs( self.blocks ) do
|
||||
local y = yOffset + ( blk.height - blk.thisY ) + blk.offset.y
|
||||
local x = xOffset
|
||||
|
||||
if ( halign == TEXT_ALIGN_CENTER ) then x = x - ( self.totalWidth / 2 )
|
||||
elseif ( halign == TEXT_ALIGN_RIGHT ) then x = x - self.totalWidth
|
||||
end
|
||||
|
||||
x = x + blk.offset.x
|
||||
|
||||
if ( valign == TEXT_ALIGN_CENTER ) then y = y - ( self.totalHeight / 2 )
|
||||
elseif ( valign == TEXT_ALIGN_BOTTOM ) then y = y - self.totalHeight
|
||||
end
|
||||
|
||||
local alpha = blk.colour.a
|
||||
if ( alphaoverride ) then alpha = alphaoverride end
|
||||
|
||||
surface.SetFont( blk.font )
|
||||
surface.SetTextColor( blk.colour.r, blk.colour.g, blk.colour.b, alpha )
|
||||
|
||||
surface.SetTextPos( x, y )
|
||||
if ( textAlign ~= TEXT_ALIGN_LEFT ) then
|
||||
local lineWidth = self.lineWidths[ blk.offset.y ]
|
||||
if ( lineWidth ) then
|
||||
if ( textAlign == TEXT_ALIGN_CENTER ) then
|
||||
surface.SetTextPos( x + ( ( self.totalWidth - lineWidth ) / 2 ), y )
|
||||
elseif ( textAlign == TEXT_ALIGN_RIGHT ) then
|
||||
surface.SetTextPos( x + ( self.totalWidth - lineWidth ), y )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
surface.DrawText( blk.text )
|
||||
end
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: Escape(str)
|
||||
Desc: Converts a string to its escaped, markup-safe equivalent
|
||||
Usage: markup.Escape( "<font=Default>The font will remain unchanged & these < > & symbols will also appear normally</font>" )
|
||||
-----------------------------------------------------------]]
|
||||
local escapeEntities, unescapeEntities = {
|
||||
["&"] = "&",
|
||||
["<"] = "<",
|
||||
[">"] = ">"
|
||||
}, {
|
||||
["&"] = "&",
|
||||
["<"] = "<",
|
||||
[">"] = ">"
|
||||
}
|
||||
function Escape( str )
|
||||
return ( string.gsub( tostring( str ), "[&<>]", escapeEntities ) )
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: Parse(ml, maxwidth)
|
||||
Desc: Parses the pseudo-html markup language, and creates a
|
||||
MarkupObject, which can be used to the draw the
|
||||
text to the screen. Valid tags are: font and colour.
|
||||
\n and \t are also available to move to the next line,
|
||||
or insert a tab character.
|
||||
Maxwidth can be used to make the text wrap to a specific
|
||||
width and allows for text alignment (e.g. centering) inside
|
||||
the bounds.
|
||||
Usage: markup.Parse( "<font=Default>changed font</font>\n<colour=255,0,255,255>changed colour</colour>" )
|
||||
-----------------------------------------------------------]]
|
||||
function Parse( ml, maxwidth )
|
||||
|
||||
ml = utf8.force( ml ) -- Ensure we have valid UTF-8 data
|
||||
|
||||
colour_stack = { Color( 255, 255, 255 ) }
|
||||
font_stack = { "DermaDefault" }
|
||||
blocks = {}
|
||||
|
||||
if ( !string.find( ml, "<" ) ) then
|
||||
ml = ml .. "<nop>"
|
||||
end
|
||||
|
||||
string.gsub( ml, "([^<>]*)(<[^>]+.)([^<>]*)", ProcessMatches )
|
||||
|
||||
local xOffset = 0
|
||||
local yOffset = 0
|
||||
local xSize = 0
|
||||
local xMax = 0
|
||||
local thisMaxY = 0
|
||||
local new_block_list = {}
|
||||
local ymaxes = {}
|
||||
local lineWidths = {}
|
||||
|
||||
local lineHeight = 0
|
||||
for i, blk in ipairs( blocks ) do
|
||||
|
||||
surface.SetFont( blk.font )
|
||||
|
||||
blk.text = string.gsub( blk.text, "(&.-;)", unescapeEntities )
|
||||
|
||||
local thisY = 0
|
||||
local curString = ""
|
||||
for j, c in utf8.codes( blk.text ) do
|
||||
|
||||
local ch = utf8.char( c )
|
||||
|
||||
if ( ch == "\n" ) then
|
||||
|
||||
if ( thisY == 0 ) then
|
||||
thisY = lineHeight
|
||||
thisMaxY = lineHeight
|
||||
else
|
||||
lineHeight = thisY
|
||||
end
|
||||
|
||||
if ( string.len( curString ) > 0 ) then
|
||||
local x1 = surface.GetTextSize( curString )
|
||||
|
||||
local new_block = {
|
||||
text = curString,
|
||||
font = blk.font,
|
||||
colour = blk.colour,
|
||||
thisY = thisY,
|
||||
thisX = x1,
|
||||
offset = {
|
||||
x = xOffset,
|
||||
y = yOffset
|
||||
}
|
||||
}
|
||||
table.insert( new_block_list, new_block )
|
||||
if ( xOffset + x1 > xMax ) then
|
||||
xMax = xOffset + x1
|
||||
end
|
||||
end
|
||||
|
||||
xOffset = 0
|
||||
xSize = 0
|
||||
yOffset = yOffset + thisMaxY
|
||||
thisY = 0
|
||||
curString = ""
|
||||
thisMaxY = 0
|
||||
|
||||
elseif ( ch == "\t" ) then
|
||||
|
||||
if ( string.len( curString ) > 0 ) then
|
||||
local x1 = surface.GetTextSize( curString )
|
||||
|
||||
local new_block = {
|
||||
text = curString,
|
||||
font = blk.font,
|
||||
colour = blk.colour,
|
||||
thisY = thisY,
|
||||
thisX = x1,
|
||||
offset = {
|
||||
x = xOffset,
|
||||
y = yOffset
|
||||
}
|
||||
}
|
||||
table.insert( new_block_list, new_block )
|
||||
if ( xOffset + x1 > xMax ) then
|
||||
xMax = xOffset + x1
|
||||
end
|
||||
end
|
||||
|
||||
curString = ""
|
||||
|
||||
local xOldSize = xSize
|
||||
xSize = 0
|
||||
local xOldOffset = xOffset
|
||||
xOffset = math.ceil( ( xOffset + xOldSize ) / 50 ) * 50
|
||||
|
||||
if ( xOffset == xOldOffset ) then
|
||||
xOffset = xOffset + 50
|
||||
|
||||
if ( maxwidth and xOffset > maxwidth ) then
|
||||
-- Needs a new line
|
||||
if ( thisY == 0 ) then
|
||||
thisY = lineHeight
|
||||
thisMaxY = lineHeight
|
||||
else
|
||||
lineHeight = thisY
|
||||
end
|
||||
xOffset = 0
|
||||
yOffset = yOffset + thisMaxY
|
||||
thisY = 0
|
||||
thisMaxY = 0
|
||||
end
|
||||
end
|
||||
else
|
||||
local x, y = surface.GetTextSize( ch )
|
||||
|
||||
if ( x == nil ) then return end
|
||||
|
||||
if ( maxwidth and maxwidth > x ) then
|
||||
if ( xOffset + xSize + x >= maxwidth ) then
|
||||
|
||||
-- need to: find the previous space in the curString
|
||||
-- if we can't find one, take off the last character
|
||||
-- and insert as a new block, incrementing the y etc
|
||||
|
||||
local lastSpacePos = string.len( curString )
|
||||
for k = 1,string.len( curString ) do
|
||||
local chspace = string.sub( curString, k, k )
|
||||
if ( chspace == " " ) then
|
||||
lastSpacePos = k
|
||||
end
|
||||
end
|
||||
|
||||
local previous_block = new_block_list[ #new_block_list ]
|
||||
local wrap = lastSpacePos == string.len( curString ) && lastSpacePos > 0
|
||||
if ( previous_block and previous_block.text:match( " $" ) and wrap and surface.GetTextSize( blk.text ) < maxwidth ) then
|
||||
-- If the block was preceded by a space, wrap the block onto the next line first, as we can probably fit it there
|
||||
local trimmed, trimCharNum = previous_block.text:gsub( " +$", "" )
|
||||
if ( trimCharNum > 0 ) then
|
||||
previous_block.text = trimmed
|
||||
previous_block.thisX = surface.GetTextSize( previous_block.text )
|
||||
end
|
||||
else
|
||||
if ( wrap ) then
|
||||
-- If the block takes up multiple lines (and has no spaces), split it up
|
||||
local sequenceStartPos = utf8.offset( curString, 0, lastSpacePos )
|
||||
ch = string.match( curString, utf8.charpattern, sequenceStartPos ) .. ch
|
||||
j = utf8.offset( curString, 1, sequenceStartPos )
|
||||
curString = string.sub( curString, 1, sequenceStartPos - 1 )
|
||||
else
|
||||
-- Otherwise, strip the trailing space and start a new line
|
||||
ch = string.sub( curString, lastSpacePos + 1 ) .. ch
|
||||
j = lastSpacePos + 1
|
||||
curString = string.sub( curString, 1, math.max( lastSpacePos - 1, 0 ) )
|
||||
end
|
||||
|
||||
local m = 1
|
||||
while ( string.sub( ch, m, m ) == " " ) do
|
||||
m = m + 1
|
||||
end
|
||||
ch = string.sub( ch, m )
|
||||
|
||||
local x1, y1 = surface.GetTextSize( curString )
|
||||
|
||||
if ( y1 > thisMaxY ) then
|
||||
thisMaxY = y1
|
||||
ymaxes[ yOffset ] = thisMaxY
|
||||
lineHeight = y1
|
||||
end
|
||||
|
||||
local new_block = {
|
||||
text = curString,
|
||||
font = blk.font,
|
||||
colour = blk.colour,
|
||||
thisY = thisY,
|
||||
thisX = x1,
|
||||
offset = {
|
||||
x = xOffset,
|
||||
y = yOffset
|
||||
}
|
||||
}
|
||||
table.insert( new_block_list, new_block )
|
||||
|
||||
if ( xOffset + x1 > xMax ) then
|
||||
xMax = xOffset + x1
|
||||
end
|
||||
|
||||
curString = ""
|
||||
end
|
||||
|
||||
xOffset = 0
|
||||
xSize = 0
|
||||
x, y = surface.GetTextSize( ch )
|
||||
yOffset = yOffset + thisMaxY
|
||||
thisY = 0
|
||||
thisMaxY = 0
|
||||
end
|
||||
end
|
||||
|
||||
curString = curString .. ch
|
||||
|
||||
thisY = y
|
||||
xSize = xSize + x
|
||||
|
||||
if ( y > thisMaxY ) then
|
||||
thisMaxY = y
|
||||
ymaxes[ yOffset ] = thisMaxY
|
||||
lineHeight = y
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if ( string.len( curString ) > 0 ) then
|
||||
|
||||
local x1 = surface.GetTextSize( curString )
|
||||
|
||||
local new_block = {
|
||||
text = curString,
|
||||
font = blk.font,
|
||||
colour = blk.colour,
|
||||
thisY = thisY,
|
||||
thisX = x1,
|
||||
offset = {
|
||||
x = xOffset,
|
||||
y = yOffset
|
||||
}
|
||||
}
|
||||
table.insert( new_block_list, new_block )
|
||||
|
||||
lineHeight = thisY
|
||||
|
||||
if ( xOffset + x1 > xMax ) then
|
||||
xMax = xOffset + x1
|
||||
end
|
||||
xOffset = xOffset + x1
|
||||
end
|
||||
xSize = 0
|
||||
end
|
||||
|
||||
local totalHeight = 0
|
||||
for i, blk in ipairs( new_block_list ) do
|
||||
blk.height = ymaxes[ blk.offset.y ]
|
||||
|
||||
if ( blk.offset.y + blk.height > totalHeight ) then
|
||||
totalHeight = blk.offset.y + blk.height
|
||||
end
|
||||
|
||||
lineWidths[ blk.offset.y ] = math.max( lineWidths[ blk.offset.y ] or 0, blk.offset.x + blk.thisX )
|
||||
end
|
||||
|
||||
return setmetatable( {
|
||||
totalHeight = totalHeight,
|
||||
totalWidth = xMax,
|
||||
maxWidth = maxwidth,
|
||||
lineWidths = lineWidths,
|
||||
blocks = new_block_list
|
||||
}, MarkupObject )
|
||||
end
|
||||
79
lua/includes/modules/matproxy.lua
Normal file
79
lua/includes/modules/matproxy.lua
Normal file
@@ -0,0 +1,79 @@
|
||||
|
||||
|
||||
module( "matproxy", package.seeall )
|
||||
|
||||
ProxyList = {}
|
||||
ActiveList = {}
|
||||
|
||||
--
|
||||
-- Called by engine, returns true if we're overriding a proxy
|
||||
--
|
||||
function ShouldOverrideProxy( name )
|
||||
|
||||
return ProxyList[ name ] != nil
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- Called to add a new proxy (see lua/matproxy/ for examples)
|
||||
--
|
||||
function Add( tbl )
|
||||
|
||||
if ( !tbl.name ) then return end
|
||||
if ( !tbl.bind ) then return end
|
||||
|
||||
local bReloading = ProxyList[ tbl.name ] != nil
|
||||
|
||||
ProxyList[ tbl.name ] = tbl
|
||||
|
||||
--
|
||||
-- If we're reloading then reload all the active entries that use this proxy
|
||||
--
|
||||
if ( bReloading ) then
|
||||
|
||||
for k, v in pairs( ActiveList ) do
|
||||
|
||||
if ( tbl.name != v.name ) then continue end
|
||||
|
||||
Msg( "Reloading: ", v.Material, "\n" )
|
||||
Init( tbl.name, k, v.Material, v.Values )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- Called by the engine from OnBind
|
||||
--
|
||||
function Call( name, mat, ent )
|
||||
|
||||
local proxy = ActiveList[ name ]
|
||||
if ( !proxy ) then return end
|
||||
if ( !proxy.bind ) then return end
|
||||
|
||||
proxy:bind( proxy.Material, ent )
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- Called by the engine from OnBind
|
||||
--
|
||||
function Init( name, uname, mat, values )
|
||||
|
||||
local proxy = ProxyList[ name ]
|
||||
if ( !proxy ) then return end
|
||||
|
||||
ActiveList[ uname ] = table.Copy( proxy )
|
||||
local active_proxy = ActiveList[ uname ]
|
||||
|
||||
-- Store these incase we reload
|
||||
active_proxy.Values = values
|
||||
active_proxy.Material = mat
|
||||
|
||||
if ( !active_proxy.init ) then return end
|
||||
|
||||
active_proxy:init( mat, values )
|
||||
|
||||
end
|
||||
39
lua/includes/modules/menubar.lua
Normal file
39
lua/includes/modules/menubar.lua
Normal file
@@ -0,0 +1,39 @@
|
||||
|
||||
menubar = {}
|
||||
|
||||
function menubar.Init()
|
||||
|
||||
menubar.Control = vgui.Create( "DMenuBar" )
|
||||
menubar.Control:Dock( TOP )
|
||||
menubar.Control:SetVisible( false )
|
||||
|
||||
hook.Run( "PopulateMenuBar", menubar.Control )
|
||||
|
||||
end
|
||||
|
||||
function menubar.ParentTo( pnl )
|
||||
|
||||
// I don't like this
|
||||
if ( !IsValid( menubar.Control ) ) then
|
||||
menubar.Init()
|
||||
end
|
||||
|
||||
menubar.Control:SetParent( pnl )
|
||||
menubar.Control:MoveToBack()
|
||||
menubar.Control:SetHeight( 30 )
|
||||
menubar.Control:SetVisible( true )
|
||||
|
||||
end
|
||||
|
||||
function menubar.IsParent( pnl )
|
||||
|
||||
return menubar.Control:GetParent() == pnl
|
||||
|
||||
end
|
||||
|
||||
|
||||
hook.Add( "OnGamemodeLoaded", "CreateMenuBar", function()
|
||||
|
||||
menubar.Init()
|
||||
|
||||
end )
|
||||
295
lua/includes/modules/notification.lua
Normal file
295
lua/includes/modules/notification.lua
Normal file
@@ -0,0 +1,295 @@
|
||||
|
||||
surface.CreateFont( "GModNotify", {
|
||||
font = "Arial",
|
||||
size = 21,
|
||||
weight = 0,
|
||||
extended = true
|
||||
} )
|
||||
|
||||
NOTIFY_GENERIC = 0
|
||||
NOTIFY_ERROR = 1
|
||||
NOTIFY_UNDO = 2
|
||||
NOTIFY_HINT = 3
|
||||
NOTIFY_CLEANUP = 4
|
||||
|
||||
module( "notification", package.seeall )
|
||||
|
||||
local NoticeMaterial = {}
|
||||
|
||||
NoticeMaterial[ NOTIFY_GENERIC ] = Material( "vgui/notices/generic" )
|
||||
NoticeMaterial[ NOTIFY_ERROR ] = Material( "vgui/notices/error" )
|
||||
NoticeMaterial[ NOTIFY_UNDO ] = Material( "vgui/notices/undo" )
|
||||
NoticeMaterial[ NOTIFY_HINT ] = Material( "vgui/notices/hint" )
|
||||
NoticeMaterial[ NOTIFY_CLEANUP ] = Material( "vgui/notices/cleanup" )
|
||||
|
||||
local Notices = {}
|
||||
|
||||
function AddProgress( uid, text, frac )
|
||||
|
||||
if ( IsValid( Notices[ uid ] ) ) then
|
||||
|
||||
Notices[ uid ].StartTime = SysTime()
|
||||
Notices[ uid ].Length = -1
|
||||
Notices[ uid ]:SetText( text )
|
||||
Notices[ uid ]:SetProgress( frac )
|
||||
return
|
||||
|
||||
end
|
||||
|
||||
local parent = nil
|
||||
if ( GetOverlayPanel ) then parent = GetOverlayPanel() end
|
||||
|
||||
local Panel = vgui.Create( "NoticePanel", parent )
|
||||
Panel.StartTime = SysTime()
|
||||
Panel.Length = -1
|
||||
Panel.VelX = -5
|
||||
Panel.VelY = 0
|
||||
Panel.fx = ScrW() + 200
|
||||
Panel.fy = ScrH()
|
||||
Panel:SetAlpha( 255 )
|
||||
Panel:SetText( text )
|
||||
Panel:SetPos( Panel.fx, Panel.fy )
|
||||
Panel:SetProgress( frac )
|
||||
|
||||
Notices[ uid ] = Panel
|
||||
|
||||
end
|
||||
|
||||
function Kill( uid )
|
||||
|
||||
if ( !IsValid( Notices[ uid ] ) ) then return end
|
||||
|
||||
Notices[ uid ].StartTime = SysTime()
|
||||
Notices[ uid ].Length = 0.8
|
||||
|
||||
end
|
||||
|
||||
function AddLegacy( text, type, length )
|
||||
|
||||
local parent = nil
|
||||
if ( GetOverlayPanel ) then parent = GetOverlayPanel() end
|
||||
|
||||
local Panel = vgui.Create( "NoticePanel", parent )
|
||||
Panel.StartTime = SysTime()
|
||||
Panel.Length = math.max( length, 0 )
|
||||
Panel.VelX = -5
|
||||
Panel.VelY = 0
|
||||
Panel.fx = ScrW() + 200
|
||||
Panel.fy = ScrH()
|
||||
Panel:SetAlpha( 255 )
|
||||
Panel:SetText( text )
|
||||
Panel:SetLegacyType( type )
|
||||
Panel:SetPos( Panel.fx, Panel.fy )
|
||||
|
||||
table.insert( Notices, Panel )
|
||||
|
||||
end
|
||||
|
||||
-- This is ugly because it's ripped straight from the old notice system
|
||||
local function UpdateNotice( pnl, total_h )
|
||||
|
||||
local x = pnl.fx
|
||||
local y = pnl.fy
|
||||
|
||||
local w = pnl:GetWide() + 16
|
||||
local h = pnl:GetTall() + 4
|
||||
|
||||
local ideal_y = ScrH() - 150 - h - total_h
|
||||
local ideal_x = ScrW() - w - 20
|
||||
|
||||
local timeleft = pnl.StartTime - ( SysTime() - pnl.Length )
|
||||
if ( pnl.Length < 0 ) then timeleft = 1 end
|
||||
|
||||
-- Cartoon style about to go thing
|
||||
if ( timeleft < 0.7 ) then
|
||||
ideal_x = ideal_x - 50
|
||||
end
|
||||
|
||||
-- Gone!
|
||||
if ( timeleft < 0.2 ) then
|
||||
ideal_x = ideal_x + w * 2
|
||||
end
|
||||
|
||||
local spd = RealFrameTime() * 15
|
||||
|
||||
y = y + pnl.VelY * spd
|
||||
x = x + pnl.VelX * spd
|
||||
|
||||
local dist = ideal_y - y
|
||||
pnl.VelY = pnl.VelY + dist * spd * 1
|
||||
if ( math.abs( dist ) < 2 && math.abs( pnl.VelY ) < 0.1 ) then pnl.VelY = 0 end
|
||||
dist = ideal_x - x
|
||||
pnl.VelX = pnl.VelX + dist * spd * 1
|
||||
if ( math.abs( dist ) < 2 && math.abs( pnl.VelX ) < 0.1 ) then pnl.VelX = 0 end
|
||||
|
||||
-- Friction.. kind of FPS independant.
|
||||
pnl.VelX = pnl.VelX * ( 0.95 - RealFrameTime() * 8 )
|
||||
pnl.VelY = pnl.VelY * ( 0.95 - RealFrameTime() * 8 )
|
||||
|
||||
pnl.fx = x
|
||||
pnl.fy = y
|
||||
|
||||
-- If the panel is too high up (out of screen), do not update its position. This lags a lot when there are lot of panels outside of the screen
|
||||
if ( ideal_y > -ScrH() ) then
|
||||
pnl:SetPos( pnl.fx, pnl.fy )
|
||||
end
|
||||
|
||||
return total_h + h
|
||||
|
||||
end
|
||||
|
||||
local function Update()
|
||||
|
||||
if ( !Notices ) then return end
|
||||
|
||||
local h = 0
|
||||
for key, pnl in pairs( Notices ) do
|
||||
|
||||
h = UpdateNotice( pnl, h )
|
||||
|
||||
end
|
||||
|
||||
for k, Panel in pairs( Notices ) do
|
||||
|
||||
if ( !IsValid( Panel ) || Panel:KillSelf() ) then Notices[ k ] = nil end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
hook.Add( "Think", "NotificationThink", Update )
|
||||
|
||||
local PANEL = {}
|
||||
|
||||
function PANEL:Init()
|
||||
|
||||
self:DockPadding( 3, 3, 3, 3 )
|
||||
|
||||
self.Label = vgui.Create( "DLabel", self )
|
||||
self.Label:Dock( FILL )
|
||||
self.Label:SetFont( "GModNotify" )
|
||||
self.Label:SetTextColor( color_white )
|
||||
self.Label:SetExpensiveShadow( 1, Color( 0, 0, 0, 200 ) )
|
||||
self.Label:SetContentAlignment( 5 )
|
||||
|
||||
self:SetBackgroundColor( Color( 20, 20, 20, 255 * 0.6 ) )
|
||||
|
||||
end
|
||||
|
||||
function PANEL:SetText( txt )
|
||||
|
||||
self.Label:SetText( txt )
|
||||
self:SizeToContents()
|
||||
|
||||
end
|
||||
|
||||
function PANEL:SizeToContents()
|
||||
|
||||
self.Label:SizeToContents()
|
||||
|
||||
local width, tall = self.Label:GetSize()
|
||||
|
||||
tall = math.max( tall, 32 ) + 6
|
||||
width = width + 20
|
||||
|
||||
if ( IsValid( self.Image ) ) then
|
||||
width = width + 32 + 8
|
||||
|
||||
local x = ( tall - 36 ) / 2
|
||||
self.Image:DockMargin( 0, x, 0, x )
|
||||
end
|
||||
|
||||
if ( self.Progress ) then
|
||||
tall = tall + 10
|
||||
self.Label:DockMargin( 0, 0, 0, 10 )
|
||||
end
|
||||
|
||||
self:SetSize( width, tall )
|
||||
|
||||
self:InvalidateLayout()
|
||||
|
||||
end
|
||||
|
||||
function PANEL:SetLegacyType( t )
|
||||
|
||||
self.Image = vgui.Create( "DImageButton", self )
|
||||
self.Image:SetMaterial( NoticeMaterial[ t ] )
|
||||
self.Image:SetSize( 32, 32 )
|
||||
self.Image:Dock( LEFT )
|
||||
self.Image:DockMargin( 0, 0, 8, 0 )
|
||||
self.Image.DoClick = function()
|
||||
self.StartTime = 0
|
||||
end
|
||||
|
||||
self:SizeToContents()
|
||||
|
||||
end
|
||||
|
||||
function PANEL:Paint( w, h )
|
||||
|
||||
local shouldDraw = !( LocalPlayer && IsValid( LocalPlayer() ) && IsValid( LocalPlayer():GetActiveWeapon() ) && LocalPlayer():GetActiveWeapon():GetClass() == "gmod_camera" )
|
||||
|
||||
if ( IsValid( self.Label ) ) then self.Label:SetVisible( shouldDraw ) end
|
||||
if ( IsValid( self.Image ) ) then self.Image:SetVisible( shouldDraw ) end
|
||||
|
||||
if ( !shouldDraw ) then return end
|
||||
|
||||
self.BaseClass.Paint( self, w, h )
|
||||
|
||||
if ( !self.Progress ) then return end
|
||||
|
||||
local boxX, boxY = 10, self:GetTall() - 13
|
||||
local boxW, boxH = self:GetWide() - 20, 5
|
||||
local boxInnerW = boxW - 2
|
||||
|
||||
surface.SetDrawColor( 0, 100, 0, 150 )
|
||||
surface.DrawRect( boxX, boxY, boxW, boxH )
|
||||
|
||||
surface.SetDrawColor( 0, 50, 0, 255 )
|
||||
surface.DrawRect( boxX + 1, boxY + 1, boxW - 2, boxH - 2 )
|
||||
|
||||
local w = math.ceil( boxInnerW * 0.25 )
|
||||
local x = math.fmod( math.floor( SysTime() * 200 ), boxInnerW + w ) - w
|
||||
|
||||
if ( self.ProgressFrac ) then
|
||||
x = 0
|
||||
w = math.ceil( boxInnerW * self.ProgressFrac )
|
||||
end
|
||||
|
||||
if ( x + w > boxInnerW ) then w = math.ceil( boxInnerW - x ) end
|
||||
if ( x < 0 ) then
|
||||
w = w + x
|
||||
x = 0
|
||||
end
|
||||
|
||||
surface.SetDrawColor( 0, 255, 0, 255 )
|
||||
surface.DrawRect( boxX + 1 + x, boxY + 1, w, boxH - 2 )
|
||||
|
||||
end
|
||||
|
||||
function PANEL:SetProgress( frac )
|
||||
|
||||
self.Progress = true
|
||||
self.ProgressFrac = frac
|
||||
|
||||
self:SizeToContents()
|
||||
|
||||
end
|
||||
|
||||
function PANEL:KillSelf()
|
||||
|
||||
-- Infinite length
|
||||
if ( self.Length < 0 ) then return false end
|
||||
|
||||
if ( self.StartTime + self.Length < SysTime() ) then
|
||||
|
||||
self:Remove()
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
return false
|
||||
|
||||
end
|
||||
|
||||
vgui.Register( "NoticePanel", PANEL, "DPanel" )
|
||||
250
lua/includes/modules/numpad.lua
Normal file
250
lua/includes/modules/numpad.lua
Normal file
@@ -0,0 +1,250 @@
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
This module was primarily developed to enable toolmodes
|
||||
to share the numpad.
|
||||
|
||||
Scripted Entities can add functions to be excecuted when
|
||||
a certain key on the numpad is pressed or released.
|
||||
-----------------------------------------------------------]]
|
||||
|
||||
if ( !SERVER ) then return end
|
||||
|
||||
local tonumber = tonumber
|
||||
local pairs = pairs
|
||||
local unpack = unpack
|
||||
local table = table
|
||||
local saverestore = saverestore
|
||||
local math = math
|
||||
local IsValid = IsValid
|
||||
local type = type
|
||||
local ErrorNoHaltWithStack = ErrorNoHaltWithStack
|
||||
local KEY_NONE = KEY_NONE
|
||||
|
||||
module( "numpad" )
|
||||
|
||||
local functions = {}
|
||||
local keys_in = {}
|
||||
local keys_out = {}
|
||||
local lastindex = 1
|
||||
local button_fired = false
|
||||
|
||||
function FromButton()
|
||||
|
||||
return button_fired == true
|
||||
|
||||
end
|
||||
|
||||
local function Save( save )
|
||||
|
||||
saverestore.WriteTable( keys_in, save )
|
||||
saverestore.WriteTable( keys_out, save )
|
||||
saverestore.WriteVar( lastindex, save )
|
||||
|
||||
end
|
||||
|
||||
local function Restore( restore )
|
||||
|
||||
keys_in = saverestore.ReadTable( restore )
|
||||
keys_out = saverestore.ReadTable( restore )
|
||||
lastindex = saverestore.ReadVar( restore )
|
||||
|
||||
end
|
||||
|
||||
saverestore.AddSaveHook( "NumpadModule", Save )
|
||||
saverestore.AddRestoreHook( "NumpadModule", Restore )
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Returns a unique index based on a player.
|
||||
-----------------------------------------------------------]]
|
||||
local function GetPlayerIndex( ply )
|
||||
|
||||
if ( !IsValid( ply ) ) then return 0 end
|
||||
|
||||
return ply:SteamID64()
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Fires the impulse to the child functions
|
||||
-----------------------------------------------------------]]
|
||||
local function FireImpulse( tab, pl, idx )
|
||||
|
||||
if ( idx == nil ) then
|
||||
idx = GetPlayerIndex( pl )
|
||||
end
|
||||
|
||||
if ( !tab ) then return end
|
||||
if ( !tab[ idx ] ) then return end
|
||||
|
||||
for k, v in pairs( tab[ idx ] ) do
|
||||
|
||||
local func = functions[ v.name ]
|
||||
|
||||
local retval = func( pl, unpack( v.arg ) )
|
||||
|
||||
-- Remove hook
|
||||
if ( retval == false ) then
|
||||
tab[ idx ][ k ] = nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Console Command
|
||||
-----------------------------------------------------------]]
|
||||
function Activate( pl, num, bIsButton )
|
||||
|
||||
local key = math.Clamp( tonumber( num ), 0, 256 )
|
||||
|
||||
-- Hack. Kinda. Don't call it again until the key has been lifted.
|
||||
-- When holding down 9 or 3 on the numpad it will repeat. Ignore that.
|
||||
if ( IsValid( pl ) ) then
|
||||
pl.keystate = pl.keystate or {}
|
||||
if ( pl.keystate[ key ] ) then return end
|
||||
pl.keystate[ key ] = true
|
||||
end
|
||||
|
||||
button_fired = bIsButton
|
||||
|
||||
FireImpulse( keys_in[ key ], pl, nil )
|
||||
|
||||
-- And fire `all`
|
||||
FireImpulse( keys_in[ key ], pl, 0 )
|
||||
|
||||
button_fired = false
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Console Command
|
||||
-----------------------------------------------------------]]
|
||||
function Deactivate( pl, num, bIsButton )
|
||||
|
||||
local key = math.Clamp( tonumber( num ) , 0, 256 )
|
||||
|
||||
if ( IsValid( pl ) ) then
|
||||
pl.keystate = pl.keystate or {}
|
||||
pl.keystate[ key ] = nil
|
||||
end
|
||||
|
||||
button_fired = bIsButton
|
||||
|
||||
FireImpulse( keys_out[ key ], pl, nil )
|
||||
|
||||
-- And fire `all`
|
||||
FireImpulse( keys_out[ key ], pl, 0 )
|
||||
|
||||
button_fired = false
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Toggle
|
||||
-----------------------------------------------------------]]
|
||||
function Toggle( pl, num )
|
||||
|
||||
local key = math.Clamp( tonumber( num ), 0, 256 )
|
||||
|
||||
pl.keystate = pl.keystate or {}
|
||||
if ( pl.keystate[ key ] ) then return Deactivate( pl, num ) end
|
||||
|
||||
return Activate( pl, num )
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Adds an impulse to to the specified table
|
||||
-----------------------------------------------------------]]
|
||||
local function AddImpulse( table, ply, impulse )
|
||||
|
||||
lastindex = lastindex + 1
|
||||
|
||||
local idx = GetPlayerIndex( ply )
|
||||
table[ idx ] = table[ idx ] or {}
|
||||
table[ idx ][ lastindex ] = impulse
|
||||
|
||||
return lastindex
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Adds a function to call when ply presses key
|
||||
-----------------------------------------------------------]]
|
||||
function OnDown( ply, key, name, ... )
|
||||
|
||||
if ( !key || key ~= key ) then ErrorNoHaltWithStack( "bad argument #2 to 'numpad.OnDown' (number expected, got ", type( key ), ")" ) return end
|
||||
if ( key == KEY_NONE ) then return end
|
||||
|
||||
keys_in[ key ] = keys_in[ key ] or {}
|
||||
|
||||
local impulse = {}
|
||||
impulse.name = name
|
||||
impulse.arg = { ... }
|
||||
|
||||
table.insert( impulse.arg, GetPlayerIndex( ply ) )
|
||||
|
||||
return AddImpulse( keys_in[ key ], ply, impulse )
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Adds a function to call when ply releases key
|
||||
-----------------------------------------------------------]]
|
||||
function OnUp( ply, key, name, ... )
|
||||
|
||||
if ( !key || key ~= key ) then ErrorNoHaltWithStack( "bad argument #2 to 'numpad.OnUp' (number expected, got ", type( key ), ")" ) return end
|
||||
if ( key == KEY_NONE ) then return end
|
||||
|
||||
keys_out[ key ] = keys_out[ key ] or {}
|
||||
|
||||
local impulse = {}
|
||||
impulse.name = name
|
||||
impulse.arg = { ... }
|
||||
|
||||
table.insert( impulse.arg, GetPlayerIndex( ply ) )
|
||||
|
||||
return AddImpulse( keys_out[ key ], ply, impulse )
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Removes key from tab (by unique index)
|
||||
-----------------------------------------------------------]]
|
||||
local function RemoveFromKeyTable( tab, idx )
|
||||
|
||||
for k, v_key in pairs( tab ) do
|
||||
|
||||
for k_, v_player in pairs( v_key ) do
|
||||
|
||||
if ( v_player[ idx ] != nil ) then
|
||||
v_player[ idx ] = nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Removes key (by unique index)
|
||||
-----------------------------------------------------------]]
|
||||
function Remove( idx )
|
||||
|
||||
if ( !idx ) then return end
|
||||
|
||||
RemoveFromKeyTable( keys_out, idx )
|
||||
RemoveFromKeyTable( keys_in, idx )
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Register a function
|
||||
-----------------------------------------------------------]]
|
||||
function Register( name, func )
|
||||
|
||||
functions[ name ] = func
|
||||
|
||||
end
|
||||
457
lua/includes/modules/player_manager.lua
Normal file
457
lua/includes/modules/player_manager.lua
Normal file
@@ -0,0 +1,457 @@
|
||||
|
||||
local ErrorNoHalt = ErrorNoHalt
|
||||
local baseclass = baseclass
|
||||
local setmetatable = setmetatable
|
||||
local SERVER = SERVER
|
||||
local string = string
|
||||
local table = table
|
||||
local util = util
|
||||
|
||||
module( "player_manager" )
|
||||
|
||||
-- Stores a table of valid player models
|
||||
local ModelList = {}
|
||||
local ModelListRev = {}
|
||||
local HandNames = {}
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Utility to add models to the acceptable model list
|
||||
-----------------------------------------------------------]]
|
||||
function AddValidModel( name, model )
|
||||
|
||||
ModelList[ name ] = model
|
||||
ModelListRev[ string.lower( model ) ] = name
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- Valid hands
|
||||
--
|
||||
function AddValidHands( name, model, skin, body, matchBodySkin )
|
||||
|
||||
HandNames[ name ] = { model = model, skin = skin or 0, body = body or "0000000", matchBodySkin = matchBodySkin or false }
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Return list of all valid player models
|
||||
-----------------------------------------------------------]]
|
||||
function AllValidModels( )
|
||||
return ModelList
|
||||
end
|
||||
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Translate the simple name of a model
|
||||
into the full model name
|
||||
-----------------------------------------------------------]]
|
||||
function TranslatePlayerModel( name )
|
||||
|
||||
if ( ModelList[ name ] != nil ) then
|
||||
return ModelList[ name ]
|
||||
end
|
||||
|
||||
return "models/player/kleiner.mdl"
|
||||
end
|
||||
|
||||
-- Translate from the full model name to simple model name
|
||||
function TranslateToPlayerModelName( model )
|
||||
|
||||
model = string.lower( model )
|
||||
|
||||
if ( ModelListRev[ model ] != nil ) then
|
||||
return ModelListRev[ model ]
|
||||
end
|
||||
|
||||
return "kleiner"
|
||||
end
|
||||
|
||||
--
|
||||
-- Translate hands based on model
|
||||
--
|
||||
function TranslatePlayerHands( name )
|
||||
|
||||
if ( HandNames[ name ] != nil ) then
|
||||
return HandNames[ name ]
|
||||
end
|
||||
|
||||
return { model = "models/weapons/c_arms_citizen.mdl", skin = 0, body = "100000000" }
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Compile a list of valid player models
|
||||
-----------------------------------------------------------]]
|
||||
|
||||
AddValidModel( "alyx", "models/player/alyx.mdl" )
|
||||
AddValidHands( "alyx", "models/weapons/c_arms_citizen.mdl", 0, "0000000" )
|
||||
|
||||
AddValidModel( "barney", "models/player/barney.mdl" )
|
||||
AddValidHands( "barney", "models/weapons/c_arms_combine.mdl", 0, "0000000" )
|
||||
|
||||
AddValidModel( "breen", "models/player/breen.mdl" )
|
||||
AddValidHands( "breen", "models/weapons/c_arms_citizen.mdl", 0, "0000000" )
|
||||
|
||||
AddValidModel( "charple", "models/player/charple.mdl" )
|
||||
AddValidHands( "charple", "models/weapons/c_arms_citizen.mdl", 2, "0000000" )
|
||||
|
||||
AddValidModel( "chell", "models/player/p2_chell.mdl" )
|
||||
AddValidHands( "chell", "models/weapons/c_arms_chell.mdl", 0, "0000000" )
|
||||
|
||||
AddValidModel( "corpse", "models/player/corpse1.mdl" )
|
||||
AddValidHands( "corpse", "models/weapons/c_arms_citizen.mdl", 2, "0000000" )
|
||||
|
||||
AddValidModel( "combine", "models/player/combine_soldier.mdl" )
|
||||
AddValidHands( "combine", "models/weapons/c_arms_combine.mdl", 0, "0000000" )
|
||||
|
||||
AddValidModel( "combineprison", "models/player/combine_soldier_prisonguard.mdl" )
|
||||
AddValidHands( "combineprison", "models/weapons/c_arms_combine.mdl", 0, "0000000" )
|
||||
|
||||
AddValidModel( "combineelite", "models/player/combine_super_soldier.mdl" )
|
||||
AddValidHands( "combineelite", "models/weapons/c_arms_combine.mdl", 0, "0000000" )
|
||||
|
||||
AddValidModel( "eli", "models/player/eli.mdl" )
|
||||
AddValidHands( "eli", "models/weapons/c_arms_citizen.mdl", 1, "0000000" )
|
||||
|
||||
AddValidModel( "gman", "models/player/gman_high.mdl" )
|
||||
AddValidHands( "gman", "models/weapons/c_arms_citizen.mdl", 0, "0000000" )
|
||||
|
||||
AddValidModel( "kleiner", "models/player/kleiner.mdl" )
|
||||
AddValidHands( "kleiner", "models/weapons/c_arms_citizen.mdl", 0, "0000000" )
|
||||
|
||||
AddValidModel( "monk", "models/player/monk.mdl" )
|
||||
AddValidHands( "monk", "models/weapons/c_arms_citizen.mdl", 0, "0000000" )
|
||||
|
||||
AddValidModel( "mossman", "models/player/mossman.mdl" )
|
||||
AddValidHands( "mossman", "models/weapons/c_arms_citizen.mdl", 0, "0000000" )
|
||||
|
||||
AddValidModel( "mossmanarctic", "models/player/mossman_arctic.mdl" )
|
||||
AddValidHands( "mossmanarctic", "models/weapons/c_arms_citizen.mdl", 0, "0000000" )
|
||||
|
||||
AddValidModel( "odessa", "models/player/odessa.mdl" )
|
||||
AddValidHands( "odessa", "models/weapons/c_arms_citizen.mdl", 0, "0000000" )
|
||||
|
||||
AddValidModel( "police", "models/player/police.mdl" )
|
||||
AddValidHands( "police", "models/weapons/c_arms_combine.mdl", 0, "0000000" )
|
||||
|
||||
AddValidModel( "policefem", "models/player/police_fem.mdl" )
|
||||
AddValidHands( "policefem", "models/weapons/c_arms_combine.mdl", 0, "0000000" )
|
||||
|
||||
AddValidModel( "magnusson", "models/player/magnusson.mdl" )
|
||||
AddValidHands( "magnusson", "models/weapons/c_arms_citizen.mdl", 0, "0000000" )
|
||||
|
||||
AddValidModel( "stripped", "models/player/soldier_stripped.mdl" )
|
||||
AddValidHands( "stripped", "models/weapons/c_arms_hev.mdl", 2, "0000000" )
|
||||
|
||||
AddValidModel( "zombie", "models/player/zombie_classic.mdl" )
|
||||
AddValidHands( "zombie", "models/weapons/c_arms_citizen.mdl", 2, "0000000" )
|
||||
|
||||
AddValidModel( "zombiefast", "models/player/zombie_fast.mdl" )
|
||||
AddValidHands( "zombiefast", "models/weapons/c_arms_citizen.mdl", 2, "0000000" )
|
||||
|
||||
AddValidModel( "female01", "models/player/Group01/female_01.mdl" )
|
||||
AddValidHands( "female01", "models/weapons/c_arms_citizen.mdl", 0, "0000000" )
|
||||
AddValidModel( "female02", "models/player/Group01/female_02.mdl" )
|
||||
AddValidHands( "female02", "models/weapons/c_arms_citizen.mdl", 0, "0000000" )
|
||||
AddValidModel( "female03", "models/player/Group01/female_03.mdl" )
|
||||
AddValidHands( "female03", "models/weapons/c_arms_citizen.mdl", 1, "0000000" )
|
||||
AddValidModel( "female04", "models/player/Group01/female_04.mdl" )
|
||||
AddValidHands( "female04", "models/weapons/c_arms_citizen.mdl", 0, "0000000" )
|
||||
AddValidModel( "female05", "models/player/Group01/female_05.mdl" )
|
||||
AddValidHands( "female05", "models/weapons/c_arms_citizen.mdl", 1, "0000000" )
|
||||
AddValidModel( "female06", "models/player/Group01/female_06.mdl" )
|
||||
AddValidHands( "female06", "models/weapons/c_arms_citizen.mdl", 0, "0000000" )
|
||||
|
||||
AddValidModel( "female07", "models/player/Group03/female_01.mdl" )
|
||||
AddValidHands( "female07", "models/weapons/c_arms_refugee.mdl", 0, "0100000" )
|
||||
AddValidModel( "female08", "models/player/Group03/female_02.mdl" )
|
||||
AddValidHands( "female08", "models/weapons/c_arms_refugee.mdl", 0, "0100000" )
|
||||
AddValidModel( "female09", "models/player/Group03/female_03.mdl" )
|
||||
AddValidHands( "female09", "models/weapons/c_arms_refugee.mdl", 1, "0100000" )
|
||||
AddValidModel( "female10", "models/player/Group03/female_04.mdl" )
|
||||
AddValidHands( "female10", "models/weapons/c_arms_refugee.mdl", 0, "0100000" )
|
||||
AddValidModel( "female11", "models/player/Group03/female_05.mdl" )
|
||||
AddValidHands( "female11", "models/weapons/c_arms_refugee.mdl", 1, "0100000" )
|
||||
AddValidModel( "female12", "models/player/Group03/female_06.mdl" )
|
||||
AddValidHands( "female12", "models/weapons/c_arms_refugee.mdl", 0, "0100000" )
|
||||
|
||||
AddValidModel( "male01", "models/player/Group01/male_01.mdl" )
|
||||
AddValidHands( "male01", "models/weapons/c_arms_citizen.mdl", 1, "0000000" )
|
||||
AddValidModel( "male02", "models/player/Group01/male_02.mdl" )
|
||||
AddValidHands( "male02", "models/weapons/c_arms_citizen.mdl", 0, "0000000" )
|
||||
AddValidModel( "male03", "models/player/Group01/male_03.mdl" )
|
||||
AddValidHands( "male03", "models/weapons/c_arms_citizen.mdl", 1, "0000000" )
|
||||
AddValidModel( "male04", "models/player/Group01/male_04.mdl" )
|
||||
AddValidHands( "male04", "models/weapons/c_arms_citizen.mdl", 0, "0000000" )
|
||||
AddValidModel( "male05", "models/player/Group01/male_05.mdl" )
|
||||
AddValidHands( "male05", "models/weapons/c_arms_citizen.mdl", 0, "0000000" )
|
||||
AddValidModel( "male06", "models/player/Group01/male_06.mdl" )
|
||||
AddValidHands( "male06", "models/weapons/c_arms_citizen.mdl", 0, "0000000" )
|
||||
AddValidModel( "male07", "models/player/Group01/male_07.mdl" )
|
||||
AddValidHands( "male07", "models/weapons/c_arms_citizen.mdl", 0, "0000000" )
|
||||
AddValidModel( "male08", "models/player/Group01/male_08.mdl" )
|
||||
AddValidHands( "male08", "models/weapons/c_arms_citizen.mdl", 0, "0000000" )
|
||||
AddValidModel( "male09", "models/player/Group01/male_09.mdl" )
|
||||
AddValidHands( "male09", "models/weapons/c_arms_citizen.mdl", 0, "0000000" )
|
||||
|
||||
AddValidModel( "male10", "models/player/Group03/male_01.mdl" )
|
||||
AddValidHands( "male10", "models/weapons/c_arms_refugee.mdl", 1, "0100000" )
|
||||
AddValidModel( "male11", "models/player/Group03/male_02.mdl" )
|
||||
AddValidHands( "male11", "models/weapons/c_arms_refugee.mdl", 0, "0100000" )
|
||||
AddValidModel( "male12", "models/player/Group03/male_03.mdl" )
|
||||
AddValidHands( "male12", "models/weapons/c_arms_refugee.mdl", 1, "0100000" )
|
||||
AddValidModel( "male13", "models/player/Group03/male_04.mdl" )
|
||||
AddValidHands( "male13", "models/weapons/c_arms_refugee.mdl", 0, "0100000" )
|
||||
AddValidModel( "male14", "models/player/Group03/male_05.mdl" )
|
||||
AddValidHands( "male14", "models/weapons/c_arms_refugee.mdl", 0, "0100000" )
|
||||
AddValidModel( "male15", "models/player/Group03/male_06.mdl" )
|
||||
AddValidHands( "male15", "models/weapons/c_arms_refugee.mdl", 0, "0100000" )
|
||||
AddValidModel( "male16", "models/player/Group03/male_07.mdl" )
|
||||
AddValidHands( "male16", "models/weapons/c_arms_refugee.mdl", 0, "0100000" )
|
||||
AddValidModel( "male17", "models/player/Group03/male_08.mdl" )
|
||||
AddValidHands( "male17", "models/weapons/c_arms_refugee.mdl", 0, "0100000" )
|
||||
AddValidModel( "male18", "models/player/Group03/male_09.mdl" )
|
||||
AddValidHands( "male18", "models/weapons/c_arms_refugee.mdl", 0, "0100000" )
|
||||
|
||||
AddValidModel( "medic01", "models/player/Group03m/male_01.mdl" )
|
||||
AddValidHands( "medic01", "models/weapons/c_arms_refugee.mdl", 1, "0100000" )
|
||||
AddValidModel( "medic02", "models/player/Group03m/male_02.mdl" )
|
||||
AddValidHands( "medic02", "models/weapons/c_arms_refugee.mdl", 0, "0000000" )
|
||||
AddValidModel( "medic03", "models/player/Group03m/male_03.mdl" )
|
||||
AddValidHands( "medic03", "models/weapons/c_arms_refugee.mdl", 1, "0100000" )
|
||||
AddValidModel( "medic04", "models/player/Group03m/male_04.mdl" )
|
||||
AddValidHands( "medic04", "models/weapons/c_arms_refugee.mdl", 0, "0000000" )
|
||||
AddValidModel( "medic05", "models/player/Group03m/male_05.mdl" )
|
||||
AddValidHands( "medic05", "models/weapons/c_arms_refugee.mdl", 0, "0100000" )
|
||||
AddValidModel( "medic06", "models/player/Group03m/male_06.mdl" )
|
||||
AddValidHands( "medic06", "models/weapons/c_arms_refugee.mdl", 0, "0000000" )
|
||||
AddValidModel( "medic07", "models/player/Group03m/male_07.mdl" )
|
||||
AddValidHands( "medic07", "models/weapons/c_arms_refugee.mdl", 0, "0000000" )
|
||||
AddValidModel( "medic08", "models/player/Group03m/male_08.mdl" )
|
||||
AddValidHands( "medic08", "models/weapons/c_arms_refugee.mdl", 0, "0000000" )
|
||||
AddValidModel( "medic09", "models/player/Group03m/male_09.mdl" )
|
||||
AddValidHands( "medic09", "models/weapons/c_arms_refugee.mdl", 0, "0000000" )
|
||||
AddValidModel( "medic10", "models/player/Group03m/female_01.mdl" )
|
||||
AddValidHands( "medic10", "models/weapons/c_arms_refugee.mdl", 0, "0100000" )
|
||||
AddValidModel( "medic11", "models/player/Group03m/female_02.mdl" )
|
||||
AddValidHands( "medic11", "models/weapons/c_arms_refugee.mdl", 0, "0000000" )
|
||||
AddValidModel( "medic12", "models/player/Group03m/female_03.mdl" )
|
||||
AddValidHands( "medic12", "models/weapons/c_arms_refugee.mdl", 1, "0000000" )
|
||||
AddValidModel( "medic13", "models/player/Group03m/female_04.mdl" )
|
||||
AddValidHands( "medic13", "models/weapons/c_arms_refugee.mdl", 0, "0100000" )
|
||||
AddValidModel( "medic14", "models/player/Group03m/female_05.mdl" )
|
||||
AddValidHands( "medic14", "models/weapons/c_arms_refugee.mdl", 0, "0100000" )
|
||||
AddValidModel( "medic15", "models/player/Group03m/female_06.mdl" )
|
||||
AddValidHands( "medic15", "models/weapons/c_arms_refugee.mdl", 1, "0100000" )
|
||||
|
||||
AddValidModel( "refugee01", "models/player/Group02/male_02.mdl" )
|
||||
AddValidHands( "refugee01", "models/weapons/c_arms_citizen.mdl", 0, "0000000" )
|
||||
AddValidModel( "refugee02", "models/player/Group02/male_04.mdl" )
|
||||
AddValidHands( "refugee02", "models/weapons/c_arms_citizen.mdl", 0, "0000000" )
|
||||
AddValidModel( "refugee03", "models/player/Group02/male_06.mdl" )
|
||||
AddValidHands( "refugee03", "models/weapons/c_arms_citizen.mdl", 0, "0000000" )
|
||||
AddValidModel( "refugee04", "models/player/Group02/male_08.mdl" )
|
||||
AddValidHands( "refugee04", "models/weapons/c_arms_citizen.mdl", 0, "0000000" )
|
||||
|
||||
--
|
||||
-- Game specific player models! (EP2, CSS, DOD)
|
||||
-- Moving them to here since we're now shipping all required files / fallbacks
|
||||
|
||||
AddValidModel( "magnusson", "models/player/magnusson.mdl" )
|
||||
AddValidHands( "magnusson", "models/weapons/c_arms_citizen.mdl", 0, "0000000" )
|
||||
AddValidModel( "skeleton", "models/player/skeleton.mdl" )
|
||||
AddValidHands( "skeleton", "models/weapons/c_arms_citizen.mdl", 2, "0000000" )
|
||||
AddValidModel( "zombine", "models/player/zombie_soldier.mdl" )
|
||||
AddValidHands( "zombine", "models/weapons/c_arms_combine.mdl", 0, "0000000" )
|
||||
|
||||
AddValidModel( "hostage01", "models/player/hostage/hostage_01.mdl" )
|
||||
AddValidModel( "hostage02", "models/player/hostage/hostage_02.mdl" )
|
||||
AddValidModel( "hostage03", "models/player/hostage/hostage_03.mdl" )
|
||||
AddValidModel( "hostage04", "models/player/hostage/hostage_04.mdl" )
|
||||
|
||||
AddValidModel( "css_arctic", "models/player/arctic.mdl" )
|
||||
AddValidHands( "css_arctic", "models/weapons/c_arms_cstrike.mdl", 0, "10000000" )
|
||||
AddValidModel( "css_gasmask", "models/player/gasmask.mdl" )
|
||||
AddValidHands( "css_gasmask", "models/weapons/c_arms_cstrike.mdl", 0, "10000000" )
|
||||
AddValidModel( "css_guerilla", "models/player/guerilla.mdl" )
|
||||
AddValidHands( "css_guerilla", "models/weapons/c_arms_cstrike.mdl", 0, "10000000" )
|
||||
AddValidModel( "css_leet", "models/player/leet.mdl" )
|
||||
AddValidHands( "css_leet", "models/weapons/c_arms_cstrike.mdl", 0, "10000000" )
|
||||
AddValidModel( "css_phoenix", "models/player/phoenix.mdl" )
|
||||
AddValidHands( "css_phoenix", "models/weapons/c_arms_cstrike.mdl", 0, "10000000" )
|
||||
AddValidModel( "css_riot", "models/player/riot.mdl" )
|
||||
AddValidHands( "css_riot", "models/weapons/c_arms_cstrike.mdl", 0, "10000000" )
|
||||
AddValidModel( "css_swat", "models/player/swat.mdl" )
|
||||
AddValidHands( "css_swat", "models/weapons/c_arms_cstrike.mdl", 0, "10000000" )
|
||||
AddValidModel( "css_urban", "models/player/urban.mdl" )
|
||||
AddValidHands( "css_urban", "models/weapons/c_arms_cstrike.mdl", 7, "10000000" )
|
||||
|
||||
AddValidModel( "dod_german", "models/player/dod_german.mdl" )
|
||||
AddValidHands( "dod_german", "models/weapons/c_arms_dod.mdl", 0, "10000000" )
|
||||
AddValidModel( "dod_american", "models/player/dod_american.mdl" )
|
||||
AddValidHands( "dod_american", "models/weapons/c_arms_dod.mdl", 1, "10000000" )
|
||||
|
||||
|
||||
--
|
||||
-- Player Class Stuff
|
||||
--
|
||||
|
||||
local Type = {}
|
||||
|
||||
function GetPlayerClasses()
|
||||
|
||||
return table.Copy( Type )
|
||||
|
||||
end
|
||||
|
||||
local function LookupPlayerClass( ply )
|
||||
|
||||
local id = ply:GetClassID()
|
||||
if ( id == 0 ) then return end
|
||||
|
||||
--
|
||||
-- Check the cache
|
||||
--
|
||||
local plyClass = ply.m_CurrentPlayerClass
|
||||
if ( plyClass && plyClass.Player == ply ) then
|
||||
if ( plyClass.ClassID == id && plyClass.Func ) then return plyClass end -- current class is still good, behave normally
|
||||
if ( plyClass.ClassChanged ) then plyClass:ClassChanged() end -- the class id changed, remove the old class
|
||||
end
|
||||
|
||||
--
|
||||
-- No class, lets create one
|
||||
--
|
||||
local classname = util.NetworkIDToString( id )
|
||||
if ( !classname ) then return end
|
||||
|
||||
--
|
||||
-- Get that type. Fail if we don't have the type.
|
||||
--
|
||||
local t = Type[ classname ]
|
||||
if ( !t ) then return end
|
||||
|
||||
local newClass = {
|
||||
Player = ply,
|
||||
ClassID = id,
|
||||
Func = function() end
|
||||
}
|
||||
|
||||
setmetatable( newClass, { __index = t } )
|
||||
|
||||
ply.m_CurrentPlayerClass = newClass
|
||||
|
||||
-- TODO: We probably want to reset previous DTVar stuff on the player
|
||||
newClass.Player:InstallDataTable()
|
||||
newClass:SetupDataTables()
|
||||
newClass:Init()
|
||||
return newClass
|
||||
|
||||
end
|
||||
|
||||
function RegisterClass( name, tab, base )
|
||||
|
||||
Type[ name ] = tab
|
||||
|
||||
--
|
||||
-- If we have a base method then hook
|
||||
-- it up in the meta table
|
||||
--
|
||||
if ( base ) then
|
||||
|
||||
if ( !Type[ name ] ) then ErrorNoHalt( "RegisterClass - deriving " .. name .. " from unknown class " .. base .. "!\n" ) end
|
||||
setmetatable( Type[ name ], { __index = Type[ base ] } )
|
||||
|
||||
end
|
||||
|
||||
if ( SERVER ) then
|
||||
util.AddNetworkString( name )
|
||||
end
|
||||
|
||||
--
|
||||
-- drive methods cooperate with the baseclass system
|
||||
-- /lua/includes/modules/baseclass.lua
|
||||
--
|
||||
baseclass.Set( name, Type[ name ] )
|
||||
|
||||
end
|
||||
|
||||
function SetPlayerClass( ply, classname )
|
||||
|
||||
if ( !Type[ classname ] ) then ErrorNoHalt( "SetPlayerClass - attempt to use unknown player class " .. classname .. "!\n" ) end
|
||||
|
||||
local id = util.NetworkStringToID( classname )
|
||||
ply:SetClassID( id )
|
||||
|
||||
-- Initialize the player class so the datatable and everything is set up
|
||||
-- This probably could be done better
|
||||
LookupPlayerClass( ply )
|
||||
|
||||
end
|
||||
|
||||
function GetPlayerClass( ply )
|
||||
|
||||
local id = ply:GetClassID()
|
||||
if ( id == 0 ) then return end
|
||||
|
||||
return util.NetworkIDToString( id )
|
||||
|
||||
end
|
||||
|
||||
function GetPlayerClassTable( ply )
|
||||
|
||||
local id = ply:GetClassID()
|
||||
if ( id == 0 ) then return end
|
||||
|
||||
local ct = Type[ util.NetworkIDToString( id ) ]
|
||||
if ( !ct ) then return end
|
||||
|
||||
return table.Copy( ct )
|
||||
|
||||
end
|
||||
|
||||
function ClearPlayerClass( ply )
|
||||
|
||||
ply:SetClassID( 0 )
|
||||
|
||||
end
|
||||
|
||||
function RunClass( ply, funcname, ... )
|
||||
|
||||
local class = LookupPlayerClass( ply )
|
||||
if ( !class ) then return end
|
||||
|
||||
local func = class[ funcname ]
|
||||
if ( !func ) then ErrorNoHalt( "Function " .. funcname .. " not found on player class!\n" ) return end
|
||||
|
||||
return func( class, ... )
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- Should be called on spawn automatically to set the variables below
|
||||
-- This is called in the base gamemode :PlayerSpawn function
|
||||
--
|
||||
function OnPlayerSpawn( ply, transiton )
|
||||
|
||||
local class = LookupPlayerClass( ply )
|
||||
if ( !class ) then return end
|
||||
|
||||
ply:SetSlowWalkSpeed( class.SlowWalkSpeed )
|
||||
ply:SetWalkSpeed( class.WalkSpeed )
|
||||
ply:SetRunSpeed( class.RunSpeed )
|
||||
ply:SetCrouchedWalkSpeed( class.CrouchedWalkSpeed )
|
||||
ply:SetDuckSpeed( class.DuckSpeed )
|
||||
ply:SetUnDuckSpeed( class.UnDuckSpeed )
|
||||
ply:SetJumpPower( class.JumpPower )
|
||||
ply:AllowFlashlight( class.CanUseFlashlight )
|
||||
ply:ShouldDropWeapon( class.DropWeaponOnDie )
|
||||
ply:SetNoCollideWithTeammates( class.TeammateNoCollide )
|
||||
ply:SetAvoidPlayers( class.AvoidPlayers )
|
||||
|
||||
if ( !transiton ) then
|
||||
ply:SetMaxHealth( class.MaxHealth )
|
||||
ply:SetMaxArmor( class.MaxArmor )
|
||||
ply:SetHealth( class.StartHealth )
|
||||
ply:SetArmor( class.StartArmor )
|
||||
end
|
||||
|
||||
end
|
||||
91
lua/includes/modules/presets.lua
Normal file
91
lua/includes/modules/presets.lua
Normal file
@@ -0,0 +1,91 @@
|
||||
|
||||
module( "presets", package.seeall )
|
||||
|
||||
-- TODO: A function to check/replace invalid characters for filenames!
|
||||
|
||||
local Presets = LoadPresets()
|
||||
|
||||
function GetTable( presetname )
|
||||
|
||||
if ( !presetname ) then return end
|
||||
presetname = presetname:Trim()
|
||||
if ( presetname == "" ) then return end
|
||||
|
||||
Presets[ presetname ] = Presets[ presetname ] or {}
|
||||
|
||||
return Presets[ presetname ]
|
||||
|
||||
end
|
||||
|
||||
function Exists( presetname, strName )
|
||||
|
||||
if ( !presetname || !strName ) then return false end
|
||||
presetname = presetname:Trim()
|
||||
strName = strName:Trim()
|
||||
if ( presetname == "" || strName == "" ) then return false end
|
||||
|
||||
if ( !Presets[ presetname ] || !Presets[ presetname ][ strName ] ) then return false end
|
||||
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
function Add( presetname, strName, pTable )
|
||||
|
||||
if ( !presetname || !strName ) then return end
|
||||
presetname = presetname:Trim()
|
||||
strName = strName:Trim()
|
||||
if ( presetname == "" || strName == "" ) then return end
|
||||
|
||||
Presets[ presetname ] = Presets[ presetname ] or {}
|
||||
Presets[ presetname ][ strName ] = pTable
|
||||
|
||||
-- Only save the specific preset group, not ALL of them
|
||||
SavePresets( { [ presetname ] = Presets[ presetname ] } )
|
||||
|
||||
end
|
||||
|
||||
function Rename( presetname, strName, strToName )
|
||||
|
||||
if ( !presetname || !strName || !strToName || strName == strToName ) then return end
|
||||
presetname = presetname:Trim()
|
||||
strName = strName:Trim()
|
||||
strToName = strToName:Trim()
|
||||
if ( presetname == "" || strName == "" || strToName == "" || strName == strToName ) then return end
|
||||
|
||||
Presets[ presetname ] = Presets[ presetname ] or {}
|
||||
Presets[ presetname ][ strToName ] = Presets[ presetname ][ strName ]
|
||||
Presets[ presetname ][ strName ] = nil
|
||||
|
||||
-- Only save the specific preset group, not ALL of them
|
||||
SavePresets( { [ presetname ] = Presets[ presetname ] } )
|
||||
|
||||
end
|
||||
|
||||
function Remove( presetname, strName )
|
||||
|
||||
if ( !presetname || !strName ) then return end
|
||||
presetname = presetname:Trim()
|
||||
strName = strName:Trim()
|
||||
if ( presetname == "" || strName == "" ) then return end
|
||||
|
||||
Presets[ presetname ] = Presets[ presetname ] or {}
|
||||
Presets[ presetname ][ strName ] = nil
|
||||
|
||||
-- Only save the specific preset group, not ALL of them
|
||||
SavePresets( { [ presetname ] = Presets[ presetname ] } )
|
||||
|
||||
end
|
||||
|
||||
-- Internal helper functions to not copypaste same code
|
||||
function BadNameAlert()
|
||||
|
||||
Derma_Message( "#preset.badname_desc", "#preset.badname_title", "#preset.okay" )
|
||||
|
||||
end
|
||||
|
||||
function OverwritePresetPrompt( func )
|
||||
|
||||
Derma_Query( "#preset.exists_desc", "#preset.exists_title", "#preset.overwrite", func, "#preset.cancel" )
|
||||
|
||||
end
|
||||
254
lua/includes/modules/properties.lua
Normal file
254
lua/includes/modules/properties.lua
Normal file
@@ -0,0 +1,254 @@
|
||||
|
||||
module( "properties", package.seeall )
|
||||
|
||||
local meta = {
|
||||
MsgStart = function( self )
|
||||
|
||||
net.Start( "properties" )
|
||||
net.WriteString( self.InternalName )
|
||||
|
||||
end,
|
||||
|
||||
MsgEnd = function( self )
|
||||
|
||||
net.SendToServer()
|
||||
|
||||
end
|
||||
}
|
||||
|
||||
meta.__index = meta
|
||||
|
||||
List = {}
|
||||
-- .MenuLabel [string] Label to show on opened menu
|
||||
-- .Filter( ent ) [function] Return true if we should show a menu for this entity
|
||||
-- .Action( ent ) [function] On menu choice selected
|
||||
-- .Order [int] The order in which it should be shown on the menu
|
||||
-- .Receive( len, ply ) [function] A message has been received from the clientside version
|
||||
|
||||
function Add( name, tab )
|
||||
|
||||
name = name:lower()
|
||||
tab.InternalName = name
|
||||
setmetatable( tab, meta )
|
||||
|
||||
List[ name ] = tab
|
||||
|
||||
end
|
||||
|
||||
function Remove( name )
|
||||
|
||||
if ( !name || name == "" ) then return end
|
||||
|
||||
name = name:lower()
|
||||
|
||||
if ( !List[ name ] ) then return end
|
||||
|
||||
List[ name ] = nil
|
||||
|
||||
end
|
||||
|
||||
local function AddToggleOption( data, menu, ent, ply, tr )
|
||||
|
||||
if ( !menu.ToggleSpacer ) then
|
||||
menu.ToggleSpacer = menu:AddSpacer()
|
||||
menu.ToggleSpacer:SetZPos( 500 )
|
||||
end
|
||||
|
||||
local option = menu:AddOption( data.MenuLabel, function() data:Action( ent, tr ) end )
|
||||
option:SetChecked( data:Checked( ent, ply ) )
|
||||
option:SetZPos( 501 )
|
||||
return option
|
||||
|
||||
end
|
||||
|
||||
local function AddOption( data, menu, ent, ply, tr )
|
||||
|
||||
if ( data.Type == "toggle" ) then return AddToggleOption( data, menu, ent, ply, tr ) end
|
||||
|
||||
if ( data.PrependSpacer ) then
|
||||
menu:AddSpacer()
|
||||
end
|
||||
|
||||
local option = menu:AddOption( data.MenuLabel, function() data:Action( ent, tr ) end )
|
||||
|
||||
if ( data.MenuIcon ) then
|
||||
option:SetImage( data.MenuIcon )
|
||||
end
|
||||
|
||||
if ( data.MenuOpen ) then
|
||||
data.MenuOpen( data, option, ent, tr )
|
||||
end
|
||||
|
||||
return option
|
||||
|
||||
end
|
||||
|
||||
function OpenEntityMenu( ent, tr )
|
||||
|
||||
local menu = DermaMenu()
|
||||
|
||||
for k, v in SortedPairsByMemberValue( List, "Order" ) do
|
||||
|
||||
if ( !v.Filter ) then continue end
|
||||
if ( !v:Filter( ent, LocalPlayer() ) ) then continue end
|
||||
|
||||
local option = AddOption( v, menu, ent, LocalPlayer(), tr )
|
||||
|
||||
if ( v.OnCreate ) then v:OnCreate( menu, option ) end
|
||||
|
||||
end
|
||||
|
||||
menu:Open()
|
||||
|
||||
end
|
||||
|
||||
function OnScreenClick( eyepos, eyevec )
|
||||
|
||||
local ent, tr = GetHovered( eyepos, eyevec )
|
||||
if ( !IsValid( ent ) ) then return end
|
||||
|
||||
OpenEntityMenu( ent, tr )
|
||||
|
||||
end
|
||||
|
||||
-- Use this check in your properties to see if given entity can be affected by it
|
||||
-- Ideally this should be done automatically for you, but due to how this system was set up, its now impossible
|
||||
function CanBeTargeted( ent, ply )
|
||||
if ( !IsValid( ent ) ) then return false end
|
||||
if ( ent:IsPlayer() ) then return false end
|
||||
|
||||
-- Check the range if player object is given
|
||||
-- This is not perfect, but it is close enough and its definitely better than nothing
|
||||
if ( IsValid( ply ) ) then
|
||||
local mins = ent:OBBMins()
|
||||
local maxs = ent:OBBMaxs()
|
||||
local maxRange = math.max( math.abs( mins.x ) + maxs.x, math.abs( mins.y ) + maxs.y, math.abs( mins.z ) + maxs.z )
|
||||
local pos = ent:LocalToWorld( ent:OBBCenter() ) --ent:GetPos()
|
||||
|
||||
if ( pos:Distance( ply:GetShootPos() ) > maxRange + 1024 ) then return false end
|
||||
end
|
||||
|
||||
return !( ent:GetPhysicsObjectCount() < 1 && ent:GetSolid() == SOLID_NONE && bit.band( ent:GetSolidFlags(), FSOLID_USE_TRIGGER_BOUNDS ) == 0 && bit.band( ent:GetSolidFlags(), FSOLID_CUSTOMRAYTEST ) == 0 )
|
||||
end
|
||||
|
||||
function GetHovered( eyepos, eyevec )
|
||||
|
||||
local ply = LocalPlayer()
|
||||
local filter = ply:GetViewEntity()
|
||||
|
||||
if ( filter == ply ) then
|
||||
local veh = ply:GetVehicle()
|
||||
|
||||
if ( veh:IsValid() && ( !veh:IsVehicle() || !veh:GetThirdPersonMode() ) ) then
|
||||
-- A dirty hack for prop_vehicle_crane. util.TraceLine returns the vehicle but it hits phys_bone_follower - something that needs looking into
|
||||
filter = { filter, veh, unpack( ents.FindByClass( "phys_bone_follower" ) ) }
|
||||
end
|
||||
end
|
||||
|
||||
local trace = util.TraceLine( {
|
||||
start = eyepos,
|
||||
endpos = eyepos + eyevec * 1024,
|
||||
filter = filter
|
||||
} )
|
||||
|
||||
-- Hit COLLISION_GROUP_DEBRIS and stuff
|
||||
if ( !trace.Hit || !IsValid( trace.Entity ) ) then
|
||||
trace = util.TraceLine( {
|
||||
start = eyepos,
|
||||
endpos = eyepos + eyevec * 1024,
|
||||
filter = filter,
|
||||
mask = MASK_ALL
|
||||
} )
|
||||
end
|
||||
|
||||
if ( !trace.Hit || !IsValid( trace.Entity ) ) then return end
|
||||
|
||||
return trace.Entity, trace
|
||||
|
||||
end
|
||||
|
||||
-- Receives commands from clients
|
||||
if ( SERVER ) then
|
||||
|
||||
util.AddNetworkString( "properties" )
|
||||
|
||||
net.Receive( "properties", function( len, client )
|
||||
if ( !IsValid( client ) ) then return end
|
||||
|
||||
local name = net.ReadString()
|
||||
if ( !name ) then return end
|
||||
|
||||
local prop = List[ name ]
|
||||
if ( !prop ) then return end
|
||||
if ( !prop.Receive ) then return end
|
||||
|
||||
prop:Receive( len, client )
|
||||
|
||||
end )
|
||||
|
||||
end
|
||||
|
||||
if ( CLIENT ) then
|
||||
|
||||
hook.Add( "PreDrawHalos", "PropertiesHover", function()
|
||||
|
||||
if ( !IsValid( vgui.GetHoveredPanel() ) || !vgui.GetHoveredPanel():IsWorldClicker() ) then return end
|
||||
|
||||
-- TODO: Remove this hack when every client has MainEyePos
|
||||
local ent = GetHovered( MainEyePos and MainEyePos() or EyePos(), LocalPlayer():GetAimVector() )
|
||||
if ( !IsValid( ent ) ) then return end
|
||||
|
||||
local c = Color( 255, 255, 255, 255 )
|
||||
c.r = 200 + math.sin( RealTime() * 50 ) * 55
|
||||
c.g = 200 + math.sin( RealTime() * 20 ) * 55
|
||||
c.b = 200 + math.cos( RealTime() * 60 ) * 55
|
||||
|
||||
local t = { ent }
|
||||
if ( ent.GetActiveWeapon && IsValid( ent:GetActiveWeapon() ) ) then table.insert( t, ent:GetActiveWeapon() ) end
|
||||
halo.Add( t, c, 2, 2, 2, true, false )
|
||||
|
||||
end )
|
||||
|
||||
hook.Add( "GUIMousePressed", "PropertiesClick", function( code, vector )
|
||||
|
||||
if ( !IsValid( vgui.GetHoveredPanel() ) || !vgui.GetHoveredPanel():IsWorldClicker() ) then return end
|
||||
|
||||
if ( code == MOUSE_RIGHT && !input.IsButtonDown( MOUSE_LEFT ) ) then
|
||||
OnScreenClick( MainEyePos and MainEyePos() or EyePos(), vector )
|
||||
end
|
||||
|
||||
end )
|
||||
|
||||
local wasPressed = false
|
||||
hook.Add( "PreventScreenClicks", "PropertiesPreventClicks", function()
|
||||
|
||||
if ( !input.IsButtonDown( MOUSE_RIGHT ) ) then wasPressed = false end
|
||||
|
||||
if ( wasPressed && input.IsButtonDown( MOUSE_RIGHT ) && !input.IsButtonDown( MOUSE_LEFT ) ) then return true end
|
||||
|
||||
if ( !IsValid( vgui.GetHoveredPanel() ) || !vgui.GetHoveredPanel():IsWorldClicker() ) then return end
|
||||
|
||||
local ply = LocalPlayer()
|
||||
if ( !IsValid( ply ) ) then return end
|
||||
|
||||
--
|
||||
-- Are we pressing the right mouse button?
|
||||
-- (We check whether we're pressing the left too, to allow for physgun freezes)
|
||||
--
|
||||
if ( input.IsButtonDown( MOUSE_RIGHT ) && !input.IsButtonDown( MOUSE_LEFT ) ) then
|
||||
|
||||
--
|
||||
-- Are we hovering an entity? If so, then stomp the action
|
||||
--
|
||||
local hovered = GetHovered( MainEyePos and MainEyePos() or EyePos(), ply:GetAimVector() )
|
||||
|
||||
if ( IsValid( hovered ) ) then
|
||||
wasPressed = true
|
||||
return true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end )
|
||||
|
||||
end
|
||||
428
lua/includes/modules/saverestore.lua
Normal file
428
lua/includes/modules/saverestore.lua
Normal file
@@ -0,0 +1,428 @@
|
||||
|
||||
local Msg = Msg
|
||||
local type = type
|
||||
local pairs = pairs
|
||||
local gmod = gmod
|
||||
local table = table
|
||||
|
||||
--[[
|
||||
|
||||
This module is to help bind the engine's saverestore stuff to Lua.
|
||||
|
||||
This is so we can save Lua stuff in the save game file. The entities
|
||||
should automatically save most of their table contents to the save file.
|
||||
|
||||
!!Warning!!: Editing this file may render old saves useless.
|
||||
|
||||
You can hook into this system like so
|
||||
|
||||
local function MySaveFunction( save )
|
||||
saverestore.WriteTable( my_table, save )
|
||||
end
|
||||
|
||||
local function MyRestoreFunction( restore )
|
||||
my_table = saverestore.ReadTable( restore )
|
||||
end
|
||||
|
||||
saverestore.AddSaveHook( "HookNameHere", MySaveFunction )
|
||||
saverestore.AddRestoreHook( "HookNameHere", MyRestoreFunction )
|
||||
|
||||
--]]
|
||||
|
||||
module( "saverestore" )
|
||||
|
||||
local TYPE_NONE = 0
|
||||
local TYPE_FLOAT = 1 -- Treat all numbers as floats (they're all the same to Lua)
|
||||
local TYPE_STRING = 2
|
||||
local TYPE_ENTITY = 3
|
||||
local TYPE_VECTOR = 4
|
||||
local TYPE_TABLE = 5
|
||||
local TYPE_BOOL = 6
|
||||
local TYPE_ANGLE = 7
|
||||
|
||||
local SaveHooks = {}
|
||||
local RestoreHooks = {}
|
||||
local TableRefs = {}
|
||||
local TableRef = 1
|
||||
|
||||
function PreSave()
|
||||
|
||||
TableRefs = {}
|
||||
TableRef = 1
|
||||
|
||||
end
|
||||
|
||||
function PreRestore()
|
||||
|
||||
TableRefs = {}
|
||||
TableRef = 1
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: GetTypeStr
|
||||
Desc: Given a string returns a TYPE_
|
||||
-----------------------------------------------------------]]
|
||||
local function GetTypeStr( name )
|
||||
|
||||
if ( name == "function" ) then return TYPE_NONE end
|
||||
|
||||
if ( name == "number" ) then return TYPE_FLOAT end
|
||||
if ( name == "string" ) then return TYPE_STRING end
|
||||
if ( name == "Entity" ) then return TYPE_ENTITY end
|
||||
if ( name == "Vehicle" ) then return TYPE_ENTITY end
|
||||
if ( name == "NPC" ) then return TYPE_ENTITY end
|
||||
if ( name == "Player" ) then return TYPE_ENTITY end
|
||||
if ( name == "Weapon" ) then return TYPE_ENTITY end
|
||||
if ( name == "Vector" ) then return TYPE_VECTOR end
|
||||
if ( name == "Angle" ) then return TYPE_ANGLE end
|
||||
if ( name == "table" ) then return TYPE_TABLE end
|
||||
if ( name == "boolean" ) then return TYPE_BOOL end
|
||||
|
||||
-- These aren't saved
|
||||
if ( name == "ConVar" ) then return TYPE_NONE end
|
||||
if ( name == "PhysObj" ) then return TYPE_NONE end
|
||||
|
||||
-- Bitch about it incase I've forgot to hook a savable type up
|
||||
Msg( "Can't save or load unknown type " .. name .. "\n" )
|
||||
return TYPE_NONE
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: GetType
|
||||
Desc: Given a variable returns a TYPE_
|
||||
-----------------------------------------------------------]]
|
||||
local function GetType( var )
|
||||
|
||||
return GetTypeStr( type(var) )
|
||||
|
||||
end
|
||||
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: IsWritable
|
||||
Desc: Returns true if we can save the K/V pair
|
||||
-----------------------------------------------------------]]
|
||||
local function IsWritable( k, v )
|
||||
|
||||
local itype = GetType( k )
|
||||
if ( itype == TYPE_NONE ) then return false end
|
||||
if ( itype == TYPE_STRING && k == "SR_Recursion" ) then return false end
|
||||
|
||||
local itype = GetType( v )
|
||||
if ( itype == TYPE_NONE ) then return false end
|
||||
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: WriteVar
|
||||
Desc: Writes a single variable to the save
|
||||
-----------------------------------------------------------]]
|
||||
function WritableKeysInTable( t )
|
||||
|
||||
local i = 0
|
||||
|
||||
for k, v in pairs( t ) do
|
||||
if ( IsWritable( k, v ) ) then
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
return i
|
||||
|
||||
end
|
||||
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: WriteVar
|
||||
Desc: Writes a single variable to the save
|
||||
-----------------------------------------------------------]]
|
||||
function WriteVar( var, save )
|
||||
|
||||
local itype = GetType( var )
|
||||
if ( itype == TYPE_NONE ) then return end
|
||||
|
||||
save:StartBlock( type( var ) )
|
||||
|
||||
if ( itype == TYPE_FLOAT ) then
|
||||
save:WriteFloat( var )
|
||||
elseif ( itype == TYPE_BOOL ) then
|
||||
save:WriteBool( var )
|
||||
elseif ( itype == TYPE_STRING ) then
|
||||
save:WriteString( var )
|
||||
elseif ( itype == TYPE_ENTITY ) then
|
||||
save:WriteEntity( var )
|
||||
elseif ( itype == TYPE_ANGLE ) then
|
||||
save:WriteAngle( var )
|
||||
elseif ( itype == TYPE_VECTOR ) then
|
||||
save:WriteVector( var )
|
||||
elseif ( itype == TYPE_TABLE ) then
|
||||
WriteTable( var, save )
|
||||
else
|
||||
Msg( "Error! Saving unsupported Type: " .. type( var ) .. "\n" )
|
||||
end
|
||||
|
||||
save:EndBlock()
|
||||
|
||||
end
|
||||
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: ReadVar
|
||||
Desc: Reads a single variable
|
||||
-----------------------------------------------------------]]
|
||||
function ReadVar( restore )
|
||||
|
||||
local retval = 0
|
||||
local typename = restore:StartBlock()
|
||||
|
||||
local itype = GetTypeStr( typename )
|
||||
|
||||
if ( itype == TYPE_FLOAT ) then
|
||||
retval = restore:ReadFloat()
|
||||
elseif ( itype == TYPE_BOOL ) then
|
||||
retval = restore:ReadBool()
|
||||
elseif ( itype == TYPE_STRING ) then
|
||||
retval = restore:ReadString()
|
||||
elseif ( itype == TYPE_ENTITY ) then
|
||||
retval = restore:ReadEntity()
|
||||
elseif ( itype == TYPE_ANGLE ) then
|
||||
retval = restore:ReadAngle()
|
||||
elseif ( itype == TYPE_VECTOR ) then
|
||||
retval = restore:ReadVector()
|
||||
elseif ( itype == TYPE_TABLE ) then
|
||||
retval = ReadTable( restore )
|
||||
else
|
||||
Msg( "Error! Loading unsupported Type: " .. typename .. "\n" )
|
||||
end
|
||||
|
||||
restore:EndBlock()
|
||||
|
||||
return retval
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: WriteTable
|
||||
Desc: Writes a table to the save
|
||||
-----------------------------------------------------------]]
|
||||
function WriteTable( tab, save )
|
||||
|
||||
-- Write a blank table (because read will be expecting one)
|
||||
if ( tab == nil ) then
|
||||
|
||||
save:StartBlock( "Table" )
|
||||
save:EndBlock()
|
||||
|
||||
end
|
||||
|
||||
-- We have already saved this table
|
||||
if ( TableRefs[ tab ] ) then
|
||||
|
||||
save:StartBlock( "TableRef" )
|
||||
save:WriteInt( TableRefs[ tab ] )
|
||||
save:EndBlock()
|
||||
|
||||
return
|
||||
|
||||
end
|
||||
|
||||
TableRefs[ tab ] = TableRef
|
||||
|
||||
save:StartBlock( "Table" )
|
||||
|
||||
local iCount = WritableKeysInTable( tab )
|
||||
|
||||
save:WriteInt( TableRef )
|
||||
TableRef = TableRef + 1
|
||||
|
||||
save:WriteInt( iCount )
|
||||
|
||||
for k, v in pairs( tab ) do
|
||||
|
||||
if ( IsWritable( k, v ) ) then
|
||||
|
||||
WriteVar( k, save )
|
||||
WriteVar( v, save )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
save:EndBlock()
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: ReadTable
|
||||
Desc: Assumes a table is waiting to be read, returns a table
|
||||
-----------------------------------------------------------]]
|
||||
function ReadTable( restore )
|
||||
|
||||
local name = restore:StartBlock()
|
||||
local tab = {}
|
||||
|
||||
if ( name == "TableRef" ) then
|
||||
|
||||
local ref = restore:ReadInt()
|
||||
if ( !TableRefs[ ref ] ) then
|
||||
TableRefs[ ref ] = {}
|
||||
restore:EndBlock()
|
||||
ErrorNoHaltWithStack( "Failed to read SaveRestore table!" )
|
||||
return
|
||||
end
|
||||
|
||||
tab = TableRefs[ ref ]
|
||||
|
||||
else
|
||||
|
||||
local iRef = restore:ReadInt()
|
||||
local iCount = restore:ReadInt()
|
||||
|
||||
if ( TableRefs[ iRef ] ) then
|
||||
tab = TableRefs[ iRef ]
|
||||
end
|
||||
|
||||
for i = 0, iCount - 1 do
|
||||
|
||||
local k = ReadVar( restore )
|
||||
local v = ReadVar( restore )
|
||||
tab[ k ] = v
|
||||
|
||||
end
|
||||
|
||||
TableRefs[ iRef ] = tab
|
||||
|
||||
end
|
||||
|
||||
restore:EndBlock()
|
||||
|
||||
return tab
|
||||
|
||||
end
|
||||
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: SaveEntity
|
||||
Desc: Called by the engine for each entity
|
||||
-----------------------------------------------------------]]
|
||||
function SaveEntity( ent, save )
|
||||
|
||||
save:StartBlock( "EntityTable" )
|
||||
|
||||
WriteTable( ent:GetTable(), save )
|
||||
|
||||
save:EndBlock()
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: LoadEntity
|
||||
Desc: Called by the engine for each entity
|
||||
-----------------------------------------------------------]]
|
||||
function LoadEntity( ent, restore )
|
||||
|
||||
local EntTable = ent:GetTable()
|
||||
|
||||
local name = restore:StartBlock()
|
||||
|
||||
if ( name == "EntityTable" ) then
|
||||
|
||||
table.Merge( EntTable, ReadTable( restore ) )
|
||||
|
||||
end
|
||||
|
||||
restore:EndBlock()
|
||||
|
||||
ent:SetTable( EntTable )
|
||||
|
||||
end
|
||||
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: AddSaveHook
|
||||
Desc: Adds a hook enabling something to save something..
|
||||
-----------------------------------------------------------]]
|
||||
function AddSaveHook( name, func )
|
||||
|
||||
local h = {}
|
||||
h.Name = name
|
||||
h.Func = func
|
||||
SaveHooks[ name ] = h
|
||||
|
||||
end
|
||||
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: AddRestoreHook
|
||||
Desc: Adds a hook enabling something to restore something..
|
||||
-----------------------------------------------------------]]
|
||||
function AddRestoreHook( name, func )
|
||||
|
||||
local h = {}
|
||||
h.Name = name
|
||||
h.Func = func
|
||||
RestoreHooks[ name ] = h
|
||||
|
||||
end
|
||||
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: SaveGlobal
|
||||
Desc: Should save any Lua stuff that isn't entity based.
|
||||
-----------------------------------------------------------]]
|
||||
function SaveGlobal( save )
|
||||
|
||||
save:StartBlock( "GameMode" )
|
||||
WriteTable( gmod.GetGamemode(), save )
|
||||
save:EndBlock()
|
||||
|
||||
for k, v in pairs( SaveHooks ) do
|
||||
|
||||
save:StartBlock( v.Name )
|
||||
|
||||
v.Func( save )
|
||||
|
||||
save:EndBlock()
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: LoadGlobal
|
||||
Desc: ...
|
||||
-----------------------------------------------------------]]
|
||||
function LoadGlobal( restore )
|
||||
|
||||
local name = restore:StartBlock()
|
||||
|
||||
if ( name == "GameMode" ) then
|
||||
table.Merge( gmod.GetGamemode(), ReadTable( restore ) )
|
||||
end
|
||||
|
||||
restore:EndBlock()
|
||||
|
||||
|
||||
while ( name != "EndGlobal" ) do
|
||||
|
||||
name = restore:StartBlock()
|
||||
|
||||
local tab = RestoreHooks[ name ]
|
||||
if ( tab ) then
|
||||
|
||||
tab.Func( restore )
|
||||
|
||||
end
|
||||
|
||||
restore:EndBlock()
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
278
lua/includes/modules/scripted_ents.lua
Normal file
278
lua/includes/modules/scripted_ents.lua
Normal file
@@ -0,0 +1,278 @@
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: scripted_ents
|
||||
Desc: Scripted Entity factory
|
||||
-----------------------------------------------------------]]
|
||||
|
||||
module( "scripted_ents", package.seeall )
|
||||
|
||||
local Aliases = {}
|
||||
local SEntList = {}
|
||||
|
||||
local BaseClasses = {}
|
||||
BaseClasses[ "anim" ] = "base_anim"
|
||||
BaseClasses[ "point" ] = "base_point"
|
||||
BaseClasses[ "brush" ] = "base_brush"
|
||||
BaseClasses[ "filter" ] = "base_filter"
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: TableInherit( t, base )
|
||||
Desc: Copies any missing data from base to t
|
||||
-----------------------------------------------------------]]
|
||||
local function TableInherit( t, base )
|
||||
|
||||
for k, v in pairs( base ) do
|
||||
|
||||
if ( t[ k ] == nil ) then
|
||||
t[ k ] = v
|
||||
elseif ( k != "BaseClass" && istable( t[ k ] ) && istable( v ) ) then
|
||||
TableInherit( t[ k ], v )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
t[ "BaseClass" ] = base
|
||||
|
||||
return t
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: IsBasedOn( name, base )
|
||||
Desc: Checks if name is based on base
|
||||
-----------------------------------------------------------]]
|
||||
function IsBasedOn( name, base )
|
||||
local t = GetStored( name )
|
||||
if not t then return false end
|
||||
if t.Base == name then return false end
|
||||
|
||||
if t.Base == base then return true end
|
||||
return IsBasedOn( t.Base, base )
|
||||
end
|
||||
|
||||
function Register( t, name )
|
||||
|
||||
if ( hook.Run( "PreRegisterSENT", t, name ) == false ) then return end
|
||||
|
||||
if ( isstring( t.ClassNameOverride ) ) then name = t.ClassNameOverride end
|
||||
|
||||
local Base = t.Base
|
||||
if ( !Base ) then Base = BaseClasses[ t.Type ] end
|
||||
|
||||
local old = SEntList[ name ]
|
||||
local tab = {}
|
||||
|
||||
tab.type = t.Type
|
||||
tab.t = t
|
||||
tab.isBaseType = true
|
||||
tab.Base = Base
|
||||
tab.t.ClassName = name
|
||||
|
||||
if ( !Base ) then
|
||||
Msg( "WARNING: Scripted entity " .. name .. " has an invalid base entity!\n" )
|
||||
end
|
||||
|
||||
SEntList[ name ] = tab
|
||||
|
||||
-- Allow all SENTS to be duplicated, unless specified
|
||||
if ( !t.DisableDuplicator ) then
|
||||
duplicator.Allow( name )
|
||||
end
|
||||
|
||||
--
|
||||
-- If we're reloading this entity class
|
||||
-- then refresh all the existing entities.
|
||||
--
|
||||
if ( old != nil ) then
|
||||
|
||||
--
|
||||
-- For each entity using this class
|
||||
--
|
||||
for _, entity in ipairs( ents.FindByClass( name ) ) do
|
||||
|
||||
--
|
||||
-- Replace the contents with this entity table
|
||||
--
|
||||
table.Merge( entity, tab.t )
|
||||
|
||||
--
|
||||
-- Call OnReloaded hook (if it has one)
|
||||
--
|
||||
if ( entity.OnReloaded ) then
|
||||
entity:OnReloaded()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Update entity table of entities that are based on this entity
|
||||
for _, e in ipairs( ents.GetAll() ) do
|
||||
if ( IsBasedOn( e:GetClass(), name ) ) then
|
||||
|
||||
table.Merge( e, Get( e:GetClass() ) )
|
||||
|
||||
if ( e.OnReloaded ) then
|
||||
e:OnReloaded()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if ( !t.Spawnable ) then return end
|
||||
|
||||
list.Set( "SpawnableEntities", name, {
|
||||
-- Required information
|
||||
PrintName = t.PrintName,
|
||||
ClassName = name,
|
||||
Category = t.Category,
|
||||
|
||||
-- Optional information
|
||||
NormalOffset = t.NormalOffset,
|
||||
DropToFloor = t.DropToFloor,
|
||||
Author = t.Author,
|
||||
AdminOnly = t.AdminOnly,
|
||||
Information = t.Information,
|
||||
ScriptedEntityType = t.ScriptedEntityType,
|
||||
IconOverride = t.IconOverride
|
||||
} )
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- All scripts have been loaded...
|
||||
--
|
||||
function OnLoaded()
|
||||
|
||||
--
|
||||
-- Once all the scripts are loaded we can set up the baseclass
|
||||
-- - we have to wait until they're all setup because load order
|
||||
-- could cause some entities to load before their bases!
|
||||
--
|
||||
for k, v in pairs( SEntList ) do
|
||||
|
||||
baseclass.Set( k, Get( k ) )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function Get( name, retval )
|
||||
|
||||
-- Do we have an alias?
|
||||
if ( Aliases[ name ] ) then
|
||||
name = Aliases[ name ]
|
||||
end
|
||||
|
||||
if ( SEntList[ name ] == nil ) then return nil end
|
||||
|
||||
-- Create/copy a new table
|
||||
local retval = retval or {}
|
||||
for k, v in pairs( SEntList[ name ].t ) do
|
||||
if ( istable( v ) ) then
|
||||
retval[ k ] = table.Copy( v )
|
||||
else
|
||||
retval[ k ] = v
|
||||
end
|
||||
end
|
||||
|
||||
-- Derive from base class
|
||||
if ( SEntList[ name ].Base != name ) then
|
||||
|
||||
local base = Get( SEntList[ name ].Base )
|
||||
|
||||
if ( !base ) then
|
||||
Msg("ERROR: Trying to derive entity " .. tostring( name ) .. " from non existant entity " .. tostring( SEntList[ name ].Base ) .. "!\n" )
|
||||
else
|
||||
retval = TableInherit( retval, base )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return retval
|
||||
|
||||
end
|
||||
|
||||
function GetType( name )
|
||||
|
||||
for k, v in pairs( BaseClasses ) do
|
||||
if ( name == v ) then return k end
|
||||
end
|
||||
|
||||
local ent = SEntList[ name ]
|
||||
if ( ent == nil ) then return nil end
|
||||
|
||||
if ( ent.type ) then
|
||||
return ent.type
|
||||
end
|
||||
|
||||
if ( ent.Base ) then
|
||||
return GetType( ent.Base )
|
||||
end
|
||||
|
||||
return nil
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: GetStored( string )
|
||||
Desc: Gets the REAL sent table, not a copy
|
||||
-----------------------------------------------------------]]
|
||||
function GetStored( name )
|
||||
return SEntList[ name ]
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: GetList( string )
|
||||
Desc: Get a list of all the registered SENTs
|
||||
-----------------------------------------------------------]]
|
||||
function GetList()
|
||||
local result = {}
|
||||
|
||||
for k,v in pairs( SEntList ) do
|
||||
result[ k ] = v
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
function GetSpawnable()
|
||||
|
||||
local result = {}
|
||||
|
||||
for k, v in pairs( SEntList ) do
|
||||
|
||||
local tab = v.t
|
||||
|
||||
if ( tab.Spawnable ) then
|
||||
table.insert( result, tab )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return result
|
||||
|
||||
end
|
||||
|
||||
function Alias( From, To )
|
||||
|
||||
Aliases[ From ] = To
|
||||
|
||||
end
|
||||
|
||||
function GetMember( entity_name, membername )
|
||||
|
||||
if ( !entity_name ) then return end
|
||||
|
||||
local ent = SEntList[ entity_name ]
|
||||
|
||||
if ( !ent ) then return end
|
||||
|
||||
local member = ent.t[ membername ]
|
||||
if ( member != nil ) then return member end
|
||||
|
||||
-- If our base is the same as us - don't infinite loop!
|
||||
if ( entity_name == ent.Base ) then return end
|
||||
|
||||
return GetMember( ent.Base, membername )
|
||||
|
||||
end
|
||||
49
lua/includes/modules/search.lua
Normal file
49
lua/includes/modules/search.lua
Normal file
@@ -0,0 +1,49 @@
|
||||
|
||||
module( "search", package.seeall )
|
||||
|
||||
local Providers = {}
|
||||
|
||||
function AddProvider( func, id )
|
||||
|
||||
local prov = {
|
||||
func = func,
|
||||
}
|
||||
|
||||
if ( id ) then
|
||||
Providers[ id ] = prov
|
||||
else
|
||||
table.insert( Providers, prov )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function GetResults( str, types, maxResults )
|
||||
|
||||
if ( !maxResults || maxResults < 1 ) then maxResults = 1024 end
|
||||
|
||||
local str = str:lower()
|
||||
if ( str == "" ) then return {} end
|
||||
|
||||
local results = {}
|
||||
|
||||
for k, v in pairs( Providers ) do
|
||||
if ( isstring( types ) ) then
|
||||
if ( types != k ) then continue end
|
||||
elseif ( istable( types ) ) then
|
||||
if ( !table.HasValue( types, k ) ) then continue end
|
||||
end
|
||||
|
||||
local tbl = v.func( str )
|
||||
for _, e in pairs( tbl ) do
|
||||
table.insert( results, e )
|
||||
end
|
||||
|
||||
if ( #results >= maxResults ) then break end
|
||||
|
||||
end
|
||||
|
||||
-- Todo. Sort, weighted?
|
||||
|
||||
return results
|
||||
|
||||
end
|
||||
311
lua/includes/modules/spawnmenu.lua
Normal file
311
lua/includes/modules/spawnmenu.lua
Normal file
@@ -0,0 +1,311 @@
|
||||
|
||||
local spawnmenu_engine = spawnmenu
|
||||
|
||||
module( "spawnmenu", package.seeall )
|
||||
|
||||
local g_ToolMenu = {}
|
||||
local CreationMenus = {}
|
||||
local PropTable = {}
|
||||
local PropTableCustom = {}
|
||||
|
||||
local ActiveToolPanel = nil
|
||||
local ActiveSpawnlistID = 1000
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
|
||||
Tool Tabs
|
||||
|
||||
-----------------------------------------------------------]]
|
||||
|
||||
function SetActiveControlPanel( pnl )
|
||||
ActiveToolPanel = pnl
|
||||
end
|
||||
|
||||
function ActiveControlPanel()
|
||||
return ActiveToolPanel
|
||||
end
|
||||
|
||||
function GetTools()
|
||||
return g_ToolMenu
|
||||
end
|
||||
|
||||
function GetToolMenu( name, label, icon )
|
||||
|
||||
--
|
||||
-- This is a dirty hack so that Main stays at the front of the tabs.
|
||||
--
|
||||
if ( name == "Main" ) then name = "AAAAAAA_Main" end
|
||||
|
||||
label = label or name
|
||||
icon = icon or "icon16/wrench.png"
|
||||
|
||||
for k, v in ipairs( g_ToolMenu ) do
|
||||
|
||||
if ( v.Name == name ) then return v.Items end
|
||||
|
||||
end
|
||||
|
||||
local NewMenu = { Name = name, Items = {}, Label = label, Icon = icon }
|
||||
table.insert( g_ToolMenu, NewMenu )
|
||||
|
||||
--
|
||||
-- Order the tabs by NAME
|
||||
--
|
||||
table.SortByMember( g_ToolMenu, "Name", true )
|
||||
|
||||
return NewMenu.Items
|
||||
|
||||
end
|
||||
|
||||
function ClearToolMenus()
|
||||
|
||||
g_ToolMenu = {}
|
||||
|
||||
end
|
||||
|
||||
function AddToolTab( strName, strLabel, Icon )
|
||||
|
||||
GetToolMenu( strName, strLabel, Icon )
|
||||
|
||||
end
|
||||
|
||||
function SwitchToolTab( id )
|
||||
|
||||
local Tab = g_SpawnMenu:GetToolMenu():GetToolPanel( id )
|
||||
if ( !IsValid( Tab ) or !IsValid( Tab.PropertySheetTab ) ) then return end
|
||||
|
||||
Tab.PropertySheetTab:DoClick()
|
||||
|
||||
end
|
||||
|
||||
function ActivateToolPanel( tabId, ctrlPnl, toolName )
|
||||
|
||||
local Tab = g_SpawnMenu:GetToolMenu():GetToolPanel( tabId )
|
||||
if ( !IsValid( Tab ) ) then return end
|
||||
|
||||
spawnmenu.SetActiveControlPanel( ctrlPnl )
|
||||
|
||||
if ( ctrlPnl ) then
|
||||
Tab:SetActive( ctrlPnl )
|
||||
end
|
||||
|
||||
SwitchToolTab( tabId )
|
||||
|
||||
if ( toolName && Tab.SetActiveToolText ) then
|
||||
Tab:SetActiveToolText( toolName )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- While technically tool class names CAN be duplicate, it normally should never happen.
|
||||
function ActivateTool( strName, noCommand )
|
||||
|
||||
-- I really don't like this triple loop
|
||||
for tab, v in ipairs( g_ToolMenu ) do
|
||||
for _, category in pairs( v.Items ) do
|
||||
for _, item in pairs( category ) do
|
||||
|
||||
if ( istable( item ) && item.ItemName && item.ItemName == strName ) then
|
||||
|
||||
if ( !noCommand && item.Command && string.len( item.Command ) > 1 ) then
|
||||
RunConsoleCommand( unpack( string.Explode( " ", item.Command ) ) )
|
||||
end
|
||||
|
||||
local cp = controlpanel.Get( strName )
|
||||
if ( !cp:GetInitialized() ) then
|
||||
cp:FillViaTable( { Text = item.Text, ControlPanelBuildFunction = item.CPanelFunction } )
|
||||
end
|
||||
|
||||
ActivateToolPanel( tab, cp, strName )
|
||||
|
||||
return
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function AddToolCategory( tab, RealName, PrintName )
|
||||
|
||||
local Menu = GetToolMenu( tab )
|
||||
|
||||
-- Does this category already exist?
|
||||
for k, v in ipairs( Menu ) do
|
||||
|
||||
if ( v.Text == PrintName ) then return end
|
||||
if ( v.ItemName == RealName ) then return end
|
||||
|
||||
end
|
||||
|
||||
table.insert( Menu, { Text = PrintName, ItemName = RealName } )
|
||||
|
||||
end
|
||||
|
||||
function AddToolMenuOption( tab, category, itemname, text, command, controls, cpanelfunction, TheTable )
|
||||
|
||||
local Menu = GetToolMenu( tab )
|
||||
local CategoryTable = nil
|
||||
|
||||
for k, v in ipairs( Menu ) do
|
||||
if ( v.ItemName && v.ItemName == category ) then CategoryTable = v break end
|
||||
end
|
||||
|
||||
-- No table found.. lets create one
|
||||
if ( !CategoryTable ) then
|
||||
CategoryTable = { Text = "#" .. category, ItemName = category }
|
||||
table.insert( Menu, CategoryTable )
|
||||
end
|
||||
|
||||
TheTable = TheTable or {}
|
||||
|
||||
TheTable.ItemName = itemname
|
||||
TheTable.Text = text
|
||||
TheTable.Command = command
|
||||
TheTable.Controls = controls
|
||||
TheTable.CPanelFunction = cpanelfunction
|
||||
|
||||
table.insert( CategoryTable, TheTable )
|
||||
|
||||
-- Keep the table sorted
|
||||
table.SortByMember( CategoryTable, "Text", true )
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
|
||||
Creation Tabs
|
||||
|
||||
-----------------------------------------------------------]]
|
||||
function AddCreationTab( strName, pFunction, pMaterial, iOrder, strTooltip )
|
||||
|
||||
iOrder = iOrder or 1000
|
||||
|
||||
pMaterial = pMaterial or "icon16/exclamation.png"
|
||||
|
||||
CreationMenus[ strName ] = { Function = pFunction, Icon = pMaterial, Order = iOrder, Tooltip = strTooltip }
|
||||
|
||||
end
|
||||
|
||||
function GetCreationTabs()
|
||||
|
||||
return CreationMenus
|
||||
|
||||
end
|
||||
|
||||
function SwitchCreationTab( id )
|
||||
|
||||
local tab = g_SpawnMenu:GetCreationMenu():GetCreationTab( id )
|
||||
if ( !tab or !IsValid( tab.Tab ) ) then return end
|
||||
|
||||
tab.Tab:DoClick()
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
|
||||
Spawn lists
|
||||
|
||||
-----------------------------------------------------------]]
|
||||
function GetPropTable()
|
||||
|
||||
return PropTable
|
||||
|
||||
end
|
||||
|
||||
function GetCustomPropTable()
|
||||
|
||||
return PropTableCustom
|
||||
|
||||
end
|
||||
|
||||
function AddPropCategory( strFilename, strName, tabContents, icon, id, parentid, needsapp )
|
||||
|
||||
PropTableCustom[ strFilename ] = {
|
||||
name = strName,
|
||||
contents = tabContents,
|
||||
icon = icon,
|
||||
id = id or ActiveSpawnlistID,
|
||||
parentid = parentid or 0,
|
||||
needsapp = needsapp
|
||||
}
|
||||
|
||||
if ( !id ) then ActiveSpawnlistID = ActiveSpawnlistID + 1 end
|
||||
|
||||
end
|
||||
|
||||
-- Populate the spawnmenu from the text files (engine)
|
||||
function PopulateFromEngineTextFiles()
|
||||
|
||||
-- Reset the already loaded prop list before loading them again.
|
||||
-- This caused the spawnlists to duplicate into crazy trees when spawnmenu_reload'ing after saving edited spawnlists
|
||||
PropTable = {}
|
||||
|
||||
spawnmenu_engine.PopulateFromTextFiles( function( strFilename, strName, tabContents, icon, id, parentid, needsapp )
|
||||
PropTable[ strFilename ] = {
|
||||
name = strName,
|
||||
contents = tabContents,
|
||||
icon = icon,
|
||||
id = id,
|
||||
parentid = parentid or 0,
|
||||
needsapp = needsapp
|
||||
}
|
||||
end )
|
||||
|
||||
end
|
||||
|
||||
-- Save the spawnfists to text files (engine)
|
||||
function DoSaveToTextFiles( props )
|
||||
|
||||
spawnmenu_engine.SaveToTextFiles( props )
|
||||
|
||||
end
|
||||
|
||||
--[[
|
||||
|
||||
Content Providers
|
||||
|
||||
Functions that populate the spawnmenu from the spawnmenu txt files.
|
||||
|
||||
function MyFunction( ContentPanel, ObjectTable )
|
||||
|
||||
local myspawnicon = CreateSpawnicon( ObjectTable.model )
|
||||
ContentPanel:AddItem( myspawnicon )
|
||||
|
||||
end
|
||||
|
||||
spawnmenu.AddContentType( "model", MyFunction )
|
||||
|
||||
--]]
|
||||
|
||||
local cp = {}
|
||||
|
||||
function AddContentType( name, func )
|
||||
cp[ name ] = func
|
||||
end
|
||||
|
||||
function GetContentType( name )
|
||||
|
||||
if ( !name ) then
|
||||
ErrorNoHaltWithStack( "spawnmenu.GetContentType got an invalid value\n" )
|
||||
return
|
||||
end
|
||||
|
||||
if ( !cp[ name ] ) then
|
||||
|
||||
cp[ name ] = function() end
|
||||
Msg( "spawnmenu.GetContentType( ", name, " ) - not found!\n" )
|
||||
|
||||
end
|
||||
|
||||
return cp[ name ]
|
||||
end
|
||||
|
||||
function CreateContentIcon( type, parent, tbl )
|
||||
|
||||
local ctrlpnl = GetContentType( type )
|
||||
if ( ctrlpnl ) then return ctrlpnl( parent, tbl ) end
|
||||
|
||||
end
|
||||
223
lua/includes/modules/team.lua
Normal file
223
lua/includes/modules/team.lua
Normal file
@@ -0,0 +1,223 @@
|
||||
module( "team", package.seeall )
|
||||
|
||||
local TeamInfo = {}
|
||||
local DefaultColor = Color( 255, 255, 100, 255 )
|
||||
|
||||
TeamInfo[TEAM_CONNECTING] = { Name = "Joining/Connecting", Color = DefaultColor, Score = 0, Joinable = false }
|
||||
TeamInfo[TEAM_UNASSIGNED] = { Name = "Unassigned", Color = DefaultColor, Score = 0, Joinable = false }
|
||||
TeamInfo[TEAM_SPECTATOR] = { Name = "Spectator", Color = DefaultColor, Score = 0, Joinable = true }
|
||||
|
||||
--[[------------------------------------------------------------
|
||||
|
||||
Call this to set up your team. It should be called in
|
||||
a shared file. This system assumes that your teams are
|
||||
static. If you want to have dynamic teams you need to
|
||||
code this yourself.
|
||||
|
||||
id should be a number. It's best to do something like
|
||||
|
||||
TEAM_TERRORISTS = 2
|
||||
|
||||
at the top of your code somewhere so you can reference
|
||||
teams via a variable rather than a number.
|
||||
|
||||
--------------------------------------------------------------]]
|
||||
function SetUp( id, name, color, joinable )
|
||||
|
||||
if ( joinable == nil ) then joinable = true end
|
||||
|
||||
TeamInfo[id] = { Name = name, Color = color, Score = 0, Joinable = joinable }
|
||||
|
||||
end
|
||||
|
||||
|
||||
function GetAllTeams()
|
||||
|
||||
return TeamInfo -- copyof?
|
||||
|
||||
end
|
||||
|
||||
function Valid( id )
|
||||
|
||||
if ( !TeamInfo[id] ) then return false end
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
function Joinable( id )
|
||||
|
||||
if ( !TeamInfo[id] ) then return false end
|
||||
return TeamInfo[id].Joinable
|
||||
|
||||
end
|
||||
|
||||
function GetSpawnPoint( id )
|
||||
|
||||
if ( !TeamInfo[id] ) then return end
|
||||
return TeamInfo[id].SpawnPointTable
|
||||
|
||||
end
|
||||
|
||||
function GetSpawnPoints( id )
|
||||
|
||||
if ( IsTableOfEntitiesValid( TeamInfo[id].SpawnPoints ) ) then return TeamInfo[id].SpawnPoints end
|
||||
|
||||
local SpawnPointTable = team.GetSpawnPoint( id )
|
||||
if ( !SpawnPointTable ) then return end
|
||||
|
||||
TeamInfo[id].SpawnPoints = {}
|
||||
|
||||
for k, entname in pairs( SpawnPointTable ) do
|
||||
|
||||
TeamInfo[id].SpawnPoints = table.Add( TeamInfo[id].SpawnPoints, ents.FindByClass( entname ) )
|
||||
|
||||
end
|
||||
|
||||
return TeamInfo[id].SpawnPoints
|
||||
|
||||
end
|
||||
|
||||
function SetSpawnPoint( id, ent_name )
|
||||
|
||||
if ( !TeamInfo[id] ) then return end
|
||||
if ( !istable( ent_name ) ) then ent_name = { ent_name } end
|
||||
|
||||
TeamInfo[id].SpawnPointTable = ent_name
|
||||
|
||||
end
|
||||
|
||||
function SetClass( id, classtable )
|
||||
|
||||
if ( !TeamInfo[id] ) then return end
|
||||
if ( !istable( classtable ) ) then classtable = { classtable } end
|
||||
|
||||
TeamInfo[id].SelectableClasses = classtable
|
||||
|
||||
end
|
||||
|
||||
function GetClass( id )
|
||||
|
||||
if ( !TeamInfo[id] ) then return end
|
||||
return TeamInfo[id].SelectableClasses
|
||||
|
||||
end
|
||||
|
||||
function TotalDeaths( index )
|
||||
|
||||
local score = 0
|
||||
|
||||
for _, ply in ipairs(player.GetAll()) do
|
||||
if ( ply:Team() == index ) then
|
||||
score = score + ply:Deaths()
|
||||
end
|
||||
end
|
||||
|
||||
return score
|
||||
|
||||
end
|
||||
|
||||
function TotalFrags( index )
|
||||
|
||||
local score = 0
|
||||
|
||||
for _, ply in ipairs(player.GetAll()) do
|
||||
if ( ply:Team() == index ) then
|
||||
score = score + ply:Frags()
|
||||
end
|
||||
end
|
||||
|
||||
return score
|
||||
|
||||
end
|
||||
|
||||
function NumPlayers( index )
|
||||
|
||||
local players = 0
|
||||
|
||||
for _, ply in ipairs(player.GetAll()) do
|
||||
if ( ply:Team() == index ) then
|
||||
players = players + 1
|
||||
end
|
||||
end
|
||||
|
||||
return players
|
||||
|
||||
end
|
||||
|
||||
function GetPlayers( index )
|
||||
|
||||
local TeamPlayers = {}
|
||||
|
||||
for _, ply in ipairs(player.GetAll()) do
|
||||
if ( ply:Team() == index ) then
|
||||
table.insert(TeamPlayers, ply)
|
||||
end
|
||||
end
|
||||
|
||||
return TeamPlayers
|
||||
|
||||
end
|
||||
|
||||
function GetScore( index )
|
||||
|
||||
return GetGlobalInt( "Team." .. tostring( index ) .. ".Score", 0 )
|
||||
|
||||
end
|
||||
|
||||
function GetName( index )
|
||||
|
||||
if ( !TeamInfo[index] ) then return "" end
|
||||
return TeamInfo[index].Name
|
||||
|
||||
end
|
||||
|
||||
function SetColor( index, color )
|
||||
|
||||
if ( !TeamInfo[ index ] ) then return false end
|
||||
TeamInfo[ index ].Color = color
|
||||
|
||||
return color
|
||||
|
||||
end
|
||||
|
||||
function GetColor( index )
|
||||
|
||||
local color = TeamInfo[index] && TeamInfo[index].Color || DefaultColor
|
||||
return Color( color.r, color.g, color.b, color.a )
|
||||
|
||||
end
|
||||
|
||||
function SetScore( index, score )
|
||||
|
||||
return SetGlobalInt( "Team." .. tostring( index ) .. ".Score", score )
|
||||
|
||||
end
|
||||
|
||||
function AddScore(index, score)
|
||||
|
||||
SetScore( index, GetScore( index ) + score )
|
||||
|
||||
end
|
||||
|
||||
function BestAutoJoinTeam()
|
||||
|
||||
local SmallestTeam = TEAM_UNASSIGNED
|
||||
local SmallestPlayers = 1000
|
||||
|
||||
for id, tm in pairs( team.GetAllTeams() ) do
|
||||
|
||||
if ( id != TEAM_SPECTATOR && id != TEAM_UNASSIGNED && id != TEAM_CONNECTING && tm.Joinable ) then
|
||||
|
||||
local PlayerCount = team.NumPlayers( id )
|
||||
if ( PlayerCount < SmallestPlayers || (PlayerCount == SmallestPlayers && id < SmallestTeam ) ) then
|
||||
SmallestPlayers = PlayerCount
|
||||
SmallestTeam = id
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return SmallestTeam
|
||||
|
||||
end
|
||||
517
lua/includes/modules/undo.lua
Normal file
517
lua/includes/modules/undo.lua
Normal file
@@ -0,0 +1,517 @@
|
||||
|
||||
module( "undo", package.seeall )
|
||||
|
||||
-- undo.Create("Wheel")
|
||||
-- undo.AddEntity( axis )
|
||||
-- undo.AddEntity( constraint )
|
||||
-- undo.SetPlayer( self.Owner )
|
||||
-- undo.Finish()
|
||||
|
||||
if ( CLIENT ) then
|
||||
|
||||
local ClientUndos = {}
|
||||
local bIsDirty = true
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
GetTable
|
||||
Returns the undo table for whatever reason
|
||||
-----------------------------------------------------------]]
|
||||
function GetTable()
|
||||
return ClientUndos
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
UpdateUI
|
||||
Actually updates the UI. Removes old controls and
|
||||
re-creates them using the new data.
|
||||
-----------------------------------------------------------]]
|
||||
local function UpdateUI()
|
||||
|
||||
local Panel = controlpanel.Get( "Undo" )
|
||||
if ( !IsValid( Panel ) ) then return end
|
||||
|
||||
Panel:Clear()
|
||||
Panel:Help( "#spawnmenu.utilities.undo.help" )
|
||||
|
||||
local ComboBox = Panel:ListBox()
|
||||
ComboBox:SetTall( 500 )
|
||||
|
||||
local Limit = 100
|
||||
local Count = 0
|
||||
for k, v in ipairs( ClientUndos ) do
|
||||
|
||||
local Item = ComboBox:AddItem( tostring( v.Name ) )
|
||||
Item.DoClick = function() RunConsoleCommand( "gmod_undonum", tostring( v.Key ) ) end
|
||||
|
||||
Count = Count + 1
|
||||
if ( Count > Limit ) then break end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
AddUndo
|
||||
Called from server. Adds a new undo to our UI
|
||||
-----------------------------------------------------------]]
|
||||
net.Receive( "Undo_AddUndo", function()
|
||||
|
||||
local key = net.ReadInt( 16 )
|
||||
local name = net.ReadString()
|
||||
|
||||
-- HACK: To support localization of "#prop_physics (models/path.mdl)"
|
||||
if ( name[ 1 ] == "#" and string.find( name, " (", nil, true ) ) then
|
||||
local undoName, undoSecondary = string.match( name, "^(#.*) %((.*)%)$" )
|
||||
if ( undoName and undoSecondary ) then
|
||||
name = string.format( "%s (%s)", language.GetPhrase( undoName ), language.GetPhrase( undoSecondary ) )
|
||||
end
|
||||
end
|
||||
|
||||
table.insert( ClientUndos, 1, { Key = key, Name = name } )
|
||||
|
||||
MakeUIDirty()
|
||||
|
||||
end )
|
||||
|
||||
-- Called from server, fires GM:OnUndo
|
||||
net.Receive( "Undo_FireUndo", function()
|
||||
|
||||
local name = net.ReadString()
|
||||
local hasCustomText = net.ReadBool()
|
||||
local customtext
|
||||
if ( hasCustomText ) then
|
||||
customtext = net.ReadString()
|
||||
end
|
||||
|
||||
hook.Run( "OnUndo", name, customtext )
|
||||
|
||||
end )
|
||||
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Undone
|
||||
Called from server. Notifies us that one of our undos
|
||||
has been undone or made redundant. We act by updating
|
||||
out data (We wait until the UI is viewed until updating)
|
||||
-----------------------------------------------------------]]
|
||||
local function Undone()
|
||||
|
||||
local key = net.ReadInt( 16 )
|
||||
|
||||
local NewUndo = {}
|
||||
local i = 1
|
||||
for k, v in ipairs( ClientUndos ) do
|
||||
|
||||
if ( v.Key != key ) then
|
||||
NewUndo [ i ] = v
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
ClientUndos = NewUndo
|
||||
NewUndo = nil
|
||||
|
||||
MakeUIDirty()
|
||||
|
||||
end
|
||||
net.Receive( "Undo_Undone", Undone )
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
MakeUIDirty
|
||||
Makes the UI dirty - it will re-create the controls
|
||||
the next time it is viewed.
|
||||
-----------------------------------------------------------]]
|
||||
function MakeUIDirty()
|
||||
|
||||
bIsDirty = true
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
CPanelPaint
|
||||
Called when the inner panel of the undo CPanel is painted
|
||||
We hook onto this to determine when the panel is viewed.
|
||||
When it's viewed we update the UI if it's marked as dirty
|
||||
-----------------------------------------------------------]]
|
||||
local function CPanelUpdate( panel )
|
||||
|
||||
-- This is kind of a shitty way of doing it - but we only want
|
||||
-- to update the UI when it's visible.
|
||||
if ( bIsDirty ) then
|
||||
|
||||
-- Doing this in a timer so it calls it in a sensible place
|
||||
-- Calling in the paint function could cause all kinds of problems
|
||||
-- It's a hack but hey welcome to GMod!
|
||||
timer.Simple( 0, UpdateUI )
|
||||
bIsDirty = false
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
SetupUI
|
||||
Adds a hook (CPanelPaint) to the control panel paint
|
||||
function so we can determine when it is being drawn.
|
||||
-----------------------------------------------------------]]
|
||||
function SetupUI()
|
||||
|
||||
local UndoPanel = controlpanel.Get( "Undo" )
|
||||
if ( !IsValid( UndoPanel ) ) then return end
|
||||
|
||||
-- Mark as dirty please
|
||||
MakeUIDirty()
|
||||
|
||||
-- Panels only think when they're visible
|
||||
UndoPanel.Think = CPanelUpdate
|
||||
|
||||
end
|
||||
|
||||
hook.Add( "PostReloadToolsMenu", "BuildUndoUI", SetupUI )
|
||||
|
||||
end
|
||||
|
||||
|
||||
if ( !SERVER ) then return end
|
||||
|
||||
local PlayerUndo = {}
|
||||
-- PlayerUndo
|
||||
-- - Player UniqueID
|
||||
-- - Undo Table
|
||||
-- - Name
|
||||
-- - Entities (table of ents)
|
||||
-- - Owner (player)
|
||||
|
||||
local Current_Undo = nil
|
||||
|
||||
util.AddNetworkString( "Undo_Undone" )
|
||||
util.AddNetworkString( "Undo_AddUndo" )
|
||||
util.AddNetworkString( "Undo_FireUndo" )
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
GetTable
|
||||
Returns the undo table for whatever reason
|
||||
-----------------------------------------------------------]]
|
||||
function GetTable()
|
||||
return PlayerUndo
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
GetTable
|
||||
Save/Restore the undo tables
|
||||
-----------------------------------------------------------]]
|
||||
local function Save( save )
|
||||
|
||||
saverestore.WriteTable( PlayerUndo, save )
|
||||
|
||||
end
|
||||
|
||||
local function Restore( restore )
|
||||
|
||||
PlayerUndo = saverestore.ReadTable( restore )
|
||||
|
||||
end
|
||||
|
||||
saverestore.AddSaveHook( "UndoTable", Save )
|
||||
saverestore.AddRestoreHook( "UndoTable", Restore )
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Start a new undo
|
||||
-----------------------------------------------------------]]
|
||||
function Create( text )
|
||||
|
||||
Current_Undo = {}
|
||||
Current_Undo.Name = text
|
||||
Current_Undo.Entities = {}
|
||||
Current_Undo.Owner = nil
|
||||
Current_Undo.Functions = {}
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Adds an entity to this undo (The entity is removed on undo)
|
||||
-----------------------------------------------------------]]
|
||||
function SetCustomUndoText( CustomUndoText )
|
||||
|
||||
if ( !Current_Undo ) then return end
|
||||
|
||||
Current_Undo.CustomUndoText = CustomUndoText
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Adds an entity to this undo (The entity is removed on undo)
|
||||
-----------------------------------------------------------]]
|
||||
function AddEntity( ent )
|
||||
|
||||
if ( !Current_Undo ) then return end
|
||||
if ( !IsValid( ent ) ) then return end
|
||||
|
||||
table.insert( Current_Undo.Entities, ent )
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Add a function to call to this undo
|
||||
-----------------------------------------------------------]]
|
||||
function AddFunction( func, ... )
|
||||
|
||||
if ( !Current_Undo ) then return end
|
||||
if ( !func ) then return end
|
||||
|
||||
table.insert( Current_Undo.Functions, { func, {...} } )
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Replace Entity
|
||||
-----------------------------------------------------------]]
|
||||
function ReplaceEntity( from, to )
|
||||
|
||||
local ActionTaken = false
|
||||
|
||||
for _, PlayerTable in pairs( PlayerUndo ) do
|
||||
for _, UndoTable in pairs( PlayerTable ) do
|
||||
if ( UndoTable.Entities ) then
|
||||
|
||||
for key, ent in pairs( UndoTable.Entities ) do
|
||||
if ( ent == from ) then
|
||||
UndoTable.Entities[ key ] = to
|
||||
ActionTaken = true
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return ActionTaken
|
||||
|
||||
end
|
||||
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Sets who's undo this is
|
||||
-----------------------------------------------------------]]
|
||||
function SetPlayer( ply )
|
||||
|
||||
if ( !Current_Undo ) then return end
|
||||
if ( !IsValid( ply ) ) then return end
|
||||
|
||||
Current_Undo.Owner = ply
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
SendUndoneMessage
|
||||
Sends a message to notify the client that one of their
|
||||
undos has been removed. They can then update their GUI.
|
||||
-----------------------------------------------------------]]
|
||||
local function SendUndoneMessage( ent, id, ply )
|
||||
|
||||
if ( !IsValid( ply ) ) then return end
|
||||
|
||||
-- For further optimization we could queue up the ids and send them
|
||||
-- in one batch every 0.5 seconds or something along those lines.
|
||||
|
||||
net.Start( "Undo_Undone" )
|
||||
net.WriteInt( id, 16 )
|
||||
net.Send( ply )
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Checks whether an undo is allowed to be created
|
||||
-----------------------------------------------------------]]
|
||||
local function Can_CreateUndo( undo )
|
||||
|
||||
local call = hook.Run( "CanCreateUndo", undo.Owner, undo )
|
||||
|
||||
return call == true or call == nil
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Finish
|
||||
-----------------------------------------------------------]]
|
||||
function Finish( NiceText )
|
||||
|
||||
if ( !Current_Undo ) then return end
|
||||
|
||||
-- Do not add undos that have no owner or anything to undo
|
||||
if ( !IsValid( Current_Undo.Owner ) or ( table.IsEmpty( Current_Undo.Entities ) && table.IsEmpty( Current_Undo.Functions ) ) or !Can_CreateUndo( Current_Undo ) ) then
|
||||
Current_Undo = nil
|
||||
return false
|
||||
end
|
||||
|
||||
local index = Current_Undo.Owner:UniqueID()
|
||||
PlayerUndo[ index ] = PlayerUndo[ index ] or {}
|
||||
|
||||
Current_Undo.NiceText = NiceText or ( "#" .. Current_Undo.Name )
|
||||
|
||||
local id = table.insert( PlayerUndo[ index ], Current_Undo )
|
||||
|
||||
net.Start( "Undo_AddUndo" )
|
||||
net.WriteInt( id, 16 )
|
||||
net.WriteString( Current_Undo.NiceText )
|
||||
net.Send( Current_Undo.Owner )
|
||||
|
||||
-- Have one of the entities in the undo tell us when it gets undone.
|
||||
if ( IsValid( Current_Undo.Entities[ 1 ] ) ) then
|
||||
|
||||
local ent = Current_Undo.Entities[ 1 ]
|
||||
ent:CallOnRemove( "undo" .. id, SendUndoneMessage, id, Current_Undo.Owner )
|
||||
|
||||
end
|
||||
|
||||
Current_Undo = nil
|
||||
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Undos an undo
|
||||
-----------------------------------------------------------]]
|
||||
function Do_Undo( undo )
|
||||
|
||||
if ( !undo ) then return false end
|
||||
|
||||
if ( hook.Run( "PreUndo", undo ) == false ) then return end
|
||||
|
||||
local count = 0
|
||||
|
||||
-- Call each function
|
||||
if ( undo.Functions ) then
|
||||
for index, func in pairs( undo.Functions ) do
|
||||
|
||||
local success = func[ 1 ]( undo, unpack( func[ 2 ] ) )
|
||||
if ( success != false ) then
|
||||
count = count + 1
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
-- Remove each entity in this undo
|
||||
if ( undo.Entities ) then
|
||||
for index, entity in pairs( undo.Entities ) do
|
||||
|
||||
if ( IsValid( entity ) ) then
|
||||
entity:Remove()
|
||||
count = count + 1
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
if ( count > 0 ) then
|
||||
net.Start( "Undo_FireUndo" )
|
||||
net.WriteString( undo.Name )
|
||||
net.WriteBool( undo.CustomUndoText != nil )
|
||||
if ( undo.CustomUndoText != nil ) then
|
||||
net.WriteString( undo.CustomUndoText )
|
||||
end
|
||||
net.Send( undo.Owner )
|
||||
end
|
||||
|
||||
hook.Run( "PostUndo", undo, count )
|
||||
|
||||
return count
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Checks whether a player is allowed to undo
|
||||
-----------------------------------------------------------]]
|
||||
local function Can_Undo( ply, undo )
|
||||
|
||||
local call = hook.Run( "CanUndo", ply, undo )
|
||||
|
||||
return call == true or call == nil
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Console command
|
||||
-----------------------------------------------------------]]
|
||||
local function CC_UndoLast( pl, command, args )
|
||||
|
||||
local index = pl:UniqueID()
|
||||
PlayerUndo[ index ] = PlayerUndo[ index ] or {}
|
||||
|
||||
local last = nil
|
||||
local lastk = nil
|
||||
|
||||
for k, v in pairs( PlayerUndo[ index ] ) do
|
||||
|
||||
lastk = k
|
||||
last = v
|
||||
|
||||
end
|
||||
|
||||
-- No undos
|
||||
if ( !last ) then return end
|
||||
|
||||
-- This is quite messy, but if the player rejoined the server
|
||||
-- 'Owner' might no longer be a valid entity. So replace the Owner
|
||||
-- with the player that is doing the undoing
|
||||
last.Owner = pl
|
||||
|
||||
if ( !Can_Undo( pl, last ) ) then return end
|
||||
|
||||
local count = Do_Undo( last )
|
||||
|
||||
net.Start( "Undo_Undone" )
|
||||
net.WriteInt( lastk, 16 )
|
||||
net.Send( pl )
|
||||
|
||||
PlayerUndo[ index ][ lastk ] = nil
|
||||
|
||||
-- If our last undo object is already deleted then compact
|
||||
-- the undos until we hit one that does something
|
||||
if ( count == 0 ) then
|
||||
CC_UndoLast( pl )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Console command
|
||||
-----------------------------------------------------------]]
|
||||
local function CC_UndoNum( ply, command, args )
|
||||
|
||||
if ( !args[ 1 ] ) then return end
|
||||
|
||||
local index = ply:UniqueID()
|
||||
PlayerUndo[ index ] = PlayerUndo[ index ] or {}
|
||||
|
||||
local UndoNum = tonumber( args[ 1 ] )
|
||||
if ( !UndoNum ) then return end
|
||||
|
||||
local TheUndo = PlayerUndo[ index ][ UndoNum ]
|
||||
if ( !TheUndo ) then return end
|
||||
|
||||
-- Do the same as above
|
||||
TheUndo.Owner = ply
|
||||
|
||||
if ( !Can_Undo( ply, TheUndo ) ) then return end
|
||||
|
||||
-- Undo!
|
||||
Do_Undo( TheUndo )
|
||||
|
||||
-- Notify the client UI that the undo happened
|
||||
-- This is normally called by the deleted entity via SendUndoneMessage
|
||||
-- But in cases where the undo only has functions that will not do
|
||||
net.Start( "Undo_Undone" )
|
||||
net.WriteInt( UndoNum, 16 )
|
||||
net.Send( ply )
|
||||
|
||||
-- Don't delete the entry completely so nothing new takes its place and ruin CC_UndoLast's logic (expecting newest entry be at highest index)
|
||||
PlayerUndo[ index ][ UndoNum ] = {}
|
||||
|
||||
end
|
||||
|
||||
concommand.Add( "undo", CC_UndoLast, nil, "", { FCVAR_DONTRECORD } )
|
||||
concommand.Add( "gmod_undo", CC_UndoLast, nil, "", { FCVAR_DONTRECORD } )
|
||||
concommand.Add( "gmod_undonum", CC_UndoNum, nil, "", { FCVAR_DONTRECORD } )
|
||||
87
lua/includes/modules/usermessage.lua
Normal file
87
lua/includes/modules/usermessage.lua
Normal file
@@ -0,0 +1,87 @@
|
||||
|
||||
-- Globals that we are going to use
|
||||
local unpack = unpack
|
||||
local Msg = Msg
|
||||
|
||||
--[[
|
||||
This is merely a convenience function. If you pass numbers
|
||||
using this they're always sent as long. Which sucks if you're sending
|
||||
numbers that are always under 100 etc.
|
||||
--]]
|
||||
function SendUserMessage( name, ply, ... )
|
||||
|
||||
if ( CLIENT ) then return end
|
||||
|
||||
umsg.Start( name, ply )
|
||||
|
||||
for k, v in ipairs( { ... } ) do
|
||||
local t = TypeID( v )
|
||||
|
||||
if ( t == TYPE_STRING ) then
|
||||
umsg.String( v )
|
||||
elseif ( t == TYPE_ENTITY ) then
|
||||
umsg.Entity( v )
|
||||
elseif ( t == TYPE_NUMBER ) then
|
||||
umsg.Long( v )
|
||||
elseif ( t == TYPE_VECTOR ) then
|
||||
umsg.Vector( v )
|
||||
elseif ( t == TYPE_ANGLE ) then
|
||||
umsg.Angle( v )
|
||||
elseif ( t == TYPE_BOOL ) then
|
||||
umsg.Bool( v )
|
||||
else
|
||||
ErrorNoHalt( "SendUserMessage: Couldn't send type " .. type( v ) .. "\n" )
|
||||
end
|
||||
end
|
||||
|
||||
umsg.End()
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: usermessage
|
||||
Desc: Enables the server to send the client messages (in a bandwidth friendly manner)
|
||||
-----------------------------------------------------------]]
|
||||
module( "usermessage" )
|
||||
|
||||
local Hooks = {}
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: GetTable
|
||||
Desc: Returns the table of hooked usermessages
|
||||
-----------------------------------------------------------]]
|
||||
function GetTable()
|
||||
|
||||
return Hooks
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: Hook
|
||||
Desc: Adds a hook
|
||||
-----------------------------------------------------------]]
|
||||
function Hook( messagename, func, ... )
|
||||
|
||||
Hooks[ messagename ] = {}
|
||||
|
||||
Hooks[ messagename ].Function = func
|
||||
Hooks[ messagename ].PreArgs = { ... }
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: Call( name, args )
|
||||
Desc: Called by the engine to call a gamemode hook
|
||||
-----------------------------------------------------------]]
|
||||
function IncomingMessage( MessageName, msg )
|
||||
|
||||
if ( Hooks[ MessageName ] ) then
|
||||
|
||||
Hooks[ MessageName ].Function( msg, unpack( Hooks[ MessageName ].PreArgs ) )
|
||||
return
|
||||
|
||||
end
|
||||
|
||||
Msg( "Warning: Unhandled usermessage '" .. MessageName .. "'\n" )
|
||||
|
||||
end
|
||||
377
lua/includes/modules/utf8.lua
Normal file
377
lua/includes/modules/utf8.lua
Normal file
@@ -0,0 +1,377 @@
|
||||
local bit = bit
|
||||
local error = error
|
||||
local ipairs = ipairs
|
||||
local string = string
|
||||
local table = table
|
||||
local unpack = unpack
|
||||
local math = math
|
||||
|
||||
module( "utf8" )
|
||||
|
||||
--
|
||||
-- Pattern that can be used with the string library to match a single UTF-8 byte-sequence.
|
||||
-- This expects the string to contain valid UTF-8 data.
|
||||
--
|
||||
charpattern = "[%z\x01-\x7F\xC2-\xF4][\x80-\xBF]*"
|
||||
|
||||
--
|
||||
-- Transforms indexes of a string to be positive.
|
||||
-- Negative indices will wrap around like the string library's functions.
|
||||
--
|
||||
local function strRelToAbs( str, ... )
|
||||
|
||||
local args = { ... }
|
||||
|
||||
for k, v in ipairs( args ) do
|
||||
v = v > 0 and v or math.max( #str + v + 1, 1 )
|
||||
|
||||
if v < 1 or v > #str + 1 then
|
||||
error( "bad index to string (out of range)", 3 )
|
||||
end
|
||||
|
||||
args[ k ] = v
|
||||
end
|
||||
|
||||
return unpack( args )
|
||||
|
||||
end
|
||||
|
||||
-- Decodes a single UTF-8 byte-sequence from a string, ensuring it is valid
|
||||
-- Returns the index of the first/last chars of a sequence and its codepoint
|
||||
--
|
||||
local function decode( str, startPos )
|
||||
|
||||
startPos = strRelToAbs( str, startPos or 1 )
|
||||
|
||||
local b1 = str:byte( startPos, startPos )
|
||||
|
||||
-- End of string
|
||||
if not b1 then
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Single-byte sequence
|
||||
if b1 < 0x80 then
|
||||
return startPos, startPos, b1
|
||||
end
|
||||
|
||||
-- Validate first byte of multi-byte sequence
|
||||
if b1 > 0xF4 or b1 < 0xC2 then
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Get 'supposed' amount of continuation bytes from primary byte
|
||||
local contByteCount = b1 >= 0xF0 and 3 or
|
||||
b1 >= 0xE0 and 2 or
|
||||
b1 >= 0xC0 and 1
|
||||
|
||||
local endPos = startPos + contByteCount
|
||||
local codePoint = 0
|
||||
|
||||
-- The string doesn't have enough data for this many continutation bytes
|
||||
if #str < endPos then
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Validate our continuation bytes
|
||||
for _, bX in ipairs { str:byte( startPos + 1, endPos ) } do
|
||||
|
||||
-- Invalid continuation byte hit
|
||||
if bit.band( bX, 0xC0 ) ~= 0x80 then
|
||||
return nil
|
||||
end
|
||||
|
||||
codePoint = bit.bor( bit.lshift( codePoint, 6 ), bit.band( bX, 0x3F ) )
|
||||
b1 = bit.lshift( b1, 1 )
|
||||
|
||||
end
|
||||
|
||||
codePoint = bit.bor( codePoint, bit.lshift( bit.band( b1, 0x7F ), contByteCount * 5 ) )
|
||||
|
||||
return startPos, endPos, codePoint
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- Takes zero or more integers and returns a string containing the UTF-8 representation of each
|
||||
--
|
||||
function char( ... )
|
||||
|
||||
local buf = {}
|
||||
|
||||
for k, v in ipairs { ... } do
|
||||
|
||||
if v < 0 or v > 0x10FFFF then
|
||||
error( "bad argument #" .. k .. " to char (out of range)", 2 )
|
||||
end
|
||||
|
||||
local b1, b2, b3, b4 = nil, nil, nil, nil
|
||||
|
||||
if v < 0x80 then -- Single-byte sequence
|
||||
|
||||
table.insert( buf, string.char( v ) )
|
||||
|
||||
elseif v < 0x800 then -- Two-byte sequence
|
||||
|
||||
b1 = bit.bor( 0xC0, bit.band( bit.rshift( v, 6 ), 0x1F ) )
|
||||
b2 = bit.bor( 0x80, bit.band( v, 0x3F ) )
|
||||
|
||||
table.insert( buf, string.char( b1, b2 ) )
|
||||
|
||||
elseif v < 0x10000 then -- Three-byte sequence
|
||||
|
||||
b1 = bit.bor( 0xE0, bit.band( bit.rshift( v, 12 ), 0x0F ) )
|
||||
b2 = bit.bor( 0x80, bit.band( bit.rshift( v, 6 ), 0x3F ) )
|
||||
b3 = bit.bor( 0x80, bit.band( v, 0x3F ) )
|
||||
|
||||
table.insert( buf, string.char( b1, b2, b3 ) )
|
||||
|
||||
else -- Four-byte sequence
|
||||
|
||||
b1 = bit.bor( 0xF0, bit.band( bit.rshift( v, 18 ), 0x07 ) )
|
||||
b2 = bit.bor( 0x80, bit.band( bit.rshift( v, 12 ), 0x3F ) )
|
||||
b3 = bit.bor( 0x80, bit.band( bit.rshift( v, 6 ), 0x3F ) )
|
||||
b4 = bit.bor( 0x80, bit.band( v, 0x3F ) )
|
||||
|
||||
table.insert( buf, string.char( b1, b2, b3, b4 ) )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return table.concat( buf, "" )
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- Iterates over a UTF-8 string similarly to pairs
|
||||
-- k = index of sequence, v = string value of sequence
|
||||
--
|
||||
function codes( str )
|
||||
|
||||
local i = 1
|
||||
|
||||
return function()
|
||||
|
||||
-- Have we hit the end of the iteration set?
|
||||
if i > #str then
|
||||
return nil
|
||||
end
|
||||
|
||||
local startPos, endPos, codePoint = decode( str, i )
|
||||
|
||||
if not startPos then
|
||||
error( "invalid UTF-8 code", 2 )
|
||||
end
|
||||
|
||||
i = endPos + 1
|
||||
|
||||
return startPos, codePoint
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- Returns an integer-representation of the UTF-8 sequence(s) in a string
|
||||
-- startPos defaults to 1, endPos defaults to startPos
|
||||
--
|
||||
function codepoint( str, startPos, endPos )
|
||||
|
||||
startPos, endPos = strRelToAbs( str, startPos or 1, endPos or startPos or 1 )
|
||||
|
||||
local ret = {}
|
||||
|
||||
repeat
|
||||
local seqStartPos, seqEndPos, codePoint = decode( str, startPos )
|
||||
|
||||
if not seqStartPos then
|
||||
error( "invalid UTF-8 code", 2 )
|
||||
end
|
||||
|
||||
-- Increment current string index
|
||||
startPos = seqEndPos + 1
|
||||
|
||||
table.insert( ret, codePoint )
|
||||
until seqEndPos >= endPos
|
||||
|
||||
return unpack( ret )
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- Returns the length of a UTF-8 string. false, index is returned if an invalid sequence is hit
|
||||
-- startPos defaults to 1, endPos defaults to -1
|
||||
--
|
||||
function len( str, startPos, endPos )
|
||||
|
||||
startPos, endPos = strRelToAbs( str, startPos or 1, endPos or -1 )
|
||||
|
||||
local len = 0
|
||||
|
||||
while endPos >= startPos and startPos <= #str do
|
||||
local seqStartPos, seqEndPos = decode( str, startPos )
|
||||
|
||||
-- Hit an invalid sequence?
|
||||
if not seqStartPos then
|
||||
return false, startPos
|
||||
end
|
||||
|
||||
-- Increment current string pointer
|
||||
startPos = seqEndPos + 1
|
||||
|
||||
-- Increment length
|
||||
len = len + 1
|
||||
end
|
||||
|
||||
return len
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- Returns the byte-index of the n'th UTF-8-character after the given byte-index (nil if none)
|
||||
-- startPos defaults to 1 when n is positive and -1 when n is negative
|
||||
-- If 0 is zero, this function instead returns the byte-index of the UTF-8-character startPos lies within.
|
||||
--
|
||||
function offset( str, n, startPos )
|
||||
|
||||
if startPos and ( startPos > #str or -startPos > #str or startPos == 0 ) then
|
||||
error( "bad index to string (out of range)", 2 )
|
||||
end
|
||||
|
||||
local pos = ( n >= 0 ) and 1 or #str
|
||||
pos = strRelToAbs( str, startPos or pos )
|
||||
|
||||
-- Back up to the start of this byte sequence
|
||||
if n == 0 then
|
||||
|
||||
while pos > 0 and not decode( str, pos ) do
|
||||
pos = pos - 1
|
||||
end
|
||||
|
||||
return pos
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- Make sure we're on a valid sequence
|
||||
--
|
||||
if not decode( str, pos ) then
|
||||
error( "initial position is a continuation byte", 2 )
|
||||
end
|
||||
|
||||
-- Back up to (-n) byte sequences
|
||||
if n < 0 then
|
||||
|
||||
for i = 1, -n do
|
||||
pos = pos - 1
|
||||
|
||||
while pos > 0 and not decode( str, pos ) do
|
||||
pos = pos - 1
|
||||
end
|
||||
end
|
||||
|
||||
if pos < 1 then
|
||||
return nil
|
||||
end
|
||||
|
||||
return pos
|
||||
|
||||
end
|
||||
|
||||
-- Jump forward (n) byte sequences
|
||||
if n > 0 then
|
||||
|
||||
for i = 1, n do
|
||||
pos = pos + 1
|
||||
|
||||
while pos <= #str and not decode( str, pos ) do
|
||||
pos = pos + 1
|
||||
end
|
||||
end
|
||||
|
||||
if pos > #str then
|
||||
return nil
|
||||
end
|
||||
|
||||
return pos
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- Forces a string to contain only valid UTF-8 data.
|
||||
-- Invalid sequences are replaced with U+FFFD.
|
||||
--
|
||||
function force( str )
|
||||
|
||||
local buf = {}
|
||||
|
||||
local curPos, endPos = 1, #str
|
||||
|
||||
-- Empty string?
|
||||
if endPos == 0 then
|
||||
return str
|
||||
end
|
||||
|
||||
repeat
|
||||
|
||||
local seqStartPos, seqEndPos = decode( str, curPos )
|
||||
|
||||
if not seqStartPos then
|
||||
|
||||
table.insert( buf, char( 0xFFFD ) )
|
||||
curPos = curPos + 1
|
||||
|
||||
else
|
||||
|
||||
table.insert( buf, str:sub( seqStartPos, seqEndPos ) )
|
||||
curPos = seqEndPos + 1
|
||||
|
||||
end
|
||||
|
||||
until curPos > endPos
|
||||
|
||||
return table.concat( buf, "" )
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- Converts a relative index to an absolute
|
||||
-- This is different from the above in that it cares about characters and not bytes
|
||||
--
|
||||
local function strRelToAbsChar( str, pos )
|
||||
if pos < 0 then
|
||||
pos = math.max( pos + len( str ) + 1, 0 )
|
||||
end
|
||||
return pos
|
||||
end
|
||||
|
||||
--
|
||||
-- UTF-8 compilant version of str[idx]
|
||||
--
|
||||
function GetChar( str, idx )
|
||||
idx = strRelToAbsChar( str, idx )
|
||||
|
||||
if idx == 0 then return "" end
|
||||
if idx > len( str ) then return "" end
|
||||
|
||||
local off = offset( str, idx - 1 )
|
||||
return char( codepoint( str, off ) )
|
||||
end
|
||||
|
||||
--
|
||||
-- UTF-8 compilant version of string.sub
|
||||
--
|
||||
function sub( str, charstart, charend )
|
||||
charstart = strRelToAbsChar( str, charstart )
|
||||
charend = strRelToAbsChar( str, charend or -1 )
|
||||
|
||||
local buf = {}
|
||||
for i = charstart, charend do
|
||||
buf[#buf + 1] = GetChar( str, i )
|
||||
end
|
||||
|
||||
return table.concat( buf )
|
||||
end
|
||||
185
lua/includes/modules/weapons.lua
Normal file
185
lua/includes/modules/weapons.lua
Normal file
@@ -0,0 +1,185 @@
|
||||
|
||||
module( "weapons", package.seeall )
|
||||
|
||||
local WeaponList = {}
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: TableInherit( t, base )
|
||||
Desc: Copies any missing data from base to t
|
||||
-----------------------------------------------------------]]
|
||||
local function TableInherit( t, base )
|
||||
|
||||
for k, v in pairs( base ) do
|
||||
|
||||
if ( t[ k ] == nil ) then
|
||||
t[ k ] = v
|
||||
elseif ( k != "BaseClass" && istable( t[ k ] ) && istable( v ) ) then
|
||||
TableInherit( t[ k ], v )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
t[ "BaseClass" ] = base
|
||||
|
||||
return t
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: IsBasedOn( name, base )
|
||||
Desc: Checks if name is based on base
|
||||
-----------------------------------------------------------]]
|
||||
function IsBasedOn( name, base )
|
||||
local t = GetStored( name )
|
||||
if ( !t ) then return false end
|
||||
if ( t.Base == name ) then return false end
|
||||
|
||||
if ( t.Base == base ) then return true end
|
||||
return IsBasedOn( t.Base, base )
|
||||
end
|
||||
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: Register( table, string, bool )
|
||||
Desc: Used to register your SWEP with the engine
|
||||
-----------------------------------------------------------]]
|
||||
function Register( t, name )
|
||||
|
||||
if ( hook.Run( "PreRegisterSWEP", t, name ) == false ) then return end
|
||||
|
||||
if ( isstring( t.ClassNameOverride ) ) then name = t.ClassNameOverride end
|
||||
|
||||
local old = WeaponList[ name ]
|
||||
t.ClassName = name
|
||||
WeaponList[ name ] = t
|
||||
|
||||
--baseclass.Set( name, t )
|
||||
|
||||
list.Set( "Weapon", name, {
|
||||
ClassName = name,
|
||||
PrintName = t.PrintName or name,
|
||||
Category = t.Category or "#spawnmenu.category.other",
|
||||
Spawnable = t.Spawnable,
|
||||
AdminOnly = t.AdminOnly,
|
||||
ScriptedEntityType = t.ScriptedEntityType,
|
||||
IconOverride = t.IconOverride
|
||||
} )
|
||||
|
||||
-- Allow all SWEPS to be duplicated, unless specified
|
||||
if ( !t.DisableDuplicator ) then
|
||||
duplicator.Allow( name )
|
||||
end
|
||||
|
||||
--
|
||||
-- If we're reloading this entity class
|
||||
-- then refresh all the existing entities.
|
||||
--
|
||||
if ( old != nil ) then
|
||||
|
||||
-- Update SWEP table of entities that are based on this SWEP
|
||||
for _, e in ipairs( ents.GetAll() ) do
|
||||
local class = e:GetClass()
|
||||
|
||||
if ( class == name ) then
|
||||
--
|
||||
-- Replace the contents with this entity table
|
||||
--
|
||||
table.Merge( e, t )
|
||||
|
||||
--
|
||||
-- Call OnReloaded hook (if it has one)
|
||||
--
|
||||
if ( e.OnReloaded ) then
|
||||
e:OnReloaded()
|
||||
end
|
||||
end
|
||||
|
||||
if ( IsBasedOn( class, name ) ) then
|
||||
table.Merge( e, Get( class ) )
|
||||
|
||||
if ( e.OnReloaded ) then
|
||||
e:OnReloaded()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- All scripts have been loaded...
|
||||
--
|
||||
function OnLoaded()
|
||||
|
||||
--
|
||||
-- Once all the scripts are loaded we can set up the baseclass
|
||||
-- - we have to wait until they're all setup because load order
|
||||
-- could cause some entities to load before their bases!
|
||||
--
|
||||
for k, v in pairs( WeaponList ) do
|
||||
|
||||
baseclass.Set( k, Get( k ) )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: Get( string )
|
||||
Desc: Get a weapon by name.
|
||||
-----------------------------------------------------------]]
|
||||
function Get( name, retval )
|
||||
|
||||
local Stored = GetStored( name )
|
||||
if ( !Stored ) then return nil end
|
||||
|
||||
-- Create/copy a new table
|
||||
local retval = retval or {}
|
||||
for k, v in pairs( Stored ) do
|
||||
if ( istable( v ) ) then
|
||||
retval[ k ] = table.Copy( v )
|
||||
else
|
||||
retval[ k ] = v
|
||||
end
|
||||
end
|
||||
retval.Base = retval.Base or "weapon_base"
|
||||
|
||||
-- If we're not derived from ourselves (a base weapon)
|
||||
-- then derive from our 'Base' weapon.
|
||||
if ( retval.Base != name ) then
|
||||
|
||||
local base = Get( retval.Base )
|
||||
|
||||
if ( !base ) then
|
||||
Msg( "ERROR: Trying to derive weapon " .. tostring( name ) .. " from non existant SWEP " .. tostring( retval.Base ) .. "!\n" )
|
||||
else
|
||||
retval = TableInherit( retval, base )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return retval
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: GetStored( string )
|
||||
Desc: Gets the REAL weapon table, not a copy
|
||||
-----------------------------------------------------------]]
|
||||
function GetStored( name )
|
||||
return WeaponList[ name ]
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Name: GetList( string )
|
||||
Desc: Get a list of all the registered SWEPs
|
||||
-----------------------------------------------------------]]
|
||||
function GetList()
|
||||
local result = {}
|
||||
|
||||
for k, v in pairs( WeaponList ) do
|
||||
table.insert( result, v )
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
175
lua/includes/modules/widget.lua
Normal file
175
lua/includes/modules/widget.lua
Normal file
@@ -0,0 +1,175 @@
|
||||
|
||||
|
||||
--
|
||||
-- Widgets are like gui controls in the 3D world(!!!)
|
||||
--
|
||||
|
||||
widgets = {}
|
||||
local widgetEntities = {}
|
||||
|
||||
--
|
||||
-- Holds the currently hovered widget
|
||||
--
|
||||
widgets.Hovered = nil
|
||||
widgets.HoveredPos = vector_origin
|
||||
|
||||
--
|
||||
-- Holds the current pressed widget
|
||||
--
|
||||
widgets.Pressed = nil
|
||||
|
||||
local tr = {}
|
||||
local trace = {
|
||||
output = tr,
|
||||
filter = widgetEntities,
|
||||
whitelist = true,
|
||||
}
|
||||
|
||||
local function UpdateHovered( pl )
|
||||
|
||||
if ( !IsValid( pl ) ) then return end
|
||||
|
||||
if ( !pl:Alive() ) then
|
||||
pl:SetHoveredWidget( NULL )
|
||||
return
|
||||
end
|
||||
|
||||
local eyePos = pl:EyePos()
|
||||
local aimVector = pl:GetAimVector()
|
||||
aimVector:Mul( 256 )
|
||||
aimVector:Add( eyePos )
|
||||
|
||||
trace.start = eyePos
|
||||
trace.endpos = aimVector
|
||||
|
||||
-- debugoverlay.Line( trace.start, trace.endpos, 0.5 )
|
||||
|
||||
widgets.Tracing = true
|
||||
util.TraceLine( trace )
|
||||
widgets.Tracing = false
|
||||
|
||||
if ( !IsValid( tr.Entity ) || tr.Entity:IsWorld() ) then
|
||||
pl:SetHoveredWidget( NULL )
|
||||
return
|
||||
end
|
||||
|
||||
-- debugoverlay.Cross( tr.HitPos, 1, 60 )
|
||||
|
||||
pl:SetHoveredWidget( tr.Entity )
|
||||
pl.WidgetHitPos = tr.HitPos
|
||||
|
||||
return tr.Entity
|
||||
|
||||
end
|
||||
|
||||
local function UpdateButton( pl, mv, btn, mousebutton, hvr, prs )
|
||||
|
||||
local now = mv:KeyDown( btn )
|
||||
local was = mv:KeyWasDown( btn )
|
||||
|
||||
if ( now && !was && IsValid( hvr ) ) then
|
||||
hvr:OnPress( pl, mousebutton, mv )
|
||||
end
|
||||
|
||||
if ( !now && was && IsValid( prs ) ) then
|
||||
prs:OnRelease( pl, mousebutton, mv )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- The idea here is to have exactly the same
|
||||
-- behaviour on the client as the server.
|
||||
--
|
||||
function widgets.PlayerTick( pl, mv )
|
||||
|
||||
local hvr = UpdateHovered( pl )
|
||||
local prs = pl:GetPressedWidget()
|
||||
|
||||
UpdateButton( pl, mv, IN_ATTACK, 1, hvr, prs )
|
||||
UpdateButton( pl, mv, IN_ATTACK2, 2, hvr, prs )
|
||||
|
||||
if ( IsValid( prs ) ) then
|
||||
prs:PressedThinkInternal( pl, mv )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
---
|
||||
--- Render the widgets!
|
||||
---
|
||||
|
||||
-- Function stub for backwards compatibility
|
||||
function widgets.RenderMe( ent )
|
||||
-- We do this solely for backwards compatibility
|
||||
ent._WantsWidgetRender = true
|
||||
end
|
||||
|
||||
local function RenderWidgets()
|
||||
|
||||
local prs = LocalPlayer():GetPressedWidget()
|
||||
local prsValid = IsValid( prs )
|
||||
|
||||
cam.Start3D( EyePos(), EyeAngles() )
|
||||
|
||||
for _, v in ipairs( widgetEntities ) do
|
||||
|
||||
--
|
||||
-- The pressed widget gets to decide what should draw
|
||||
--
|
||||
if ( prsValid && !prs:PressedShouldDraw( v ) ) then continue end
|
||||
if ( !v._WantsWidgetRender ) then continue end
|
||||
|
||||
v:OverlayRender()
|
||||
v._WantsWidgetRender = nil
|
||||
|
||||
end
|
||||
|
||||
cam.End3D()
|
||||
|
||||
end
|
||||
|
||||
hook.Add( "OnEntityCreated", "CreateWidgets", function( ent )
|
||||
|
||||
timer.Simple( 0.001, function()
|
||||
|
||||
if ( !ent:IsWidget() ) then return end
|
||||
|
||||
table.insert( widgetEntities, ent )
|
||||
hook.Add( "PlayerTick", "TickWidgets", widgets.PlayerTick )
|
||||
|
||||
if ( CLIENT ) then
|
||||
hook.Add( "PostDrawEffects", "RenderWidgets", RenderWidgets )
|
||||
end
|
||||
|
||||
end )
|
||||
|
||||
end )
|
||||
|
||||
hook.Add( "EntityRemoved", "RemoveWidgets", function( ent )
|
||||
|
||||
if ( !ent:IsWidget() ) then return end
|
||||
|
||||
for k, v in next, widgetEntities do
|
||||
if ( v == ent ) then
|
||||
table.remove( widgetEntities, k )
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if ( #widgetEntities == 0 ) then
|
||||
hook.Remove( "PlayerTick", "TickWidgets" )
|
||||
|
||||
if ( CLIENT ) then
|
||||
hook.Remove( "PostDrawEffects", "RenderWidgets" )
|
||||
end
|
||||
end
|
||||
|
||||
end )
|
||||
|
||||
local ENTITY = FindMetaTable( "Entity" )
|
||||
|
||||
function ENTITY:IsWidget()
|
||||
return self.Widget
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user