This commit is contained in:
lifestorm
2024-08-04 23:12:27 +03:00
parent 8064ba84d8
commit 9c918c46e5
7081 changed files with 2173485 additions and 14 deletions

View File

@@ -0,0 +1,72 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
CreateClientConVar( "cl_showhints", "1", true, false, "Whether to display popup hints." )
-- A list of hints we've already done so we don't repeat ourselves`
local ProcessedHints = {}
--
-- Throw's a Hint to the screen
--
local function ThrowHint( name )
local show = GetConVarNumber( "cl_showhints" )
if ( show == 0 ) then return end
if ( engine.IsPlayingDemo() ) then return end
local text = language.GetPhrase( "Hint_" .. name )
local s, e, group = string.find( text, "%%([^%%]+)%%" )
while ( s ) do
local key = input.LookupBinding( group )
if ( !key ) then key = "<NOT BOUND>" end
text = string.gsub( text, "%%([^%%]+)%%", "'" .. key:upper() .. "'" )
s, e, group = string.find( text, "%%([^%%]+)%%" )
end
GAMEMODE:AddNotify( text, NOTIFY_HINT, 20 )
surface.PlaySound( "ambient/water/drip" .. math.random( 1, 4 ) .. ".wav" )
end
--
-- Adds a hint to the queue
--
function GM:AddHint( name, delay )
if ( ProcessedHints[ name ] ) then return end
timer.Create( "HintSystem_" .. name, delay, 1, function() ThrowHint( name ) end )
ProcessedHints[ name ] = true
end
--
-- Removes a hint from the queue
--
function GM:SuppressHint( name )
timer.Remove( "HintSystem_" .. name )
end
-- Show opening menu hint if they haven't opened the menu within 30 seconds
GM:AddHint( "OpeningMenu", 30 )
-- Tell them how to turn the hints off after 1 minute
GM:AddHint( "Annoy1", 5 )
GM:AddHint( "Annoy2", 7 )

View File

@@ -0,0 +1,188 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
--[[---------------------------------------------------------
Sandbox Gamemode
This is GMod's default gamemode
-----------------------------------------------------------]]
include( "shared.lua" )
include( "cl_spawnmenu.lua" )
include( "cl_notice.lua" )
include( "cl_hints.lua" )
include( "cl_worldtips.lua" )
include( "cl_search_models.lua" )
include( "gui/IconEditor.lua" )
--
-- Make BaseClass available
--
DEFINE_BASECLASS( "gamemode_base" )
local physgun_halo = CreateConVar( "physgun_halo", "1", { FCVAR_ARCHIVE }, "Draw the Physics Gun grab effect?" )
function GM:Initialize()
BaseClass.Initialize( self )
end
function GM:LimitHit( name )
local str = "#SBoxLimit_" .. name
local translated = language.GetPhrase( str )
if ( str == translated ) then
-- No translation available, apply our own
translated = string.format( language.GetPhrase( "hint.hitXlimit" ), language.GetPhrase( name ) )
end
self:AddNotify( translated, NOTIFY_ERROR, 6 )
surface.PlaySound( "buttons/button10.wav" )
end
function GM:OnUndo( name, strCustomString )
local text = strCustomString
if ( !text ) then
local strId = "#Undone_" .. name
text = language.GetPhrase( strId )
if ( strId == text ) then
-- No translation available, generate our own
text = string.format( language.GetPhrase( "hint.undoneX" ), language.GetPhrase( name ) )
end
end
-- This is a hack for SWEPs, Tools, etc, that already have hardcoded English only translations
-- TODO: Do this for non English languages only
local strMatch = string.match( text, "^Undone (.*)$" )
if ( strMatch ) then
text = string.format( language.GetPhrase( "hint.undoneX" ), language.GetPhrase( strMatch ) )
end
self:AddNotify( text, NOTIFY_UNDO, 2 )
-- Find a better sound :X
surface.PlaySound( "buttons/button15.wav" )
end
function GM:OnCleanup( name )
local str = "#Cleaned_" .. name
local translated = language.GetPhrase( str )
if ( str == translated ) then
-- No translation available, apply our own
translated = string.format( language.GetPhrase( "hint.cleanedX" ), language.GetPhrase( name ) )
end
self:AddNotify( translated, NOTIFY_CLEANUP, 5 )
-- Find a better sound :X
surface.PlaySound( "buttons/button15.wav" )
end
function GM:UnfrozeObjects( num )
self:AddNotify( string.format( language.GetPhrase( "hint.unfrozeX" ), num ), NOTIFY_GENERIC, 3 )
-- Find a better sound :X
surface.PlaySound( "npc/roller/mine/rmine_chirp_answer1.wav" )
end
function GM:HUDPaint()
self:PaintWorldTips()
-- Draw all of the default stuff
BaseClass.HUDPaint( self )
self:PaintNotes()
end
--[[---------------------------------------------------------
Draws on top of VGUI..
-----------------------------------------------------------]]
function GM:PostRenderVGUI()
BaseClass.PostRenderVGUI( self )
end
local PhysgunHalos = {}
--[[---------------------------------------------------------
Name: gamemode:DrawPhysgunBeam()
Desc: Return false to override completely
-----------------------------------------------------------]]
function GM:DrawPhysgunBeam( ply, weapon, bOn, target, boneid, pos )
if ( physgun_halo:GetInt() == 0 ) then return true end
if ( IsValid( target ) ) then
PhysgunHalos[ ply ] = target
end
return true
end
hook.Add( "PreDrawHalos", "AddPhysgunHalos", function()
if ( !PhysgunHalos || table.IsEmpty( PhysgunHalos ) ) then return end
for k, v in pairs( PhysgunHalos ) do
if ( !IsValid( k ) ) then continue end
local size = math.random( 1, 2 )
local colr = k:GetWeaponColor() + VectorRand() * 0.3
halo.Add( PhysgunHalos, Color( colr.x * 255, colr.y * 255, colr.z * 255 ), size, size, 1, true, false )
end
PhysgunHalos = {}
end )
--[[---------------------------------------------------------
Name: gamemode:NetworkEntityCreated()
Desc: Entity is created over the network
-----------------------------------------------------------]]
function GM:NetworkEntityCreated( ent )
--
-- If the entity wants to use a spawn effect
-- then create a propspawn effect if the entity was
-- created within the last second (this function gets called
-- on every entity when joining a server)
--
if ( ent:GetSpawnEffect() && ent:GetCreationTime() > ( CurTime() - 1.0 ) ) then
local ed = EffectData()
ed:SetOrigin( ent:GetPos() )
ed:SetEntity( ent )
util.Effect( "propspawn", ed, true, true )
end
end

View File

@@ -0,0 +1,19 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
function GM:AddNotify( str, type, length )
notification.AddLegacy( str, type, length )
end
function GM:PaintNotes()
end

View File

@@ -0,0 +1,174 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local sbox_search_maxresults = CreateClientConVar( "sbox_search_maxresults", "1024", true, false, "The maximum amount of results the spawnmenu search should show. Model amount limited to 1/2 of this value, entities are limited to 1/4", 1024 )
local totalCalls = 0
local expectedCalls = 1
local queuedSearch = {}
local function GetAllFiles( tab, folder, extension, path )
totalCalls = totalCalls + 1
local files, folders = file.Find( folder .. "*", path )
if ( !files ) then
MsgN( "Warning! Ignoring '" .. folder .. "' because we cannot search in it!" )
return
end
for k, v in ipairs( files ) do
if ( v:EndsWith( extension ) ) then
table.insert( tab, ( folder .. v ):lower() )
end
end
for k, v in ipairs( folders ) do
expectedCalls = expectedCalls + 1
table.insert( queuedSearch, { tab, folder .. v .. "/", extension, path } )
end
notification.AddProgress( "SandboxSearchIndexing", "#spawnmenu.searchindex", totalCalls / expectedCalls )
if ( totalCalls >= expectedCalls ) then notification.Kill( "SandboxSearchIndexing" ) end
end
hook.Add( "Think", "sandbox_queued_search", function()
if ( #queuedSearch < 1 ) then return end
local call = queuedSearch[ 1 ]
GetAllFiles( unpack( call ) )
table.remove( queuedSearch, 1 )
if ( !timer.Exists( "search_models_update" ) || #queuedSearch < 1 ) then
timer.Create( "search_models_update", 1, 1, function() hook.Run( "SearchUpdate" ) end )
end
end )
--
-- Model Search
--
local model_list = nil
search.AddProvider( function( str )
if ( model_list == nil ) then
model_list = {}
GetAllFiles( model_list, "models/", ".mdl", "GAME" )
end
local models = {}
for k, v in ipairs( model_list ) do
-- Don't search in the models/ and .mdl bit of every model, because every model has this bit, unless they are looking for direct model path
local modelpath = v
if ( modelpath:StartsWith( "models/" ) && modelpath:EndsWith( ".mdl" ) && !str:EndsWith( ".mdl" ) ) then modelpath = modelpath:sub( 8, modelpath:len() - 4 ) end
if ( modelpath:find( str, nil, true ) ) then
if ( IsUselessModel( v ) ) then continue end
local entry = {
text = v:GetFileFromFilename(),
func = function() RunConsoleCommand( "gm_spawn", v ) end,
icon = spawnmenu.CreateContentIcon( "model", g_SpawnMenu.SearchPropPanel, { model = v } ),
words = { v }
}
table.insert( models, entry )
end
if ( #models >= sbox_search_maxresults:GetInt() / 2 ) then break end
end
return models
end, "props" )
hook.Add( "GameContentChanged", "ResetModelSearchCache", function()
-- Addons got remounted, reset the model search cache
model_list = nil
-- Reset any ongoing search process
totalCalls = 0
expectedCalls = 1
queuedSearch = {}
end )
--
-- Entity, vehicles
--
local function AddSearchProvider( listname, ctype, stype )
search.AddProvider( function( str )
local results = {}
local entities = {}
for k, v in pairs( list.Get( listname ) ) do
if ( listname == "Weapon" && !v.Spawnable ) then continue end
v.ClassName = k
v.PrintName = v.PrintName or v.Name
v.ScriptedEntityType = ctype
table.insert( entities, v )
end
for k, v in ipairs( entities ) do
local name = v.PrintName
local name_c = v.ClassName
if ( !isstring( name ) && !isstring( name_c ) ) then continue end
if ( ( isstring( name ) && name:lower():find( str, nil, true ) ) || ( isstring( name_c ) && name_c:lower():find( str, nil, true ) ) ) then
local entry = {
text = v.PrintName or v.ClassName,
icon = spawnmenu.CreateContentIcon( v.ScriptedEntityType or "entity", nil, {
nicename = v.PrintName or v.ClassName,
spawnname = v.ClassName,
material = "entities/" .. v.ClassName .. ".png",
admin = v.AdminOnly
} ),
words = { v }
}
table.insert( results, entry )
end
if ( #results >= sbox_search_maxresults:GetInt() / 4 ) then break end
end
table.SortByMember( results, "text", true )
return results
end, stype )
end
AddSearchProvider( "SpawnableEntities", "entity", "entities" )
AddSearchProvider( "Vehicles", "vehicle", "vehicles" )
AddSearchProvider( "NPC", "npc", "npcs" )
AddSearchProvider( "Weapon", "weapon", "weapons" )

View File

@@ -0,0 +1,146 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
include( "spawnmenu/spawnmenu.lua" )
--[[---------------------------------------------------------
If false is returned then the spawn menu is never created.
This saves load times if your mod doesn't actually use the
spawn menu for any reason.
-----------------------------------------------------------]]
function GM:SpawnMenuEnabled()
return true
end
--[[---------------------------------------------------------
Called when spawnmenu is trying to be opened.
Return false to dissallow it.
-----------------------------------------------------------]]
function GM:SpawnMenuOpen()
return true
end
function GM:SpawnMenuOpened()
self:SuppressHint( "OpeningMenu" )
self:AddHint( "OpeningContext", 20 )
self:AddHint( "EditingSpawnlists", 5 )
end
function GM:SpawnMenuClosed()
end
function GM:SpawnMenuCreated(spawnmenu)
end
--[[---------------------------------------------------------
If false is returned then the context menu is never created.
This saves load times if your mod doesn't actually use the
context menu for any reason.
-----------------------------------------------------------]]
function GM:ContextMenuEnabled()
return true
end
--[[---------------------------------------------------------
Called when context menu is trying to be opened.
Return false to dissallow it.
-----------------------------------------------------------]]
function GM:ContextMenuOpen()
return true
end
function GM:ContextMenuOpened()
self:SuppressHint( "OpeningContext" )
self:AddHint( "ContextClick", 20 )
end
function GM:ContextMenuClosed()
end
function GM:ContextMenuCreated()
end
--[[---------------------------------------------------------
Backwards compatibility. Do Not Use!!!
-----------------------------------------------------------]]
function GM:GetSpawnmenuTools( name )
return spawnmenu.GetToolMenu( name )
end
--[[---------------------------------------------------------
Backwards compatibility. Do Not Use!!!
-----------------------------------------------------------]]
function GM:AddSTOOL( category, itemname, text, command, controls, cpanelfunction )
self:AddToolMenuOption( "Main", category, itemname, text, command, controls, cpanelfunction )
end
function GM:PreReloadToolsMenu()
end
--[[---------------------------------------------------------
Don't hook or override this function.
Hook AddToolMenuTabs instead!
-----------------------------------------------------------]]
function GM:AddGamemodeToolMenuTabs()
-- This is named like this to force it to be the first tab
spawnmenu.AddToolTab( "Main", "#spawnmenu.tools_tab", "icon16/wrench.png" )
spawnmenu.AddToolTab( "Utilities", "#spawnmenu.utilities_tab", "icon16/page_white_wrench.png" )
end
--[[---------------------------------------------------------
Add your custom tabs here.
-----------------------------------------------------------]]
function GM:AddToolMenuTabs()
-- Hook me!
end
--[[---------------------------------------------------------
Add categories to your tabs
-----------------------------------------------------------]]
function GM:AddGamemodeToolMenuCategories()
spawnmenu.AddToolCategory( "Main", "Constraints", "#spawnmenu.tools.constraints" )
spawnmenu.AddToolCategory( "Main", "Construction", "#spawnmenu.tools.construction" )
spawnmenu.AddToolCategory( "Main", "Poser", "#spawnmenu.tools.posing" )
spawnmenu.AddToolCategory( "Main", "Render", "#spawnmenu.tools.render" )
end
--[[---------------------------------------------------------
Add categories to your tabs
-----------------------------------------------------------]]
function GM:AddToolMenuCategories()
-- Hook this function to add custom stuff
end
function GM:PopulateToolMenu()
end
function GM:PostReloadToolsMenu()
end
--[[---------------------------------------------------------
Add categories to your tabs
-----------------------------------------------------------]]
function GM:PopulatePropMenu()
-- This function makes the engine load the spawn menu text files.
-- We call it here so that any gamemodes not using the default
-- spawn menu can totally not call it.
spawnmenu.PopulateFromEngineTextFiles()
end

View File

@@ -0,0 +1,103 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
surface.CreateFont( "GModWorldtip",
{
font = "Helvetica",
size = 20,
weight = 700
})
local cl_drawworldtooltips = CreateConVar( "cl_drawworldtooltips", "1", { FCVAR_ARCHIVE }, "Whether tooltips should draw when looking at certain Sandbox entities." )
local WorldTip = nil
local TipColor = Color( 250, 250, 200, 255 )
--
-- Adds a hint to the queue
--
function AddWorldTip( unused1, text, unused2, pos, ent )
WorldTip = {}
WorldTip.dietime = SysTime() + 0.05
WorldTip.text = text
WorldTip.pos = pos
WorldTip.ent = ent
end
local function DrawWorldTip( tip )
if ( IsValid( tip.ent ) ) then
tip.pos = tip.ent:GetPos()
end
local pos = tip.pos:ToScreen()
local black = Color( 0, 0, 0, 255 )
local tipcol = Color( TipColor.r, TipColor.g, TipColor.b, 255 )
local x = 0
local y = 0
local padding = 10
local offset = 50
surface.SetFont( "GModWorldtip" )
local w, h = surface.GetTextSize( tip.text )
x = pos.x - w
y = pos.y - h
x = x - offset
y = y - offset
draw.RoundedBox( 8, x-padding-2, y-padding-2, w+padding*2+4, h+padding*2+4, black )
local verts = {}
verts[1] = { x=x+w/1.5-2, y=y+h+2 }
verts[2] = { x=x+w+2, y=y+h/2-1 }
verts[3] = { x=pos.x-offset/2+2, y=pos.y-offset/2+2 }
draw.NoTexture()
surface.SetDrawColor( 0, 0, 0, tipcol.a )
surface.DrawPoly( verts )
draw.RoundedBox( 8, x-padding, y-padding, w+padding*2, h+padding*2, tipcol )
local verts = {}
verts[1] = { x=x+w/1.5, y=y+h }
verts[2] = { x=x+w, y=y+h/2 }
verts[3] = { x=pos.x-offset/2, y=pos.y-offset/2 }
draw.NoTexture()
surface.SetDrawColor( tipcol.r, tipcol.g, tipcol.b, tipcol.a )
surface.DrawPoly( verts )
draw.DrawText( tip.text, "GModWorldtip", x + w/2, y, black, TEXT_ALIGN_CENTER )
end
function GM:PaintWorldTips()
if ( !cl_drawworldtooltips:GetBool() ) then return end
if ( WorldTip && WorldTip.dietime > SysTime() ) then
DrawWorldTip( WorldTip )
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,307 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
AddCSLuaFile()
local default_animations = { "idle_all_01", "menu_walk" }
list.Set( "DesktopWindows", "PlayerEditor", {
title = "#smwidget.playermodel",
icon = "icon64/playermodel.png",
width = 960,
height = 700,
onewindow = true,
init = function( icon, window )
window:SetTitle( "#smwidget.playermodel_title" )
window:SetSize( math.min( ScrW() - 16, window:GetWide() ), math.min( ScrH() - 16, window:GetTall() ) )
window:SetSizable( true )
window:SetMinWidth( window:GetWide() )
window:SetMinHeight( window:GetTall() )
window:Center()
local mdl = window:Add( "DModelPanel" )
mdl:Dock( FILL )
mdl:SetFOV( 36 )
mdl:SetCamPos( vector_origin )
mdl:SetDirectionalLight( BOX_RIGHT, Color( 255, 160, 80, 255 ) )
mdl:SetDirectionalLight( BOX_LEFT, Color( 80, 160, 255, 255 ) )
mdl:SetAmbientLight( Vector( -64, -64, -64 ) )
mdl:SetAnimated( true )
mdl.Angles = angle_zero
mdl:SetLookAt( Vector( -100, 0, -22 ) )
local sheet = window:Add( "DPropertySheet" )
sheet:Dock( RIGHT )
sheet:SetSize( 430, 0 )
local modelListPnl = window:Add( "DPanel" )
modelListPnl:DockPadding( 8, 8, 8, 8 )
local SearchBar = modelListPnl:Add( "DTextEntry" )
SearchBar:Dock( TOP )
SearchBar:DockMargin( 0, 0, 0, 8 )
SearchBar:SetUpdateOnType( true )
SearchBar:SetPlaceholderText( "#spawnmenu.quick_filter" )
local PanelSelect = modelListPnl:Add( "DPanelSelect" )
PanelSelect:Dock( FILL )
for name, model in SortedPairs( player_manager.AllValidModels() ) do
local icon = vgui.Create( "SpawnIcon" )
icon:SetModel( model )
icon:SetSize( 64, 64 )
icon:SetTooltip( name )
icon.playermodel = name
icon.model_path = model
icon.OpenMenu = function( button )
local menu = DermaMenu()
menu:AddOption( "#spawnmenu.menu.copy", function() SetClipboardText( model ) end ):SetIcon( "icon16/page_copy.png" )
menu:Open()
end
PanelSelect:AddPanel( icon, { cl_playermodel = name } )
end
SearchBar.OnValueChange = function( s, str )
for id, pnl in pairs( PanelSelect:GetItems() ) do
if ( !pnl.playermodel:find( str, 1, true ) && !pnl.model_path:find( str, 1, true ) ) then
pnl:SetVisible( false )
else
pnl:SetVisible( true )
end
end
PanelSelect:InvalidateLayout()
end
sheet:AddSheet( "#smwidget.model", modelListPnl, "icon16/user.png" )
local controls = window:Add( "DPanel" )
controls:DockPadding( 8, 8, 8, 8 )
local lbl = controls:Add( "DLabel" )
lbl:SetText( "#smwidget.color_plr" )
lbl:SetTextColor( Color( 0, 0, 0, 255 ) )
lbl:Dock( TOP )
local plycol = controls:Add( "DColorMixer" )
plycol:SetAlphaBar( false )
plycol:SetPalette( false )
plycol:Dock( TOP )
plycol:SetSize( 200, math.min( window:GetTall() / 3, 260 ) )
local lbl = controls:Add( "DLabel" )
lbl:SetText( "#smwidget.color_wep" )
lbl:SetTextColor( Color( 0, 0, 0, 255 ) )
lbl:DockMargin( 0, 32, 0, 0 )
lbl:Dock( TOP )
local wepcol = controls:Add( "DColorMixer" )
wepcol:SetAlphaBar( false )
wepcol:SetPalette( false )
wepcol:Dock( TOP )
wepcol:SetSize( 200, math.min( window:GetTall() / 3, 260 ) )
wepcol:SetVector( Vector( GetConVarString( "cl_weaponcolor" ) ) )
sheet:AddSheet( "#smwidget.colors", controls, "icon16/color_wheel.png" )
local bdcontrols = window:Add( "DPanel" )
bdcontrols:DockPadding( 8, 8, 8, 8 )
local bdcontrolspanel = bdcontrols:Add( "DPanelList" )
bdcontrolspanel:EnableVerticalScrollbar()
bdcontrolspanel:Dock( FILL )
local bgtab = sheet:AddSheet( "#smwidget.bodygroups", bdcontrols, "icon16/cog.png" )
-- Helper functions
local function PlayPreviewAnimation( panel, playermodel )
if ( !panel or !IsValid( panel.Entity ) ) then return end
local anims = list.Get( "PlayerOptionsAnimations" )
local anim = default_animations[ math.random( 1, #default_animations ) ]
if ( anims[ playermodel ] ) then
anims = anims[ playermodel ]
anim = anims[ math.random( 1, #anims ) ]
end
local iSeq = panel.Entity:LookupSequence( anim )
if ( iSeq > 0 ) then panel.Entity:ResetSequence( iSeq ) end
end
-- Updating
local function UpdateBodyGroups( pnl, val )
if ( pnl.type == "bgroup" ) then
mdl.Entity:SetBodygroup( pnl.typenum, math.Round( val ) )
local str = string.Explode( " ", GetConVarString( "cl_playerbodygroups" ) )
if ( #str < pnl.typenum + 1 ) then for i = 1, pnl.typenum + 1 do str[ i ] = str[ i ] or 0 end end
str[ pnl.typenum + 1 ] = math.Round( val )
RunConsoleCommand( "cl_playerbodygroups", table.concat( str, " " ) )
elseif ( pnl.type == "skin" ) then
mdl.Entity:SetSkin( math.Round( val ) )
RunConsoleCommand( "cl_playerskin", math.Round( val ) )
end
end
local function RebuildBodygroupTab()
bdcontrolspanel:Clear()
bgtab.Tab:SetVisible( false )
local nskins = mdl.Entity:SkinCount() - 1
if ( nskins > 0 ) then
local skins = vgui.Create( "DNumSlider" )
skins:Dock( TOP )
skins:SetText( "Skin" )
skins:SetDark( true )
skins:SetTall( 50 )
skins:SetDecimals( 0 )
skins:SetMax( nskins )
skins:SetValue( GetConVarNumber( "cl_playerskin" ) )
skins.type = "skin"
skins.OnValueChanged = UpdateBodyGroups
bdcontrolspanel:AddItem( skins )
mdl.Entity:SetSkin( GetConVarNumber( "cl_playerskin" ) )
bgtab.Tab:SetVisible( true )
end
local groups = string.Explode( " ", GetConVarString( "cl_playerbodygroups" ) )
for k = 0, mdl.Entity:GetNumBodyGroups() - 1 do
if ( mdl.Entity:GetBodygroupCount( k ) <= 1 ) then continue end
local bgroup = vgui.Create( "DNumSlider" )
bgroup:Dock( TOP )
bgroup:SetText( string.NiceName( mdl.Entity:GetBodygroupName( k ) ) )
bgroup:SetDark( true )
bgroup:SetTall( 50 )
bgroup:SetDecimals( 0 )
bgroup.type = "bgroup"
bgroup.typenum = k
bgroup:SetMax( mdl.Entity:GetBodygroupCount( k ) - 1 )
bgroup:SetValue( groups[ k + 1 ] or 0 )
bgroup.OnValueChanged = UpdateBodyGroups
bdcontrolspanel:AddItem( bgroup )
mdl.Entity:SetBodygroup( k, groups[ k + 1 ] or 0 )
bgtab.Tab:SetVisible( true )
end
sheet.tabScroller:InvalidateLayout()
end
local function UpdateFromConvars()
local model = LocalPlayer():GetInfo( "cl_playermodel" )
local modelname = player_manager.TranslatePlayerModel( model )
util.PrecacheModel( modelname )
mdl:SetModel( modelname )
mdl.Entity.GetPlayerColor = function() return Vector( GetConVarString( "cl_playercolor" ) ) end
mdl.Entity:SetPos( Vector( -100, 0, -61 ) )
plycol:SetVector( Vector( GetConVarString( "cl_playercolor" ) ) )
wepcol:SetVector( Vector( GetConVarString( "cl_weaponcolor" ) ) )
PlayPreviewAnimation( mdl, model )
RebuildBodygroupTab()
end
local function UpdateFromControls()
RunConsoleCommand( "cl_playercolor", tostring( plycol:GetVector() ) )
RunConsoleCommand( "cl_weaponcolor", tostring( wepcol:GetVector() ) )
end
plycol.ValueChanged = UpdateFromControls
wepcol.ValueChanged = UpdateFromControls
UpdateFromConvars()
function PanelSelect:OnActivePanelChanged( old, new )
if ( old != new ) then -- Only reset if we changed the model
RunConsoleCommand( "cl_playerbodygroups", "0" )
RunConsoleCommand( "cl_playerskin", "0" )
end
timer.Simple( 0.1, function() UpdateFromConvars() end )
end
-- Hold to rotate
function mdl:DragMousePress()
self.PressX, self.PressY = input.GetCursorPos()
self.Pressed = true
end
function mdl:DragMouseRelease() self.Pressed = false end
function mdl:LayoutEntity( ent )
if ( self.bAnimated ) then self:RunAnimation() end
if ( self.Pressed ) then
local mx, my = input.GetCursorPos()
self.Angles = self.Angles - Angle( 0, ( ( self.PressX or mx ) - mx ) / 2, 0 )
self.PressX, self.PressY = mx, my
end
ent:SetAngles( self.Angles )
end
end
} )
list.Set( "PlayerOptionsAnimations", "gman", { "menu_gman" } )
list.Set( "PlayerOptionsAnimations", "hostage01", { "idle_all_scared" } )
list.Set( "PlayerOptionsAnimations", "hostage02", { "idle_all_scared" } )
list.Set( "PlayerOptionsAnimations", "hostage03", { "idle_all_scared" } )
list.Set( "PlayerOptionsAnimations", "hostage04", { "idle_all_scared" } )
list.Set( "PlayerOptionsAnimations", "zombine", { "menu_zombie_01" } )
list.Set( "PlayerOptionsAnimations", "corpse", { "menu_zombie_01" } )
list.Set( "PlayerOptionsAnimations", "zombiefast", { "menu_zombie_01" } )
list.Set( "PlayerOptionsAnimations", "zombie", { "menu_zombie_01" } )
list.Set( "PlayerOptionsAnimations", "skeleton", { "menu_zombie_01" } )
list.Set( "PlayerOptionsAnimations", "combine", { "menu_combine" } )
list.Set( "PlayerOptionsAnimations", "combineprison", { "menu_combine" } )
list.Set( "PlayerOptionsAnimations", "combineelite", { "menu_combine" } )
list.Set( "PlayerOptionsAnimations", "police", { "menu_combine" } )
list.Set( "PlayerOptionsAnimations", "policefem", { "menu_combine" } )
list.Set( "PlayerOptionsAnimations", "css_arctic", { "pose_standing_02", "idle_fist" } )
list.Set( "PlayerOptionsAnimations", "css_gasmask", { "pose_standing_02", "idle_fist" } )
list.Set( "PlayerOptionsAnimations", "css_guerilla", { "pose_standing_02", "idle_fist" } )
list.Set( "PlayerOptionsAnimations", "css_leet", { "pose_standing_02", "idle_fist" } )
list.Set( "PlayerOptionsAnimations", "css_phoenix", { "pose_standing_02", "idle_fist" } )
list.Set( "PlayerOptionsAnimations", "css_riot", { "pose_standing_02", "idle_fist" } )
list.Set( "PlayerOptionsAnimations", "css_swat", { "pose_standing_02", "idle_fist" } )
list.Set( "PlayerOptionsAnimations", "css_urban", { "pose_standing_02", "idle_fist" } )

View File

@@ -0,0 +1,654 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local PANEL = {}
AccessorFunc( PANEL, "m_strModel", "Model" )
AccessorFunc( PANEL, "m_pOrigin", "Origin" )
AccessorFunc( PANEL, "m_bCustomIcon", "CustomIcon" )
function PANEL:Init()
self:SetSize( 762, 502 )
self:SetTitle( "#smwidget.icon_editor" )
local left = self:Add( "Panel" )
left:Dock( LEFT )
left:SetWide( 400 )
self.LeftPanel = left
local bg = left:Add( "DPanel" )
bg:Dock( FILL )
bg:DockMargin( 0, 0, 0, 4 )
bg.Paint = function( s, w, h ) draw.RoundedBox( 0, 0, 0, w, h, Color( 0, 0, 0, 128 ) ) end
self.SpawnIcon = bg:Add( "SpawnIcon" )
--self.SpawnIcon.DoClick = function() self:RenderIcon() end
self.ModelPanel = bg:Add( "DAdjustableModelPanel" )
self.ModelPanel:Dock( FILL )
self.ModelPanel.FarZ = 32768
local mat_wireframe = Material( "models/wireframe" )
function self.ModelPanel.PostDrawModel( mdlpnl, ent )
if ( self.ShowOriginPnl:GetChecked() ) then
render.DrawLine( vector_origin, Vector( 0, 0, 100 ), Color( 0, 0, 255 ) )
render.DrawLine( vector_origin, Vector( 0, 100, 0 ), Color( 0, 255, 0 ) )
render.DrawLine( vector_origin, Vector( 100, 0, 0 ), Color( 255, 0, 0 ) )
end
if ( self.ShowBBoxPnl:GetChecked() ) then
local mins, maxs = ent:GetRenderBounds()
local scale = 1
mat_wireframe:SetVector( "$color", Vector( 1, 1, 1 ) )
render.SetMaterial( mat_wireframe )
render.DrawBox( ent:GetPos(), ent:GetAngles(), mins * scale, maxs * scale )
end
end
local controls = left:Add( "Panel" )
controls:SetTall( 64 )
controls:Dock( BOTTOM )
local controls_anim = controls:Add( "Panel" )
controls_anim:SetTall( 20 )
controls_anim:Dock( TOP )
controls_anim:DockMargin( 0, 0, 0, 4 )
controls_anim:MoveToBack()
self.AnimTrack = controls_anim:Add( "DSlider" )
self.AnimTrack:Dock( FILL )
self.AnimTrack:SetNotches( 100 )
self.AnimTrack:SetTrapInside( true )
self.AnimTrack:SetLockY( 0.5 )
self.AnimPause = controls_anim:Add( "DImageButton" )
self.AnimPause:SetImage( "icon16/control_pause_blue.png" )
self.AnimPause:SetStretchToFit( false )
self.AnimPause:SetPaintBackground( true )
self.AnimPause:SetIsToggle( true )
self.AnimPause:SetToggle( false )
self.AnimPause:Dock( LEFT )
self.AnimPause:SetWide( 32 )
local BestGuess = controls:Add( "DImageButton" )
BestGuess:SetImage( "icon32/wand.png" )
BestGuess:SetStretchToFit( false )
BestGuess:SetPaintBackground( true )
BestGuess.DoClick = function() self:BestGuessLayout() end
BestGuess:Dock( LEFT )
BestGuess:DockMargin( 0, 0, 0, 0 )
BestGuess:SetWide( 50 )
BestGuess:SetTooltip( "Best Guess" )
local FullFrontal = controls:Add( "DImageButton" )
FullFrontal:SetImage( "icon32/hand_point_090.png" )
FullFrontal:SetStretchToFit( false )
FullFrontal:SetPaintBackground( true )
FullFrontal.DoClick = function() self:FullFrontalLayout() end
FullFrontal:Dock( LEFT )
FullFrontal:DockMargin( 2, 0, 0, 0 )
FullFrontal:SetWide( 50 )
FullFrontal:SetTooltip( "Front" )
local Above = controls:Add( "DImageButton" )
Above:SetImage( "icon32/hand_property.png" )
Above:SetStretchToFit( false )
Above:SetPaintBackground( true )
Above.DoClick = function() self:AboveLayout() end
Above:Dock( LEFT )
Above:DockMargin( 2, 0, 0, 0 )
Above:SetWide( 50 )
Above:SetTooltip( "Above" )
local Right = controls:Add( "DImageButton" )
Right:SetImage( "icon32/hand_point_180.png" )
Right:SetStretchToFit( false )
Right:SetPaintBackground( true )
Right.DoClick = function() self:RightLayout() end
Right:Dock( LEFT )
Right:DockMargin( 2, 0, 0, 0 )
Right:SetWide( 50 )
Right:SetTooltip( "Right" )
local Origin = controls:Add( "DImageButton" )
Origin:SetImage( "icon32/hand_point_090.png" )
Origin:SetStretchToFit( false )
Origin:SetPaintBackground( true )
Origin.DoClick = function() self:OriginLayout() end
Origin:Dock( LEFT )
Origin:DockMargin( 2, 0, 0, 0 )
Origin:SetWide( 50 )
Origin:SetTooltip( "Center" )
local Render = controls:Add( "DButton" )
Render:SetText( "RENDER" )
Render.DoClick = function() self:RenderIcon() end
Render:Dock( RIGHT )
Render:DockMargin( 2, 0, 0, 0 )
Render:SetWide( 50 )
Render:SetTooltip( "Render Icon" )
local Picker = controls:Add( "DImageButton" )
Picker:SetImage( "icon32/color_picker.png" )
Picker:SetStretchToFit( false )
Picker:SetPaintBackground( true )
Picker:Dock( RIGHT )
Picker:DockMargin( 2, 0, 0, 0 )
Picker:SetWide( 50 )
Picker:SetTooltip( "Pick a new model from an entity" )
Picker.DoClick = function()
self:SetVisible( false )
util.worldpicker.Start( function( tr )
self:SetVisible( true )
if ( !IsValid( tr.Entity ) ) then return end
self:SetFromEntity( tr.Entity )
end )
end
local right = self:Add( "DPropertySheet" )
right:Dock( FILL )
right:SetPadding( 0 )
right:DockMargin( 4, 0, 0, 0 )
self.PropertySheet = right
-- Animations
local anims = right:Add( "Panel" )
anims:Dock( FILL )
anims:DockPadding( 2, 0, 2, 2 )
right:AddSheet( "#smwidget.animations", anims, "icon16/monkey.png" )
self.AnimList = anims:Add( "DListView" )
self.AnimList:AddColumn( "name" )
self.AnimList:Dock( FILL )
self.AnimList:SetMultiSelect( false )
self.AnimList:SetHideHeaders( true )
-- Bodygroups
local pnl = right:Add( "Panel" )
pnl:Dock( FILL )
pnl:DockPadding( 7, 0, 7, 7 )
self.BodygroupTab = right:AddSheet( "#smwidget.bodygroups", pnl, "icon16/brick.png" )
self.BodyList = pnl:Add( "DScrollPanel" )
self.BodyList:Dock( FILL )
--This kind of works but they don't move their stupid mouths. So fuck off.
--[[
self.Scenes = pnl:Add( "DTree" )
self.Scenes:Dock( BOTTOM )
self.Scenes:SetSize( 200, 200 )
self.Scenes.DoClick = function( _, node )
if ( !node.FileName ) then return end
local ext = string.GetExtensionFromFilename( node.FileName )
if( ext != "vcd" ) then return end
self.ModelPanel:StartScene( node.FileName )
MsgN( node.FileName )
end
local materials = self.Scenes.RootNode:AddFolder( "Scenes", "scenes/", true )
materials:SetIcon( "icon16/photos.png" )--]]
-- Settings
local settings = right:Add( "Panel" )
settings:Dock( FILL )
settings:DockPadding( 7, 0, 7, 7 )
right:AddSheet( "#smwidget.settings", settings, "icon16/cog.png" )
local bbox = settings:Add( "DCheckBoxLabel" )
bbox:SetText( "Show Bounding Box" )
bbox:Dock( TOP )
bbox:DockMargin( 0, 0, 0, 3 )
bbox:SetDark( true )
bbox:SetCookieName( "model_editor_bbox" )
self.ShowBBoxPnl = bbox
local origin = settings:Add( "DCheckBoxLabel" )
origin:SetText( "Show Origin" )
origin:Dock( TOP )
origin:SetDark( true )
origin:SetCookieName( "model_editor_origin" )
self.ShowOriginPnl = origin
local playSpeed = settings:Add( "DNumSlider" )
playSpeed:SetText( "Playback Speed" )
playSpeed:Dock( TOP )
playSpeed:SetValue( 1 )
playSpeed:SetMinMax( -1, 2 )
playSpeed:SetDark( true )
playSpeed.OnValueChanged = function( s, value )
self.ModelPanel:GetEntity():SetPlaybackRate( value )
end
local moveSpeed = settings:Add( "DNumSlider" )
moveSpeed:SetText( "Move Speed" )
moveSpeed:Dock( TOP )
moveSpeed:SetMinMax( 0.5, 8 )
moveSpeed:SetValue( 1 )
moveSpeed:SetDark( true )
moveSpeed.OnValueChanged = function( p )
self.ModelPanel:SetMovementScale( p:GetValue() )
end
moveSpeed:SetCookieName( "iconeditor_movespeed" )
local angle = settings:Add( "DTextEntry" )
angle:SetTooltip( "Entity Angles" )
angle:Dock( TOP )
angle:DockMargin( 0, 0, 0, 3 )
angle:SetZPos( 100 )
angle.OnChange = function( p )
self.ModelPanel:GetEntity():SetAngles( Angle( p:GetText() ) )
end
self.TargetAnglePanel = angle
local cam_angle = settings:Add( "DTextEntry" )
cam_angle:SetTooltip( "Camera Angles" )
cam_angle:Dock( TOP )
cam_angle:DockMargin( 0, 0, 0, 3 )
cam_angle:SetZPos( 101 )
cam_angle.OnChange = function( p )
self.ModelPanel:SetLookAng( Angle( p:GetText() ) )
end
self.TargetCamAnglePanel = cam_angle
local cam_pos = settings:Add( "DTextEntry" )
cam_pos:SetTooltip( "Camera Position" )
cam_pos:Dock( TOP )
cam_pos:DockMargin( 0, 0, 0, 3 )
cam_pos:SetZPos( 102 )
cam_pos.OnChange = function( p )
self.ModelPanel:SetCamPos( Vector( p:GetText() ) )
end
self.TargetCamPosPanel = cam_pos
local cam_fov = settings:Add( "DNumSlider" )
cam_fov:SetText( "Camera FOV" )
cam_fov:Dock( TOP )
cam_fov:DockMargin( 0, 0, 0, 3 )
cam_fov:SetZPos( 103 )
cam_fov:SetMinMax( 0.001, 179 )
cam_fov:SetDark( true )
cam_fov.OnValueChanged = function( p )
self.ModelPanel:SetFOV( p:GetValue() )
end
self.TargetCamFOVPanel = cam_fov
local copypaste_cam = settings:Add( "Panel" )
copypaste_cam:SetTall( 20 )
copypaste_cam:Dock( TOP )
copypaste_cam:SetZPos( 104 )
copypaste_cam:DockMargin( 0, 0, 0, 4 )
local copy = copypaste_cam:Add( "DButton" )
copy:Dock( FILL )
copy:SetText( "Copy Camera Settings" )
copy:DockMargin( 0, 0, 3, 0 )
copy.DoClick = function() SetClipboardText( util.TableToJSON( {
pos = self.ModelPanel:GetCamPos(),
ang = self.ModelPanel:GetLookAng(),
fov = self.ModelPanel:GetFOV(),
mdl_ang = self.ModelPanel:GetEntity():GetAngles()
} ) ) end
local paste = copypaste_cam:Add( "DTextEntry" )
paste:SetWide( 140 ) -- Ew
paste:Dock( RIGHT )
paste:SetPlaceholderText( "Paste Camera Settings Here" )
paste.OnChange = function( p )
local tabl = util.JSONToTable( p:GetText() )
if ( tabl ) then
self.ModelPanel:SetCamPos( tabl.pos )
self.ModelPanel:SetLookAng( tabl.ang )
self.ModelPanel:SetFOV( tabl.fov )
self.ModelPanel:GetEntity():SetAngles( tabl.mdl_ang )
end
p:SetText( "" )
end
local labels = { "Pitch", "Yaw", "Roll" }
for i = 1, 3 do
local rotate45 = settings:Add( "DButton" )
rotate45:SetText( "Rotate Entity +/-45 " .. labels[ i ] )
rotate45:Dock( TOP )
rotate45:DockMargin( 0, 0, 0, 3 )
rotate45:SetZPos( 110 + i )
rotate45.DoClick = function( p )
local aang = self.ModelPanel:GetEntity():GetAngles()
aang[ i ] = aang[ i ] + 45
self.ModelPanel:GetEntity():SetAngles( aang )
end
rotate45.DoRightClick = function( p )
local aang = self.ModelPanel:GetEntity():GetAngles()
aang[ i ] = aang[ i ] - 45
self.ModelPanel:GetEntity():SetAngles( aang )
end
end
end
function PANEL:SetDefaultLighting()
self.ModelPanel:SetAmbientLight( Color( 255 * 0.3, 255 * 0.3, 255 * 0.3 ) )
self.ModelPanel:SetDirectionalLight( BOX_FRONT, Color( 255 * 1.3, 255 * 1.3, 255 * 1.3 ) )
self.ModelPanel:SetDirectionalLight( BOX_BACK, Color( 255 * 0.2, 255 * 0.2, 255 * 0.2 ) )
self.ModelPanel:SetDirectionalLight( BOX_RIGHT, Color( 255 * 0.2, 255 * 0.2, 255 * 0.2 ) )
self.ModelPanel:SetDirectionalLight( BOX_LEFT, Color( 255 * 0.2, 255 * 0.2, 255 * 0.2 ) )
self.ModelPanel:SetDirectionalLight( BOX_TOP, Color( 255 * 2.3, 255 * 2.3, 255 * 2.3 ) )
self.ModelPanel:SetDirectionalLight( BOX_BOTTOM, Color( 255 * 0.1, 255 * 0.1, 255 * 0.1 ) )
end
function PANEL:BestGuessLayout()
local ent = self.ModelPanel:GetEntity()
if ( !IsValid( ent ) ) then return end
local pos = ent:GetPos()
local ang = ent:GetAngles()
local tab = PositionSpawnIcon( ent, pos, true )
ent:SetAngles( ang )
if ( tab ) then
self.ModelPanel:SetCamPos( tab.origin )
self.ModelPanel:SetFOV( tab.fov )
self.ModelPanel:SetLookAng( tab.angles )
end
end
function PANEL:FullFrontalLayout()
local ent = self.ModelPanel:GetEntity()
if ( !IsValid( ent ) ) then return end
local pos = ent:GetPos()
local campos = pos + Vector( -200, 0, 0 )
self.ModelPanel:SetCamPos( campos )
self.ModelPanel:SetFOV( 45 )
self.ModelPanel:SetLookAng( ( campos * -1 ):Angle() )
end
function PANEL:AboveLayout()
local ent = self.ModelPanel:GetEntity()
if ( !IsValid( ent ) ) then return end
local pos = ent:GetPos()
local campos = pos + Vector( 0, 0, 200 )
self.ModelPanel:SetCamPos( campos )
self.ModelPanel:SetFOV( 45 )
self.ModelPanel:SetLookAng( ( campos * -1 ):Angle() )
end
function PANEL:RightLayout()
local ent = self.ModelPanel:GetEntity()
if ( !IsValid( ent ) ) then return end
local pos = ent:GetPos()
local campos = pos + Vector( 0, 200, 0 )
self.ModelPanel:SetCamPos( campos )
self.ModelPanel:SetFOV( 45 )
self.ModelPanel:SetLookAng( ( campos * -1 ):Angle() )
end
function PANEL:OriginLayout()
local ent = self.ModelPanel:GetEntity()
if ( !IsValid( ent ) ) then return end
local pos = ent:GetPos()
local campos = pos + vector_origin
self.ModelPanel:SetCamPos( campos )
self.ModelPanel:SetFOV( 45 )
self.ModelPanel:SetLookAng( Angle( 0, -180, 0 ) )
end
function PANEL:UpdateEntity( ent )
ent:SetEyeTarget( self.ModelPanel:GetCamPos() )
if ( IsValid( self.TargetAnglePanel ) && !self.TargetAnglePanel:IsEditing() && !self.TargetAnglePanel:IsHovered() ) then
self.TargetAnglePanel:SetText( tostring( ent:GetAngles() ) )
end
if ( IsValid( self.TargetCamAnglePanel ) && !self.TargetCamAnglePanel:IsEditing() && !self.TargetCamAnglePanel:IsHovered() ) then
self.TargetCamAnglePanel:SetText( tostring( self.ModelPanel:GetLookAng() ) )
end
if ( IsValid( self.TargetCamPosPanel ) && !self.TargetCamPosPanel:IsEditing() && !self.TargetCamPosPanel:IsHovered() ) then
self.TargetCamPosPanel:SetText( tostring( self.ModelPanel:GetCamPos() ) )
end
if ( IsValid( self.TargetCamFOVPanel ) && !self.TargetCamFOVPanel:IsEditing() && !self.TargetCamFOVPanel:IsHovered() ) then
self.TargetCamFOVPanel:SetValue( self.ModelPanel:GetFOV() )
end
if ( self.AnimTrack:GetDragging() ) then
ent:SetCycle( self.AnimTrack:GetSlideX() )
self.AnimPause:SetToggle( true )
elseif ( ent:GetCycle() != self.AnimTrack:GetSlideX() ) then
local cyc = ent:GetCycle()
if ( cyc < 0 ) then cyc = cyc + 1 end
self.AnimTrack:SetSlideX( cyc )
end
if ( !self.AnimPause:GetToggle() ) then
ent:FrameAdvance( FrameTime() )
end
end
function PANEL:RenderIcon()
local tab = {}
tab.ent = self.ModelPanel:GetEntity()
tab.cam_pos = self.ModelPanel:GetCamPos()
tab.cam_ang = self.ModelPanel:GetLookAng()
tab.cam_fov = self.ModelPanel:GetFOV()
self.SpawnIcon:RebuildSpawnIconEx( tab )
end
function PANEL:SetIcon( icon )
if ( !IsValid( icon ) ) then return end
local model = icon:GetModelName()
self:SetOrigin( icon )
self.SpawnIcon:SetSize( icon:GetSize() )
self.SpawnIcon:InvalidateLayout( true )
local w, h = icon:GetSize()
if ( w / h < 1 ) then
self:SetSize( 700, 502 + 400 )
self.LeftPanel:SetWide( 400 )
elseif ( w / h > 1 ) then
self:SetSize( 900, 502 - 100 )
self.LeftPanel:SetWide( 600 )
else
self:SetSize( 700, 502 )
self.LeftPanel:SetWide( 400 )
end
if ( !model or model == "" ) then
self:SetModel( "error.mdl" )
self.SpawnIcon:SetSpawnIcon( icon:GetIconName() )
self:SetCustomIcon( true )
else
self:SetModel( model )
self.SpawnIcon:SetModel( model, icon:GetSkinID(), icon:GetBodyGroup() )
self:SetCustomIcon( false )
end
-- Keep the spawnmenu open
g_SpawnMenu:HangOpen( true )
end
function PANEL:Refresh()
if ( !self:GetModel() ) then return end
self.ModelPanel:SetModel( self:GetModel() )
self.ModelPanel.LayoutEntity = function() self:UpdateEntity( self.ModelPanel:GetEntity() ) end
local ent = self.ModelPanel:GetEntity()
if ( !IsValid( ent ) ) then return end
ent:SetSkin( self.SpawnIcon:GetSkinID() )
ent:SetBodyGroups( self.SpawnIcon:GetBodyGroup() )
ent:SetLOD( 0 )
self:BestGuessLayout()
self:FillAnimations( ent )
self:SetDefaultLighting()
end
function PANEL:FillAnimations( ent )
self.AnimList:Clear()
for k, v in SortedPairsByValue( ent:GetSequenceList() or {} ) do
local line = self.AnimList:AddLine( string.lower( v ) )
line.OnSelect = function()
local speed = ent:GetPlaybackRate()
ent:ResetSequence( v )
ent:SetCycle( 0 )
ent:SetPlaybackRate( speed )
if ( speed < 0 ) then ent:SetCycle( 1 ) end
end
end
self.BodyList:Clear()
local newItems = 0
if ( ent:SkinCount() > 1 ) then
local skinSlider = self.BodyList:Add( "DNumSlider" )
skinSlider:Dock( TOP )
skinSlider:DockMargin( 0, 0, 0, 3 )
skinSlider:SetText( "Skin" )
skinSlider:SetDark( true )
skinSlider:SetDecimals( 0 )
skinSlider:SetMinMax( 0, ent:SkinCount() - 1 )
skinSlider:SetValue( ent:GetSkin() )
skinSlider.OnValueChanged = function( s, newVal )
newVal = math.Round( newVal )
ent:SetSkin( newVal )
if ( IsValid( self:GetOrigin() ) ) then self:GetOrigin():SkinChanged( newVal ) end
-- If we're not using a custom, change our spawnicon
-- so we save the new skin in the right place...
if ( !self:GetCustomIcon() ) then
self.SpawnIcon:SetModel( self.SpawnIcon:GetModelName(), newVal, self.SpawnIcon:GetBodyGroup() )
end
end
newItems = newItems + 1
end
for k = 0, ent:GetNumBodyGroups() - 1 do
if ( ent:GetBodygroupCount( k ) <= 1 ) then continue end
local bgSlider = self.BodyList:Add( "DNumSlider" )
bgSlider:Dock( TOP )
bgSlider:DockMargin( 0, 0, 0, 3 )
bgSlider:SetDark( true )
bgSlider:SetDecimals( 0 )
bgSlider:SetText( ent:GetBodygroupName( k ) )
bgSlider:SetMinMax( 0, ent:GetBodygroupCount( k ) - 1 )
bgSlider:SetValue( ent:GetBodygroup( k ) )
bgSlider.BodyGroupID = k
bgSlider.OnValueChanged = function( s, newVal )
newVal = math.Round( newVal )
ent:SetBodygroup( s.BodyGroupID, newVal )
if ( IsValid( self:GetOrigin() ) ) then self:GetOrigin():BodyGroupChanged( s.BodyGroupID, newVal ) end
-- If we're not using a custom, change our spawnicon
-- so we save the new skin in the right place...
if ( !self:GetCustomIcon() ) then
self.SpawnIcon:SetBodyGroup( s.BodyGroupID, newVal )
self.SpawnIcon:SetModel( self.SpawnIcon:GetModelName(), self.SpawnIcon:GetSkinID(), self.SpawnIcon:GetBodyGroup() )
end
end
newItems = newItems + 1
end
if ( newItems > 0 ) then
self.BodygroupTab.Tab:SetVisible( true )
else
self.BodygroupTab.Tab:SetVisible( false )
end
local propertySheet = self.PropertySheet
propertySheet.tabScroller:InvalidateLayout()
end
function PANEL:SetFromEntity( ent )
if ( !IsValid( ent ) ) then return end
local bodyStr = ""
for i = 0, 8 do
bodyStr = bodyStr .. math.min( ent:GetBodygroup( i ) or 0, 9 )
end
self.SpawnIcon:SetModel( ent:GetModel(), ent:GetSkin(), bodyStr )
self:SetModel( ent:GetModel() )
self:Refresh()
end
vgui.Register( "IconEditor", PANEL, "DFrame" )

View File

@@ -0,0 +1,185 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
--[[---------------------------------------------------------
Sandbox Gamemode
This is GMod's default gamemode
-----------------------------------------------------------]]
-- These files get sent to the client
AddCSLuaFile( "cl_hints.lua" )
AddCSLuaFile( "cl_init.lua" )
AddCSLuaFile( "cl_notice.lua" )
AddCSLuaFile( "cl_search_models.lua" )
AddCSLuaFile( "cl_spawnmenu.lua" )
AddCSLuaFile( "cl_worldtips.lua" )
AddCSLuaFile( "persistence.lua" )
AddCSLuaFile( "player_extension.lua" )
AddCSLuaFile( "save_load.lua" )
AddCSLuaFile( "shared.lua" )
AddCSLuaFile( "gui/IconEditor.lua" )
include( 'shared.lua' )
include( 'commands.lua' )
include( 'player.lua' )
include( 'spawnmenu/init.lua' )
--
-- Make BaseClass available
--
DEFINE_BASECLASS( "gamemode_base" )
--[[---------------------------------------------------------
Name: gamemode:PlayerSpawn()
Desc: Called when a player spawns
-----------------------------------------------------------]]
function GM:PlayerSpawn( pl, transiton )
player_manager.SetPlayerClass( pl, "player_sandbox" )
BaseClass.PlayerSpawn( self, pl, transiton )
end
--[[---------------------------------------------------------
Name: gamemode:OnPhysgunFreeze( weapon, phys, ent, player )
Desc: The physgun wants to freeze a prop
-----------------------------------------------------------]]
function GM:OnPhysgunFreeze( weapon, phys, ent, ply )
-- Don't freeze persistent props (should already be frozen)
if ( ent:GetPersistent() && GetConVarString( "sbox_persist" ):Trim() != "" ) then return false end
BaseClass.OnPhysgunFreeze( self, weapon, phys, ent, ply )
ply:SendHint( "PhysgunUnfreeze", 0.3 )
ply:SuppressHint( "PhysgunFreeze" )
end
--[[---------------------------------------------------------
Name: gamemode:OnPhysgunReload( weapon, player )
Desc: The physgun wants to unfreeze
-----------------------------------------------------------]]
function GM:OnPhysgunReload( weapon, ply )
local num = ply:PhysgunUnfreeze()
if ( num > 0 ) then
ply:SendLua( string.format( "GAMEMODE:UnfrozeObjects(%d)", num ) )
end
ply:SuppressHint( "PhysgunUnfreeze" )
end
--[[---------------------------------------------------------
Name: gamemode:PlayerShouldTakeDamage
Return true if this player should take damage from this attacker
Note: This is a shared function - the client will think they can
damage the players even though they can't. This just means the
prediction will show blood.
-----------------------------------------------------------]]
function GM:PlayerShouldTakeDamage( ply, attacker )
-- Global godmode, players can't be damaged in any way
if ( cvars.Bool( "sbox_godmode", false ) ) then return false end
-- No player vs player damage
if ( attacker:IsValid() && attacker:IsPlayer() && ply != attacker ) then
return cvars.Bool( "sbox_playershurtplayers", true )
end
-- Default, let the player be hurt
return true
end
--[[---------------------------------------------------------
Show the search when f1 is pressed
-----------------------------------------------------------]]
function GM:ShowHelp( ply )
ply:SendLua( "hook.Run( 'StartSearch' )" )
end
--[[---------------------------------------------------------
Called once on the player's first spawn
-----------------------------------------------------------]]
function GM:PlayerInitialSpawn( ply, transiton )
BaseClass.PlayerInitialSpawn( self, ply, transiton )
end
--[[---------------------------------------------------------
A ragdoll of an entity has been created
-----------------------------------------------------------]]
function GM:CreateEntityRagdoll( entity, ragdoll )
-- Replace the entity with the ragdoll in cleanups etc
undo.ReplaceEntity( entity, ragdoll )
cleanup.ReplaceEntity( entity, ragdoll )
end
--[[---------------------------------------------------------
Player unfroze an object
-----------------------------------------------------------]]
function GM:PlayerUnfrozeObject( ply, entity, physobject )
local effectdata = EffectData()
effectdata:SetOrigin( physobject:GetPos() )
effectdata:SetEntity( entity )
util.Effect( "phys_unfreeze", effectdata, true, true )
end
--[[---------------------------------------------------------
Player froze an object
-----------------------------------------------------------]]
function GM:PlayerFrozeObject( ply, entity, physobject )
if ( DisablePropCreateEffect ) then return end
local effectdata = EffectData()
effectdata:SetOrigin( physobject:GetPos() )
effectdata:SetEntity( entity )
util.Effect( "phys_freeze", effectdata, true, true )
end
--
-- Who can edit variables?
-- If you're writing prop protection or something, you'll
-- probably want to hook or override this function.
--
function GM:CanEditVariable( ent, ply, key, val, editor )
-- Only allow admins to edit admin only variables!
local isAdmin = ply:IsAdmin() || game.SinglePlayer()
if ( editor.AdminOnly && !isAdmin ) then
return false
end
-- This entity decides who can edit its variables
if ( isfunction( ent.CanEditVariables ) ) then
return ent:CanEditVariables( ply )
end
-- default in sandbox is.. anyone can edit anything.
return true
end

View File

@@ -0,0 +1,100 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
if ( CLIENT ) then return end
local CurrentlyActivePersistencePage = ""
hook.Add( "InitPostEntity", "PersistenceInit", function()
local PersistPage = GetConVarString( "sbox_persist" ):Trim()
if ( PersistPage == "" ) then return end
hook.Run( "PersistenceLoad", PersistPage )
end )
hook.Add( "ShutDown", "SavePersistenceOnShutdown", function() hook.Run( "PersistenceSave" ) end )
hook.Add( "PersistenceSave", "PersistenceSave", function( name )
local PersistPage = ( name or GetConVarString( "sbox_persist" ) ):Trim()
if ( PersistPage == "" ) then return end
local Ents = ents.GetAll()
for k, v in ipairs( Ents ) do
if ( !v:GetPersistent() ) then
Ents[ k ] = nil
end
end
local tab = duplicator.CopyEnts( Ents )
if ( !tab ) then return end
local out = util.TableToJSON( tab )
file.CreateDir( "persist" )
file.Write( "persist/" .. game.GetMap() .. "_" .. PersistPage .. ".txt", out )
end )
hook.Add( "PersistenceLoad", "PersistenceLoad", function( name )
CurrentlyActivePersistencePage = name
local data = file.Read( "persist/" .. game.GetMap() .. "_" .. name .. ".txt" )
if ( !data ) then return end
local tab = util.JSONToTable( data )
if ( !tab ) then return end
if ( !tab.Entities ) then return end
if ( !tab.Constraints ) then return end
local entities = duplicator.Paste( nil, tab.Entities, tab.Constraints )
for k, v in pairs( entities ) do
v:SetPersistent( true )
end
end )
cvars.AddChangeCallback( "sbox_persist", function( name, old, new )
-- A timer in case someone tries to rapidly change the convar, such as addons with "live typing" or whatever
timer.Create( "sbox_persist_change_timer", 2, 1, function()
local newPage = new:Trim()
if ( CurrentlyActivePersistencePage == newPage ) then return end
-- old:Trim() would be incorrect for more than 1 convar change within the 2 second timer window
hook.Run( "PersistenceSave", CurrentlyActivePersistencePage )
CurrentlyActivePersistencePage = ""
if ( newPage == "" ) then return end
-- Addons are forcing us to use this hook
hook.Add( "PostCleanupMap", "GMod_Sandbox_PersistanceLoad", function()
hook.Remove( "PostCleanupMap", "GMod_Sandbox_PersistanceLoad" )
hook.Run( "PersistenceLoad", newPage )
end )
-- Maybe this game.CleanUpMap call should be moved to PersistenceLoad?
game.CleanUpMap( false, nil, function() end )
end )
end, "sbox_persist_load" )

View File

@@ -0,0 +1,227 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
--[[---------------------------------------------------------
Name: gamemode:PlayerSpawnObject( ply )
Desc: Called to ask whether player is allowed to spawn any objects
-----------------------------------------------------------]]
function GM:PlayerSpawnObject( ply )
return true
end
--[[---------------------------------------------------------
Name: gamemode:CanPlayerUnfreeze( )
Desc: Can the player unfreeze this entity & physobject
-----------------------------------------------------------]]
function GM:CanPlayerUnfreeze( ply, entity, physobject )
if ( entity:GetPersistent() && GetConVarString( "sbox_persist" ):Trim() != "" ) then return false end
return true
end
--[[---------------------------------------------------------
Name: LimitReachedProcess
-----------------------------------------------------------]]
local function LimitReachedProcess( ply, str )
if ( !IsValid( ply ) ) then return true end
return ply:CheckLimit( str )
end
--[[---------------------------------------------------------
Name: gamemode:PlayerSpawnRagdoll( ply, model )
Desc: Return true if it's allowed
-----------------------------------------------------------]]
function GM:PlayerSpawnRagdoll( ply, model )
return LimitReachedProcess( ply, "ragdolls" )
end
--[[---------------------------------------------------------
Name: gamemode:PlayerSpawnProp( ply, model )
Desc: Return true if it's allowed
-----------------------------------------------------------]]
function GM:PlayerSpawnProp( ply, model )
return LimitReachedProcess( ply, "props" )
end
--[[---------------------------------------------------------
Name: gamemode:PlayerSpawnEffect( ply, model )
Desc: Return true if it's allowed
-----------------------------------------------------------]]
function GM:PlayerSpawnEffect( ply, model )
return LimitReachedProcess( ply, "effects" )
end
--[[---------------------------------------------------------
Name: gamemode:PlayerSpawnVehicle( ply, model, vname, vtable )
Desc: Return true if it's allowed
-----------------------------------------------------------]]
function GM:PlayerSpawnVehicle( ply, model, vname, vtable )
return LimitReachedProcess( ply, "vehicles" )
end
--[[---------------------------------------------------------
Name: gamemode:PlayerSpawnSWEP( ply, wname, wtable )
Desc: Return true if it's allowed
-----------------------------------------------------------]]
function GM:PlayerSpawnSWEP( ply, wname, wtable )
return LimitReachedProcess( ply, "sents" )
end
--[[---------------------------------------------------------
Name: gamemode:PlayerGiveSWEP( ply, wname, wtable )
Desc: Return true if it's allowed
-----------------------------------------------------------]]
function GM:PlayerGiveSWEP( ply, wname, wtable )
return true
end
--[[---------------------------------------------------------
Name: gamemode:PlayerSpawnSENT( ply, name )
Desc: Return true if player is allowed to spawn the SENT
-----------------------------------------------------------]]
function GM:PlayerSpawnSENT( ply, name )
return LimitReachedProcess( ply, "sents" )
end
--[[---------------------------------------------------------
Name: gamemode:PlayerSpawnNPC( ply, npc_type )
Desc: Return true if player is allowed to spawn the NPC
-----------------------------------------------------------]]
function GM:PlayerSpawnNPC( ply, npc_type, equipment )
return LimitReachedProcess( ply, "npcs" )
end
--[[---------------------------------------------------------
Name: gamemode:PlayerSpawnedRagdoll( ply, model, ent )
Desc: Called after the player spawned a ragdoll
-----------------------------------------------------------]]
function GM:PlayerSpawnedRagdoll( ply, model, ent )
ply:AddCount( "ragdolls", ent )
end
--[[---------------------------------------------------------
Name: gamemode:PlayerSpawnedProp( ply, model, ent )
Desc: Called after the player spawned a prop
-----------------------------------------------------------]]
function GM:PlayerSpawnedProp( ply, model, ent )
ply:AddCount( "props", ent )
end
--[[---------------------------------------------------------
Name: gamemode:PlayerSpawnedEffect( ply, model, ent )
Desc: Called after the player spawned an effect
-----------------------------------------------------------]]
function GM:PlayerSpawnedEffect( ply, model, ent )
ply:AddCount( "effects", ent )
end
--[[---------------------------------------------------------
Name: gamemode:PlayerSpawnedVehicle( ply, ent )
Desc: Called after the player spawned a vehicle
-----------------------------------------------------------]]
function GM:PlayerSpawnedVehicle( ply, ent )
ply:AddCount( "vehicles", ent )
end
--[[---------------------------------------------------------
Name: gamemode:PlayerSpawnedNPC( ply, ent )
Desc: Called after the player spawned an NPC
-----------------------------------------------------------]]
function GM:PlayerSpawnedNPC( ply, ent )
ply:AddCount( "npcs", ent )
end
--[[---------------------------------------------------------
Name: gamemode:PlayerSpawnedSENT( ply, ent )
Desc: Called after the player has spawned a SENT
-----------------------------------------------------------]]
function GM:PlayerSpawnedSENT( ply, ent )
ply:AddCount( "sents", ent )
end
--[[---------------------------------------------------------
Name: gamemode:PlayerSpawnedWeapon( ply, ent )
Desc: Called after the player has spawned a Weapon
-----------------------------------------------------------]]
function GM:PlayerSpawnedSWEP( ply, ent )
-- This is on purpose..
ply:AddCount( "sents", ent )
end
--[[---------------------------------------------------------
Name: gamemode:PlayerEnteredVehicle( player, vehicle, role )
Desc: Player entered the vehicle fine
-----------------------------------------------------------]]
function GM:PlayerEnteredVehicle( player, vehicle, role )
player:SendHint( "VehicleView", 2 )
end
--[[---------------------------------------------------------
These are buttons that the client is pressing. They're used
in Sandbox mode to control things like wheels, thrusters etc.
-----------------------------------------------------------]]
function GM:PlayerButtonDown( ply, btn )
numpad.Activate( ply, btn )
end
--[[---------------------------------------------------------
These are buttons that the client is pressing. They're used
in Sandbox mode to control things like wheels, thrusters etc.
-----------------------------------------------------------]]
function GM:PlayerButtonUp( ply, btn )
numpad.Deactivate( ply, btn )
end

View File

@@ -0,0 +1,209 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
AddCSLuaFile()
DEFINE_BASECLASS( "player_default" )
if ( CLIENT ) then
CreateConVar( "cl_playercolor", "0.24 0.34 0.41", { FCVAR_ARCHIVE, FCVAR_USERINFO, FCVAR_DONTRECORD }, "The value is a Vector - so between 0-1 - not between 0-255" )
CreateConVar( "cl_weaponcolor", "0.30 1.80 2.10", { FCVAR_ARCHIVE, FCVAR_USERINFO, FCVAR_DONTRECORD }, "The value is a Vector - so between 0-1 - not between 0-255" )
CreateConVar( "cl_playerskin", "0", { FCVAR_ARCHIVE, FCVAR_USERINFO, FCVAR_DONTRECORD }, "The skin to use, if the model has any" )
CreateConVar( "cl_playerbodygroups", "0", { FCVAR_ARCHIVE, FCVAR_USERINFO, FCVAR_DONTRECORD }, "The bodygroups to use, if the model has any" )
end
local PLAYER = {}
PLAYER.DuckSpeed = 0.1 -- How fast to go from not ducking, to ducking
PLAYER.UnDuckSpeed = 0.1 -- How fast to go from ducking, to not ducking
--
-- Creates a Taunt Camera
--
PLAYER.TauntCam = TauntCamera()
--
-- See gamemodes/base/player_class/player_default.lua for all overridable variables
--
PLAYER.SlowWalkSpeed = 100
PLAYER.WalkSpeed = 200
PLAYER.RunSpeed = 400
--
-- Set up the network table accessors
--
function PLAYER:SetupDataTables()
BaseClass.SetupDataTables( self )
end
function PLAYER:Loadout()
self.Player:RemoveAllAmmo()
if ( cvars.Bool( "sbox_weapons", true ) ) then
self.Player:GiveAmmo( 256, "Pistol", true )
self.Player:GiveAmmo( 256, "SMG1", true )
self.Player:GiveAmmo( 5, "grenade", true )
self.Player:GiveAmmo( 64, "Buckshot", true )
self.Player:GiveAmmo( 32, "357", true )
self.Player:GiveAmmo( 32, "XBowBolt", true )
self.Player:GiveAmmo( 6, "AR2AltFire", true )
self.Player:GiveAmmo( 100, "AR2", true )
self.Player:Give( "weapon_crowbar" )
self.Player:Give( "weapon_pistol" )
self.Player:Give( "weapon_smg1" )
self.Player:Give( "weapon_frag" )
self.Player:Give( "weapon_physcannon" )
self.Player:Give( "weapon_crossbow" )
self.Player:Give( "weapon_shotgun" )
self.Player:Give( "weapon_357" )
self.Player:Give( "weapon_rpg" )
self.Player:Give( "weapon_ar2" )
-- The only reason I'm leaving this out is because
-- I don't want to add too many weapons to the first
-- row because that's where the gravgun is.
--pl:Give( "weapon_stunstick" )
end
self.Player:Give( "gmod_tool" )
self.Player:Give( "gmod_camera" )
self.Player:Give( "weapon_physgun" )
self.Player:SwitchToDefaultWeapon()
end
function PLAYER:SetModel()
BaseClass.SetModel( self )
local skin = self.Player:GetInfoNum( "cl_playerskin", 0 )
self.Player:SetSkin( skin )
local bodygroups = self.Player:GetInfo( "cl_playerbodygroups" )
if ( bodygroups == nil ) then bodygroups = "" end
local groups = string.Explode( " ", bodygroups )
for k = 0, self.Player:GetNumBodyGroups() - 1 do
self.Player:SetBodygroup( k, tonumber( groups[ k + 1 ] ) or 0 )
end
end
--
-- Called when the player spawns
--
function PLAYER:Spawn()
BaseClass.Spawn( self )
local plyclr = self.Player:GetInfo( "cl_playercolor" )
self.Player:SetPlayerColor( Vector( plyclr ) )
local wepclr = Vector( self.Player:GetInfo( "cl_weaponcolor" ) )
if ( wepclr:Length() < 0.001 ) then
wepclr = Vector( 0.001, 0.001, 0.001 )
end
self.Player:SetWeaponColor( wepclr )
end
--
-- Return true to draw local (thirdperson) camera - false to prevent - nothing to use default behaviour
--
function PLAYER:ShouldDrawLocal()
if ( self.TauntCam:ShouldDrawLocalPlayer( self.Player, self.Player:IsPlayingTaunt() ) ) then return true end
end
--
-- Allow player class to create move
--
function PLAYER:CreateMove( cmd )
if ( self.TauntCam:CreateMove( cmd, self.Player, self.Player:IsPlayingTaunt() ) ) then return true end
end
--
-- Allow changing the player's view
--
function PLAYER:CalcView( view )
if ( self.TauntCam:CalcView( view, self.Player, self.Player:IsPlayingTaunt() ) ) then return true end
-- Your stuff here
end
--
-- Reproduces the jump boost from HL2 singleplayer
--
local JUMPING
function PLAYER:StartMove( move )
-- Only apply the jump boost in FinishMove if the player has jumped during this frame
-- Using a global variable is safe here because nothing else happens between SetupMove and FinishMove
if bit.band( move:GetButtons(), IN_JUMP ) ~= 0 and bit.band( move:GetOldButtons(), IN_JUMP ) == 0 and self.Player:OnGround() then
JUMPING = true
end
end
function PLAYER:FinishMove( move )
-- If the player has jumped this frame
if ( JUMPING ) then
-- Get their orientation
local forward = move:GetAngles()
forward.p = 0
forward = forward:Forward()
-- Compute the speed boost
-- HL2 normally provides a much weaker jump boost when sprinting
-- For some reason this never applied to GMod, so we won't perform
-- this check here to preserve the "authentic" feeling
local speedBoostPerc = ( ( not self.Player:Crouching() ) and 0.5 ) or 0.1
local speedAddition = math.abs( move:GetForwardSpeed() * speedBoostPerc )
local maxSpeed = move:GetMaxSpeed() * ( 1 + speedBoostPerc )
local newSpeed = speedAddition + move:GetVelocity():Length2D()
-- Clamp it to make sure they can't bunnyhop to ludicrous speed
if newSpeed > maxSpeed then
speedAddition = speedAddition - ( newSpeed - maxSpeed )
end
-- Reverse it if the player is running backwards
if move:GetVelocity():Dot( forward ) < 0 then
speedAddition = -speedAddition
end
-- Apply the speed boost
move:SetVelocity( forward * speedAddition + move:GetVelocity() )
end
JUMPING = nil
end
player_manager.RegisterClass( "player_sandbox", PLAYER, "player_default" )

View File

@@ -0,0 +1,149 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local meta = FindMetaTable( "Player" )
-- Return if there's nothing to add on to
if ( !meta ) then return end
g_SBoxObjects = {}
function meta:CheckLimit( str )
-- No limits in single player
if ( game.SinglePlayer() ) then return true end
local c = cvars.Number( "sbox_max" .. str, 0 )
local count = self:GetCount( str )
local ret = hook.Run( "PlayerCheckLimit", self, str, count, c )
if ( ret != nil ) then
if ( !ret && SERVER ) then self:LimitHit( str ) end
return ret
end
if ( c < 0 ) then return true end
if ( count > c - 1 ) then
if ( SERVER ) then self:LimitHit( str ) end
return false
end
return true
end
function meta:GetCount( str, minus )
if ( CLIENT ) then
return self:GetNWInt( "Count." .. str, 0 )
end
minus = minus or 0
if ( !self:IsValid() ) then return end
local key = self:UniqueID()
local tab = g_SBoxObjects[ key ]
if ( !tab || !tab[ str ] ) then
self:SetNWInt( "Count." .. str, 0 )
return 0
end
local c = 0
for k, v in pairs( tab[ str ] ) do
if ( IsValid( v ) && !v:IsMarkedForDeletion() ) then
c = c + 1
else
tab[ str ][ k ] = nil
end
end
self:SetNWInt( "Count." .. str, math.max( c - minus, 0 ) )
return c
end
function meta:AddCount( str, ent )
if ( SERVER ) then
local key = self:UniqueID()
g_SBoxObjects[ key ] = g_SBoxObjects[ key ] or {}
g_SBoxObjects[ key ][ str ] = g_SBoxObjects[ key ][ str ] or {}
local tab = g_SBoxObjects[ key ][ str ]
table.insert( tab, ent )
-- Update count (for client)
self:GetCount( str )
ent:CallOnRemove( "GetCountUpdate", function( ent, ply, str ) ply:GetCount( str ) end, self, str )
end
end
function meta:LimitHit( str )
self:SendLua( string.format( 'hook.Run("LimitHit",%q)', str ) )
end
function meta:AddCleanup( type, ent )
cleanup.Add( self, type, ent )
end
function meta:GetTool( mode )
local wep = self:GetWeapon( "gmod_tool" )
if ( !IsValid( wep ) || !wep.GetToolObject ) then return nil end
local tool = wep:GetToolObject( mode )
if ( !tool ) then return nil end
return tool
end
if ( SERVER ) then
function meta:SendHint( str, delay )
self.Hints = self.Hints or {}
if ( self.Hints[ str ] ) then return end
self:SendLua( string.format( 'hook.Run("AddHint",%q,%d)', str, delay ) )
self.Hints[ str ] = true
end
function meta:SuppressHint( str )
self.Hints = self.Hints or {}
if ( self.Hints[ str ] ) then return end
self:SendLua( string.format( 'hook.Run("SuppressHint",%q)', str ) )
self.Hints[ str ] = true
end
end

View File

@@ -0,0 +1,55 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
--[[---------------------------------------------------------
If the entity is colliding with something this function
will make it non solid and call a timer to itself.
If it isn't colliding with anything it will make it solid
and the timer will end.
-----------------------------------------------------------]]
function CheckPropSolid( e, solidtype, nonsolidtype, SolidCallback )
if ( !IsValid( e ) ) then return end
-- Trace our entity to check for collisions
local trace = { start = e:GetPos(), endpos = e:GetPos(), filter = e }
local tr = util.TraceEntity( trace, e )
-- If it collides mark it non solid and test again in 0.5 seconds
if ( tr.Hit ) then
e:SetCollisionGroup( nonsolidtype )
-- Check later and later every time.
--timerlast = timerlast + 0.1
timer.Simple( 0.5, function() CheckPropSolid( e, solidtype, nonsolidtype, SolidCallback ) end )
else
e:SetCollisionGroup( solidtype )
-- If we have a solid callback function call it
if ( SolidCallback ) then
SolidCallback( e )
end
end
end
function DoPropSpawnedEffect( e )
if ( DisablePropCreateEffect ) then return end
e:SetSpawnEffect( true );
end

View File

@@ -0,0 +1,147 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
if ( SERVER ) then
--
-- Pool the shared network string
--
util.AddNetworkString( "GModSave" )
--
-- Console command to trigger the save serverside (and send the save to the client)
--
concommand.Add( "gm_save", function( ply, cmd, args )
if ( !IsValid( ply ) ) then return end
-- gmsave.SaveMap is very expensive for big maps/lots of entities. Do not allow random ppl to save the map in multiplayer!
-- TODO: Actually do proper hooks for this
if ( !game.SinglePlayer() && !ply:IsAdmin() ) then return end
if ( ply.m_NextSave && ply.m_NextSave > CurTime() && !game.SinglePlayer() ) then
ServerLog( tostring( ply ) .. " tried to save too quickly!\n" )
return
end
ply.m_NextSave = CurTime() + 10
ServerLog( tostring( ply ) .. " requested a save.\n" )
local save = gmsave.SaveMap( ply )
if ( !save ) then return end
local compressed_save = util.Compress( save )
if ( !compressed_save ) then compressed_save = save end
local len = string.len( compressed_save )
local send_size = 60000
local parts = math.ceil( len / send_size )
local ShowSave = false
if ( args[ 1 ] == "spawnmenu" ) then ShowSave = true end
local start = 0
for i = 1, parts do
local endbyte = math.min( start + send_size, len )
local size = endbyte - start
net.Start( "GModSave" )
net.WriteBool( i == parts )
net.WriteBool( ShowSave )
net.WriteUInt( size, 16 )
net.WriteData( compressed_save:sub( start + 1, endbyte + 1 ), size )
net.Send( ply )
start = endbyte
end
end, nil, "", { FCVAR_DONTRECORD } )
local function LoadGModSave( savedata )
-- If we loaded the save from main menu and the player entity is not ready yet
if ( game.SinglePlayer() && !IsValid( Entity( 1 ) ) ) then
timer.Create( "LoadGModSave_WaitForPlayer", 0.1, 0, function()
if ( !IsValid( Entity( 1 ) ) ) then return end
timer.Remove( "LoadGModSave_WaitForPlayer" )
LoadGModSave( savedata )
end )
return
end
local ply = nil
if ( IsValid( Entity( 1 ) ) && ( game.SinglePlayer() || Entity( 1 ):IsListenServerHost() ) ) then ply = Entity( 1 ) end
if ( !IsValid( ply ) && #player.GetHumans() == 1 ) then ply = player.GetHumans()[ 1 ] end
gmsave.LoadMap( savedata, ply )
end
hook.Add( "LoadGModSave", "LoadGModSave", function( savedata, mapname, maptime )
savedata = util.Decompress( savedata )
if ( !isstring( savedata ) ) then
MsgN( "gm_load: Couldn't load save!" )
return
end
LoadGModSave( savedata )
end )
else
local buffer = ""
net.Receive( "GModSave", function( len, client )
local done = net.ReadBool()
local showsave = net.ReadBool()
local length = net.ReadUInt( 16 )
local data = net.ReadData( length )
buffer = buffer .. data
if ( !done ) then return end
MsgN( "Received save. Size: " .. buffer:len() )
local uncompressed = util.Decompress( buffer )
if ( !uncompressed ) then
MsgN( "Received save - but couldn't decompress!?" )
buffer = ""
return
end
local MapAddon = nil
for id, addon in pairs( engine.GetAddons() ) do
if ( file.Exists( "maps/" .. game.GetMap() .. ".bsp", addon.title ) ) then
MapAddon = addon.wsid
end
end
engine.WriteSave( buffer, game.GetMap() .. " " .. util.DateStamp(), CurTime(), game.GetMap(), MapAddon )
buffer = ""
if ( showsave ) then
hook.Run( "PostGameSaved" )
end
end )
end

View File

@@ -0,0 +1,333 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
--[[---------------------------------------------------------
Sandbox Gamemode
This is GMod's default gamemode
-----------------------------------------------------------]]
include( "player_extension.lua" )
include( "persistence.lua" )
include( "save_load.lua" )
include( "player_class/player_sandbox.lua" )
include( "drive/drive_sandbox.lua" )
include( "editor_player.lua" )
--
-- Make BaseClass available
--
DEFINE_BASECLASS( "gamemode_base" )
GM.Name = "Sandbox"
GM.Author = "TEAM GARRY"
GM.Email = "teamgarry@garrysmod.com"
GM.Website = "www.garrysmod.com"
--[[
Note: This is so that in addons you can do stuff like
if ( !GAMEMODE.IsSandboxDerived ) then return end
--]]
GM.IsSandboxDerived = true
cleanup.Register( "props" )
cleanup.Register( "ragdolls" )
cleanup.Register( "effects" )
cleanup.Register( "npcs" )
cleanup.Register( "constraints" )
cleanup.Register( "ropeconstraints" )
cleanup.Register( "sents" )
cleanup.Register( "vehicles" )
local physgun_limited = CreateConVar( "physgun_limited", "0", FCVAR_REPLICATED, "Prevent the Physics Gun from picking up certain map entities." )
--[[---------------------------------------------------------
Name: gamemode:CanTool( ply, trace, mode, tool, button )
Return true if the player is allowed to use this tool
-----------------------------------------------------------]]
function GM:CanTool( ply, trace, mode, tool, button )
-- The jeep spazzes out when applying something
-- todo: Find out what it's reacting badly to and change it in _physprops
if ( mode == "physprop" && trace.Entity:IsValid() && trace.Entity:GetClass() == "prop_vehicle_jeep" ) then
return false
end
-- If we have a toolsallowed table, check to make sure the toolmode is in it
if ( trace.Entity.m_tblToolsAllowed ) then
local vFound = false
for k, v in pairs( trace.Entity.m_tblToolsAllowed ) do
if ( mode == v ) then vFound = true end
end
if ( !vFound ) then return false end
end
-- Give the entity a chance to decide
if ( trace.Entity.CanTool ) then
return trace.Entity:CanTool( ply, trace, mode, tool, button )
end
return true
end
--[[---------------------------------------------------------
Name: gamemode:GravGunPunt( )
Desc: We're about to punt an entity (primary fire).
Return true if we're allowed to.
-----------------------------------------------------------]]
function GM:GravGunPunt( ply, ent )
if ( ent:IsValid() && ent.GravGunPunt ) then
return ent:GravGunPunt( ply )
end
return BaseClass.GravGunPunt( self, ply, ent )
end
--[[---------------------------------------------------------
Name: gamemode:GravGunPickupAllowed( )
Desc: Return true if we're allowed to pickup entity
-----------------------------------------------------------]]
function GM:GravGunPickupAllowed( ply, ent )
if ( ent:IsValid() && ent.GravGunPickupAllowed ) then
return ent:GravGunPickupAllowed( ply )
end
return BaseClass.GravGunPickupAllowed( self, ply, ent )
end
--[[---------------------------------------------------------
Name: gamemode:PhysgunPickup( )
Desc: Return true if player can pickup entity
-----------------------------------------------------------]]
function GM:PhysgunPickup( ply, ent )
-- Don't pick up persistent props
if ( ent:GetPersistent() && GetConVarString( "sbox_persist" ):Trim() != "" ) then return false end
if ( ent:IsValid() && ent.PhysgunPickup ) then
return ent:PhysgunPickup( ply )
end
-- Some entities specifically forbid physgun interaction
if ( ent.PhysgunDisabled ) then return false end
local EntClass = ent:GetClass()
-- Never pick up players
if ( EntClass == "player" ) then return false end
if ( physgun_limited:GetBool() ) then
if ( string.find( EntClass, "prop_dynamic" ) ) then return false end
if ( string.find( EntClass, "prop_door" ) ) then return false end
-- Don't move physboxes if the mapper logic says no
if ( EntClass == "func_physbox" && ent:HasSpawnFlags( SF_PHYSBOX_MOTIONDISABLED ) ) then return false end
-- If the physics object is frozen by the mapper, don't allow us to move it.
if ( string.find( EntClass, "prop_" ) && ( ent:HasSpawnFlags( SF_PHYSPROP_MOTIONDISABLED ) || ent:HasSpawnFlags( SF_PHYSPROP_PREVENT_PICKUP ) ) ) then return false end
-- Allow physboxes, but get rid of all other func_'s (ladder etc)
if ( EntClass != "func_physbox" && string.find( EntClass, "func_" ) ) then return false end
end
if ( SERVER ) then
ply:SendHint( "PhysgunFreeze", 2 )
ply:SendHint( "PhysgunUse", 8 )
end
return true
end
--[[---------------------------------------------------------
Name: gamemode:EntityKeyValue( ent, key, value )
Desc: Called when an entity has a keyvalue set
Returning a string it will override the value
-----------------------------------------------------------]]
function GM:EntityKeyValue( ent, key, value )
-- Physgun not allowed on this prop..
if ( key == "gmod_allowphysgun" && value == '0' ) then
ent.PhysgunDisabled = true
end
-- Prop has a list of tools that are allowed on it.
if ( key == "gmod_allowtools" ) then
ent.m_tblToolsAllowed = string.Explode( " ", value )
end
end
--[[---------------------------------------------------------
Name: gamemode:PlayerNoClip( player, bool )
Desc: Player pressed the noclip key, return true if
the player is allowed to noclip, false to block
-----------------------------------------------------------]]
function GM:PlayerNoClip( pl, on )
-- Don't allow if player is in vehicle
if ( !IsValid( pl ) || pl:InVehicle() || !pl:Alive() ) then return false end
-- Always allow to turn off noclip, and in single player
if ( !on || game.SinglePlayer() ) then return true end
return GetConVarNumber( "sbox_noclip" ) > 0
end
--[[---------------------------------------------------------
Name: gamemode:CanProperty( pl, property, ent )
Desc: Can the player do this property, to this entity?
-----------------------------------------------------------]]
function GM:CanProperty( pl, property, ent )
--
-- Always a chance some bastard got through
--
if ( !IsValid( ent ) ) then return false end
--
-- If we have a toolsallowed table, check to make sure the toolmode is in it
-- This is used by things like map entities
--
if ( ent.m_tblToolsAllowed ) then
local vFound = false
for k, v in pairs( ent.m_tblToolsAllowed ) do
if ( property == v ) then vFound = true end
end
if ( !vFound ) then return false end
end
--
-- Who can who bone manipulate?
--
if ( property == "bonemanipulate" ) then
if ( game.SinglePlayer() ) then return true end
if ( ent:IsNPC() ) then return GetConVarNumber( "sbox_bonemanip_npc" ) != 0 end
if ( ent:IsPlayer() ) then return GetConVarNumber( "sbox_bonemanip_player" ) != 0 end
return GetConVarNumber( "sbox_bonemanip_misc" ) != 0
end
--
-- Weapons can only be property'd if nobody is holding them
--
if ( ent:IsWeapon() and IsValid( ent:GetOwner() ) ) then
return false
end
-- Give the entity a chance to decide
if ( ent.CanProperty ) then
return ent:CanProperty( pl, property )
end
return true
end
--[[---------------------------------------------------------
Name: gamemode:CanDrive( pl, ent )
Desc: Return true to let the entity drive.
-----------------------------------------------------------]]
function GM:CanDrive( pl, ent )
local classname = ent:GetClass();
--
-- Only let physics based NPCs be driven for now
--
if ( ent:IsNPC() ) then
if ( classname == "npc_cscanner" ) then return true end
if ( classname == "npc_clawscanner" ) then return true end
if ( classname == "npc_manhack" ) then return true end
if ( classname == "npc_turret_floor" ) then return true end
if ( classname == "npc_rollermine" ) then return true end
return false
end
if ( classname == "prop_dynamic" ) then return false end
if ( classname == "prop_door" ) then return false end
--
-- I'm guessing we'll find more things we don't want the player to fly around during development
--
return true
end
--[[---------------------------------------------------------
To update the player's animation during a drive
-----------------------------------------------------------]]
function GM:PlayerDriveAnimate( ply )
local driving = ply:GetDrivingEntity()
if ( !IsValid( driving ) ) then return end
ply:SetPlaybackRate( 1 )
ply:ResetSequence( ply:SelectWeightedSequence( ACT_HL2MP_IDLE_MAGIC ) )
--
-- Work out the direction from the player to the entity, and set parameters
--
local DirToEnt = driving:GetPos() - ( ply:GetPos() + Vector( 0, 0, 50 ) )
local AimAng = DirToEnt:Angle()
if ( AimAng.p > 180 ) then
AimAng.p = AimAng.p - 360
end
ply:SetPoseParameter( "aim_yaw", 0 )
ply:SetPoseParameter( "aim_pitch", AimAng.p )
ply:SetPoseParameter( "move_x", 0 )
ply:SetPoseParameter( "move_y", 0 )
ply:SetPoseParameter( "move_yaw", 0 )
ply:SetPoseParameter( "move_scale", 0 )
AimAng.p = 0;
AimAng.r = 0;
ply:SetRenderAngles( AimAng )
ply:SetEyeTarget( driving:GetPos() )
end

View File

@@ -0,0 +1,251 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local PANEL = {}
AccessorFunc( PANEL, "m_bHangOpen", "HangOpen" )
function PANEL:Init()
--
-- This makes it so that when you're hovering over this panel
-- you can `click` on the world. Your viewmodel will aim etc.
--
self:SetWorldClicker( true )
self.Canvas = vgui.Create( "DCategoryList", self )
self.m_bHangOpen = false
self:Dock( FILL )
end
function PANEL:Open()
self:SetHangOpen( false )
-- If the spawn menu is open, try to close it..
if ( IsValid( g_SpawnMenu ) && g_SpawnMenu:IsVisible() ) then
g_SpawnMenu:Close( true )
end
if ( self:IsVisible() ) then return end
CloseDermaMenus()
self:MakePopup()
self:SetVisible( true )
self:SetKeyboardInputEnabled( false )
self:SetMouseInputEnabled( true )
RestoreCursorPosition()
local bShouldShow = hook.Run( "ContextMenuShowTool" )
local bShow = bShouldShow == nil or bShouldShow
-- Set up the active panel..
if ( bShow && IsValid( spawnmenu.ActiveControlPanel() ) ) then
self.OldParent = spawnmenu.ActiveControlPanel():GetParent()
self.OldPosX, self.OldPosY = spawnmenu.ActiveControlPanel():GetPos()
spawnmenu.ActiveControlPanel():SetParent( self )
self.Canvas:Clear()
self.Canvas:AddItem( spawnmenu.ActiveControlPanel() )
self.Canvas:Rebuild()
self.Canvas:SetVisible( true )
else
self.Canvas:SetVisible( false )
end
self:InvalidateLayout( true )
end
function PANEL:Close( bSkipAnim )
if ( self:GetHangOpen() ) then
self:SetHangOpen( false )
return
end
RememberCursorPosition()
CloseDermaMenus()
self:SetKeyboardInputEnabled( false )
self:SetMouseInputEnabled( false )
self:SetAlpha( 255 )
self:SetVisible( false )
self:RestoreControlPanel()
end
function PANEL:PerformLayout()
if ( IsValid( spawnmenu.ActiveControlPanel() ) ) then
spawnmenu.ActiveControlPanel():InvalidateLayout( true )
local Tall = math.min( spawnmenu.ActiveControlPanel():GetTall() + 10, ScrH() * 0.8 )
if ( self.Canvas:GetTall() != Tall ) then self.Canvas:SetTall( Tall ) end
if ( self.Canvas:GetWide() != 320 ) then self.Canvas:SetWide( 320 ) end
self.Canvas:SetPos( ScrW() - self.Canvas:GetWide() - 50, ScrH() - 50 - Tall )
self.Canvas:InvalidateLayout( true )
end
end
function PANEL:StartKeyFocus( pPanel )
self:SetKeyboardInputEnabled( true )
self:SetHangOpen( true )
end
function PANEL:EndKeyFocus( pPanel )
self:SetKeyboardInputEnabled( false )
end
function PANEL:RestoreControlPanel()
-- Restore the active panel
if ( !spawnmenu.ActiveControlPanel() ) then return end
if ( !self.OldParent ) then return end
spawnmenu.ActiveControlPanel():SetParent( self.OldParent )
spawnmenu.ActiveControlPanel():SetPos( self.OldPosX, self.OldPosY )
self.OldParent = nil
end
--
-- Note here: EditablePanel is important! Child panels won't be able to get
-- keyboard input if it's a DPanel or a Panel. You need to either have an EditablePanel
-- or a DFrame (which is derived from EditablePanel) as your first panel attached to the system.
--
vgui.Register( "ContextMenu", PANEL, "EditablePanel" )
function CreateContextMenu()
if ( !hook.Run( "ContextMenuEnabled" ) ) then return end
if ( IsValid( g_ContextMenu ) ) then
g_ContextMenu:Remove()
g_ContextMenu = nil
end
g_ContextMenu = vgui.Create( "ContextMenu" )
if ( !IsValid( g_ContextMenu ) ) then return end
g_ContextMenu:SetVisible( false )
--
-- We're blocking clicks to the world - but we don't want to
-- so feed clicks to the proper functions..
--
g_ContextMenu.OnMousePressed = function( p, code )
hook.Run( "GUIMousePressed", code, gui.ScreenToVector( input.GetCursorPos() ) )
end
g_ContextMenu.OnMouseReleased = function( p, code )
hook.Run( "GUIMouseReleased", code, gui.ScreenToVector( input.GetCursorPos() ) )
end
hook.Run( "ContextMenuCreated", g_ContextMenu )
local IconLayout = g_ContextMenu:Add( "DIconLayout" )
IconLayout:SetBorder( 8 )
IconLayout:SetSpaceX( 8 )
IconLayout:SetSpaceY( 8 )
IconLayout:SetLayoutDir( LEFT )
IconLayout:SetWorldClicker( true )
IconLayout:SetStretchWidth( true )
IconLayout:SetStretchHeight( false ) -- No infinite re-layouts
IconLayout:Dock( LEFT )
-- This overrides DIconLayout's OnMousePressed (which is inherited from DPanel), but we don't care about that in this case
IconLayout.OnMousePressed = function( s, ... ) s:GetParent():OnMousePressed( ... ) end
for k, v in pairs( list.Get( "DesktopWindows" ) ) do
local icon = IconLayout:Add( "DButton" )
icon:SetText( "" )
icon:SetSize( 80, 82 )
icon.Paint = function() end
local image = icon:Add( "DImage" )
image:SetImage( v.icon )
image:SetSize( 64, 64 )
image:Dock( TOP )
image:DockMargin( 8, 0, 8, 0 )
local label = icon:Add( "DLabel" )
label:Dock( BOTTOM )
label:SetText( v.title )
label:SetContentAlignment( 5 )
label:SetTextColor( color_white )
label:SetExpensiveShadow( 1, Color( 0, 0, 0, 200 ) )
icon.DoClick = function()
--
-- v might have changed using autorefresh so grab it again
--
local newv = list.Get( "DesktopWindows" )[ k ]
if ( v.onewindow and IsValid( icon.Window ) ) then
icon.Window:Center()
return
end
-- Make the window
icon.Window = g_ContextMenu:Add( "DFrame" )
icon.Window:SetSize( newv.width, newv.height )
icon.Window:SetTitle( newv.title )
icon.Window:Center()
newv.init( icon, icon.Window )
end
end
end
function GM:OnContextMenuOpen()
-- Let the gamemode decide whether we should open or not..
if ( !hook.Call( "ContextMenuOpen", self ) ) then return end
if ( IsValid( g_ContextMenu ) && !g_ContextMenu:IsVisible() ) then
g_ContextMenu:Open()
menubar.ParentTo( g_ContextMenu )
end
hook.Call( "ContextMenuOpened", self )
end
function GM:OnContextMenuClose()
if ( IsValid( g_ContextMenu ) ) then g_ContextMenu:Close() end
hook.Call( "ContextMenuClosed", self )
end

View File

@@ -0,0 +1,443 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
--
-- Note: This is only really here as a layer between the spawnmenu
-- and the DForm Derma control. You shouldn't ever really be
-- calling AddControl. If you're writing new code - don't call
-- AddControl!! Add stuff directly using the DForm member functions!
--
include( "controls/manifest.lua" )
local PANEL = {}
AccessorFunc( PANEL, "m_bInitialized", "Initialized" )
function PANEL:Init()
self:SetInitialized( false )
end
function PANEL:ClearControls()
self:Clear()
end
function PANEL:GetEmbeddedPanel()
return self
end
function PANEL:AddPanel( pnl )
self:AddItem( pnl, nil )
self:InvalidateLayout()
end
function PANEL:MatSelect( strConVar, tblOptions, bAutoStretch, iWidth, iHeight )
local MatSelect = vgui.Create( "MatSelect", self )
Derma_Hook( MatSelect.List, "Paint", "Paint", "Panel" )
MatSelect:SetConVar( strConVar )
if ( bAutoStretch != nil ) then MatSelect:SetAutoHeight( bAutoStretch ) end
if ( iWidth != nil ) then MatSelect:SetItemWidth( iWidth ) end
if ( iHeight != nil ) then MatSelect:SetItemHeight( iHeight ) end
if ( tblOptions != nil ) then
for k, v in pairs( tblOptions ) do
local nam = isnumber( k ) and v or k
MatSelect:AddMaterial( nam, v )
end
end
self:AddPanel( MatSelect )
return MatSelect
end
function PANEL:ToolPresets( group, cvarlist )
local preset = vgui.Create( "ControlPresets", self )
preset:SetPreset( group )
preset:AddOption( "#preset.default", cvarlist )
for k, v in pairs( cvarlist ) do
preset:AddConVar( k )
end
self:AddItem( preset )
return preset
end
function PANEL:KeyBinder( label1, convar1, label2, convar2 )
local binder = vgui.Create( "CtrlNumPad", self )
binder:SetLabel1( label1 )
binder:SetConVar1( convar1 )
if ( label2 != nil and convar2 != nil ) then
binder:SetLabel2( label2 )
binder:SetConVar2( convar2 )
end
self:AddPanel( binder )
return binder
end
function PANEL:ColorPicker( label, convarR, convarG, convarB, convarA )
local color = vgui.Create( "CtrlColor", self )
color:Dock( TOP )
color:SetLabel( label )
color:SetConVarR( convarR )
color:SetConVarG( convarG )
color:SetConVarB( convarB )
if ( convarA != nil ) then
color:SetConVarA( convarA )
end
self:AddPanel( color )
return color
end
function PANEL:FillViaTable( Table )
self:SetInitialized( true )
self:SetName( Table.Text )
--
-- If we have a function to create the control panel, use that
--
if ( Table.ControlPanelBuildFunction ) then
Table.ControlPanelBuildFunction( self )
end
end
function PANEL:FillViaFunction( func )
func( self )
end
function PANEL:ControlValues( data )
if ( data.label) then
self:SetLabel( data.label )
end
if ( data.closed ) then
self:SetExpanded( false )
end
end
function PANEL:AddControl( control, data )
local data = table.LowerKeyNames( data )
local original = control
control = string.lower( control )
-- Retired
if ( control == "header" ) then
if ( data.description ) then
local ctrl = self:Help( data.description )
return ctrl
end
return
end
if ( control == "textbox" ) then
local ctrl = self:TextEntry( data.label or "Untitled", data.command )
return ctrl
end
if ( control == "label" ) then
local ctrl = self:Help( data.text )
return ctrl
end
if ( control == "checkbox" or control == "toggle" ) then
local ctrl = self:CheckBox( data.label or "Untitled", data.command )
if ( data.help ) then
self:ControlHelp( data.label .. ".help" )
end
return ctrl
end
if ( control == "slider" ) then
local Decimals = 0
if ( data.type && string.lower(data.type) == "float" ) then Decimals = 2 end
local ctrl = self:NumSlider( data.label or "Untitled", data.command, data.min or 0, data.max or 100, Decimals )
if ( data.help ) then
self:ControlHelp( data.label .. ".help" )
end
if ( data.default ) then
ctrl:SetDefaultValue( data.default )
elseif ( data.command ) then
local cvar = GetConVar( data.command )
if ( cvar ) then
ctrl:SetDefaultValue( cvar:GetDefault() )
end
end
return ctrl
end
if ( control == "propselect" ) then
local ctrl = vgui.Create( "PropSelect", self )
ctrl:ControlValues( data ) -- Yack.
self:AddPanel( ctrl )
return ctrl
end
if ( control == "matselect" ) then
local ctrl = vgui.Create( "MatSelect", self )
ctrl:ControlValues( data ) -- Yack.
self:AddPanel( ctrl )
Derma_Hook( ctrl.List, "Paint", "Paint", "Panel" )
return ctrl
end
if ( control == "ropematerial" ) then
local ctrl = vgui.Create( "RopeMaterial", self )
ctrl:SetConVar( data.convar )
self:AddPanel( ctrl )
return ctrl
end
if ( control == "button" ) then
local ctrl = vgui.Create( "DButton", self )
-- Note: Buttons created this way use the old method of calling commands,
-- via LocalPlayer:ConCommand. This way is flawed. This way is legacy.
-- The new way is to make buttons via controlpanel:Button( name, command, commandarg1, commandarg2 ) etc
if ( data.command ) then
function ctrl:DoClick() LocalPlayer():ConCommand( data.command ) end
end
ctrl:SetText( data.label or data.text or "No Label" )
self:AddPanel( ctrl )
return ctrl
end
if ( control == "numpad" ) then
local ctrl = vgui.Create( "CtrlNumPad", self )
ctrl:SetConVar1( data.command )
ctrl:SetConVar2( data.command2 )
ctrl:SetLabel1( data.label )
ctrl:SetLabel2( data.label2 )
self:AddPanel( ctrl )
return ctrl
end
if ( control == "color" ) then
local ctrl = vgui.Create( "CtrlColor", self )
ctrl:SetLabel( data.label )
ctrl:SetConVarR( data.red )
ctrl:SetConVarG( data.green )
ctrl:SetConVarB( data.blue )
ctrl:SetConVarA( data.alpha )
self:AddPanel( ctrl )
return ctrl
end
if ( control == "combobox" ) then
if ( tostring( data.menubutton ) == "1" ) then
local ctrl = vgui.Create( "ControlPresets", self )
ctrl:SetPreset( data.folder )
if ( data.options ) then
for k, v in pairs( data.options ) do
ctrl:AddOption( k, v )
end
end
if ( data.cvars ) then
for k, v in pairs( data.cvars ) do
ctrl:AddConVar( v )
end
end
self:AddPanel( ctrl )
return ctrl
end
control = "listbox"
end
if ( control == "listbox" ) then
if ( data.height ) then
local ctrl = vgui.Create( "DListView" )
ctrl:SetMultiSelect( false )
ctrl:AddColumn( data.label or "unknown" )
if ( data.options ) then
for k, v in pairs( data.options ) do
local line = ctrl:AddLine( k )
line.data = v
-- This is kind of broken because it only checks one convar
-- instead of all of them. But this is legacy. It will do for now.
for k, v in pairs( line.data ) do
if ( GetConVarString( k ) == tostring( v ) ) then
line:SetSelected( true )
end
end
end
end
ctrl:SetTall( data.height )
ctrl:SortByColumn( 1, false )
function ctrl:OnRowSelected( LineID, Line )
for k, v in pairs( Line.data ) do
RunConsoleCommand( k, v )
end
end
self:AddItem( ctrl )
return ctrl
else
local ctrl = vgui.Create( "CtrlListBox", self )
if ( data.options ) then
for k, v in pairs( data.options ) do
ctrl:AddOption( k, v )
end
end
local left = vgui.Create( "DLabel", self )
left:SetText( data.label )
left:SetDark( true )
ctrl:SetHeight( 25 )
ctrl:Dock( TOP )
self:AddItem( left, ctrl )
return ctrl
end
end
if ( control == "materialgallery" ) then
local ctrl = vgui.Create( "MatSelect", self )
--ctrl:ControlValues( data ) -- Yack.
ctrl:SetItemWidth( data.width or 32 )
ctrl:SetItemHeight( data.height or 32 )
ctrl:SetNumRows( data.rows or 4 )
ctrl:SetConVar( data.convar or nil )
Derma_Hook( ctrl.List, "Paint", "Paint", "Panel" )
for name, tab in pairs( data.options ) do
local mat = tab.material
local value = tab.value
tab.material = nil
tab.value = nil
ctrl:AddMaterialEx( name, mat, value, tab )
end
self:AddPanel( ctrl )
return ctrl
end
local ctrl = vgui.Create( original, self )
-- Fallback for scripts that relied on the old behaviour
if ( !ctrl ) then
ctrl = vgui.Create( control, self )
end
if ( ctrl ) then
if ( ctrl.ControlValues ) then
ctrl:ControlValues( data )
end
self:AddPanel( ctrl )
return ctrl
end
MsgN( "UNHANDLED CONTROL: ", control )
PrintTable( data )
MsgN( "\n\n" )
end
vgui.Register( "ControlPanel", PANEL, "DForm" )

View File

@@ -0,0 +1,173 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
include( "preset_editor.lua" )
local PANEL = {}
function PANEL:Init()
self.DropDown = vgui.Create( "DComboBox", self )
self.DropDown.OnSelect = function( dropdown, index, value, data ) self:OnSelect( index, value, data ) end
self.DropDown:SetText( "Presets" )
self.DropDown:Dock( FILL )
self.Button = vgui.Create( "DImageButton", self )
self.Button.DoClick = function() self:OpenPresetEditor() end
self.Button:Dock( RIGHT )
self.Button:SetToolTip( "#preset.edit" )
self.Button:SetImage( "icon16/wrench.png" )
self.Button:SetStretchToFit( false )
self.Button:SetSize( 20, 20 )
self.Button:DockMargin( 0, 0, 0, 0 )
self.AddButton = vgui.Create( "DImageButton", self )
self.AddButton.DoClick = function()
if ( !IsValid( self ) ) then return end
self:QuickSavePreset()
end
self.AddButton:Dock( RIGHT )
self.AddButton:SetToolTip( "#preset.add" )
self.AddButton:SetImage( "icon16/add.png" )
self.AddButton:SetStretchToFit( false )
self.AddButton:SetSize( 20, 20 )
self.AddButton:DockMargin( 2, 0, 0, 0 )
self:SetTall( 20 )
self.Options = {}
self.ConVars = {}
end
function PANEL:SetLabel( strName )
self.Label:SetText( strName )
end
function PANEL:AddOption( strName, data )
self.DropDown:AddChoice( strName, data )
self.Options[ strName ] = data
end
function PANEL:SetOptions( Options )
if ( Options ) then
table.Merge( self.Options, Options )
end
end
function PANEL:OnSelect( index, value, data )
if ( !data ) then return end
for k, v in pairs( data ) do
RunConsoleCommand( k, v )
end
end
function PANEL:QuickSaveInternal( text )
local tabValues = {}
for k, v in pairs( self:GetConVars() ) do
tabValues[ v ] = GetConVarString( v )
end
presets.Add( self.m_strPreset, text, tabValues )
self:Update()
end
function PANEL:QuickSavePreset()
Derma_StringRequest( "#preset.saveas_title", "#preset.saveas_desc", "", function( text )
if ( !text || text:Trim() == "" ) then presets.BadNameAlert() return end
if ( presets.Exists( self.m_strPreset, text ) ) then
presets.OverwritePresetPrompt( function()
self:QuickSaveInternal( text )
end )
return
end
self:QuickSaveInternal( text )
end )
end
function PANEL:OpenPresetEditor()
if ( !self.m_strPreset ) then return end
self.Window = vgui.Create( "PresetEditor" )
self.Window:MakePopup()
self.Window:Center()
self.Window:SetType( self.m_strPreset )
self.Window:SetConVars( self:GetConVars() )
self.Window:SetPresetControl( self )
end
function PANEL:AddConVar( convar )
table.insert( self.ConVars, convar )
end
function PANEL:GetConVars()
return self.ConVars
end
function PANEL:SetPreset( strName )
self.m_strPreset = strName
self:ReloadPresets()
end
function PANEL:ReloadPresets()
self:Clear()
for name, data in pairs( self.Options ) do
self:AddOption( name, data )
end
local Presets = presets.GetTable( self.m_strPreset )
local sortedPresets, i = {}, 1
for name in pairs( Presets ) do
sortedPresets[ i ] = name
i = i + 1
end
table.sort( sortedPresets )
for _, name in ipairs( sortedPresets ) do
self.DropDown:AddChoice( name, Presets[ name ] )
end
end
function PANEL:Update()
self:ReloadPresets()
end
function PANEL:Clear()
self.DropDown:Clear()
end
vgui.Register( "ControlPresets", PANEL, "Panel" )

View File

@@ -0,0 +1,59 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local PANEL = {}
function PANEL:Init()
self.Mixer = vgui.Create( "DColorMixer", self )
self.Mixer:Dock( FILL )
self:SetTall( 245 )
end
function PANEL:PerformLayout( x, y )
-- Magic number, the target width for self.Mixer
-- Number picked to that Palette would not have a gap, at button size 17
local targetWidth = 272
-- Don't scale the Mixer in width, keep it to the target width
local s = math.max( ( self:GetWide() - targetWidth ) / 2, 0 )
self.Mixer:DockMargin( s, 8, s, 0 )
-- Ugly hack, because of the docking system
self.OldMixerW = self.OldMixerW or self.Mixer:GetWide()
-- Number of panels in one row
local ColorRows = math.ceil( #self.Mixer.Palette:GetChildren() / 3 )
-- Set the button size closest to fill the Mixer width
local bSize = math.floor( self:GetWide() / ColorRows )
self.Mixer.Palette:SetButtonSize( math.min( bSize, 17 ) )
end
function PANEL:Paint()
-- Invisible background!
end
function PANEL:SetLabel( text ) self.Mixer:SetLabel( text ) end
function PANEL:SetConVarR( cvar ) self.Mixer:SetConVarR( cvar ) end
function PANEL:SetConVarG( cvar ) self.Mixer:SetConVarG( cvar ) end
function PANEL:SetConVarB( cvar ) self.Mixer:SetConVarB( cvar ) end
function PANEL:SetConVarA( cvar ) self.Mixer:SetConVarA( cvar ) end
function PANEL:GetConVarR() return self.Mixer:GetConVarR() end
function PANEL:GetConVarG() return self.Mixer:GetConVarG() end
function PANEL:GetConVarB() return self.Mixer:GetConVarB() end
function PANEL:GetConVarA() return self.Mixer:GetConVarA() end
vgui.Register( "CtrlColor", PANEL, "DPanel" )

View File

@@ -0,0 +1,118 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local PANEL = {}
--AccessorFunc( PANEL, "m_ConVarR", "ConVarR" )
--[[---------------------------------------------------------
Name: Init
-----------------------------------------------------------]]
function PANEL:Init()
self.ConVars = {}
self.Options = {}
end
--[[---------------------------------------------------------
Name: AddOption
-----------------------------------------------------------]]
function PANEL:AddOption( strName, tabConVars )
self:AddChoice( strName, tabConVars )
for k, v in pairs( tabConVars ) do
self.ConVars[ k ] = 1
end
end
--[[---------------------------------------------------------
Name: OnSelect
-----------------------------------------------------------]]
function PANEL:OnSelect( index, value, data )
for k, v in pairs( data ) do
RunConsoleCommand( k, tostring( v ) )
end
end
--[[---------------------------------------------------------
Name: Think
-----------------------------------------------------------]]
function PANEL:Think( CheckConvarChanges )
self:CheckConVarChanges()
end
--[[---------------------------------------------------------
Name: ConVarsChanged
-----------------------------------------------------------]]
function PANEL:ConVarsChanged()
for k, v in pairs( self.ConVars ) do
if ( self[ k ] == nil ) then return true end
if ( self[ k ] != GetConVarString( k ) ) then return true end
end
return false
end
--[[---------------------------------------------------------
Name: CheckForMatch
-----------------------------------------------------------]]
function PANEL:CheckForMatch( cvars )
if ( table.IsEmpty( cvars ) ) then return false end
for k, v in pairs( cvars ) do
if ( tostring(v) != GetConVarString( k ) ) then
return false
end
end
return true
end
--[[---------------------------------------------------------
Name: CheckConVarChanges
-----------------------------------------------------------]]
function PANEL:CheckConVarChanges()
if (!self:ConVarsChanged()) then return end
for k, v in pairs( self.ConVars ) do
self[ k ] = GetConVarString( k )
end
for k, v in pairs( self.Data ) do
if ( self:CheckForMatch( v ) ) then
self:SetText( self:GetOptionText(k) )
return
end
end
end
vgui.Register( "CtrlListBox", PANEL, "DComboBox" )

View File

@@ -0,0 +1,117 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local PANEL = {}
AccessorFunc( PANEL, "m_ConVar1", "ConVar1" )
AccessorFunc( PANEL, "m_ConVar2", "ConVar2" )
--[[---------------------------------------------------------
Name: Init
-----------------------------------------------------------]]
function PANEL:Init()
self.NumPad1 = vgui.Create( "DBinder", self )
self.Label1 = vgui.Create( "DLabel", self )
self.Label1:SetDark( true )
self.NumPad2 = vgui.Create( "DBinder", self )
self.Label2 = vgui.Create( "DLabel", self )
self.Label2:SetDark( true )
self:SetPaintBackground( false )
self:SetHeight( 200 )
end
--[[---------------------------------------------------------
Name: SetLabel1
-----------------------------------------------------------]]
function PANEL:SetLabel1( txt )
if ( !txt ) then return end
self.Label1:SetText( txt )
end
--[[---------------------------------------------------------
Name: SetLabel2
-----------------------------------------------------------]]
function PANEL:SetLabel2( txt )
if ( !txt ) then return end
self.Label2:SetText( txt )
end
--[[---------------------------------------------------------
Name: SetConVar1
-----------------------------------------------------------]]
function PANEL:SetConVar1( cvar )
self.NumPad1:SetConVar( cvar )
self.m_ConVar1 = cvar
end
--[[---------------------------------------------------------
Name: SetConVar2
-----------------------------------------------------------]]
function PANEL:SetConVar2( cvar )
self.NumPad2:SetConVar( cvar )
self.m_ConVar2 = cvar
end
--[[---------------------------------------------------------
Name: Init
-----------------------------------------------------------]]
function PANEL:PerformLayout()
self:SetTall( 70 )
self.NumPad1:InvalidateLayout( true )
self.NumPad1:SetSize( 100, 50 )
if ( self.m_ConVar2 ) then
self.NumPad2:InvalidateLayout( true )
self.NumPad2:SetSize( 100, 50 )
end
if ( !self.m_ConVar2 ) then
self.Label1:SizeToContents()
self.NumPad2:SetVisible( false )
self.Label2:SetVisible( false )
self.NumPad1:CenterHorizontal( 0.5 )
self.NumPad1:AlignTop( 20 )
self.Label1:CenterHorizontal()
self.Label1:AlignTop( 0 )
else
self.Label1:SizeToContents()
self.Label2:SizeToContents()
self.NumPad2:SetVisible( true )
self.Label2:SetVisible( true )
self.NumPad1:CenterHorizontal( 0.25 )
self.Label1:CenterHorizontal( 0.25 )
self.NumPad1:AlignTop( 20 )
self.NumPad2:CenterHorizontal( 0.75 )
self.Label2:CenterHorizontal( 0.75 )
self.NumPad2:AlignTop( 20 )
self.Label2:AlignTop( 0 )
end
end
vgui.Register( "CtrlNumPad", PANEL, "DPanel" )

View File

@@ -0,0 +1,17 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
include( "control_presets.lua" )
include( "ropematerial.lua" )
include( "ctrlnumpad.lua" )
include( "ctrlcolor.lua" )
include( "ctrllistbox.lua" )

View File

@@ -0,0 +1,272 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local PANEL = {}
AccessorFunc( PANEL, "m_strType", "Type" )
AccessorFunc( PANEL, "m_ConVars", "ConVars" )
AccessorFunc( PANEL, "m_PresetControl", "PresetControl" )
function PANEL:Init()
self:SetSize( 450, 350 )
self:SetMinWidth( 450 )
self:SetMinHeight( 350 )
self:SetSizable( true )
self:SetTitle( "#preset.editor" )
self:DockPadding( 6, 29, 6, 6 )
local pnlTop = vgui.Create( "Panel", self )
pnlTop:Dock( FILL )
pnlTop:DockMargin( 0, 0, 0, 5 )
self.PresetList = vgui.Create( "DListView", pnlTop )
self.PresetList:Dock( LEFT )
self.PresetList:DockMargin( 0, 0, 5, 0 )
self.PresetList:SetWide( 150 )
self.PresetList:SetMultiSelect( false )
self.PresetList:SetSortable( false )
self.PresetList.OnRowSelected = function( s, idx, pnl ) self:OnPresetSelected( pnl ) end
local pnlEditor = vgui.Create( "DPanel", pnlTop )
pnlEditor:Dock( FILL )
self.pnlDetails = vgui.Create( "DProperties", pnlEditor )
self.pnlDetails:Dock( FILL )
self.pnlDetails:DockMargin( 5, 5, 5, 5 )
----------
local pnlModify = vgui.Create( "Panel", pnlEditor )
pnlModify:Dock( BOTTOM )
pnlModify:SetTall( 20 )
pnlModify:DockMargin( 5, 0, 5, 5 )
local btnDelete = vgui.Create( "DButton", pnlModify )
btnDelete.DoClick = function() self:Delete() end
btnDelete:SetTooltip( "#preset.delete" )
btnDelete:SetImage( "icon16/bin.png" )
btnDelete:SetText( "" )
btnDelete:Dock( RIGHT )
btnDelete:SetWide( 25 )
btnDelete:SetEnabled( false )
self.txtRename = vgui.Create( "DTextEntry", pnlModify )
self.txtRename:Dock( FILL )
self.txtRename:SetEnabled( false )
self.txtRename:SetTooltip( "#preset.rename" )
local btnRename = vgui.Create( "DButton", pnlModify )
btnRename:SetTooltip( "#preset.save" )
btnRename:SetImage( "icon16/disk.png" )
btnRename:SetText( "" )
btnRename:Dock( RIGHT )
btnRename:DockMargin( 5, 0, 5, 0 )
btnRename:SetWide( 24 )
btnRename.DoClick = function() self:SaveChanges() end
btnRename:SetEnabled( false )
----------
local bottom = vgui.Create( "Panel", self )
bottom:Dock( BOTTOM )
bottom:SetTall( 30 )
self.pnlAdd = vgui.Create( "DPanel", bottom )
self.pnlAdd:Dock( FILL )
self.pnlAdd:DockPadding( 5, 5, 5, 5 )
self.pnlAdd:DockMargin( 0, 0, 5, 0 )
self.txtName = vgui.Create( "DTextEntry", self.pnlAdd )
self.txtName:SetTooltip( "#preset.addnew_field" )
self.txtName:Dock( FILL )
self.txtName:DockMargin( 0, 0, 5, 0 )
self.txtName.OnChange = function( s ) self.btnAdd:SetEnabled( s:GetText():Trim() != "" ) end
self.btnAdd = vgui.Create( "DButton", self.pnlAdd )
self.btnAdd:SetText( "#preset.addnew" )
self.btnAdd:Dock( RIGHT )
self.btnAdd:SetEnabled( false )
self.btnAdd.DoClick = function() self:Add() end
----------
local pnlClose = vgui.Create( "DPanel", bottom )
pnlClose:Dock( RIGHT )
pnlClose:SetWide( 100 )
pnlClose:DockPadding( 5, 5, 5, 5 )
local btnCloseIt = vgui.Create( "DButton", pnlClose )
btnCloseIt:SetText( "#preset.close" )
btnCloseIt:Dock( FILL )
btnCloseIt.DoClick = function() self:Remove() end
end
function PANEL:SetType( strType )
self.m_strType = strType
self.PresetList:AddColumn( self:GetType() )
self:Update()
end
function PANEL:OnPresetSelected( item )
local name = item:GetValue( 1 )
self.txtRename:SetText( name )
for id, pnl in ipairs( self.txtRename:GetParent():GetChildren() ) do pnl:SetEnabled( true ) end
self.pnlDetails:Clear()
for cvar, val in SortedPairs( item:GetTable().Data ) do
local Row = self.pnlDetails:CreateRow( name, cvar:lower() )
if ( tonumber( val ) != nil && false ) then
Row:Setup( "Float", { min = 0, max = 1000 } )
Row:SetValue( val )
else
Row:Setup( "Generic" )
end
Row:SetValue( val )
Row.__Value = val
Row.DataChanged = function( s, value ) Row.__Value = value end
end
end
function PANEL:Update()
self.PresetList:Clear()
self.pnlDetails:Clear()
self.txtRename:SetText( "" )
local Presets = presets.GetTable( self:GetType() )
local sortedPresets, i = {}, 1
for name in pairs( Presets ) do
sortedPresets[i] = name
i = i + 1
end
table.sort( sortedPresets )
for _, name in ipairs( sortedPresets ) do
local item = self.PresetList:AddLine( name )
item.Data = Presets[ name ]
end
end
function PANEL:SelectPresetByName( name )
for id, line in pairs( self.PresetList:GetLines() ) do
if ( line:GetValue( 1 ) != name ) then continue end
self.PresetList:SelectItem( line )
end
end
function PANEL:Delete()
if ( !self.PresetList:GetSelectedLine() || !IsValid( self.PresetList:GetLine( self.PresetList:GetSelectedLine() ) ) ) then return end
local Selected = self.PresetList:GetLine( self.PresetList:GetSelectedLine() ):GetValue( 1 ):Trim()
if ( Selected == "" ) then return end
presets.Remove( self:GetType(), Selected )
self:Update()
if ( self:GetPresetControl() ) then self:GetPresetControl():Update() end
end
function PANEL:SaveChangesInternal( Selected, ToName )
local tabValues = {}
local cat = self.pnlDetails:GetCategory( Selected )
-- WARNING: This will discard ConVars in the preset that no longer exist on the tool/whatever this preset is for
for k, v in pairs( self:GetConVars() ) do
if ( cat:GetRow( v:lower() ) ) then
tabValues[ v:lower() ] = cat:GetRow( v:lower() ).__Value
end
end
presets.Rename( self:GetType(), Selected, ToName ) -- Raname the preset if necessary
presets.Add( self:GetType(), ToName, tabValues ) -- Update the values
self:Update()
self.txtRename:SetText( "" )
self:SelectPresetByName( ToName )
if ( self:GetPresetControl() ) then self:GetPresetControl():Update() end
end
function PANEL:SaveChanges()
if ( !self.PresetList:GetSelectedLine() || !IsValid( self.PresetList:GetLine( self.PresetList:GetSelectedLine() ) ) ) then return end
local Selected = self.PresetList:GetLine( self.PresetList:GetSelectedLine() ):GetValue( 1 ):Trim()
if ( Selected == "" ) then return end
local ToName = self.txtRename:GetValue():Trim()
if ( !ToName || ToName == "" ) then presets.BadNameAlert() return end
if ( presets.Exists( self:GetType(), ToName ) && Selected != ToName ) then
presets.OverwritePresetPrompt( function()
self:SaveChangesInternal( Selected, ToName )
end )
return
end
self:SaveChangesInternal( Selected, ToName )
end
function PANEL:InternalAdd( ToName )
local tabValues = {}
for k, v in pairs( self:GetConVars() ) do
tabValues[ v ] = GetConVarString( v:lower() )
end
presets.Add( self:GetType(), ToName, tabValues )
self:Update()
self.txtName:SetText( "" )
self:SelectPresetByName( ToName )
if ( self:GetPresetControl() ) then self:GetPresetControl():Update() end
end
function PANEL:Add()
if ( !self:GetConVars() ) then return end
local ToName = self.txtName:GetValue():Trim()
if ( !ToName || ToName == "" ) then presets.BadNameAlert() return end
if ( presets.Exists( self:GetType(), ToName ) ) then
presets.OverwritePresetPrompt( function()
self:InternalAdd( ToName )
end )
return
end
self:InternalAdd( ToName )
end
vgui.Register( "PresetEditor", PANEL, "DFrame" )

View File

@@ -0,0 +1,46 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
list.Set( "RopeMaterials", "#ropematerial.rope", "cable/rope" )
list.Set( "RopeMaterials", "#ropematerial.cable", "cable/cable2" )
list.Set( "RopeMaterials", "#ropematerial.xbeam", "cable/xbeam" )
list.Set( "RopeMaterials", "#ropematerial.laser", "cable/redlaser" )
list.Set( "RopeMaterials", "#ropematerial.electric", "cable/blue_elec" )
list.Set( "RopeMaterials", "#ropematerial.physbeam", "cable/physbeam" )
list.Set( "RopeMaterials", "#ropematerial.hydra", "cable/hydra" )
local PANEL = {}
--[[---------------------------------------------------------
Name: Paint
-----------------------------------------------------------]]
function PANEL:Init()
self:SetItemWidth( 0.14 )
self:SetItemHeight( 0.3 )
self:SetAutoHeight( true )
local mats = list.Get( "RopeMaterials" )
for k, v in pairs( mats ) do
self:AddMaterial( k, v )
end
end
function PANEL:Paint( w, h )
draw.RoundedBox( 4, 0, 0, w, h, Color( 128, 128, 128, 255 ) )
end
vgui.Register( "RopeMaterial", PANEL, "MatSelect" )

View File

@@ -0,0 +1,67 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
include( "creationmenu/manifest.lua" )
local PANEL = {}
function PANEL:Init()
self.CreationTabs = {}
self:SetFadeTime( 0 )
self:Populate()
end
function PANEL:GetCreationTab( id )
return self.CreationTabs[ id ]
end
function PANEL:GetCreationTabs()
return self.CreationTabs
end
function PANEL:Populate()
local tabs = spawnmenu.GetCreationTabs()
for k, v in SortedPairsByMemberValue( tabs, "Order" ) do
--
-- Here we create a panel and populate it on the first paint
-- that way everything is created on the first view instead of
-- being created on load.
--
local pnl = vgui.Create( "Panel" )
local tab = self:AddSheet( k, pnl, v.Icon, nil, nil, v.Tooltip )
self.CreationTabs[ k ] = tab
-- Populate the panel
-- We have to add the timer to make sure g_Spawnmenu is available
-- in case some addon needs it ready when populating the creation tab.
timer.Simple( 0, function()
if ( !IsValid( pnl ) ) then return end
local childpnl = v.Function()
childpnl:SetParent( pnl )
childpnl:Dock( FILL )
end )
end
end
vgui.Register( "CreationMenu", PANEL, "DPropertySheet" )

View File

@@ -0,0 +1,110 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
include( "contenticon.lua" )
include( "postprocessicon.lua" )
include( "contentcontainer.lua" )
include( "contentsidebar.lua" )
include( "contenttypes/custom.lua" )
include( "contenttypes/npcs.lua" )
include( "contenttypes/weapons.lua" )
include( "contenttypes/entities.lua" )
include( "contenttypes/postprocess.lua" )
include( "contenttypes/vehicles.lua" )
include( "contenttypes/saves.lua" )
include( "contenttypes/dupes.lua" )
include( "contenttypes/gameprops.lua" )
include( "contenttypes/addonprops.lua" )
local PANEL = {}
AccessorFunc( PANEL, "m_pSelectedPanel", "SelectedPanel" )
function PANEL:Init()
self:SetPaintBackground( false )
self.CategoryTable = {}
self.HorizontalDivider = vgui.Create( "DHorizontalDivider", self )
self.HorizontalDivider:Dock( FILL )
self.HorizontalDivider:SetLeftWidth( 192 )
self.HorizontalDivider:SetLeftMin( 100 )
self.HorizontalDivider:SetRightMin( 100 )
if ( ScrW() >= 1024 ) then self.HorizontalDivider:SetLeftMin( 192 ) self.HorizontalDivider:SetRightMin( 400 ) end
self.HorizontalDivider:SetDividerWidth( 6 )
self.HorizontalDivider:SetCookieName( "SpawnMenuCreationMenuDiv" )
self.ContentNavBar = vgui.Create( "ContentSidebar", self.HorizontalDivider )
self.HorizontalDivider:SetLeft( self.ContentNavBar )
end
function PANEL:EnableModify()
self.ContentNavBar:EnableModify()
end
function PANEL:EnableSearch( ... )
self.ContentNavBar:EnableSearch( ... )
end
function PANEL:CallPopulateHook( HookName )
hook.Call( HookName, GAMEMODE, self, self.ContentNavBar.Tree, self.OldSpawnlists )
end
function PANEL:SwitchPanel( panel )
if ( IsValid( self.SelectedPanel ) ) then
self.SelectedPanel:SetVisible( false )
self.SelectedPanel = nil
end
self.SelectedPanel = panel
if ( !IsValid( panel ) ) then return end
self.HorizontalDivider:SetRight( self.SelectedPanel )
self.HorizontalDivider:InvalidateLayout( true )
self.SelectedPanel:SetVisible( true )
self:InvalidateParent()
end
function PANEL:OnSizeChanged()
self.HorizontalDivider:LoadCookies()
end
vgui.Register( "SpawnmenuContentPanel", PANEL, "DPanel" )
local function CreateContentPanel()
local ctrl = vgui.Create( "SpawnmenuContentPanel" )
ctrl.OldSpawnlists = ctrl.ContentNavBar.Tree:AddNode( "#spawnmenu.category.browse", "icon16/cog.png" )
ctrl:EnableModify()
hook.Call( "PopulatePropMenu", GAMEMODE )
ctrl:CallPopulateHook( "PopulateContent" )
ctrl.OldSpawnlists:MoveToFront()
ctrl.OldSpawnlists:SetExpanded( true )
return ctrl
end
spawnmenu.AddCreationTab( "#spawnmenu.content_tab", CreateContentPanel, "icon16/application_view_tile.png", -10 )

View File

@@ -0,0 +1,224 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local PANEL = {}
DEFINE_BASECLASS( "DScrollPanel" )
AccessorFunc( PANEL, "m_pControllerPanel", "ControllerPanel" )
AccessorFunc( PANEL, "m_strCategoryName", "CategoryName" )
AccessorFunc( PANEL, "m_bTriggerSpawnlistChange", "TriggerSpawnlistChange" )
--[[---------------------------------------------------------
Name: Init
-----------------------------------------------------------]]
function PANEL:Init()
self:SetPaintBackground( false )
self.IconList = vgui.Create( "DTileLayout", self:GetCanvas() )
self.IconList:SetBaseSize( 64 )
self.IconList:MakeDroppable( "SandboxContentPanel", true )
self.IconList:SetSelectionCanvas( true )
--self.IconList:SetUseLiveDrag( true )
self.IconList:Dock( TOP )
self.IconList.OnModified = function() self:OnModified() end
self.IconList.OnMousePressed = function( s, btn )
-- A bit of a hack
s:EndBoxSelection()
if ( btn != MOUSE_RIGHT ) then DPanel.OnMousePressed( s, btn ) end
end
self.IconList.OnMouseReleased = function( s, btn )
DPanel.OnMouseReleased( s, btn )
if ( btn != MOUSE_RIGHT || s:GetReadOnly() ) then return end
local menu = DermaMenu()
menu:AddOption( "#spawnmenu.newlabel", function()
local label = vgui.Create( "ContentHeader" )
self:Add( label )
-- Move the label to player's cursor, but make sure it's per line, not per icon
local x, y = self.IconList:ScreenToLocal( input.GetCursorPos() )
label:MoveToAfter( self.IconList:GetClosestChild( self:GetCanvas():GetWide(), y ) )
self:OnModified()
-- Scroll to the newly added item
--[[timer.Simple( 0, function()
local x, y = label:GetPos()
self.VBar:AnimateTo( y - self:GetTall() / 2 + label:GetTall() / 2, 0.5, 0, 0.5 )
end )]]
end ):SetIcon( "icon16/text_heading_1.png" )
menu:Open()
end
self.IconList.ContentContainer = self
end
function PANEL:Add( pnl )
self.IconList:Add( pnl )
if ( pnl.InstallMenu ) then
pnl:InstallMenu( self )
end
self:Layout()
end
function PANEL:Layout()
self.IconList:Layout()
self:InvalidateLayout()
end
function PANEL:PerformLayout( w, h )
BaseClass.PerformLayout( self, w, h )
self.IconList:SetMinHeight( self:GetTall() - 16 )
end
--[[---------------------------------------------------------
Name: RebuildAll
-----------------------------------------------------------]]
function PANEL:RebuildAll( proppanel )
for k, v in ipairs( self.IconList:GetChildren() ) do
v:RebuildSpawnIcon()
end
end
--[[---------------------------------------------------------
Name: GetCount
-----------------------------------------------------------]]
function PANEL:GetCount()
return #self.IconList:GetChildren()
end
function PANEL:Clear()
self.IconList:Clear()
end
function PANEL:SetTriggerSpawnlistChange( bTrigger )
self.m_bTriggerSpawnlistChange = bTrigger
self.IconList:SetReadOnly( !bTrigger )
end
function PANEL:OnModified()
if ( !self:GetTriggerSpawnlistChange() ) then return end
hook.Run( "SpawnlistContentChanged" )
end
function PANEL:ContentsToTable( contentpanel )
local tab = {}
for k, v in ipairs( self.IconList:GetChildren() ) do
v:ToTable( tab )
end
return tab
end
function PANEL:Copy()
local copy = vgui.Create( "ContentContainer", self:GetParent() )
copy:CopyBase( self )
copy.IconList:CopyContents( self.IconList )
return copy
end
vgui.Register( "ContentContainer", PANEL, "DScrollPanel" )
hook.Add( "SpawnlistOpenGenericMenu", "DragAndDropSelectionMenu", function( canvas )
if ( canvas:GetReadOnly() ) then return end
local selected = canvas:GetSelectedChildren()
local menu = DermaMenu()
-- This is less than ideal
local spawnicons = 0
local icon = nil
for id, pnl in pairs( selected ) do
if ( pnl.InternalAddResizeMenu ) then
spawnicons = spawnicons + 1
icon = pnl
end
end
if ( spawnicons > 0 ) then
icon:InternalAddResizeMenu( menu, function( w, h )
for id, pnl in pairs( selected ) do
if ( !pnl.InternalAddResizeMenu ) then continue end
pnl:SetSize( w, h )
pnl:InvalidateLayout( true )
pnl:GetParent():OnModified()
pnl:GetParent():Layout()
pnl:SetModel( pnl:GetModelName(), pnl:GetSkinID(), pnl:GetBodyGroup() )
end
end, language.GetPhrase( "spawnmenu.menu.resizex" ):format( spawnicons ) )
menu:AddOption( language.GetPhrase( "spawnmenu.menu.rerenderx" ):format( spawnicons ), function()
for id, pnl in pairs( selected ) do
if ( !pnl.RebuildSpawnIcon ) then continue end
pnl:RebuildSpawnIcon()
end
end ):SetIcon( "icon16/picture.png" )
end
menu:AddSpacer()
menu:AddOption( language.GetPhrase( "spawnmenu.menu.deletex" ):format( #selected ), function()
for k, v in pairs( selected ) do
v:Remove()
end
hook.Run( "SpawnlistContentChanged" )
end ):SetIcon( "icon16/bin_closed.png" )
menu:Open()
end )

View File

@@ -0,0 +1,140 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
surface.CreateFont( "ContentHeader", {
font = "Helvetica",
size = 50,
weight = 1000
} )
local PANEL = {}
function PANEL:Init()
self:SetFont( "ContentHeader" )
self:SetBright( true )
self:SetExpensiveShadow( 2, Color( 0, 0, 0, 130 ) )
self:SetSize( 64, 64 )
self.OwnLine = true
self:SetAutoStretch( true )
end
function PANEL:PerformLayout()
self:SizeToContents()
end
function PANEL:SizeToContents()
local w = self:GetContentSize()
-- Don't let the text overflow the parent's width
if ( IsValid( self:GetParent() ) ) then
w = math.min( w, self:GetParent():GetWide() - 32 )
end
-- Add a bit more room so it looks nice as a textbox :)
-- And make sure it has at least some width
self:SetSize( math.max( w, 64 ) + 16, 64 )
end
function PANEL:ToTable( bigtable )
local tab = {}
tab.type = "header"
tab.text = self:GetText()
table.insert( bigtable, tab )
end
function PANEL:Copy()
local copy = vgui.Create( "ContentHeader", self:GetParent() )
copy:SetText( self:GetText() )
copy:CopyBounds( self )
return copy
end
function PANEL:PaintOver( w, h )
self:DrawSelections()
end
function PANEL:OnLabelTextChanged( txt )
hook.Run( "SpawnlistContentChanged" )
return txt
end
function PANEL:IsEnabled()
-- This is a hack!
return !IsValid( self:GetParent() ) || !self:GetParent().GetReadOnly || !self:GetParent():GetReadOnly()
end
function PANEL:DoRightClick()
local pCanvas = self:GetSelectionCanvas()
if ( IsValid( pCanvas ) && pCanvas:NumSelectedChildren() > 0 && self:IsSelected() ) then
return hook.Run( "SpawnlistOpenGenericMenu", pCanvas )
end
self:OpenMenu()
end
function PANEL:UpdateColours( skin )
if ( self:GetHighlight() ) then return self:SetTextStyleColor( skin.Colours.Label.Highlight ) end
if ( self:GetBright() ) then return self:SetTextStyleColor( skin.Colours.Label.Bright ) end
if ( self:GetDark() ) then return self:SetTextStyleColor( skin.Colours.Label.Dark ) end
return self:SetTextStyleColor( skin.Colours.Label.Default )
end
function PANEL:OpenMenu()
-- Do not allow removal from read only panels
if ( IsValid( self:GetParent() ) && self:GetParent().GetReadOnly && self:GetParent():GetReadOnly() ) then return end
local menu = DermaMenu()
menu:AddOption( "#spawnmenu.menu.delete", function() self:Remove() hook.Run( "SpawnlistContentChanged" ) end ):SetIcon( "icon16/bin_closed.png" )
menu:Open()
end
vgui.Register( "ContentHeader", PANEL, "DLabelEditable" )
spawnmenu.AddContentType( "header", function( container, obj )
if ( !obj.text || !isstring( obj.text ) ) then return end
local label = vgui.Create( "ContentHeader", container )
label:SetText( obj.text )
container:Add( label )
return label
end )

View File

@@ -0,0 +1,449 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
AddCSLuaFile()
local PANEL = {}
local matOverlay_Normal = Material( "gui/ContentIcon-normal.png" )
local matOverlay_Hovered = Material( "gui/ContentIcon-hovered.png" )
local matOverlay_AdminOnly = Material( "icon16/shield.png" )
local matOverlay_NPCWeapon = Material( "icon16/monkey.png" )
local matOverlay_NPCWeaponSelected = Material( "icon16/monkey_tick.png" )
AccessorFunc( PANEL, "m_Color", "Color" )
AccessorFunc( PANEL, "m_Type", "ContentType" )
AccessorFunc( PANEL, "m_SpawnName", "SpawnName" )
AccessorFunc( PANEL, "m_NPCWeapon", "NPCWeapon" )
AccessorFunc( PANEL, "m_bAdminOnly", "AdminOnly" )
AccessorFunc( PANEL, "m_bIsNPCWeapon", "IsNPCWeapon" )
local function DoGenericSpawnmenuRightclickMenu( self )
local menu = DermaMenu()
menu:AddOption( "#spawnmenu.menu.copy", function() SetClipboardText( self:GetSpawnName() ) end ):SetIcon( "icon16/page_copy.png" )
if ( isfunction( self.OpenMenuExtra ) ) then
self:OpenMenuExtra( menu )
end
if ( !IsValid( self:GetParent() ) || !self:GetParent().GetReadOnly || !self:GetParent():GetReadOnly() ) then
menu:AddSpacer()
menu:AddOption( "#spawnmenu.menu.delete", function() self:Remove() hook.Run( "SpawnlistContentChanged" ) end ):SetIcon( "icon16/bin_closed.png" )
end
menu:Open()
end
function PANEL:Init()
self:SetPaintBackground( false )
self:SetSize( 128, 128 )
self:SetText( "" )
self:SetDoubleClickingEnabled( false )
self.Image = self:Add( "DImage" )
self.Image:SetPos( 3, 3 )
self.Image:SetSize( 128 - 6, 128 - 6 )
self.Image:SetVisible( false )
self.Label = self:Add( "DLabel" )
self.Label:Dock( BOTTOM )
self.Label:SetTall( 18 )
self.Label:SetContentAlignment( 5 )
self.Label:DockMargin( 4, 0, 4, 6 )
self.Label:SetTextColor( color_white )
self.Label:SetExpensiveShadow( 1, Color( 0, 0, 0, 200 ) )
self.Border = 0
end
function PANEL:SetName( name )
self:SetTooltip( name )
self.Label:SetText( name )
self.m_NiceName = name
end
function PANEL:SetMaterial( name )
self.m_MaterialName = name
local mat = Material( name )
-- Look for the old style material
if ( !mat || mat:IsError() ) then
name = name:Replace( "entities/", "VGUI/entities/" )
name = name:Replace( ".png", "" )
mat = Material( name )
end
-- Couldn't find any material.. just return
if ( !mat || mat:IsError() ) then
return
end
self.Image:SetMaterial( mat )
end
function PANEL:DoRightClick()
local pCanvas = self:GetSelectionCanvas()
if ( IsValid( pCanvas ) && pCanvas:NumSelectedChildren() > 0 && self:IsSelected() ) then
return hook.Run( "SpawnlistOpenGenericMenu", pCanvas )
end
self:OpenMenu()
end
function PANEL:DoClick()
end
function PANEL:OpenMenu()
end
function PANEL:OnDepressionChanged( b )
end
function PANEL:Paint( w, h )
if ( self.Depressed && !self.Dragging ) then
if ( self.Border != 8 ) then
self.Border = 8
self:OnDepressionChanged( true )
end
else
if ( self.Border != 0 ) then
self.Border = 0
self:OnDepressionChanged( false )
end
end
render.PushFilterMag( TEXFILTER.ANISOTROPIC )
render.PushFilterMin( TEXFILTER.ANISOTROPIC )
self.Image:PaintAt( 3 + self.Border, 3 + self.Border, 128 - 8 - self.Border * 2, 128 - 8 - self.Border * 2 )
render.PopFilterMin()
render.PopFilterMag()
surface.SetDrawColor( 255, 255, 255, 255 )
if ( !dragndrop.IsDragging() && ( self:IsHovered() || self.Depressed || self:IsChildHovered() ) ) then
surface.SetMaterial( matOverlay_Hovered )
self.Label:Hide()
else
surface.SetMaterial( matOverlay_Normal )
self.Label:Show()
end
surface.DrawTexturedRect( self.Border, self.Border, w-self.Border*2, h-self.Border*2 )
if ( self:GetAdminOnly() ) then
surface.SetMaterial( matOverlay_AdminOnly )
surface.DrawTexturedRect( self.Border + 8, self.Border + 8, 16, 16 )
end
-- This whole thing could be more dynamic
if ( self:GetIsNPCWeapon() ) then
surface.SetMaterial( matOverlay_NPCWeapon )
if ( self:GetSpawnName() == GetConVarString( "gmod_npcweapon" ) ) then
surface.SetMaterial( matOverlay_NPCWeaponSelected )
end
surface.DrawTexturedRect( w - self.Border - 24, self.Border + 8, 16, 16 )
end
self:ScanForNPCWeapons()
end
function PANEL:ScanForNPCWeapons()
if ( self.HasScanned ) then return end
self.HasScanned = true
for _, v in pairs( list.Get( "NPCUsableWeapons" ) ) do
if ( v.class == self:GetSpawnName() ) then
self:SetIsNPCWeapon( true )
break
end
end
end
function PANEL:PaintOver( w, h )
self:DrawSelections()
end
function PANEL:ToTable( bigtable )
local tab = {}
tab.type = self:GetContentType()
tab.nicename = self.m_NiceName
tab.material = self.m_MaterialName
tab.admin = self:GetAdminOnly()
tab.spawnname = self:GetSpawnName()
tab.weapon = self:GetNPCWeapon()
table.insert( bigtable, tab )
end
function PANEL:Copy()
local copy = vgui.Create( "ContentIcon", self:GetParent() )
copy:SetContentType( self:GetContentType() )
copy:SetSpawnName( self:GetSpawnName() )
copy:SetName( self.m_NiceName )
copy:SetMaterial( self.m_MaterialName )
copy:SetNPCWeapon( self:GetNPCWeapon() )
copy:SetAdminOnly( self:GetAdminOnly() )
copy:CopyBase( self )
copy.DoClick = self.DoClick
copy.OpenMenu = self.OpenMenu
copy.OpenMenuExtra = self.OpenMenuExtra
return copy
end
vgui.Register( "ContentIcon", PANEL, "DButton" )
spawnmenu.AddContentType( "entity", function( container, obj )
if ( !obj.material ) then return end
if ( !obj.nicename ) then return end
if ( !obj.spawnname ) then return end
local icon = vgui.Create( "ContentIcon", container )
icon:SetContentType( "entity" )
icon:SetSpawnName( obj.spawnname )
icon:SetName( obj.nicename )
icon:SetMaterial( obj.material )
icon:SetAdminOnly( obj.admin )
icon:SetColor( Color( 205, 92, 92, 255 ) )
-- Generate a nice tooltip with extra info.
local ENTinfo = scripted_ents.Get( obj.spawnname )
local toolTip = language.GetPhrase( obj.nicename )
if ( !ENTinfo ) then ENTinfo = list.Get( "SpawnableEntities" )[ obj.spawnname ] end
if ( ENTinfo ) then
local extraInfo = ""
if ( ENTinfo.Information and ENTinfo.Information != "" ) then extraInfo = extraInfo .. "\n" .. ENTinfo.Information end
if ( ENTinfo.Author and ENTinfo.Author != "" ) then extraInfo = extraInfo .. "\nAuthor: " .. ENTinfo.Author end
if ( #extraInfo > 0 ) then toolTip = toolTip .. "\n" .. extraInfo end
end
icon:SetTooltip( toolTip )
icon.DoClick = function()
RunConsoleCommand( "gm_spawnsent", obj.spawnname )
surface.PlaySound( "ui/buttonclickrelease.wav" )
end
icon.OpenMenuExtra = function( self, menu )
menu:AddOption( "#spawnmenu.menu.spawn_with_toolgun", function() RunConsoleCommand( "gmod_tool", "creator" ) RunConsoleCommand( "creator_type", "0" ) RunConsoleCommand( "creator_name", obj.spawnname ) end ):SetIcon( "icon16/brick_add.png" )
end
icon.OpenMenu = DoGenericSpawnmenuRightclickMenu
if ( IsValid( container ) ) then
container:Add( icon )
end
return icon
end )
spawnmenu.AddContentType( "vehicle", function( container, obj )
if ( !obj.material ) then return end
if ( !obj.nicename ) then return end
if ( !obj.spawnname ) then return end
local icon = vgui.Create( "ContentIcon", container )
icon:SetContentType( "vehicle" )
icon:SetSpawnName( obj.spawnname )
icon:SetName( obj.nicename )
icon:SetMaterial( obj.material )
icon:SetAdminOnly( obj.admin )
icon:SetColor( Color( 0, 0, 0, 255 ) )
icon.DoClick = function()
RunConsoleCommand( "gm_spawnvehicle", obj.spawnname )
surface.PlaySound( "ui/buttonclickrelease.wav" )
end
icon.OpenMenuExtra = function( self, menu )
menu:AddOption( "#spawnmenu.menu.spawn_with_toolgun", function() RunConsoleCommand( "gmod_tool", "creator" ) RunConsoleCommand( "creator_type", "1" ) RunConsoleCommand( "creator_name", obj.spawnname ) end ):SetIcon( "icon16/brick_add.png" )
end
icon.OpenMenu = DoGenericSpawnmenuRightclickMenu
if ( IsValid( container ) ) then
container:Add( icon )
end
return icon
end )
local gmod_npcweapon = CreateConVar( "gmod_npcweapon", "", { FCVAR_ARCHIVE }, "Overrides the weapon all spawnmenu NPCs will spawn with. Set to \"\" to not override." )
spawnmenu.AddContentType( "npc", function( container, obj )
if ( !obj.material ) then return end
if ( !obj.nicename ) then return end
if ( !obj.spawnname ) then return end
if ( !obj.weapon ) then obj.weapon = {} end
local icon = vgui.Create( "ContentIcon", container )
icon:SetContentType( "npc" )
icon:SetSpawnName( obj.spawnname )
icon:SetName( obj.nicename )
icon:SetMaterial( obj.material )
icon:SetAdminOnly( obj.admin )
icon:SetNPCWeapon( obj.weapon )
icon:SetColor( Color( 244, 164, 96, 255 ) )
icon.DoClick = function()
local weapon = table.Random( obj.weapon ) or ""
if ( gmod_npcweapon:GetString() != "" ) then weapon = gmod_npcweapon:GetString() end
RunConsoleCommand( "gmod_spawnnpc", obj.spawnname, weapon )
surface.PlaySound( "ui/buttonclickrelease.wav" )
end
icon.OpenMenuExtra = function( self, menu )
local weapon = table.Random( obj.weapon ) or ""
if ( gmod_npcweapon:GetString() != "" ) then weapon = gmod_npcweapon:GetString() end
menu:AddOption( "#spawnmenu.menu.spawn_with_toolgun", function()
RunConsoleCommand( "gmod_tool", "creator" ) RunConsoleCommand( "creator_type", "2" )
RunConsoleCommand( "creator_name", obj.spawnname ) RunConsoleCommand( "creator_arg", weapon )
end ):SetIcon( "icon16/brick_add.png" )
-- Quick access to spawning NPCs with a spcific weapon without the need to change gmod_npcweapon
if ( table.IsEmpty( obj.weapon ) ) then return end
local subMenu, swg = menu:AddSubMenu( "#spawnmenu.menu.spawn_with_weapon" )
swg:SetIcon( "icon16/gun.png" )
subMenu:AddOption( "#menubar.npcs.noweapon", function() RunConsoleCommand( "gmod_spawnnpc", obj.spawnname, "" ) end ):SetIcon( "icon16/cross.png" )
-- Kind of a hack!
local function addWeps( subm, weps )
if ( table.Count( weps ) < 1 ) then return end
subMenu:AddSpacer()
for title, class in SortedPairs( weps ) do
subMenu:AddOption( title, function() RunConsoleCommand( "gmod_spawnnpc", obj.spawnname, class ) end ):SetIcon( "icon16/gun.png" )
end
end
local weaps = {}
for _, class in pairs( obj.weapon ) do
if ( class == "" ) then continue end
weaps[ language.GetPhrase( class ) ] = class
end
addWeps( subMenu, weaps )
local weaps = {}
for _, t in pairs( list.Get( "NPCUsableWeapons" ) ) do
if ( table.HasValue( obj.weapon, t.class ) ) then continue end
weaps[ language.GetPhrase( t.title ) ] = t.class
end
addWeps( subMenu, weaps )
end
icon.OpenMenu = DoGenericSpawnmenuRightclickMenu
if ( IsValid( container ) ) then
container:Add( icon )
end
return icon
end )
spawnmenu.AddContentType( "weapon", function( container, obj )
if ( !obj.material ) then return end
if ( !obj.nicename ) then return end
if ( !obj.spawnname ) then return end
local icon = vgui.Create( "ContentIcon", container )
icon:SetContentType( "weapon" )
icon:SetSpawnName( obj.spawnname )
icon:SetName( obj.nicename )
icon:SetMaterial( obj.material )
icon:SetAdminOnly( obj.admin )
icon:SetColor( Color( 135, 206, 250, 255 ) )
-- Generate a nice tooltip with extra info.
local SWEPinfo = weapons.Get( obj.spawnname )
local toolTip = language.GetPhrase( obj.nicename )
if ( !SWEPinfo ) then SWEPinfo = list.Get( "Weapon" )[ obj.spawnname ] end
if ( SWEPinfo ) then
toolTip = toolTip .. "\n"
-- These 2 really should be one
if ( SWEPinfo.Purpose and SWEPinfo.Purpose != "" ) then toolTip = toolTip .. "\n" .. SWEPinfo.Purpose end
if ( SWEPinfo.Instructions and SWEPinfo.Instructions != "" ) then toolTip = toolTip .. "\n" .. SWEPinfo.Instructions end
if ( SWEPinfo.Author and SWEPinfo.Author != "" ) then toolTip = toolTip .. "\nAuthor: " .. SWEPinfo.Author end
end
toolTip = toolTip .. "\n\n" .. language.GetPhrase( "spawnmenu.mmb_weapons" )
icon:SetTooltip( toolTip )
icon.DoClick = function()
RunConsoleCommand( "gm_giveswep", obj.spawnname )
surface.PlaySound( "ui/buttonclickrelease.wav" )
end
icon.DoMiddleClick = function()
RunConsoleCommand( "gm_spawnswep", obj.spawnname )
surface.PlaySound( "ui/buttonclickrelease.wav" )
end
icon.OpenMenuExtra = function( self, menu )
menu:AddOption( "#spawnmenu.menu.spawn_with_toolgun", function() RunConsoleCommand( "gmod_tool", "creator" ) RunConsoleCommand( "creator_type", "3" ) RunConsoleCommand( "creator_name", obj.spawnname ) end ):SetIcon( "icon16/brick_add.png" )
if ( self:GetIsNPCWeapon() ) then
local opt = menu:AddOption( "#spawnmenu.menu.use_as_npc_gun", function() RunConsoleCommand( "gmod_npcweapon", self:GetSpawnName() ) end )
if ( self:GetSpawnName() == GetConVarString( "gmod_npcweapon" ) ) then
opt:SetIcon( "icon16/monkey_tick.png" )
else
opt:SetIcon( "icon16/monkey.png" )
end
end
end
icon.OpenMenu = DoGenericSpawnmenuRightclickMenu
if ( IsValid( container ) ) then
container:Add( icon )
end
return icon
end )

View File

@@ -0,0 +1,161 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
AddCSLuaFile()
PANEL.Base = "Panel"
function PANEL:Init()
self.CurrentSearch = ""
self.OldResults = -1
self.RebuildResults = false
self:Dock( TOP )
self:SetHeight( 20 )
self:DockMargin( 0, 0, 0, 3 )
self.Search = self:Add( "DTextEntry" )
self.Search:Dock( FILL )
self.Search:SetPlaceholderText( "#spawnmenu.search" )
self.Search.OnEnter = function() self:RefreshResults() end
self.Search.OnFocusChanged = function( _, b ) if ( b ) then self.ContentPanel:SwitchPanel( self.PropPanel ) end end
self.Search:SetTooltip( "#spawnmenu.enter_search" )
local btn = self.Search:Add( "DImageButton" )
btn:SetImage( "icon16/magnifier.png" )
btn:SetText( "" )
btn:Dock( RIGHT )
btn:DockMargin( 4, 2, 4, 2 )
btn:SetSize( 16, 16 )
btn:SetTooltip( "#spawnmenu.press_search" )
btn.DoClick = function()
self:RefreshResults()
end
self.Search.OnKeyCode = function( p, code )
if ( code == KEY_F1 ) then hook.Run( "OnSpawnMenuClose" ) end
if ( code == KEY_ESCAPE ) then hook.Run( "OnSpawnMenuClose" ) end
end
self.PropPanel = vgui.Create( "ContentContainer", self )
self.PropPanel:SetVisible( false )
self.PropPanel:SetTriggerSpawnlistChange( false )
-- Some sort of placeholder
local Header = self:Add( "ContentHeader" )
Header:SetText( "#spawnmenu.enter_search" )
self.PropPanel:Add( Header )
end
function PANEL:Paint()
-- This is a bit of a hack, if there was a request to rebuild the results from the search indexer
-- Do it when the player next sees the search panel, in case they got the spawnmenu closed
-- Think hook causes unexpected 1 frame duplication of all the elements
if ( self.RebuildResults ) then
self.RebuildResults = false
self:RefreshResults( self.CurrentSearch )
end
end
function PANEL:SetSearchType( stype, hookname )
self.m_strSearchType = stype
hook.Add( hookname, "AddSearchContent_" .. hookname, function( pnlContent, tree, node )
self.ContentPanel = pnlContent
end )
hook.Add( "SearchUpdate", "SearchUpdate_" .. hookname, function()
if ( !g_SpawnMenu:IsVisible() ) then self.RebuildResults = true return end
self:RefreshResults( self.CurrentSearch )
end )
-- This stuff is only for the primary search
if ( hookname != "PopulateContent" ) then return end
g_SpawnMenu.SearchPropPanel = self.PropPanel
hook.Add( "StartSearch", "StartSearch", function()
if ( g_SpawnMenu:IsVisible() ) then return hook.Run( "OnSpawnMenuClose" ) end
hook.Run( "OnSpawnMenuOpen" )
hook.Run( "OnTextEntryGetFocus", self.Search )
self.Search:RequestFocus()
self.Search:SetText( "" )
--
-- If we don't call this we'd have to press F1 twice to close it!
-- It's in a timer because of some good reason that!
--
timer.Simple( 0.1, function() g_SpawnMenu:HangOpen( false ) end )
self.ContentPanel:SwitchPanel( self.PropPanel )
end )
end
function PANEL:RefreshResults( str )
if ( !str ) then -- User tried to search for something
self.CurrentSearch = self.Search:GetText()
str = self.CurrentSearch
self.OldResults = -1
else
-- Don't force open the search when you click away from search while this function is called from cl_search_models.lua
if ( self.ContentPanel.SelectedPanel != self.PropPanel ) then
return
end
end
if ( !str or str == "" ) then return end
local results = search.GetResults( str, self.m_strSearchType, GetConVarNumber( "sbox_search_maxresults" ) )
for id, result in ipairs( results ) do
if ( !IsValid( result.icon ) ) then ErrorNoHalt( "Failed to create icon for " .. ( result.words && isstring( result.words[ 1 ] ) && result.words[ 1 ] || result.text ).. "\n" ) continue end
result.icon:SetParent( vgui.GetWorldPanel() ) -- Don't parent the icons to search panel prematurely
end
-- I know this is not perfect, but this is the best I am willing to do with how the search library was set up
if ( self.OldResults == #results ) then -- No updates, don't rebuild
for id, result in ipairs( results ) do
if ( IsValid( result.icon ) ) then result.icon:Remove() end -- Kill all icons
end
return
end
self.OldResults = #results
self.PropPanel:Clear()
local Header = self:Add( "ContentHeader" )
Header:SetText( #results .. " Results for \"" .. str .. "\"" )
self.PropPanel:Add( Header )
for k, v in ipairs( results ) do
self:AddSearchResult( v.text, v.func, v.icon )
end
self.PropPanel:SetParent( self.ContentPanel )
self.ContentPanel:SwitchPanel( self.PropPanel )
end
function PANEL:AddSearchResult( text, func, icon )
if ( !IsValid( icon ) ) then return end
icon:SetParent( self.PropPanel )
self.PropPanel:Add( icon )
end

View File

@@ -0,0 +1,96 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
include( "contentsidebartoolbox.lua" )
local pnlSearch = vgui.RegisterFile( "contentsearch.lua" )
local PANEL = {}
function PANEL:Init()
self.Tree = vgui.Create( "DTree", self )
self.Tree:SetClickOnDragHover( true )
self.Tree.OnNodeSelected = function( Tree, Node ) hook.Call( "ContentSidebarSelection", GAMEMODE, self:GetParent(), Node ) end
self.Tree:Dock( FILL )
self.Tree:SetBackgroundColor( Color( 240, 240, 240, 255 ) )
self:SetPaintBackground( false )
end
function PANEL:EnableSearch( stype, hookname )
self.Search = vgui.CreateFromTable( pnlSearch, self )
self.Search:SetSearchType( stype, hookname or "PopulateContent" )
end
function PANEL:EnableModify()
self:EnableSearch()
self:CreateSaveNotification()
self.Toolbox = vgui.Create( "ContentSidebarToolbox", self )
hook.Add( "OpenToolbox", "OpenToolbox", function()
if ( !IsValid( self.Toolbox ) ) then return end
self.Toolbox:Open()
end )
end
function PANEL:CreateSaveNotification()
local SavePanel = vgui.Create( "Panel", self )
SavePanel:Dock( TOP )
SavePanel:SetVisible( false )
SavePanel:DockMargin( 8, 1, 8, 4 )
local SaveButton = vgui.Create( "DButton", SavePanel )
SaveButton:Dock( FILL )
SaveButton:SetIcon( "icon16/disk.png" )
SaveButton:SetText( "#spawnmenu.savechanges" )
SaveButton.DoClick = function()
SavePanel:SlideUp( 0.2 )
hook.Run( "OnSaveSpawnlist" )
end
local RevertButton = vgui.Create( "DButton", SavePanel )
RevertButton:Dock( RIGHT )
RevertButton:SetIcon( "icon16/arrow_rotate_clockwise.png" )
RevertButton:SetText( "" )
RevertButton:SetTooltip( "#spawnmenu.revert_tooptip" )
RevertButton:SetWide( 26 )
RevertButton:DockMargin( 4, 0, 0, 0 )
RevertButton.DoClick = function()
SavePanel:SlideUp( 0.2 )
hook.Run( "OnRevertSpawnlist" )
end
hook.Add( "SpawnlistContentChanged", "ShowSaveButton", function()
if ( SavePanel:IsVisible() ) then return end
SavePanel:SlideDown( 0.2 )
GAMEMODE:AddHint( "EditingSpawnlistsSave", 5 )
end )
end
vgui.Register( "ContentSidebar", PANEL, "DPanel" )

View File

@@ -0,0 +1,115 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
include( "contentheader.lua" )
local PANEL = {}
Derma_Hook( PANEL, "Paint", "Paint", "Tree" )
PANEL.m_bBackground = true -- Hack for above
function PANEL:Init()
self:SetOpenSize( 200 )
self:DockPadding( 5, 5, 5, 5 )
local label = vgui.Create( "DTextEntry", self )
label:Dock( TOP )
label:SetZPos( 1 )
label:DockMargin( 0, 0, 0, 2 )
label:SetTooltip( "#spawnmenu.listname_tooltip" )
local panel = vgui.Create( "DPanel", self )
panel:Dock( TOP )
panel:SetZPos( 2 )
panel:SetSize( 24, 24 )
panel:DockPadding( 2, 2, 2, 2 )
local Button = vgui.Create( "DImageButton", panel )
Button:SetImage( "icon16/text_heading_1.png" )
Button:Dock( LEFT )
Button:SetStretchToFit( false )
Button:SetSize( 20, 20 )
Button:SetCursor( "sizeall" )
Button:SetTooltip( "#spawnmenu.header_tooltip" )
Button:Droppable( "SandboxContentPanel" )
Button.OnDrop = function( s, target )
local label = vgui.Create( "ContentHeader", target )
return label
end
local panel = vgui.Create( "Panel", self )
panel:Dock( FILL )
panel:SetZPos( 3 )
local icon_filter = vgui.Create( "DTextEntry", panel )
icon_filter:Dock( TOP )
icon_filter:SetUpdateOnType( true )
icon_filter:SetPlaceholderText( "#spawnmenu.quick_filter" )
icon_filter:DockMargin( 0, 2, 0, 1 )
local icons = vgui.Create( "DIconBrowser", panel )
icons:Dock( FILL )
icon_filter.OnValueChange = function( s, str )
icons:FilterByText( str )
end
local overlay = vgui.Create( "DPanel", self )
overlay:SetZPos( 9999 )
overlay.Paint = function( s, w, h )
surface.SetDrawColor( 0, 0, 0, 200 )
surface.DrawRect( 0, 0, w, h )
end
self.Overlay = overlay
--
-- If we select a node from the sidebar, update the text/icon/actions in the toolbox (at the bottom)
--
hook.Add( "ContentSidebarSelection", "SidebarToolboxSelection", function( pnlContent, node )
if ( !IsValid( node ) || !IsValid( label ) || !IsValid( icons ) ) then return end
if ( node.CustomSpawnlist ) then
label:SetText( node:GetText() )
icons:SelectIcon( node:GetIcon() )
icons:ScrollToSelected()
overlay:SetVisible( false )
else
label:SetText( "" )
overlay:SetVisible( true )
end
label.OnChange = function()
if ( !node.CustomSpawnlist ) then return end
node:SetText( label:GetText() )
hook.Run( "SpawnlistContentChanged" )
end
icons.OnChange = function()
if ( !node.CustomSpawnlist ) then return end
node:SetIcon( icons:GetSelectedIcon() )
hook.Run( "SpawnlistContentChanged" )
end
end )
end
function PANEL:PerformLayout()
-- Not using docking because it will mess up other elements using docking!
self.Overlay:SetSize( self:GetSize() )
end
vgui.Register( "ContentSidebarToolbox", PANEL, "DDrawer" )

View File

@@ -0,0 +1,172 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local function AddRecursive( pnl, folder, path, wildcard )
local files, folders = file.Find( folder .. "*", path )
if ( !files ) then MsgN( "Warning! Not opening '" .. folder .. "' because we cannot search in it!" ) return false end
local added = false
for k, v in ipairs( files ) do
if ( !string.EndsWith( v, ".mdl" ) ) then continue end
local cp = spawnmenu.GetContentType( "model" )
if ( cp ) then
cp( pnl, { model = folder .. v } )
added = true
end
end
for k, v in ipairs( folders ) do
local added_rec = AddRecursive( pnl, folder .. v .. "/", path, wildcard )
added = added || added_rec
end
return added
end
local function recurseAddFilesSpawnlist( folder, pathid, list )
local addedLabel = false
local files, folders = file.Find( folder .. "/*", pathid )
for id, file in pairs( files or {} ) do
if ( file:EndsWith( ".mdl" ) ) then
if ( !addedLabel ) then
table.insert( list, { type = "header", text = folder } )
addedLabel = true
end
table.insert( list, { type = "model", model = folder .. "/" .. file } )
end
end
for id, fold in pairs( folders or {} ) do
recurseAddFilesSpawnlist( folder .. "/" .. fold, pathid, list )
end
end
local function GenerateSpawnlistFromAddon( folder, path, name )
local contents = {}
recurseAddFilesSpawnlist( folder, path, contents )
AddPropsOfParent( g_SpawnMenu.CustomizableSpawnlistNode.SMContentPanel, g_SpawnMenu.CustomizableSpawnlistNode, 0, { [ folder ] = {
icon = "icon16/page.png",
id = math.random( 0, 999999 ), -- Eeehhhh
name = name or folder,
parentid = 0,
contents = contents
} } )
-- We added a new spawnlist, show the save changes button
hook.Run( "SpawnlistContentChanged" )
end
local function AddonsRightClick( self )
if ( !IsValid( self ) || !self.wsid || self.wsid == "0" ) then return end
local menu = DermaMenu()
menu:AddOption( "#spawnmenu.openaddononworkshop", function()
steamworks.ViewFile( self.wsid )
end ):SetIcon( "icon16/link_go.png" )
menu:AddOption( "#spawnmenu.createautospawnlist", function()
GenerateSpawnlistFromAddon( "models", self.searchPath, self.searchPath )
end ):SetIcon( "icon16/page_add.png" )
menu:Open()
end
local function RefreshAddons( MyNode )
local ViewPanel = MyNode.ViewPanel
for _, addon in SortedPairsByMemberValue( engine.GetAddons(), "title" ) do
if ( !addon.downloaded || !addon.mounted ) then continue end
if ( addon.models <= 0 ) then continue end
local models = MyNode:AddNode( addon.title .. " (" .. addon.models .. ")", "icon16/bricks.png" )
models.DoClick = function()
ViewPanel:Clear()
local anyAdded = AddRecursive( ViewPanel, "models/", addon.title, "*.mdl" )
if ( !anyAdded ) then
local text = "<font=ContentHeader>" .. language.GetPhrase( "spawnmenu.failedtofindmodels" ) .. "\n\n" .. tostring( addon.title ) .. " (ID: " .. tostring( addon.wsid ) .. ")" .. "</font>"
local msg = vgui.Create( "Panel", ViewPanel )
msg.Paint = function( s, w, h )
-- Shadow. Ew.
local parsedShadow = markup.Parse( "<colour=0,0,0,130>" .. text .. "</colour>", s:GetParent():GetWide() )
parsedShadow:Draw( 2, 2 )
-- The actual text
local parsed = markup.Parse( text, s:GetParent():GetWide() )
parsed:Draw( 0, 0 )
-- Size to contents. Ew.
s:SetSize( parsed:GetWidth(), parsed:GetHeight() )
end
ViewPanel:Add( msg )
end
MyNode.pnlContent:SwitchPanel( ViewPanel )
end
models.DoRightClick = AddonsRightClick
models.wsid = addon.wsid
models.searchPath = addon.title
end
end
local myAddonsNode
hook.Add( "PopulateContent", "AddonProps", function( pnlContent, tree, node )
local myViewPanel = vgui.Create( "ContentContainer", pnlContent )
myViewPanel:SetVisible( false )
myViewPanel.IconList:SetReadOnly( true )
myAddonsNode = node:AddNode( "#spawnmenu.category.addons", "icon16/folder_database.png" )
myAddonsNode.ViewPanel = myViewPanel
myAddonsNode.pnlContent = pnlContent
RefreshAddons( myAddonsNode )
end )
hook.Add( "GameContentChanged", "RefreshSpawnmenuAddons", function()
if ( !IsValid( myAddonsNode ) ) then return end
-- TODO: Maybe be more advaced and do not delete => recreate all the nodes, only delete nodes for addons that were removed, add only the new ones?
myAddonsNode:Clear()
myAddonsNode.ViewPanel:Clear()
RefreshAddons( myAddonsNode )
end )

View File

@@ -0,0 +1,296 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local AddCustomizableNode = nil
local function SetupCustomNode( node, pnlContent, needsapp )
node.CustomSpawnlist = !node.AddonSpawnlist -- Used to determine which nodes ContentSidebarToolBox can edit
-- This spawnlist needs a certain app mounted before it will show up.
if ( needsapp && needsapp != "" ) then
node:SetVisible( IsMounted( needsapp ) )
node.NeedsApp = needsapp
if ( !IsMounted( needsapp ) ) then
-- Make it look different
node:SetAlpha( 200 )
-- Give a detailed tooltip explaining why it looks different
local name = language.GetPhrase( "spawnmenu.mountablegame" )
for id, t in pairs( engine.GetGames() ) do
if ( needsapp == t.folder ) then name = t.title break end
end
node:SetTooltip( string.format( language.GetPhrase( "spawnmenu.spawnlistnocontent" ), name ) )
end
end
node.SetupCopy = function( self, copy )
SetupCustomNode( copy, pnlContent, needsapp )
self:DoPopulate()
copy.PropPanel = self.PropPanel:Copy()
copy.PropPanel:SetVisible( false )
copy.PropPanel:SetTriggerSpawnlistChange( true )
copy.DoPopulate = function() end
end
if ( !node.AddonSpawnlist ) then
node.OnModified = function()
hook.Run( "SpawnlistContentChanged" )
end
node.DoRightClick = function( self )
local menu = DermaMenu()
menu:AddOption( "#spawnmenu.menu.edit", function() self:InternalDoClick() hook.Run( "OpenToolbox" ) end ):SetIcon( "icon16/folder_edit.png" )
menu:AddOption( "#spawnmenu.menu.add_subcategory", function() AddCustomizableNode( pnlContent, "New Category", "", self ) self:SetExpanded( true ) hook.Run( "SpawnlistContentChanged" ) end ):SetIcon( "icon16/folder_add.png" )
menu:AddSpacer()
menu:AddOption( "#spawnmenu.menu.delete", function() node:Remove() hook.Run( "SpawnlistContentChanged" ) end ):SetIcon( "icon16/folder_delete.png" )
menu:Open()
end
end
node.DoPopulate = function( self )
if ( IsValid( self.PropPanel ) ) then return end
self.PropPanel = vgui.Create( "ContentContainer", pnlContent )
self.PropPanel:SetVisible( false )
self.PropPanel:SetTriggerSpawnlistChange( true )
end
node.DoClick = function( self )
self:DoPopulate()
pnlContent:SwitchPanel( self.PropPanel )
end
end
AddCustomizableNode = function( pnlContent, name, icon, parent, needsapp )
local node = parent:AddNode( name, icon )
node.AddonSpawnlist = parent.AddonSpawnlist
SetupCustomNode( node, pnlContent, needsapp )
return node
end
local function ReadSpawnlists( node, parentid )
local tab = {}
tab.name = node:GetText()
tab.icon = node:GetIcon()
tab.parentid = parentid
tab.id = SPAWNLIST_ID
tab.version = 3
tab.needsapp = node.NeedsApp
node:DoPopulate()
if ( IsValid( node.PropPanel ) ) then
tab.contents = node.PropPanel:ContentsToTable()
end
if ( SPAWNLIST_ID > 0 ) then
SPAWNLISTS[ string.format( "%03d", tab.id ) .. "-" .. tab.name ] = util.TableToKeyValues( tab )
end
SPAWNLIST_ID = SPAWNLIST_ID + 1
if ( node.ChildNodes ) then
for k, v in ipairs( node.ChildNodes:GetChildren() ) do
ReadSpawnlists( v, tab.id )
end
end
end
local function ConstructSpawnlist( node )
SPAWNLIST_ID = 0
SPAWNLISTS = {}
ReadSpawnlists( node, 0 )
local tab = SPAWNLISTS
SPAWNLISTS = nil
SPAWNLIST_ID = nil
return tab
end
function AddPropsOfParent( pnlContent, node, parentid, customProps )
local Props = customProps or spawnmenu.GetPropTable()
for FileName, Info in SortedPairs( Props ) do
if ( parentid != Info.parentid ) then continue end
local pnlnode = AddCustomizableNode( pnlContent, Info.name, Info.icon, node, Info.needsapp )
pnlnode:SetExpanded( true )
pnlnode.OnRemove = function( self ) if ( IsValid( self.PropPanel ) ) then self.PropPanel:Remove() end end
pnlnode.DoPopulate = function( self )
if ( IsValid( self.PropPanel ) ) then return end
self.PropPanel = vgui.Create( "ContentContainer", pnlContent )
self.PropPanel:SetVisible( false )
self.PropPanel:SetTriggerSpawnlistChange( true )
if ( node.AddonSpawnlist ) then self.PropPanel.IconList:SetReadOnly( true ) end
for i, object in SortedPairs( Info.contents ) do
local cp = spawnmenu.GetContentType( object.type )
if ( cp ) then cp( self.PropPanel, object ) end
end
end
AddPropsOfParent( pnlContent, pnlnode, Info.id, customProps )
end
end
-- This helps avoid empty spawnlist list when you delete some but the hidden ones remain so the default spawnlists never regenerate
-- TODO: Maybe show spawnlists that need games when any spawnlist was changed? Allow to set needed game from in-game?
local function CheckIfAnyVisible( node )
local pnlContent = node.SMContentPanel
if ( node:GetChildNodeCount() < 1 ) then
spawnmenu.PopulateFromEngineTextFiles()
AddPropsOfParent( pnlContent, node, 0 )
node:SetExpanded( true )
return
end
local visible = 0
for id, pnl in pairs( node:GetChildNodes() ) do
if ( pnl:IsVisible() ) then visible = visible + 1 end
end
if ( visible < 1 ) then
for id, pnl in pairs( node:GetChildNodes() ) do
pnl:SetVisible( true )
end
end
end
hook.Add( "PopulateContent", "AddCustomContent", function( pnlContent, tree, node )
local node = AddCustomizableNode( pnlContent, "#spawnmenu.category.your_spawnlists", "", tree )
node:SetDraggableName( "CustomContent" )
node:SetExpanded( true )
node.CustomSpawnlist = nil
node.SMContentPanel = pnlContent
node.DoRightClick = function( self )
local menu = DermaMenu()
menu:AddOption( "New Category", function() AddCustomizableNode( pnlContent, "New Category", "", node ) node:SetExpanded( true ) hook.Run( "SpawnlistContentChanged" ) end ):SetIcon( "icon16/folder_add.png" )
menu:Open()
end
-- Save the spawnlist when children drag and dropped
node.OnModified = function()
hook.Run( "SpawnlistContentChanged" )
end
AddPropsOfParent( pnlContent, node, 0 )
CheckIfAnyVisible( node )
node:MoveToBack()
g_SpawnMenu.CustomizableSpawnlistNode = node
-- Select the first visible panel
for id, pnl in pairs( node:GetChildNodes() ) do
if ( pnl:IsVisible() ) then
pnl:InternalDoClick()
pnl:SetExpanded( true )
break
end
end
-- Custom stuff from addons
local CustomProps = spawnmenu.GetCustomPropTable()
if ( !table.IsEmpty( CustomProps ) ) then
local node = AddCustomizableNode( pnlContent, "#spawnmenu.category.addon_spawnlists", "", tree )
node:SetExpanded( true )
--node:SetDraggableName( "CustomContent" )
node.DoRightClick = function() end
node.OnModified = function() end
node.AddonSpawnlist = true
node.CustomSpawnlist = nil
AddPropsOfParent( pnlContent, node, 0, CustomProps )
end
end )
hook.Add( "OnSaveSpawnlist", "DoSaveSpawnlist", function()
local Spawnlist = ConstructSpawnlist( g_SpawnMenu.CustomizableSpawnlistNode )
spawnmenu.DoSaveToTextFiles( Spawnlist )
CheckIfAnyVisible( g_SpawnMenu.CustomizableSpawnlistNode )
end )
hook.Add( "OnRevertSpawnlist", "DoRevertSpawnlists", function()
-- First delete all of the existing spawnlists
g_SpawnMenu.CustomizableSpawnlistNode:Clear()
-- Next load all the custom spawnlists again
spawnmenu.PopulateFromEngineTextFiles()
AddPropsOfParent( g_SpawnMenu.CustomizableSpawnlistNode.SMContentPanel, g_SpawnMenu.CustomizableSpawnlistNode, 0 )
-- Select the first visible panel. TODO: why this requires a timer?
timer.Simple( 0, function()
CheckIfAnyVisible( g_SpawnMenu.CustomizableSpawnlistNode )
for id, pnl in pairs( g_SpawnMenu.CustomizableSpawnlistNode:GetChildNodes() ) do
if ( pnl:IsVisible() ) then
pnl:InternalDoClick()
pnl:SetExpanded( true )
break
end
end
g_SpawnMenu.CustomizableSpawnlistNode:SetExpanded( true )
end )
end )

View File

@@ -0,0 +1,125 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local HTML = nil
local DupeInClipboard = false
spawnmenu.AddCreationTab( "#spawnmenu.category.dupes", function()
HTML = vgui.Create( "DHTML" )
JS_Language( HTML )
JS_Workshop( HTML )
ws_dupe = WorkshopFileBase( "dupe", { "dupe" } )
ws_dupe.HTML = HTML
function ws_dupe:FetchLocal( offset, perpage )
local f = file.Find( "dupes/*.dupe", "MOD", "datedesc" )
local saves = {}
for k, v in ipairs( f ) do
if ( k <= offset ) then continue end
if ( k > offset + perpage ) then break end
local entry = {
file = "dupes/" .. v,
name = v:StripExtension(),
preview = "dupes/" .. v:StripExtension() .. ".jpg",
description = "Local duplication stored on your computer. Local content can be deleted in the main menu."
}
table.insert( saves, entry )
end
local results = {
totalresults = #f,
results = saves
}
local json = util.TableToJSON( results, false )
HTML:Call( "dupe.ReceiveLocal( " .. json .. " )" )
end
function ws_dupe:Arm( filename )
RunConsoleCommand( "dupe_arm", filename )
end
function ws_dupe:DownloadAndArm( id )
-- Server doesn't allow us to arm dupes, don't even try to download anything
local res, msg = hook.Run( "CanArmDupe", LocalPlayer() )
if ( res == false ) then LocalPlayer():ChatPrint( msg or "Refusing to download Workshop dupe, server has blocked usage of the Duplicator tool!" ) return end
MsgN( "Downloading Dupe..." )
steamworks.DownloadUGC( id, function( name )
MsgN( "Finished - arming!" )
ws_dupe:Arm( name )
end )
end
function ws_dupe:Publish( filename, imagename )
RunConsoleCommand( "dupe_publish", filename, imagename )
end
HTML:OpenURL( "asset://garrysmod/html/dupes.html" )
HTML:Call( "SetDupeSaveState( " .. tostring( DupeInClipboard ) .. " );" )
return HTML
end, "icon16/control_repeat_blue.png", 200 )
hook.Add( "DupeSaveAvailable", "UpdateDupeSpawnmenuAvailable", function()
DupeInClipboard = true
if ( !IsValid( HTML ) ) then return end
HTML:Call( "SetDupeSaveState( true );" )
end )
hook.Add( "DupeSaveUnavailable", "UpdateDupeSpawnmenuUnavailable", function()
DupeInClipboard = false
if ( !IsValid( HTML ) ) then return end
HTML:Call( "SetDupeSaveState( false );" )
end )
hook.Add( "DupeSaved", "DuplicationSavedSpawnMenu", function()
if ( !IsValid( HTML ) ) then return end
HTML:Call( "ShowLocalDupes();" )
end )
concommand.Add( "dupe_show", function()
g_SpawnMenu:OpenCreationMenuTab( "#spawnmenu.category.dupes" )
timer.Simple( 1.0, function() if ( !IsValid( HTML ) ) then return end HTML:Call( "ShowLocalDupes();" ) end )
end, nil, "", { FCVAR_DONTRECORD } )

View File

@@ -0,0 +1,94 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
list.Set( "ContentCategoryIcons", "Half-Life: Source", "games/16/hl1.png" )
list.Set( "ContentCategoryIcons", "Half-Life 2", "games/16/hl2.png" )
list.Set( "ContentCategoryIcons", "Portal", "games/16/portal.png" )
hook.Add( "PopulateEntities", "AddEntityContent", function( pnlContent, tree, browseNode )
local Categorised = {}
-- Add this list into the tormoil
local SpawnableEntities = list.Get( "SpawnableEntities" )
if ( SpawnableEntities ) then
for k, v in pairs( SpawnableEntities ) do
local Category = v.Category or "Other"
if ( !isstring( Category ) ) then Category = tostring( Category ) end
Categorised[ Category ] = Categorised[ Category ] or {}
v.SpawnName = k
table.insert( Categorised[ Category ], v )
end
end
--
-- Add a tree node for each category
--
local CustomIcons = list.Get( "ContentCategoryIcons" )
for CategoryName, v in SortedPairs( Categorised ) do
-- Add a node to the tree
local node = tree:AddNode( CategoryName, CustomIcons[ CategoryName ] or "icon16/bricks.png" )
-- When we click on the node - populate it using this function
node.DoPopulate = function( self )
-- If we've already populated it - forget it.
if ( self.PropPanel ) then return end
-- Create the container panel
self.PropPanel = vgui.Create( "ContentContainer", pnlContent )
self.PropPanel:SetVisible( false )
self.PropPanel:SetTriggerSpawnlistChange( false )
for k, ent in SortedPairsByMemberValue( v, "PrintName" ) do
spawnmenu.CreateContentIcon( ent.ScriptedEntityType or "entity", self.PropPanel, {
nicename = ent.PrintName or ent.ClassName,
spawnname = ent.SpawnName,
material = ent.IconOverride or "entities/" .. ent.SpawnName .. ".png",
admin = ent.AdminOnly
} )
end
end
-- If we click on the node populate it and switch to it.
node.DoClick = function( self )
self:DoPopulate()
pnlContent:SwitchPanel( self.PropPanel )
end
end
-- Select the first node
local FirstNode = tree:Root():GetChildNode( 0 )
if ( IsValid( FirstNode ) ) then
FirstNode:InternalDoClick()
end
end )
spawnmenu.AddCreationTab( "#spawnmenu.category.entities", function()
local ctrl = vgui.Create( "SpawnmenuContentPanel" )
ctrl:EnableSearch( "entities", "PopulateEntities" )
ctrl:CallPopulateHook( "PopulateEntities" )
return ctrl
end, "icon16/bricks.png", 20 )

View File

@@ -0,0 +1,188 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local function recurseAddFiles( folder, pathid, list )
local addedLabel = false
local files, folders = file.Find( folder .. "/*", pathid )
for id, file in pairs( files or {} ) do
if ( file:EndsWith( ".mdl" ) ) then
if ( !addedLabel ) then
table.insert( list, { type = "header", text = folder } )
addedLabel = true
end
table.insert( list, { type = "model", model = folder .. "/" .. file } )
end
end
for id, fold in pairs( folders or {} ) do
recurseAddFiles( folder .. "/" .. fold, pathid, list )
end
end
local function GenerateSpawnlistFromPath( folder, path, name, icon, appid )
local contents = {}
recurseAddFiles( folder, path, contents )
AddPropsOfParent( g_SpawnMenu.CustomizableSpawnlistNode.SMContentPanel, g_SpawnMenu.CustomizableSpawnlistNode, 0, { [ folder ] = {
icon = icon or "icon16/page.png",
id = math.random( 0, 999999 ), -- Eeehhhh
name = name or folder,
parentid = 0,
needsapp = appid,
contents = contents
} } )
-- We added a new spawnlist, show the save changes button
hook.Run( "SpawnlistContentChanged" )
end
local function GamePropsRightClick( self )
local menu = DermaMenu()
menu:AddOption( "#spawnmenu.createautospawnlist", function()
-- Find the "root" node for this game
local parent = self
local icon = parent:GetIcon()
while ( !icon:StartsWith( "games" ) ) do
parent = parent:GetParentNode()
if ( !IsValid( parent ) ) then break end
icon = parent:GetIcon()
end
local name = parent:GetText()
if ( self:GetFolder() != "models" ) then
name = name .. " - " .. self:GetFolder():sub( 8 )
end
GenerateSpawnlistFromPath( self:GetFolder(), self:GetPathID(), name, icon, parent.GamePathID )
end ):SetIcon( "icon16/page_add.png" )
menu:Open()
end
local function InstallNodeRightclick( self, newNode )
newNode.DoRightClick = GamePropsRightClick
newNode.OnNodeAdded = InstallNodeRightclick
end
local function AddBrowseContent( ViewPanel, node, name, icon, path, pathid, pnlContent )
local models = node:AddFolder( name, path .. "models", pathid, false )
models:SetIcon( icon )
models.BrowseContentType = "models"
models.BrowseExtension = "*.mdl"
models.ContentType = "model"
models.ViewPanel = ViewPanel
models.GamePathID = pathid
-- If we click on a subnode of this tree, it gets reported upwards (to us)
models.OnNodeSelected = function( slf, node )
-- Already viewing this panel
if ( ViewPanel && ViewPanel.CurrentNode && ViewPanel.CurrentNode == node ) then
if ( pnlContent.SelectedPanel != ViewPanel ) then pnlContent:SwitchPanel( ViewPanel ) end
return
end
-- Clear the viewpanel in preperation for displaying it
ViewPanel:Clear()
ViewPanel.CurrentNode = node
-- Fill the viewpanel with models that are in this node's folder
local node_path = node:GetFolder()
local SearchString = node_path .. "/*.mdl"
local mdls = file.Find( SearchString, node:GetPathID() )
if ( mdls ) then
for k, v in ipairs( mdls ) do
local cp = spawnmenu.GetContentType( "model" )
if ( cp ) then
cp( ViewPanel, { model = node_path .. "/" .. v } )
end
end
else
MsgN( "Warning! Not opening '" .. node_path .. "' because we cannot search in it!" )
end
-- Switch to it
pnlContent:SwitchPanel( ViewPanel )
ViewPanel.CurrentNode = node
end
InstallNodeRightclick( node, models )
end
local function RefreshGames( MyNode )
local games = engine.GetGames()
table.insert( games, {
title = "All",
folder = "GAME",
icon = "all",
mounted = true
} )
table.insert( games, {
title = "Garry's Mod",
folder = "garrysmod",
mounted = true
} )
-- Create a list of mounted games, allowing us to browse them
for _, game in SortedPairsByMemberValue( games, "title" ) do
if ( !game.mounted ) then continue end
AddBrowseContent( MyNode.ViewPanel, MyNode, game.title, "games/16/" .. ( game.icon or game.folder ) .. ".png", "", game.folder, MyNode.pnlContent )
end
end
-- Called when setting up the sidebar on the spawnmenu - to populate the tree
local myGamesNode
hook.Add( "PopulateContent", "GameProps", function( pnlContent, tree, node )
-- Create a node in the `other` category on the tree
myGamesNode = node:AddNode( "#spawnmenu.category.games", "icon16/folder_database.png" )
myGamesNode.pnlContent = pnlContent
local ViewPanel = vgui.Create( "ContentContainer", pnlContent )
ViewPanel:SetVisible( false )
ViewPanel.IconList:SetReadOnly( true )
myGamesNode.ViewPanel = ViewPanel
RefreshGames( myGamesNode )
end )
hook.Add( "GameContentChanged", "RefreshSpawnmenuGames", function()
if ( !IsValid( myGamesNode ) ) then return end
-- TODO: Maybe be more advaced and do not delete => recreate all the nodes, only delete nodes for addons that were removed, add only the new ones?
myGamesNode:Clear()
myGamesNode.ViewPanel:Clear()
RefreshGames( myGamesNode )
end )

View File

@@ -0,0 +1,159 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
hook.Add( "PopulateNPCs", "AddNPCContent", function( pnlContent, tree, browseNode )
-- Get a list of available NPCs
local NPCList = list.Get( "NPC" )
-- Categorize them
local Categories = {}
for k, v in pairs( NPCList ) do
local Category = v.Category or "Other"
if ( !isstring( Category ) ) then Category = tostring( Category ) end
local Tab = Categories[ Category ] or {}
Tab[ k ] = v
Categories[ Category ] = Tab
end
-- Create an icon for each one and put them on the panel
local CustomIcons = list.Get( "ContentCategoryIcons" )
for CategoryName, v in SortedPairs( Categories ) do
-- Add a node to the tree
local node = tree:AddNode( CategoryName, CustomIcons[ CategoryName ] or "icon16/monkey.png" )
-- When we click on the node - populate it using this function
node.DoPopulate = function( self )
-- If we've already populated it - forget it.
if ( self.PropPanel ) then return end
-- Create the container panel
self.PropPanel = vgui.Create( "ContentContainer", pnlContent )
self.PropPanel:SetVisible( false )
self.PropPanel:SetTriggerSpawnlistChange( false )
for name, ent in SortedPairsByMemberValue( v, "Name" ) do
spawnmenu.CreateContentIcon( ent.ScriptedEntityType or "npc", self.PropPanel, {
nicename = ent.Name or name,
spawnname = name,
material = ent.IconOverride or "entities/" .. name .. ".png",
weapon = ent.Weapons,
admin = ent.AdminOnly
} )
end
end
-- If we click on the node populate it and switch to it.
node.DoClick = function( self )
self:DoPopulate()
pnlContent:SwitchPanel( self.PropPanel )
end
end
-- Select the first node
local FirstNode = tree:Root():GetChildNode( 0 )
if ( IsValid( FirstNode ) ) then
FirstNode:InternalDoClick()
end
end )
local PANEL = {}
Derma_Hook( PANEL, "Paint", "Paint", "Tree" )
PANEL.m_bBackground = true -- Hack for above
function PANEL:AddCheckbox( text, cvar )
local DermaCheckbox = self:Add( "DCheckBoxLabel", self )
DermaCheckbox:Dock( TOP )
DermaCheckbox:SetText( text )
DermaCheckbox:SetDark( true )
DermaCheckbox:SetConVar( cvar)
DermaCheckbox:SizeToContents()
DermaCheckbox:DockMargin( 0, 5, 0, 0 )
end
function PANEL:Init()
self:SetOpenSize( 150 )
self:DockPadding( 15, 10, 15, 10 )
self:AddCheckbox( "#menubar.npcs.disableai", "ai_disabled" )
self:AddCheckbox( "#menubar.npcs.ignoreplayers", "ai_ignoreplayers" )
self:AddCheckbox( "#menubar.npcs.keepcorpses", "ai_serverragdolls" )
self:AddCheckbox( "#menubar.npcs.autoplayersquad", "npc_citizen_auto_player_squad" )
local label = vgui.Create( "DLabel", self )
label:Dock( TOP )
label:DockMargin( 0, 5, 0, 0 )
label:SetDark( true )
label:SetText( "#menubar.npcs.weapon" )
local DComboBox = vgui.Create( "DComboBox", self )
DComboBox:Dock( TOP )
DComboBox:DockMargin( 0, 0, 0, 0 )
DComboBox:SetConVar( "gmod_npcweapon" )
DComboBox:SetSortItems( false )
DComboBox:AddChoice( "#menubar.npcs.defaultweapon", "" )
DComboBox:AddChoice( "#menubar.npcs.noweapon", "none" )
-- Sort the items by name, and group by category
local groupedWeps = {}
for _, v in pairs( list.Get( "NPCUsableWeapons" ) ) do
local cat = (v.category or ""):lower()
groupedWeps[ cat ] = groupedWeps[ cat ] or {}
groupedWeps[ cat ][ v.class ] = language.GetPhrase( v.title )
end
for group, items in SortedPairs( groupedWeps ) do
DComboBox:AddSpacer()
for class, title in SortedPairsByValue( items ) do
DComboBox:AddChoice( title, class )
end
end
function DComboBox:OnSelect( index, value )
self:ConVarChanged( self.Data[ index ] )
end
self:Open()
end
function PANEL:PerformLayout()
end
vgui.Register( "SpawnmenuNPCSidebarToolbox", PANEL, "DDrawer" )
spawnmenu.AddCreationTab( "#spawnmenu.category.npcs", function()
local ctrl = vgui.Create( "SpawnmenuContentPanel" )
ctrl:EnableSearch( "npcs", "PopulateNPCs" )
ctrl:CallPopulateHook( "PopulateNPCs" )
local sidebar = ctrl.ContentNavBar
sidebar.Options = vgui.Create( "SpawnmenuNPCSidebarToolbox", sidebar )
return ctrl
end, "icon16/monkey.png", 20 )

View File

@@ -0,0 +1,93 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
hook.Add( "PopulatePostProcess", "AddPostProcess", function( pnlContent, tree, node )
-- Get a list of postproceess effects
-- and organise them into categories
local Categorised = {}
local PostProcess = list.Get( "PostProcess" )
if ( PostProcess ) then
for k, v in pairs( PostProcess ) do
local Category = v.category or "Other"
if ( !isstring( Category ) ) then Category = tostring( Category ) end
Categorised[ Category ] = Categorised[ Category ] or {}
v.name = k
table.insert( Categorised[ Category ], v )
end
end
--
-- Create an entry for each category
--
for CategoryName, v in SortedPairs( Categorised ) do
-- Add a node to the tree
local node = tree:AddNode( CategoryName, "icon16/picture.png" )
-- When we click on the node - populate it using this function
node.DoPopulate = function( self )
-- If we've already populated it - forget it.
if ( self.PropPanel ) then return end
-- Create the container panel
self.PropPanel = vgui.Create( "ContentContainer", pnlContent )
self.PropPanel:SetVisible( false )
self.PropPanel:SetTriggerSpawnlistChange( false )
for k, pp in SortedPairsByMemberValue( v, "PrintName" ) do
if ( pp.func ) then
pp.func( self.PropPanel )
continue
end
spawnmenu.CreateContentIcon( "postprocess", self.PropPanel, {
name = pp.name,
icon = pp.icon
} )
end
end
-- If we click on the node populate it and switch to it.
node.DoClick = function( self )
self:DoPopulate()
pnlContent:SwitchPanel( self.PropPanel )
end
end
-- Select the first node
local FirstNode = tree:Root():GetChildNode( 0 )
if ( IsValid( FirstNode ) ) then
FirstNode:InternalDoClick()
end
end )
spawnmenu.AddCreationTab( "#spawnmenu.category.postprocess", function()
local ctrl = vgui.Create( "SpawnmenuContentPanel" )
ctrl:CallPopulateHook( "PopulatePostProcess" )
return ctrl
end, "icon16/picture.png", 100 )

View File

@@ -0,0 +1,81 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local HTML = nil
spawnmenu.AddCreationTab( "#spawnmenu.category.saves", function()
HTML = vgui.Create( "DHTML" )
JS_Language( HTML )
JS_Workshop( HTML )
ws_save = WorkshopFileBase( "save", { "save" } )
ws_save.HTML = HTML
function ws_save:FetchLocal( offset, perpage )
local f = file.Find( "saves/*.gms", "MOD", "datedesc" )
local saves = {}
for k, v in ipairs( f ) do
if ( k <= offset ) then continue end
if ( k > offset + perpage ) then break end
local entry = {
file = "saves/" .. v,
name = v:StripExtension(),
preview = "saves/" .. v:StripExtension() .. ".jpg",
description = "Local map saves stored on your computer. Local content can be deleted in the main menu."
}
table.insert( saves, entry )
end
local results = {
totalresults = #f,
results = saves
}
local json = util.TableToJSON( results, false )
HTML:Call( "save.ReceiveLocal( " .. json .. " )" )
end
function ws_save:DownloadAndLoad( id )
steamworks.DownloadUGC( id, function( name )
ws_save:Load( name )
end )
end
function ws_save:Load( filename ) RunConsoleCommand( "gm_load", filename ) end
function ws_save:Publish( filename, imagename ) RunConsoleCommand( "save_publish", filename, imagename ) end
HTML:OpenURL( "asset://garrysmod/html/saves.html" )
HTML:Call( "SetMap( '" .. game.GetMap() .. "' );" )
return HTML
end, "icon16/disk_multiple.png", 200 )
hook.Add( "PostGameSaved", "OnCreationsSaved", function()
if ( !HTML ) then return end
HTML:Call( "OnGameSaved()" )
end )

View File

@@ -0,0 +1,91 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
hook.Add( "PopulateVehicles", "AddEntityContent", function( pnlContent, tree, browseNode )
local Categorised = {}
-- Add this list into the tormoil
local Vehicles = list.Get( "Vehicles" )
if ( Vehicles ) then
for k, v in pairs( Vehicles ) do
local Category = v.Category or "Other"
if ( !isstring( Category ) ) then Category = tostring( Category ) end
Categorised[ Category ] = Categorised[ Category ] or {}
v.ClassName = k
v.PrintName = v.Name
v.ScriptedEntityType = "vehicle"
table.insert( Categorised[ Category ], v )
end
end
--
-- Add a tree node for each category
--
local CustomIcons = list.Get( "ContentCategoryIcons" )
for CategoryName, v in SortedPairs( Categorised ) do
-- Add a node to the tree
local node = tree:AddNode( CategoryName, CustomIcons[ CategoryName ] or "icon16/bricks.png" )
-- When we click on the node - populate it using this function
node.DoPopulate = function( self )
-- If we've already populated it - forget it.
if ( self.PropPanel ) then return end
-- Create the container panel
self.PropPanel = vgui.Create( "ContentContainer", pnlContent )
self.PropPanel:SetVisible( false )
self.PropPanel:SetTriggerSpawnlistChange( false )
for k, ent in SortedPairsByMemberValue( v, "PrintName" ) do
spawnmenu.CreateContentIcon( ent.ScriptedEntityType or "entity", self.PropPanel, {
nicename = ent.PrintName or ent.ClassName,
spawnname = ent.ClassName,
material = ent.IconOverride or "entities/" .. ent.ClassName .. ".png",
admin = ent.AdminOnly
} )
end
end
-- If we click on the node populate it and switch to it.
node.DoClick = function( self )
self:DoPopulate()
pnlContent:SwitchPanel( self.PropPanel )
end
end
-- Select the first node
local FirstNode = tree:Root():GetChildNode( 0 )
if ( IsValid( FirstNode ) ) then
FirstNode:InternalDoClick()
end
end )
spawnmenu.AddCreationTab( "#spawnmenu.category.vehicles", function()
local ctrl = vgui.Create( "SpawnmenuContentPanel" )
ctrl:EnableSearch( "vehicles", "PopulateVehicles" )
ctrl:CallPopulateHook( "PopulateVehicles" )
return ctrl
end, "icon16/car.png", 50 )

View File

@@ -0,0 +1,87 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
hook.Add( "PopulateWeapons", "AddWeaponContent", function( pnlContent, tree, browseNode )
-- Loop through the weapons and add them to the menu
local Weapons = list.Get( "Weapon" )
local Categorised = {}
-- Build into categories
for k, weapon in pairs( Weapons ) do
if ( !weapon.Spawnable ) then continue end
local Category = weapon.Category or "Other"
if ( !isstring( Category ) ) then Category = tostring( Category ) end
Categorised[ Category ] = Categorised[ Category ] or {}
table.insert( Categorised[ Category ], weapon )
end
-- Loop through each category
local CustomIcons = list.Get( "ContentCategoryIcons" )
for CategoryName, v in SortedPairs( Categorised ) do
-- Add a node to the tree
local node = tree:AddNode( CategoryName, CustomIcons[ CategoryName ] or "icon16/gun.png" )
-- When we click on the node - populate it using this function
node.DoPopulate = function( self )
-- If we've already populated it - forget it.
if ( self.PropPanel ) then return end
-- Create the container panel
self.PropPanel = vgui.Create( "ContentContainer", pnlContent )
self.PropPanel:SetVisible( false )
self.PropPanel:SetTriggerSpawnlistChange( false )
for k, ent in SortedPairsByMemberValue( v, "PrintName" ) do
spawnmenu.CreateContentIcon( ent.ScriptedEntityType or "weapon", self.PropPanel, {
nicename = ent.PrintName or ent.ClassName,
spawnname = ent.ClassName,
material = ent.IconOverride or "entities/" .. ent.ClassName .. ".png",
admin = ent.AdminOnly
} )
end
end
-- If we click on the node populate it and switch to it.
node.DoClick = function( self )
self:DoPopulate()
pnlContent:SwitchPanel( self.PropPanel )
end
end
-- Select the first node
local FirstNode = tree:Root():GetChildNode( 0 )
if ( IsValid( FirstNode ) ) then
FirstNode:InternalDoClick()
end
end )
spawnmenu.AddCreationTab( "#spawnmenu.category.weapons", function()
local ctrl = vgui.Create( "SpawnmenuContentPanel" )
ctrl:EnableSearch( "weapons", "PopulateWeapons" )
ctrl:CallPopulateHook( "PopulateWeapons" )
return ctrl
end, "icon16/gun.png", 10 )

View File

@@ -0,0 +1,192 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local PANEL = {}
--[[---------------------------------------------------------
Name: Paint
-----------------------------------------------------------]]
function PANEL:Init()
self:SetPaintBackground( false )
self:SetSize( 128, 128 )
self:SetText( "" )
end
function PANEL:OnDepressionChanged( b )
if ( IsValid( self.checkbox ) ) then
self.checkbox:SetVisible( !b )
end
end
function PANEL:Setup( name, icon, label )
self.label = label
self.name = name
self.icon = icon
self:SetMaterial( icon )
self:SetName( label or name )
self.PP = list.Get( "PostProcess" )[ name ]
if ( !self.PP ) then return end
self.DoClick = function()
if ( self.PP.onclick ) then
return self.PP.onclick()
end
if ( !self.PP.cpanel ) then return end
if ( !IsValid( self.cp ) ) then
self.cp = vgui.Create( "ControlPanel" )
self.cp:SetName( name )
self.PP.cpanel( self.cp )
end
spawnmenu.ActivateToolPanel( 1, self.cp )
end
if ( self.PP.convar ) then
self.checkbox = self:Add( "DCheckBox" )
self.checkbox:SetConVar( self.PP.convar )
self.checkbox:SetSize( 20, 20 )
self.checkbox:SetPos( self:GetWide() - 20 - 8, 8 )
self.Enabled = function() return self.checkbox:GetChecked() end
elseif ( self.ConVars ) then
self.checkbox = self:Add( "DCheckBox" )
self.checkbox:SetSize( 20, 20 )
self.checkbox:SetPos( self:GetWide() - 20 - 8, 8 )
self.checkbox.OnChange = function( pnl, on )
for k, v in pairs( self.ConVars ) do
if ( on ) then
RunConsoleCommand( k, v.on )
else
RunConsoleCommand( k, v.off or "" )
end
end
end
self.checkbox.Think = function( pnl, on )
local good = true
for k, v in pairs( self.ConVars ) do
if ( GetConVarString( k ) != v.on ) then
good = false
end
end
pnl:SetChecked( good )
end
self.Enabled = function() return self.checkbox:GetChecked() end
end
end
function PANEL:DoRightClick()
local pCanvas = self:GetSelectionCanvas()
if ( IsValid( pCanvas ) && pCanvas:NumSelectedChildren() > 0 && self:IsSelected() ) then
return hook.Run( "SpawnlistOpenGenericMenu", pCanvas )
end
self:OpenMenu()
end
function PANEL:DoClick()
end
function PANEL:OpenMenu()
-- Do not allow removal from read only panels
if ( IsValid( self:GetParent() ) && self:GetParent().GetReadOnly && self:GetParent():GetReadOnly() ) then return end
local menu = DermaMenu()
menu:AddOption( "#spawnmenu.menu.delete", function()
self:Remove()
hook.Run( "SpawnlistContentChanged" )
end ):SetIcon( "icon16/bin_closed.png" )
menu:Open()
end
function PANEL:Enabled()
return false
end
function PANEL:ToTable( bigtable )
local tab = {}
tab.type = "postprocess"
tab.name = self.name
tab.label = self.label
tab.icon = self.icon
tab.convars = self.ConVars
table.insert( bigtable, tab )
end
function PANEL:Copy()
local copy = vgui.Create( "PostProcessIcon", self:GetParent() )
copy:CopyBounds( self )
copy.ConVars = self.ConVars
copy:Setup( self.name, self.icon, self.label )
return copy
end
vgui.Register( "PostProcessIcon", PANEL, "ContentIcon" )
spawnmenu.AddContentType( "postprocess", function( container, obj )
if ( !obj.name ) then return end
if ( !obj.icon ) then return end
local icon = vgui.Create( "PostProcessIcon", container )
if ( obj.convars ) then
icon.ConVars = obj.convars
end
icon:Setup( obj.name, obj.icon, obj.label )
container:Add( icon )
return icon
end )

View File

@@ -0,0 +1,12 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
include( "content/content.lua" )

View File

@@ -0,0 +1,43 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
AddCSLuaFile( "sandbox/gamemode/spawnmenu/controls/manifest.lua" )
AddCSLuaFile( "sandbox/gamemode/spawnmenu/controls/control_presets.lua" )
AddCSLuaFile( "sandbox/gamemode/spawnmenu/controls/preset_editor.lua" )
AddCSLuaFile( "sandbox/gamemode/spawnmenu/controls/ropematerial.lua" )
AddCSLuaFile( "sandbox/gamemode/spawnmenu/controls/ctrlnumpad.lua" )
AddCSLuaFile( "sandbox/gamemode/spawnmenu/controls/ctrlcolor.lua" )
AddCSLuaFile( "sandbox/gamemode/spawnmenu/controls/ctrllistbox.lua" )
AddCSLuaFile( "sandbox/gamemode/spawnmenu/contextmenu.lua" )
AddCSLuaFile( "sandbox/gamemode/spawnmenu/creationmenu.lua" )
AddCSLuaFile( "sandbox/gamemode/spawnmenu/spawnmenu.lua" )
AddCSLuaFile( "sandbox/gamemode/spawnmenu/toolmenu.lua" )
AddCSLuaFile( "sandbox/gamemode/spawnmenu/controlpanel.lua" )
AddCSLuaFile( "sandbox/gamemode/spawnmenu/toolpanel.lua" )
AddCSLuaFile( "sandbox/gamemode/spawnmenu/creationmenu/manifest.lua" )
AddCSLuaFile( "sandbox/gamemode/spawnmenu/creationmenu/content/content.lua" )
AddCSLuaFile( "sandbox/gamemode/spawnmenu/creationmenu/content/contentcontainer.lua" )
AddCSLuaFile( "sandbox/gamemode/spawnmenu/creationmenu/content/contentheader.lua" )
AddCSLuaFile( "sandbox/gamemode/spawnmenu/creationmenu/content/contenticon.lua" )
AddCSLuaFile( "sandbox/gamemode/spawnmenu/creationmenu/content/contentsearch.lua" )
AddCSLuaFile( "sandbox/gamemode/spawnmenu/creationmenu/content/contentsidebar.lua" )
AddCSLuaFile( "sandbox/gamemode/spawnmenu/creationmenu/content/contentsidebartoolbox.lua" )
AddCSLuaFile( "sandbox/gamemode/spawnmenu/creationmenu/content/postprocessicon.lua" )
AddCSLuaFile( "sandbox/gamemode/spawnmenu/creationmenu/content/contenttypes/addonprops.lua" )
AddCSLuaFile( "sandbox/gamemode/spawnmenu/creationmenu/content/contenttypes/custom.lua" )
AddCSLuaFile( "sandbox/gamemode/spawnmenu/creationmenu/content/contenttypes/entities.lua" )
AddCSLuaFile( "sandbox/gamemode/spawnmenu/creationmenu/content/contenttypes/gameprops.lua" )
AddCSLuaFile( "sandbox/gamemode/spawnmenu/creationmenu/content/contenttypes/npcs.lua" )
AddCSLuaFile( "sandbox/gamemode/spawnmenu/creationmenu/content/contenttypes/postprocess.lua" )
AddCSLuaFile( "sandbox/gamemode/spawnmenu/creationmenu/content/contenttypes/weapons.lua" )
AddCSLuaFile( "sandbox/gamemode/spawnmenu/creationmenu/content/contenttypes/vehicles.lua" )
AddCSLuaFile( "sandbox/gamemode/spawnmenu/creationmenu/content/contenttypes/saves.lua" )
AddCSLuaFile( "sandbox/gamemode/spawnmenu/creationmenu/content/contenttypes/dupes.lua" )

View File

@@ -0,0 +1,406 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local spawnmenu_border = CreateConVar( "spawnmenu_border", "0.1", { FCVAR_ARCHIVE }, "Amount of empty space around the Sandbox spawn menu." )
include( "toolmenu.lua" )
include( "contextmenu.lua" )
include( "creationmenu.lua" )
local PANEL = {}
function PANEL:Init()
self:Dock( FILL )
self.HorizontalDivider = vgui.Create( "DHorizontalDivider", self )
self.HorizontalDivider:Dock( FILL )
self.HorizontalDivider:SetLeftWidth( ScrW() ) -- It will be automatically resized by DHorizontalDivider to account for GetRightMin/GetLeftMin
self.HorizontalDivider:SetDividerWidth( 6 )
self.HorizontalDivider:SetCookieName( "SpawnMenuDiv" )
self.HorizontalDivider:SetRightMin( 300 )
if ( ScrW() >= 1024 ) then self.HorizontalDivider:SetRightMin( 460 ) end
self.ToolMenu = vgui.Create( "ToolMenu", self.HorizontalDivider )
self.HorizontalDivider:SetRight( self.ToolMenu )
self.CreateMenu = vgui.Create( "CreationMenu", self.HorizontalDivider )
self.HorizontalDivider:SetLeft( self.CreateMenu )
self.m_bHangOpen = false
self:SetMouseInputEnabled( true )
self.ToolToggle = vgui.Create( "DImageButton", self )
self.ToolToggle:SetImage( "gui/spawnmenu_toggle" )
self.ToolToggle:SetSize( 16, 16 )
self.ToolToggle.DoClick = function()
self.ToolMenu:SetVisible( !self.ToolMenu:IsVisible() )
self:InvalidateLayout()
if ( self.ToolMenu:IsVisible() ) then
self.ToolToggle:SetImage( "gui/spawnmenu_toggle" )
self.CreateMenu:Dock( NODOCK ) -- What an ugly hack
self.HorizontalDivider:SetRight( self.ToolMenu )
self.HorizontalDivider:SetLeft( self.CreateMenu )
else
self.ToolToggle:SetImage( "gui/spawnmenu_toggle_back" )
self.HorizontalDivider:SetRight( nil ) -- What an ugly hack
self.HorizontalDivider:SetLeft( nil )
self.CreateMenu:SetParent( self.HorizontalDivider )
self.CreateMenu:Dock( FILL )
end
end
end
function PANEL:OpenCreationMenuTab( name )
self.CreateMenu:SwitchToName( name )
end
function PANEL:GetToolMenu()
return self.ToolMenu
end
function PANEL:GetCreationMenu()
return self.CreateMenu
end
--[[---------------------------------------------------------
Name: OnClick
-----------------------------------------------------------]]
function PANEL:OnMousePressed()
self:Close()
end
--[[---------------------------------------------------------
Name: HangOpen
-----------------------------------------------------------]]
function PANEL:HangOpen( bHang )
self.m_bHangOpen = bHang
end
--[[---------------------------------------------------------
Name: HangingOpen
-----------------------------------------------------------]]
function PANEL:HangingOpen()
return self.m_bHangOpen
end
--[[---------------------------------------------------------
Name: Paint
-----------------------------------------------------------]]
function PANEL:Open()
RestoreCursorPosition()
self.m_bHangOpen = false
-- If the context menu is open, try to close it..
if ( IsValid( g_ContextMenu ) && g_ContextMenu:IsVisible() ) then
g_ContextMenu:Close( true )
end
if ( self:IsVisible() ) then return end
CloseDermaMenus()
self:MakePopup()
self:SetVisible( true )
self:SetKeyboardInputEnabled( false )
self:SetMouseInputEnabled( true )
self:SetAlpha( 255 )
achievements.SpawnMenuOpen()
if ( IsValid( self.StartupTool ) && self.StartupTool.Name ) then
self.StartupTool:SetSelected( true )
spawnmenu.ActivateTool( self.StartupTool.Name, true )
self.StartupTool = nil
end
end
--[[---------------------------------------------------------
Name: Paint
-----------------------------------------------------------]]
function PANEL:Close( bSkipAnim )
if ( self.m_bHangOpen ) then
self.m_bHangOpen = false
return
end
if ( self:IsVisible() ) then RememberCursorPosition() end
CloseDermaMenus()
self:SetKeyboardInputEnabled( false )
self:SetMouseInputEnabled( false )
self:SetVisible( false )
end
function PANEL:PerformLayout()
local MarginX = math.Clamp( ( ScrW() - 1024 ) * spawnmenu_border:GetFloat(), 25, 256 )
local MarginY = math.Clamp( ( ScrH() - 768 ) * spawnmenu_border:GetFloat(), 25, 256 )
-- At this size we can't spare any space for emptiness
if ( ScrW() < 1024 || ScrH() < 768 ) then
MarginX = 0
MarginY = 0
end
self:DockPadding( 0, 0, 0, 0 )
self.HorizontalDivider:DockMargin( MarginX, MarginY, MarginX, MarginY )
self.HorizontalDivider:SetLeftMin( self.HorizontalDivider:GetWide() / 3 )
self.ToolToggle:AlignRight( 6 )
self.ToolToggle:AlignTop( 6 )
end
function PANEL:StartKeyFocus( pPanel )
self.m_pKeyFocus = pPanel
self:SetKeyboardInputEnabled( true )
self:HangOpen( true )
end
function PANEL:EndKeyFocus( pPanel )
if ( self.m_pKeyFocus != pPanel ) then return end
self:SetKeyboardInputEnabled( false )
end
function PANEL:OnSizeChanged( newW, newH )
local divW = self.HorizontalDivider:GetWide()
local divL = self.HorizontalDivider:GetLeftWidth()
self:InvalidateLayout( true )
local divWnew = self.HorizontalDivider:GetWide()
if ( divW > divL && divW < divWnew ) then
local ratio = divL / divW
self.HorizontalDivider:SetLeftWidth( ratio * divWnew )
end
end
vgui.Register( "SpawnMenu", PANEL, "EditablePanel" )
--[[---------------------------------------------------------
Called to create the spawn menu..
-----------------------------------------------------------]]
local function CreateSpawnMenu()
if ( !hook.Run( "SpawnMenuEnabled" ) ) then return end
-- If we have an old spawn menu remove it.
if ( IsValid( g_SpawnMenu ) ) then
g_SpawnMenu:Remove()
g_SpawnMenu = nil
end
hook.Run( "PreReloadToolsMenu" )
-- Start Fresh
spawnmenu.ClearToolMenus()
-- Add defaults for the gamemode. In sandbox these defaults
-- are the Main/Postprocessing/Options tabs.
-- They're added first in sandbox so they're always first
hook.Run( "AddGamemodeToolMenuTabs" )
-- Use this hook to add your custom tools
-- This ensures that the default tabs are always
-- first.
hook.Run( "AddToolMenuTabs" )
-- Use this hook to add your custom tools
-- We add the gamemode tool menu categories first
-- to ensure they're always at the top.
hook.Run( "AddGamemodeToolMenuCategories" )
hook.Run( "AddToolMenuCategories" )
-- Add the tabs to the tool menu before trying
-- to populate them with tools.
hook.Run( "PopulateToolMenu" )
g_SpawnMenu = vgui.Create( "SpawnMenu" )
if ( IsValid( g_SpawnMenu ) ) then
g_SpawnMenu:SetVisible( false )
hook.Run( "SpawnMenuCreated", g_SpawnMenu )
end
CreateContextMenu()
hook.Run( "PostReloadToolsMenu" )
end
-- Hook to create the spawnmenu at the appropriate time (when all sents and sweps are loaded)
hook.Add( "OnGamemodeLoaded", "CreateSpawnMenu", CreateSpawnMenu )
concommand.Add( "spawnmenu_reload", CreateSpawnMenu )
function GM:OnSpawnMenuOpen()
-- Let the gamemode decide whether we should open or not..
if ( !hook.Call( "SpawnMenuOpen", self ) ) then return end
if ( IsValid( g_SpawnMenu ) ) then
g_SpawnMenu:Open()
menubar.ParentTo( g_SpawnMenu )
end
hook.Call( "SpawnMenuOpened", self )
end
function GM:OnSpawnMenuClose()
if ( IsValid( g_SpawnMenu ) ) then g_SpawnMenu:Close() end
hook.Call( "SpawnMenuClosed", self )
end
--[[---------------------------------------------------------
Name: HOOK SpawnMenuKeyboardFocusOn
Called when text entry needs keyboard focus
-----------------------------------------------------------]]
local function SpawnMenuKeyboardFocusOn( pnl )
if ( IsValid( g_SpawnMenu ) && IsValid( pnl ) && pnl:HasParent( g_SpawnMenu ) ) then
g_SpawnMenu:StartKeyFocus( pnl )
end
if ( IsValid( g_ContextMenu ) && IsValid( pnl ) && pnl:HasParent( g_ContextMenu ) ) then
g_ContextMenu:StartKeyFocus( pnl )
end
end
hook.Add( "OnTextEntryGetFocus", "SpawnMenuKeyboardFocusOn", SpawnMenuKeyboardFocusOn )
--[[---------------------------------------------------------
Name: HOOK SpawnMenuKeyboardFocusOff
Called when text entry stops needing keyboard focus
-----------------------------------------------------------]]
local function SpawnMenuKeyboardFocusOff( pnl )
if ( IsValid( g_SpawnMenu ) && IsValid( pnl ) && pnl:HasParent( g_SpawnMenu ) ) then
g_SpawnMenu:EndKeyFocus( pnl )
end
if ( IsValid( g_ContextMenu ) && IsValid( pnl ) && pnl:HasParent( g_ContextMenu ) ) then
g_ContextMenu:EndKeyFocus( pnl )
end
end
hook.Add( "OnTextEntryLoseFocus", "SpawnMenuKeyboardFocusOff", SpawnMenuKeyboardFocusOff )
--[[---------------------------------------------------------
Name: HOOK SpawnMenuOpenGUIMousePressed
Don't do context screen clicking if spawnmenu is open
-----------------------------------------------------------]]
local function SpawnMenuOpenGUIMousePressed()
if ( !IsValid( g_SpawnMenu ) ) then return end
if ( !g_SpawnMenu:IsVisible() ) then return end
return true
end
hook.Add( "GUIMousePressed", "SpawnMenuOpenGUIMousePressed", SpawnMenuOpenGUIMousePressed )
--[[---------------------------------------------------------
Name: HOOK SpawnMenuOpenGUIMousePressed
Close spawnmenu if it's open
-----------------------------------------------------------]]
local function SpawnMenuOpenGUIMouseReleased()
if ( !IsValid( g_SpawnMenu ) ) then return end
if ( !g_SpawnMenu:IsVisible() ) then return end
g_SpawnMenu:Close()
return true
end
hook.Add( "GUIMouseReleased", "SpawnMenuOpenGUIMouseReleased", SpawnMenuOpenGUIMouseReleased )
--[[---------------------------------------------------------
Handle spawn menu language switching
- The spawn menu needs to be recreated ("refreshed") after a language switch
- We SHOULDN'T refresh it if the user has unsaved changes to their spawn list (these would be lost!)
- We SHOULDN'T refresh it if the user has the spawn menu open (that would be bad user experience)
- But, we SHOULD refresh it if the user saves or reverts any changes and closes the spawn menu
- What if the user switches BACK to the original language they were using? Surely, a refresh is not needed now?
- No, in this case we should still refresh the spawn menu because some text and labels do actually update during use of the spawn menu and might be left "dirty"
-----------------------------------------------------------]]
local function SpawnMenuLanguageChanged()
if ( !IsValid( g_SpawnMenu ) ) then return end
if ( g_SpawnMenu.m_UnsavedModifications || g_SpawnMenu:IsVisible() ) then
-- If there are unsaved modifications, or the spawn menu is somehow open, mark the spawn menu for recreation when the opportunity arises
g_SpawnMenu.m_NeedsLanguageRefresh = true
else
-- If there are no unsaved modifications, and the spawn menu isn't open, we can go ahead and safely refresh the spawn menu
CreateSpawnMenu()
end
end
-- When gmod_language changes, call SpawnMenuLanguageChanged
cvars.AddChangeCallback( "gmod_language", SpawnMenuLanguageChanged, "spawnmenu_reload" )
local function ProtectSpawnMenuChanges()
if ( !IsValid( g_SpawnMenu ) ) then return end
-- Mark the spawn menu as having unsaved modifications
g_SpawnMenu.m_UnsavedModifications = true
end
hook.Add( "SpawnlistContentChanged", "ProtectSpawnMenuChanges", ProtectSpawnMenuChanges )
local function SpawnMenuChangesFinished()
if ( !IsValid( g_SpawnMenu ) ) then return end
-- Mark the spawn menu as no longer having unsaved modifications
g_SpawnMenu.m_UnsavedModifications = nil
end
hook.Add( "OnRevertSpawnlist", "SpawnMenuChangesFinished", SpawnMenuChangesFinished )
hook.Add( "OnSaveSpawnlist", "SpawnMenuChangesFinished", SpawnMenuChangesFinished )
local function SpawnMenuLanguageRefresh()
if ( !IsValid( g_SpawnMenu ) ) then return end
-- When the spawn menu is closed, check if it needs a language refresh. If it has no unsaved modifications, refresh it!
if ( !g_SpawnMenu.m_UnsavedModifications && g_SpawnMenu.m_NeedsLanguageRefresh ) then
g_SpawnMenu.m_NeedsLanguageRefresh = nil
CreateSpawnMenu()
end
end
hook.Add( "OnSpawnMenuClose", "SpawnMenuLanguageRefresh", SpawnMenuLanguageRefresh )

View File

@@ -0,0 +1,80 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
include( "toolpanel.lua" )
local PANEL = {}
--[[---------------------------------------------------------
Name: Paint
-----------------------------------------------------------]]
function PANEL:Init()
self.ToolPanels = {}
self:LoadTools()
self:SetFadeTime( 0 )
end
--[[---------------------------------------------------------
LoadTools
-----------------------------------------------------------]]
function PANEL:LoadTools()
local tools = spawnmenu.GetTools()
for strName, pTable in pairs( tools ) do
self:AddToolPanel( strName, pTable )
end
end
--[[---------------------------------------------------------
AddToolPanel
-----------------------------------------------------------]]
function PANEL:AddToolPanel( Name, ToolTable )
-- I hate relying on a table's internal structure
-- but this isn't really that avoidable.
local Panel = vgui.Create( "ToolPanel" )
Panel:SetTabID( Name )
Panel:LoadToolsFromTable( ToolTable.Items )
Panel.PropertySheet = self
Panel.PropertySheetTab = self:AddSheet( ToolTable.Label, Panel, ToolTable.Icon ).Tab
self.ToolPanels[ Name ] = Panel
end
--[[---------------------------------------------------------
Name: Paint
-----------------------------------------------------------]]
function PANEL:Paint( w, h )
DPropertySheet.Paint( self, w, h )
end
--[[---------------------------------------------------------
Name: GetToolPanel
-----------------------------------------------------------]]
function PANEL:GetToolPanel( id )
return self.ToolPanels[ id ]
end
vgui.Register( "ToolMenu", PANEL, "DPropertySheet" )

View File

@@ -0,0 +1,268 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
include( "controlpanel.lua" )
local PANEL = {}
AccessorFunc( PANEL, "m_TabID", "TabID" )
function PANEL:Init()
self.HorizontalDivider = vgui.Create( "DHorizontalDivider", self )
self.HorizontalDivider:Dock( FILL )
self.HorizontalDivider:SetLeftWidth( 130 )
self.HorizontalDivider:SetLeftMin( 130 )
self.HorizontalDivider:SetRightMin( 200 )
if ( ScrW() >= 1024 ) then self.HorizontalDivider:SetRightMin( 256 ) end
self.HorizontalDivider:SetDividerWidth( 6 )
self.HorizontalDivider:SetCookieName( "SpawnMenuToolMenuDiv" )
local leftContainer = vgui.Create( "Panel", self.HorizontalDivider )
self.SearchBar = vgui.Create( "DTextEntry", leftContainer )
self.SearchBar:SetWidth( 130 )
self.SearchBar:SetPlaceholderText( "#spawnmenu.quick_filter" )
self.SearchBar:DockMargin( 0, 0, 0, 5 )
self.SearchBar:Dock( TOP )
self.SearchBar:SetUpdateOnType( true )
self.SearchBar.OnValueChange = function( s, text )
self:PerformToolFiltering( text:Trim():lower() )
end
self.List = vgui.Create( "DCategoryList", leftContainer )
self.List:SetWidth( 130 )
self.List:Dock( FILL )
self.HorizontalDivider:SetLeft( leftContainer )
self.Content = vgui.Create( "DCategoryList", self.HorizontalDivider )
self.HorizontalDivider:SetRight( self.Content )
self.LastUpdate = 0
self.IsToolTab = false
local label = vgui.Create( "Panel", self )
label:Dock( BOTTOM )
label:SetVisible( false )
label:SetTall( 72 )
label.Text = ""
label.Paint = function( s, w, h )
local parsed = markup.Parse( "<font=DermaLarge>" .. s.Text .. "</font>", w )
parsed:Draw( w / 2, h / 2, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER, 255, TEXT_ALIGN_CENTER )
end
self.WarningLabel = label
end
function PANEL:Think()
if ( self.LastUpdate + 0.5 < SysTime() ) then
self.LastUpdate = SysTime()
self:UpdateToolDisabledStatus()
if ( !self.IsToolTab ) then return end
local disabled = false
local noToolgun = IsValid( LocalPlayer() ) && !LocalPlayer():HasWeapon( "gmod_tool" )
if ( self.ActiveCPName ) then
local cvar = GetConVar( "toolmode_allow_" .. self.ActiveCPName )
if ( cvar ) then disabled = !cvar:GetBool() end
end
self.WarningLabel.Text = noToolgun and "You do not have the Tool Gun to use tools!" or "Currently selected tool is disabled by the server!"
if ( ( disabled or noToolgun ) != self.WarningLabel:IsVisible() ) then
self.WarningLabel:SetVisible( disabled or noToolgun )
self:InvalidateLayout()
end
end
end
function PANEL:PerformToolFiltering( text )
for cid, category in ipairs( self.List.pnlCanvas:GetChildren() ) do
local count = 0
local category_matched = false
if ( string.find( category.Header:GetText():lower(), text, nil, true ) ) then
category_matched = true
end
for id, item in ipairs( category:GetChildren() ) do
if ( item == category.Header ) then continue end
local str = item.Text
if ( str:StartsWith( "#" ) ) then str = str:sub( 2 ) end
str = language.GetPhrase( str )
if ( !category_matched && !string.find( str:lower(), text, nil, true ) ) then
item:SetVisible( false )
else
item:SetVisible( true )
count = count + 1
end
item:InvalidateLayout()
end
if ( count < 1 && !category_matched ) then
category:SetVisible( false )
else
category:SetVisible( true )
-- Make sure the category is expanded, but restore the state when we quit searching
if ( text == "" ) then
if ( category._preSearchState != nil ) then
category:SetExpanded( category._preSearchState )
category._preSearchState = nil
end
else
if ( category._preSearchState == nil ) then category._preSearchState = category:GetExpanded() end
category:SetExpanded( true )
end
end
category:InvalidateLayout()
end
self.List.pnlCanvas:InvalidateLayout()
self.List:InvalidateLayout()
end
function PANEL:UpdateToolDisabledStatus()
for cid, category in ipairs( self.List.pnlCanvas:GetChildren() ) do
for id, item in ipairs( category:GetChildren() ) do
if ( item == category.Header ) then continue end
local cvar = GetConVar( "toolmode_allow_" .. item.Name )
if ( !cvar ) then continue end
local enabled = cvar:GetBool()
if ( enabled == item:IsEnabled() ) then continue end
item:SetEnabled( enabled )
if ( enabled ) then
item:SetTooltip()
else
item:SetTooltip( "This tool is disabled by the server!" )
end
end
end
end
function PANEL:LoadToolsFromTable( inTable )
for k, v in pairs( table.Copy( inTable ) ) do
if ( istable( v ) ) then
-- Remove these from the table so we can
-- send the rest of the table to the other
-- function
local Name = v.ItemName
local Label = v.Text
v.ItemName = nil
v.Text = nil
if ( v[ 1 ] && v[ 1 ].Command and v[ 1 ].Command:StartsWith( "gmod_tool " ) ) then
self.IsToolTab = true
end
self:AddCategory( Name, Label, v )
end
end
end
function PANEL:AddCategory( name, catName, tItems )
local Category = self.List:Add( catName )
Category:SetCookieName( "ToolMenu." .. tostring( self:GetTabID() ) .. "." .. tostring( name ) )
local tools = {}
for k, v in pairs( tItems ) do
local name = v.Text or v.ItemName or v.Controls or v.Command or tostring( k )
tools[ language.GetPhrase( name ) ] = v
end
local currentMode = GetConVar( "gmod_toolmode" ):GetString()
for k, v in SortedPairs( tools ) do
local item = Category:Add( v.Text or k )
item.DoClick = function( button )
spawnmenu.ActivateTool( button.Name )
end
item.ControlPanelBuildFunction = v.CPanelFunction
item.Command = v.Command
item.Name = v.ItemName
item.Controls = v.Controls
item.Text = v.Text
-- Mark this button as the one to select on first spawnmenu open
if ( currentMode == v.ItemName ) then
timer.Simple( 0, function() -- Have to wait a frame to get the g_SpawnMenu global, ew
g_SpawnMenu.StartupTool = item
end )
end
end
self:InvalidateLayout()
end
-- Internal, makes the given tool highlighted in its DCategoryList
function PANEL:SetActiveToolText( str )
for cid, category in ipairs( self.List.pnlCanvas:GetChildren() ) do
for id, item in ipairs( category:GetChildren() ) do
if ( item == category.Header ) then continue end
if ( item.Name == str ) then
self.List:UnselectAll()
item:SetSelected( true )
return
end
end
end
end
function PANEL:SetActive( cp )
local kids = self.Content:GetCanvas():GetChildren()
for k, v in pairs( kids ) do
v:SetVisible( false )
end
self.Content:AddItem( cp )
self.ActiveCPName = cp.Name
cp:SetVisible( true )
cp:Dock( TOP )
end
vgui.Register( "ToolPanel", PANEL, "Panel" )