This commit is contained in:
lifestorm
2024-08-05 18:40:29 +03:00
parent 9f505a0646
commit c6d9b6f580
8044 changed files with 1853472 additions and 21 deletions

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 )