This commit is contained in:
lifestorm
2024-08-04 23:54:45 +03:00
parent 0e770b2b49
commit df294d03aa
7526 changed files with 4011945 additions and 15 deletions

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