Initial commit

This commit is contained in:
pavetr
2026-03-15 14:54:49 +03:00
commit 64f8029c06
4027 changed files with 254888 additions and 0 deletions

View 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

View 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

View 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

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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 )

View 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

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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 )

View 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

View 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 )
--]]

View 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" )

View 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

View 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 = {
["&"] = "&amp;",
["<"] = "&lt;",
[">"] = "&gt;"
}, {
["&amp;"] = "&",
["&lt;"] = "<",
["&gt;"] = ">"
}
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

View 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

View 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 )

View 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" )

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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 } )

View 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

View 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

View 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

View 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