This commit is contained in:
lifestorm
2024-08-04 22:55:00 +03:00
parent 0e770b2b49
commit 94063e4369
7342 changed files with 1718932 additions and 14 deletions

174
lua/menu/background.lua Normal file
View File

@@ -0,0 +1,174 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local MenuGradient = Material( "html/img/gradient.png", "nocull smooth" )
local FreeMaterial = nil
local function CreateBackgroundMaterial( path )
if ( FreeMaterial ) then
FreeMaterial:SetDynamicImage( path )
local ret = FreeMaterial
FreeMaterial = nil
return ret
end
return DynamicMaterial( path, "0100010" ) -- nocull smooth
end
local function FreeBackgroundMaterial( mat )
if ( FreeMaterial ) then
MsgN( "Warning! Menu shouldn't be releasing a material when one is already queued for use!" )
end
FreeMaterial = mat
end
local Images = {}
local Active = nil
local Outgoing = nil
local function Think( tbl )
tbl.Angle = tbl.Angle + ( tbl.AngleVel * FrameTime() )
tbl.Size = tbl.Size + ( ( tbl.SizeVel / tbl.Size) * FrameTime() )
if ( tbl.AlphaVel ) then
tbl.Alpha = tbl.Alpha - tbl.AlphaVel * FrameTime()
end
if ( tbl.DieTime > 0 ) then
tbl.DieTime = tbl.DieTime - FrameTime()
if ( tbl.DieTime <= 0 ) then
ChangeBackground()
end
end
end
local function Render( tbl )
if ( !tbl.mat ) then return end
surface.SetMaterial( tbl.mat )
surface.SetDrawColor( 255, 255, 255, tbl.Alpha )
local w = ScrH() * tbl.Size * tbl.Ratio
local h = ScrH() * tbl.Size
local x = ScrW() * 0.5
local y = ScrH() * 0.5
surface.DrawTexturedRectRotated( x, y, w, h, tbl.Angle )
end
local function ShouldBackgroundUpdate()
return !IsInGame() && !IsInLoading()
end
function DrawBackground()
if ( ShouldBackgroundUpdate() ) then
if ( Active ) then
Think( Active )
Render( Active )
end
if ( Outgoing ) then
Think( Outgoing )
Render( Outgoing )
end
end
surface.SetMaterial( MenuGradient )
surface.SetDrawColor( 255, 255, 255, 255 )
surface.DrawTexturedRect( 0, 0, 1024, ScrH() )
end
function ClearBackgroundImages( img )
Images = {}
end
function AddBackgroundImage( img )
table.insert( Images, img )
end
local LastGamemode = "none"
function ChangeBackground( currentgm )
if ( !ShouldBackgroundUpdate() ) then return end -- Don't try to load new images while in-game or loading
if ( currentgm && currentgm == LastGamemode ) then return end
if ( currentgm ) then LastGamemode = currentgm end
local img = table.Random( Images )
if ( !img ) then
print( "No main menu backgrounds found!" )
return
end
-- We just rolled the same image, no thank you, reroll
if ( Active && img == Active.Name && #Images > 1 ) then
ChangeBackground()
return
end
if ( Outgoing ) then
FreeBackgroundMaterial( Outgoing.mat )
Outgoing.mat = nil
end
Outgoing = Active
if ( Outgoing ) then
Outgoing.AlphaVel = 255
end
local mat = CreateBackgroundMaterial( img )
if ( !mat || mat:IsError() ) then
print( "Failed to create material for background ", img )
table.RemoveByValue( Images, img )
ChangeBackground()
return
end
Active = {
Ratio = mat:GetInt( "$realwidth" ) / mat:GetInt( "$realheight" ),
Size = 1,
Angle = 0,
AngleVel = -( 5 / 30 ),
SizeVel = 0.3 / 30,
Alpha = 255,
DieTime = 30,
mat = mat,
Name = img
}
if ( Active.Ratio < ScrW() / ScrH() ) then
Active.Size = Active.Size + ( ( ScrW() / ScrH() ) - Active.Ratio )
end
end

54
lua/menu/cef_credits.lua Normal file
View File

@@ -0,0 +1,54 @@
--[[
| 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 = {}
PANEL.Base = "DFrame"
function PANEL:Init()
self.HTML = vgui.Create( "Chromium", self )
-- Trying to open credits on a non-cef build?
if ( !self.HTML ) then
self:Remove()
return
end
self.HTML:Dock( FILL )
self.HTML:OpenURL( "chrome://credits/" )
self.HTML:SetOpenLinksExternally( true )
self:SetTitle( "Chromium Embedded Framework Credits" )
self:SetPos( 16, 16 )
self:SetSize( 720, 400 )
self:SetSizable( true )
self:MakePopup()
end
concommand.Add( "cef_credits", function()
vgui.CreateFromTable( PANEL )
end )
concommand.Add( "gmod_tos", function()
gui.OpenURL( "https://facepunch.com/legal/tos" )
end )
concommand.Add( "gmod_privacy", function()
gui.OpenURL( "https://facepunch.com/legal/privacy" )
end )
concommand.Add( "gmod_modding", function()
gui.OpenURL( "https://facepunch.com/legal/modding" )
end )
concommand.Add( "gmod_servers", function()
gui.OpenURL( "https://wiki.facepunch.com/gmod/server_operator_rules" )
end )

View File

@@ -0,0 +1,219 @@
--[[
| 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 crosshair_sliders = {
{ title = "Style", cvar = "cl_crosshairstyle", values = { ["0"] = "Half-Life 2", ["1"] = "Dot Image", ["2"] = "Classic" } },
{ title = "Gap", cvar = "cl_crosshairgap", min = 0, max = 200 },
{ title = "Size", cvar = "cl_crosshairsize", min = 0, max = 200 },
{ title = "Thickness", cvar = "cl_crosshairthickness", min = 0, max = 200 },
{ title = "Dot", cvar = "cl_crosshairdot" },
{ title = "T-Style", cvar = "cl_crosshair_t" },
{ title = "Use Alpha", cvar = "cl_crosshairusealpha" },
{ title = "Quick Info", cvar = "hud_quickinfo" },
{ title = "Outline", cvar = "cl_crosshair_drawoutline" },
{ title = "Outline Thickness", cvar = "cl_crosshair_outlinethickness", min = 0.1, max = 3 },
}
local function GetCrosshairColor()
return Color( GetConVarNumber( "cl_crosshaircolor_r" ),
GetConVarNumber( "cl_crosshaircolor_g" ),
GetConVarNumber( "cl_crosshaircolor_b" ),
GetConVarNumber( "cl_crosshairalpha" )
)
end
local function DrawCrosshairRect( color, x0, y0, x1, y1, bAdditive )
if ( GetConVarNumber( "cl_crosshair_drawoutline" ) != 0 ) then
local flThick = GetConVarNumber( "cl_crosshair_outlinethickness" )
surface.SetDrawColor( 0, 0, 0, color.a )
surface.DrawRect( x0 - flThick, y0 - flThick, (x1 + flThick) - x0 + flThick, (y1 + flThick) - y0 + flThick )
end
surface.SetDrawColor( color.r, color.g, color.b, color.a )
if ( bAdditive ) then
surface.DrawTexturedRect( x0, y0, x1 - x0, y1 - y0 )
else
surface.DrawRect( x0, y0, x1 - x0, y1 - y0 )
end
end
local additiveTex = Material( "vgui/white_additive" )
local function DrawSimpleCrosshairPreview( x, y )
local color = GetCrosshairColor()
local bAdditive = GetConVarNumber( "cl_crosshairusealpha" ) == 0
if ( bAdditive ) then
surface.SetMaterial( additiveTex )
color.a = 200
end
local iBarSize = math.Round( ScreenScaleH( GetConVarNumber( "cl_crosshairsize" ) ))
local iBarThickness = math.max( 1, math.Round( ScreenScaleH( GetConVarNumber( "cl_crosshairthickness" ) ) ) )
local iInnerCrossDist = GetConVarNumber( "cl_crosshairgap" )
-- draw horizontal crosshair lines
local iInnerLeft = x - iInnerCrossDist - iBarThickness / 2
local iInnerRight = iInnerLeft + 2 * iInnerCrossDist + iBarThickness
local iOuterLeft = iInnerLeft - iBarSize
local iOuterRight = iInnerRight + iBarSize
local y0 = y - iBarThickness / 2
local y1 = y0 + iBarThickness
DrawCrosshairRect( color, iOuterLeft, y0, iInnerLeft, y1, bAdditive )
DrawCrosshairRect( color, iInnerRight, y0, iOuterRight, y1, bAdditive )
-- draw vertical crosshair lines
local iInnerTop = y - iInnerCrossDist - iBarThickness / 2
local iInnerBottom = iInnerTop + 2 * iInnerCrossDist + iBarThickness
local iOuterTop = iInnerTop - iBarSize
local iOuterBottom = iInnerBottom + iBarSize
local x0 = x - iBarThickness / 2
local x1 = x0 + iBarThickness
if ( GetConVarNumber( "cl_crosshair_t" ) == 0 ) then
DrawCrosshairRect( color, x0, iOuterTop, x1, iInnerTop, bAdditive )
end
DrawCrosshairRect( color, x0, iInnerBottom, x1, iOuterBottom, bAdditive )
-- draw dot
if ( GetConVarNumber( "cl_crosshairdot" ) != 0 ) then
x0 = x - iBarThickness / 2
x1 = x0 + iBarThickness
y0 = y - iBarThickness / 2
y1 = y0 + iBarThickness
DrawCrosshairRect( color, x0, y0, x1, y1, bAdditive )
end
end
concommand.Add( "crosshair_setup", function()
surface.CreateFont( "QuickInfoLarge", {
font = "HL2cross",
size = 64,
additive = false,
} )
local frame = vgui.Create( "DFrame" )
frame:SetSize( 600, 480 )
frame:Center()
frame:SetTitle( "Default Crosshair Setup" )
frame:MakePopup()
local crosshairMat = Material( "gui/crosshair.png" )
local preview = vgui.Create( "DImage", frame )
preview:Dock( LEFT )
preview:DockPadding( 5, 5, 5, 5 )
preview:SetWide( 320 )
preview:SetMouseInputEnabled( true )
preview:SetImage( "gui/crosshair_bg.png" )
preview.PaintOver = function( p, w, h )
if ( GetConVarNumber( "cl_crosshairstyle" ) == 0 ) then
surface.SetFont( "Crosshairs" )
surface.SetTextColor( 255, 208, 64, 255 )
local width, height = surface.GetTextSize( "Q" )
surface.SetTextPos( w / 2 - width / 2, h / 2 - height / 2 )
surface.DrawText( "Q" )
elseif ( GetConVarNumber( "cl_crosshairstyle" ) == 1 ) then
surface.SetDrawColor( GetCrosshairColor() )
surface.SetMaterial( crosshairMat )
surface.DrawTexturedRect( w / 2 - 32, h / 2 - 32, 64, 64 )
elseif ( GetConVarNumber( "cl_crosshairstyle" ) >= 2 ) then
DrawSimpleCrosshairPreview( w / 2, h / 2 )
end
if ( GetConVarNumber( "hud_quickinfo" ) != 0 ) then
surface.SetFont( "QuickInfoLarge" )
surface.SetTextColor( 255, 208, 64, 200 )
local width, height = surface.GetTextSize( "{ ]" )
surface.SetTextPos( w / 2 - width / 2, h / 2 - height / 2 )
surface.DrawText( "{ ]" )
end
end
local previewBtns = vgui.Create( "Panel", preview )
previewBtns:Dock( TOP )
local img1btn = vgui.Create( "DButton", previewBtns )
img1btn:Dock( LEFT )
img1btn:SetWide( 320 / 2 )
img1btn:SetText( "Preview 1" )
img1btn.DoClick = function() preview:SetImage( "gui/crosshair_bg.png" ) end
local img2btn = vgui.Create( "DButton", previewBtns )
img2btn:SetText( "Preview 2" )
img2btn.DoClick = function() preview:SetImage( "gui/crosshair_bg2.png" ) end
img2btn:Dock( FILL )
local settings = vgui.Create( "Panel", frame )
settings:Dock( FILL )
settings:DockPadding( 5, 0, 0, 0 )
local function HideUselessStuff( style )
style = style or GetConVarNumber( "cl_crosshairstyle" )
for i, pnl in pairs( settings:GetChildren() ) do
if ( pnl.ClassName == "DColorMixer" and style == 0 ) then
pnl:SetVisible( false )
elseif ( pnl.ClassName == "DNumSlider" and style != 2 ) then
pnl:SetVisible( false )
elseif ( pnl.ClassName == "DCheckBoxLabel" ) then
pnl:SetVisible( pnl.Button.m_strConVar == "hud_quickinfo" or style == 2 )
else
pnl:SetVisible( true )
end
end
settings:InvalidateLayout()
end
for i, str in pairs( crosshair_sliders ) do
local setting = nil
if ( str.min ) then
setting = vgui.Create( "DNumSlider", settings )
setting:SetMinMax( str.min, str.max )
setting:SetDefaultValue( GetConVar( str.cvar ):GetDefault() )
setting:SetDecimals( 0 )
elseif ( str.values ) then
setting = vgui.Create( "DComboBox", settings )
setting.OnSelect = function( pnl, indx, val, data ) pnl:ConVarChanged( data ) HideUselessStuff( tonumber( data ) ) end
for id, title in pairs( str.values ) do setting:AddChoice( title, id ) end
else
setting = vgui.Create( "DCheckBoxLabel", settings )
end
setting:Dock( TOP )
setting:SetText( str.title )
setting:SetConVar( str.cvar )
end
local mixer = vgui.Create( "DColorMixer", settings )
mixer:Dock( TOP )
mixer:SetTall( 220 )
mixer:SetColor( GetCrosshairColor() )
mixer:SetConVarR( "cl_crosshaircolor_r" )
mixer:SetConVarG( "cl_crosshaircolor_g" )
mixer:SetConVarB( "cl_crosshaircolor_b" )
mixer:SetConVarA( "cl_crosshairalpha" )
HideUselessStuff()
local resetCrosshair = vgui.Create( "DButton", preview )
resetCrosshair.DoClick = function()
for i, str in pairs( crosshair_sliders ) do
local def = GetConVar( str.cvar ):GetDefault()
RunConsoleCommand( str.cvar, def )
end
mixer:SetColor( color_white ) -- hack
HideUselessStuff()
end
resetCrosshair:SetText( "Reset Crosshair" )
resetCrosshair:Dock( BOTTOM )
end )

333
lua/menu/demo_to_video.lua Normal file
View File

@@ -0,0 +1,333 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local ActiveVideo = nil
VideoSettings = nil
local stats = {
reset = 0,
encodetime = 0,
starttime = 0,
last_encodetime = 0
}
concommand.Add( "gm_demo_to_video", function( ply, cmd, args )
local demoname = args[ 1 ]
if ( !demoname ) then return end
local settings = {
name = "filled_in_later",
container = "webm",
video = "vp8",
audio = "vorbis",
bitrate = 25000,
quality = 1,
width = 640,
height = 480,
fps = 25,
frameblend = 1,
fbshutter = 0.5,
dofsteps = 0,
dofpasses = 0,
doffocusspeed = 1.0,
dofsize = 1.0,
viewsmooth = 0.0,
possmooth = 0.0
}
local Window = vgui.Create( "DFrame" )
Window:SetTitle( "Render Video" )
Window:SetSize( 600, 400 )
Window:LoadGWENFile( "resource/ui/DemoToVideo.gwen" )
Window:Center()
Window:MakePopup()
local inCodec = Window:Find( "inCodec" )
local inQuality = Window:Find( "inQuality" )
local inSize = Window:Find( "inSize" )
local btnStart = Window:Find( "btnStart" )
local inBitRate = Window:Find( "inBitRate" )
local inFPS = Window:Find( "inFPS" )
local inFrameBlend = Window:Find( "inFrameBlend" )
local inFBShutter = Window:Find( "inFBShutter" )
local inDOF = Window:Find( "inDepthOfField" )
local inDOFSpeed = Window:Find( "inDOFFocusSpeed" )
local inDOFSize = Window:Find( "inDOFBlurSize" )
local inViewSmooth = Window:Find( "inViewSmooth" )
local inPosSmooth = Window:Find( "inPosSmooth" )
inFPS.OnChange = function() settings.fps = inFPS:GetInt() end
inFPS:SetText( settings.fps )
inBitRate.OnChange = function() settings.bitrate = inBitRate:GetInt() end
inBitRate:SetText( settings.bitrate )
-- TODO!!!
inCodec.OnSelect = function( _, index, value, data ) settings.container = data[1]; settings.video = data[2]; settings.audio = data[3] end
inCodec:AddChoice( "webm", { "webm", "vp8", "vorbis" }, true )
inCodec:AddChoice( "ogg", { "ogg", "theora", "vorbis" } )
inQuality.OnSelect = function( _, index, value, data ) settings.quality = data end
inQuality:AddChoice( "0.0 - Low (but fast)", 0 )
inQuality:AddChoice( "0.5 - Medium", 0.5 )
inQuality:AddChoice( "1.0 - Highest (but slow)", 1, true )
-- TODO!!!
inSize.OnSelect = function( _, index, value, data ) settings.width = data[1] settings.height = data[2] end
local sw, sh = ScrW(), ScrH()
inSize:AddChoice( sw .. " x " .. sh .. " (highest)", { sw, sh }, true )
sw, sh = sw * 0.66666, sh * 0.66666
inSize:AddChoice( math.ceil( sw ) .. " x " .. math.ceil( sh ), { sw, sh }, true )
sw, sh = sw * 0.5, sh * 0.5
inSize:AddChoice( math.ceil( sw ) .. " x " .. math.ceil( sh ), { sw, sh }, true )
inFrameBlend.OnSelect = function( _, index, value, data ) settings.frameblend = data end
inFrameBlend:AddChoice( "Off", 1, true )
inFrameBlend:AddChoice( "Draft (8 Samples)", 8 )
inFrameBlend:AddChoice( "Good (16 Samples)", 16 )
inFrameBlend:AddChoice( "Great (32 Samples)", 32 )
inFrameBlend:AddChoice( "Overkill (64 Samples)", 64 )
inFrameBlend:AddChoice( "OverOverKill (128 Samples)", 128 )
inFBShutter.OnSelect = function( _, index, value, data ) settings.fbshutter = data end
inFBShutter:AddChoice( "90", 0.75 )
inFBShutter:AddChoice( "180", 0.5, true )
inFBShutter:AddChoice( "240", 0.25 )
inFBShutter:AddChoice( "360", 0.0 )
--
-- DOF
--
inDOF.OnSelect = function( _, index, value, data ) settings.dofsteps = data[1]; settings.dofpasses = data[2] end
inDOF:AddChoice( "Off", { 0, 0 }, true )
inDOF:AddChoice( "Draft (21 Samples)", { 6, 3 } )
inDOF:AddChoice( "Good (72 Samples)", { 12, 6 } )
inDOF:AddChoice( "Best (288 Samples)", { 24, 12 } )
inDOFSpeed.OnChange = function() settings.doffocusspeed = inDOFSpeed:GetFloat() end
inDOFSpeed:SetText( "1.0" )
inDOFSize.OnChange = function() settings.dofsize = inDOFSize:GetFloat() end
inDOFSize:SetText( "1.0" )
--
-- Smoothing
--
inViewSmooth.OnSelect = function( _, index, value, data ) settings.viewsmooth = data end
inViewSmooth:AddChoice( "Off", 0.0, true )
inViewSmooth:AddChoice( "Minimal", 0.2 )
inViewSmooth:AddChoice( "Low", 0.4 )
inViewSmooth:AddChoice( "Medium", 0.7 )
inViewSmooth:AddChoice( "High", 0.8 )
inViewSmooth:AddChoice( "Lots", 0.9 )
inViewSmooth:AddChoice( "Too Smooth", 0.97 )
inPosSmooth.OnSelect = function( _, index, value, data ) settings.possmooth = data end
inPosSmooth:AddChoice( "Off", 0.0, true )
inPosSmooth:AddChoice( "Minimal", 0.2 )
inPosSmooth:AddChoice( "Low", 0.4 )
inPosSmooth:AddChoice( "Medium", 0.7 )
inPosSmooth:AddChoice( "High", 0.8 )
inPosSmooth:AddChoice( "Lots", 0.9 )
inPosSmooth:AddChoice( "Too Smooth", 0.97 )
btnStart.DoClick = function()
-- Fill in the name here, or we'll be overwriting the same video!
local cleanname = string.GetFileFromFilename( demoname )
cleanname = cleanname:Replace( ".", "_" )
cleanname = cleanname .. " " .. util.DateStamp()
settings.name = cleanname
PrintTable( settings )
ActiveVideo, error = video.Record( settings )
if ( !ActiveVideo ) then
Derma_Message( "Couldn't record video: \n" .. error, "Make Video Error", "OK" )
return
end
RunConsoleCommand( "sv_cheats", 1 )
RunConsoleCommand( "host_framerate", settings.fps * settings.frameblend )
RunConsoleCommand( "snd_fixed_rate", 1 )
RunConsoleCommand( "progress_enable", 1 )
RunConsoleCommand( "playdemo", demoname )
VideoSettings = table.Copy( settings )
Window:Remove()
end
end, nil, "", { FCVAR_DONTRECORD } )
local function FinishRecording()
VideoSettings = nil
ActiveVideo:Finish()
ActiveVideo = nil
RunConsoleCommand( "host_framerate", 0 )
RunConsoleCommand( "sv_cheats", 0 )
RunConsoleCommand( "snd_fixed_rate", 0 )
MsgN( "Rendering Finished - Took ", SysTime() - stats.starttime, " seconds" )
end
local function UpdateFrame()
if ( !engine.IsPlayingDemo() ) then
if ( !VideoSettings.started ) then return end
FinishRecording()
return
end
if ( !VideoSettings.started ) then
if ( gui.IsGameUIVisible() ) then return end
VideoSettings.started = true
VideoSettings.framecount = 0
stats.starttime = SysTime()
end
end
local function DrawOverlay()
if ( !VideoSettings ) then return end
local complete = engine.GetDemoPlaybackTick() / engine.GetDemoPlaybackTotalTicks()
local x = ScrW() * 0.1
local y = ScrH() * 0.8
local w = ScrW() * 0.8
local h = ScrH() * 0.05
surface.SetFont( "DermaDefault" )
surface.SetTextColor( 255, 255, 255, 255 )
-- Static text
local info = "Rendering " .. math.floor( VideoSettings.width ) .. "x" .. math.floor( VideoSettings.height ) .. " at " .. math.floor( VideoSettings.fps ) .. "fps "
local with = {}
if ( VideoSettings.dofsteps > 0 ) then table.insert( with, "DOF" ) end
if ( VideoSettings.frameblend > 1 ) then table.insert( with, "Frame Blending" ) end
if ( VideoSettings.viewsmooth > 0 ) then table.insert( with, "View Smoothing" ) end
if ( VideoSettings.possmooth > 0 ) then table.insert( with, "Position Smoothing" ) end
if ( #with > 0 ) then
local withS = table.concat( with, ", " )
info = info .. "with " .. withS
end
info = info .. " (rendering " .. ( VideoSettings.frameblend * math.max( VideoSettings.dofsteps, 1 ) * math.max( VideoSettings.dofpasses, 1 ) ) .. " frames per frame)"
local tw, th = surface.GetTextSize( info )
surface.SetTextPos( x, y - th - 10 )
surface.DrawText( info )
-- The box
surface.SetDrawColor( 0, 0, 0, 50 )
surface.DrawRect( x-3, y-3, w + 6, h + 6 )
surface.SetDrawColor( 255, 255, 255, 200 )
surface.DrawRect( x -2, y-2, w + 4, 2 )
surface.DrawRect( x - 2, y, 2, h )
surface.DrawRect( x + w, y, 2, h )
surface.DrawRect( x-2, y + h, w + 4, 2 )
surface.SetDrawColor( 255, 255, 100, 150 )
surface.DrawRect( x + 1, y + 1, w * complete - 2, h - 2 )
-- Demo length
local demolength = "Demo Length: " .. string.FormattedTime( engine.GetDemoPlaybackTotalTicks() * engine.TickInterval(), "%2i:%02i" )
local tw, th = surface.GetTextSize( demolength )
surface.SetTextPos( x + w - tw, y - th - 10 )
surface.DrawText( demolength )
if ( !VideoSettings.started ) then return end
-- Timers
surface.SetTextPos( x, y + h + 10 )
surface.DrawText( "Time Taken: " .. string.NiceTime( SysTime() - stats.starttime ) )
local tw, th = surface.GetTextSize( "Time Left: " .. string.NiceTime( stats.timeremaining ) )
surface.SetTextPos( x + w - tw, y + h + 10 )
surface.DrawText( "Time Left: " .. string.NiceTime( stats.timeremaining ) )
local demotime = string.FormattedTime( engine.GetDemoPlaybackTick() * engine.TickInterval(), "%2i:%02i" )
local tw, th = surface.GetTextSize( demotime )
if ( w * complete > tw + 20 ) then
surface.SetTextColor( 0, 0, 0, 200 )
surface.SetTextPos( x + w * complete - tw - 10, y + h * 0.5 - th * 0.5 )
surface.DrawText( demotime )
end
local demotime = string.FormattedTime( ( engine.GetDemoPlaybackTotalTicks() - engine.GetDemoPlaybackTick() ) * engine.TickInterval(), "%2i:%02i" )
local tw, th = surface.GetTextSize( demotime )
if ( w - w * complete > tw + 20 ) then
surface.SetTextColor( 255, 255, 255, 200 )
surface.SetTextPos( x + w * complete + 10, y + h * 0.5 - th * 0.5 )
surface.DrawText( demotime )
end
end
hook.Add( "CaptureVideo", "CaptureDemoFrames", function()
if ( !ActiveVideo ) then return end
if ( !VideoSettings ) then return end
UpdateFrame()
DrawOverlay()
if ( stats.reset < SysTime() ) then
stats.reset = SysTime() + 1
stats.last_encodetime = stats.encodetime
stats.encodetime = 0
local timetaken = SysTime() - stats.starttime
local fractioncomplete = engine.GetDemoPlaybackTotalTicks() / engine.GetDemoPlaybackTick()
stats.timeremaining = ( timetaken * fractioncomplete ) - timetaken
if ( stats.timeremaining < 0 ) then stats.timeremaining = 0 end
end
end )
function RecordDemoFrame()
if ( !ActiveVideo ) then return end
if ( !VideoSettings ) then return end
if ( !VideoSettings.started ) then return end
ActiveVideo:AddFrame( 1 / VideoSettings.fps, true )
VideoSettings.framecount = VideoSettings.framecount + 1
end

View File

@@ -0,0 +1,153 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
function Derma_OpenIconBrowser()
-- Because the icon browser will be part of the menu, activate the menu so that it definitely shows up otherwise a derma_icon_browser bind does nothing
gui.ActivateGameUI()
if ( IsValid( Derma_IconBrowser ) ) then
Derma_IconBrowser:SetVisible( true )
Derma_IconBrowser:MakePopup()
return
end
Derma_IconBrowser = vgui.Create( "DFrame" )
Derma_IconBrowser:SetTitle( "Derma Icon Browser" )
Derma_IconBrowser:SetIcon( "icon16/pictures.png" )
Derma_IconBrowser:SetScreenLock( true ) -- Don't let the icon browser out of the screen bounds, we don't want it to get lost
Derma_IconBrowser:SetSizable( true )
-- Minimum size the user can resize the icon browser to
local minFrameW, minFrameH = 250, 200
-- Remember the user's custom size for the icon browser
-- If any dimension of the custom size is bigger than the screen bounds, or smaller than the minimum bounds, forget about it
local frameW, frameH = cookie.GetNumber( "Derma_IconBrowser_W", 400 ), cookie.GetNumber( "Derma_IconBrowser_H", 400 )
if ( frameW > ScrW() || frameH > ScrH() ) then
frameW, frameH = 400, 400
cookie.Delete( "Derma_IconBrowser_W" )
cookie.Delete( "Derma_IconBrowser_H" )
end
Derma_IconBrowser.OnScreenSizeChanged = function( self )
-- Make sure if the screen resolution changes we keep the icon browser within its bounds
self:SetSize( math.min( self:GetWide(), ScrW() ), math.min( self:GetTall(), ScrH() ) )
-- Store changes
cookie.Set( "Derma_IconBrowser_W", self:GetWide() )
cookie.Set( "Derma_IconBrowser_H", self:GetTall() )
-- Prevent changes being stored twice
self.m_bStoreResize = false
end
Derma_IconBrowser.OnSizeChanged = function( self )
-- Don't store the custom size in the database yet, we don't want to spam it
if ( self.m_bStoreResize != false ) then
self.m_bStoreResize = true
else
-- The screen resolution just changed, we already stored that in the database
self.m_bStoreResize = nil
end
end
Derma_IconBrowser.OnMouseReleased = function( self )
if ( self.m_bStoreResize ) then
self.m_bStoreResize = nil
-- Now we can store it - the user has finished dragging
cookie.Set( "Derma_IconBrowser_W", self:GetWide() )
cookie.Set( "Derma_IconBrowser_H", self:GetTall() )
end
-- Call the function we had overridden
DFrame.OnMouseReleased( self )
end
-- Set the size of the icon browser and pop it up on the screen
Derma_IconBrowser:SetSize( frameW, frameH )
Derma_IconBrowser:SetMinimumSize( minFrameW, minFrameH )
Derma_IconBrowser:Center()
Derma_IconBrowser:MakePopup()
-- Some variables for our "copied" icon
local copyIconSize = 16
local copyIconSpacing = 5
local matCopyIcon = Material( "icon16/page_copy.png" )
Derma_IconBrowser.PaintOver = function( self )
-- Nice animation for feedback when copying an icon
if ( self.m_nCopiedTime && SysTime() <= self.m_nCopiedTime ) then
local wasEnabled = DisableClipping( true )
-- Animation lasts 1 second (the fade starts after .25 seconds)
local slideAnimFrac = 1 - math.TimeFraction( self.m_nCopiedTime - 1, self.m_nCopiedTime, SysTime() )
local fadeAnimFrac = 1 - math.max( math.TimeFraction( self.m_nCopiedTime - .75, self.m_nCopiedTime, SysTime() ), 0 )
-- Draw a small label underneath the mouse cursor that gradually slides down and fades away
surface.SetFont( "BudgetLabel" )
surface.SetTextColor( 255, 255, 255, fadeAnimFrac * 255 )
local mouseX, mouseY = self:ScreenToLocal( input.GetCursorPos() )
local textW, textH = surface.GetTextSize( self.m_strCopiedIcon )
local textX = mouseX - ( ( textW - copyIconSize - copyIconSpacing ) / 2 ) -- Draw it center-aligned at the cursor, subtract 16px for the page_copy icon and 5px for its spacing
local textY = mouseY + textH + ( ( 1 - slideAnimFrac ) * textH ) + 5 -- Animate it to slide down plus a further 5px for spacing
surface.SetTextPos( textX, textY )
surface.DrawText( self.m_strCopiedIcon )
-- Draw a small page_copy icon next to the label
local copyIconX = textX - copyIconSize - copyIconSpacing
local copyIconY = textY - ( ( math.max( textH, copyIconSize ) - math.min( textH, copyIconSize ) ) / 2 ) -- Center align the icon to the text (ambigious to whether the text or the icon is bigger)
surface.SetDrawColor( 255, 255, 255, fadeAnimFrac * 255 )
surface.SetMaterial( matCopyIcon )
surface.DrawTexturedRect( copyIconX, copyIconY, copyIconSize, copyIconSize )
DisableClipping( wasEnabled )
end
end
local IconBrowser = Derma_IconBrowser:Add( "DIconBrowser" )
IconBrowser:Dock( FILL )
IconBrowser.OnChange = function( self )
-- Label animation data
Derma_IconBrowser.m_nCopiedTime = SysTime() + 1
Derma_IconBrowser.m_strCopiedIcon = self:GetSelectedIcon()
-- Set the clipboard text to the icon path
SetClipboardText( Derma_IconBrowser.m_strCopiedIcon )
-- Play a nice sound
surface.PlaySound( "garrysmod/content_downloaded.wav" )
end
-- Create our search box
local Search = Derma_IconBrowser:Add( "DTextEntry" )
Search:MoveToBefore( IconBrowser ) -- We need it to be above the icon browser itself
Search:Dock( TOP )
Search:DockMargin( 0, 0, 0, 5 )
Search:SetPlaceholderText( "#spawnmenu.search" )
Search:SetTall( 24 )
Search:SetUpdateOnType( true )
Search.OnValueChange = function( self )
local str = Search:GetValue():Trim():gsub( "^icon16/(.+)", "%1" )
IconBrowser:FilterByText( str ) -- If the user typed icon16/ at the start, get rid of it for them and trim any whitespace
end
-- Add a little magnifying glass icon in the right corner of the search box
local SearchIcon = Search:Add( "DImage" )
SearchIcon:SetImage( "icon16/magnifier.png" )
SearchIcon:Dock( RIGHT )
SearchIcon:DockMargin( 4, 4, 4, 4 )
SearchIcon:SetSize( 16, 16 )
-- Keep this line at the bottom otherwise an error could zombify the icon browser
Derma_IconBrowser:SetDeleteOnClose( false )
end
concommand.Add( "derma_icon_browser", Derma_OpenIconBrowser, nil, "Opens the Derma Icon Browser", FCVAR_DONTRECORD )

126
lua/menu/errors.lua Normal file
View File

@@ -0,0 +1,126 @@
--[[
| 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/
--]]
--
-- Here we get a callback from the game/client code on Lua errors, and display a nice notification.
--
-- This should help `newbs` find out which addons are crashing.
--
local Errors = {}
hook.Add( "OnLuaError", "MenuErrorHandler", function( str, realm, stack, addontitle, addonid )
-- This error is caused by a specific workshop addon
--[[if ( isstring( addonid ) ) then
-- Down Vote
steamworks.Vote( addonid, false )
-- Disable Naughty Addon
timer.Simple( 5, function()
MsgN( "Disabling addon '", addontitle, "' due to lua errors" )
steamworks.SetShouldMountAddon( addonid, false )
steamworks.ApplyAddons()
end )
end]]
if ( addonid == nil ) then addonid = 0 end
if ( Errors[ addonid ] ) then
Errors[ addonid ].times = Errors[ addonid ].times + 1
Errors[ addonid ].last = SysTime()
return
end
local text = language.GetPhrase( "errors.something_p" )
-- We know the name, display it to the user
if ( isstring( addontitle ) ) then
text = string.format( language.GetPhrase( "errors.addon_p" ), addontitle )
end
local error = {
first = SysTime(),
last = SysTime(),
times = 1,
title = addontitle,
x = 32,
text = text
}
Errors[ addonid ] = error
end )
hook.Add( "OnPauseMenuBlockedTooManyTimes", "TellAboutShiftEsc", function()
Errors[ "internal_shift+esc" ] = {
first = SysTime(),
last = SysTime(),
times = 1,
title = "",
x = 32,
text = "You can force open the main menu by holding Shift when pressing the Escape key."
}
end )
local matAlert = Material( "icon16/error.png" )
local cl_drawhud = GetConVar( "cl_drawhud" )
hook.Add( "DrawOverlay", "MenuDrawLuaErrors", function()
if ( table.IsEmpty( Errors ) ) then return end
if ( !cl_drawhud:GetBool() ) then return end
local idealy = 32
local height = 30
local EndTime = SysTime() - 10
local Recent = SysTime() - 0.5
for k, v in SortedPairsByMemberValue( Errors, "last" ) do
surface.SetFont( "DermaDefaultBold" )
if ( v.y == nil ) then v.y = idealy end
if ( v.w == nil ) then v.w = surface.GetTextSize( v.text ) + 48 end
draw.RoundedBox( 2, v.x + 2, v.y + 2, v.w, height, Color( 40, 40, 40, 255 ) )
draw.RoundedBox( 2, v.x, v.y, v.w, height, Color( 240, 240, 240, 255 ) )
if ( v.last > Recent ) then
draw.RoundedBox( 2, v.x, v.y, v.w, height, Color( 255, 200, 0, ( v.last - Recent ) * 510 ) )
end
surface.SetTextColor( 90, 90, 90, 255 )
surface.SetTextPos( v.x + 34, v.y + 8 )
surface.DrawText( v.text )
surface.SetDrawColor( 255, 255, 255, 150 + math.sin( v.y + SysTime() * 30 ) * 100 )
surface.SetMaterial( matAlert )
surface.DrawTexturedRect( v.x + 6, v.y + 6, 16, 16 )
v.y = idealy
idealy = idealy + 40
if ( v.last < EndTime ) then
Errors[ k ] = nil
end
end
end )

453
lua/menu/getmaps.lua Normal file
View File

@@ -0,0 +1,453 @@
--[[
| 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 MapPatterns = {}
local MapNames = {}
local AddonMaps = {}
local function UpdateMaps()
MapPatterns = {}
MapNames = {}
MapNames[ "aoc_" ] = "Age of Chivalry"
MapNames[ "infra_" ] = "INFRA"
MapPatterns[ "^asi-" ] = "Alien Swarm"
MapNames[ "lobby" ] = "Alien Swarm"
MapNames[ "cp_docks" ] = "Blade Symphony"
MapNames[ "cp_parkour" ] = "Blade Symphony"
MapNames[ "cp_sequence" ] = "Blade Symphony"
MapNames[ "cp_terrace" ] = "Blade Symphony"
MapNames[ "cp_test" ] = "Blade Symphony"
MapNames[ "duel_" ] = "Blade Symphony"
MapNames[ "ffa_community" ] = "Blade Symphony"
MapNames[ "free_" ] = "Blade Symphony"
MapNames[ "practice_box" ] = "Blade Symphony"
MapNames[ "tut_training" ] = "Blade Symphony"
MapNames[ "lightstyle_test" ] = "Blade Symphony"
MapNames[ "ar_" ] = "Counter-Strike"
MapNames[ "cs_" ] = "Counter-Strike"
MapNames[ "de_" ] = "Counter-Strike"
MapNames[ "es_" ] = "Counter-Strike"
MapNames[ "fy_" ] = "Counter-Strike"
MapNames[ "gd_" ] = "Counter-Strike"
MapNames[ "dz_" ] = "Counter-Strike"
MapNames[ "training1" ] = "Counter-Strike"
MapNames[ "lobby_mapveto" ] = "Counter-Strike"
-- Various custom cs maps
MapNames[ "35hp_" ] = "Counter-Strike (Custom)"
MapNames[ "aim_" ] = "Counter-Strike (Custom)"
MapNames[ "awp_" ] = "Counter-Strike (Custom)"
MapNames[ "am_" ] = "Counter-Strike (Custom)"
MapNames[ "fy_" ] = "Counter-Strike (Custom)"
MapNames[ "1v1_" ] = "Counter-Strike (Custom)"
MapNames[ "dod_" ] = "Day Of Defeat"
MapNames[ "ddd_" ] = "Dino D-Day"
MapNames[ "de_dam" ] = "DIPRIP"
MapNames[ "dm_city" ] = "DIPRIP"
MapNames[ "dm_refinery" ] = "DIPRIP"
MapNames[ "dm_supermarket" ] = "DIPRIP"
MapNames[ "dm_village" ] = "DIPRIP"
MapNames[ "ur_city" ] = "DIPRIP"
MapNames[ "ur_refinery" ] = "DIPRIP"
MapNames[ "ur_supermarket" ] = "DIPRIP"
MapNames[ "ur_village" ] = "DIPRIP"
MapNames[ "dys_" ] = "Dystopia"
MapNames[ "pb_dojo" ] = "Dystopia"
MapNames[ "pb_rooftop" ] = "Dystopia"
MapNames[ "pb_round" ] = "Dystopia"
MapNames[ "pb_urbandome" ] = "Dystopia"
MapNames[ "sav_dojo6" ] = "Dystopia"
MapNames[ "varena" ] = "Dystopia"
-- Do these manually, so edits of these maps don't end up in the same category.
local HL2Maps = {
"d1_trainstation_01", "d1_trainstation_02", "d1_trainstation_03", "d1_trainstation_04", "d1_trainstation_05", "d1_trainstation_06",
"d1_canals_01", "d1_canals_01a", "d1_canals_02", "d1_canals_03", "d1_canals_05", "d1_canals_06", "d1_canals_07", "d1_canals_08", "d1_canals_09",
"d1_canals_10", "d1_canals_11","d1_canals_12", "d1_canals_13", "d1_eli_01", "d1_eli_02",
"d1_town_01", "d1_town_01a", "d1_town_02", "d1_town_02a", "d1_town_03", "d1_town_04","d1_town_05",
"d2_coast_01", "d2_coast_03", "d2_coast_04", "d2_coast_05","d2_coast_07", "d2_coast_08", "d2_coast_09", "d2_coast_10", "d2_coast_11", "d2_coast_12",
"d2_prison_01", "d2_prison_02", "d2_prison_03", "d2_prison_04", "d2_prison_05", "d2_prison_06", "d2_prison_07", "d2_prison_08",
"d3_c17_01", "d3_c17_02", "d3_c17_03", "d3_c17_04", "d3_c17_05", "d3_c17_06a", "d3_c17_06b", "d3_c17_07", "d3_c17_08",
"d3_c17_09", "d3_c17_10a", "d3_c17_10b", "d3_c17_11", "d3_c17_12", "d3_c17_12b", "d3_c17_13",
"d3_citadel_01", "d3_citadel_02", "d3_citadel_03", "d3_citadel_04", "d3_citadel_05", "d3_breen_01"
}
for _, map in ipairs( HL2Maps ) do MapNames[ map ] = "Half-Life 2" end
local EP1Maps = {
"ep1_citadel_00", "ep1_citadel_01", "ep1_citadel_02", "ep1_citadel_02b", "ep1_citadel_03", "ep1_citadel_04", "ep1_c17_00",
"ep1_c17_00a", "ep1_c17_01", "ep1_c17_01a", "ep1_c17_02", "ep1_c17_02b", "ep1_c17_02a", "ep1_c17_05", "ep1_c17_06"
}
for _, map in ipairs( EP1Maps ) do MapNames[ map ] = "Half-Life 2: Episode 1" end
local EP2Maps = {
"ep2_outland_01", "ep2_outland_01a", "ep2_outland_02", "ep2_outland_03", "ep2_outland_04", "ep2_outland_05", "ep2_outland_06", "ep2_outland_06a", "ep2_outland_07",
"ep2_outland_08", "ep2_outland_09", "ep2_outland_10", "ep2_outland_10a", "ep2_outland_11", "ep2_outland_11a", "ep2_outland_11b", "ep2_outland_12", "ep2_outland_12a"
}
for _, map in ipairs( EP2Maps ) do MapNames[ map ] = "Half-Life 2: Episode 2" end
MapNames[ "dm_" ] = "Half-Life 2: Deathmatch"
MapNames[ "halls3" ] = "Half-Life 2: Deathmatch"
MapNames[ "d2_lostcoast" ] = "Half-Life 2: Lost Coast"
MapPatterns[ "^c[%d]a" ] = "Half-Life"
MapPatterns[ "^t0a" ] = "Half-Life"
MapNames[ "boot_camp" ] = "Half-Life Deathmatch"
MapNames[ "bounce" ] = "Half-Life Deathmatch"
MapNames[ "crossfire" ] = "Half-Life Deathmatch"
MapNames[ "datacore" ] = "Half-Life Deathmatch"
MapNames[ "frenzy" ] = "Half-Life Deathmatch"
MapNames[ "lambda_bunker" ] = "Half-Life Deathmatch"
MapNames[ "rapidcore" ] = "Half-Life Deathmatch"
MapNames[ "snarkpit" ] = "Half-Life Deathmatch"
MapNames[ "stalkyard" ] = "Half-Life Deathmatch"
MapNames[ "subtransit" ] = "Half-Life Deathmatch"
MapNames[ "undertow" ] = "Half-Life Deathmatch"
MapNames[ "ins_" ] = "Insurgency"
MapNames[ "l4d_" ] = "Left 4 Dead"
MapPatterns[ "^c[%d]m" ] = "Left 4 Dead 2"
MapPatterns[ "^c1[%d]m" ] = "Left 4 Dead 2"
MapNames[ "curling_stadium" ] = "Left 4 Dead 2"
MapNames[ "tutorial_standards" ] = "Left 4 Dead 2"
MapNames[ "tutorial_standards_vs" ] = "Left 4 Dead 2"
MapNames[ "clocktower" ] = "Nuclear Dawn"
MapNames[ "coast" ] = "Nuclear Dawn"
MapNames[ "downtown" ] = "Nuclear Dawn"
MapNames[ "gate" ] = "Nuclear Dawn"
MapNames[ "hydro" ] = "Nuclear Dawn"
MapNames[ "metro" ] = "Nuclear Dawn"
MapNames[ "metro_training" ] = "Nuclear Dawn"
MapNames[ "oasis" ] = "Nuclear Dawn"
MapNames[ "oilfield" ] = "Nuclear Dawn"
MapNames[ "silo" ] = "Nuclear Dawn"
MapNames[ "sk_metro" ] = "Nuclear Dawn"
MapNames[ "training" ] = "Nuclear Dawn"
MapNames[ "bt_" ] = "Pirates, Vikings, & Knights II"
MapNames[ "lts_" ] = "Pirates, Vikings, & Knights II"
MapNames[ "te_" ] = "Pirates, Vikings, & Knights II"
MapNames[ "tw_" ] = "Pirates, Vikings, & Knights II"
MapNames[ "escape_" ] = "Portal"
MapNames[ "testchmb_" ] = "Portal"
MapNames[ "e1912" ] = "Portal 2"
MapPatterns[ "^mp_coop_" ] = "Portal 2"
MapPatterns[ "^sp_a" ] = "Portal 2"
MapNames[ "achievement_" ] = "Team Fortress 2"
MapNames[ "arena_" ] = "Team Fortress 2"
MapNames[ "cp_" ] = "Team Fortress 2"
MapNames[ "ctf_" ] = "Team Fortress 2"
MapNames[ "itemtest" ] = "Team Fortress 2"
MapNames[ "koth_" ] = "Team Fortress 2"
MapNames[ "mvm_" ] = "Team Fortress 2"
MapNames[ "pl_" ] = "Team Fortress 2"
MapNames[ "plr_" ] = "Team Fortress 2"
MapNames[ "rd_" ] = "Team Fortress 2"
MapNames[ "pd_" ] = "Team Fortress 2"
MapNames[ "sd_" ] = "Team Fortress 2"
MapNames[ "tc_" ] = "Team Fortress 2"
MapNames[ "tr_" ] = "Team Fortress 2"
MapNames[ "trade_" ] = "Team Fortress 2"
MapNames[ "pass_" ] = "Team Fortress 2"
MapNames[ "vsh_" ] = "Team Fortress 2"
MapNames[ "zi_" ] = "Team Fortress 2"
MapNames[ "zpa_" ] = "Zombie Panic! Source"
MapNames[ "zpl_" ] = "Zombie Panic! Source"
MapNames[ "zpo_" ] = "Zombie Panic! Source"
MapNames[ "zps_" ] = "Zombie Panic! Source"
MapNames[ "zph_" ] = "Zombie Panic! Source"
MapNames[ "fof_" ] = "Fistful of Frags"
MapNames[ "fofhr_" ] = "Fistful of Frags"
MapNames[ "cm_" ] = "Fistful of Frags"
MapNames[ "gt_" ] = "Fistful of Frags"
MapNames[ "tp_" ] = "Fistful of Frags"
MapNames[ "vs_" ] = "Fistful of Frags"
MapNames[ "bhop_" ] = "Bunny Hop"
MapNames[ "cinema_" ] = "Cinema"
MapNames[ "theater_" ] = "Cinema"
MapNames[ "xc_" ] = "Climb"
MapNames[ "deathrun_" ] = "Deathrun"
MapNames[ "dr_" ] = "Deathrun"
MapNames[ "fm_" ] = "Flood"
MapNames[ "gmt_" ] = "GMod Tower"
MapNames[ "gg_" ] = "Gun Game"
MapNames[ "scoutzknivez" ] = "Gun Game"
MapNames[ "ba_" ] = "Jailbreak"
MapNames[ "jail_" ] = "Jailbreak"
MapNames[ "jb_" ] = "Jailbreak"
MapNames[ "mg_" ] = "Minigames"
MapNames[ "pw_" ] = "Pirate Ship Wars"
MapNames[ "ph_" ] = "Prop Hunt"
MapNames[ "rp_" ] = "Roleplay"
MapNames[ "slb_" ] = "Sled Build"
MapNames[ "sb_" ] = "Spacebuild"
MapNames[ "slender_" ] = "Stop it Slender"
MapNames[ "gms_" ] = "Stranded"
MapNames[ "surf_" ] = "Surf"
MapNames[ "ts_" ] = "The Stalker"
MapNames[ "zm_" ] = "Zombie Survival"
MapNames[ "zombiesurvival_" ] = "Zombie Survival"
MapNames[ "zs_" ] = "Zombie Survival"
MapNames[ "coop_" ] = "Cooperative"
local GamemodeList = engine.GetGamemodes()
for k, gm in ipairs( GamemodeList ) do
local Name = gm.title or "Unnammed Gamemode"
local Maps = string.Split( gm.maps, "|" )
if ( Maps and gm.maps != "" ) then
for _, pattern in ipairs( Maps ) do
-- When in doubt, just try to match it with string.find
MapPatterns[ string.lower( pattern ) ] = Name
end
end
end
AddonMaps = {}
for k, addon in ipairs( engine.GetAddons() ) do
local name = addon.title or "Unnammed Addon"
local files = file.Find( "maps/*.bsp", name )
if ( #files > 0 ) then AddonMaps[ name ] = files end
end
end
local favmaps
local function LoadFavourites()
local cookiestr = cookie.GetString( "favmaps" )
favmaps = favmaps or ( cookiestr and string.Explode( ";", cookiestr ) or {} )
end
function UpdateAddonMapList()
local json = util.TableToJSON( AddonMaps )
if ( !json ) then return end
pnlMainMenu:Call( "UpdateAddonMaps(" .. json .. ")" )
end
-- Called from JS when starting a new game
function UpdateMapList()
UpdateAddonMapList()
local mapList = GetMapList()
if ( !mapList ) then return end
local json = util.TableToJSON( mapList )
if ( !json ) then return end
pnlMainMenu:Call( "UpdateMaps(" .. json .. ")" )
end
local IgnorePatterns = {
"^background",
"^devtest",
"^ep1_background",
"^ep2_background",
"^styleguide",
}
local IgnoreMaps = {
-- Prefixes
[ "sdk_" ] = true,
[ "test_" ] = true,
[ "vst_" ] = true,
-- Maps
[ "c4a1y" ] = true,
[ "credits" ] = true,
[ "d2_coast_02" ] = true,
[ "d3_c17_02_camera" ] = true,
[ "ep1_citadel_00_demo" ] = true,
[ "c5m1_waterfront_sndscape" ] = true,
[ "intro" ] = true,
[ "test" ] = true
}
local MapList = {}
local function RefreshMaps( skip )
if ( !skip ) then UpdateMaps() end
MapList = {}
local maps = file.Find( "maps/*.bsp", "GAME" )
LoadFavourites()
for k, v in ipairs( maps ) do
local name = string.lower( string.gsub( v, "%.bsp$", "" ) )
local prefix = string.match( name, "^(.-_)" )
local Ignore = IgnoreMaps[ name ] or IgnoreMaps[ prefix ]
-- Don't loop if it's already ignored
if ( Ignore ) then continue end
for _, ignore in ipairs( IgnorePatterns ) do
if ( string.find( name, ignore ) ) then
Ignore = true
break
end
end
-- Don't add useless maps
if ( Ignore ) then continue end
-- Check if the map has a simple name or prefix
local Category = MapNames[ name ] or MapNames[ prefix ]
-- Check if the map has an embedded prefix, or is TTT/Sandbox
if ( !Category ) then
for pattern, category in pairs( MapPatterns ) do
if ( string.find( name, pattern ) ) then
Category = category
end
end
end
-- Throw all uncategorised maps into Other
Category = Category or "Other"
local fav
if ( table.HasValue( favmaps, name ) ) then
fav = true
end
local csgo = false
if ( Category == "Counter-Strike" and file.Exists( "maps/" .. name .. ".bsp", "csgo" ) ) then
if ( file.Exists( "maps/" .. name .. ".bsp", "cstrike" ) ) then -- Map also exists in CS:GO
csgo = true
else
Category = "Counter-Strike: GO"
end
end
if ( !MapList[ Category ] ) then
MapList[ Category ] = {}
end
table.insert( MapList[ Category ], name )
if ( fav ) then
if ( !MapList[ "Favourites" ] ) then
MapList[ "Favourites" ] = {}
end
table.insert( MapList[ "Favourites" ], name )
end
if ( csgo ) then
if ( !MapList[ "Counter-Strike: GO" ] ) then
MapList[ "Counter-Strike: GO" ] = {}
end
-- HACK: We have to make the CS:GO name different from the CS:S name to prevent Favourites conflicts
table.insert( MapList[ "Counter-Strike: GO" ], name .. " " )
end
end
-- Send the new list to the HTML menu
UpdateMapList()
end
-- Update only after a short while for when these hooks are called very rapidly back to back
local function DelayedRefreshMaps()
timer.Create( "menu_refreshmaps", 0.1, 1, RefreshMaps )
end
hook.Add( "MenuStart", "FindMaps", DelayedRefreshMaps )
hook.Add( "GameContentChanged", "RefreshMaps", DelayedRefreshMaps )
-- Nice maplist accessor instead of a global table
function GetMapList()
return MapList
end
function ToggleFavourite( map )
LoadFavourites()
if ( table.HasValue( favmaps, map ) ) then -- is favourite, remove it
table.remove( favmaps, table.KeysFromValue( favmaps, map )[1] )
else -- not favourite, add it
table.insert( favmaps, map )
end
cookie.Set( "favmaps", table.concat( favmaps, ";" ) )
RefreshMaps( true )
UpdateMapList()
end
function SaveLastMap( map, cat )
local t = string.Explode( ";", cookie.GetString( "lastmap", "" ) )
if ( !map ) then map = t[ 1 ] or "gm_flatgrass" end
if ( !cat ) then cat = t[ 2 ] or "Sandbox" end
cookie.Set( "lastmap", map .. ";" .. cat )
end
function LoadLastMap()
local t = string.Explode( ";", cookie.GetString( "lastmap", "" ) )
local map = t[ 1 ] or "gm_flatgrass"
local cat = t[ 2 ] or "Sandbox"
cat = string.gsub( cat, "'", "\\'" )
if ( !file.Exists( "maps/" .. map .. ".bsp", "GAME" ) ) then return end
pnlMainMenu:Call( "SetLastMap('" .. map:JavascriptSafe() .. "','" .. cat:JavascriptSafe() .. "')" )
end

287
lua/menu/loading.lua Normal file
View File

@@ -0,0 +1,287 @@
--[[
| 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/
--]]
g_ServerName = ""
g_MapName = ""
g_ServerURL = ""
g_MaxPlayers = ""
g_SteamID = ""
local PANEL = {}
function PANEL:Init()
self:SetSize( ScrW(), ScrH() )
end
function PANEL:ShowURL( url, force )
if ( string.len( url ) < 5 ) then
return
end
if ( IsValid( self.HTML ) ) then
if ( !force ) then return end
self.HTML:Remove()
end
self:SetSize( ScrW(), ScrH() )
self.HTML = vgui.Create( "DHTML", self )
self.HTML:SetSize( ScrW(), ScrH() )
self.HTML:Dock( FILL )
self.HTML:OpenURL( url )
self:InvalidateLayout()
self:SetMouseInputEnabled( false )
self.LoadedURL = url
end
function PANEL:PerformLayout()
self:SetSize( ScrW(), ScrH() )
end
function PANEL:Paint()
surface.SetDrawColor( 30, 30, 30, 255 )
surface.DrawRect( 0, 0, self:GetWide(), self:GetTall() )
if ( self.JavascriptRun && IsValid( self.HTML ) && !self.HTML:IsLoading() ) then
self:RunJavascript( self.JavascriptRun )
self.JavascriptRun = nil
end
end
function PANEL:RunJavascript( str )
if ( !IsValid( self.HTML ) ) then return end
if ( self.HTML:IsLoading() ) then return end
self.HTML:RunJavascript( str )
end
function PANEL:OnActivate()
g_ServerName = ""
g_MapName = ""
g_ServerURL = ""
g_MaxPlayers = ""
g_SteamID = ""
self:ShowURL( GetDefaultLoadingHTML() )
self.NumDownloadables = 0
end
function PANEL:OnDeactivate()
if ( IsValid( self.HTML ) ) then self.HTML:Remove() end
self.LoadedURL = nil
self.NumDownloadables = 0
-- Notify the user that the game is ready.
-- TODO: A convar for this?
system.FlashWindow()
end
function PANEL:OnScreenSizeChanged( oldW, oldH, newW, newH )
self:InvalidateLayout( true )
end
function PANEL:Think()
self:CheckForStatusChanges()
self:CheckDownloadTables()
end
function PANEL:StatusChanged( strStatus )
-- new FastDL/ServerDL format
local matchedFileName = string.match( strStatus, "%w+/%w+ [-] (.+) is downloading" )
if ( matchedFileName ) then
self:RunJavascript( "if ( window.DownloadingFile ) DownloadingFile( '" .. matchedFileName:JavascriptSafe() .. "' )" )
return
end
-- WorkshopDL and old FastDL
local startPos, _ = string.find( strStatus, "Downloading " )
if ( startPos ) then
-- Snip everything before the Download part
strStatus = string.sub( strStatus, startPos )
-- Special case needed for workshop, snip the "' via Workshop" part
if ( string.EndsWith( strStatus, "via Workshop" ) ) then
strStatus = string.gsub( strStatus, "' via Workshop", "" )
strStatus = string.gsub( strStatus, "Downloading '", "" ) -- We need to handle the quote marks
end
local fileName = string.gsub( strStatus, "Downloading ", "" )
self:RunJavascript( "if ( window.DownloadingFile ) DownloadingFile( '" .. fileName:JavascriptSafe() .. "' )" )
return
end
self:RunJavascript( "if ( window.SetStatusChanged ) SetStatusChanged( '" .. strStatus:JavascriptSafe() .. "' )" )
end
--[[---------------------------------------------------------
-----------------------------------------------------------]]
function PANEL:CheckForStatusChanges()
local str = GetLoadStatus()
if ( !str ) then return end
str = string.Trim( str )
str = string.Trim( str, "\n" )
str = string.Trim( str, "\t" )
str = string.gsub( str, ".bz2", "" )
str = string.gsub( str, ".ztmp", "" )
str = string.gsub( str, "\\", "/" )
if ( self.OldStatus && self.OldStatus == str ) then return end
self.OldStatus = str
self:StatusChanged( str )
end
function PANEL:RefreshDownloadables()
self.Downloadables = GetDownloadables()
if ( !self.Downloadables ) then return end
local iDownloading = 0
local iFileCount = 0
for k, v in pairs( self.Downloadables ) do
v = string.gsub( v, ".bz2", "" )
v = string.gsub( v, ".ztmp", "" )
v = string.gsub( v, "\\", "/" )
iDownloading = iDownloading + self:FileNeedsDownload( v )
iFileCount = iFileCount + 1
end
if ( iDownloading == 0 ) then return end
self:RunJavascript( "if ( window.SetFilesNeeded ) SetFilesNeeded( " .. iDownloading .. ")" )
self:RunJavascript( "if ( window.SetFilesTotal ) SetFilesTotal( " .. iFileCount .. ")" )
end
function PANEL:FileNeedsDownload( filename )
local bExists = file.Exists( filename, "GAME" )
if ( bExists ) then return 0 end
return 1
end
function PANEL:CheckDownloadTables()
local NumDownloadables = NumDownloadables()
if ( !NumDownloadables ) then return end
if ( self.NumDownloadables && NumDownloadables == self.NumDownloadables ) then return end
self.NumDownloadables = NumDownloadables
self:RefreshDownloadables()
end
local PanelType_Loading = vgui.RegisterTable( PANEL, "EditablePanel" )
local pnlLoading = nil
function GetLoadPanel()
if ( !IsValid( pnlLoading ) ) then
pnlLoading = vgui.CreateFromTable( PanelType_Loading )
end
return pnlLoading
end
function IsInLoading()
if ( !IsValid( pnlLoading ) || !IsValid( pnlLoading.HTML ) ) then
return false
end
return true
end
function GameDetails( servername, serverurl, mapname, maxplayers, steamid, gamemode )
if ( engine.IsPlayingDemo() ) then return end
g_ServerName = servername
g_MapName = mapname
g_ServerURL = serverurl
g_MaxPlayers = maxplayers
g_SteamID = steamid
g_GameMode = gamemode
MsgN( servername )
MsgN( serverurl )
MsgN( gamemode )
MsgN( mapname )
MsgN( maxplayers )
MsgN( steamid )
serverurl = serverurl:Replace( "%s", steamid )
serverurl = serverurl:Replace( "%m", mapname )
if ( maxplayers > 1 && GetConVar( "cl_enable_loadingurl" ):GetBool() && ( serverurl:StartsWith( "http" ) || serverurl:StartsWith( "asset://" ) ) ) then
pnlLoading:ShowURL( serverurl, true )
end
-- TODO: This should be pulled from the server
local niceGamemode = g_GameMode
for k, v in pairs( engine.GetGamemodes() ) do
if ( niceGamemode == v.name ) then
niceGamemode = v.title
break
end
end
pnlLoading.JavascriptRun = string.format( [[if ( window.GameDetails ) GameDetails( "%s", "%s", "%s", %i, "%s", "%s", %.2f, "%s", "%s" );]],
servername:JavascriptSafe(), serverurl:JavascriptSafe(), mapname:JavascriptSafe(), maxplayers, steamid:JavascriptSafe(), g_GameMode:JavascriptSafe(),
GetConVarNumber( "snd_musicvolume" ), GetConVarString( "gmod_language" ), niceGamemode:JavascriptSafe() )
end

684
lua/menu/mainmenu.lua Normal file
View File

@@ -0,0 +1,684 @@
--[[
| 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( "background.lua" )
include( "cef_credits.lua" )
include( "crosshair_setup.lua" )
include( "openurl.lua" )
include( "ugcpublish.lua" )
pnlMainMenu = nil
local pnlMainMenuFallback = nil
local PANEL = {}
function PANEL:Init()
self:Dock( FILL )
self:SetKeyboardInputEnabled( true )
self:SetMouseInputEnabled( true )
self.HTML = vgui.Create( "DHTML", self )
JS_Language( self.HTML )
JS_Utility( self.HTML )
JS_Workshop( self.HTML )
-- Detect whenther the HTML engine is even there
self.menuLoaded = false
self.HTML.OnBeginLoadingDocument = function()
self.menuLoaded = true
if ( IsValid( pnlMainMenuFallback ) ) then pnlMainMenuFallback:Remove() end
end
self.HTML:Dock( FILL )
self.HTML:OpenURL( "asset://garrysmod/html/menu.html" )
self.HTML:SetKeyboardInputEnabled( true )
self.HTML:SetMouseInputEnabled( true )
self.HTML:SetAllowLua( true )
self.HTML:RequestFocus()
ws_dupe.HTML = self.HTML
ws_save.HTML = self.HTML
addon.HTML = self.HTML
demo.HTML = self.HTML
self:MakePopup()
self:SetPopupStayAtBack( true )
-- If the console is already open, we've got in its way.
if ( gui.IsConsoleVisible() ) then
gui.ShowConsole()
end
end
function PANEL:ScreenshotScan( folder )
local bReturn = false
local Screenshots = file.Find( folder .. "*.*", "GAME" )
for k, v in RandomPairs( Screenshots ) do
AddBackgroundImage( folder .. v )
bReturn = true
end
return bReturn
end
function PANEL:Paint()
DrawBackground()
if ( self.IsInGame != IsInGame() ) then
self.IsInGame = IsInGame()
if ( self.IsInGame ) then
if ( IsValid( self.InnerPanel ) ) then self.InnerPanel:Remove() end
self:Call( "SetInGame( true )" )
else
self:Call( "SetInGame( false )" )
end
end
if ( !self.IsInGame ) then return end
local canAdd = CanAddServerToFavorites()
local isFav = serverlist.IsCurrentServerFavorite()
if ( self.CanAddServerToFavorites != canAdd or self.IsCurrentServerFavorite != isFav ) then
self.CanAddServerToFavorites = canAdd
self.IsCurrentServerFavorite = isFav
self:Call( "SetShowFavButton( " .. tostring( self.CanAddServerToFavorites ) .. ", " .. tostring( self.IsCurrentServerFavorite ) .. " )" )
end
end
function PANEL:RefreshContent()
self:RefreshGamemodes()
self:RefreshAddons()
end
function PANEL:RefreshGamemodes()
local json = util.TableToJSON( engine.GetGamemodes() )
self:Call( "UpdateGamemodes( " .. json .. " )" )
self:UpdateBackgroundImages()
self:Call( "UpdateCurrentGamemode( '" .. engine.ActiveGamemode():JavascriptSafe() .. "' )" )
end
function PANEL:RefreshAddons()
-- TODO
end
function PANEL:SetProblemCount( problems, severity )
self:Call( "SetProblemCount(" .. problems .. ", " .. severity .. ")" )
end
function PANEL:UpdateBackgroundImages()
ClearBackgroundImages()
--
-- If there's screenshots in gamemodes/<gamemode>/backgrounds/*.jpg use them
--
if ( !self:ScreenshotScan( "gamemodes/" .. engine.ActiveGamemode() .. "/backgrounds/" ) ) then
--
-- If there's no gamemode specific here we'll use the default backgrounds
--
self:ScreenshotScan( "backgrounds/" )
end
ChangeBackground( engine.ActiveGamemode() )
end
function PANEL:Call( js )
self.HTML:QueueJavascript( js )
end
vgui.Register( "MainMenuPanel", PANEL, "EditablePanel" )
local PANEL = {}
function PANEL:SetText( txt )
self.Text = txt
end
function PANEL:Paint( w, h )
-- Draw the text
local parsed = markup.Parse( self.Text, self:GetParent():GetWide() )
parsed:Draw( 0, 0 )
-- Size to contents. Ew.
self:SetSize( parsed:GetWidth(), parsed:GetHeight() )
end
-- TODO: Maybe this panel belongs in client realm as well?
local markupPanel = vgui.RegisterTable( PANEL, "Panel" )
function OnMenuFailedToLoad()
local frame = vgui.Create( "DFrame" )
frame:SetSize( ScrW() / 2, ScrH() / 2 )
frame:Center()
frame:SetDraggable( false )
frame:ShowCloseButton( false )
frame:SetTitle( "Menu failed to load" )
frame:MakePopup()
pnlMainMenuFallback = frame
local lbl = vgui.CreateFromTable( markupPanel, frame )
lbl:Dock( TOP )
lbl:DockMargin( 0, 0, 0, 5 )
lbl:SetText( "Looks like the main menu failed to load.\n\nThis could be due to missing game files (run verification of game file integrity through Steam), or the HTML engine failed to load.\n\nBelow are some simple options to exit the game." )
local btn_srv = frame:Add( "DButton" )
btn_srv:Dock( TOP )
btn_srv:DockMargin( 0, 0, 0, 5 )
btn_srv:SetText( "Open legacy server browser" )
btn_srv:SetConsoleCommand( "gamemenucommand", "OpenServerBrowser" )
local btn_opt = frame:Add( "DButton" )
btn_opt:Dock( TOP )
btn_opt:DockMargin( 0, 0, 0, 5 )
btn_opt:SetText( "Open Settings" )
btn_opt:SetConsoleCommand( "gamemenucommand", "OpenOptionsDialog" )
local btn_exit = frame:Add( "DButton" )
btn_exit:Dock( TOP )
btn_exit:DockMargin( 0, 0, 0, 5 )
btn_exit:SetText( "Exit the game" )
btn_exit.DoClick = function() RunGameUICommand( "quit" ) end
end
--
-- Called from JS when starting a new game
--
function UpdateServerSettings()
local array = {
hostname = GetConVarString( "hostname" ),
sv_lan = GetConVarString( "sv_lan" ),
p2p_enabled = GetConVarString( "p2p_enabled" )
}
local settings_file = file.Read( "gamemodes/" .. engine.ActiveGamemode() .. "/" .. engine.ActiveGamemode() .. ".txt", true )
if ( settings_file ) then
local Settings = util.KeyValuesToTable( settings_file )
if ( istable( Settings.settings ) ) then
array.settings = {}
for k, v in pairs( Settings.settings ) do
local cvar = GetConVar( v.name )
if ( !cvar ) then continue end
array.settings[ k ] = v
array.settings[ k ].Value = cvar:GetString()
array.settings[ k ].Singleplayer = v.singleplayer and true or false
end
end
end
local json = util.TableToJSON( array )
pnlMainMenu:Call( "UpdateServerSettings(" .. json .. ")" )
end
--
-- Get the player list for this server
--
function GetPlayerList( serverip )
serverlist.PlayerList( serverip, function( tbl )
local json = util.TableToJSON( tbl )
pnlMainMenu:Call( "SetPlayerList( '" .. serverip:JavascriptSafe() .. "', " .. json .. ")" )
end )
end
local BlackList = {
Addresses = {},
Hostnames = {},
Descripts = {},
Gamemodes = {},
Maps = {},
}
local NewsList = {}
GetAPIManifest( function( result )
result = util.JSONToTable( result )
if ( !result ) then return end
NewsList = result.News and result.News.Blogs or {}
LoadNewsList()
for k, v in pairs( result.Servers and result.Servers.Banned or {} ) do
if ( v:StartsWith( "map:" ) ) then
table.insert( BlackList.Maps, v:sub( 5 ) )
elseif ( v:StartsWith( "host:" ) or v:StartsWith( "name:" ) ) then
table.insert( BlackList.Hostnames, v:sub( 6 ) )
elseif ( v:StartsWith( "desc:" ) ) then
table.insert( BlackList.Descripts, v:sub( 6 ) )
elseif ( v:StartsWith( "gm:" ) ) then
table.insert( BlackList.Gamemodes, v:sub( 4 ) )
else
table.insert( BlackList.Addresses, v )
end
end
end )
function LoadNewsList()
if ( !pnlMainMenu ) then return end
local json = util.TableToJSON( NewsList )
local bHide = cookie.GetString( "hide_newslist", "false" ) == "true"
pnlMainMenu:Call( "UpdateNewsList(" .. json .. ", " .. tostring( bHide ) .. " )" )
end
function SaveHideNews( bHide )
cookie.Set( "hide_newslist", tostring( bHide ) )
end
function IsServerBlacklisted( address, hostname, description, gm, map )
local addressNoPort = address:match( "[^:]*" )
for k, v in ipairs( BlackList.Addresses ) do
if ( address == v or addressNoPort == v ) then
return v
end
if ( v:EndsWith( "*" ) and address:sub( 1, v:len() - 1 ) == v:sub( 1, v:len() - 1 ) ) then return v end
-- IP Ranges
if ( string.find( v, "/", 1, false ) ) then
local o1, o2, o3, o4, o5 = string.match( v, "(%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)/(%d%d?)" )
local blacklistedIP = 2 ^ 24 * o1 + 2 ^ 16 * o2 + 2 ^ 8 * o3 + o4
local mask = bit.lshift( 0xFFFFFFFF, 32-o5 )
o1, o2, o3, o4 = string.match( address, "(%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)" )
local testIP = 2 ^ 24 * o1 + 2 ^ 16 * o2 + 2 ^ 8 * o3 + o4
if ( bit.band( testIP, mask ) == bit.band( blacklistedIP, mask ) ) then return v end
end
end
for k, v in ipairs( BlackList.Hostnames ) do
if ( string.match( hostname, v ) or string.match( hostname:lower(), v ) ) then
return "host: " .. v
end
end
for k, v in ipairs( BlackList.Descripts ) do
if ( string.match( description, v ) or string.match( description:lower(), v ) ) then
return "desc: " .. v
end
end
for k, v in ipairs( BlackList.Gamemodes ) do
if ( string.match( gm, v ) or string.match( gm:lower(), v ) ) then
return "gm: " .. v
end
end
for k, v in ipairs( BlackList.Maps ) do
if ( string.match( map, v ) or string.match( map:lower(), v ) ) then
return "map: " .. v
end
end
return nil
end
local Servers = {}
local ShouldStop = {}
local function SendServer( pnlMainMenu, category, id,
ping, name, desc, map, players, maxplayers, botplayers, pass, lastplayed, address, gm, workshopid, isAnon, netVersion, luaVersion, loc, gmcat )
name = string.JavascriptSafe( name )
desc = string.JavascriptSafe( desc )
map = string.JavascriptSafe( map )
address = string.JavascriptSafe( address )
gm = string.JavascriptSafe( gm )
workshopid = string.JavascriptSafe( workshopid )
netVersion = string.JavascriptSafe( tostring( netVersion ) )
loc = string.JavascriptSafe( loc )
gmcat = string.JavascriptSafe( gmcat )
pnlMainMenu:Call( string.format( [[AddServer( "%s", "%s", %i, "%s", "%s", "%s", %i, %i, %i, %s, %i, "%s", "%s", "%s", %s, "%s", "%s", "%s" , "%s" );]],
category, id, ping, name, desc, map, players, maxplayers, botplayers, tostring( pass ), lastplayed, address, gm, workshopid,
tostring( isAnon ), netVersion, tostring( serverlist.IsServerFavorite( address ) ), loc, gmcat ) )
end
function GetServers( category, id )
category = string.JavascriptSafe( category )
id = string.JavascriptSafe( id )
ShouldStop[ category ] = false
Servers[ category ] = {}
local data = {
Callback = function( ping, name, desc, map, players, maxplayers, botplayers, pass, lastplayed, address, gm, workshopid, isAnon, netVersion, luaVersion, loc, gmcat )
if ( Servers[ category ] and Servers[ category ][ address ] ) then print( "Server Browser Error!", address, category ) return end
Servers[ category ][ address ] = true
local blackListMatch = IsServerBlacklisted( address, name, desc, gm, map )
if ( blackListMatch == nil ) then
SendServer( pnlMainMenu, category, id,
ping, name, desc, map, players, maxplayers, botplayers, pass, lastplayed, address, gm, workshopid,
isAnon, netVersion, luaVersion, loc, gmcat )
else
Msg( "Ignoring server '", name, "' @ ", address, " - ", blackListMatch, " is blacklisted\n" )
end
return !ShouldStop[ category ]
end,
CallbackFailed = function( address )
if ( Servers[ category ] and Servers[ category ][ address ] ) then print( "Server Browser Error!", address, category ) return end
Servers[ category ][ address ] = true
local version = string.JavascriptSafe( tostring( VERSION ) )
SendServer( pnlMainMenu, category, id,
2000, "The server at address " .. address .. " failed to respond", "Unreachable Servers", "no_map", 0, 2, 0, "false", 0, address, "unkn", "0",
"true", version, tostring( serverlist.IsServerFavorite( address ) ), "", "" )
return !ShouldStop[ category ]
end,
Finished = function()
pnlMainMenu:Call( "FinishedServeres( '" .. category:JavascriptSafe() .. "' )" )
Servers[ category ] = {}
end,
Type = category,
GameDir = "garrysmod",
AppID = 4000,
}
serverlist.Query( data )
end
function DoStopServers( category )
pnlMainMenu:Call( "FinishedServeres( '" .. category:JavascriptSafe() .. "' )" )
ShouldStop[ category ] = true
Servers[ category ] = {}
end
function PingServer( srvAddress )
serverlist.PingServer( srvAddress, function( ping, name, desc, map, players, maxplayers, botplayers, pass, lastplayed, address, gm, workshopid, isAnon, netVersion, luaVersion, loc, gmcat )
if ( !name ) then return end
name = string.JavascriptSafe( name )
map = string.JavascriptSafe( map )
address = string.JavascriptSafe( address )
pnlMainMenu:Call( string.format( [[UpdateServer( "%s", %i, "%s", "%s", %i, %i, %i, %s );]],
address, ping, name, map, players, maxplayers, botplayers, tostring( pass ) ) )
end )
end
function FindServersAtAddress( inputStr )
local hasPort = string.find( inputStr, ":", 0, true )
local addresses = {}
if ( hasPort ) then
table.insert( addresses, inputStr )
else
for i = 0, 5 do
table.insert( addresses, inputStr .. ":" .. tostring( 27015 + i ) )
table.insert( addresses, inputStr .. ":" .. tostring( 26900 + i ) )
end
end
local output = {}
for i, addr in ipairs( addresses ) do
serverlist.PingServer( addr, function( ping, name, desc, map, players, maxplayers, botplayers, pass, lastplayed, address, gm, ... )
if ( !name ) then
table.insert( output, { name = "Server at " .. addr .. " did not respond", address = addr, ping = 2000, favorite = false, players = 0, maxplayers = 0, botplayers = 0, map = "", gamemode = "" } )
else
name = string.JavascriptSafe( name )
map = string.JavascriptSafe( map )
address = string.JavascriptSafe( address )
gm = string.JavascriptSafe( gm )
table.insert( output, {
address = address,
name = name,
ping = ping,
map = map,
gamemode = gm,
favorite = serverlist.IsServerFavorite( address ),
players = players,
botplayers = botplayers,
maxplayers = maxplayers,
} )
end
//if ( #output == #addresses ) then
local json = util.TableToJSON( output )
pnlMainMenu:Call( "ReceiveFoundServers(" .. json .. ")" )
//end
end )
end
end
--
-- Called from JS
--
function UpdateLanguages()
local f = file.Find( "resource/localization/*.png", "MOD" )
local json = util.TableToJSON( f )
pnlMainMenu:Call( "UpdateLanguages(" .. json .. ")" )
end
--
-- Called from the engine any time the language changes
--
function LanguageChanged( lang )
if ( !IsValid( pnlMainMenu ) ) then return end
UpdateLanguages()
pnlMainMenu:Call( "UpdateLanguage( \"" .. lang:JavascriptSafe() .. "\" )" )
end
function UpdateGames()
local games = engine.GetGames()
local json = util.TableToJSON( games )
pnlMainMenu:Call( "UpdateGames( " .. json .. ")" )
end
function UpdateSubscribedAddons()
local subscriptions = engine.GetAddons()
local json = util.TableToJSON( subscriptions )
pnlMainMenu:Call( "subscriptions.Update( " .. json .. " )" )
local UGCsubs = engine.GetUserContent()
local jsonUGC = util.TableToJSON( UGCsubs )
pnlMainMenu:Call( "subscriptions.UpdateUGC( " .. jsonUGC .. " )" )
end
function UpdateAddonDisabledState()
local noaddons, noworkshop = GetAddonStatus()
pnlMainMenu:Call( "UpdateAddonDisabledState( " .. tostring( noaddons ) .. ", " .. tostring( noworkshop ) .. " )" )
end
function MenuGetAddonData( wsid )
steamworks.FileInfo( wsid, function( data )
local json = util.TableToJSON( data ) or ""
pnlMainMenu:Call( "ReceivedChildAddonInfo( " .. json .. " )" )
end )
end
local presetCache = {}
local function EnsurePresetsLoaded()
if ( table.IsEmpty( presetCache ) ) then
presetCache = util.JSONToTable( LoadAddonPresets() or "", true, true ) or {}
end
end
function CreateNewAddonPreset( json )
EnsurePresetsLoaded()
local data = util.JSONToTable( json )
presetCache[ data.name ] = data
SaveAddonPresets( util.TableToJSON( presetCache ) )
end
function ImportAddonPreset( id, json )
EnsurePresetsLoaded()
steamworks.FileInfo( id, function( fileInfo )
if ( !fileInfo.children or #fileInfo.children < 1 ) then
pnlMainMenu:Call( "OnImportPresetFailed()" )
return
end
local data = util.JSONToTable( json )
presetCache[ data.name ] = data
presetCache[ data.name ].enabled = fileInfo.children
SaveAddonPresets( util.TableToJSON( presetCache ) )
ListAddonPresets()
end )
end
function DeleteAddonPreset( name )
EnsurePresetsLoaded()
presetCache[ name ] = {}
presetCache[ name ] = nil
SaveAddonPresets( util.TableToJSON( presetCache ) )
ListAddonPresets()
end
function ListAddonPresets()
EnsurePresetsLoaded()
pnlMainMenu:Call( "OnReceivePresetList(" .. util.TableToJSON( presetCache ) .. ")" )
end
-- Called when UGC subscription status changes
hook.Add( "WorkshopSubscriptionsChanged", "WorkshopSubscriptionsChanged", function( msg )
UpdateSubscribedAddons()
end )
hook.Add( "GameContentChanged", "RefreshMainMenu", function()
if ( !IsValid( pnlMainMenu ) ) then return end
pnlMainMenu:RefreshContent()
UpdateGames()
UpdateServerSettings()
UpdateSubscribedAddons()
end )
hook.Add( "LoadGModSaveFailed", "LoadGModSaveFailed", function( str, wsid )
local button2 = nil
if ( wsid and wsid:len() > 0 and wsid != "0" ) then button2 = "Open map on Steam Workshop" end
Derma_Query( str, "Failed to load save!", "OK", nil, button2, function() steamworks.ViewFile( wsid ) end )
gui.ActivateGameUI()
end )
--
-- Initialize
--
timer.Simple( 0, function()
pnlMainMenu = vgui.Create( "MainMenuPanel" )
pnlMainMenu:Call( "UpdateVersion( '" .. VERSIONSTR:JavascriptSafe() .. "', '" .. NETVERSIONSTR:JavascriptSafe() .. "', '" .. BRANCH:JavascriptSafe() .. "' )" )
local lang = GetConVarString( "gmod_language" )
LanguageChanged( lang )
hook.Run( "GameContentChanged" )
if ( !file.Exists( "html/menu.html", "MOD" ) ) then
OnMenuFailedToLoad()
end
timer.Simple( 5, function()
if ( !pnlMainMenu.menuLoaded ) then OnMenuFailedToLoad() end
end )
end )

27
lua/menu/menu.lua Normal file
View File

@@ -0,0 +1,27 @@
--[[
| 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( "mount/mount.lua" )
include( "getmaps.lua" )
include( "loading.lua" )
include( "mainmenu.lua" )
include( "video.lua" )
include( "demo_to_video.lua" )
include( "menu_save.lua" )
include( "menu_demo.lua" )
include( "menu_addon.lua" )
include( "menu_dupe.lua" )
include( "errors.lua" )
include( "problems/problems.lua" )
include( "motionsensor.lua" )
include( "util.lua" )

12
lua/menu/menu_addon.lua Normal file
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/
--]]
addon = WorkshopFileBase( "addon", {} )

89
lua/menu/menu_demo.lua Normal file
View File

@@ -0,0 +1,89 @@
--[[
| 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/
--]]
demo = WorkshopFileBase( "demo", { "demo" } )
function demo:FetchLocal( offset, perpage )
local f = file.Find( "demos/*.dem", "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 = "demos/" .. v,
name = v:StripExtension(),
preview = "demos/" .. v:StripExtension() .. ".jpg",
description = "Local demo 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 )
pnlMainMenu:Call( "demo.ReceiveLocal( " .. json .. " )" )
end
function demo:DownloadAndPlay( id )
steamworks.DownloadUGC( id, function( name )
if ( !name ) then hook.Call( "LoadGModSaveFailed", nil, "Failed to download demo from Steam Workshop!" ) return end
self:Play( name )
end )
end
function demo:Play( filename )
RunConsoleCommand( "progress_enable", "1" )
RunConsoleCommand( "playdemo", filename )
end
function demo:DownloadAndToVideo( id )
steamworks.DownloadUGC( id, function( name )
if ( !name ) then hook.Call( "LoadGModSaveFailed", nil, "Failed to download demo from Steam Workshop!" ) return end
self:ToVideo( name )
end )
end
function demo:ToVideo( filename )
RunConsoleCommand( "gm_demo_to_video", filename )
end
function demo:FinishPublish( filename, imagename, name, desc, chosenTag, other )
local info = GetDemoFileDetails( filename )
if ( !info ) then return "Couldn't get demo information!" end
steamworks.Publish( filename, imagename, name, desc, { "demo", info.mapname }, other.Callback, other.WorkshopID, other.ChangeNotes )
end

60
lua/menu/menu_dupe.lua Normal file
View File

@@ -0,0 +1,60 @@
--[[
| 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/
--]]
ws_dupe = WorkshopFileBase( "dupe", { "dupe" } )
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 )
pnlMainMenu:Call( "dupe.ReceiveLocal( " .. json .. " )" )
end
function ws_dupe:FinishPublish( filename, imagename, name, desc, chosenTag, other )
steamworks.Publish( filename, imagename, name, desc, { "dupe", chosenTag }, other.Callback, other.WorkshopID, other.ChangeNotes )
end
--
-- Called from the engine!
--
concommand.Add( "dupe_publish", function( ply, cmd, args )
ws_dupe:Publish( args[1], args[2] )
gui.ActivateGameUI()
end, nil, "", { FCVAR_DONTRECORD } )

81
lua/menu/menu_save.lua Normal file
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/
--]]
ws_save = WorkshopFileBase( "save", { "save" } )
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 save 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 )
pnlMainMenu:Call( "save.ReceiveLocal( " .. json .. " )" )
end
function ws_save:DownloadAndLoad( id )
steamworks.DownloadUGC( id, function( name )
if ( !name ) then hook.Call( "LoadGModSaveFailed", nil, "Failed to download save from Steam Workshop!" ) return end
ws_save:Load( name )
end )
end
function ws_save:Load( filename )
RunConsoleCommand( "gm_load", filename )
end
function ws_save:FinishPublish( filename, imagename, name, desc, chosenTag, other )
local info = GetSaveFileDetails( filename )
if ( !info ) then return "Couldn't get save information!" end
steamworks.Publish( filename, imagename, name, desc, { "save", info.map, chosenTag }, other.Callback, other.WorkshopID, other.ChangeNotes )
end
--
-- Called from the engine!
--
concommand.Add( "save_publish", function( ply, cmd, args )
ws_save:Publish( args[1], args[2] )
gui.ActivateGameUI()
end, nil, "", { FCVAR_DONTRECORD } )

68
lua/menu/motionsensor.lua Normal file
View File

@@ -0,0 +1,68 @@
--[[
| 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 sensor_color_show = CreateConVar( "sensor_color_show", "0", FCVAR_DONTRECORD )
local sensor_color_scale = CreateConVar( "sensor_color_scale", "0.5", FCVAR_ARCHIVE + FCVAR_DONTRECORD )
local sensor_color_x = CreateConVar( "sensor_color_x", "32", FCVAR_ARCHIVE + FCVAR_DONTRECORD )
local sensor_color_y = CreateConVar( "sensor_color_y", "-32", FCVAR_ARCHIVE + FCVAR_DONTRECORD )
local function DrawColorBox()
if ( !sensor_color_show:GetBool() ) then return end
local mat = motionsensor.GetColourMaterial()
if ( !mat ) then return end
local size = sensor_color_scale:GetFloat()
local w = 640 * size
local h = 480 * size
local x = sensor_color_x:GetInt()
if ( x < 0 ) then
x = x * -1
x = ScrW() - x - w
end
local y = sensor_color_y:GetInt()
if ( y < 0 ) then
y = y * -1
y = ScrH() - y - h
end
local alpha = 255
--
-- fade the box down if we get close, so we can click on stuff that's under it.
--
if ( vgui.CursorVisible() ) then
local mx, my = input.GetCursorPos()
local dist = Vector( mx, my, 0 ):Distance( Vector( x + w * 0.5, y + h * 0.5, 0 ) )
alpha = math.Clamp( alpha - ( 512 - dist ), 10, 255 )
end
surface.SetDrawColor( 0, 0, 0, alpha )
surface.DrawRect( x - 3, y - 3, 3, h + 6 )
surface.DrawRect( w + x, y - 3, 3, h + 6 )
surface.DrawRect( x, y - 3, w, 3 )
surface.DrawRect( x, y + h, w, 3 )
surface.SetDrawColor( 255, 255, 255, alpha )
surface.DrawRect( x - 1, y - 1, 1, h + 2 )
surface.DrawRect( w + x, y - 1, 1, h + 2 )
surface.DrawRect( x, y - 1, w, 1 )
surface.DrawRect( x, y + h, w, 1 )
surface.SetMaterial( mat )
surface.DrawTexturedRectUV( x, y, w, h, 640 / 1024, 0, 0, 480 / 512 )
end
hook.Add( "DrawOverlay", "DrawMotionSensor", DrawColorBox )

114
lua/menu/mount/mount.lua Normal file
View File

@@ -0,0 +1,114 @@
--[[
| 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 pnlWorkshop = vgui.RegisterFile( "vgui/workshop.lua" )
local vgui_workshop = nil
hook.Add( "WorkshopStart", "WorkshopStart", function()
if ( IsValid( vgui_workshop ) ) then vgui_workshop:Remove() end
vgui_workshop = GetOverlayPanel():Add( pnlWorkshop )
end )
hook.Add( "WorkshopEnd", "WorkshopEnd", function()
if ( !IsValid( vgui_workshop ) ) then return end
vgui_workshop:Remove()
end )
hook.Add( "WorkshopDownloadFile", "WorkshopDownloadFile", function( id, iImageID, title, iSize )
if ( !IsValid( vgui_workshop ) ) then
vgui_workshop = GetOverlayPanel():Add( pnlWorkshop )
end
vgui_workshop:PrepareDownloading()
vgui_workshop:StartDownloading( id, iImageID, title, iSize )
end )
hook.Add( "WorkshopDownloadedFile", "WorkshopDownloadedFile", function( id )
if ( !IsValid( vgui_workshop ) ) then return end
vgui_workshop:FinishedDownloading( id )
end )
hook.Add( "WorkshopDownloadProgress", "WorkshopDownloadProgress", function( id, iImageID, title, downloaded, expected )
if ( !IsValid( vgui_workshop ) ) then
vgui_workshop = GetOverlayPanel():Add( pnlWorkshop )
vgui_workshop:PrepareDownloading()
vgui_workshop:StartDownloading( id, iImageID, title, expected )
end
vgui_workshop:UpdateProgress( downloaded, expected )
end )
hook.Add( "WorkshopExtractProgress", "WorkshopExtractProgress", function( id, iImageID, title, percent )
if ( !IsValid( vgui_workshop ) ) then
vgui_workshop = GetOverlayPanel():Add( pnlWorkshop )
vgui_workshop:PrepareDownloading()
vgui_workshop:StartDownloading( id, iImageID, title, percent )
end
vgui_workshop:ExtractProgress( title, percent )
end )
hook.Add( "WorkshopDownloadTotals", "WorkshopDownloadTotals", function( iRemain, iTotal )
if ( !IsValid( vgui_workshop ) ) then
vgui_workshop = GetOverlayPanel():Add( pnlWorkshop )
end
--
-- Finished..
--
if ( iRemain == iTotal ) then
return
end
local completed = ( iTotal - iRemain )
if ( IsValid( vgui_workshop ) ) then
vgui_workshop:UpdateTotalProgress( completed, iTotal )
end
end )
hook.Add( "WorkshopSubscriptionsProgress", "WorkshopSubscriptionsProgress", function( iCurrent, iMax )
if ( !IsValid( vgui_workshop ) ) then
vgui_workshop = GetOverlayPanel():Add( pnlWorkshop )
end
vgui_workshop:SubscriptionsProgress( iCurrent, iMax )
end )
hook.Add( "WorkshopSubscriptionsMessage", "WorkshopSubscriptionsMessage", function( msg )
if ( !IsValid( vgui_workshop ) ) then
vgui_workshop = GetOverlayPanel():Add( pnlWorkshop )
end
vgui_workshop:SetMessage( msg )
end )

View File

@@ -0,0 +1,86 @@
--[[
| 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/
--]]
PANEL.Base = "DPanel"
local matWorkshopRocket = Material( "gui/workshop_rocket.png", "nocull smooth" )
function PANEL:Init()
self:SetSize( 64, 64 )
self.Size = 64
end
--[[
function PANEL:Think()
if ( self.Blasting ) then
self.VelY = self.VelY - FrameTime()
self.PosY = self.PosY + self.VelY * FrameTime() * 500
self.VelX = self.VelX + FrameTime() * self.VelX
self.PosX = self.PosX + self.VelX * FrameTime() * 500
self:SetPos( self.PosX, self.PosY )
if ( self.PosY < -70 ) then self:Remove() end
end
end
]]
function PANEL:Paint()
if ( !self.Material ) then return end
local angle = 0
DisableClipping( true )
surface.SetDrawColor( 255, 255, 255, 255 )
surface.SetMaterial( matWorkshopRocket )
surface.DrawTexturedRectRotated( self:GetWide() * 0.5, self:GetTall() * 0.5, self.Size * 2, self.Size * 2, angle )
if ( self.Material ) then
surface.SetMaterial( self.Material )
surface.DrawTexturedRectRotated( self:GetWide() * 0.5, self:GetTall() * 0.5, self.Size, self.Size, angle )
end
DisableClipping( false )
end
function PANEL:Charging( id, iImageID )
self.Material = nil
steamworks.Download( iImageID, false, function( name )
if ( name == nil ) then return end
if ( !IsValid( self ) ) then return end
self.Material = AddonMaterial( name )
end)
end
function PANEL:Blast()
self:Remove()
end

View File

@@ -0,0 +1,223 @@
--[[
| 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/
--]]
PANEL.Base = "DPanel"
local wsFont
if ( system.IsLinux() ) then
wsFont = "DejaVu Sans"
elseif ( system.IsWindows() ) then
wsFont = "Tahoma"
else
wsFont = "Helvetica"
end
surface.CreateFont( "WorkshopLarge", {
font = wsFont,
size = 19,
antialias = true,
weight = 800
})
local pnlRocket = vgui.RegisterFile( "addon_rocket.lua" )
local matProgressCog = Material( "gui/progress_cog.png", "nocull smooth" )
local matHeader = Material( "gui/steamworks_header.png" )
AccessorFunc( PANEL, "m_bDrawProgress", "DrawProgress", FORCE_BOOL )
function PANEL:Init()
self.Label = self:Add( "DLabel" )
self.Label:SetText( "..." )
self.Label:SetFont( "WorkshopLarge" )
self.Label:SetTextColor( Color( 255, 255, 255, 200 ) )
self.Label:Dock( TOP )
self.Label:DockMargin( 16, 10, 16, 8 )
self.Label:SetContentAlignment( 5 )
self.ProgressLabel = self:Add( "DLabel" )
self.ProgressLabel:SetText( "" )
self.ProgressLabel:SetContentAlignment( 7 )
self.ProgressLabel:SetVisible( false )
self.ProgressLabel:SetTextColor( Color( 255, 255, 255, 50 ) )
self.TotalsLabel = self:Add( "DLabel" )
self.TotalsLabel:SetText( "" )
self.TotalsLabel:SetContentAlignment( 7 )
self.TotalsLabel:SetVisible( false )
self.TotalsLabel:SetTextColor( Color( 255, 255, 255, 50 ) )
self:SetDrawProgress( false )
self.Progress = 0
self.TotalProgress = 0
end
function PANEL:PerformLayout()
self:SetSize( 500, 80 )
self:Center()
self:AlignBottom( 16 )
self.ProgressLabel:SetSize( 100, 20 )
self.ProgressLabel:SetPos( self:GetWide() - 100, 40 )
self.TotalsLabel:SetSize( 100, 20 )
self.TotalsLabel:SetPos( self:GetWide() - 100, 60 )
end
function PANEL:Spawn()
self:InvalidateLayout( true )
end
function PANEL:PrepareDownloading()
if ( self.Rocket ) then self.Rocket:Remove() end
self.Rocket = self:Add( pnlRocket )
self.Rocket:Dock( LEFT )
self.Rocket:MoveToBack()
self.Rocket:DockMargin( 8, 0, 8, 0 )
end
function PANEL:StartDownloading( id, iImageID, title, iSize )
self.Label:SetText( language.GetPhrase( "ugc.downloadingX" ):format( title ) )
self.Rocket:Charging( id, iImageID )
self:SetDrawProgress( true )
self.ProgressLabel:Show()
self.ProgressLabel:SetText( "" )
self.TotalsLabel:Show()
self:UpdateProgress( 0, iSize )
end
function PANEL:FinishedDownloading( id )
self.Progress = -1
--self:SetDrawProgress( false )
--self.ProgressLabel:Hide()
--self.TotalsLabel:Hide()
--self.Rocket:Blast()
end
function PANEL:SetMessage( msg )
self.Label:SetText( msg )
self:SetDrawProgress( false )
end
function PANEL:Paint()
DisableClipping( true )
draw.RoundedBox( 4, -1, -1, self:GetWide() + 2, self:GetTall() + 2, color_black )
DisableClipping( false )
draw.RoundedBox( 4, 0, 0, self:GetWide(), self:GetTall(), Color( 50, 50, 50, 255 ) )
surface.SetDrawColor( 0, 0, 0, 100 )
surface.SetMaterial( matProgressCog )
surface.DrawTexturedRectRotated( 0, 32, 64 * 4, 64 * 4, SysTime() * -20 )
if ( self:GetDrawProgress() ) then
-- Overall progress
local off = 0
local w = (self:GetWide() - 64 - 64 - 100)
local x = 80
draw.RoundedBox( 4, x + 32 + off, 44 + 18, w, 10, Color( 0, 0, 0, 150 ) )
draw.RoundedBox( 4, x + 33 + off, 45 + 18, w * math.Clamp( self.TotalProgress, 0.05, 1 ) - 2, 8, Color( 255, 255, 255, 200 ) )
-- Current file Progress
if ( self.Progress >= 0 ) then
draw.RoundedBox( 4, x + 32, 40, w, 15, Color( 0, 0, 0, 150 ) )
draw.RoundedBox( 4, x + 33, 41, w * math.Clamp( self.Progress, 0.05, 1 ) - 2, 15-2, Color( 255, 255, 255, 200 ) )
end
end
-- Workshop LOGO
DisableClipping( true )
local x = -8
surface.SetDrawColor( 255, 255, 255, 255 )
surface.SetMaterial( matHeader )
surface.DrawTexturedRect( x, -22, 128, 32 )
surface.SetDrawColor( 255, 255, 255, math.random( 0, 255 ) )
surface.DrawTexturedRect( x, -22, 128, 32 )
DisableClipping( false )
end
function PANEL:UpdateProgress( downloaded, expected )
if ( expected <= 0 ) then
self.Progress = 0
self.ProgressLabel:SetText( "" )
return
end
self.Progress = downloaded / expected
if ( self.Progress > 0 ) then
self.ProgressLabel:SetText( language.GetPhrase( "ugc.XoutofY" ):format( Format( "%.0f%%", self.Progress * 100 ), string.NiceSize( expected ) ) )
else
self.ProgressLabel:SetText( string.NiceSize( expected ) )
end
end
function PANEL:ExtractProgress( title, percent )
self.Label:SetText( language.GetPhrase( "ugc.extractingX" ):format( title ) )
self.Progress = percent / 100
if ( self.Progress > 0 ) then
self.ProgressLabel:SetText( Format( "%.0f%%", percent ) )
else
self.ProgressLabel:SetText( "0%" )
end
end
function PANEL:UpdateTotalProgress( iCurrent, iTotal )
self.TotalsLabel:SetText( language.GetPhrase( "ugc.addonXofY" ):format( iCurrent, iTotal ) )
self.TotalProgress = iCurrent / iTotal
end
function PANEL:SubscriptionsProgress( iCurrent, iTotal )
self.Label:SetText( "#ugc.fetching" )
self:SetDrawProgress( true )
self.Progress = iCurrent / iTotal
self.ProgressLabel:Show()
self.ProgressLabel:SetText( language.GetPhrase( "ugc.XofY" ):format( iCurrent, iTotal ) )
end

303
lua/menu/openurl.lua Normal file
View File

@@ -0,0 +1,303 @@
--[[
| 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_Browser = {}
PANEL_Browser.Base = "DFrame"
function PANEL_Browser:Init()
self.HTML = vgui.Create( "HTML", self )
if ( !self.HTML ) then
print( "SteamOverlayReplace: Failed to create HTML element" )
self:Remove()
return
end
self.HTML:Dock( FILL )
--self.HTML:SetOpenLinksExternally( true )
self:SetTitle( "Steam overlay replacement" )
self:SetSize( ScrW() * 0.75, ScrH() * 0.75 )
self:SetSizable( true )
self:Center()
self:MakePopup()
end
function PANEL_Browser:SetURL( url )
self.HTML:OpenURL( url )
end
-- Called from the engine
function GMOD_OpenURLNoOverlay( url )
local BrowserInst = vgui.CreateFromTable( PANEL_Browser )
BrowserInst:SetURL( url )
timer.Simple( 0, function()
if ( !gui.IsGameUIVisible() ) then gui.ActivateGameUI() end
end )
end
----------------------------------------------
local RememberedDenials = {}
local PANEL = {}
PANEL.Base = "DFrame"
function PANEL:Init()
self.Type = "openurl"
self:SetTitle( "#openurl.title" )
self.Garble = vgui.Create( "DLabel", self )
self.Garble:SetText( "#openurl.text" )
self.Garble:SetContentAlignment( 5 )
self.Garble:Dock( TOP )
self.URL = vgui.Create( "DTextEntry", self )
self.URL:SetEnabled( false )
self.URL:Dock( TOP )
self.CustomPanel = vgui.Create( "DLabel", self )
self.CustomPanel:Dock( TOP )
self.CustomPanel:SetContentAlignment( 5 )
self.CustomPanel:DockMargin( 0, 5, 0, 0 )
self.CustomPanel:SetVisible( false )
self.CustomPanel.Color = Color( 0, 0, 0, 0 )
self.CustomPanel.Paint = function( s, w, h )
draw.RoundedBox( 0, 0, 0, w, h, s.Color )
end
self.Buttons = vgui.Create( "Panel", self )
self.Buttons:Dock( TOP )
self.Buttons:DockMargin( 0, 5, 0, 0 )
self.Disconnect = vgui.Create( "DButton", self.Buttons )
self.Disconnect:SetText( "#openurl.disconnect" )
self.Disconnect.DoClick = function() self:DoNope() RunConsoleCommand( "disconnect" ) end
self.Disconnect:Dock( LEFT )
self.Disconnect:SizeToContents()
self.Disconnect:SetWide( self.Disconnect:GetWide() + 10 )
self.Nope = vgui.Create( "DButton", self.Buttons )
self.Nope:SetText( "#openurl.nope" )
self.Nope.DoClick = function() self:DoNope() end
self.Nope:DockMargin( 0, 0, 0, 0 )
self.Nope:Dock( RIGHT )
self.Yes = vgui.Create( "DButton", self.Buttons )
self.Yes:SetText( "#openurl.yes" )
self.Yes.DoClick = function() self:DoYes() end
self.Yes:DockMargin( 0, 0, 20, 0 )
self.Yes:Dock( RIGHT )
self.YesPerma = vgui.Create( "DCheckBoxLabel", self.Buttons )
self.YesPerma:SetText( "#openurl.yes_remember" )
self.YesPerma:DockMargin( 0, 0, 20, 0 )
self.YesPerma:Dock( RIGHT )
self.YesPerma:SetVisible( false )
self:SetSize( 680, 104 )
self:Center()
self:MakePopup()
self:DoModal()
hook.Add( "Think", self, self.AlwaysThink )
if ( !IsInGame() ) then self.Disconnect:SetVisible( false ) end
end
function PANEL:LoadServerInfo()
self.CustomPanel:SetVisible( true )
self.CustomPanel:SetText( "#askconnect.loading" )
self.CustomPanel:SizeToContents()
serverlist.PingServer( self:GetURL(), function( ping, name, desc, map, players, maxplayers, bot, pass, lp, ip, gamemode )
if ( !IsValid( self ) ) then return end
if ( !ping ) then
self.CustomPanel.Color = Color( 200, 50, 50 )
self.CustomPanel:SetText( "#askconnect.no_response" )
else
self.CustomPanel:SetText( string.format( "%s\n%i/%i players | %s | %s | %ims", name, players, maxplayers, map, desc, ping ) )
end
self.CustomPanel:SizeToContents()
end )
end
function PANEL:DisplayPermissionInfo()
self.CustomPanel.Color = Color( 200, 200, 200 )
self.CustomPanel:SetVisible( true )
self.CustomPanel:SetDark( true )
self.CustomPanel:SetText( "\n" .. language.GetPhrase( "permission." .. self:GetURL() ) .. "\n" .. language.GetPhrase( "permission." .. self:GetURL() .. ".help" ) .. "\n" )
self.CustomPanel:SizeToContents()
end
function PANEL:AlwaysThink()
-- Ping the server for details
if ( SysTime() - self.StartTime > 0.1 and self.Type == "askconnect" and !self.CustomPanel:IsVisible() ) then
self:LoadServerInfo()
end
if ( self.StartTime + 1 > SysTime() ) then
return
end
if ( !self.Yes:IsEnabled() ) then
self.Yes:SetEnabled( true )
end
if ( !gui.IsGameUIVisible() ) then
self:Remove()
end
end
function PANEL:PerformLayout( w, h )
DFrame.PerformLayout( self, w, h )
self:SizeToChildren( false, true )
end
function PANEL:SetURL( url )
self.URL:SetText( url )
self.StartTime = SysTime()
self.Yes:SetEnabled( false )
self.CustomPanel:SetVisible( false )
self.CustomPanel.Color = Color( 0, 0, 0, 0 )
self:InvalidateLayout()
if ( self.Type == "permission" ) then
self:DisplayPermissionInfo()
end
end
function PANEL:GetURL()
return self.URL:GetText()
end
function PANEL:DoNope()
self:Remove()
gui.HideGameUI()
-- Keep track of what the player wants to ignore, but only for this session.
local remember = self.YesPerma:GetChecked()
if ( remember ) then
RememberedDenials[ self.uniquePermID ] = true
end
end
function PANEL:DoYes()
if ( self.StartTime + 1 > SysTime() ) then
return
end
local saveYes = self.YesPerma:GetChecked()
self:DoYesAction( !saveYes )
self:Remove()
gui.HideGameUI()
end
function PANEL:DoYesAction( bSessionOnly )
if ( self.Type == "openurl" ) then
gui.OpenURL( self.URL:GetText() )
elseif ( self.Type == "askconnect" ) then
permissions.Grant( "connect", bSessionOnly )
permissions.Connect( self.URL:GetText() )
elseif ( self.Type == "permission" ) then
permissions.Grant( self.URL:GetText(), bSessionOnly )
else
ErrorNoHaltWithStack( "Unhandled confirmation type '" .. tostring( self.Type ) .. "'!" )
end
end
function PANEL:SetType( t )
self.Type = t
self:SetTitle( "#" .. t .. ".title" )
self.Garble:SetText( "#" .. t .. ".text" )
if ( self.Type == "permission" or self.Type == "askconnect" ) then
self.YesPerma:SetVisible( true )
end
end
local PanelInst = nil
local function OpenConfirmationDialog( address, confirm_type )
local permID = tostring( confirm_type ) .. "|" .. tostring( address ) .. "|" .. tostring( engine.CurrentServerAddress() )
if ( RememberedDenials[ permID ] ) then
print( "Ignoring request for denied permission " .. tostring( confirm_type ) .. " to " .. tostring( address ) ) -- Debug
return
end
if ( IsValid( PanelInst ) and PanelInst:GetURL() == address ) then return end
if ( !IsValid( PanelInst ) ) then PanelInst = vgui.CreateFromTable( PANEL ) end
PanelInst.uniquePermID = permID
PanelInst:SetType( confirm_type )
PanelInst:SetURL( address )
timer.Simple( 0, function()
if ( !gui.IsGameUIVisible() ) then gui.ActivateGameUI() end
end )
end
-- Called from the engine
function RequestOpenURL( url )
OpenConfirmationDialog( url, "openurl" )
end
function RequestConnectToServer( serverip )
if ( permissions.IsGranted( "connect" ) ) then
permissions.Connect( serverip )
else
OpenConfirmationDialog( serverip, "askconnect" )
end
end
function RequestPermission( perm )
OpenConfirmationDialog( perm, "permission" )
end

View File

@@ -0,0 +1,241 @@
--[[
| 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:Dock( TOP )
self:DockMargin( 0, 0, 0, 1 )
self.RemoveBtn = self:Add( "DImageButton" )
self.RemoveBtn:SetImage( "icon16/cross.png" )
self.RemoveBtn:SetSize( 16, 16 )
self.RemoveBtn.DoClick = function( btm )
if ( !self.Permission ) then return end
permissions.Revoke( self.Permission, self:GetParent():GetParent().Title )
end
end
local textPaddingX = 5
local textPaddingY = 5
local buttonPad = 8
-- lua_run_cl permissions.AskToConnect( "da" )
function PANEL:PerformLayout( w, h )
local txt = "<font=DermaLarge>" .. language.GetPhrase( "permission." .. self.Permission ) .. ( self.IsTemporary and " <color=255,128,0>(TEMPORARY)</color>" or "" ) .. "</font>\n <font=DermaDefault>" .. language.GetPhrase( "permission." .. self.Permission .. ".help" ) .. "</font>"
self.Markup = markup.Parse( txt, self:GetWide() - self.RemoveBtn:GetWide() - buttonPad * 2 - textPaddingX * 2 )
self:SetTall( textPaddingY + self.Markup:GetHeight() + textPaddingY )
local bW, bH = self.RemoveBtn:GetSize()
self.RemoveBtn:SetPos( w - bW - buttonPad, self:GetTall() / 2 - bH / 2 )
end
local bgClr = Color( 75, 75, 75, 255 )
function PANEL:Paint( w, h )
bgClr.a = self:GetAlpha()
draw.RoundedBox( 0, 0, 0, w, h, bgClr )
-- No info yet
if ( !self.Permission ) then return end
-- The error
self.Markup:Draw( textPaddingX, textPaddingY, nil, nil, self:GetAlpha() )
end
function PANEL:Think()
end
function PANEL:SetPermission( perm, temp )
self.Permission = perm
self.IsTemporary = temp
self:InvalidateLayout( true )
end
vgui.Register( "Permission", PANEL, "Panel" )
local PANEL = {}
local arrowMat = Material( "gui/point.png" )
local collapsedCache = {}
function PANEL:Init()
self:Dock( TOP )
self:SetTall( 20 )
self:DockMargin( 0, 0, 0, 5 )
self.PermissionPanels = {}
self.PermissionList = self:Add( "Panel" )
self.Collapsed = false
end
local white = Color( 255, 255, 255, 255 )
local bg = Color( 50, 50, 50, 255 )
function PANEL:Paint( w, h )
white.a = self:GetAlpha()
bg.a = self:GetAlpha()
draw.RoundedBox( 4, 0, 0, w, h, bg )
draw.SimpleText( self.Title, "DermaLarge", 4, 2, white, draw.TEXT_ALIGN_LEFT, draw.TEXT_ALIGN_TOP )
surface.SetMaterial( arrowMat )
surface.SetDrawColor( white )
surface.DrawTexturedRectRotated( w - 20, 20, 20, 20, self.Collapsed and 180 or 0 )
end
function PANEL:OnMousePressed()
self.Collapsed = !self.Collapsed
self:InvalidateLayout()
collapsedCache[ self.Title ] = self.Collapsed
end
function PANEL:SetGroup( groupID )
self.Title = groupID
end
function PANEL:PerformLayout( w, h )
self.PermissionList:InvalidateLayout( true )
self.PermissionList:SizeToChildren( false, true )
surface.SetFont( "DermaLarge" )
local _, headerSize = surface.GetTextSize( self.Title )
self.PermissionList:SetPos( 4, 4 + headerSize )
self.PermissionList:SetWide( self:GetWide() - 8 )
if ( self.Collapsed ) then
self:SetTall( headerSize + 5 )
return
end
self:SetTall( self.PermissionList:GetTall() + ( 8 + headerSize ) )
end
function PANEL:ReceivePerm( perm, temp )
local pnl = self.PermissionPanels[ perm ]
if ( !IsValid( pnl ) ) then
pnl = self.PermissionList:Add( "Permission" )
self.PermissionPanels[ perm ] = pnl
self:InvalidateLayout()
end
pnl:SetPermission( perm, temp )
end
function PANEL:ReceivePerms( perms, temp )
for id, str in pairs( string.Explode( ",", perms ) ) do
self:ReceivePerm( str, temp )
end
end
vgui.Register( "ServerPermissionsPanel", PANEL, "Panel" )
local PANEL = {}
local g_Permissions = nil
function PANEL:Init()
self.ServerPanels = {}
self:DoPermissions()
g_Permissions = self
end
function PANEL:AddServerGroup( address, perms, temp )
local pnl = self.ServerPanels[ address ]
if ( !IsValid( pnl ) ) then
pnl = self:Add( "ServerPermissionsPanel" )
pnl:SetGroup( address )
self.ServerPanels[ address ] = pnl
self:InvalidateLayout()
end
pnl:ReceivePerms( perms, temp )
end
function PANEL:DoPermissions()
for id, pnl in pairs( self.ServerPanels ) do
pnl:Remove()
end
local perms = permissions.GetAll()
for address, perm in pairs( perms.temporary ) do
self:AddServerGroup( address, perm, true )
end
for address, perm in pairs( perms.permanent ) do
self:AddServerGroup( address, perm )
end
end
function PANEL:Think()
if ( self:GetCanvas():ChildCount() < 1 and !IsValid( self.NoPermissionsLabel ) ) then
self.NoPermissionsLabel = self.ParentFrame:AddEmptyWarning( "#permissions.none", self )
elseif ( self:GetCanvas():ChildCount() > 1 and IsValid( self.NoPermissionsLabel ) ) then
self.NoPermissionsLabel:Remove()
end
end
vgui.Register( "PermissionViewer", PANEL, "DScrollPanel" )
hook.Add( "OnPermissionsChanged", "OnPermissionsChanged", function()
if ( IsValid( g_Permissions ) ) then
g_Permissions:DoPermissions()
end
end )

View File

@@ -0,0 +1,204 @@
--[[
| 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:Dock( TOP )
self:DockMargin( 0, 0, 0, 1 )
self.CopyBtn = self:Add( "DImageButton" )
self.CopyBtn:SetImage( "icon16/page_copy.png" )
self.CopyBtn:SetSize( 16, 16 )
self.CopyBtn.DoClick = function( btm )
if ( !self.Problem ) then return end
SetClipboardText( language.GetPhrase( self.Problem.text ) )
end
self.FixBtn = self:Add( "DButton" )
self.FixBtn.DoClick = function( btm )
if ( !self.Problem or !self.Problem.fix ) then return end
self.Problem.fix()
end
end
local severityWidth = 8
local severityOffset = severityWidth + 4
local textPaddingX = 5
local textPaddingY = 10
local copyIconPad = 8
local severityColors = {}
severityColors[ 0 ] = Color( 217, 217, 217 )
severityColors[ 1 ] = Color( 255, 200, 0 )
severityColors[ 2 ] = Color( 255, 64, 0 )
function PANEL:PerformLayout( w, h )
self.Markup = markup.Parse( "<font=DermaDefault>" .. language.GetPhrase( self.Problem.text ) .. "</font>", w - self.CopyBtn:GetWide() - copyIconPad * 2 - severityOffset - textPaddingX * 2 )
local targetH = textPaddingY
if ( self.Problem ) then
targetH = targetH + self.Markup:GetHeight() + textPaddingY
end
local fixW, fixH = self.FixBtn:GetSize()
self.FixBtn:SetPos( textPaddingX + severityOffset, targetH )
targetH = targetH + fixH + textPaddingY
self:SetTall( targetH )
local bW, bH = self.CopyBtn:GetSize()
self.CopyBtn:SetPos( w - bW - copyIconPad, targetH / 2 - bH / 2 )
end
local bgClr = Color( 75, 75, 75, 255 )
function PANEL:Paint( w, h )
bgClr.a = self:GetAlpha()
-- No info yet
if ( !self.Problem ) then
draw.RoundedBox( 0, 0, 0, w, h, bgClr )
return
end
-- Background
local bgClrH = bgClr
if ( self.Problem.lastOccurence ) then
local add = 75 + math.max( 25 - ( SysTime() - self.Problem.lastOccurence ) * 25, 0 )
bgClrH = Color( add, add, add, self:GetAlpha() )
end
draw.RoundedBox( 0, 0, 0, w, h, bgClrH )
-- Severity indicator
local color = severityColors[ self.Problem.severity ]
if ( color == nil ) then color = severityColors[ 0 ] end
draw.RoundedBox( 0, 0, 0, severityWidth, h, color )
-- The error
self.Markup:Draw( textPaddingX + severityOffset, textPaddingY, nil, nil, self:GetAlpha() )
end
function PANEL:Think()
end
function PANEL:Setup( problem )
self.Problem = problem
self.Markup = markup.Parse( "<font=DermaDefault>" .. language.GetPhrase( self.Problem.text ) .. "</font>", self:GetWide() - self.CopyBtn:GetWide() - copyIconPad * 2 - severityOffset - textPaddingX * 2 )
self.FixBtn:SetEnabled( problem.fix != nil )
self.FixBtn:SetText( problem.fix and "#problems.quick_fix" or "#problems.no_quick_fix" )
self.FixBtn:SizeToContentsX( 10 )
end
vgui.Register( "GenericProblem", PANEL, "Panel" )
--[[
ProblemGroup
]]
local PANEL = {}
local arrowMat = Material( "gui/point.png" )
local collapsedCache = {}
function PANEL:Init()
self:Dock( TOP )
self:SetTall( 20 )
self:DockMargin( 0, 0, 0, 5 )
self.ProblemPanels = {}
self.ProblemList = self:Add( "Panel" )
self.Collapsed = false
end
local white = Color( 255, 255, 255, 255 )
local bg = Color( 50, 50, 50, 255 )
function PANEL:Paint( w, h )
white.a = self:GetAlpha()
bg.a = self:GetAlpha()
draw.RoundedBox( 4, 0, 0, w, h, bg )
draw.SimpleText( language.GetPhrase( "problem_grp." .. self.Title ), "DermaLarge", 4, 2, white, draw.TEXT_ALIGN_LEFT, draw.TEXT_ALIGN_TOP )
surface.SetMaterial( arrowMat )
surface.SetDrawColor( white )
surface.DrawTexturedRectRotated( w - 20, 20, 20, 20, self.Collapsed and 180 or 0 )
end
function PANEL:OnMousePressed()
self.Collapsed = !self.Collapsed
self:InvalidateLayout()
collapsedCache[ self.Title ] = self.Collapsed
end
function PANEL:SetGroup( groupID )
self.Title = groupID
end
function PANEL:PerformLayout( w, h )
self.ProblemList:InvalidateLayout( true )
self.ProblemList:SizeToChildren( false, true )
surface.SetFont( "DermaLarge" )
local _, headerSize = surface.GetTextSize( self.Title )
self.ProblemList:SetPos( 4, 4 + headerSize )
self.ProblemList:SetWide( self:GetWide() - 8 )
if ( self.Collapsed ) then
self:SetTall( headerSize + 5 )
return
end
self:SetTall( self.ProblemList:GetTall() + ( 8 + headerSize ) )
end
function PANEL:ReceivedProblem( uid, prob )
local pnl = self.ProblemPanels[ uid ]
if ( !IsValid( pnl ) ) then
pnl = self.ProblemList:Add( "GenericProblem" )
self.ProblemPanels[ uid ] = pnl
self:InvalidateLayout()
end
pnl:Setup( prob )
end
vgui.Register( "GenericProblemGroup", PANEL, "Panel" )

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 realmColors = {}
realmColors[ "menu" ] = Color( 121, 221, 100 )
realmColors[ "client" ] = Color( 255, 222, 102 )
realmColors[ "server" ] = Color( 137, 222, 255 )
surface.CreateFont( "DermaMedium", {
font = "Roboto",
size = 24,
weight = 500
} )
local PANEL = {}
function PANEL:Init()
self:Dock( TOP )
self:DockMargin( 0, 0, 0, 1 )
self.CopyBtn = self:Add( "DImageButton" )
self.CopyBtn:SetImage( "icon16/page_copy.png" )
self.CopyBtn:SetSize( 16, 16 )
self.CopyBtn.DoClick = function( btm )
if ( !self.Problem ) then return end
local prepend = ""
if ( self.Problem.title and self.Problem.title:len() > 0 and self.Problem.title != "Other" ) then prepend = "[" .. self.Problem.title .. "] " end
SetClipboardText( prepend .. self.Problem.text )
end
end
function PANEL:PerformLayout( w, h )
surface.SetFont( "DermaDefault" )
if ( self.Problem ) then
local _, th = surface.GetTextSize( self.Problem.text )
self:SetTall( th + 10 )
end
local bW, bH = self.CopyBtn:GetSize()
self.CopyBtn:SetPos( w - bW - 8, h / 2 - bH / 2 )
end
local bgClr = Color( 75, 75, 75, 255 )
local fgClr = Color( 255, 255, 255, 255 )
function PANEL:Paint( w, h )
bgClr.a = self:GetAlpha()
-- No info yet
if ( !self.Problem ) then
draw.RoundedBox( 0, 0, 0, w, h, bgClr )
return
end
-- Get the colors
local clr = table.Copy( realmColors[ self.Problem.realm ] or fgClr )
clr.a = self:GetAlpha()
-- Background color
local bgClrH = bgClr
if ( self.Problem.lastOccurence ) then
local add = 75 + math.max( 25 - ( SysTime() - self.Problem.lastOccurence ) * 25, 0 )
bgClrH = Color( add, add, add, self:GetAlpha() )
end
draw.RoundedBox( 0, 0, 0, w, h, bgClrH )
-- Draw background
local count = 0
if ( self.Problem and self.Problem.count ) then count = self.Problem.count end
-- The error count
if ( count > 0 ) then
local txt = "x" .. count
surface.SetFont( "DermaMedium" )
local tW, tH = surface.GetTextSize( txt )
tW = tW
draw.SimpleText( txt, "DermaMedium", w - 16 - 16, h / 2, clr, draw.TEXT_ALIGN_RIGHT, draw.TEXT_ALIGN_CENTER )
end
-- The error
draw.DrawText( self.Problem.text, "DermaDefault", 5, 5, clr )
end
function PANEL:Setup( problem )
self.Problem = problem
end
vgui.Register( "LuaProblem", PANEL, "Panel" )
--[[ ////////////////////////////////////////////////// GROUP ////////////////////////////////////////////////// ]] --
local PANEL = {}
local arrowMat = Material( "gui/point.png" )
local collapsedCache = {}
function PANEL:Init()
self:Dock( TOP )
self:SetTall( 20 )
self:DockMargin( 0, 0, 0, 5 )
self.ErrorPanels = {}
self.LuaErrorList = self:Add( "Panel" )
self.ClearButton = self:Add( "DImageButton" )
self.ClearButton:SetImage( "gui/cross.png" )
self.ClearButton:SetSize( 18, 18 )
self.ClearButton.DoClick = function( s ) self:ClearThisGroup() end
self.Collapsed = false
end
local white = Color( 255, 255, 255, 255 )
local bg = Color( 50, 50, 50, 255 )
function PANEL:Paint( w, h )
white.a = self:GetAlpha()
bg.a = self:GetAlpha()
draw.RoundedBox( 4, 0, 0, w, h, bg )
draw.SimpleText( self.Title, "DermaLarge", 4, 2, white, draw.TEXT_ALIGN_LEFT, draw.TEXT_ALIGN_TOP )
surface.SetMaterial( arrowMat )
surface.SetDrawColor( white )
surface.DrawTexturedRectRotated( w - 20, 18, 20, 12, self.Collapsed and 180 or 0 )
local h2 = self.LuaErrorList:GetTall()
local _, lY = self.LuaErrorList:GetPos()
draw.DrawText( self:GetExplainerText(), "DermaDefault", w / 2, lY + h2 + 5, white, draw.TEXT_ALIGN_CENTER )
end
function PANEL:ClearThisGroup()
ClearLuaErrorGroup( self.GroupID )
end
function PANEL:OnMousePressed( code )
if ( code != MOUSE_LEFT ) then return end
self.Collapsed = !self.Collapsed
self:InvalidateLayout()
collapsedCache[ self.Title ] = self.Collapsed
end
function PANEL:GetExplainerText()
if ( self.Title == "Other" and self.AddonID and self.AddonID:len() < 2 ) then
return language.GetPhrase( "problems.generic_lua_error" )
end
-- Not a workshop addon, or a floating .gma (WSID=0)
if ( self.AddonID and self.AddonID:len() < 2 ) then
return language.GetPhrase( "problems.addon_lua_error" ):format( self.Title )
end
return language.GetPhrase( "problems.workshop_lua_error" ):format( self.Title )
end
function PANEL:SetTitleAndID( title, id, groupid )
self.Title = title
self.AddonID = id
self.GroupID = groupid
self.Collapsed = collapsedCache[ self.Title ]
if ( self.AddonID and self.AddonID:len() > 1 ) then
self.DisableBtn = self:Add( "DButton" )
self.DisableBtn:SetText( "#problems.disable" )
self.DisableBtn:SizeToContentsX( 10 )
self.DisableBtn.DoClick = function() steamworks.SetShouldMountAddon( self.AddonID, false ) steamworks.ApplyAddons() end
self.OpenWSBtn = self:Add( "DButton" )
self.OpenWSBtn:SetText( "#problems.open_on_workshop" )
self.OpenWSBtn:SizeToContentsX( 10 )
self.OpenWSBtn.DoClick = function() steamworks.ViewFile( self.AddonID ) end
self.UninstallBtn = self:Add( "DButton" )
self.UninstallBtn:SetText( "#problems.uninstall" )
self.UninstallBtn:SizeToContentsX( 10 )
self.UninstallBtn.DoClick = function() steamworks.Unsubscribe( self.AddonID ) end
local maxS = math.max( self.DisableBtn:GetWide(), self.OpenWSBtn:GetWide(), self.UninstallBtn:GetWide() )
self.DisableBtn:SetWide( maxS )
self.OpenWSBtn:SetWide( maxS )
self.UninstallBtn:SetWide( maxS )
end
end
function PANEL:PerformLayout( w, h )
self.ClearButton:SetPos( w - 56, 9 )
self.LuaErrorList:InvalidateLayout( true )
self.LuaErrorList:SizeToChildren( false, true )
surface.SetFont( "DermaLarge" )
local _, headerSize = surface.GetTextSize( self.Title )
self.LuaErrorList:SetPos( 4, 4 + headerSize )
self.LuaErrorList:SetWide( self:GetWide() - 8 )
surface.SetFont( "DermaDefault" )
local _, etH = surface.GetTextSize( self:GetExplainerText() )
if ( IsValid( self.DisableBtn ) ) then
local h2 = self.LuaErrorList:GetTall()
local _, lY = self.LuaErrorList:GetPos()
local y = lY + h2 + 5 + etH + 5
self.OpenWSBtn:SetPos( w * 0.25 - self.OpenWSBtn:GetWide() / 2, y )
self.DisableBtn:SetPos( w * 0.5 - self.DisableBtn:GetWide() / 2, y )
self.UninstallBtn:SetPos( w * 0.75 - self.UninstallBtn:GetWide() / 2, y )
etH = etH + self.DisableBtn:GetTall() + 5
end
if ( self.Collapsed ) then
self:SetTall( headerSize + 5 )
return
end
self:SetTall( self.LuaErrorList:GetTall() + ( 8 + headerSize ) + ( etH + 5 ) )
end
function PANEL:Think()
if ( IsValid( self.DisableBtn ) ) then
self.DisableBtn:SetEnabled( steamworks.IsSubscribed( self.AddonID ) and steamworks.ShouldMountAddon( self.AddonID ) )
self.UninstallBtn:SetEnabled( steamworks.IsSubscribed( self.AddonID ) )
end
end
function PANEL:ReceivedError( uid, err )
local pnl = self.ErrorPanels[ uid ]
local shouldSort = false
if ( !IsValid( pnl ) ) then
pnl = self.LuaErrorList:Add( "LuaProblem" )
self.ErrorPanels[ uid ] = pnl
self:InvalidateLayout()
shouldSort = true
end
pnl:Setup( err )
if ( shouldSort ) then
local sorted = {}
for gid, epnl in pairs( self.ErrorPanels ) do
sorted[ epnl.Problem.firstOccurence ] = epnl
end
local z = 0
for sort, spnl in SortedPairs( sorted ) do
spnl:SetZPos( z )
z = z + 1
end
end
end
vgui.Register( "LuaProblemGroup", PANEL, "Panel" )

View File

@@ -0,0 +1,352 @@
--[[
| 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( "problems_pnl.lua" )
local ProblemsPanel
local ProblemsCount = 0
local ProblemSeverity = 0
local MenuUpdated = false
Problems = Problems or {}
ErrorLog = ErrorLog or {}
local function RefreshGenericProblemList()
if ( IsValid( ProblemsPanel ) ) then
ProblemsPanel.ProblemsList:Clear()
ProblemsPanel.ProblemPanels = {}
for id, prob in pairs( Problems ) do
ProblemsPanel:ReceivedProblem( id, prob )
end
ProblemsPanel:InvalidateLayout()
end
end
local function RefreshLuaErrorList()
if ( IsValid( ProblemsPanel ) ) then
ProblemsPanel.LuaErrorList:Clear()
ProblemsPanel.ErrorPanels = {}
for id, err in pairs( ErrorLog ) do
ProblemsPanel:ReceivedError( id, err )
end
ProblemsPanel:InvalidateLayout()
end
end
local function CountProblem( severity )
ProblemsCount = ProblemsCount + 1
ProblemSeverity = math.max( ProblemSeverity, severity or 0 )
if ( IsValid( pnlMainMenu ) ) then
pnlMainMenu:SetProblemCount( ProblemsCount, ProblemSeverity )
MenuUpdated = true
end
end
local function RecountProblems()
ProblemsCount = 0
ProblemSeverity = 0
for id, err in pairs( ErrorLog ) do
ProblemsCount = ProblemsCount + 1
ProblemSeverity = math.max( err.severity or 0, ProblemSeverity )
end
for id, prob in pairs( Problems ) do
ProblemsCount = ProblemsCount + 1
ProblemSeverity = math.max( prob.severity or 0, ProblemSeverity )
end
if ( IsValid( pnlMainMenu ) ) then
pnlMainMenu:SetProblemCount( ProblemsCount, ProblemSeverity )
MenuUpdated = true
end
end
timer.Create( "menu_problem_counter", 1, 0, function()
if ( MenuUpdated ) then timer.Remove( "menu_problem_counter" ) return end
RecountProblems()
end )
function ClearLuaErrorGroup( group_id )
-- pairs should guard us against changing the array we are looping through
for id, err in pairs( ErrorLog ) do
if ( err.type == group_id ) then
ErrorLog[ id ] = nil
end
end
RecountProblems()
RefreshLuaErrorList()
end
function ClearProblem( id )
if ( !Problems[ id ] ) then return end
Problems[ id ] = nil
RecountProblems()
RefreshGenericProblemList()
end
function FireProblem( prob )
local probID = prob.id or prob.text
if ( !Problems[ probID ] ) then
CountProblem( prob.severity )
end
Problems[ probID ] = prob
if ( IsValid( ProblemsPanel ) ) then ProblemsPanel:ReceivedProblem( probID, prob ) end
end
local function FireError( str, realm, stack, addontitle, addonid )
-- Reconstruct the stack trace
local errorText = str
errorText = string.Replace( errorText, "\t", ( " " ):rep( 12 ) ) -- Ew
for i, t in pairs( stack ) do
if ( !t.Function or t.Function == "" ) then t.Function = "unknown" end
errorText = errorText .. "\n" .. (" "):rep( i ) .. i .. ". " .. t.Function .. " - " .. t.File .. ":" .. t.Line
end
local errorUniqueID = errorText .. realm
if ( !addontitle or addontitle == "" ) then addontitle = "Other" end
if ( !ErrorLog[ errorUniqueID ] ) then
local newErr = {
text = errorText,
realm = realm,
addonid = addonid or "",
title = addontitle,
count = 1,
severity = 1,
lastOccurence = SysTime(),
firstOccurence = SysTime()
}
newErr.type = newErr.title .. "-" .. newErr.addonid
CountProblem( newErr.severity )
ErrorLog[ errorUniqueID ] = newErr
else
ErrorLog[ errorUniqueID ].count = ErrorLog[ errorUniqueID ].count + 1
ErrorLog[ errorUniqueID ].lastOccurence = SysTime()
end
if ( IsValid( ProblemsPanel ) ) then ProblemsPanel:ReceivedError( errorUniqueID, ErrorLog[ errorUniqueID ] ) end
end
hook.Add( "OnLuaError", "MenuErrorLogger", function( str, realm, stack, addontitle, addonid )
local good, err = pcall( function()
FireError( str, realm, stack, addontitle, addonid )
end )
if ( !good ) then
print( "Failed to log a Lua error!\n", err)
end
end )
function OpenProblemsPanel()
if ( IsValid( ProblemsPanel ) ) then ProblemsPanel:Remove() end
ProblemsPanel = vgui.Create( "ProblemsPanel" )
local anyErrors = false
for id, err in pairs( table.Copy( ErrorLog ) ) do
ProblemsPanel:ReceivedError( id, err )
anyErrors = true
end
for id, prob in pairs( Problems ) do
ProblemsPanel:ReceivedProblem( id, prob )
end
if ( !anyErrors ) then
ProblemsPanel.Tabs:SwitchToName( "#problems.problems" )
end
end
-- Called from the engine to notify the player about a problem in a more user friendly way compared to a console message
function FireProblemFromEngine( id, severity, params )
if ( id == "menu_cleanupgmas" ) then
local text = language.GetPhrase( "problem." .. id ) .. "\n\n" .. params:Replace( ";", "\n" )
FireProblem( { id = id, text = text, severity = severity, type = "addons", fix = function() RunConsoleCommand( "menu_cleanupgmas" ) ClearProblem( id ) end } )
elseif ( id == "readonly_file" ) then
local text = params
FireProblem( { id = id .. params, text = text, severity = severity, type = "config" } )
else
-- missing_addon_file
-- addon_download_failed = title;reason
local text = language.GetPhrase( "problem." .. id )
text = text:format( unpack( string.Explode( ";", params ) ) )
FireProblem( { id = id .. params, text = text, severity = severity, type = "addons" } )
end
end
timer.Create( "menu_check_for_problems", 1, 0, function()
if ( math.floor( GetConVarNumber( "mat_hdr_level" ) ) != 2 ) then
FireProblem( { id = "mat_hdr_level", text = "#problem.mat_hdr_level", type = "config", fix = function() RunConsoleCommand( "mat_hdr_level", "2" ) end, severity = 1 } )
else
ClearProblem( "mat_hdr_level" )
end
if ( math.floor( math.abs( GetConVarNumber( "mat_bumpmap" ) ) ) == 0 ) then
FireProblem( { id = "mat_bumpmap", text = "#problem.mat_bumpmap", type = "config", fix = function() RunConsoleCommand( "mat_bumpmap", "1" ) end } )
else
ClearProblem( "mat_bumpmap" )
end
if ( math.floor( math.abs( GetConVarNumber( "mat_specular" ) ) ) == 0 ) then
FireProblem( { id = "mat_specular", text = "#problem.mat_specular", type = "config", fix = function() RunConsoleCommand( "mat_specular", "1" ) end } )
else
ClearProblem( "mat_specular" )
end
if ( math.floor( math.abs( GetConVarNumber( "gmod_mcore_test" ) ) ) != 0 ) then
FireProblem( { id = "gmod_mcore_test", text = "#problem.gmod_mcore_test", type = "config" } )
else
ClearProblem( "gmod_mcore_test" )
end
if ( math.abs( GetConVarNumber( "voice_fadeouttime" ) - 0.1 ) > 0.001 ) then
FireProblem( { id = "voice_fadeouttime", text = "#problem.voice_fadeouttime", type = "config", fix = function() RunConsoleCommand( "voice_fadeouttime", "0.1" ) ClearProblem( "voice_fadeouttime" ) end } )
else
ClearProblem( "voice_fadeouttime" )
end
if ( GetConVarNumber( "mat_viewportscale" ) < 0.1 ) then
FireProblem( { id = "mat_viewportscale", text = "#problem.mat_viewportscale", type = "config", fix = function() RunConsoleCommand( "mat_viewportscale", "1" ) ClearProblem( "mat_viewportscale" ) end } )
else
ClearProblem( "mat_viewportscale" )
end
if ( ScrW() < 1000 or ScrH() < 700 ) then
FireProblem( { id = "screen_res", text = "#problem.screen_res", type = "config" } )
else
ClearProblem( "screen_res" )
end
-- These are not saved, but still affect gameplay
if ( GetConVarNumber( "cl_forwardspeed" ) != 10000 or GetConVarNumber( "cl_sidespeed" ) != 10000 or GetConVarNumber( "cl_backspeed" ) != 10000 ) then
FireProblem( { id = "cl_speeds", text = "#problem.cl_speeds", type = "config", fix = function()
RunConsoleCommand( "cl_forwardspeed", "10000" )
RunConsoleCommand( "cl_sidespeed", "10000" )
RunConsoleCommand( "cl_backspeed", "10000" )
end } )
else
ClearProblem( "cl_speeds" )
end
if ( render.GetDXLevel() != 95 and render.GetDXLevel() != 90 ) then
FireProblem( { id = "mat_dxlevel", text = language.GetPhrase( "problem.mat_dxlevel" ):format( render.GetDXLevel() ), type = "config" } )
else
ClearProblem( "mat_dxlevel" )
end
end )
-- Problems that we only need to check on startup
if ( !render.SupportsHDR() ) then FireProblem( { text = "#problem.no_hdr", type = "hardware" } ) end
if ( !render.SupportsPixelShaders_1_4() ) then FireProblem( { text = "#problem.no_ps14", type = "hardware" } ) end
if ( !render.SupportsPixelShaders_2_0() ) then FireProblem( { text = "#problem.no_ps20", type = "hardware" } ) end
if ( !render.SupportsVertexShaders_2_0() ) then FireProblem( { text = "#problem.no_vs20", type = "hardware" } ) end
local AddonConflicts = {}
hook.Add( "OnNotifyAddonConflict", "AddonConflictNotification", function( addon1, addon2, fileName )
local id = addon1 .. "vs" .. addon2
local id1 = addon2 .. "vs" .. addon1
if ( AddonConflicts[ id1 ] ) then id = id1 end
if ( AddonConflicts[ id ] == nil ) then
AddonConflicts[ id ] = {
addon1 = addon1,
addon2 = addon2,
files = {}
}
steamworks.FileInfo( addon1, function( result )
if ( !result ) then return end
AddonConflicts[ id ].addon1 = result.title
RefreshAddonConflicts()
end )
steamworks.FileInfo( addon2, function( result )
if ( !result ) then return end
AddonConflicts[ id ].addon2 = result.title
RefreshAddonConflicts()
end )
end
AddonConflicts[ id ].files[ fileName ] = true
RefreshAddonConflicts()
end )
function RefreshAddonConflicts()
timer.Create( "addon_conflicts", 1, 1, FireAddonConflicts )
end
function FireAddonConflicts()
for id, tbl in pairs( AddonConflicts ) do
local files = ""
for file, _ in pairs( tbl.files ) do
files = files .. file .. "\n"
end
local text = language.GetPhrase( "problem.addon_conflict" )
text = text:format( "<color=255,128,0>" .. tbl.addon1 .. "</color>", "<color=255,128,0>" .. tbl.addon2 .. "</color>", files )
FireProblem( {
id = id,
type = "addons",
severity = 0,
text = text
} )
end
end

View File

@@ -0,0 +1,139 @@
--[[
| 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( "problem_lua.lua" )
include( "problem_generic.lua" )
include( "permissions.lua" )
local PANEL = {}
function PANEL:Init()
self:SetSize( ScrW(), ScrH() )
self:MakePopup()
self.ErrorPanels = {}
self.ProblemPanels = {}
local ProblemsFrame = vgui.Create( "DFrame", self )
ProblemsFrame:SetSize( ScrW() * 0.75, ScrH() * 0.75 )
ProblemsFrame:Center()
ProblemsFrame:SetTitle( "" )
ProblemsFrame:SetDraggable( false )
ProblemsFrame:SetBackgroundBlur( true )
ProblemsFrame.OnRemove = function( frm ) self:Remove() end
ProblemsFrame.Paint = function( frm ) end
ProblemsFrame:DockPadding( 5, 3, 5, 0 )
local sheet = vgui.Create( "DPropertySheet", ProblemsFrame )
sheet:Dock( FILL )
self.Tabs = sheet
-- Lua errors
local luaErrorList = ProblemsFrame:Add( "DScrollPanel" )
sheet:AddSheet( "#problems.lua_errors", luaErrorList, "icon16/error.png" )
self.LuaErrorList = luaErrorList
-- Generic problems
local problemsList = ProblemsFrame:Add( "DScrollPanel" )
sheet:AddSheet( "#problems.problems", problemsList, "icon16/tick.png" )
self.ProblemsList = problemsList
-- Permissions
local permissionList = ProblemsFrame:Add( "PermissionViewer" )
permissionList.ParentFrame = self
sheet:AddSheet( "#permissions.title", permissionList, "icon16/lock.png" )
ProblemsFrame.btnClose:MoveToFront()
ProblemsFrame.btnMaxim:MoveToFront()
ProblemsFrame.btnMinim:MoveToFront()
end
function PANEL:AddEmptyWarning( txt, parent )
local lab = parent:Add( "DLabel" )
lab:SetText( txt )
lab:SetBright( true )
lab:SetFont( "DermaLarge" )
lab:SetContentAlignment( 5 )
lab:Dock( FILL )
-- Horrible hack
lab.Paint = function( s, w, h )
s:SetTall( parent:GetTall() )
end
return lab
end
function PANEL:Paint( w, h )
draw.RoundedBox( 0, 0, 0, w, h, Color( 0, 0, 0, 240 ) )
end
function PANEL:PerformLayout()
if ( self.LuaErrorList:GetCanvas():ChildCount() < 1 ) then
self.NoErrorsLabel = self:AddEmptyWarning( "#problems.no_lua_errors", self.LuaErrorList )
end
if ( self.ProblemsList:GetCanvas():ChildCount() < 1 ) then
self.NoProblemsLabel = self:AddEmptyWarning( "#problems.no_problems", self.ProblemsList )
end
end
function PANEL:ReceivedError( uid, err )
if ( IsValid( self.NoErrorsLabel ) ) then self.NoErrorsLabel:Remove() end
local groupID = err.type or "Other"
local pnl = self.ErrorPanels[ groupID ]
if ( !IsValid( pnl ) ) then
pnl = self.LuaErrorList:Add( "LuaProblemGroup" )
pnl:SetTitleAndID( err.title, err.addonid, groupID )
self.ErrorPanels[ groupID ] = pnl
-- Sort
local z = 0
for gid, epnl in SortedPairs( self.ErrorPanels ) do
epnl:SetZPos( z )
z = z + 1
end
self:InvalidateLayout()
end
pnl:ReceivedError( uid, err )
end
function PANEL:ReceivedProblem( uid, prob )
if ( IsValid( self.NoProblemsLabel ) ) then self.NoProblemsLabel:Remove() end
local groupID = prob.type or "other"
local pnl = self.ProblemPanels[ groupID ]
if ( !IsValid( pnl ) ) then
pnl = self.ProblemsList:Add( "GenericProblemGroup" )
pnl:SetGroup( groupID )
self.ProblemPanels[ groupID ] = pnl
self:InvalidateLayout()
end
pnl:ReceivedProblem( uid, prob )
end
vgui.Register( "ProblemsPanel", PANEL, "EditablePanel" )

325
lua/menu/ugcpublish.lua Normal file
View File

@@ -0,0 +1,325 @@
--[[
| 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:SetSize( 700, 200 )
self:Center()
self:DockPadding( 10, 24 + 10, 10, 10 )
self:SetTitle( "#ugc_upload.frametitle" )
self:SetBackgroundBlur( true ) -- Indicate that the background elements won't be usable
--self:SetSizable( true )
self.AddonList = self:Add( "DListView" )
self.AddonList:Dock( RIGHT )
self.AddonList:SetWide( 200 )
self.AddonList:AddColumn( "#ugc_upload.existing" )
self.AddonList:DockMargin( 10, 0, 0, 0 )
self.AddonList:SetMultiSelect( false )
self.AddonList:SetMouseInputEnabled( false )
self.AddonList.OnRowSelected = function( s, ... ) self:OnRowSelected( ... ) end
self.AddonListLabel = self.AddonList:Add( "DLabel" )
self.AddonListLabel:Dock( FILL )
self.AddonListLabel:SetTextColor( color_black )
self.AddonListLabel:SetContentAlignment( 5 )
self.AddonListLabel:SetZPos( 100 )
self.AddonListLabel.Paint = function( s, w, h ) draw.RoundedBox( 0, 0, 0, w, h, Color( 0, 0, 0, 100 ) ) end -- TODO: Prolly should be hooked to skin
local topContainer = self:Add( "Panel" )
topContainer:Dock( TOP )
topContainer:SetTall( 200 )
self.Image = topContainer:Add( "DImage" )
self.Image:Dock( LEFT )
self.Image:SetWide( 200 )
self.Image:DockMargin( 0, 0, 5, 0 )
local titleLabel = topContainer:Add( "DLabel" )
titleLabel:Dock( TOP )
titleLabel:SetText( "#ugc_upload.title" )
self.Title = topContainer:Add( "DTextEntry" )
self.Title:Dock( TOP )
self.Title:SetUpdateOnType( true )
self.Title.OnTextChanged = function( s ) self:CheckInput() end
local descLabel = topContainer:Add( "DLabel" )
descLabel:Dock( TOP )
descLabel:SetText( "#ugc_upload.description" )
self.Description = topContainer:Add( "DTextEntry" )
self.Description:Dock( FILL )
self.Description:SetMultiline( true )
self.TagsLabel = self:Add( "DLabel" )
self.TagsLabel:Dock( TOP )
self.TagsLabel:SetText( "#ugc_upload.tags" )
self.Tags = self:Add( "Panel" )
self.Tags:Dock( TOP )
self.ChangeNotesLabel = self:Add( "DLabel" )
self.ChangeNotesLabel:Dock( TOP )
self.ChangeNotesLabel:DockMargin( 0, 5, 0, 0 )
self.ChangeNotesLabel:SetText( "#ugc_upload.changenotes" )
self.ChangeNotes = self:Add( "DTextEntry" )
self.ChangeNotes:Dock( TOP )
self.ChangeNotes:SetPlaceholderText( "ugc_upload.changenotes.help" )
self.ChangeNotes:SetVisible( false )
self.ChangeNotes:SetUpdateOnType( true )
self.ChangeNotes:SetMultiline( true )
self.ChangeNotes:SetTall( 80 )
self.Publish = self:Add( "DButton" )
self.Publish:Dock( TOP )
self.Publish:SetEnabled( false )
self.Publish:SetText( "#ugc_upload.publish" )
self.Publish:DockMargin( 0, 5, 0, 0 )
self.Publish.DoClick = function( s ) self:DoPublish() end
self.UpdateProgress = self:Add( "DLabel" )
self.UpdateProgress:SetText( "Publising your content, please wait..." )
self.UpdateProgress:SetTextColor( color_black )
self.UpdateProgress:SetContentAlignment( 5 )
self.UpdateProgress:SetZPos( 100 )
self.UpdateProgress:SetVisible( false )
self.UpdateProgress.Paint = function( s, w, h )
s:StretchToParent( 4, 4, 4, 4 )
draw.RoundedBox( 0, 0, 0, w, h, Color( 200, 200, 200, 255 ) ) -- TODO: Prolly should be hooked to skin
end
end
PANEL.TagsPerType = {
save = {
scenes = "#ugc_upload.tag_scenes_save",
courses = "#ugc_upload.tag_courses",
machines = "#ugc_upload.tag_machines",
buildings = "#ugc_upload.tag_buildings",
others = "#ugc_upload.tag_others"
},
dupe = {
posed = "#ugc_upload.tag_posed",
scenes = "#ugc_upload.tag_scenes",
vehicles = "#ugc_upload.tag_vehicles",
machines = "#ugc_upload.tag_machines",
buildings = "#ugc_upload.tag_buildings",
others = "#ugc_upload.tag_others"
}
}
function PANEL:FitContents()
-- Make this panel smaller, stuff below won't shrink it, only expand it
self:SetTall( 200 )
-- Set the window height to nicely fit everything, no extra space anywhere
self:InvalidateLayout( true )
self:SizeToChildren( false, true )
end
function PANEL:OnRowSelected( rowID, row )
-- Create new node, reset the fields
if ( !row.WorkshopData ) then
self.Publish:SetText( "#ugc_upload.publish" )
self.ChangeNotes:SetVisible( false )
self.ChangeNotesLabel:SetVisible( false )
self.Title:SetText( "" )
self.Description:SetText( "" )
for k, v in pairs( self.Tags:GetChildren() ) do v:SetChecked( false ) end -- SetChecked to not invoke OnChange
self:FitContents()
self:CheckInput()
return
end
local dat = row.WorkshopData
self.Title:SetText( dat.title )
self.Description:SetText( dat.description )
for k, v in pairs( self.Tags:GetChildren() ) do
if ( dat.tags:find( v:GetName() ) ) then v:SetValue( true ) break end
end
self.Publish:SetText( "#ugc_upload.update" )
self.ChangeNotes:SetVisible( true )
self.ChangeNotesLabel:SetVisible( true )
self:FitContents()
self:CheckInput()
end
function PANEL:Setup( ugcType, file, imageFile, handler )
self.ugcType = ugcType
self.ugcFile = file
self.ugcImage = imageFile
self.ugcHandler = handler
self.Image:SetImage( "../" .. self.ugcImage )
if ( self.TagsPerType[ self.ugcType ] ) then
for k, v in pairs( self.TagsPerType[ self.ugcType ] ) do
local rb = self.Tags:Add( "DCheckBoxLabel" )
rb:Dock( TOP )
rb:SetText( v )
rb:SetName( k )
rb:DockMargin( 4, 0, 0, 2 )
rb.OnChange = function( s, val )
self:CheckInput()
local num = 0
for id, pnl in pairs( s:GetParent():GetChildren() ) do
if ( pnl:GetChecked() ) then num = num + 1 end
if ( pnl == s or !val ) then continue end
pnl:SetValue( false ) -- Validate that only 1 is selected
end
if ( !val && num == 0 ) then s:SetValue( true ) end -- Don't allow to unselect the only 1 selected
end
Derma_Hook( rb.Button, "Paint", "Paint", "RadioButton" )
end
self.Tags:InvalidateChildren( false ) -- Update position of all the docked elements
self.Tags:SizeToChildren( false, true ) -- Set nice size for the tags container
else
self.TagsLabel:SetVisible( false )
self.Tags:SetVisible( false )
end
self:FitContents()
self:UpdateWorkshopItems()
end
function PANEL:UpdateWorkshopItems()
self.AddonListLabel:SetVisible( true )
self.AddonListLabel:SetText( "#ugc_upload.loading" )
self.AddonList:Clear()
self.AddonList:AddLine( "#ugc_upload.createnew" )
self.AddonList:SelectFirstItem()
steamworks.GetList( "mine", { self.ugcType }, 0, 9999, 0, "1", function( data )
for i, id in pairs( data.results ) do
steamworks.FileInfo( id, function( info )
local a = self.AddonList:AddLine( info.title )
a:SetTooltip( "#ugc_upload.rightclickopen" )
a.WorkshopID = id
a.WorkshopData = info
a.OnRightClick = function( s )
if ( !s.WorkshopID ) then return end
steamworks.ViewFile( s.WorkshopID )
end
self.AddonList:SetMouseInputEnabled( true )
self.AddonListLabel:SetVisible( false )
end )
end
if ( data.totalresults == 0 ) then
self.AddonListLabel:SetText( "#ugc_upload.nothingfound" )
end
end )
end
function PANEL:GetChosenTag()
for k, v in pairs( self.Tags:GetChildren() ) do
if ( v:GetChecked() ) then
return v:GetName()
end
end
end
function PANEL:CheckInput()
if ( self.TagsPerType[ self.ugcType ] && !self:GetChosenTag() ) then return self.Publish:SetEnabled( false ) end
if ( self.Title:GetText() == "" ) then return self.Publish:SetEnabled( false ) end
self.Publish:SetEnabled( true )
end
local errors = {}
errors[ "badhandle" ] = "Generic error while trying to create item."
errors[ "createid" ] = "Generic error while trying to create workshop item ID."
errors[ "badupdatehandle" ] = "Generic error while trying to update item."
errors[ "badsubmitupdatehandle" ] = "Could not submit update to Steam."
errors[ "cantupdate" ] = "Failed to upload workshop files to Steam."
errors[ "badwords" ] = "Please refrain from uploading spam, vulgar, sexual or offensive content to Steam Workshop."
function PANEL:DisplayError( err )
-- Unlock the UI and allow user to try again/make changes
self.UpdateProgress:SetVisible( false )
self:SetMouseInputEnabled( true )
if ( err == "termsofservice" ) then
err = "You must accept Steam Workshop Terms of Service before you can upload content!\nWould you like to open it right now?"
Derma_Query( err, "Error while publishing content!", "Yes", function()
gui.OpenURL( "http://steamcommunity.com/sharedfiles/workshoplegalagreement" )
end, "Nah" )
return
end
if ( errors[ err ] ) then err = errors[ err ] end
Derma_Message( err or "UNKNOWN ERROR", "Error while publishing content!", "OK" )
end
function PANEL:OnPublishFinished( wsid, err )
if ( !wsid ) then
self:DisplayError( err )
return
end
-- TODO: Show "View item"/"Close" buttons instead?
steamworks.ViewFile( wsid )
self:Remove()
end
function PANEL:DoPublish()
local ChosenTag = self:GetChosenTag()
if ( self.TagsPerType[ self.ugcType ] && ChosenTag == nil ) then
self:DisplayError( "You must choose a tag!")
return
end
if ( self.Title:GetText() == "" ) then
self:DisplayError( "You must provide a title!" )
return
end
local workshopUpdateID = nil
local updateButtonID, updateButton = self.AddonList:GetSelectedLine()
if ( IsValid( updateButton ) ) then
workshopUpdateID = updateButton.WorkshopID
end
-- Lock down the UI
self.UpdateProgress:SetVisible( true )
self:SetMouseInputEnabled( false )
-- TODO: Display update progress?
-- TODO: Confirmation dialog?
-- Start the process
local err = self.ugcHandler:FinishPublish( self.ugcFile, self.ugcImage, self.Title:GetText(), self.Description:GetText(), ChosenTag, {
WorkshopID = workshopUpdateID,
ChangeNotes = self.ChangeNotes:GetText(),
Callback = function( wsid, erro ) self:OnPublishFinished( wsid, erro ) end
} )
if ( err ) then self:DisplayError( err ) end
end
vgui.Register( "UGCPublishWindow", PANEL, "DFrame" )

42
lua/menu/util.lua Normal file
View File

@@ -0,0 +1,42 @@
--[[
| 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/
--]]
concommand.Add( "whereis", function( _, _, _, path )
local absolutePath = util.RelativePathToFull_Menu( path, "GAME" )
if ( !absolutePath or !file.Exists( path, "GAME" ) ) then
MsgN "File not found."
return
end
local relativePath = util.FullPathToRelative_Menu( absolutePath, "MOD" )
-- If the relative path is inside the workshop dir, it's part of a workshop addon
if ( relativePath && relativePath:match( "^workshop[\\/].*" ) ) then
local addonInfo = util.RelativePathToGMA_Menu( path )
-- Not here? Maybe somebody just put their own file in ./workshop
if ( addonInfo ) then
local addonRelativePath = util.RelativePathToFull_Menu( addonInfo.File )
MsgN( "'", addonInfo.Title, "' - ", addonRelativePath or addonInfo.File )
return
end
end
MsgN( absolutePath )
end, nil, "Searches for the highest priority instance of a file within the GAME mount path." )

70
lua/menu/video.lua Normal file
View File

@@ -0,0 +1,70 @@
--[[
| 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 vid_width = CreateConVar( "vid_width", "640", { FCVAR_ARCHIVE + FCVAR_DONTRECORD }, "Specifies the width of the recorded video. The height will be adjusted automatically based on your aspect ratio" )
local vid_fps = CreateConVar( "vid_fps", "30", { FCVAR_ARCHIVE + FCVAR_DONTRECORD }, "The FPS of the recorded video" )
local vid_sound = CreateConVar( "vid_sound", "1", { FCVAR_ARCHIVE + FCVAR_DONTRECORD }, "Enable sound recording" )
local activeVideo
local videoStart
concommand.Add( "gm_video", function()
if ( activeVideo ) then
activeVideo:Finish()
activeVideo = nil
local time = SysTime() - videoStart
MsgN( string.format( "Finished recording. Length: %.1fs", time ) )
return
end
local dynamic_name = game.GetMap() .. " " .. util.DateStamp()
local width = math.Round( vid_width:GetFloat() )
local height = math.Round( ScrH() * ( width / ScrW() ) )
local fps = math.Round( vid_fps:GetFloat() )
local err
activeVideo, err = video.Record( {
name = dynamic_name,
container = "webm",
video = "vp8",
audio = "vorbis",
quality = 0,
bitrate = 1024 * 64,
width = width,
height = height,
fps = fps,
lockfps = true
} )
if ( !activeVideo ) then
MsgN( "Couldn't record video: ", err )
return
end
activeVideo:SetRecordSound( vid_sound:GetBool() )
videoStart = SysTime()
MsgN( string.format( "Recording %ix%i@%iFPS video to \"videos/%s.webm\"...", width, height, fps, dynamic_name ) )
end, nil, "Starts and stops the recording of a .webm (VP8/Vorbis) video. See vid_* convars for settings.", { FCVAR_DONTRECORD } )
hook.Add( "DrawOverlay", "CaptureFrames", function()
if ( !activeVideo ) then return end
activeVideo:AddFrame( FrameTime(), true )
end )